// kc-sections.jsx — Commercial Energy Knowledge Centre (the Q&A parent page). // Rendered at the question-answer CPT archive: /question-answer/. // // Data flow (v1.41.0): // KnowledgeCentreApp fetches, in parallel: // GET /qa/topics → topic grid (slug, name, desc, icon, count, url) // GET /qa → full question index (slug, question, preview, topic) for search // GET /qa/popular → 8 popular questions // Topic cards LINK to /question-answer/topic/{slug}/ (real archive pages — see // the qa-topic taxonomy). Search filters the index in place and links each // result to its individual /question-answer/{slug}/ page. // // Reuses shared primitives: Eyebrow, SectionHeader, Button, LockedNavBar, Footer. const KC_SHELL = { maxWidth: 1440, margin: '0 auto', padding: '0 48px' }; const KC_REST_BASE = (typeof window !== 'undefined' && window.GI_REST_BASE) || '/wp-json/claude-pages/v1/'; // --------------------------------------------------------------- // Breadcrumb — Home / Resources / Knowledge Centre (real URLs). // --------------------------------------------------------------- const KCBreadcrumb = ({ trail }) => ( ); // --------------------------------------------------------------- // Search field (hero global search + topic-page scoped filter). // --------------------------------------------------------------- const KCSearch = ({ value, onChange, placeholder, autoFocus }) => (
onChange(e.target.value)} placeholder={placeholder} aria-label={placeholder} style={{ width: '100%', boxSizing: 'border-box', font: '400 16px/24px var(--ff-sans)', color: 'var(--gi-deep-ink)', background: '#fff', border: '1px solid rgba(15,33,51,0.16)', borderRadius: 4, padding: '18px 20px 18px 54px', outline: 'none', transition: 'border-color 180ms ease, box-shadow 180ms ease' }} onFocus={(e) => { e.target.style.borderColor = 'var(--gi-sage-accent)'; e.target.style.boxShadow = '0 0 0 3px rgba(94,143,109,0.14)'; }} onBlur={(e) => { e.target.style.borderColor = 'rgba(15,33,51,0.16)'; e.target.style.boxShadow = 'none'; }} /> {value && ( )}
); // --------------------------------------------------------------- // Hero — cream. Tag · headline · description · search · stats. // --------------------------------------------------------------- const KCHero = ({ query, onQuery, total, topicCount }) => { const rounded = `${Math.floor((total || 0) / 10) * 10}+`; const stats = [ { value: rounded, label: 'Questions answered' }, { value: String(topicCount || 0), label: 'Energy topics' }, { value: 'C&I', label: 'Commercial & industrial focus' } ]; return (
); }; // --------------------------------------------------------------- // Topic card — links to its topic archive page. // --------------------------------------------------------------- const KCTopicCard = ({ topic, gtmLocation = 'qa-topic-link' }) => { const [hover, setHover] = React.useState(false); return ( setHover(true)} onMouseLeave={() => setHover(false)} style={{ textAlign: 'left', cursor: 'pointer', textDecoration: 'none', border: '1px solid rgba(15,33,51,0.12)', opacity: 1, /* override global a:hover { opacity: 0.6 } — card has its own hover affordance */ background: '#fff', borderRadius: 8, padding: '28px 26px 24px', display: 'flex', flexDirection: 'column', gap: 0, minHeight: 248, width: 'auto', boxShadow: hover ? '0 18px 40px -20px rgba(15,33,51,0.24)' : 'var(--shadow-1)', borderColor: hover ? 'rgba(94,143,109,0.55)' : 'rgba(15,33,51,0.12)', transform: hover ? 'translateY(-3px)' : 'none', transition: 'transform 240ms var(--ease-standard), box-shadow 240ms var(--ease-standard), border-color 240ms var(--ease-standard)' }}>

{topic.name}

{topic.desc}

{topic.count} Questions Browse
); }; // --------------------------------------------------------------- // Sectors grid — sits ABOVE the topics grid; mirrors its design. // Cards link to the sector archive (/sector/{slug}/). // --------------------------------------------------------------- const KCSectorGrid = ({ sectors }) => (
{sectors.map((s) => )}
); const KCTopicGrid = ({ topics }) => (
{topics.map((t) => )}
); // --------------------------------------------------------------- // Question row — list item (search results). Links to the Q&A page. // --------------------------------------------------------------- const KCQuestionRow = ({ q, showTopic }) => { const [hover, setHover] = React.useState(false); return ( setHover(true)} onMouseLeave={() => setHover(false)} data-gtm-event="cta_click" data-gtm-label={q.question} data-gtm-location="qa-search-result" data-gtm-stage="consideration" style={{ display: 'grid', gridTemplateColumns: '1fr auto', gap: 24, alignItems: 'center', padding: '26px 0', borderBottom: '1px solid rgba(15,33,51,0.12)', textDecoration: 'none', border: 'none', borderBottomStyle: 'solid', borderBottomWidth: 1, borderBottomColor: 'rgba(15,33,51,0.12)', opacity: 1 /* override global a:hover fade */ }}>
{showTopic && q.topicName && (
{q.topicName}
)}

{q.question}

{q.preview &&

{q.preview}

}
View answer
); }; // --------------------------------------------------------------- // Global search results — replaces grid/popular while typing. // --------------------------------------------------------------- const KCSearchResults = ({ query, index }) => { const q = query.trim().toLowerCase(); const tokens = q.split(/\s+/).filter(Boolean); const results = index.filter((item) => { const hay = (item.question + ' ' + (item.preview || '') + ' ' + (item.topicName || '')).toLowerCase(); return tokens.every((t) => hay.includes(t)); }); return (
Search results

{results.length} {results.length === 1 ? 'answer' : 'answers'} for “{query.trim()}”

{results.length > 0 ? results.map((item) => ) : (

We couldn’t find a match. Try a broader term — or speak with our team and we’ll answer your question directly.

)}
); }; // --------------------------------------------------------------- // Popular questions — compact card grid. // --------------------------------------------------------------- const KCPopularCard = ({ q }) => { const [hover, setHover] = React.useState(false); return ( setHover(true)} onMouseLeave={() => setHover(false)} style={{ display: 'flex', flexDirection: 'column', textDecoration: 'none', border: 'none', background: '#fff', borderRadius: 8, padding: '24px 24px 22px', opacity: 1, boxShadow: hover ? '0 16px 36px -20px rgba(15,33,51,0.22)' : 'var(--shadow-1)', outline: hover ? '1px solid rgba(94,143,109,0.55)' : '1px solid rgba(15,33,51,0.10)', transform: hover ? 'translateY(-3px)' : 'none', transition: 'transform 220ms var(--ease-standard), box-shadow 220ms var(--ease-standard), outline-color 220ms var(--ease-standard)' }}>
{q.topicName}

{q.question}

View answer
); }; const KCPopular = ({ popular }) => ( ); // --------------------------------------------------------------- // CTA — "Still have questions?" (soft-green brand surface). // --------------------------------------------------------------- const KCQuestionsCTA = () => (
Talk to a specialist

Still have questions?

Speak with our team about your facility, energy goals, and the opportunities available to you.

); // --------------------------------------------------------------- // App — fetches the three datasets and orchestrates hub vs search. // --------------------------------------------------------------- const KnowledgeCentreApp = () => { // Prerender/hydration seed: the build injects __GI_INITIAL_STATE__.kc so the // server markup (topic grid + popular) matches the client's first render. const SEED = (typeof window !== 'undefined' && window.__GI_INITIAL_STATE__ && window.__GI_INITIAL_STATE__.kc) || null; const [state, setState] = React.useState(SEED ? { loading: false, sectors: SEED.sectors || [], topics: SEED.topics || [], index: SEED.index || [], popular: SEED.popular || [], total: SEED.total || 0 } : { loading: true, sectors: [], topics: [], index: [], popular: [], total: 0 }); const [query, setQuery] = React.useState(''); React.useEffect(() => { if (SEED) return; // already populated from the prerender seed const base = KC_REST_BASE.replace(/\/?$/, '/'); const get = (path) => fetch(base + path, { headers: { Accept: 'application/json' } }).then((r) => r.ok ? r.json() : Promise.reject(r.status)); Promise.all([get('qa/sectors'), get('qa/topics'), get('qa'), get('qa/popular')]) .then(([sectors, topics, index, popular]) => setState({ loading: false, sectors: (sectors && sectors.sectors) || [], topics: (topics && topics.topics) || [], index: (index && index.items) || [], popular: (popular && popular.items) || [], total: (topics && topics.total) || ((index && index.total) || 0) })) .catch(() => setState((s) => ({ ...s, loading: false }))); }, []); // Keep lucide icons painted as content swaps in. React.useEffect(() => { const t = setInterval(() => window.lucide && window.lucide.createIcons({ attrs: { 'stroke-width': 1.5 } }), 200); return () => clearInterval(t); }, []); const searching = query.trim().length > 0; return (
{searching ? : ( {state.sectors.length > 0 && } {state.popular.length > 0 && } )}
); }; Object.assign(window, { KnowledgeCentreApp });