// Sortable country table — the entry point to the explorer
const CE_COLS = [
  { k: 'ownCurrent', l: 'Currently own', base: 'all' },
  { k: 'ownEver', l: 'Ever owned', base: 'all' },
  { k: 'financialFreedom', l: 'Fin. freedom', base: 'aware' },
];
function CountryTable({ countries, perCountry, current, onSelect }) {
  const [sort, setSort] = uS('ownCurrent');
  const rows = countries.map((c, i) => ({ name: c, ...perCountry[i] }))
    .sort((a, b) => (b[sort] || 0) - (a[sort] || 0));
  const th = { fontFamily: 'JetBrains Mono, monospace', fontSize: 10, textTransform: 'uppercase', letterSpacing: '.1em', color: 'var(--muted)', fontWeight: 500, padding: '8px 12px', cursor: 'pointer', whiteSpace: 'nowrap' };
  return (
    <div className="chart-card" style={{ padding: 0, overflowX: 'auto' }}>
      <table style={{ borderCollapse: 'collapse', width: '100%', minWidth: 560 }}>
        <thead>
          <tr style={{ borderBottom: '1px solid var(--rule)' }}>
            <th style={{ ...th, textAlign: 'left', cursor: 'default' }}>Country</th>
            {CE_COLS.map(col => (
              <th key={col.k} style={{ ...th, textAlign: 'right', color: sort === col.k ? 'var(--fg)' : 'var(--muted)' }} onClick={() => setSort(col.k)}>
                {col.l} {sort === col.k ? '↓' : ''}
              </th>
            ))}
          </tr>
        </thead>
        <tbody>
          {rows.map(r => {
            const me = r.name === current;
            return (
              <tr key={r.name} onClick={() => onSelect(r.name)}
                style={{ borderBottom: '1px solid var(--rule)', cursor: 'pointer', background: me ? 'color-mix(in srgb, var(--brand) 8%, transparent)' : 'transparent' }}>
                <td style={{ padding: '9px 12px', fontFamily: 'Inter Tight, sans-serif', fontSize: 14, fontWeight: me ? 700 : 400, color: me ? 'var(--brand)' : 'var(--fg)', whiteSpace: 'nowrap' }}>
                  <span style={{ marginRight: 8, fontSize: 15 }}>{flagEmoji(COUNTRY_META[r.name]?.iso || '')}</span>
                  {titleCase(r.name)}
                  <span style={{ fontFamily: 'JetBrains Mono, monospace', fontSize: 10, color: 'var(--muted)', letterSpacing: '.1em', marginLeft: 8 }}>{COUNTRY_META[r.name]?.iso3}</span>
                </td>
                {CE_COLS.map(col => (
                  <td key={col.k} className="mono" style={{ padding: '9px 12px', textAlign: 'right', fontFamily: 'JetBrains Mono, monospace', fontSize: 12.5, color: r[col.k] == null ? 'var(--muted)' : (sort === col.k ? 'var(--fg)' : 'var(--ink-2)'), fontWeight: sort === col.k ? 600 : 400 }}>
                    {r[col.k] == null ? '—' : fmtPct(r[col.k], 0)}
                  </td>
                ))}
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
}

// Compute unweighted study average across countries for a metric
function studyAvg(perCountry, key) {
  const vals = perCountry.map(r => r[key]).filter(v => typeof v === 'number');
  return vals.length ? vals.reduce((a, b) => a + b, 0) / vals.length : null;
}

const COMPARE_METRICS = [
  { k: 'ownCurrent', l: 'Currently own bitcoin', base: 'All survey respondents' },
  { k: 'ownEver', l: 'Ever owned bitcoin', base: 'All survey respondents' },
  { k: 'financialFreedom', l: 'Agree: increases financial freedom', base: 'Among those aware of Bitcoin' },
  { k: 'protectsPrivacy', l: 'Agree: protects privacy', base: 'Among those aware of Bitcoin' },
  { k: 'confusing', l: 'Agree: is confusing / hard to grasp', base: 'Among those aware of Bitcoin' },
  { k: 'proneToFraud', l: 'Agree: is prone to fraud', base: 'Among those aware of Bitcoin' },
];

function CompareSelect({ value, onChange, countries, label, allowAvg }) {
  return (
    <div style={{ flex: 1, minWidth: 180 }}>
      <div className="eyebrow sans" style={{ marginBottom: 6 }}>{label}</div>
      <select className="pill-select sans" value={value} onChange={e => onChange(e.target.value)} style={{ width: '100%' }}>
        {allowAvg && <option value="__AVG__">Study average (25 countries)</option>}
        {countries.map(c => <option key={c} value={c}>{titleCase(c)}</option>)}
      </select>
    </div>
  );
}

function CountryCompare({ data, current, selectCountry }) {
  const { countries, perCountry } = data.summary;
  const [b, setB] = uS('__AVG__');
  const cmpRef = uR(null);
  const a = current;
  const rowA = perCountry[countries.indexOf(a)];
  const isAvgB = b === '__AVG__';
  const rowB = isAvgB ? null : perCountry[countries.indexOf(b)];
  const valA = (k) => rowA ? rowA[k] : null;
  const valB = (k) => isAvgB ? studyAvg(perCountry, k) : (rowB ? rowB[k] : null);
  const nameA = titleCase(a), nameB = isAvgB ? 'Study average' : titleCase(b);
  return (
    <section className="section" style={{ paddingTop: 8, paddingBottom: 40 }}>
      <div className="container">
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 16, marginBottom: 14 }}>
          <div className="eyebrow sans">Compare two countries</div>
          <ExportButton targetRef={cmpRef} filename="cbai-country-comparison.png" />
        </div>
        <div style={{ display: 'flex', gap: 20, flexWrap: 'wrap', alignItems: 'flex-end', marginBottom: 28, maxWidth: 620 }}>
          <CompareSelect label="Country A" value={a} onChange={selectCountry} countries={countries} />
          <div style={{ fontFamily: 'Source Serif 4, serif', fontSize: 20, color: 'var(--muted)', paddingBottom: 6 }}>vs</div>
          <CompareSelect label="Country B" value={b} onChange={setB} countries={countries} allowAvg />
        </div>
        <div className="chart-card" ref={cmpRef}>
          <div className="sans" style={{ display: 'grid', gridTemplateColumns: '1.6fr 52px 1fr 52px', gap: 14, alignItems: 'center', paddingBottom: 10, marginBottom: 12, borderBottom: '1px solid var(--rule)', fontFamily: 'Inter Tight, sans-serif', fontSize: 12, textTransform: 'uppercase', letterSpacing: '.08em', color: 'var(--muted)' }}>
            <div>Measure</div>
            <div style={{ textAlign: 'right', color: 'var(--brand)' }}>{flagEmoji(COUNTRY_META[a]?.iso || '')} A</div>
            <div></div>
            <div style={{ textAlign: 'right', color: 'var(--accent-data)' }}>{isAvgB ? '⌀' : flagEmoji(COUNTRY_META[b]?.iso || '')} B</div>
          </div>
          {COMPARE_METRICS.map(m => {
            const va = valA(m.k), vb = valB(m.k);
            const mx = Math.max(va || 0, vb || 0, 0.01);
            return (
              <div key={m.k} style={{ display: 'grid', gridTemplateColumns: '1.6fr 52px 1fr 52px', gap: 14, alignItems: 'center', padding: '9px 0', borderBottom: '1px solid var(--rule)' }}>
                <div>
                  <div style={{ fontFamily: 'Source Serif 4, serif', fontSize: 15 }}>{m.l}</div>
                  <div style={{ fontFamily: 'Inter Tight, sans-serif', fontSize: 11, color: 'var(--muted)', fontStyle: 'italic' }}>{m.base}</div>
                </div>
                <div className="mono" style={{ textAlign: 'right', fontFamily: 'JetBrains Mono, monospace', fontSize: 13, fontWeight: 700, color: 'var(--brand)' }}>{va == null ? '—' : fmtPct(va, 0)}</div>
                <div style={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
                  <div style={{ height: 8, background: 'var(--surface-2)' }}><div style={{ height: '100%', width: ((va || 0) / mx * 100) + '%', background: 'var(--brand)' }} /></div>
                  <div style={{ height: 8, background: 'var(--surface-2)' }}><div style={{ height: '100%', width: ((vb || 0) / mx * 100) + '%', background: 'var(--accent-data)' }} /></div>
                </div>
                <div className="mono" style={{ textAlign: 'right', fontFamily: 'JetBrains Mono, monospace', fontSize: 13, fontWeight: 700, color: 'var(--accent-data)' }}>{vb == null ? '—' : fmtPct(vb, 0)}</div>
              </div>
            );
          })}
          <div className="sans" style={{ display: 'flex', gap: 20, marginTop: 14, fontFamily: 'Inter Tight, sans-serif', fontSize: 12, color: 'var(--ink-2)' }}>
            <span><span style={{ display: 'inline-block', width: 10, height: 10, background: 'var(--brand)', marginRight: 6, verticalAlign: 'middle' }} />{nameA}</span>
            <span><span style={{ display: 'inline-block', width: 10, height: 10, background: 'var(--accent-data)', marginRight: 6, verticalAlign: 'middle' }} />{nameB}</span>
          </div>
        </div>
      </div>
    </section>
  );
}

// Country Explorer — pick a country, see its data (uses NEW PDF data)
function CountryPage({ data, country, setCountry }) {
  const { countries, perCountry } = data.summary;
  const [current, setCurrent] = uS(country || 'US');
  uE(()=>{ if(country && country!==current) setCurrent(country); }, [country]);
  const selectCountry = (name) => { setCurrent(name); if (setCountry) setCountry(name); };
  const [mode, setMode] = uS('single');
  const detailRef = uR(null);
  const pRef = uR(null);
  const dRef = uR(null);
  const wRef = uR(null);
  const pickFromTable = (name) => {
    selectCountry(name);
    if (mode === 'single' && detailRef.current) {
      const y = detailRef.current.getBoundingClientRect().top + window.pageYOffset - 80;
      window.scrollTo({ top: y, behavior: 'smooth' });
    }
  };
  const idx = countries.indexOf(current);
  const row = perCountry[idx];
  const tip = useTooltip();

  const rankBy = (key) => {
    const sorted = [...perCountry].filter(r=>r[key]!=null).sort((a,b)=>(b[key]||0)-(a[key]||0));
    return sorted.findIndex(x=>x.name===current) + 1;
  };
  const rankByOf = (key) => {
    const valid = perCountry.filter(r=>r[key]!=null).length;
    return `${rankBy(key)} of ${valid}`;
  };

  // For demographic breakdown
  const demoGroups = [
    {l:'Gender',     items:[['Men','men'],['Women','women']]},
    {l:'Age',        items:[['18–29','age_18_29'],['30–44','age_30_44'],['45–64','age_45_64'],['65+','age_65plus']]},
    {l:'Income',     items:[['High','high_income'],['Middle','mid_income'],['Low','low_income']]},
    {l:'Education',  items:[['College+','college'],['No college','no_college']]},
    {l:'Trust gov.', items:[['Trust','trust_govt'],['Distrust','no_trust_govt']]},
    {l:'Trust fin.', items:[['Trust','trust_finance'],['Distrust','no_trust_finance']]},
  ];

  const perceptions = [
    {l:'Increases financial freedom', k:'financialFreedom', color:'var(--accent-data)'},
    {l:'Protects my privacy',         k:'protectsPrivacy',  color:'var(--accent-data)'},
    {l:'Is confusing / hard to grasp', k:'confusing',       color:'var(--accent-neutral)'},
    {l:'Is prone to fraud',           k:'proneToFraud',     color:'var(--brand)'},
  ];

  return (
    <>
      <section className="section" style={{paddingTop:56, paddingBottom:24}}>
        <div className="container">
          <div className="eyebrow sans" style={{marginBottom:8}}>Country Explorer · {perCountry.length} countries</div>
          <h1 style={{maxWidth:'20ch', marginBottom:14}}>Explore by country.</h1>
          <p className="sans" style={{fontFamily:'Inter Tight, sans-serif', fontSize:16, color:'var(--ink-2)', maxWidth:'64ch', marginBottom:22}}>
            Pick a country from the table below to see its full profile — ownership, how people
            see bitcoin, and a demographic breakdown. Switch to <strong>Compare</strong> to place
            two countries (or a country against the 25-country average) side by side. Each country
            has its own shareable link.
          </p>
          <div style={{display:'inline-flex', border:'1px solid var(--rule)', marginBottom:24}}>
            {[['single','Single country'],['compare','Compare']].map(([k,l]) => (
              <button key={k} onClick={()=>setMode(k)}
                style={{background: k===mode?'var(--ink)':'transparent',
                        color: k===mode?'var(--paper)':'var(--ink-2)',
                        border:0, padding:'8px 18px', whiteSpace:'nowrap',
                        fontFamily:'JetBrains Mono, monospace', fontSize:11,
                        textTransform:'uppercase', letterSpacing:'.12em', cursor:'pointer'}}>
                {l}
              </button>
            ))}
          </div>
          <CountryTable countries={countries} perCountry={perCountry} current={current} onSelect={pickFromTable}/>
        </div>
      </section>

      {mode === 'compare'
        ? <CountryCompare data={data} current={current} selectCountry={selectCountry}/>
        : <div ref={detailRef}>
      <section className="section" style={{paddingTop:24, paddingBottom:32}}>
        <div className="container">
          <div style={{display:'grid', gridTemplateColumns:'1fr auto', alignItems:'end', gap:24, marginBottom:40}}>
            <h1 style={{fontSize:'clamp(40px, 5vw, 64px)'}}>
              <span style={{marginRight:14}}>{flagEmoji(COUNTRY_META[current]?.iso || '')}</span>{titleCase(current)} <span style={{fontFamily:'JetBrains Mono, monospace', fontSize:'.35em', color:'var(--muted)', verticalAlign:'middle', marginLeft:12, letterSpacing:'.12em'}}>{COUNTRY_META[current]?.iso3}</span>
            </h1>
            <select className="pill-select sans" value={current} onChange={e=>selectCountry(e.target.value)}>
              {countries.map(c => <option key={c} value={c}>{titleCase(c)}</option>)}
            </select>
          </div>

          {/* Headline metrics — PDF data */}
          <div style={{display:'grid', gridTemplateColumns:'repeat(4, 1fr)', gap:1, background:'var(--rule)', border:'1px solid var(--rule)'}}>
            {[
              {l:'Currently own bitcoin', v:row.ownCurrent, sub:`Rank ${rankByOf('ownCurrent')}`, base:'All survey respondents'},
              {l:'Ever owned bitcoin', v:row.ownEver, sub:`Currently or previously`, base:'All survey respondents'},
              {l:'Agree: increases financial freedom', v:row.financialFreedom, sub:`Rank ${rankByOf('financialFreedom')}`, base:'Among those aware · rated 5–7 of 1–7'},
              {l:'Agree: protects privacy', v:row.protectsPrivacy, sub:`Rank ${rankByOf('protectsPrivacy')}`, base:'Among those aware · rated 5–7 of 1–7'},
            ].map(s => (
              <div key={s.l} style={{background:'var(--surface)', padding:'22px 18px'}}>
                <div className="eyebrow sans" style={{marginBottom:8}}>{s.l}</div>
                <div style={{fontSize:40, fontFamily:'Source Serif 4, serif', lineHeight:1, letterSpacing:'-0.02em'}}>
                  {s.v==null ? <span style={{color:'var(--muted)'}}>—</span> : fmtPct(s.v)}
                </div>
                <div style={{fontFamily:'Inter Tight, sans-serif', fontSize:11, color:'var(--muted)', marginTop:8, fontStyle:'italic'}}>{s.base}</div>
                <div style={{fontFamily:'JetBrains Mono, monospace', fontSize:10, color:'var(--muted)', textTransform:'uppercase', letterSpacing:'.12em', marginTop:6}}>{s.sub}</div>
              </div>
            ))}
          </div>
          <div style={{fontFamily:'JetBrains Mono, monospace', fontSize:10, textTransform:'uppercase', letterSpacing:'.12em', color:'var(--muted)', marginTop:10}}>
            Sample size · n = {fmtNum(row.n)} respondents · Ownership shown as % of all survey respondents; perceptions among those aware of Bitcoin
          </div>
        </div>
      </section>

      {/* Perceptions block */}
      <section className="section" style={{paddingTop:8, paddingBottom:32}}>
        <div className="container">
          <div style={{display:'flex', justifyContent:'space-between', alignItems:'baseline', gap:16, marginBottom:14}}>
            <div className="eyebrow sans">Perceptions of Bitcoin — {titleCase(current)}</div>
            <ExportButton targetRef={pRef} filename={`cbai-${current.toLowerCase()}-perceptions.png`}/>
          </div>
          <div className="chart-card" ref={pRef}>
            <div className="chart-head">
              <div>
                <h3>How {titleCase(current)} sees Bitcoin</h3>
              </div>
              <div className="src">% agree above midpoint (5–7 of 1–7) · among those aware of Bitcoin</div>
            </div>
            <div style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap:32}}>
              {perceptions.map(p => {
                const v = row[p.k];
                const rank = rankBy(p.k);
                return (
                  <div key={p.k}>
                    <div className="eyebrow sans" style={{marginBottom:8}}>{p.l}</div>
                    <div style={{display:'flex', alignItems:'baseline', gap:16}}>
                      <div style={{fontFamily:'Source Serif 4, serif', fontSize:36, letterSpacing:'-0.01em'}}>
                        {v==null ? <span style={{color:'var(--muted)'}}>—</span> : fmtPct(v, 0)}
                      </div>
                      <div style={{fontFamily:'JetBrains Mono, monospace', fontSize:10, textTransform:'uppercase', letterSpacing:'.12em', color:'var(--muted)'}}>
                        Rank {rank} of {perCountry.filter(r=>r[p.k]!=null).length}
                      </div>
                    </div>
                    <div style={{height:8, background:'var(--surface-2)', marginTop:10}}>
                      <div style={{height:'100%', width:((v||0)*100)+'%', background:p.color}}/>
                    </div>
                  </div>
                );
              })}
            </div>
          </div>
        </div>
      </section>

      {/* Demographic breakdown */}
      <section className="section" style={{paddingTop:8, paddingBottom:32}}>
        <div className="container">
          <div style={{display:'flex', justifyContent:'space-between', alignItems:'baseline', gap:16, marginBottom:14}}>
            <div className="eyebrow sans">Bitcoin ownership in {titleCase(current)} — by group</div>
            <ExportButton targetRef={dRef} filename={`cbai-${current.toLowerCase()}-ownership-by-group.png`}/>
          </div>
          <div className="chart-card" ref={dRef}>
            <div className="chart-head">
              <div>
                <h3>Who owns bitcoin in {titleCase(current)}?</h3>
              </div>
              <div className="src">% currently own, among those aware of Bitcoin · groups with n &lt; 50 suppressed</div>
            </div>
            <div style={{display:'grid', gridTemplateColumns:'repeat(3, 1fr)', gap:32}}>
              {demoGroups.map(g => {
                // find max within country for relative bar
                const vals = g.items.map(([_,k]) => row.byDemo[k]).filter(v=>v!=null);
                const maxV = Math.max(0.05, ...vals);
                return (
                  <div key={g.l}>
                    <div className="eyebrow sans" style={{marginBottom:8}}>{g.l}</div>
                    {g.items.map(([n,k]) => {
                      const v = row.byDemo[k];
                      return (
                        <div key={n} style={{display:'grid', gridTemplateColumns:'80px 1fr 52px', gap:8, alignItems:'center', marginBottom:5}}>
                          <div style={{fontFamily:'Inter Tight, sans-serif', fontSize:13}}>{n}</div>
                          <div style={{height:14, background:'var(--surface-2)', position:'relative'}}>
                            {v != null && <div style={{height:'100%', width:((v/maxV)*100)+'%', background:'var(--accent-data)'}}/>}
                          </div>
                          <div className="mono" style={{textAlign:'right', fontSize:12, fontFamily:'JetBrains Mono, monospace', color: v==null?'var(--muted)':'inherit'}}>
                            {v==null ? '—' : fmtPct(v, 0)}
                          </div>
                        </div>
                      );
                    })}
                  </div>
                );
              })}
            </div>
            <p style={{fontFamily:'Inter Tight, sans-serif', fontSize:12, color:'var(--muted)', marginTop:14, maxWidth:'70ch'}}>
              Bars are scaled relative to the highest group within this country (not against the global maximum) — to show internal contrast.
            </p>
          </div>
        </div>
      </section>

      <CountryVoices quotes={data.quotes} country={current} />

      {/* Cross-country comparison: where this country ranks */}
      <section className="section" style={{paddingTop:8, paddingBottom:32}}>
        <div className="container">
          <div style={{display:'flex', justifyContent:'space-between', alignItems:'baseline', gap:16, marginBottom:14}}>
            <div className="eyebrow sans">Where {titleCase(current)} sits</div>
            <ExportButton targetRef={wRef} filename={`cbai-${current.toLowerCase()}-rankings.png`}/>
          </div>
          <div ref={wRef} style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap:32}}>
            {[['ownCurrent','Currently own bitcoin','All survey respondents'],
              ['financialFreedom','Agree: Bitcoin increases financial freedom','Among those aware · rated 5–7 of 1–7']
             ].map(([k, title, base]) => {
              const sorted = [...perCountry].filter(r=>r[k]!=null).sort((a,b)=>(b[k]||0)-(a[k]||0));
              const maxV = sorted[0]?.[k] || 1;
              return (
                <div key={k} className="chart-card">
                  <div className="chart-head">
                    <h3 style={{fontFamily:'Source Serif 4, serif', fontSize:17, fontWeight:500, textTransform:'none', letterSpacing:'-0.01em'}}>{title}</h3>
                    <div className="src">{base}</div>
                  </div>
                  <div data-keep-cols className="uchart-rows" style={{ display: 'grid', gridTemplateColumns: '120px 1fr 50px', gap:8, alignItems:'center', fontSize:12}}>
                    {sorted.map(r => {
                      const me = r.name === current;
                      return (
                        <React.Fragment key={r.name}>
                          <div className="sans" style={{fontFamily:'Inter Tight, sans-serif', fontWeight: me?700:400, color: me?'var(--brand)':'var(--ink-2)'}}>
                            {titleCase(r.name)}
                          </div>
                          <div style={{height:12, background:'var(--surface-2)'}}>
                            <div style={{height:'100%', width:((r[k]/maxV)*100)+'%', background: me?'var(--brand)':'var(--accent-data)'}}/>
                          </div>
                          <div className="mono" style={{textAlign:'right', fontSize:11, fontWeight: me?700:400}}>{fmtPct(r[k])}</div>
                        </React.Fragment>
                      );
                    })}
                  </div>
                </div>
              );
            })}
          </div>
        </div>
      </section>

      </div>}
    </>
  );
}

// Question Explorer (LEGACY banner data — flagged with note)
function QuestionPage({ data, defaultQuestion }) {
  const { countries } = data.summary;
  const { questions } = data.q;
  const [qid, setQid] = uS(defaultQuestion || 'CU6');
  uE(()=>{ if(defaultQuestion) setQid(defaultQuestion); }, [defaultQuestion]);
  const [search, setSearch] = uS('');
  const [open, setOpen] = uS(false);
  const [hi, setHi] = uS(0);
  const [sortBy, setSortBy] = uS(0);
  const [view, setView] = uS('chart');
  const q = questions.find(x => x.id === qid) || questions[0];
  const base = getQuestionBase(q.id);
  const note = getQuestionNote(q.id);
  const baseN = (q.unweightedN || []).reduce((a, b) => a + (typeof b === 'number' ? b : 0), 0);
  const tip = useTooltip();
  const chartRef = uR(null);

  const filtered = search ? questions.filter(x => (x.text+x.id).toLowerCase().includes(search.toLowerCase())) : questions;

  const rows = countries.map((c, i) => ({
    country: c,
    n: q.unweightedN?.[i],
    vals: q.answers.map(a => a.values[i])
  }))
  // Filter out countries that have no data for this question — useful for
  // country-specific questions like CU26_12+ (local currencies asked only of that country)
  .filter(r => r.vals.some(v => typeof v === 'number' && v > 0))
  .sort((a,b)=>(b.vals[sortBy]||0)-(a.vals[sortBy]||0));

  const downloadCSV = () => {
    const q2 = (s) => '"' + String(s==null?'':s).replace(/"/g,'""') + '"';
    const meta = ['Question,' + q2(q.id), 'Base,' + q2(base ? base.label : ''), 'Subsample defined by,' + q2(base && base.sourceCU.length ? base.sourceCU.join(' + ') : 'All respondents'), 'Base n,' + baseN, 'Note,' + q2(note || '')].join('\n');
    const header = ['Country','n', ...q.answers.map(a=>a.label)].join(',');
    const body = rows.map(r => [r.country, r.n, ...r.vals.map(v=>v==null?'':(v*100).toFixed(3))].join(',')).join('\n');
    const blob = new Blob([meta+'\n\n'+header+'\n'+body], {type:'text/csv'});
    const a = document.createElement('a'); a.href = URL.createObjectURL(blob);
    a.download = `${q.id}.csv`; a.click();
  };

  // Tidy/long-format download of EVERY question × answer × country.
  // Columns: question_id, question_text, answer_label, country, n, percent
  const downloadAllCSV = () => {
    const csvEscape = (s) => {
      if (s == null) return '';
      const str = String(s);
      return /[",\n]/.test(str) ? '"' + str.replace(/"/g, '""') + '"' : str;
    };
    const header = ['question_id','question_text','base','note','answer_label','country','n','percent'].join(',');
    const lines = [header];
    for (const qq of questions) {
      const ns = qq.unweightedN || [];
      const qBase = getQuestionBase(qq.id);
      const qNote = getQuestionNote(qq.id);
      for (const a of qq.answers) {
        for (let i = 0; i < countries.length; i++) {
          const v = a.values[i];
          if (v == null) continue;
          lines.push([
            csvEscape(qq.id),
            csvEscape(qq.text),
            csvEscape(qBase ? qBase.label : ''),
            csvEscape(qNote || ''),
            csvEscape(a.label),
            csvEscape(titleCase(countries[i])),
            csvEscape(ns[i] ?? ''),
            (v * 100).toFixed(3),
          ].join(','));
        }
      }
    }
    const blob = new Blob([lines.join('\n')], {type: 'text/csv'});
    const a = document.createElement('a'); a.href = URL.createObjectURL(blob);
    a.download = 'BTPI_all_questions.csv'; a.click();
    setTimeout(() => URL.revokeObjectURL(a.href), 1000);
  };

  return (
    <>
      <section className="section" style={{paddingTop:56, paddingBottom:32, borderBottom:'1px solid var(--rule)'}}>
        <div className="container">
          <div className="eyebrow sans" style={{marginBottom:8}}>Question Explorer · {questions.length} banner questions</div>

          <div data-keep-cols className="uchart-rows" style={{ display: 'grid', gridTemplateColumns: '420px 1fr', gap:40, alignItems:'start'}}>
            <div style={{display:'flex', flexDirection:'column', gap:10}}>
              <p className="sans" style={{fontFamily:'Inter Tight, sans-serif', fontSize:15, color:'var(--ink-2)', lineHeight:1.5, margin:'0 0 4px'}}>
                Explore the findings for every question in the survey. <strong>Pick a question</strong> from
                the menu below — or search by keyword — to see how all 25 countries answered it.
              </p>
              <div className="qcombo" style={{ position: 'relative' }}>
                <input className="search-input" placeholder="Search 125 questions…" value={search}
                  onChange={e=>{setSearch(e.target.value); setOpen(true); setHi(0);}}
                  onFocus={()=>setOpen(true)}
                  onKeyDown={e=>{
                    if(e.key==='ArrowDown'){e.preventDefault(); setOpen(true); setHi(h=>Math.min(h+1, filtered.length-1));}
                    else if(e.key==='ArrowUp'){e.preventDefault(); setHi(h=>Math.max(h-1,0));}
                    else if(e.key==='Enter'&&open&&filtered[hi]){e.preventDefault(); setQid(filtered[hi].id); setSortBy(0); setOpen(false); setSearch('');}
                    else if(e.key==='Escape'){setOpen(false);}
                  }}
                  style={{width:'100%'}}/>
                {open &&
                <div className="qcombo-list" style={{position:'absolute', top:'calc(100% + 4px)', left:0, right:0, zIndex:40, maxHeight:320, overflowY:'auto', background:'var(--surface)', border:'1px solid var(--ink)', boxShadow:'0 10px 30px rgba(0,0,0,.14)'}}>
                  {filtered.length===0 &&
                  <div className="sans" style={{padding:'12px 14px', fontFamily:'Inter Tight, sans-serif', fontSize:13, color:'var(--muted)'}}>No question matches “{search}”.</div>}
                  {filtered.slice(0,120).map((x,i)=>
                  <button key={x.id} onMouseDown={e=>e.preventDefault()}
                    onClick={()=>{setQid(x.id); setSortBy(0); setOpen(false); setSearch('');}}
                    onMouseEnter={()=>setHi(i)}
                    className="sans qcombo-item"
                    style={{display:'block', width:'100%', textAlign:'left', border:0, cursor:'pointer', padding:'9px 13px',
                      background: i===hi ? 'var(--surface-2)' : 'transparent',
                      borderLeft: x.id===qid ? '3px solid var(--brand)' : '3px solid transparent',
                      fontFamily:'Inter Tight, sans-serif', fontSize:13, lineHeight:1.35, color:'var(--fg)'}}>
                    <span className="mono" style={{fontFamily:'JetBrains Mono, monospace', fontSize:10.5, color:'var(--muted)', letterSpacing:'.04em'}}>{x.id}</span>
                    <span style={{display:'block'}}>{chartTitleFor(x)}</span>
                  </button>
                  )}
                </div>}
              </div>
              {open && <div onClick={()=>setOpen(false)} style={{position:'fixed', inset:0, zIndex:30}}/>}
              <div className="sans" style={{fontFamily:'Inter Tight, sans-serif', fontSize:12.5, color:'var(--muted)'}}>
                Showing <strong style={{color:'var(--fg)'}}>{q.id}</strong> · {chartTitleFor(q)}
              </div>
              <div style={{display:'flex', gap:8}}>
                <button onClick={downloadCSV} style={{background:'var(--ink)', color:'var(--paper)', border:0, padding:'8px 14px', fontFamily:'JetBrains Mono, monospace', fontSize:10, textTransform:'uppercase', letterSpacing:'.12em', cursor:'pointer'}}>
                  ↓ This question
                </button>
                <button onClick={downloadAllCSV} title="All 125 questions × every answer × every country, in tidy/long format"
                        style={{background:'transparent', color:'var(--ink)', border:'1px solid var(--ink)', padding:'8px 14px', fontFamily:'JetBrains Mono, monospace', fontSize:10, textTransform:'uppercase', letterSpacing:'.12em', cursor:'pointer'}}>
                  ↓ All questions
                </button>
              </div>
            </div>
            <div>
              <div className="chip" style={{marginBottom:14}}>{q.id}</div>
              <h1 style={{fontSize:'clamp(28px, 3.2vw, 40px)', maxWidth:'26ch'}}>{q.text.replace(/^CU\d+[a-z]?_?\d*\s*-\s*/,'')}</h1>
              {base && (
                <div className="sans" style={{display:'flex', alignItems:'baseline', gap:12, marginTop:14, padding:'9px 14px', border:'1px solid var(--rule)', background:'var(--surface-2)', maxWidth:'64ch'}}>
                  <span style={{fontFamily:'JetBrains Mono, monospace', fontSize:10, textTransform:'uppercase', letterSpacing:'.12em', color:'var(--muted)', flexShrink:0}}>Base</span>
                  <div>
                    <div style={{fontFamily:'Inter Tight, sans-serif', fontSize:13, color:'var(--ink-2)'}}>{base.label}</div>
                    <div style={{fontFamily:'JetBrains Mono, monospace', fontSize:10.5, color:'var(--muted)', marginTop:5, letterSpacing:'.04em', display:'flex', flexWrap:'wrap', gap:'4px 10px'}}>
                      {base.sourceCU.length > 0 && <span>Subsample defined by {base.sourceCU.join(' + ')}</span>}
                      <span>n = {fmtNum(baseN)} respondents</span>
                    </div>
                    {note && (
                      <div style={{fontFamily:'Inter Tight, sans-serif', fontSize:12, fontStyle:'italic', color:'var(--muted)', marginTop:8, lineHeight:1.5, maxWidth:'58ch'}}>{note}</div>
                    )}
                  </div>
                </div>
              )}
            </div>
          </div>
        </div>
      </section>

      <section className="section" style={{paddingTop:32}}>
        <div className="container">
          <div style={{display:'flex', gap:8, alignItems:'center', marginBottom:16, flexWrap:'wrap'}}>
            <span className="eyebrow sans">View:</span>
            <div style={{display:'inline-flex', border:'1px solid var(--rule)'}}>
              {['chart','table'].map(v => (
                <button key={v} onClick={()=>setView(v)}
                        style={{background: v===view?'var(--ink)':'transparent',
                                color: v===view?'var(--paper)':'var(--ink-2)',
                                border:0, padding:'5px 12px',
                                fontFamily:'JetBrains Mono, monospace', fontSize:10,
                                textTransform:'uppercase', letterSpacing:'.1em', cursor:'pointer'}}>
                  {v}
                </button>
              ))}
            </div>
            <span className="eyebrow sans" style={{marginLeft:16}}>Sort by:</span>
            {q.answers.map((a, i) => (
              <button key={i} onClick={()=>setSortBy(i)}
                      style={{background: i===sortBy?'var(--ink)':'transparent',
                              color: i===sortBy?'var(--paper)':'var(--ink-2)',
                              border:'1px solid var(--rule)', padding:'5px 10px',
                              fontFamily:'JetBrains Mono, monospace', fontSize:10,
                              textTransform:'uppercase', letterSpacing:'.1em', cursor:'pointer'}}>
                {a.label.length>26?a.label.slice(0,24)+'…':a.label}
              </button>
            ))}
          </div>

          {(() => {
            const included = new Set(rows.map(r => r.country));
            const excluded = countries.filter(c => !included.has(c));
            const regulatoryExcl = excluded.filter(c => c === 'CHINA' || c === 'HONG KONG');
            if (regulatoryExcl.length === 0) return null;
            return (
              <div style={{display:'flex', alignItems:'baseline', gap:12, padding:'10px 14px', marginBottom:16,
                            background:'var(--surface-2)', border:'1px solid var(--rule)',
                            fontFamily:'Inter Tight, sans-serif', fontSize:13, color:'var(--ink-2)', lineHeight:1.5}}>
                <span style={{fontFamily:'JetBrains Mono, monospace', fontSize:10, textTransform:'uppercase', letterSpacing:'.12em', color:'var(--muted)', fontWeight:600, flexShrink:0}}>Note</span>
                <span style={{maxWidth:'72ch'}}>
                  {regulatoryExcl.map(titleCase).join(' and ')} {regulatoryExcl.length === 1 ? 'is' : 'are'} not shown for this question. Bitcoin-ownership responses were not collected in these jurisdictions due to regulatory constraints.
                </span>
              </div>
            );
          })()}

          {view === 'chart' 
            ? <QuestionChart q={q} countries={countries} rows={rows} sortBy={sortBy} tip={tip}/>
            : <QuestionTable q={q} rows={rows} sortBy={sortBy}/>
          }
        </div>
      </section>
    </>
  );
}

// ----- Question view: TABLE -----
function QuestionTable({ q, rows, sortBy }) {
  return (
    <div className="chart-card">
      <Exportable filename={`${q.id}-table.png`} buttonStyle={{ top: 12, right: 12 }}>
      <div className="chart-head">
        <div>
          <div className="eyebrow" style={{marginBottom:6}}>{q.id}</div>
          <h3 style={{fontFamily:'Source Serif 4, serif', fontSize:18, fontWeight:500, textTransform:'none', letterSpacing:'-0.01em'}}>{chartTitleFor(q)}</h3>
        </div>
        <div className="src" style={{paddingRight:96}}>Every answer choice · all countries</div>
      </div>
      <table className="qtable">
        <thead>
          <tr>
            <th>Country</th>
            <th>n</th>
            {q.answers.map(a => <th key={a.label} className="num" style={{textAlign:'right'}}>{a.label.length>22?a.label.slice(0,20)+'…':a.label}</th>)}
          </tr>
        </thead>
        <tbody>
          {rows.map(r => (
            <tr key={r.country}>
              <td style={{fontWeight:600}}>{titleCase(r.country)}</td>
              <td className="num">{fmtNum(r.n)}</td>
              {r.vals.map((v, i) => {
                const cellColor = colorForAnswer(q.answers[i].label, i);
                return (
                  <td key={i} className="num" style={{background: i===sortBy && v!=null ? `color-mix(in srgb, ${cellColor} ${Math.min((v*100),100)*.5}%, transparent)` : ''}}>
                    {v==null?'—':fmtPct(v, 0)}
                  </td>
                );
              })}
            </tr>
          ))}
        </tbody>
      </table>
      </Exportable>
    </div>
  );
}

// Try to extract leading numeric position from an answer label like "3 – neutral" or "5 – very important"
function parseOrdinal(label) {
  const m = String(label||'').match(/^(\d+)/);
  return m ? parseInt(m[1], 10) : null;
}
// Returns {scale: max, getMean(values): number} if ordinal, else null
function detectOrdinal(q) {
  const positions = q.answers.map(a => parseOrdinal(a.label));
  if (positions.some(p => p == null)) return null;
  // Need at least 3 ordered answers to make sense
  if (q.answers.length < 3) return null;
  // Confirm strictly increasing
  for (let i=1; i<positions.length; i++) if (positions[i] <= positions[i-1]) return null;
  const max = positions[positions.length-1];
  return {
    scale: max,
    positions,
    mean(values) {
      let num = 0, denom = 0;
      for (let i=0; i<values.length; i++) {
        const v = values[i];
        if (typeof v !== 'number') continue;
        num += positions[i] * v;
        denom += v;
      }
      return denom ? num/denom : null;
    }
  };
}
const CHART_PALETTE = [
  '#073949', // 0 Cornell Navy — priority
  '#B31B1B', // 1 Cornell Carnelian — priority
  '#222222', // 2 Dark gray — priority
  '#A2998B', // 3 Cornell Warm gray
  '#9FAD9F', // 4 Cornell Sage green (replaces orange)
  '#006699', // 5 Cornell Blue
  '#4B7B2B', // 6 Cornell Green
  '#585858', // 7 Cornell Steel
  '#5A8AA0', // 8 Light navy
  '#6FA3C9', // 9 Sky blue
  '#7c5295', // 10 Plum
  '#C9A95B', // 11 Gold
  '#6B6E20', // 12 Olive
  '#B07AA1', // 13 Lavender
];

// Map semantic answer categories to consistent colors across questions
// so the same concept (e.g. "Volatility concerns") always shows the same color
// — whether it's in CU16 (no longer own), CU17 (never owned), or CU20 (more likely to use).
const CATEGORY_COLORS = {
  // Priority categories use the three priority brand colors
  volatility:            '#073949', // Cornell Navy
  regulatory:            '#B31B1B', // Cornell Carnelian
  not_interested:        '#222222', // Dark gray
  // Cornell secondary brand colors
  security:              '#006699', // Cornell Blue
  financial_constraints: '#A2998B', // Cornell Warm gray
  adoption:              '#4B7B2B', // Cornell Green
  sold_for_profit:       '#9FAD9F', // Cornell Sage green
  fees:                  '#585858', // Cornell Steel
  // Cornell-derived tints / shades
  complexity:            '#C9A95B', // Gold
  better_alternatives:   '#6FA3C9', // Sky
  tax_issues:            '#5A8AA0', // Light navy
  // Tertiary categories (rarely top-cited)
  environmental:         '#7c5295', // Plum
  illegal:               '#6B6E20', // Olive
  social:                '#B07AA1', // Lavender
  knowledge:             '#7a6f5d', // Khaki
  other:                 '#aaa5a0', // Light gray
};

function categorize(label) {
  if (!label) return null;
  const l = String(label).toLowerCase();
  if (l.includes('sold') && l.includes('profit')) return 'sold_for_profit';
  if (l.includes('volatil') || (l.includes('price') && l.includes('stable'))) return 'volatility';
  if (l.includes('regulat') || l.includes('onerous')) return 'regulatory';
  if (l.includes('scam') || l.includes('fraud') || l.includes('hack') || l.includes('safer')) return 'security';
  if (l.includes('cost-effective') || l.includes('cost effective') || l.includes('transaction fee') || l.match(/^high transaction fees/) || l.includes('more cost effective')) return 'fees';
  if (l.includes('technical complex') || l.includes('easier to use')) return 'complexity';
  if (l.includes('knew more about') || l.includes('know more about how')) return 'knowledge';
  if (l.includes('adoption') || l.includes('accepted at more') || l.includes('practical use')) return 'adoption';
  if (l.includes('environment')) return 'environmental';
  if (l.includes('better alternat')) return 'better_alternatives';
  if (l.includes('never interested') || l.includes('no longer interested') || (l.includes('never') && l.includes('interest'))) return 'not_interested';
  if (l.includes('tax')) return 'tax_issues';
  if (l.includes('illegal') || l.includes('criminal')) return 'illegal';
  if (l.includes('financial constraint') || l.includes('cannot afford') || l.includes('could not afford') || l.includes('had more money') || l.includes('more money')) return 'financial_constraints';
  if (l.includes('friends and family') || l.includes('friends were using')) return 'social';
  if (l.startsWith('other')) return 'other';
  return null;
}

// Custody methods use a fixed mapping shared by the Key Findings and Questions views:
// blues for methods held through a third party, carnelian for the hardware wallet,
// gold for shared control, gray for combinations and non-answers.
const CUSTODY_COLORS = {
  'digital wallet': '#073949',
  'exchange': '#006699',
  'etf': '#6FA3C9',
  'combination of wallet and exchange': '#A2998B',
  'physical wallet': '#B31B1B',
  "don't know": '#d6d2cb',
  'collaborative custody': '#C9A95B' };

// Risk comparisons keep a fixed, intuitive scale: carnelian = higher risk,
// warm gray = equal, navy = lower risk. Checked before category matching.
const RISK_COLORS = { higher: '#B31B1B', equal: '#A2998B', lower: '#073949' };
function riskColor(label) {
  const l = String(label || '').toLowerCase();
  if (!l.includes('risk')) return null;
  if (l.includes('higher risk') || l.includes('more risk')) return RISK_COLORS.higher;
  if (l.includes('equal risk') || l.includes('same risk') || l.includes('equally risky')) return RISK_COLORS.equal;
  if (l.includes('lower risk') || l.includes('less risk')) return RISK_COLORS.lower;
  return null;
}

// Returns the color for an answer, preferring semantic category over index position.
// Falls back to index palette for non-reason questions (Yes/No, ordinal scales, etc.)
function colorForAnswer(label, index) {
  const custody = CUSTODY_COLORS[String(label || '').toLowerCase()];
  if (custody) return custody;
  const risk = riskColor(label);
  if (risk) return risk;
  const cat = categorize(label);
  if (cat && CATEGORY_COLORS[cat]) return CATEGORY_COLORS[cat];
  return CHART_PALETTE[index % CHART_PALETTE.length];
}

// Human-readable chart titles per base CU number. Battery items (text after "---")
// are appended so each sub-question reads specifically, e.g. "Reason for Using Bitcoin: Privacy".
const CHART_TITLE_BASE = {
  CU1:'Have a Bank or Financial Account', CU2:'Financial Assets Owned',
  CU3:'Awareness of Cryptocurrency', CU4:'Will Crypto Become the Main Form of Payment?',
  CU5:'Self-Rated Knowledge of Cryptocurrency', CU6:'Awareness of Bitcoin',
  CU7:'Most Commonly Heard Cryptocurrencies', CU8:'Self-Rated Knowledge of Bitcoin',
  CU9:'Will Bitcoin Become the Main Form of Payment?', CU10:'Have Ever Owned Bitcoin',
  CU11:'Year First Owned Bitcoin', CU12:'How Current Owners Hold Bitcoin',
  CU13:'How Former Owners Held Bitcoin', CU14:'How Often Owners Transact in Bitcoin',
  CU15:'How Often Former Owners Transacted in Bitcoin', CU16:'Reasons for No Longer Owning Bitcoin',
  CU17:'Reasons for Never Owning Bitcoin', CU18:'Likelihood of Increasing Bitcoin Use',
  CU19:'Likelihood of Using Bitcoin', CU20:'What Would Encourage Bitcoin Use',
  CU21:'Reason for Using Bitcoin', CU22:'Share of Savings Held in Bitcoin',
  CU23:'Trust in Bitcoin', CU25:'Trust in Bitcoin Services & Providers',
  CU26:"Bitcoin's Risk vs. Other Assets", CU27:'Agreement About Bitcoin',
  CU28:"Knowledge of Bitcoin's Supply Cap", CU29:'Awareness of Stablecoins',
  CU30:'Self-Rated Knowledge of Stablecoins', CU31:'Most Commonly Heard Stablecoins',
  CU32:'Will Stablecoins Become the Main Form of Payment?', CU33:'Have Ever Owned Stablecoins',
  CU34:'Year First Owned Stablecoins', CU35:'Reason for Owning Stablecoins',
  CU36:'How Often Owners Transact in Stablecoins', CU37:'Reasons for Not Using Stablecoins',
  CU38:'Social & Political Attitudes', CU39:'Self-Rated Knowledge of Personal Finance',
  CU40:'Financial Literacy: Stocks vs. Index', CU41:'Financial Literacy: Inflation',
  CU42:'Financial Literacy: Compound Interest', CU43:'Trust in Government',
  CU44:'Most Important Problem Facing the Country', CU45:'Rating of Government Performance',
  CU46:"Community's Rating of Government Performance", CU47:'Belief That People Have Good Intentions',
  CU48:'Confidence in Banks & Financial Institutions', CU49:'Membership in Community Organizations',
  CU50:'Worked to Solve a Community Problem',
};
function chartTitleFor(q) {
  const m = String(q.id || '').match(/^CU(\d+)/);
  const base = m ? CHART_TITLE_BASE['CU' + m[1]] : null;
  let item = null;
  const dash = q.text.indexOf('---');
  if (dash >= 0) item = q.text.slice(dash + 3).trim().replace(/\s+/g, ' ').replace(/[.:]$/, '');
  if (base && item) return base + ': ' + item;
  if (base) return base;
  let t = q.text.replace(/^CU\d+[a-z]?_?\d*(stitch)?\s*-\s*/i, '').replace(/\s*---.*$/, '');
  t = t.replace(/\s*\([^)]*\)\s*/g, ' ').trim();
  return t.length > 72 ? t.slice(0, 70) + '…' : t;
}

function QuestionChart({ q, countries, rows, sortBy, tip }) {
  // Detect single-select vs multi-select using the first country that actually has data
  // (some questions like CU26_14+ only have data for one country)
  let firstSum = 0;
  for (let i = 0; i < countries.length; i++) {
    const sum = (q.answers||[]).map(a => a.values?.[i]).filter(v => typeof v === 'number').reduce((a,b)=>a+b, 0);
    if (sum > 0) { firstSum = sum; break; }
  }
  const isStackable = firstSum > 0.95 && firstSum < 1.05;
  const ordinal = isStackable ? detectOrdinal(q) : null;

  // For ordinal questions, override the parent sort: rank by weighted mean (descending)
  const sortedRows = ordinal
    ? [...rows].sort((a,b) => (ordinal.mean(b.vals) ?? -Infinity) - (ordinal.mean(a.vals) ?? -Infinity))
    : rows;

  return (
    <div className="chart-card">
      <Exportable filename={`${q.id}.png`} buttonStyle={{ top: 12, right: 12 }}>
      <div className="chart-head">
        <div>
          <div className="eyebrow" style={{marginBottom:6}}>{q.id}</div>
          <h3 style={{fontFamily:'Source Serif 4, serif', fontSize:18, fontWeight:500, textTransform:'none', letterSpacing:'-0.01em'}}>
            {chartTitleFor(q)}
          </h3>
        </div>
        <div className="src" style={{paddingRight:96}}>
          {ordinal
            ? `Sorted by weighted mean · scale 1–${ordinal.scale}`
            : (isStackable ? ((q.id === 'CU12' || q.id === 'CU13') ? 'Stacked columns · sorted by selected answer' : 'Stacked horizontal · sorted by selected answer') : 'Small multiples · multi-select')}
        </div>
      </div>

      {/* Legend */}
      <div className="sans" style={{display:'flex', flexWrap:'wrap', gap:14, marginBottom:18, fontFamily:'Inter Tight, sans-serif', fontSize:11.5, color:'var(--muted)'}}>
        {q.answers.map((a, i) => (
          <span key={i} style={{display:'inline-flex', alignItems:'center', gap:6,
                                 opacity: (ordinal || i===sortBy) ? 1 : .85,
                                 fontWeight: (!ordinal && i===sortBy) ? 600 : 400,
                                 color: (!ordinal && i===sortBy) ? 'var(--fg)' : 'var(--muted)'}}>
            <span style={{display:'inline-block', width:12, height:12, background: colorForAnswer(a.label, i)}}/>
            {a.label.length>40?a.label.slice(0,38)+'…':a.label}
          </span>
        ))}
      </div>

      {isStackable
        ? ((q.id === 'CU12' || q.id === 'CU13')
            ? <QuestionStackedColumns q={q} rows={sortedRows} sortBy={sortBy} tip={tip} ordinal={ordinal}/>
            : <QuestionStackedChart q={q} rows={sortedRows} sortBy={sortBy} tip={tip} ordinal={ordinal}/>)
        : <TopCitedChart q={q} countries={countries} rows={rows} tip={tip}/>}
      </Exportable>
      {isStackable
        ? (!ordinal && <Exportable filename={`${q.id}-breakdown.png`} buttonStyle={{ top: 0, right: 0 }} style={{ marginTop: 30 }}>
            <div className="eyebrow sans" style={{marginBottom:10}}>Each category by country</div>
            <QuestionSmallMultiples q={q} countries={countries} rows={rows} sortBy={sortBy} tip={tip}/>
          </Exportable>)
        : <Exportable filename={`${q.id}-breakdown.png`} buttonStyle={{ top: 0, right: 0 }} style={{ marginTop: 24 }}>
            <div className="eyebrow sans" style={{marginBottom:10}}>Each answer choice in detail</div>
            <QuestionSmallMultiples q={q} countries={countries} rows={rows} sortBy={sortBy} tip={tip}/>
          </Exportable>}
    </div>
  );
}

// Pick a readable text color for label on top of a hex background
function labelTextColor(hex) {
  const h = hex.replace('#','');
  const r = parseInt(h.substring(0,2),16);
  const g = parseInt(h.substring(2,4),16);
  const b = parseInt(h.substring(4,6),16);
  const lum = (0.299*r + 0.587*g + 0.114*b) / 255;
  return lum > 0.6 ? '#1a1a1a' : '#ffffff';
}

function QuestionStackedColumns({ q, rows, sortBy, tip, ordinal }) {
  return (
    <div className="ucols-wrap" style={{ paddingLeft: 54, paddingBottom: 10 }}>
      <div className="ucols" style={{ display: 'grid', gridTemplateColumns: `repeat(${rows.length}, minmax(0, 1fr))`, gap: 4, alignItems: 'end' }}>
        {rows.map(r => {
          const total = r.vals.filter(v => typeof v === 'number').reduce((a, b) => a + b, 0);
          const mean = ordinal ? ordinal.mean(r.vals) : null;
          return (
            <div key={r.country} style={{ display: 'grid', gap: 6 }}>
              <div className="mono" style={{ textAlign: 'center', fontSize: 9.5, fontFamily: 'JetBrains Mono, monospace', color: 'var(--muted)' }}>
                {r.vals[sortBy] == null ? '—' : Math.round(r.vals[sortBy] * 100) + '%'}
              </div>
              <div style={{ display: 'flex', flexDirection: 'column-reverse', height: 300, background: 'var(--surface-2)', overflow: 'hidden' }}
                onMouseEnter={(e) => tip.show(e, buildStackTip(r, q, mean, ordinal))}
                onMouseMove={tip.move}
                onMouseLeave={tip.hide}>
                {r.vals.map((v, i) => {
                  const heightPct = ((v || 0) / Math.max(0.0001, total)) * 100;
                  const color = colorForAnswer(q.answers[i].label, i);
                  return (
                    <span key={i} style={{
                      height: heightPct + '%',
                      background: color,
                      outline: i === sortBy ? '2px solid rgba(0,0,0,.35)' : 'none',
                      outlineOffset: '-2px',
                      display: 'flex', alignItems: 'center', justifyContent: 'center', position: 'relative' }}>
                      {heightPct >= 9 && (
                        <span style={{ fontFamily: 'JetBrains Mono, monospace', fontSize: 9.5, color: labelTextColor(color), fontVariantNumeric: 'tabular-nums', pointerEvents: 'none' }}>
                          {Math.round(v * 100)}
                        </span>
                      )}
                    </span>
                  );
                })}
              </div>
            </div>
          );
        })}
      </div>
      <div className="ucols-labels" style={{ display: 'grid', gridTemplateColumns: `repeat(${rows.length}, minmax(0, 1fr))`, gap: 4, height: 88, marginTop: 4 }}>
        {rows.map(r => (
          <div key={r.country} style={{ position: 'relative' }}>
            <div className="sans" style={{ position: 'absolute', top: 0, right: '50%', transform: 'rotate(-45deg)', transformOrigin: 'top right', whiteSpace: 'nowrap', fontFamily: 'Inter Tight, sans-serif', fontSize: 11.5, color: 'var(--ink-2)' }}>
              {titleCase(r.country)}
            </div>
          </div>
        ))}
      </div>
      <div className="sans" style={{ fontFamily: 'Inter Tight, sans-serif', fontSize: 11, color: 'var(--muted)', marginTop: 14 }}>
        Columns total 100%. Numbers inside bands are percentages; the figure above each column is the selected category.
      </div>
    </div>
  );
}

function QuestionStackedChart({ q, rows, sortBy, tip, ordinal }) {
  return (
    <div data-keep-cols className="uchart-rows" style={{ display: 'grid', gridTemplateColumns: '140px 1fr 78px', gap:10, alignItems:'center'}}>
      {rows.map(r => {
        const total = r.vals.filter(v=>typeof v==='number').reduce((a,b)=>a+b, 0);
        const mean = ordinal ? ordinal.mean(r.vals) : null;
        return (
          <React.Fragment key={r.country}>
            <div className="sans" style={{fontFamily:'Inter Tight, sans-serif', fontSize:12.5}}>
              {titleCase(r.country)}
            </div>
            <div style={{display:'flex', height:26, background:'var(--surface-2)', overflow:'hidden'}}
                 onMouseEnter={(e)=> tip.show(e, buildStackTip(r, q, mean, ordinal))}
                 onMouseMove={tip.move}
                 onMouseLeave={tip.hide}>
              {r.vals.map((v, i) => {
                const widthPct = ((v||0)/Math.max(0.0001,total)) * 100;
                const color = colorForAnswer(q.answers[i].label, i);
                const showLabel = v != null && v > 0;
                return (
                  <span key={i} style={{
                    width: widthPct + '%',
                    background: color,
                    outline: (!ordinal && i===sortBy) ? '2px solid rgba(0,0,0,.35)' : 'none',
                    outlineOffset: '-2px',
                    display:'flex', alignItems:'center', justifyContent:'center',
                    overflow:'visible', position:'relative',
                  }}>
                    {showLabel && (
                      <span style={{
                        fontFamily:'JetBrains Mono, monospace',
                        fontSize: widthPct >= 14 ? 11 : (widthPct >= 7 ? 9.5 : 8.5),
                        color: labelTextColor(color),
                        fontVariantNumeric:'tabular-nums',
                        whiteSpace:'nowrap',
                        pointerEvents:'none',
                      }}>
                        {Math.round(v*100)}%
                      </span>
                    )}
                  </span>
                );
              })}
            </div>
            <div className="mono" style={{textAlign:'right', fontSize:12, fontFamily:'JetBrains Mono, monospace'}}>
              {ordinal
                ? (mean == null ? '—' : <span><strong>{mean.toFixed(2)}</strong><span style={{color:'var(--muted)'}}>/{ordinal.scale}</span></span>)
                : (r.vals[sortBy]==null ? '—' : fmtPct(r.vals[sortBy]))
              }
            </div>
          </React.Fragment>
        );
      })}
    </div>
  );
}

// ----- Question view: CHART -----

function buildStackTip(r, q, mean, ordinal) {
  const parts = q.answers.map((a, i) => {
    const v = r.vals[i];
    return `<div style="display:flex; gap:8px; justify-content:space-between; font-family:Inter Tight, sans-serif; font-size:11.5px;"><span style="opacity:.75; max-width:180px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">${a.label}</span><span style="font-family:JetBrains Mono, monospace;">${v==null?'—':(v*100).toFixed(0)+'%'}</span></div>`;
  }).join('');
  const meanRow = ordinal && mean != null
    ? `<div style="margin-top:6px; padding-top:6px; border-top:1px solid rgba(255,255,255,.2); display:flex; justify-content:space-between; font-family:Inter Tight, sans-serif; font-size:11.5px;"><span><strong>Weighted mean</strong></span><span style="font-family:JetBrains Mono, monospace;"><strong>${mean.toFixed(2)} / ${ordinal.scale}</strong></span></div>`
    : '';
  return `<strong>${titleCase(r.country)}</strong><div style="margin-top:6px; min-width:240px;">${parts}${meanRow}</div>`;
}

function TopCitedChart({ q, countries, rows, tip }) {
  // For each country, find the top-cited answer and its value
  const data = countries.map((c, ci) => {
    let topIdx = -1, topV = -Infinity;
    q.answers.forEach((a, ai) => {
      const v = a.values[ci];
      if (typeof v === 'number' && v > topV) { topV = v; topIdx = ai; }
    });
    return { country: c, topIdx, topV };
  }).filter(d => d.topIdx >= 0 && d.topV > 0).sort((a,b) => b.topV - a.topV);

  // Which answer indices actually win at least one country?
  // Sort alphabetically by label for consistent legend ordering
  const winners = [...new Set(data.map(d => d.topIdx))]
    .sort((a, b) => {
      const la = String(q.answers[a].label).replace(/\s*\(.*?\)\s*$/, '').toLowerCase();
      const lb = String(q.answers[b].label).replace(/\s*\(.*?\)\s*$/, '').toLowerCase();
      return la.localeCompare(lb);
    });
  const maxV = Math.max(...data.map(d => d.topV));
  const axisMax = Math.ceil((maxV * 100) / 10) * 10 / 100;
  const ticks = [];
  for (let t = 0; t <= axisMax + 0.0001; t += 0.1) ticks.push(Math.round(t*100)/100);

  // Title text varies based on question content
  const lowerText = q.text.toLowerCase();
  const isReason = /why|reason|no longer own|never owned|more likely to use/.test(lowerText);
  const titleVerb = lowerText.includes('no longer') ? 'No Longer Own'
                  : lowerText.includes('never owned') ? 'Never Own'
                  : lowerText.includes('more likely to use') ? 'Be More Likely to Use'
                  : 'Cite for';
  const chartTitle = isReason
    ? `The Top-Cited Reason to ${titleVerb} Bitcoin in Each Country`
    : 'The Most-Selected Answer in Each Country';

  return (
    <div className="chart-card" style={{marginBottom:32, padding:'28px 32px 22px'}}>
      <div style={{textAlign:'center', marginBottom:6}}>
        <div className="eyebrow" style={{marginBottom:8, opacity:.7}}>{q.id} · {isReason ? 'top reason' : 'most-selected'} per country</div>
        <h3 style={{fontFamily:'Source Serif 4, serif', fontSize:'clamp(20px, 2.2vw, 28px)', fontWeight:500, letterSpacing:'-0.01em', lineHeight:1.2, margin:'0 auto', maxWidth:'30ch', textTransform:'none'}}>
          {chartTitleFor(q)}
        </h3>
      </div>

      {/* Legend */}
      <div className="sans" style={{display:'flex', flexWrap:'wrap', justifyContent:'center', gap:24, margin:'22px 0 26px',
                                       fontFamily:'Inter Tight, sans-serif', fontSize:12, color:'var(--fg)',
                                       fontWeight:500}}>
        {winners.map(ai => {
          const lbl = titleCaseWords(q.answers[ai].label.replace(/\s*\(.*?\)\s*$/, ''));
          return (
            <span key={ai} style={{display:'inline-flex', alignItems:'center', gap:8, maxWidth:'22ch'}}>
              <span style={{display:'inline-block', width:16, height:16, background: colorForAnswer(q.answers[ai].label, ai), flexShrink:0}}/>
              <span style={{lineHeight:1.2}}>{lbl}</span>
            </span>
          );
        })}
      </div>

      {/* Plot area with vertical gridlines */}
      <div data-keep-cols className="uchart-rows" style={{ display: 'grid', gridTemplateColumns: '170px 1fr 56px', columnGap:14, alignItems:'center', position:'relative'}}>
        {data.map((d, i) => {
          const color = colorForAnswer(q.answers[d.topIdx].label, d.topIdx);
          const pct = (d.topV / axisMax) * 100;
          return (
            <React.Fragment key={d.country}>
              <div className="sans" style={{fontFamily:'Inter Tight, sans-serif', fontSize:13, fontWeight:500, color:'var(--fg)', textAlign:'right'}}>
                {titleCase(d.country)}
              </div>
              <div style={{height:22, background:'transparent', position:'relative', borderBottom:'1px solid var(--rule)'}}
                   onMouseEnter={(e)=> tip.show(e, `<strong>${titleCase(d.country)}</strong><div class="tv">${q.answers[d.topIdx].label} — ${fmtPct(d.topV, 0)}</div>`)}
                   onMouseMove={tip.move}
                   onMouseLeave={tip.hide}>
                {ticks.map((t, ti) => (
                  ti===0 ? null :
                  <div key={ti} style={{position:'absolute', top:0, bottom:0, left:`${(t/axisMax)*100}%`, width:1, background:'var(--rule)', opacity:.45}}/>
                ))}
                <div style={{position:'absolute', top:3, bottom:3, left:0, width: pct+'%', background: color}}/>
              </div>
              <div className="mono" style={{textAlign:'right', fontSize:11.5, fontFamily:'JetBrains Mono, monospace', color:'var(--ink-2)'}}>
                {Math.round(d.topV*100)}%
              </div>
            </React.Fragment>
          );
        })}

        {/* X axis labels row */}
        <div></div>
        <div style={{position:'relative', height:24, marginTop:6}}>
          {ticks.map((t, ti) => (
            <div key={ti} style={{position:'absolute', top:0, left:`${(t/axisMax)*100}%`, transform: ti===0?'translateX(0)':'translateX(-50%)',
                                    fontFamily:'Inter Tight, sans-serif', fontSize:10.5, color:'var(--muted)', whiteSpace:'nowrap'}}>
              {Math.round(t*100)}%
            </div>
          ))}
        </div>
        <div></div>
      </div>

      <p style={{fontFamily:'Inter Tight, sans-serif', fontStyle:'italic', fontSize:13, color:'var(--muted)', marginTop:24, textAlign:'center'}}>
        {isReason
          ? 'Respondents could choose more than one reason to never/no longer own bitcoin.'
          : 'Respondents could select more than one option, so shares need not add to 100%.'}
      </p>
    </div>
  );
}

function QuestionSmallMultiples({ q, countries, rows, sortBy, tip }) {
  // One small bar chart per answer choice, sorted descending by value
  return (
    <div style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap:32}}>
      {q.answers.map((a, ai) => {
        const series = countries.map((c, ci) => ({country:c, v:a.values[ci]}))
          .filter(x => typeof x.v === 'number')
          .sort((x,y) => y.v - x.v);
        const maxV = Math.max(0.05, ...series.map(s=>s.v));
        const color = colorForAnswer(a.label, ai);
        return (
          <div key={ai} style={{border: ai===sortBy?'1px solid var(--ink)':'1px solid var(--rule)',
                                  padding:'16px 18px', background:'var(--surface)'}}>
            <div style={{display:'flex', alignItems:'center', gap:8, marginBottom:10}}>
              <span style={{display:'inline-block', width:14, height:14, background: color}}/>
              <div style={{fontFamily:'Source Serif 4, serif', fontSize:14, fontWeight:500}}>{titleCaseWords(a.label.replace(/\s*\(.*?\)\s*$/,''))}</div>
            </div>
            <div data-keep-cols className="uchart-rows" style={{ display: 'grid', gridTemplateColumns: '110px 1fr 44px', gap:6, alignItems:'center', fontSize:11.5}}>
              {series.map(s => (
                <React.Fragment key={s.country}>
                  <div className="sans" style={{fontFamily:'Inter Tight, sans-serif'}}>{titleCase(s.country)}</div>
                  <div style={{height:10, background:'var(--surface-2)'}}>
                    <div style={{height:'100%', width: ((s.v/maxV)*100)+'%', background: color}}/>
                  </div>
                  <div className="mono" style={{textAlign:'right', fontFamily:'JetBrains Mono, monospace', fontSize:10.5}}>
                    {fmtPct(s.v, 0)}
                  </div>
                </React.Fragment>
              ))}
            </div>
          </div>
        );
      })}
    </div>
  );
}

Object.assign(window, { CountryPage, QuestionPage, QuestionTable, QuestionChart, TopCitedChart });
