function renderGuidedTour() {
var mount = document.getElementById('widget-guided-tour-body');
if (!mount) return;
var isActive = (tourStep >= 0 && tourStep < TOUR_STEPS.length);
var step = isActive ? TOUR_STEPS[tourStep] : null;
var chipsHtml = '';
for (var i = 0; i < TOUR_STEPS.length; i++) {
var isCurrent = isActive && i === tourStep;
var isDone = isActive && i < tourStep;
var chipClass = 'tour-chip' +
(isCurrent ? ' tour-chip-active' : '') +
(isDone ? ' tour-chip-done' : '');
chipsHtml +=
'<button type="button" class="' + chipClass + '"' +
' aria-label="Go to step ' + (i + 1) + ': ' + TOUR_STEPS[i].title + '"' +
' aria-current="' + (isCurrent ? 'step' : 'false') + '"' +
' data-tour-step="' + i + '">' +
(i + 1) +
'</button>';
}
var noteHtml = '';
if (isActive && step) {
noteHtml =
'<div class="tour-note" role="status" aria-live="polite">' +
'<span class="tour-note-title">' + escapeHtml(step.title) + '</span>' +
' — ' + escapeHtml(step.note) +
'</div>';
}
var prevDisabled = !isActive || tourStep === 0;
var nextLabel = (!isActive || tourStep === TOUR_STEPS.length - 1) ? 'Exit tour' : 'Next';
var navHtml =
'<div class="tour-nav">' +
'<div class="tour-chips" role="list" aria-label="Tour steps">' +
chipsHtml +
'</div>' +
'<div class="tour-buttons">' +
(isActive
? '<button type="button" class="tour-btn" id="tour-prev"' +
(prevDisabled ? ' disabled' : '') +
' aria-label="Previous tour step">Prev</button>'
: '') +
'<button type="button" class="tour-btn tour-btn-primary" id="tour-next">' +
escapeHtml(isActive ? nextLabel : 'Start tour') +
'</button>' +
(isActive
? '<button type="button" class="tour-btn tour-btn-ghost" id="tour-exit">' +
'Exit' +
'</button>'
: '') +
'</div>' +
'</div>' +
noteHtml;
mount.innerHTML = navHtml;
var prevBtn = document.getElementById('tour-prev');
var nextBtn = document.getElementById('tour-next');
var exitBtn = document.getElementById('tour-exit');
if (prevBtn) {
prevBtn.addEventListener('click', function () {
if (tourStep > 0) {
tourStep -= 1;
applyTourStep(tourStep);
}
});
}
if (nextBtn) {
nextBtn.addEventListener('click', function () {
if (!isActive) {
tourStep = 0;
applyTourStep(tourStep);
} else if (tourStep >= TOUR_STEPS.length - 1) {
exitTour();
} else {
tourStep += 1;
applyTourStep(tourStep);
}
});
}
if (exitBtn) {
exitBtn.addEventListener('click', function () {
exitTour();
});
}
var chips = mount.querySelectorAll('[data-tour-step]');
for (var ci = 0; ci < chips.length; ci++) {
chips[ci].addEventListener('click', (function (idx) {
return function () {
tourStep = idx;
applyTourStep(tourStep);
};
}(parseInt(chips[ci].getAttribute('data-tour-step'), 10))));
}
}
function bindChartResize(chart, container) {
if (container._codeloreResizeObserver) {
container._codeloreResizeObserver.disconnect();
}
const ro = new ResizeObserver(function () { chart.resize(); });
ro.observe(container);
container._codeloreResizeObserver = ro;
}
function mountEcharts(container) {
const prior = echarts.getInstanceByDom(container);
if (prior) prior.dispose();
const chart = echarts.init(container, null, { renderer: 'canvas' });
bindChartResize(chart, container);
return chart;
}
function interpolate(formula, opts) {
return formula.replace(/\$\{([a-z_][a-z0-9_]*)\}/g, function (match, key) {
return Object.prototype.hasOwnProperty.call(opts, key) ? String(opts[key]) : match;
});
}
function buildTooltipHtml(defKey) {
const def = METRIC_DEFS[defKey];
if (!def) return '';
const citationHref = RESEARCH_FOUNDATIONS_URL + (def.citation.anchor || '');
const formulaResolved = interpolate(def.formula, data.options || {});
return '<span class="tooltip-host">' +
'<button type="button" class="tooltip-trigger" aria-label="What does this metric mean?" tabindex="0">?</button>' +
'<span class="tooltip-popup" role="tooltip">' +
'<strong>Formula</strong>' +
'<div class="tooltip-formula">' + escapeHtml(formulaResolved) + '</div>' +
'<div class="tooltip-citation">📖 <a href="' + escapeHtml(citationHref) + '" target="_blank" rel="noopener">' +
escapeHtml(def.citation.label) + ' ↗</a></div>' +
'</span>' +
'</span>';
}
function getCssVar(name) {
return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
}
function fmtInt(v) {
if (typeof v !== 'number' || !isFinite(v)) return '';
return Math.round(v).toLocaleString('en-US');
}
function fmtNumberFlex(v, decimals) {
if (typeof v !== 'number' || !isFinite(v)) return '';
return v.toFixed(decimals);
}
function escapeHtml(s) {
return String(s || '')
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
function registerThemeRerender(fn) {
window._codeloreRerenderers.push(function () {
invalidateTokenCache();
fn();
});
}
function resolveCssColor(cssExpr) {
if (!_colorResolver) {
_colorResolver = document.createElement('div');
_colorResolver.style.cssText =
'position:absolute;visibility:hidden;pointer-events:none;';
document.body.appendChild(_colorResolver);
}
_colorResolver.style.color = cssExpr;
return getComputedStyle(_colorResolver).color;
}
function bandLeafColor(band) {
if (band === 'green') return token('--color-success');
if (band === 'yellow') return token('--color-warning');
if (band === 'red') return token('--color-error');
return 'rgba(140, 140, 140, 0.55)';
}
const BIVARIATE_PALETTE = [
'#d4efd0', '#a3d99b', '#6bbf6b', '#d9b74a', '#c19a2e', '#a37d18', '#c65c46', '#a83c28', '#7d2414' ];
function healthBucket(band) {
if (band === 'green') return 0;
if (band === 'yellow') return 1;
if (band === 'red') return 2;
return -1; }
function activityBucket(hotspotScore) {
const s = (typeof hotspotScore === 'number') ? hotspotScore : 0;
if (s < 2) return 0;
if (s < 5) return 1;
return 2;
}
function bivariateColor(band, hotspotScore) {
const h = healthBucket(band);
if (h < 0) return 'rgba(140, 140, 140, 0.55)';
return BIVARIATE_PALETTE[h * 3 + activityBucket(hotspotScore)];
}
function heatRamp(ratio) {
const pct = Math.max(0, Math.min(1, ratio)) * 100;
const expr = 'color-mix(in oklch, ' + token('--color-warning') +
', ' + token('--color-error') + ' ' + pct + '%)';
return resolveCssColor(expr);
}
function startViewTransition(updateFn, scope) {
if (typeof document.startViewTransition !== 'function') {
updateFn();
return;
}
const prefersReducedMotion =
typeof window.matchMedia === 'function' &&
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (prefersReducedMotion) {
updateFn();
return;
}
if (scope && typeof scope.startViewTransition === 'function') {
scope.startViewTransition(updateFn);
return;
}
document.startViewTransition(updateFn);
}
function yieldToMain() {
if (typeof scheduler === 'object' && scheduler && typeof scheduler.yield === 'function') {
return scheduler.yield();
}
if (!yieldToMain._initialized) {
yieldToMain._initialized = true;
if (typeof MessageChannel === 'function') {
yieldToMain._channel = new MessageChannel();
}
}
if (yieldToMain._channel) {
return new Promise(function (resolve) {
yieldToMain._channel.port1.onmessage = function () { resolve(); };
yieldToMain._channel.port2.postMessage(0);
});
}
return Promise.resolve();
}
window._codeloreYieldToMain = yieldToMain;
function bandFor(score, opts) {
const greenMin = (opts && opts.health_green_min != null) ? opts.health_green_min : 70;
const yellowMin = (opts && opts.health_yellow_min != null) ? opts.health_yellow_min : 40;
if (score >= greenMin) return 'green';
if (score >= yellowMin) return 'yellow';
return 'red';
}
function bandColor(band) {
if (band === 'red') return 'var(--color-error, oklch(0.637 0.237 25.331))';
if (band === 'yellow') return 'var(--color-warning, oklch(0.845 0.143 84.429))';
return 'var(--color-success, oklch(0.753 0.152 163.216))';
}