/* enterprise-landing.jsx — the public Globally page (#/business).
 *
 * This page is written to work as the primary front page. A first-time visitor
 * should be able to answer, inside the first viewport: what Globally is, what
 * it does, what Mira is, and why they would use it.
 *
 * FIVE SECTIONS, in this order, and no more:
 *
 *   1. Hero ................ cream        find funding / see what's changing /
 *                                         know what to do next, over a real map
 *   2. What Globally does .. white        three concepts, one calm row
 *   3. See Globally work ... pastel blue  ONE product demo (enterprise-demo.jsx)
 *   4. Why Globally ........ sand         scattered tools vs one workflow
 *   5. Final CTA ........... pastel blue
 *
 * followed by a deliberately quiet closing band carrying the demo-request form
 * (#early-access) and pricing (#pricing), which is disclosed on request rather
 * than shown. Both anchors are kept because #/pricing redirects here and plan
 * CTAs deep-link with ?plan=…; the routes and the pricing module are untouched.
 *
 * Marketing copy sits directly on the canvas. Cards are reserved for actual
 * product UI — opportunities, signals, Mira output — so the product reads as
 * the important thing on the page.
 *
 * Real data: the funding corpus via MiraViz, and the article corpus MainApp has
 * already loaded. Nothing here hard-codes a count or a claim.
 *
 * Exposes window.MiraLanding.
 */
(function () {
  'use strict';
  var h = React.createElement;

  // Destinations that are being built separately. A link appears only once its
  // route exists — a dead link is worse than a missing one. Fill these in and
  // the links render themselves.
  var ROUTES = {
    whyGlobally: null,        // e.g. '#/why-globally'
    fundingReadiness: null,   // e.g. '#/funding-readiness'
  };

  // ── hash query, shared with the route layer ─────────────────────────────
  function hashQuery() {
    try {
      var q = (window.location.hash || '').split('?')[1];
      if (!q) return {};
      var out = {};
      q.split('&').forEach(function (pair) {
        if (!pair) return;
        var kv = pair.split('=');
        out[decodeURIComponent(kv[0])] = decodeURIComponent((kv[1] || '').replace(/\+/g, ' '));
      });
      return out;
    } catch (e) { return {}; }
  }
  // The app scrolls inside .gl-main, not the document. scrollIntoView's smooth
  // animation reliably stops short over long distances in that container (a
  // "Book a demo" click from the final CTA landed 2,800px early), so drive the
  // container directly: one well-defined animation to a computed offset.
  function scroller(el) {
    var n = el && el.parentElement;
    while (n && n !== document.body) {
      var oy = getComputedStyle(n).overflowY;
      if ((oy === 'auto' || oy === 'scroll') && n.scrollHeight > n.clientHeight) return n;
      n = n.parentElement;
    }
    return null;
  }
  function offsetIn(sc, el) {
    return sc.scrollTop + el.getBoundingClientRect().top - sc.getBoundingClientRect().top;
  }
  // .gl-main carries scroll-behavior:smooth, and in that container BOTH
  // scrollTo({behavior:'smooth'}) and a plain scrollTop assignment silently do
  // nothing — verified by instrumenting the scroll event: a click fired, no
  // scroll event followed. Forcing behaviour to auto and tweening the offset
  // ourselves is the only thing that actually moves it.
  var _anim = 0;
  function glide(sc, to) {
    cancelAnimationFrame(_anim);
    var prev = sc.style.scrollBehavior;
    sc.style.scrollBehavior = 'auto';
    var reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    var from = sc.scrollTop, dist = to - from;
    if (reduce || Math.abs(dist) < 2) { sc.scrollTop = to; sc.style.scrollBehavior = prev; return; }
    var ms = Math.min(700, 260 + Math.abs(dist) * 0.12), t0 = null;
    _anim = requestAnimationFrame(function step(now) {
      if (t0 === null) t0 = now;
      // rAF stops in a hidden tab; land on the target rather than stranding
      // the reader halfway when they come back.
      if (document.hidden) { sc.scrollTop = to; sc.style.scrollBehavior = prev; return; }
      var p = Math.min(1, (now - t0) / ms);
      var e = p < .5 ? 4 * p * p * p : 1 - Math.pow(-2 * p + 2, 3) / 2;   // easeInOutCubic
      sc.scrollTop = from + dist * e;
      if (p < 1) _anim = requestAnimationFrame(step);
      else sc.style.scrollBehavior = prev;
    });
  }
  function scrollTo(id, offset) {
    var el = document.getElementById(id);
    if (!el) return;
    var sc = scroller(el);
    if (!sc) { el.scrollIntoView({ behavior: 'smooth', block: 'start' }); return; }
    glide(sc, Math.max(0, offsetIn(sc, el) - (offset || 0)));
  }
  // Deep links land while the app is still mounting and the shell resets the
  // scroller afterwards, so retry until the target arrives. Retries must be
  // 'instant' — 'auto' defers to the container's scroll-behavior:smooth and
  // each call would restart the animation.
  function scrollToWhenReady(id) {
    var tries = 0;
    (function attempt() {
      var el = document.getElementById(id);
      if (el) {
        if (Math.abs(el.getBoundingClientRect().top) < 180) return;
        var sc = scroller(el);
        if (!sc) el.scrollIntoView({ behavior: 'instant', block: 'start' });
        else if (tries) { sc.style.scrollBehavior = 'auto'; sc.scrollTop = offsetIn(sc, el); }
        else glide(sc, offsetIn(sc, el));
      }
      if (++tries < 8) setTimeout(attempt, 400);
    })();
  }
  // Funding lives behind the Enterprise preview gate on production, so the
  // primary CTA goes to the workspace where it exists and to the demo where it
  // does not, rather than to a route that would bounce.
  function exploreFunding() {
    var gated = typeof window.isEnterprisePreviewEnabled === 'function'
      && window.isEnterprisePreviewEnabled();
    if (gated) { window.location.hash = '#/enterprise/funding'; return; }
    scrollTo('see-it-work');
  }

  // ══ 1. HERO ═════════════════════════════════════════════════════════════
  // No map. A curved typographic frame — "See what's changing." set on an SVG
  // path that rises up the left of the headline and sweeps across its top —
  // and, on the right, a layered "intelligence desk": funding, a signal, Mira's
  // briefing and the day's attention, as softly stacked papers joined by one
  // quiet editorial line. Real corpus data fills every card.
  function clampStr(s, n) { s = String(s || ''); return s.length > n ? s.slice(0, n - 1) + '…' : s; }

  // The hero desk is a fixed editorial composition — an illustrative snapshot of
  // what Mira surfaces, matched to the approved reference. It is deliberately
  // static (not live corpus rows) so the four papers always read cleanly at the
  // scale the composition needs.
  function HeroDesk() {
    function field(k, v) { return h('div', { className: 'gl-paper-field' }, h('dt', null, k), h('dd', null, v)); }
    function attn(tone, title, impact) {
      return h('li', { className: 'gl-attn-row' },
        h('span', { className: 'gl-attn-ic gl-attn-ic--' + tone }),
        h('span', { className: 'gl-attn-tx' }, h('b', null, title), h('em', null, impact)));
    }
    return h('div', { className: 'gl-desk' },
      // The flowing line: the signal and the funding opportunity both feed down
      // into Mira's briefing, which resolves into what deserves attention. Two
      // orange nodes sit where information converges.
      h('svg', { className: 'gl-desk-link', viewBox: '0 0 700 620', preserveAspectRatio: 'none', 'aria-hidden': 'true', focusable: 'false' },
        h('path', { className: 'gl-desk-link-p',
          d: 'M 300 150 C 420 190 470 210 452 250 C 430 300 250 300 236 360 C 226 405 330 430 470 452' }),
        h('circle', { className: 'gl-desk-node', cx: '452', cy: '250', r: '5' }),
        h('circle', { className: 'gl-desk-node', cx: '236', cy: '360', r: '5' })),

      // One coherent illustrative scenario: a Sri Lankan NGO working on child
      // protection, education and community programmes. All four papers connect.

      // A — Funding opportunity
      h('div', { className: 'gl-paper gl-paper--fund' },
        h('span', { className: 'gl-paper-bk', 'aria-hidden': 'true' }),
        h('p', { className: 'gl-paper-k gl-paper-k--fund' }, 'Funding opportunity'),
        h('h3', { className: 'gl-paper-t' }, 'South Asia Child Protection Innovation Fund'),
        h('p', { className: 'gl-paper-sub' }, 'Regional Development Foundation'),
        h('div', { className: 'gl-paper-fields' },
          field('Grant', 'Up to £500k'),
          field('Deadline', '30 Sep 2026'),
          field('Eligibility', 'NGOs · South Asia'))),

      // B — Signal (same organisation's world)
      h('div', { className: 'gl-paper gl-paper--sig gl-fold-br' },
        h('p', { className: 'gl-paper-k gl-paper-k--sig' }, 'Signal'),
        h('h4', { className: 'gl-paper-t2' }, 'Sri Lanka expands digital safeguarding requirements for schools'),
        h('p', { className: 'gl-paper-meta' }, 'Sri Lanka · Child protection'),
        h('svg', { className: 'gl-spark', viewBox: '0 0 120 28', 'aria-hidden': 'true' },
          h('polyline', { points: '0,22 18,18 34,20 52,10 70,14 88,5 106,9 120,3' })),
        h('div', { className: 'gl-paper-tags' },
          h('span', null, 'Safeguarding'), h('span', null, 'Education'))),

      // C — Mira briefing (ties the funding + signal into one read)
      h('div', { className: 'gl-paper gl-paper--mira gl-fold-bl' },
        h('p', { className: 'gl-paper-k gl-paper-k--mira' }, 'Mira briefing'),
        h('h4', { className: 'gl-paper-t2' }, '3 things your organisation should know'),
        h('ol', { className: 'gl-paper-list' },
          h('li', null, h('span', { className: 'gl-paper-n' }, '01'), h('span', null, 'New regional child-protection funding opportunity')),
          h('li', null, h('span', { className: 'gl-paper-n' }, '02'), h('span', null, 'Sri Lanka safeguarding rules may affect school programmes')),
          h('li', null, h('span', { className: 'gl-paper-n' }, '03'), h('span', null, 'Education partnerships are becoming more important')))),

      // D — What deserves attention (the decision layer for the same scenario)
      h('div', { className: 'gl-paper gl-paper--attn' },
        h('span', { className: 'gl-paper-sidetab', 'aria-hidden': 'true' }),
        h('p', { className: 'gl-paper-k' }, 'What deserves attention'),
        h('ul', { className: 'gl-paper-attn' },
          attn('f', 'Child-protection funding', 'New opportunity · High priority'),
          attn('s', 'School safeguarding rules', 'Programme impact · Medium priority'),
          attn('d', 'Regional education partners', 'Partnership opportunity · Low priority'))),

      // A discreet note that this is an illustrative, not-live example.
      h('p', { className: 'gl-desk-note' }, 'Illustrative Mira example'));
  }

  function Hero(p) {
    return h('header', { className: 'gl-hero', id: 'top' },
      h('div', { className: 'gl-in gl-hero-in' },
        h('div', { className: 'gl-hero-copy' },
          // The curved phrase — one continuous typographic path wrapping the
          // upper-left of the headline. Hidden on small screens (see CSS),
          // where a plain stacked label takes its place.
          h('svg', { className: 'gl-curve', viewBox: '0 0 480 470', 'aria-hidden': 'true', focusable: 'false' },
            h('defs', null,
              // A rounded right-angle bracket: the phrase runs up the left of the
              // headline, turns a soft ~90° corner at the top-left, and runs
              // across the top — like the corner of a rounded rectangle wrapping
              // the headline, not a shallow arc.
              h('path', { id: 'glCurvePath', fill: 'none',
                d: 'M 80 442 L 80 214 Q 80 108 188 86 L 456 70' })),
            h('text', { className: 'gl-curve-t' },
              h('textPath', { xlinkHref: '#glCurvePath', startOffset: '0%' }, 'See what’s changing.'))),
          h('span', { className: 'gl-curve-fallback' }, 'See what’s changing.'),
          h('h1', { className: 'gl-h1' },
            h('span', { className: 'gl-h1-fund' }, 'Find funding.'),
            h('span', { className: 'gl-h1-know' }, 'Know what', h('br'), 'to do ',
              h('span', { className: 'gl-h1-next' }, 'next.'))),
          h('p', { className: 'gl-hero-sub' },
            'Globally helps development organisations track change, find funding and ' +
            'turn important developments into clear next steps.'),
          h('div', { className: 'gl-ctas' },
            h('button', { type: 'button', className: 'gl-btn gl-btn--primary', onClick: exploreFunding }, 'Explore funding'),
            h('button', { type: 'button', className: 'gl-btn gl-btn--ghost', onClick: function () { scrollTo('see-it-work'); } }, 'See how Mira works'))),
        h(HeroDesk, { intel: p.intel, articles: p.articles })));
  }

  // ══ 2. WHAT GLOBALLY DOES ═══════════════════════════════════════════════
  // INTERACTION 1 — the topic lens. Pick a topic your programmes work on and
  // the panel behind it re-reads the real corpus for that topic. It is the
  // shortest possible demonstration of the page's actual claim: everything is
  // organised around the things you work on. Every figure is counted live from
  // funding-opportunities.json — nothing here is written down in advance.
  function TopicLens(p) {
    var intel = p.intel;
    var sectors = intel
      ? intel.sectors.filter(function (s) { return s.n >= 4; }).slice(0, 6)
      : [];
    var sS = React.useState(null); var picked = sS[0], setPicked = sS[1];
    var active = picked || (sectors[0] && sectors[0].label) || null;

    var view = React.useMemo(function () {
      if (!intel || !active) return null;
      var rows = intel.opps.filter(function (o) { return o.sectors.indexOf(active) !== -1; });
      var cc = {}, fc = {};
      rows.forEach(function (o) {
        o.countries.forEach(function (c) { cc[c] = (cc[c] || 0) + 1; });
        if (o.funder) fc[o.funder] = (fc[o.funder] || 0) + 1;
      });
      var countries = Object.keys(cc).sort(function (a, b) { return cc[b] - cc[a]; });
      var funders = Object.keys(fc).sort(function (a, b) { return fc[b] - fc[a]; });
      var example = rows.filter(function (o) { return o.title && o.countries.length; })[0] || rows[0];
      return { n: rows.length, countries: countries, funders: funders, example: example };
    }, [intel, active]);

    if (!sectors.length) return null;

    return h('div', { className: 'gl-lens' },
      h('p', { className: 'gl-lens-k' }, 'Pick a topic your programmes work on'),
      h('div', { className: 'gl-lens-pills', role: 'tablist' }, sectors.map(function (s) {
        return h('button', {
          key: s.label, type: 'button', role: 'tab', 'aria-selected': s.label === active,
          className: 'gl-pill' + (s.label === active ? ' is-on' : ''),
          onClick: function () { setPicked(s.label); },
        }, s.label);
      })),
      view ? h('div', { className: 'gl-lens-panel', key: active },
        h('span', { className: 'gl-lens-tab' }, active),
        h('div', { className: 'gl-lens-stats' },
          h('div', null, h('b', null, view.n), h('span', null, view.n === 1 ? 'opportunity' : 'opportunities')),
          h('div', null, h('b', null, view.countries.length), h('span', null, view.countries.length === 1 ? 'country' : 'countries')),
          h('div', null, h('b', null, view.funders.length), h('span', null, view.funders.length === 1 ? 'funder' : 'funders'))),
        view.countries.length
          ? h('div', { className: 'gl-lens-chips' }, view.countries.slice(0, 6).map(function (c) {
            return h('span', { key: c }, c);
          }), view.countries.length > 6
            ? h('span', { className: 'is-more' }, '+' + (view.countries.length - 6) + ' more')
            : null)
          : null,
        view.example ? h('p', { className: 'gl-lens-eg' },
          h('span', null, 'For example'),
          h('b', null, view.example.title.length > 68 ? view.example.title.slice(0, 67) + '…' : view.example.title),
          h('em', null, [view.example.funder, view.example.countries[0]].filter(Boolean).join(' · '))) : null) : null);
  }

  function capIcon(kind) {
    var c = { fill: 'none', stroke: 'currentColor', strokeWidth: '1.6', strokeLinecap: 'round', strokeLinejoin: 'round' };
    if (kind === 'find') {
      return h('svg', Object.assign({ viewBox: '0 0 24 24', width: '20', height: '20' }, c),
        h('circle', { cx: '11', cy: '11', r: '7' }), h('path', { d: 'M16 16 L21 21' }));
    }
    if (kind === 'see') {
      return h('svg', Object.assign({ viewBox: '0 0 24 24', width: '20', height: '20' }, c),
        h('path', { d: 'M4 20 V4' }), h('path', { d: 'M4 20 H20' }),
        h('rect', { x: '7', y: '12', width: '3', height: '5' }),
        h('rect', { x: '12', y: '8', width: '3', height: '9' }),
        h('rect', { x: '17', y: '5', width: '3', height: '12' }));
    }
    return h('svg', Object.assign({ viewBox: '0 0 24 24', width: '20', height: '20' }, c),
      h('path', { d: 'M6 3 H15 L19 7 V21 H6 Z' }), h('path', { d: 'M15 3 V7 H19' }),
      h('path', { d: 'M9 12 H16 M9 16 H16' }));
  }

  function WhatWeDo(p) {
    var items = [
      ['find', 'Find funding',
        'Discover grants, donors and tenders that match your mission and priorities.', 'Explore funding', exploreFunding],
      ['see', 'Track signals',
        'Monitor policies, funding shifts and developments that matter to your work.', 'See signals', function () { scrollTo('see-it-work'); }],
      ['know', 'Get briefings & act',
        'Mira turns information into clear insights and next steps for your team.', 'Try Mira', function () { scrollTo('see-it-work'); }],
    ];
    return h('section', { className: 'gl-sec gl-sec--white' },
      h('div', { className: 'gl-in' },
        h('h2', { className: 'gl-h2 gl-h2--serif' }, 'Everything your team needs'),
        h('div', { className: 'gl-three' }, items.map(function (it, i) {
          return h('div', { className: 'gl-three-i gl-three-i--' + it[0], key: it[0] },
            h('span', { className: 'gl-three-ic' }, capIcon(it[0])),
            h('h3', null, it[1]),
            h('p', null, it[2]),
            h('button', { type: 'button', className: 'gl-three-link', onClick: it[4] }, it[3], ' →'));
        })),
        h(TopicLens, { intel: p.intel })));
  }

  // ══ 3. SEE GLOBALLY WORK ════════════════════════════════════════════════
  function SeeItWork(p) {
    return h('section', { className: 'gl-sec gl-sec--blue', id: 'see-it-work' },
      h('div', { className: 'gl-in' },
        h('div', { className: 'gl-sec-hd' },
          h('h2', { className: 'gl-h2' }, 'See Globally work.'),
          // No volume claim: the corpus is what it is, and the demo prints its
          // real size a few centimetres below this line.
          h('p', { className: 'gl-lede' },
            'From every opportunity and development we track, down to the few things ' +
            'your organisation needs to act on.')),
        window.GloballyDemo
          ? h(window.GloballyDemo.Demo, { intel: p.intel, articles: p.articles })
          : null));
  }

  // ══ 4. WHY GLOBALLY ═════════════════════════════════════════════════════
  // INTERACTION 3 — the briefing fold-out. The "with Globally" diagram used to
  // end on the words "what deserves your attention", which is a promise. It now
  // ends on the artefact itself: a folded briefing the visitor can open, filled
  // with real current developments from the corpus the app has already loaded.
  function BriefingFold(p) {
    // Ranked the way a briefing would be, not the way the array happens to
    // arrive: the corpus carries a significance score, so the three shown are
    // the three most significant developments that have a country attached.
    var items = (p.articles || []).filter(function (a) {
      return a.country && (a.display_title || a.title);
    }).slice().sort(function (a, b) {
      return (b.significance || 0) - (a.significance || 0);
    }).slice(0, 3);
    var sO = React.useState(false); var open = sO[0], setOpen = sO[1];
    if (!items.length) return null;
    var places = [];
    items.forEach(function (a) { if (places.indexOf(a.country) === -1) places.push(a.country); });
    return h('div', { className: 'gl-fold' + (open ? ' is-open' : '') },
      h('button', {
        type: 'button', className: 'gl-fold-hd', 'aria-expanded': open,
        onClick: function () { setOpen(!open); },
      },
        h('span', { className: 'gl-fold-k' }, 'Today’s briefing'),
        h('span', { className: 'gl-fold-t' },
          items.length + ' developments your team should know'),
        h('span', { className: 'gl-fold-m' }, places.slice(0, 3).join(' · ')),
        h('span', { className: 'gl-fold-x', 'aria-hidden': 'true' }, open ? 'Close' : 'Open')),
      h('div', { className: 'gl-fold-body' },
        h('div', { className: 'gl-fold-inner' }, items.map(function (a, i) {
          return h('div', { className: 'gl-fold-i', key: i },
            h('span', { className: 'gl-fold-n' }, '0' + (i + 1)),
            h('div', null,
              h('h4', null, a.display_title || a.title),
              h('p', null, [a.country, a.sourceName].filter(Boolean).join(' · '))));
        }))));
  }

  function WhyGlobally(p) {
    var intel = p.intel;
    var scattered = ['Grant databases', 'News sites', 'Spreadsheets', 'Newsletters', 'Manual research'];
    return h('section', { className: 'gl-sec gl-sec--sand', id: 'why' },
      h('div', { className: 'gl-in' },
        h('h2', { className: 'gl-h2 gl-h2--wide' },
          'Built for organisations working on the world’s biggest challenges.'),
        h('div', { className: 'gl-compare' },
          h('div', { className: 'gl-compare-side' },
            h('p', { className: 'gl-compare-k' }, 'Without Globally'),
            h('div', { className: 'gl-scatter' }, scattered.map(function (s, i) {
              return h('span', { className: 'gl-scatter-i', key: s, style: { '--i': i } }, s);
            })),
            h('p', { className: 'gl-compare-n' }, 'Five places to look, and no view of what connects them.')),
          h('div', { className: 'gl-compare-side gl-compare-side--with' },
            h('p', { className: 'gl-compare-k' }, 'With Globally'),
            h('div', { className: 'gl-flow' },
              h('div', { className: 'gl-flow-row' },
                h('span', { className: 'gl-flow-i gl-flow-i--fund' }, 'Funding'),
                h('span', { className: 'gl-flow-i gl-flow-i--sig' }, 'Signals'),
                h('span', { className: 'gl-flow-i gl-flow-i--ctx' }, 'Context')),
              h('span', { className: 'gl-flow-arrow' }, '↓'),
              h('span', { className: 'gl-flow-mira' }, 'Mira'),
              h('span', { className: 'gl-flow-arrow' }, '↓'),
              h(BriefingFold, { articles: p.articles })))),
        intel ? h('p', { className: 'gl-proof' },
          'Opportunities are collected from ', h('b', null, intel.sourceCount + ' official sources'),
          ' across ', h('b', null, intel.countryCount + ' countries'),
          ', and every record links back to the funder’s own notice.') : null,
        ROUTES.whyGlobally || ROUTES.fundingReadiness
          ? h('div', { className: 'gl-links' },
            ROUTES.whyGlobally ? h('a', { className: 'gl-textlink', href: ROUTES.whyGlobally }, 'Why Globally →') : null,
            ROUTES.fundingReadiness ? h('a', { className: 'gl-textlink', href: ROUTES.fundingReadiness }, 'Funding readiness quiz →') : null)
          : null));
  }

  // ══ 5. FINAL CTA ════════════════════════════════════════════════════════
  function FinalCTA(p) {
    return h('section', { className: 'gl-sec gl-sec--blue gl-final' },
      h('div', { className: 'gl-in gl-final-in' },
        h('h2', { className: 'gl-h2' },
          h('span', null, 'Spend less time searching.'),
          h('span', null, 'More time doing the work.')),
        h('p', { className: 'gl-lede' },
          'Find funding, follow the world around your programmes and let Mira surface ' +
          'what deserves attention.'),
        h('div', { className: 'gl-ctas' },
          h('button', { type: 'button', className: 'gl-btn gl-btn--primary', onClick: exploreFunding }, 'Explore Globally'),
          h('button', { type: 'button', className: 'gl-btn gl-btn--ghost', onClick: function () { scrollTo('early-access'); } }, 'Book a demo')),
        h('div', { className: 'gl-links' },
          ROUTES.whyGlobally ? h('a', { className: 'gl-textlink', href: ROUTES.whyGlobally }, 'Why Globally') : null,
          ROUTES.fundingReadiness ? h('a', { className: 'gl-textlink', href: ROUTES.fundingReadiness }, 'Funding readiness') : null,
          h('button', { type: 'button', className: 'gl-textlink', onClick: function () { p.openPricing(); } }, 'View pricing →'))));
  }

  // ══ CLOSING BAND — demo request, and pricing on request ═════════════════
  // Pricing is not a homepage story, but #/pricing redirects here and plan CTAs
  // deep-link with ?plan=…&section=pricing, so the module and its anchor stay.
  function Pricing(p) {
    var MP = window.MiraPricing;
    if (!MP) return null;
    return h('div', { className: 'gl-pricing', id: 'pricing' },
      h('button', {
        type: 'button', className: 'gl-disclose', 'aria-expanded': p.open,
        onClick: function () { p.setOpen(!p.open); },
      },
        h('span', null, 'Pricing'),
        h('span', { className: 'gl-disclose-s' }, p.open ? 'Hide −' : 'View plans +')),
      p.open
        ? h('div', { className: 'gl-pricing-body' },
          h(MP.Section, { selectedPlan: p.selectedPlan || null }),
          h(MP.SouthAsiaAccess),
          h(MP.SponsoredAccess))
        : null);
  }

  function RequestAccess(p) {
    var sF = React.useState({
      firstName: '', lastName: '', email: '', organisation: '', role: '',
      organisationType: '', teamSize: '', useCases: [], message: '',
    });
    var form = sF[0], setForm = sF[1];
    var sS = React.useState(false); var sent = sS[0], setSent = sS[1];
    var sB = React.useState(false); var busy = sB[0], setBusy = sB[1];
    var sE = React.useState(null); var err = sE[0], setErr = sE[1];

    var OPTS = [
      { id: 'discovery', label: 'Finding relevant funding' },
      { id: 'eligibility', label: 'Eligibility checking' },
      { id: 'signals', label: 'Monitoring risks and signals' },
      { id: 'briefings', label: 'Team briefings' },
      { id: 'pursuits', label: 'Managing pursuits' },
      { id: 'reporting', label: 'Reporting and oversight' },
    ];

    function field(e) {
      var k = e.target.name, v = e.target.value;
      setForm(function (s) { var n = Object.assign({}, s); n[k] = v; return n; });
    }
    function toggle(id) {
      setForm(function (s) {
        var has = s.useCases.indexOf(id) !== -1;
        return Object.assign({}, s, {
          useCases: has ? s.useCases.filter(function (u) { return u !== id; }) : s.useCases.concat(id),
        });
      });
    }
    function submit(e) {
      e.preventDefault();
      setErr(null);
      if (!form.firstName.trim() || !form.lastName.trim() || !form.email.trim() ||
          !form.organisation.trim() || !form.organisationType) {
        setErr('Please fill in the required fields.'); return;
      }
      if (!form.message.trim() && form.useCases.length === 0) {
        setErr('Please add a message or select at least one use case.'); return;
      }
      setBusy(true);
      var payload = Object.assign({}, form, {
        message: p.ctxLabel ? ('[' + p.ctxLabel + ']\n\n' + form.message) : form.message,
      });
      fetch('/api/mira-business-contact', {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(payload),
      })
        .then(function (r) { return r.json(); })
        .then(function (d) {
          setBusy(false);
          if (!d || d.ok !== true) { setErr("We couldn't send this yet. Please try again."); return; }
          try {
            var list = JSON.parse(localStorage.getItem('globally_business_interests') || '[]');
            list.push(Object.assign({}, payload, { submittedAt: new Date().toISOString() }));
            localStorage.setItem('globally_business_interests', JSON.stringify(list));
          } catch (_) {}
          setSent(true);
        })
        .catch(function () { setBusy(false); setErr("We couldn't send this yet. Please try again."); });
    }

    return h('div', { className: 'gl-book', id: 'early-access' },
      h('div', { className: 'gl-book-copy' },
        h('h3', null, 'See Globally on your own organisation.'),
        h('p', null, 'Tell us what your team is trying to fund or monitor and we will walk you through it. Nothing is billed when you send this.')),
      sent
        ? h('div', { className: 'mbc-success' },
          h('div', { className: 'mbc-success-check' }, '✓'),
          h('p', { className: 'mbc-success-title' }, 'Thanks — we’ve received your request.'),
          h('p', { className: 'mbc-success-body' }, 'We’ll get back to you soon.'))
        : h('form', { className: 'mbc-form', onSubmit: submit, autoComplete: 'on' },
          p.ctxLabel ? h('div', { className: 'elp-plan-ctx' }, h('strong', null, p.ctxLabel)) : null,
          h('div', { className: 'mbc-form-row' },
            h('div', { className: 'mbc-field' },
              h('label', { className: 'mbc-label' }, 'First name *'),
              h('input', { className: 'mbc-input', type: 'text', name: 'firstName', required: true,
                placeholder: 'First name', value: form.firstName, onChange: field })),
            h('div', { className: 'mbc-field' },
              h('label', { className: 'mbc-label' }, 'Last name *'),
              h('input', { className: 'mbc-input', type: 'text', name: 'lastName', required: true,
                placeholder: 'Last name', value: form.lastName, onChange: field }))),
          h('div', { className: 'mbc-field' },
            h('label', { className: 'mbc-label' }, 'Work email *'),
            h('input', { className: 'mbc-input', type: 'email', name: 'email', required: true,
              placeholder: 'you@organisation.org', value: form.email, onChange: field })),
          h('div', { className: 'mbc-form-row' },
            h('div', { className: 'mbc-field' },
              h('label', { className: 'mbc-label' }, 'Organisation *'),
              h('input', { className: 'mbc-input', type: 'text', name: 'organisation', required: true,
                placeholder: 'Organisation name', value: form.organisation, onChange: field })),
            h('div', { className: 'mbc-field' },
              h('label', { className: 'mbc-label' }, 'Role / job title'),
              h('input', { className: 'mbc-input', type: 'text', name: 'role',
                placeholder: 'Your role', value: form.role, onChange: field }))),
          h('div', { className: 'mbc-form-row' },
            h('div', { className: 'mbc-field' },
              h('label', { className: 'mbc-label' }, 'Organisation type *'),
              h('select', { className: 'mbc-input mbc-select', name: 'organisationType', required: true,
                value: form.organisationType, onChange: field },
                h('option', { value: '' }, 'Select type...'),
                h('option', { value: 'ngo' }, 'NGO / INGO'),
                h('option', { value: 'donor' }, 'Foundation / donor'),
                h('option', { value: 'csr' }, 'CSR / sustainability'),
                h('option', { value: 'policy' }, 'Policy / government'),
                h('option', { value: 'research' }, 'Research / advisory'),
                h('option', { value: 'other' }, 'Other'))),
            h('div', { className: 'mbc-field' },
              h('label', { className: 'mbc-label' }, 'Team size'),
              h('select', { className: 'mbc-input mbc-select', name: 'teamSize',
                value: form.teamSize, onChange: field },
                h('option', { value: '' }, 'Select size...'),
                ['1-5', '6-20', '21-100', '101-500', '500+'].map(function (v) {
                  return h('option', { key: v, value: v }, v);
                })))),
          h('div', { className: 'mbc-field' },
            h('label', { className: 'mbc-label' }, 'What do you want Globally to help with?'),
            h('div', { className: 'mbc-checkbox-grid' }, OPTS.map(function (o) {
              return h('label', { className: 'mbc-checkbox', key: o.id },
                h('input', { type: 'checkbox', checked: form.useCases.indexOf(o.id) !== -1,
                  onChange: function () { toggle(o.id); } }), o.label);
            }))),
          h('div', { className: 'mbc-field' },
            h('label', { className: 'mbc-label' }, 'Message'),
            h('textarea', { className: 'mbc-input mbc-textarea', name: 'message', rows: 3,
              placeholder: 'Tell us what your team is trying to fund or monitor...',
              value: form.message, onChange: field })),
          err ? h('p', { className: 'mbc-error' }, err) : null,
          h('button', { className: 'mbc-submit-btn', type: 'submit', disabled: busy },
            busy ? 'Sending...' : 'Book a demo'),
          h('p', { className: 'mbc-form-note' },
            'No spam, and no card. We’ll only contact you about Globally.')));
  }

  function Closing(p) {
    return h('section', { className: 'gl-sec gl-sec--cream gl-closing' },
      h('div', { className: 'gl-in' },
        h(RequestAccess, { ctxLabel: p.ctxLabel }),
        h(Pricing, { open: p.pricingOpen, setOpen: p.setPricingOpen, selectedPlan: p.selectedPlan })));
  }

  // ══ PAGE ════════════════════════════════════════════════════════════════
  function Page(props) {
    var V = window.MiraViz;
    var intel = V.useFundingIntel();
    var articles = (props.articles || []).filter(function (a) {
      return a && (a.display_title || a.title);
    }).slice(0, 80);

    var sC = React.useState(hashQuery()); var ctx = sC[0], setCtx = sC[1];
    var sP = React.useState(false); var pricingOpen = sP[0], setPricingOpen = sP[1];

    React.useEffect(function () {
      function apply() {
        var q = hashQuery();
        setCtx(q);
        if (q.section === 'pricing') { setPricingOpen(true); scrollToWhenReady('pricing'); return; }
        if (q.plan || q.type) { scrollToWhenReady('early-access'); }
      }
      apply();
      window.addEventListener('hashchange', apply);
      return function () { window.removeEventListener('hashchange', apply); };
    }, []);

    var M = window.MIRA_PLANS;
    var plan = ctx.plan && M ? M.byId(ctx.plan) : null;
    var ctxLabel = plan
      ? (ctx.demo ? 'Requesting a demo of ' + plan.name : 'Requesting early access to ' + plan.name)
      : ctx.type === 'south-asia-access' ? 'Applying to the South Asia Access programme'
      : ctx.type === 'sponsored-access' ? 'Enquiring about sponsored access for partner organisations'
      : null;

    function openPricing() { setPricingOpen(true); setTimeout(function () { scrollTo('pricing'); }, 30); }

    return h('div', { className: 'gl-page', 'data-ui-version': 'globally-front-page-v3' },
      h(Hero, { intel: intel, articles: articles }),
      h(WhatWeDo, { intel: intel }),
      h(SeeItWork, { intel: intel, articles: articles }),
      h(WhyGlobally, { intel: intel, articles: articles }),
      h(FinalCTA, { openPricing: openPricing }),
      h(Closing, {
        ctxLabel: ctxLabel, pricingOpen: pricingOpen, setPricingOpen: setPricingOpen,
        selectedPlan: ctx.plan || null,
      }));
  }

  window.MiraLanding = {
    Page: Page,
    Hero: Hero, HeroDesk: HeroDesk, WhatWeDo: WhatWeDo, SeeItWork: SeeItWork,
    WhyGlobally: WhyGlobally, FinalCTA: FinalCTA, Pricing: Pricing,
    RequestAccess: RequestAccess, ROUTES: ROUTES,
  };
})();
