/* global React, ReactDOM */
const { useState, useEffect, useMemo, useRef } = React;

// ---------------- Hash routing (full-page letter view) ----------------
function parseHash() {
  const h = (typeof window !== 'undefined' && window.location.hash) || '';
  const m = h.match(/^#letter\/(.+)$/);
  if (m) {
    try { return { type: 'letter', name: decodeURIComponent(m[1]) }; }
    catch (e) { return { type: 'home' }; }
  }
  return { type: 'home' };
}
function useHashRoute() {
  const [route, setRoute] = useState(() => parseHash());
  useEffect(() => {
    const handler = () => setRoute(parseHash());
    window.addEventListener('hashchange', handler);
    return () => window.removeEventListener('hashchange', handler);
  }, []);
  return route;
}

const D = window.PCAOB_DATA;
const Q = window.PCAOB_QUOTES || {};
const URLS = window.PCAOB_LETTER_URLS || {};
const THEMES = window.PCAOB_THEMES || {};
const GROUP_COLORS = {
  'investors':           'var(--g-investors)',
  'accounting':          'var(--g-accounting)',
  'audit-committee':     'var(--g-audit-committee)',
  'preparer':            'var(--g-preparer)',
  'technology-provider': 'var(--g-technology-provider)',
  'advisory-firm':       'var(--g-advisory-firm)',
  'regulators':          'var(--g-regulators)',
  'academic':            'var(--g-academic)',
  'other':               'var(--g-other)',
};
// Group display order — kept in sync with the bar chart (sorted by total letters descending).
const GROUP_ORDER = ['accounting', 'investors', 'audit-committee', 'preparer', 'technology-provider', 'advisory-firm', 'regulators', 'academic', 'other'];

const commenterByName = Object.fromEntries(D.COMMENTERS.map(c => [c.name, c]));

const MATRIX_SHORTS = {
  q1: 'Priorities',
  q2: 'QC 1000',
  q3: 'Reporting',
  q4: 'Standards',
  q5: 'Int\u2019l',
  q6: 'Tech & AI',
  q7: 'Transparency',
};

// Direct letter URLs from spreadsheet; fall back to PCAOB.org search if missing.
function letterUrlFor(name) {
  if (URLS[name]) return URLS[name];
  const q = encodeURIComponent(`site:pcaobus.org "PCAOB No. 2026-001" "${name}"`);
  return `https://www.google.com/search?q=${q}`;
}
function hasDirectLetter(name) { return !!URLS[name]; }

const QUOTE_PREVIEW = 3;

// ---------------- Top nav ----------------
function TopBar() {
  const [active, setActive] = useState('overview');
  const sections = [
    { id: 'overview',  label: 'Overview' },
    { id: 'dashboard', label: 'Dashboard' },
    { id: 'q1',        label: 'Q1' },
    { id: 'q2',        label: 'Q2' },
    { id: 'q3',        label: 'Q3' },
    { id: 'q4',        label: 'Q4' },
    { id: 'q5',        label: 'Q5' },
    { id: 'q6',        label: 'Q6' },
    { id: 'q7',        label: 'Q7' },
    { id: 'other-comments', label: 'Other Comments' },
    { id: 'looking-ahead',  label: 'Looking Ahead' },
    { id: 'appendix',  label: 'Appendix' },
  ];

  useEffect(() => {
    const ids = sections.map(s => s.id);
    let rafId = null;
    function update() {
      rafId = null;
      const trigger = 120; // px from top of viewport
      let current = ids[0];
      for (const id of ids) {
        const el = document.getElementById(id);
        if (!el) continue;
        const top = el.getBoundingClientRect().top;
        if (top - trigger <= 0) current = id;
        else break;
      }
      setActive(prev => prev === current ? prev : current);
    }
    function onScroll() {
      if (rafId == null) rafId = requestAnimationFrame(update);
    }
    update();
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    return () => {
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
      if (rafId != null) cancelAnimationFrame(rafId);
    };
  }, []);

  return (
    <nav className="topbar">
      <div className="topbar__inner">
        <div className="topbar__brand">
          <span className="b1">C. Aubrey Smith Center · UT McCombs</span>
          Comment Letter Analysis
        </div>
        <div className="topbar__nav">
          {sections.map(s => (
            <a key={s.id} href={`#${s.id}`} className={active === s.id ? 'is-active' : ''}>{s.label}</a>
          ))}
        </div>
        <button className="topbar__cta" onClick={() => window.print()}>
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="square"><path d="M6 9V3h12v6M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"/><rect x="6" y="14" width="12" height="8"/></svg>
          Print / PDF
        </button>
      </div>
    </nav>
  );
}

// ---------------- Cover ----------------
function Cover() {
  return (
    <header id="overview" className="cover">
      <div className="cover__grid"></div>
      <div className="cover__inner">
        <div>
          <div className="cover__eyebrow">PCAOB · No. 2026-001 · Strategic Priorities</div>
          <h1>Comment Letter <em>Analysis</em><br/>2026–2030 Strategic Plan</h1>
          <p className="cover__sub">
            What stakeholders told the PCAOB — by question, by stakeholder group, in their own words.
          </p>
          <dl className="cover__meta">
            <dt>Published</dt><dd>May 2026</dd>
            <dt>Prepared by</dt><dd>C. Aubrey Smith Center, UT McCombs School of Business</dd>
            <dt>Comment period</dt><dd>Mar 31 — May 15, 2026</dd>
            <dt>Coverage</dt><dd>68 of 68 letters as of 2026-05-29</dd>
          </dl>
        </div>
        <div className="cover__hero-stat">
          <div className="cover__hero-num">68</div>
          <div className="cover__hero-label">Comment Letters Reviewed</div>
          <div className="cover__hero-sub">
            From investors, accounting firms, preparers and audit committees, audit-technology and advisory firms, academics, regulators, and the public.
          </div>
        </div>
      </div>
    </header>
  );
}

// ---------------- Executive summary ----------------
function ExecSummary() {
  return (
    <section className="section section--paper" id="exec-summary">
      <div className="section__inner">
        <div className="section__label">Executive Summary</div>
        <h2 className="section__title">A diverse chorus — converging on inspection modernization, technology, and transparency.</h2>
        <div className="exec">
          <div>
            <p className="section__lede" style={{margin: 0}}>
              The PCAOB's Request for Public Comment on its 2026–2030 Strategic Priorities drew 68 letters
              spanning nine stakeholder groups — with global network firms (KPMG, PwC, Deloitte, EY, BDO,
              Grant Thornton) and non-affiliate annually inspected firms (RSM, Crowe, Forvis Mazars, Baker
              Tilly, CohnReznick, CBIZ); institutional investors led by the PCAOB's own Investor Advisory
              Group, ICI, ICGN, CII, and CalSTRS; audit committees (Audit Committee Council, Tapestry
              Networks); preparers (US Chamber CCMC, FEI CCR, ASML, ABA); technology providers (Auditchain,
              MindBridge, XBRL US); advisory firms (CPA Club, Johnson Global, ICR); the federal regulator
              FHFA, NASAA (state securities administrators), the AICPA Auditing Standards Board, and a US
              Senate Banking Committee letter from Senator Warren. While viewpoints diverged on the specifics, three themes ran through nearly every
              letter: a sharper, more system-based inspection program; thoughtful integration of AI (the
              highest-coverage theme); and more decision-useful reporting and engagement.
            </p>
          </div>
          <ul className="exec__bullets">
            {D.EXEC_SUMMARY.map((s, i) => <li key={i}>{s}</li>)}
          </ul>
        </div>
      </div>
    </section>
  );
}

// ---------------- Dashboard ----------------
function Dashboard() {
  const totalCovered = D.QUESTIONS.length;
  const themeMax = Math.max(...D.THEMES.map(t => t.weight));

  // matrix: rows = questions, cols = stakeholder groups,
  // value = share of each group's letters that substantively address this question (0-100).
  const groupTotals = useMemo(() => {
    const t = {};
    GROUP_ORDER.forEach(g => {
      const s = D.STAKEHOLDERS.find(x => x.id === g);
      t[g] = s ? s.count : 0;
    });
    return t;
  }, []);
  const matrix = useMemo(() => {
    return D.QUESTIONS.map(q => {
      const row = { id: q.id, num: q.num, short: q.short, cells: {} };
      GROUP_ORDER.forEach(g => {
        const n = (q.groups[g] && q.groups[g].commenters) ? q.groups[g].commenters.length : 0;
        const total = groupTotals[g] || 0;
        row.cells[g] = { count: n, pct: total ? Math.round((n / total) * 100) : 0 };
      });
      return row;
    });
  }, [groupTotals]);
  const matrixMax = 100; // percentages are already normalized 0-100

  return (
    <section className="section section--cream" id="dashboard">
      <div className="section__inner">
        <div className="section__label">At a glance</div>
        <h2 className="section__title">The shape of the comment file.</h2>
        <p className="section__lede">
          Nine stakeholder groups responded. Thirty-six commenters addressed all seven prompts; others zeroed in on
          a single issue. The strongest signal across the file is on technology and AI, with inspection design, transparency, and standard-setting close behind.
        </p>

        <div className="dash">
          <div className="stat">
            <div className="stat__label">Letters</div>
            <div className="stat__num">68</div>
            <div className="stat__sub">Reviewed in this analysis</div>
          </div>
          <div className="stat">
            <div className="stat__label">Groups</div>
            <div className="stat__num">9</div>
            <div className="stat__sub">Stakeholder categories represented</div>
          </div>
          <div className="stat">
            <div className="stat__label">Prompts</div>
            <div className="stat__num">7</div>
            <div className="stat__sub">Numbered RFC questions analyzed</div>
          </div>
          <div className="stat">
            <div className="stat__label">Comprehensive</div>
            <div className="stat__num">36</div>
            <div className="stat__sub">Commenters addressed all 7 questions</div>
          </div>
        </div>

        <div className="dash-grid">
          {/* Left column: stakeholder breakdown + themes */}
          <div style={{display: 'flex', flexDirection: 'column', gap: 28, minWidth: 0}}>
          {/* Stakeholder breakdown */}
          <div className="panel">
            <h3 className="panel__title">Who responded</h3>
            <div className="panel__caption">Letters by stakeholder group · % of total</div>
            <div className="bars">
              {[...D.STAKEHOLDERS].sort((a,b) => b.count - a.count).map(s => {
                const max = Math.max(...D.STAKEHOLDERS.map(x => x.count));
                const w = (s.count / max) * 100;
                return (
                  <div key={s.id} className="bar-row">
                    <div className="bar-row__label">
                      <span className="bar-row__dot" style={{background: GROUP_COLORS[s.id]}}></span>
                      {s.label}
                    </div>
                    <div className="bar-row__track">
                      <div className="bar-row__fill" style={{width: `${w}%`, background: GROUP_COLORS[s.id]}}>
                        {s.share}%
                      </div>
                    </div>
                    <div className="bar-row__count">{s.count}</div>
                  </div>
                );
              })}
            </div>
            <p style={{fontSize: 13, color: 'var(--ink-4)', marginTop: 18, lineHeight: 1.45, marginBottom: 0}}>
              Accounting firms and associations submitted the most letters (22); Advisory Firms is the smallest group (3).
            </p>
          </div>

          {/* Themes */}
          <div className="panel">
            <h3 className="panel__title">Cross-cutting themes</h3>
            <div className="panel__caption">Topics surfaced across multiple letters</div>
            <div className="themes">
              {D.THEMES.map(t => (
                <button
                  key={t.label}
                  type="button"
                  className={`theme theme--btn ${t.weight >= 13 ? 'theme--xl' : ''}`}
                  onClick={() => window.openThemeDrawer && window.openThemeDrawer(t.id)}>
                  {t.label}
                  <span className="theme__count">{t.weight}</span>
                  <svg className="theme__caret" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="square"><path d="M9 18l6-6-6-6"/></svg>
                </button>
              ))}
            </div>
            <p style={{fontSize: 12, color: 'var(--ink-4)', marginTop: 14, fontFamily: 'var(--mono)', letterSpacing: '0.1em', textTransform: 'uppercase'}}>Click any theme to see relevant quotes from the letter file</p>
          </div>
          </div>

          {/* Coverage matrix */}
          <div className="panel">
            <h3 className="panel__title">Question coverage by group</h3>
            <div className="panel__caption">Share of each group's letters addressing each prompt</div>
            <div className="matrix">
              <div></div>
              {GROUP_ORDER.map(g => {
                const s = D.STAKEHOLDERS.find(x => x.id === g);
                return <div key={g} className="matrix__head" title={s.label}><span>{s.label}</span></div>;
              })}
              {matrix.map(row => (
                <React.Fragment key={row.id}>
                  <div className="matrix__row-head">
                    <span className="matrix__row-q">Q{row.num}</span>
                    <span className="matrix__row-text">{MATRIX_SHORTS[row.id]}</span>
                  </div>
                  {GROUP_ORDER.map(g => {
                    const v = row.cells[g] || { count: 0, pct: 0 };
                    const intensity = v.count === 0 ? 0 : 0.25 + (v.pct / 100) * 0.75;
                    return (
                      <div key={g} className="matrix__cell">
                        <div className="matrix__chip" style={{
                          background: v.count === 0 ? 'var(--paper-2)' : GROUP_COLORS[g],
                          opacity: v.count === 0 ? 0.5 : intensity,
                          color: v.count === 0 ? 'var(--ink-4)' : '#fff',
                        }} title={`${v.count} of ${groupTotals[g]} letters in this group (${v.pct}%)`}>{v.count === 0 ? '—' : `${v.pct}%`}</div>
                      </div>
                    );
                  })}
                </React.Fragment>
              ))}
            </div>
            <p style={{fontSize: 13, color: 'var(--ink-4)', marginTop: 18, lineHeight: 1.45, marginBottom: 0}}>
              Each cell shows the share of that group's letters that substantively address each question; cell shading scales with the percentage. Hover for the count.
              Inspection priorities (Q1) and inspection-program changes (Q2) drew engagement from every group.
            </p>
          </div>
        </div>
      </div>
    </section>
  );
}

// ---------------- Commenter chip ----------------
function CChip({ name, focusQ }) {
  const c = commenterByName[name];
  if (!c) return <span className="cchip">{name}</span>;
  return (
    <button
      type="button"
      className="cchip"
      title={`${c.name} — ${c.sub} · Click for quotes & letter`}
      onClick={() => window.openCommenterDrawer && window.openCommenterDrawer(c.name, focusQ)}>
      <span className="cchip__dot" style={{background: GROUP_COLORS[c.group]}}></span>
      <span className="cchip__ref">#{c.ref}</span>
      {c.name}
    </button>
  );
}

// ---------------- Question section ----------------
function QuestionSection({ q, idx }) {
  // Default: no groups shown. 'all' = every group; <group-id> = just that group.
  const [filter, setFilter] = useState(null);
  // group counts available
  const groupCounts = GROUP_ORDER.map(g => ({
    id: g,
    label: D.STAKEHOLDERS.find(s => s.id === g).label,
    count: (q.groups[g] && q.groups[g].commenters) ? q.groups[g].commenters.length : 0,
  })).filter(g => g.count > 0);
  const totalCommenters = groupCounts.reduce((s,g) => s + g.count, 0);

  return (
    <section className="q" id={q.id} data-screen-label={`Q${q.num} ${q.short}`}>
      <div className="section__inner">
        <div className="q__head">
          <div className="q__num">{String(q.num).padStart(2, '0')}</div>
          <div className="q__title-block">
            <div className="q__kicker">Question {q.num} of 7</div>
            <h2 className="q__title">{q.short}</h2>
            <div className="q__prompt">
              <span className="q__prompt-label">PCAOB asked</span>
              {q.prompt}
            </div>
            <p className="q__topline">{q.topline}</p>
          </div>
        </div>

        <div className="filter" role="tablist" aria-label="Filter by stakeholder group">
          <span className="filter__label">View by group</span>
          <button
            className={`filter__chip ${filter === 'all' ? 'is-active' : ''}`}
            onClick={() => setFilter(filter === 'all' ? null : 'all')}>
            All groups <span className="filter__chip__count">{totalCommenters}</span>
          </button>
          {groupCounts.map(g => (
            <button
              key={g.id}
              className={`filter__chip ${filter === g.id ? 'is-active' : ''}`}
              onClick={() => setFilter(filter === g.id ? null : g.id)}
              style={filter === g.id ? {background: GROUP_COLORS[g.id], borderColor: GROUP_COLORS[g.id]} : {}}>
              <span className="filter__chip__dot" style={{background: GROUP_COLORS[g.id]}}></span>
              {g.label}
              <span className="filter__chip__count">{g.count}</span>
            </button>
          ))}
        </div>

        {filter === null ? (
          <div className="groups-empty">
            <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"><circle cx="12" cy="12" r="9"/><path d="M12 8v4M12 16h.01"/></svg>
            <span>Select a stakeholder group above to see how it weighed in — or choose <strong>All groups</strong> to view every perspective.</span>
          </div>
        ) : (
          <div className="groups">
            {GROUP_ORDER.map(gid => {
              const g = q.groups[gid];
              if (!g) return null;
              if (filter !== 'all' && filter !== gid) return null;
              const s = D.STAKEHOLDERS.find(x => x.id === gid);
              return (
                <article
                  key={gid}
                  className="gcard"
                  style={{borderTopColor: GROUP_COLORS[gid]}}>
                  <header className="gcard__head">
                    <h3 className="gcard__name">{s.label}</h3>
                    <span className="gcard__count">{g.commenters.length} {g.commenters.length === 1 ? 'commenter' : 'commenters'}</span>
                  </header>
                  <p className="gcard__intro">{g.intro}</p>
                  <div className="gcard__body">
                    {g.paras.map((p, i) => <p key={i}>{p}</p>)}
                  </div>
                  <div className="gcard__commenters">
                    <div className="gcard__commenters-label">Cited in this section</div>
                    <div className="gcard__commenters-list">
                      {g.commenters.map(c => <CChip key={c} name={c} focusQ={q.id} />)}
                    </div>
                  </div>
                </article>
              );
            })}
          </div>
        )}

        {q.convergence && (
          <div className="convergence">
            <div className="convergence__label">Convergence & divergence</div>
            <p className="convergence__body">{q.convergence}</p>
          </div>
        )}

        <figure className="pullq">
          <blockquote className="pullq__text">{q.quote.text}</blockquote>
          <div className="pullq__attr">{q.quote.attr}</div>
        </figure>

        <div className="pov">
          <div className="pov__label">The Way We See It</div>
          <p className="pov__body">{q.caqView}</p>
        </div>
      </div>
    </section>
  );
}

// ---------------- Other comments ----------------
function OtherComments() {
  return (
    <section className="section section--paper" id="other-comments">
      <div className="section__inner">
        <div className="section__label">Beyond the seven prompts</div>
        <h2 className="section__title">Other comments raised outside the PCAOB's questions.</h2>
        <p className="section__lede">{D.OTHER_COMMENTS.intro}</p>
        <div className="other-grid">
          {D.OTHER_COMMENTS.sections.map((s, i) => (
            <article key={i} className="other-card">
              <h3>{s.title}</h3>
              <p>{s.body}</p>
              <div className="gcard__commenters" style={{borderTop: 'none', paddingTop: 0, marginTop: 0}}>
                <div className="gcard__commenters-label">Raised by</div>
                <div className="gcard__commenters-list">
                  {s.commenters.map(c => <CChip key={c} name={c} focusQ="other" />)}
                </div>
              </div>
            </article>
          ))}
        </div>
        <div className="pov" style={{marginTop: 36}}>
          <div className="pov__label">The Way We See It</div>
          <p className="pov__body">{D.OTHER_COMMENTS.caqView}</p>
        </div>
      </div>
    </section>
  );
}

// ---------------- Looking ahead ----------------
function LookingAhead() {
  return (
    <section className="section section--navy" id="looking-ahead">
      <div className="section__inner">
        <div className="section__label">Looking Ahead</div>
        <h2 className="section__title">Six priorities the Board should carry into the strategic plan.</h2>
        <p className="section__lede">{D.LOOKING_AHEAD.intro}</p>
        <div className="la-grid">
          {D.LOOKING_AHEAD.recs.map(r => (
            <article key={r.num} className="la-card">
              <div className="la-card__num">{String(r.num).padStart(2, '0')}</div>
              <h3 className="la-card__title">{r.title}</h3>
              <p className="la-card__body">{r.body}</p>
            </article>
          ))}
        </div>
        <p className="la-outro">{D.LOOKING_AHEAD.outro}</p>
      </div>
    </section>
  );
}

// ---------------- Appendix ----------------
function Appendix() {
  const [q, setQ] = useState('');
  const [grp, setGrp] = useState('all');

  const filtered = useMemo(() => {
    return D.COMMENTERS.filter(c => {
      if (grp !== 'all' && c.group !== grp) return false;
      if (q && !(c.name.toLowerCase().includes(q.toLowerCase()) || c.sub.toLowerCase().includes(q.toLowerCase()))) return false;
      return true;
    });
  }, [q, grp]);

  function downloadCSV() {
    const rows = [['Ref', 'Commenter', 'Stakeholder Group', 'Sub-Category', 'Q1', 'Q2', 'Q3', 'Q4', 'Q5', 'Q6', 'Q7']];
    D.COMMENTERS.forEach(c => {
      const cov = D.COVERAGE[c.name] || [];
      const groupLabel = D.STAKEHOLDERS.find(s => s.id === c.group).label;
      rows.push([
        c.ref, c.name, groupLabel, c.sub,
        ...['q1','q2','q3','q4','q5','q6','q7'].map(qid => cov.includes(qid) ? '✓' : ''),
      ]);
    });
    const csv = rows.map(r => r.map(v => {
      const s = String(v);
      return s.includes(',') || s.includes('"') ? `"${s.replace(/"/g, '""')}"` : s;
    }).join(',')).join('\n');
    const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url; a.download = 'pcaob-strategic-priorities-commenters.csv';
    a.click();
    URL.revokeObjectURL(url);
  }

  return (
    <section className="section section--white" id="appendix">
      <div className="section__inner">
        <div className="section__label">Appendix</div>
        <h2 className="section__title">All 68 commenters.</h2>
        <p className="section__lede">
          Filter by stakeholder group or search by name. The coverage columns show which of the seven RFC
          prompts each commenter substantively addressed.
        </p>

        <div className="appendix-toolbar">
          <input
            className="appendix-search"
            type="search"
            placeholder="Search commenters or sub-categories…"
            value={q}
            onChange={e => setQ(e.target.value)}
          />
          <button className={`filter__chip ${grp === 'all' ? 'is-active' : ''}`} onClick={() => setGrp('all')}>
            All
            <span className="filter__chip__count">{D.COMMENTERS.length}</span>
          </button>
          {GROUP_ORDER.map(gid => {
            const s = D.STAKEHOLDERS.find(x => x.id === gid);
            const active = grp === gid;
            return (
              <button
                key={gid}
                className={`filter__chip ${active ? 'is-active' : ''}`}
                onClick={() => setGrp(active ? 'all' : gid)}
                style={active ? {background: GROUP_COLORS[gid], borderColor: GROUP_COLORS[gid]} : {}}>
                <span className="filter__chip__dot" style={{background: GROUP_COLORS[gid]}}></span>
                {s.label}
                <span className="filter__chip__count">{s.count}</span>
              </button>
            );
          })}
          <button className="topbar__cta" style={{marginLeft: 'auto'}} onClick={downloadCSV}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="square"><path d="M12 3v14m0 0l-5-5m5 5l5-5M5 21h14"/></svg>
            Download CSV
          </button>
        </div>

        <table className="atable">
          <thead>
            <tr>
              <th>Ref</th>
              <th>Commenter</th>
              <th>Group</th>
              <th>Sub-category</th>
              <th>Question Coverage</th>
            </tr>
          </thead>
          <tbody>
            {filtered.map(c => {
              const groupLabel = D.STAKEHOLDERS.find(s => s.id === c.group).label;
              const cov = D.COVERAGE[c.name] || [];
              return (
                <tr key={c.ref}>
                  <td className="ref">#{String(c.ref).padStart(2, '0')}</td>
                  <td className="name">
                    <button type="button" className="cchip" style={{padding:'4px 10px', background:'transparent', border:'none'}} onClick={() => window.openCommenterDrawer && window.openCommenterDrawer(c.name)}>
                      {c.name}
                    </button>
                  </td>
                  <td>
                    <span className="group-pill" style={{background: `${GROUP_COLORS[c.group]}22`, color: GROUP_COLORS[c.group]}}>
                      <span className="bar-row__dot" style={{background: GROUP_COLORS[c.group]}}></span>
                      {groupLabel}
                    </span>
                  </td>
                  <td style={{color: 'var(--ink-3)'}}>
                    {c.sub}
                    {c.formerPCAOB && <span className="pcaob-tag" title={`Former PCAOB: ${c.formerPCAOB}`}>★ Former PCAOB</span>}
                  </td>
                  <td>
                    <div className="coverage-cells">
                      {['q1','q2','q3','q4','q5','q6','q7'].map(qid => (
                        <span key={qid} className={`cov-cell ${cov.includes(qid) ? 'on' : 'off'}`} title={`Q${qid.slice(1)}`}>
                          {qid.slice(1)}
                        </span>
                      ))}
                    </div>
                  </td>
                </tr>
              );
            })}
            {filtered.length === 0 && (
              <tr><td colSpan="5" style={{textAlign: 'center', color: 'var(--ink-4)', padding: 32}}>No commenters match this filter.</td></tr>
            )}
          </tbody>
        </table>
      </div>
    </section>
  );
}

// ---------------- Footer ----------------
function Footer() {
  return (
    <footer className="footer">
      <div className="footer__inner">
        <div>
          <h4>Comment Letter Analysis · PCAOB No. 2026-001</h4>
          <p style={{margin: 0, maxWidth: '60ch'}}>
            Prepared by the
            {' '}<strong style={{color: 'var(--cream)'}}>C. Aubrey Smith Center</strong> at the McCombs School of
            Business, The University of Texas at Austin. This is a draft analysis of public comment letters
            submitted in response to the PCAOB's Request for Public Comment on its Strategic Priorities
            informing the 2026–2030 Strategic Plan.
          </p>
        </div>
        <div style={{textAlign: 'right'}}>
          <h4>Source materials</h4>
          <p style={{margin: 0}}>
            <a href="https://pcaobus.org/news-events/news-releases/news-release-detail/pcaob-requests-public-comment-on-strategic-priorities" target="_blank" rel="noreferrer">PCAOB Request for Public Comment</a><br/>
            Comment period: Mar 31 – May 15, 2026
          </p>
        </div>
      </div>
    </footer>
  );
}

// ---------------- Quotes list with cap + show-all ----------------
function QuotesList({ quotes }) {
  const [expanded, setExpanded] = useState(false);
  const showAll = expanded || quotes.length <= QUOTE_PREVIEW;
  const visible = showAll ? quotes : quotes.slice(0, QUOTE_PREVIEW);
  const extra = quotes.length - QUOTE_PREVIEW;
  return (
    <>
      <div className="drawer__quotes-label">
        {quotes.length === 1 ? 'Direct quote from the letter' : 'Direct quotes from the letter'}
        {!showAll && <span className="drawer__quotes-count"> · showing top {QUOTE_PREVIEW} of {quotes.length}</span>}
      </div>
      <ol className="drawer__quotes">
        {visible.map((q, i) => <li key={i}>“{q}”</li>)}
      </ol>
      {quotes.length > QUOTE_PREVIEW && (
        <button type="button" className="drawer__show-more" onClick={() => setExpanded(e => !e)}>
          {expanded ? `− Show fewer` : `+ Show ${extra} more ${extra === 1 ? 'quote' : 'quotes'}`}
        </button>
      )}
    </>
  );
}

// ---------------- Commenter drawer ----------------
function Drawer({ state, onClose }) {
  const bodyRef = useRef(null);
  const open = !!state;

  useEffect(() => {
    if (open) document.body.classList.add('drawer-open');
    else document.body.classList.remove('drawer-open');
  }, [open]);

  useEffect(() => {
    function onKey(e) { if (e.key === 'Escape') onClose(); }
    if (open) window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [open, onClose]);

  // Scroll-to-focus when opened with a specific question
  useEffect(() => {
    if (!open || !state.focusQ) return;
    const t = setTimeout(() => {
      const el = bodyRef.current?.querySelector(`[data-q="${state.focusQ}"]`);
      if (el) el.scrollIntoView({ block: 'start', behavior: 'instant' });
    }, 380); // wait for slide-in to settle
    return () => clearTimeout(t);
  }, [open, state]);

  const c = state && commenterByName[state.name];
  const qdata = state && Q[state.name];
  const groupLabel = c && D.STAKEHOLDERS.find(s => s.id === c.group).label;

  return (
    <>
      <div className={`drawer-backdrop ${open ? 'is-open' : ''}`} onClick={onClose}></div>
      <aside className={`drawer drawer--fullscreen ${open ? 'is-open' : ''}`} role="dialog" aria-modal="true" aria-label="Commenter detail">
        {c && (
          <>
            <header className="drawer__header">
              <button className="drawer__close" onClick={onClose} aria-label="Close">
                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="square"><path d="M18 6L6 18M6 6l12 12"/></svg>
              </button>
              <div className="drawer__ref">
                <span className="drawer__ref-num">#{String(c.ref).padStart(2,'0')}</span>
                Commenter
              </div>
              <h3 className="drawer__name">{c.name}</h3>
              <div className="drawer__meta">
                <span className="drawer__meta-pill">
                  <span style={{width:8, height:8, background: GROUP_COLORS[c.group], display:'inline-block', borderRadius: 2}}></span>
                  {groupLabel}
                </span>
                {c.sub && <span className="drawer__meta-pill">{c.sub}</span>}
                {c.formerPCAOB && <span className="drawer__meta-pill drawer__meta-pill--pcaob" title={`Former PCAOB: ${c.formerPCAOB}`}>★ Former PCAOB</span>}
                {qdata?.date && <span className="drawer__meta-pill">Submitted {qdata.date}</span>}
              </div>
              <a className="drawer__link" href={letterUrlFor(c.name)} target="_blank" rel="noreferrer">
                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="square"><path d="M14 3h7v7m0-7L10 14M19 14v6a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1h6"/></svg>
                {hasDirectLetter(c.name) ? 'Read the letter (PDF) on PCAOB.org' : 'Find letter on PCAOB.org'}
              </a>
            </header>
            <div className="drawer__body" ref={bodyRef}>
              {qdata?.caqSummary && (
                <div className="drawer__caq">
                  <div className="drawer__caq-label">Summary of this letter</div>
                  <p className="drawer__caq-body">{qdata.caqSummary}</p>
                </div>
              )}
              {D.QUESTIONS.map(q => {
                const entry = qdata?.questions?.[q.id] || { summary: '', quotes: [] };
                const addressed = entry.summary || entry.quotes.length;
                const highlight = state.focusQ === q.id;
                return (
                  <section key={q.id} className={`drawer__q ${highlight ? 'is-highlight' : ''}`} data-q={q.id}>
                    <div className="drawer__q-head">
                      <span className="drawer__q-num">Q{q.num}</span>
                      <h4 className="drawer__q-title">{q.short}</h4>
                    </div>
                    {addressed ? (
                      <>
                        {entry.summary && <p className="drawer__summary">{entry.summary}</p>}
                        {entry.quotes.length > 0 && <QuotesList quotes={entry.quotes} />}
                      </>
                    ) : (
                      <div className="drawer__nomention">— Not addressed in this letter</div>
                    )}
                  </section>
                );
              })}
              {qdata?.other && (qdata.other.summary || qdata.other.quotes.length) ? (
                <section className="drawer__q" data-q="other">
                  <div className="drawer__q-head">
                    <span className="drawer__q-num" style={{color:'var(--navy-700)', borderColor:'var(--navy-700)'}}>OTH</span>
                    <h4 className="drawer__q-title">Other comments</h4>
                  </div>
                  {qdata.other.summary && <p className="drawer__summary">{qdata.other.summary}</p>}
                  {qdata.other.quotes.length > 0 && <QuotesList quotes={qdata.other.quotes} />}
                </section>
              ) : null}
            </div>
          </>
        )}
      </aside>
    </>
  );
}

// ---------------- Theme drawer ----------------
function ThemeDrawer({ state, onClose }) {
  const open = !!state;
  useEffect(() => {
    if (open) document.body.classList.add('drawer-open');
    return () => document.body.classList.remove('drawer-open');
  }, [open]);
  useEffect(() => {
    function onKey(e) { if (e.key === 'Escape') onClose(); }
    if (open) window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [open, onClose]);

  const theme = state && THEMES[state.themeId];

  return (
    <>
      <div className={`drawer-backdrop ${open ? 'is-open' : ''}`} onClick={onClose}></div>
      <aside className={`drawer ${open ? 'is-open' : ''}`} role="dialog" aria-modal="true" aria-label="Theme detail">
        {theme && (
          <>
            <header className="drawer__header">
              <button className="drawer__close" onClick={onClose} aria-label="Close">
                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="square"><path d="M18 6L6 18M6 6l12 12"/></svg>
              </button>
              <div className="drawer__ref">
                <span className="drawer__ref-num" style={{background: 'var(--gold-soft)'}}>THEME</span>
                Cross-cutting topic
              </div>
              <h3 className="drawer__name">{theme.label}</h3>
            </header>
            <div className="drawer__body">
              <p style={{fontFamily: 'var(--serif)', fontSize: 16, color: 'var(--ink-3)', fontStyle: 'italic', margin: '0 0 24px', lineHeight: 1.5}}>
                A handful of the most direct quotes from the letter file on this theme. Click a commenter's name to open their full letter view.
              </p>
              <ul className="theme-quotes">
                {theme.quotes.map((q, i) => {
                  const c = commenterByName[q.name];
                  return (
                    <li key={i}>
                      <p className="theme-quotes__text">“{q.text}”</p>
                      <button
                        type="button"
                        className="theme-quotes__attr"
                        onClick={() => {
                          onClose();
                          setTimeout(() => window.openCommenterDrawer && window.openCommenterDrawer(q.name, q.qid === 'other' ? null : q.qid), 200);
                        }}>
                        <span className="theme-quotes__attr-dot" style={{background: c ? GROUP_COLORS[c.group] : 'var(--ink-4)'}}></span>
                        {q.name}
                        <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="square"><path d="M9 18l6-6-6-6"/></svg>
                      </button>
                    </li>
                  );
                })}
              </ul>
            </div>
          </>
        )}
      </aside>
    </>
  );
}

// ---------------- App ----------------
// ---------------- Full-page letter view ----------------
function LetterPage({ name }) {
  const c = commenterByName[name];
  const qdata = Q[name];
  const groupLabel = c && D.STAKEHOLDERS.find(s => s.id === c.group)?.label;

  useEffect(() => {
    document.body.classList.add('letter-page-mode');
    return () => document.body.classList.remove('letter-page-mode');
  }, []);

  if (!c) {
    return (
      <main className="letter-page">
        <div className="letter-page__inner">
          <a className="letter-page__back" href="#" onClick={(e) => { e.preventDefault(); window.location.hash = ''; }}>← Back to overview</a>
          <h1 className="letter-page__notfound">Letter not found</h1>
          <p>No commenter matches "{name}". <a href="#" onClick={(e) => { e.preventDefault(); window.location.hash = ''; }}>Return to the overview.</a></p>
        </div>
      </main>
    );
  }

  return (
    <main className="letter-page">
      <div className="letter-page__inner">
        <a className="letter-page__back" href="#" onClick={(e) => { e.preventDefault(); window.location.hash = ''; }}>← Back to overview</a>
        <header className="letter-page__header">
          <div className="letter-page__ref">
            <span className="letter-page__ref-num">#{String(c.ref).padStart(2,'0')}</span>
            Commenter
          </div>
          <h1 className="letter-page__name">{c.name}</h1>
          <div className="letter-page__meta">
            <span className="drawer__meta-pill">
              <span style={{width:8, height:8, background: GROUP_COLORS[c.group], display:'inline-block', borderRadius: 2}}></span>
              {groupLabel}
            </span>
            {c.sub && <span className="drawer__meta-pill">{c.sub}</span>}
            {c.formerPCAOB && <span className="drawer__meta-pill drawer__meta-pill--pcaob" title={`Former PCAOB: ${c.formerPCAOB}`}>★ Former PCAOB</span>}
            {qdata?.date && <span className="drawer__meta-pill">Submitted {qdata.date}</span>}
          </div>
          <a className="letter-page__link" href={letterUrlFor(c.name)} target="_blank" rel="noreferrer">
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="square"><path d="M14 3h7v7m0-7L10 14M19 14v6a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1h6"/></svg>
            {hasDirectLetter(c.name) ? 'Read the letter (PDF) on PCAOB.org' : 'Find letter on PCAOB.org'}
          </a>
        </header>

        <article className="letter-page__body">
          {qdata?.caqSummary && (
            <section className="letter-page__caq">
              <div className="letter-page__caq-label">Summary of this letter</div>
              <p className="letter-page__caq-body">{qdata.caqSummary}</p>
            </section>
          )}
          {D.QUESTIONS.map(q => {
            const entry = qdata?.questions?.[q.id] || { summary: '', quotes: [] };
            const addressed = entry.summary || entry.quotes.length;
            return (
              <section key={q.id} className="letter-page__q">
                <div className="letter-page__q-head">
                  <span className="letter-page__q-num">Q{q.num}</span>
                  <h2 className="letter-page__q-title">{q.short}</h2>
                </div>
                {addressed ? (
                  <>
                    {entry.summary && <p className="letter-page__summary">{entry.summary}</p>}
                    {entry.quotes.length > 0 && (
                      <ol className="letter-page__quotes">
                        {entry.quotes.map((quote, i) => <li key={i}>{quote}</li>)}
                      </ol>
                    )}
                  </>
                ) : (
                  <p className="letter-page__not-addressed">This question was not substantively addressed in the letter.</p>
                )}
              </section>
            );
          })}
        </article>

        <footer className="letter-page__footer">
          <a className="letter-page__back" href="#" onClick={(e) => { e.preventDefault(); window.location.hash = ''; }}>← Back to overview</a>
        </footer>
      </div>
    </main>
  );
}

function App() {
  const route = useHashRoute();
  const [drawer, setDrawer] = useState(null);
  const [themeDrawer, setThemeDrawer] = useState(null);
  useEffect(() => {
    window.openCommenterDrawer = (name, focusQ) => setDrawer({ name, focusQ });
    window.openThemeDrawer = (themeId) => setThemeDrawer({ themeId });
    return () => { delete window.openCommenterDrawer; delete window.openThemeDrawer; };
  }, []);

  if (route.type === 'letter') {
    return <LetterPage name={route.name} />;
  }

  return (
    <>
      <TopBar />
      <Cover />
      <ExecSummary />
      <Dashboard />
      {D.QUESTIONS.map((q, i) => <QuestionSection key={q.id} q={q} idx={i} />)}
      <OtherComments />
      <LookingAhead />
      <Appendix />
      <Footer />
      <Drawer state={drawer} onClose={() => setDrawer(null)} />
      <ThemeDrawer state={themeDrawer} onClose={() => setThemeDrawer(null)} />
    </>
  );
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
