// ─── GRANTS ───────────────────────────────────────────────────────────────────
// Public-safety grant funding hub for the WRAP® Non-Lethal Response™ System.
// Surfaces (1) Lexipol's Grant Assistance Program (GAP) — a free service WRAP
// offers to agencies, (2) the three GrantFinder application forms mapped to WRAP
// product lines, and (3) a structured, easy-to-extend catalog of active + recently
// expired grant opportunities.
//
// ── HOW TO ADD / UPDATE A GRANT ───────────────────────────────────────────────
//   Add an object to the GRANTS array below. Required fields:
//     id, name, level ('federal' | 'state'), agency, dueLabel, dueSort (ISO date
//     used for sorting/expiry), fundingLabel, summary, productFits (array of
//     PRODUCTS ids), source ({ name, url }).
//   Optional: sublabel, awardLabel, note, logo (path under assets/grants/),
//     stateName (for state grants).
//   A grant automatically moves from ACTIVE to RECENTLY EXPIRED once dueSort is
//   in the past — no code change needed. Keep expired grants for ~60 days so
//   returning agencies can see the cycle, then delete them.
//
// ── CONTENT / BRAND GUARDRAILS (do not remove) ────────────────────────────────
//   • Grant facts (funding, deadlines, eligibility) are ATTRIBUTED to their
//     administering agency / source — never stated in WRAP's own voice. Each card
//     links to the authoritative source so agencies verify current terms there.
//   • Deadlines and dollar amounts change; the source link is the source of truth.
//   • "Best WRAP fits" describes where WRAP products MAY be eligible within a
//     program — it is guidance, not a guarantee of award. Framed accordingly.
//   • WRAP is a system (Policy + Training + Technology), not a weapon. Copy leads
//     with the agency's mission (funding the tools to keep everyone safe), not hype.

const { useState: useGRState, useEffect: useGREffect, useRef: useGRRef } = React;

// ─── APPLICATION FORMS (Lexipol GrantFinder GAP lead forms) ───────────────────
// Each maps to a WRAP product line. `formUrl` is loaded into an iframe on demand
// (click-to-select) with the parent URL appended, per GrantFinder's embed spec.
// The tsp.wyf.mybluehost.me origin must stay whitelisted in vercel.json frame-src.
const GAP_FORMS = [
    {
        id: 'body-cameras',
        label: 'Body Cameras',
        product: 'wrap-vision',
        productName: 'WRAP Vision™',
        blurb: 'Body-worn cameras & evidence management — WRAP Vision™.',
        formUrl: 'https://tsp.wyf.mybluehost.me/grantfinder/clientLeadForms/GAP_Lead_Forms/GAP_Lead_Form_Wrap_Body_Cameras_Police1.html',
    },
    {
        id: 'vr-training',
        label: 'VR Training',
        product: 'wrap-reality',
        productName: 'WRAP Reality™',
        blurb: 'Immersive virtual-reality de-escalation training — WRAP Reality™.',
        formUrl: 'https://tsp.wyf.mybluehost.me/grantfinder/clientLeadForms/GAP_Lead_Forms/GAP_Lead_Form_WRAP_VR_Training_Police1.html',
    },
    {
        id: 'de-escalation',
        label: 'De-Escalation',
        product: 'bolawrap-150',
        productName: 'BolaWrap® 150',
        blurb: 'Pain-free remote restraint & de-escalation — BolaWrap® 150.',
        formUrl: 'https://tsp.wyf.mybluehost.me/grantfinder/clientLeadForms/GAP_Lead_Forms/GAP_Lead_Form_Wrap_DeEscalation_Police1.html',
    },
];

// ─── GRANT CATALOG ────────────────────────────────────────────────────────────
// productFits values are PRODUCTS ids: 'bolawrap-150', 'wrap-reality',
// 'wrap-tactics', 'wrap-vision', 'dfr-x'. Used both to render the fit chips here
// and to power the reverse cross-link on each product page (see grantsForProduct).
const GRANTS = [
    // ── FEDERAL ─────────────────────────────────────────────────────────────────
    {
        id: 'cops-cpd-microgrants',
        name: 'COPS CPD Microgrants',
        sublabel: 'Community Policing Development — Microgrants Program (FY26)',
        level: 'federal',
        agency: 'U.S. DOJ — COPS Office',
        dueLabel: 'Aug. 17 & Aug. 24, 2026',
        dueSort: '2026-08-24',
        fundingLabel: '$6.7M total',
        awardLabel: 'Up to $200,000 per award',
        summary: 'Funds demonstration or pilot projects that advance community policing, officer and public safety, and crime reduction. Two-step submission (Grants.gov, then JustGrants); 24-month awards with no local match.',
        productFits: ['wrap-vision', 'wrap-reality', 'bolawrap-150', 'dfr-x'],
        fitNote: 'Cameras, LPRs, VR training, and BolaWrap® 150 fit best inside a broader crime-reduction or public-safety pilot.',
        source: { name: 'COPS Office', url: 'https://cops.usdoj.gov/cpdmicrogrants' },
    },
    {
        id: 'cops-safer-outcomes',
        name: 'COPS Safer Outcomes',
        level: 'federal',
        agency: 'U.S. DOJ — COPS Office',
        dueLabel: 'Aug. 27 & Sept. 3, 2026',
        dueSort: '2026-09-03',
        fundingLabel: '$13M total',
        awardLabel: '$350,000–$750,000 per award',
        summary: 'Supports comprehensive de-escalation and crisis-response training programs. Equipment-only projects are not eligible — training must anchor the request.',
        productFits: ['wrap-reality', 'bolawrap-150'],
        fitNote: 'Strongest fit: WRAP Reality™ VR training and BolaWrap® 150 integrated into a comprehensive de-escalation or crisis-response training program.',
        source: { name: 'COPS Office', url: 'https://cops.usdoj.gov/' },
    },
    {
        id: 'doj-model-cities',
        name: 'DOJ Model Cities Initiative',
        level: 'federal',
        agency: 'U.S. Department of Justice',
        dueLabel: 'Sept. 1, 2026',
        dueSort: '2026-09-01',
        fundingLabel: '≈ $300M',
        awardLabel: 'Only 2–4 awards',
        summary: 'A large-scale, citywide violent-crime reduction initiative with a small number of substantial awards.',
        productFits: ['wrap-vision', 'dfr-x', 'wrap-reality', 'bolawrap-150'],
        fitNote: 'Body cameras and LPRs are specifically supported; in-car cameras, VR training, and BolaWrap® 150 may also fit within a citywide violent-crime reduction strategy.',
        source: { name: 'DOJ / Office of Justice Programs', url: 'https://www.ojp.gov/funding' },
    },
    {
        id: 'houses-of-worship',
        name: 'Enhancing Security for Houses of Worship',
        level: 'federal',
        agency: 'U.S. Department of Justice',
        dueLabel: 'Sept. 2 & Sept. 9, 2026',
        dueSort: '2026-09-09',
        fundingLabel: 'Up to $200,000 / $150,000 per award',
        summary: 'Primarily supports fixed security cameras and law-enforcement overtime for protecting houses of worship.',
        productFits: ['wrap-vision'],
        fitNote: 'A limited opportunity for WRAP — the program mainly funds fixed security cameras and overtime rather than the full NLR System.',
        source: { name: 'Office of Justice Programs', url: 'https://www.ojp.gov/funding' },
    },
    {
        id: 'doj-biden-program',
        name: 'DOJ Byrne Discretionary (BIDEN) Program',
        level: 'federal',
        agency: 'U.S. Department of Justice',
        dueLabel: 'Aug. 19/21 & Sept. 23/24, 2026',
        dueSort: '2026-09-24',
        fundingLabel: '$3B',
        summary: 'A large discretionary program tied to federal enforcement priorities including immigration enforcement, trafficking investigations, and related DOJ operations.',
        productFits: ['wrap-vision', 'dfr-x', 'wrap-reality', 'bolawrap-150'],
        fitNote: 'Cameras, LPRs, VR training, and BolaWrap® 150 fit when directly tied to immigration enforcement, trafficking investigations, or related DOJ operations.',
        source: { name: 'Bureau of Justice Assistance', url: 'https://bja.ojp.gov/funding' },
    },
    {
        id: 'ctas-pa1',
        name: 'CTAS Purpose Area 1',
        sublabel: 'Coordinated Tribal Assistance Solicitation',
        level: 'federal',
        agency: 'U.S. DOJ — Office of Justice Programs',
        dueLabel: 'Oct. 15 & Oct. 22, 2026',
        dueSort: '2026-10-22',
        fundingLabel: '$29M for PA1',
        awardLabel: '$700,000–$900,000 per award',
        summary: 'Public-safety and community-policing funding for eligible Tribal law-enforcement projects under Purpose Area 1.',
        productFits: ['wrap-vision', 'dfr-x', 'wrap-reality', 'bolawrap-150'],
        fitNote: 'Cameras, LPRs, VR training, and BolaWrap® 150 all fit eligible Tribal law-enforcement projects.',
        source: { name: 'Office of Justice Programs', url: 'https://www.ojp.gov/ctas' },
    },

    // ── STATE ───────────────────────────────────────────────────────────────────
    {
        id: 'va-jag-training-equipment',
        name: 'Virginia JAG — Training & Equipment',
        level: 'state',
        stateName: 'Virginia',
        agency: 'VA Dept. of Criminal Justice Services',
        dueLabel: 'Aug. 11, 2026',
        dueSort: '2026-08-11',
        fundingLabel: '$762,000 total',
        awardLabel: 'Up to $33,333 per award',
        summary: 'State Byrne-JAG pass-through for law-enforcement training and equipment purchases.',
        productFits: ['wrap-vision', 'dfr-x', 'wrap-reality', 'bolawrap-150', 'wrap-tactics'],
        fitNote: 'Broad fit across the NLR System — cameras, LPRs, VR training, and BolaWrap® 150.',
        source: { name: 'Virginia DCJS', url: 'https://www.dcjs.virginia.gov/law-enforcement/grants/byrne-justice-assistance-grant-program-jag' },
    },
    {
        id: 'nd-jag',
        name: 'North Dakota JAG',
        level: 'state',
        stateName: 'North Dakota',
        agency: 'ND — Byrne Justice Assistance Grant',
        dueLabel: 'Aug. 12, 2026',
        dueSort: '2026-08-12',
        fundingLabel: 'Not listed',
        summary: 'State Byrne-JAG funding for law-enforcement operations and training.',
        productFits: ['wrap-vision', 'dfr-x', 'wrap-reality', 'bolawrap-150'],
        fitNote: 'Cameras, LPRs, VR training, and BolaWrap® 150 fit when tied to law-enforcement operations or training.',
        source: { name: 'Bureau of Justice Assistance', url: 'https://bja.ojp.gov/program/jag/overview' },
    },
    {
        id: 'ky-jag',
        name: 'Kentucky JAG',
        level: 'state',
        stateName: 'Kentucky',
        agency: 'KY — Byrne Justice Assistance Grant',
        dueLabel: 'Aug. 20, 2026',
        dueSort: '2026-08-20',
        fundingLabel: '$2.1M total',
        awardLabel: '$10,000–$350,000 per award',
        summary: 'Prioritizes technology, crime reduction, and officer-safety projects under the state Byrne-JAG program.',
        productFits: ['wrap-reality', 'bolawrap-150', 'wrap-vision'],
        fitNote: 'VR training, BolaWrap® 150, cameras, and LPRs fit under technology, crime-reduction, and officer-safety priorities.',
        source: { name: 'Bureau of Justice Assistance', url: 'https://bja.ojp.gov/program/jag/overview' },
    },
    {
        id: 'ak-jag',
        name: 'Alaska JAG',
        level: 'state',
        stateName: 'Alaska',
        agency: 'AK — Byrne Justice Assistance Grant',
        dueLabel: 'Aug. 21, 2026',
        dueSort: '2026-08-21',
        fundingLabel: '≈ $428,193',
        summary: 'State Byrne-JAG funding supporting drug-task-force and enforcement operations.',
        productFits: ['wrap-vision', 'dfr-x', 'wrap-reality', 'bolawrap-150'],
        fitNote: 'Cameras, LPRs, VR training, and BolaWrap® 150 fit when connected to drug-task-force or enforcement operations.',
        source: { name: 'Bureau of Justice Assistance', url: 'https://bja.ojp.gov/program/jag/overview' },
    },
    {
        id: 'ny-letech',
        name: 'New York LETech',
        sublabel: 'Law Enforcement Technology Program',
        level: 'state',
        stateName: 'New York',
        agency: 'NY Div. of Criminal Justice Services',
        dueLabel: 'Sept. 2, 2026',
        dueSort: '2026-09-02',
        fundingLabel: 'Up to $75M',
        awardLabel: '≈ 500 awards',
        summary: 'A large state technology program well-suited to body cameras, in-car cameras, and LPRs, with room for supporting implementation.',
        productFits: ['wrap-vision', 'wrap-reality', 'bolawrap-150'],
        fitNote: 'Excellent for body cameras, in-car cameras, and LPRs; VR training may support implementation and BolaWrap® 150 may be considered under "Other Equipment."',
        source: { name: 'NY DCJS', url: 'https://www.criminaljustice.ny.gov/' },
    },
];

// ─── HELPERS ──────────────────────────────────────────────────────────────────
// A grant is "active" until the end of its due date. Compared against today so
// the split maintains itself with no manual edits.
function isActive(grant) {
    try {
        const due = new Date(grant.dueSort + 'T23:59:59');
        return due.getTime() >= Date.now();
    } catch (e) { return true; }
}

// Reverse lookup for product-page cross-links: which grants list this product id?
// Active grants first (by soonest deadline), then recently expired.
function grantsForProduct(productId) {
    const matches = GRANTS.filter(g => (g.productFits || []).includes(productId));
    const active = matches.filter(isActive).sort((a, b) => a.dueSort.localeCompare(b.dueSort));
    const expired = matches.filter(g => !isActive(g)).sort((a, b) => b.dueSort.localeCompare(a.dueSort));
    return { active, expired };
}

// Single source of truth for "what's open" / "what's closed", sorted. Every
// surface (Grants page, product pages, the home-page carousel) reads these
// instead of re-filtering GRANTS, so the data AND the open/closed logic live in
// exactly one place.
function openGrants() {
    return GRANTS.filter(isActive).sort((a, b) => a.dueSort.localeCompare(b.dueSort));
}
function expiredGrants() {
    return GRANTS.filter(g => !isActive(g)).sort((a, b) => b.dueSort.localeCompare(a.dueSort));
}

const PRODUCT_LABEL = {
    'bolawrap-150': 'BolaWrap® 150',
    'wrap-reality': 'WRAP Reality™',
    'wrap-tactics': 'WRAP Tactics™',
    'wrap-vision': 'WRAP Vision™',
    'dfr-x': 'C-UAS / DFR-X',
};

// ─── FORM PANEL (click-to-load iframe) ────────────────────────────────────────
// Renders the selected GrantFinder lead form. The iframe src is set only after a
// user selects a form (deferred load), with parent_url appended per the embed spec.
function GapFormPanel({ form, isMobile }) {
    const iframeRef = useGRRef(null);
    useGREffect(() => {
        if (form && iframeRef.current) {
            iframeRef.current.src = form.formUrl + '?parent_url=' + encodeURIComponent(window.location.href);
        }
    }, [form && form.id]);

    if (!form) return null;
    return (
        <div style={{
            background: 'var(--card-bg)', border: '1px solid var(--border)',
            borderRadius: 10, overflow: 'hidden', marginTop: 24,
        }}>
            <div style={{ padding: isMobile ? '18px 20px' : '20px 28px', borderBottom: '1px solid var(--border)' }}>
                <div style={{ fontFamily: 'Barlow Condensed', fontWeight: 700, fontSize: 22, textTransform: 'uppercase', color: 'var(--text)' }}>
                    {form.label} — Grant Assistance Request
                </div>
                <div style={{ fontSize: 13, color: 'var(--text-sub)', marginTop: 4 }}>{form.blurb}</div>
            </div>
            <iframe
                ref={iframeRef}
                title={form.label + ' Grant Assistance Form'}
                width="100%"
                height="1200"
                frameBorder="0"
                style={{ border: 'none', display: 'block', background: '#fff' }}
            />
        </div>
    );
}

// ─── GRANT CARD ───────────────────────────────────────────────────────────────
function GrantCard({ grant, navigate, isMobile, expired, onApply }) {
    const [hover, setHover] = useGRState(false);
    return (
        <div
            onMouseEnter={() => setHover(true)}
            onMouseLeave={() => setHover(false)}
            style={{
                background: 'var(--card-bg)', borderRadius: 10,
                border: `1px solid ${hover ? 'rgba(255,181,5,0.28)' : 'var(--border)'}`,
                padding: isMobile ? '22px 20px' : '26px 28px',
                transition: 'border-color 0.2s, transform 0.2s',
                transform: hover ? 'translateY(-2px)' : 'none',
                opacity: expired ? 0.72 : 1,
                display: 'flex', flexDirection: 'column',
            }}
        >
            {/* Meta row */}
            <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12, flexWrap: 'wrap' }}>
        <span style={{
            fontSize: 10, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase',
            color: grant.level === 'federal' ? 'var(--accent)' : 'var(--text-sub)',
            border: `1px solid ${grant.level === 'federal' ? 'rgba(255,181,5,0.4)' : 'var(--border-strong)'}`,
            borderRadius: 3, padding: '3px 8px',
        }}>{grant.level === 'federal' ? 'Federal' : (grant.stateName || 'State')}</span>
                <span style={{ fontSize: 12, color: 'var(--text-muted)', fontFamily: 'DM Sans' }}>{grant.agency}</span>
            </div>

            {/* Title */}
            <div style={{ fontFamily: 'Barlow Condensed', fontWeight: 700, fontSize: 24, lineHeight: 1.05, textTransform: 'uppercase', color: 'var(--text)', marginBottom: grant.sublabel ? 4 : 12 }}>
                {grant.name}
            </div>
            {grant.sublabel && <div style={{ fontSize: 12.5, color: 'var(--text-sub)', marginBottom: 12, fontStyle: 'italic' }}>{grant.sublabel}</div>}

            {/* Key facts */}
            <div style={{ display: 'flex', gap: 22, flexWrap: 'wrap', marginBottom: 14 }}>
                <div>
                    <div style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--text-muted)', marginBottom: 3 }}>{expired ? 'Closed' : 'Due'}</div>
                    <div style={{ fontSize: 14, fontWeight: 600, color: expired ? 'var(--text-sub)' : 'var(--accent)', fontFamily: 'DM Sans' }}>{grant.dueLabel}</div>
                </div>
                <div>
                    <div style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--text-muted)', marginBottom: 3 }}>Funding</div>
                    <div style={{ fontSize: 14, fontWeight: 600, color: 'var(--text)', fontFamily: 'DM Sans' }}>{grant.fundingLabel}</div>
                </div>
                {grant.awardLabel && (
                    <div>
                        <div style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--text-muted)', marginBottom: 3 }}>Per Award</div>
                        <div style={{ fontSize: 14, fontWeight: 600, color: 'var(--text)', fontFamily: 'DM Sans' }}>{grant.awardLabel}</div>
                    </div>
                )}
            </div>

            {/* Summary */}
            <p style={{ fontSize: 14, lineHeight: 1.6, color: 'var(--text-sub)', marginBottom: 16 }}>{grant.summary}</p>

            {/* Best WRAP fits */}
            <div style={{ marginBottom: 16 }}>
                <div style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: 'var(--text-muted)', marginBottom: 8 }}>Best WRAP® Fits</div>
                <div style={{ display: 'flex', gap: 7, flexWrap: 'wrap' }}>
                    {(grant.productFits || []).map(pid => (
                        <button
                            key={pid}
                            onClick={() => navigate('product', pid)}
                            style={{
                                background: 'var(--accent-dim)', color: 'var(--accent)', border: '1px solid rgba(255,181,5,0.25)',
                                borderRadius: 4, padding: '5px 10px', fontSize: 12, fontWeight: 600, fontFamily: 'DM Sans',
                                cursor: 'pointer', transition: 'background 0.2s',
                            }}
                            onMouseEnter={e => e.currentTarget.style.background = 'rgba(255,181,5,0.2)'}
                            onMouseLeave={e => e.currentTarget.style.background = 'var(--accent-dim)'}
                        >{PRODUCT_LABEL[pid] || pid}</button>
                    ))}
                </div>
                {grant.fitNote && <p style={{ fontSize: 12.5, lineHeight: 1.55, color: 'var(--text-muted)', marginTop: 10 }}>{grant.fitNote}</p>}
            </div>

            {/* Footer: attribution + apply */}
            <div style={{ marginTop: 'auto', paddingTop: 14, borderTop: '1px solid var(--border)', display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
                <a href={grant.source.url} target="_blank" rel="noopener noreferrer" style={{ fontSize: 12, color: 'var(--text-muted)', textDecoration: 'none', fontFamily: 'DM Sans' }}
                   onMouseEnter={e => e.currentTarget.style.color = 'var(--text-sub)'}
                   onMouseLeave={e => e.currentTarget.style.color = 'var(--text-muted)'}>
                    Details & eligibility: {grant.source.name} ↗
                </a>
                {!expired && (
                    <button onClick={() => onApply && onApply()} style={{
                        background: 'none', border: 'none', cursor: 'pointer', color: 'var(--accent)',
                        fontSize: 13, fontWeight: 600, fontFamily: 'DM Sans', padding: 0,
                    }}>Get GAP help →</button>
                )}
            </div>
        </div>
    );
}

// ─── PAGE ─────────────────────────────────────────────────────────────────────
function GrantsPage({ navigate, initialQuery }) {
    useGREffect(() => { document.title = 'Grant Funding — WRAP® Non-Lethal Response™'; }, []);
    const isMobile = useMobile();
    const [selectedForm, setSelectedForm] = useGRState(GAP_FORMS[0]);
    const [levelFilter, setLevelFilter] = useGRState('all'); // all | federal | state
    const [query, setQuery] = useGRState(initialQuery || ''); // seeded from deep-link (?q=)
    const [page, setPage] = useGRState(1);
    const PER_PAGE = 6;

    // Deep-link from a grant card (home carousel / product page): if we arrived
    // with a search term, scroll to the Open Grants section so the pre-filtered
    // results are in view. Runs once after mount; App remounts this component
    // (via key) when the query changes, so this re-fires per deep-link.
    useGREffect(() => {
        if (!initialQuery) return;
        const t = setTimeout(() => {
            const el = document.getElementById('open-grants');
            if (el) el.scrollIntoView({ behavior: 'smooth' });
        }, 120); // let the page paint before scrolling
        return () => clearTimeout(t);
    }, []);

    const active = openGrants();
    const expired = expiredGrants();

    const applyLevel = list => levelFilter === 'all' ? list : list.filter(g => g.level === levelFilter);

    // Free-text search across the fields an agency would actually search by:
    // name, sublabel, agency, summary, state, and the human labels of matching
    // products (so "camera" surfaces grants that fit WRAP Vision™, etc.).
    const matchesQuery = g => {
        const q = query.trim().toLowerCase();
        if (!q) return true;
        const fitLabels = (g.productFits || []).map(id => (PRODUCT_LABEL[id] || id)).join(' ');
        const hay = [g.name, g.sublabel, g.agency, g.summary, g.fitNote, g.stateName, g.level, fitLabels]
            .filter(Boolean).join(' ').toLowerCase();
        return q.split(/\s+/).every(term => hay.includes(term));
    };

    const activeFiltered = applyLevel(active).filter(matchesQuery);

    // Reset to page 1 whenever the result set changes (search or level filter),
    // so a user isn't stranded on an out-of-range page.
    useGREffect(() => { setPage(1); }, [query, levelFilter]);

    const totalPages = Math.max(1, Math.ceil(activeFiltered.length / PER_PAGE));
    const safePage = Math.min(page, totalPages);
    const pageStart = (safePage - 1) * PER_PAGE;
    const activeShown = activeFiltered.slice(pageStart, pageStart + PER_PAGE);

    // When paging, bring the section header back into view so the new cards
    // aren't below the fold.
    const changePage = p => {
        setPage(p);
        const el = document.getElementById('open-grants');
        if (el) el.scrollIntoView({ behavior: 'smooth' });
    };

    // Every "get help / apply" CTA on this page routes here — the Start a Grant
    // Request section (the GrantFinder forms), which now sits below Open Grants.
    const goToForms = () => { const el = document.getElementById('gap-forms'); if (el) el.scrollIntoView({ behavior: 'smooth' }); };

    return (
        <main style={{ paddingTop: 72 }}>
            {/* ── HERO: Grant Assistance Program (GAP) ── */}
            <section style={{ position: 'relative', overflow: 'hidden', background: 'var(--bg)', padding: isMobile ? '48px 20px 56px' : '80px 48px 72px', borderBottom: '1px solid var(--border)' }}>
                {/* Background image (BolaWrap® 150 deployment) + navy overlay.
            Overlay is heavier on the left where the copy sits, so the headline
            and body stay legible while the imagery reads on the right. */}
                <div aria-hidden="true" style={{
                    position: 'absolute', inset: 0, zIndex: 0,
                    backgroundImage: 'url("assets/grants/grants-hero-bg.jpg")',
                    backgroundSize: 'cover',
                    backgroundPosition: isMobile ? 'center' : 'center right',
                }} />
                <div aria-hidden="true" style={{
                    position: 'absolute', inset: 0, zIndex: 1,
                    background: isMobile
                        ? 'linear-gradient(to bottom, rgba(6,12,28,0.86) 0%, rgba(6,12,28,0.82) 60%, rgba(6,12,28,0.9) 100%)'
                        : 'linear-gradient(to right, rgba(6,12,28,0.96) 0%, rgba(6,12,28,0.9) 45%, rgba(6,12,28,0.62) 75%, rgba(6,12,28,0.5) 100%)',
                }} />
                <div style={{ position: 'relative', zIndex: 2, maxWidth: 1200, margin: '0 auto' }}>
                    <div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '1.15fr 0.85fr', gap: isMobile ? 40 : 72, alignItems: 'center' }}>
                        <div>
                            <div style={{ fontFamily: 'DM Sans', fontSize: 12, fontWeight: 600, letterSpacing: '0.14em', color: 'var(--accent)', textTransform: 'uppercase', marginBottom: 18 }}>Funding Your Mission</div>
                            <h1 style={{ fontFamily: 'Barlow Condensed', fontWeight: 900, fontSize: 'clamp(44px, 7vw, 88px)', textTransform: 'uppercase', color: 'var(--text)', lineHeight: 0.92, marginBottom: 22 }}>
                                Grant Assistance<br />for Safer Communities
                            </h1>
                            <p style={{ fontSize: 17, lineHeight: 1.7, color: 'var(--text-sub)', marginBottom: 28, maxWidth: 560 }}>
                                Nearly $1 trillion in federal, state, and local grants is available each year for public-safety agencies. Through the Lexipol Grant Assistance Program (GAP), WRAP® connects your agency to real-time grant search, application support, and expert narrative feedback — <strong style={{ color: 'var(--text)' }}>at no cost to you</strong> — so the tools, training, and policy of the Non-Lethal Response™ System are within reach.
                            </p>
                            {/* Powered by — partner logos, no background, subtly on navy.
                  Both marks are white; GrantFinder is set taller than Lexipol so
                  the two wordmarks read at the same visual size (Lexipol's caps
                  fill ~76% of its art, GrantFinder's ~100%, so ~1.3x evens them). */}
                            <div style={{ marginBottom: 28 }}>
                                <div style={{ fontFamily: 'DM Sans', fontSize: 10, fontWeight: 700, letterSpacing: '0.16em', textTransform: 'uppercase', color: 'var(--text-muted)', marginBottom: 12 }}>Powered by</div>
                                <div style={{ display: 'inline-flex', alignItems: 'center', gap: 20, opacity: 0.9 }}>
                                    <img src="assets/grants/lexipol-logo-white.png" alt="Lexipol" style={{ height: 18, width: 'auto', display: 'block' }} />
                                    <span aria-hidden="true" style={{ width: 1, height: 20, background: 'var(--border-strong)' }} />
                                    <img src="assets/grants/grantfinder-logo-white.png" alt="GrantFinder" style={{ height: 24, width: 'auto', display: 'block' }} />
                                </div>
                            </div>
                            <div style={{ display: 'flex', gap: 14, flexWrap: 'wrap' }}>
                                <button onClick={goToForms} style={{
                                    background: 'var(--accent)', color: '#000', border: 'none', cursor: 'pointer',
                                    padding: '14px 30px', borderRadius: 4, fontFamily: 'DM Sans', fontWeight: 700, fontSize: 14,
                                }}>Start a Grant Request</button>
                                <button onClick={() => { const el = document.getElementById('open-grants'); if (el) el.scrollIntoView({ behavior: 'smooth' }); }} style={{
                                    background: 'transparent', color: 'var(--text)', border: '1px solid var(--border-strong)', cursor: 'pointer',
                                    padding: '14px 30px', borderRadius: 4, fontFamily: 'DM Sans', fontWeight: 600, fontSize: 14,
                                }}>View Open Grants</button>
                            </div>
                        </div>

                        {/* What GAP includes */}
                        <div style={{ background: 'var(--card-bg)', border: '1px solid var(--border)', borderRadius: 12, padding: isMobile ? '26px 24px' : '34px 32px' }}>
                            <div style={{ fontFamily: 'Barlow Condensed', fontWeight: 700, fontSize: 22, textTransform: 'uppercase', color: 'var(--text)', marginBottom: 6 }}>What GAP Includes</div>
                            <div style={{ fontSize: 12.5, color: 'var(--accent)', fontWeight: 600, marginBottom: 20 }}>Free for eligible agencies</div>
                            {[
                                ['GrantFinder access', 'A real-time grants search engine to find opportunities that fit your agency.'],
                                ['Application support', 'Hands-on help completing applications, with narrative feedback from grant experts.'],
                                ['Strategic consulting', 'Guidance throughout the process, from grant-readiness through submission.'],
                            ].map(([t, d], i) => (
                                <div key={i} style={{ display: 'flex', gap: 14, alignItems: 'flex-start', padding: '12px 0', borderTop: i === 0 ? 'none' : '1px solid var(--border)' }}>
                                    <div style={{ width: 20, height: 20, borderRadius: '50%', background: 'var(--accent)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, marginTop: 2 }}>
                                        <svg width="10" height="8" viewBox="0 0 10 8"><path d="M1 4l2.5 2.5 5.5-5.5" stroke="#000" strokeWidth="1.6" fill="none" strokeLinecap="round" strokeLinejoin="round"/></svg>
                                    </div>
                                    <div>
                                        <div style={{ fontSize: 15, fontWeight: 600, color: 'var(--text)', marginBottom: 3 }}>{t}</div>
                                        <div style={{ fontSize: 13, color: 'var(--text-sub)', lineHeight: 1.55 }}>{d}</div>
                                    </div>
                                </div>
                            ))}
                        </div>
                    </div>
                </div>
            </section>

            {/* ── OPEN GRANT OPPORTUNITIES ── */}
            <section id="open-grants" style={{ background: 'var(--bg)', padding: isMobile ? '56px 20px' : '88px 48px' }}>
                <div style={{ maxWidth: 1200, margin: '0 auto' }}>
                    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end', gap: 20, flexWrap: 'wrap', marginBottom: 12 }}>
                        <div>
                            <div style={{ fontFamily: 'DM Sans', fontSize: 12, fontWeight: 600, letterSpacing: '0.14em', color: 'var(--accent)', textTransform: 'uppercase', marginBottom: 14 }}>Open Opportunities</div>
                            <h2 style={{ fontFamily: 'Barlow Condensed', fontWeight: 900, fontSize: isMobile ? 40 : 56, textTransform: 'uppercase', color: 'var(--text)', lineHeight: 1 }}>Open Grants</h2>
                        </div>
                        {/* Level filter */}
                        <div style={{ display: 'flex', gap: 8 }}>
                            {[['all', 'All'], ['federal', 'Federal'], ['state', 'State']].map(([k, lbl]) => (
                                <button key={k} onClick={() => setLevelFilter(k)} style={{
                                    background: levelFilter === k ? 'var(--accent)' : 'transparent',
                                    color: levelFilter === k ? '#000' : 'var(--text-sub)',
                                    border: '1px solid ' + (levelFilter === k ? 'transparent' : 'var(--border-strong)'),
                                    padding: '8px 20px', borderRadius: 20, fontFamily: 'DM Sans',
                                    fontWeight: levelFilter === k ? 700 : 500, fontSize: 13, cursor: 'pointer', transition: 'all 0.2s',
                                }}>{lbl}</button>
                            ))}
                        </div>
                    </div>
                    <p style={{ fontSize: 14, color: 'var(--text-muted)', maxWidth: 640, lineHeight: 1.6, marginBottom: 24 }}>
                        Deadlines and amounts are set by each administering agency and change often — always confirm current terms at the source link on each grant. "Best WRAP® fits" indicates where our solutions may be eligible within a program; it is guidance, not a guarantee of award.
                    </p>

                    {/* Search */}
                    <div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap', marginBottom: 20 }}>
                        <div style={{ position: 'relative', flex: isMobile ? '1 1 100%' : '0 1 420px' }}>
              <span style={{ position: 'absolute', left: 14, top: '50%', transform: 'translateY(-50%)', color: 'var(--text-muted)', pointerEvents: 'none', display: 'flex' }}>
                <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="7" cy="7" r="5" stroke="currentColor" strokeWidth="1.5"/><path d="M11 11l3 3" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/></svg>
              </span>
                            <input
                                type="text"
                                value={query}
                                onChange={e => setQuery(e.target.value)}
                                placeholder="Search grants by name, agency, state, or product…"
                                aria-label="Search open grants"
                                style={{
                                    width: '100%', background: 'var(--bg-2)', border: '1px solid var(--border-strong)',
                                    borderRadius: 8, padding: '11px 38px 11px 40px', color: 'var(--text)',
                                    fontFamily: 'DM Sans', fontSize: 14, outline: 'none', boxSizing: 'border-box',
                                }}
                            />
                            {query && (
                                <button onClick={() => setQuery('')} aria-label="Clear search" style={{
                                    position: 'absolute', right: 10, top: '50%', transform: 'translateY(-50%)',
                                    background: 'none', border: 'none', cursor: 'pointer', color: 'var(--text-muted)',
                                    fontSize: 18, lineHeight: 1, padding: '2px 4px',
                                }}>×</button>
                            )}
                        </div>
                        <div style={{ fontSize: 13, color: 'var(--text-muted)', fontFamily: 'DM Sans' }}>
                            {activeFiltered.length} {activeFiltered.length === 1 ? 'grant' : 'grants'}
                            {query || levelFilter !== 'all' ? ' found' : ''}
                        </div>
                    </div>

                    <div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : 'repeat(2, 1fr)', gap: 20 }}>
                        {activeShown.map(g => <GrantCard key={g.id} grant={g} navigate={navigate} isMobile={isMobile} expired={false} onApply={goToForms} />)}
                    </div>
                    {activeFiltered.length === 0 && (
                        <p style={{ fontSize: 15, color: 'var(--text-sub)', padding: '20px 0' }}>
                            No open grants match{query ? ` "${query.trim()}"` : ''}{levelFilter !== 'all' ? (query ? ' in ' : ' for ') + levelFilter + ' programs' : ''}. Try a different term or <button onClick={() => { setQuery(''); setLevelFilter('all'); }} style={{ background: 'none', border: 'none', padding: 0, cursor: 'pointer', color: 'var(--accent)', fontSize: 15, fontWeight: 600, fontFamily: 'DM Sans' }}>clear filters</button>.
                        </p>
                    )}

                    {/* Pagination */}
                    {totalPages > 1 && (
                        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6, marginTop: 40, flexWrap: 'wrap' }}>
                            <button
                                onClick={() => changePage(safePage - 1)}
                                disabled={safePage === 1}
                                aria-label="Previous page"
                                style={{
                                    background: 'transparent', border: '1px solid var(--border-strong)',
                                    color: safePage === 1 ? 'var(--text-muted)' : 'var(--text)',
                                    cursor: safePage === 1 ? 'not-allowed' : 'pointer', opacity: safePage === 1 ? 0.5 : 1,
                                    minWidth: 38, height: 38, borderRadius: 6, fontSize: 18, lineHeight: 1,
                                    display: 'flex', alignItems: 'center', justifyContent: 'center', fontFamily: 'DM Sans',
                                }}
                            >‹</button>
                            {Array.from({ length: totalPages }, (_, i) => i + 1).map(n => (
                                <button key={n} onClick={() => changePage(n)} aria-label={`Page ${n}`} aria-current={n === safePage ? 'page' : undefined} style={{
                                    background: n === safePage ? 'var(--accent)' : 'transparent',
                                    color: n === safePage ? '#000' : 'var(--text-sub)',
                                    border: '1px solid ' + (n === safePage ? 'transparent' : 'var(--border-strong)'),
                                    cursor: 'pointer', minWidth: 38, height: 38, borderRadius: 6,
                                    fontFamily: 'DM Sans', fontWeight: n === safePage ? 700 : 500, fontSize: 14,
                                    transition: 'all 0.2s',
                                }}>{n}</button>
                            ))}
                            <button
                                onClick={() => changePage(safePage + 1)}
                                disabled={safePage === totalPages}
                                aria-label="Next page"
                                style={{
                                    background: 'transparent', border: '1px solid var(--border-strong)',
                                    color: safePage === totalPages ? 'var(--text-muted)' : 'var(--text)',
                                    cursor: safePage === totalPages ? 'not-allowed' : 'pointer', opacity: safePage === totalPages ? 0.5 : 1,
                                    minWidth: 38, height: 38, borderRadius: 6, fontSize: 18, lineHeight: 1,
                                    display: 'flex', alignItems: 'center', justifyContent: 'center', fontFamily: 'DM Sans',
                                }}
                            >›</button>
                        </div>
                    )}
                </div>
            </section>

            {/* ── RECENTLY EXPIRED ── */}
            {expired.length > 0 && (
                <section style={{ background: 'var(--bg-2)', padding: isMobile ? '48px 20px 64px' : '64px 48px 88px', borderTop: '1px solid var(--border)' }}>
                    <div style={{ maxWidth: 1200, margin: '0 auto' }}>
                        <div style={{ fontFamily: 'DM Sans', fontSize: 12, fontWeight: 600, letterSpacing: '0.14em', color: 'var(--text-muted)', textTransform: 'uppercase', marginBottom: 14 }}>For Reference</div>
                        <h2 style={{ fontFamily: 'Barlow Condensed', fontWeight: 800, fontSize: isMobile ? 32 : 42, textTransform: 'uppercase', color: 'var(--text-sub)', lineHeight: 1, marginBottom: 12 }}>Recently Closed</h2>
                        <p style={{ fontSize: 14, color: 'var(--text-muted)', maxWidth: 640, lineHeight: 1.6, marginBottom: 32 }}>
                            These cycles have passed but typically reopen — a good preview of what to prepare for next round. Ask our grant team to be notified when they return.
                        </p>
                        <div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : 'repeat(2, 1fr)', gap: 20 }}>
                            {applyLevel(expired).map(g => <GrantCard key={g.id} grant={g} navigate={navigate} isMobile={isMobile} expired={true} />)}
                        </div>
                    </div>
                </section>
            )}

            {/* ── START A GRANT REQUEST (application forms) ── */}
            <section id="gap-forms" style={{ background: 'var(--bg-2)', padding: isMobile ? '56px 20px' : '88px 48px', borderTop: '1px solid var(--border)' }}>
                <div style={{ maxWidth: 1000, margin: '0 auto' }}>
                    <div style={{ fontFamily: 'DM Sans', fontSize: 12, fontWeight: 600, letterSpacing: '0.14em', color: 'var(--accent)', textTransform: 'uppercase', marginBottom: 14 }}>Request Assistance</div>
                    <h2 style={{ fontFamily: 'Barlow Condensed', fontWeight: 900, fontSize: isMobile ? 40 : 52, textTransform: 'uppercase', color: 'var(--text)', lineHeight: 1, marginBottom: 16 }}>Start a Grant Request</h2>
                    <p style={{ fontSize: 16, lineHeight: 1.7, color: 'var(--text-sub)', maxWidth: 620, marginBottom: 32 }}>
                        Choose the WRAP® solution you're seeking funding for. Our grant experts will follow up to help you find matching opportunities and build a competitive application.
                    </p>

                    {/* Selector */}
                    <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap', marginBottom: 4 }}>
                        {GAP_FORMS.map(f => {
                            const on = selectedForm && selectedForm.id === f.id;
                            return (
                                <button key={f.id} onClick={() => setSelectedForm(f)} style={{
                                    background: on ? 'var(--accent)' : 'transparent',
                                    color: on ? '#000' : 'var(--text)',
                                    border: '1px solid ' + (on ? 'transparent' : 'var(--border-strong)'),
                                    borderRadius: 6, padding: isMobile ? '12px 16px' : '14px 22px',
                                    fontFamily: 'DM Sans', fontWeight: 700, fontSize: 14, cursor: 'pointer',
                                    transition: 'all 0.2s', textAlign: 'left', flex: isMobile ? '1 1 100%' : '0 1 auto',
                                }}>
                                    {f.label}
                                    <span style={{ display: 'block', fontWeight: 500, fontSize: 12, color: on ? 'rgba(0,0,0,0.65)' : 'var(--text-muted)', marginTop: 2 }}>{f.productName}</span>
                                </button>
                            );
                        })}
                    </div>

                    <GapFormPanel form={selectedForm} isMobile={isMobile} />
                </div>
            </section>

            {/* ── CLOSING CTA ── */}
            <section style={{ background: 'var(--bg)', padding: isMobile ? '56px 20px' : '88px 48px', borderTop: '1px solid var(--border)' }}>
                <div style={{ maxWidth: 800, margin: '0 auto', textAlign: 'center' }}>
                    <h2 style={{ fontFamily: 'Barlow Condensed', fontWeight: 900, fontSize: isMobile ? 36 : 48, textTransform: 'uppercase', color: 'var(--text)', lineHeight: 1, marginBottom: 18 }}>
                        Not sure where to start?
                    </h2>
                    <p style={{ fontSize: 16, lineHeight: 1.7, color: 'var(--text-sub)', marginBottom: 28 }}>
                        Our grant experts will help you match the right opportunity to your agency's goals and build a competitive application — free of charge.
                    </p>
                    <button onClick={goToForms} style={{
                        background: 'var(--accent)', color: '#000', border: 'none', cursor: 'pointer',
                        padding: '15px 34px', borderRadius: 4, fontFamily: 'DM Sans', fontWeight: 700, fontSize: 15,
                    }}>Start a Grant Request</button>
                </div>
            </section>

            <Footer navigate={navigate} />
        </main>
    );
}

Object.assign(window, { GrantsPage, GRANTS, grantsForProduct, openGrants, expiredGrants, PRODUCT_LABEL: PRODUCT_LABEL });