/* Result renderers for the Castle Connolly search. Consumes the plain-data sections in data.jsx (see DATA-SCHEMA.md). */
(function () {
  const Icon = window.Icon;
  const { useState, useMemo } = React;

  /* "text with [1][2] markers" → React nodes with citation chips (handled globally by the App cite popover) */
  /* Consecutive markers collapse into one quiet group; whitespace before a marker is dropped so punctuation hugs the text. */
  function cite(text) {
    if (!text) return null;
    const src = String(text).replace(/\s+(?=\[\d+\])/g, '');
    const parts = src.split(/((?:\[\d+\])+)/g);
    return parts.map((p, i) => {
      if (/^(?:\[\d+\])+$/.test(p)) {
        const nums = [...p.matchAll(/\[(\d+)\]/g)].map((m) => m[1]);
        return <Cites key={i} nums={nums} />;
      }
      return <React.Fragment key={i}>{p}</React.Fragment>;
    });
  }
  const Cites = ({ nums }) => (nums && nums.length ? <sup className="cites">{nums.map((n) => <a key={n} href={'#src-' + n} className="cite">{n}</a>)}</sup> : null);
  const Cite = ({ n }) => (n ? <Cites nums={[String(n)]} /> : null);

  const Badge = ({ kind, children, title }) => (
    <span className={'badge badge--' + kind} title={title}>{children}</span>
  );
  const TopMark = () => (
    <svg viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M8 1.5l1.9 3.9 4.3.6-3.1 3 .7 4.3L8 11.3l-3.8 2 .7-4.3-3.1-3 4.3-.6z" fill="currentColor" /></svg>
  );

  function insuranceSummary(list, active) {
    if (!list || !list.length) return 'Insurance not listed';
    const ordered = active && list.includes(active) ? [active, ...list.filter((x) => x !== active)] : list;
    const shown = ordered.slice(0, 2).join(', ');
    const rest = ordered.length - 2;
    return 'Accepts ' + shown + (rest > 0 ? ' +' + rest + ' more' : '');
  }

  const milesOf = (d) => { const m = /([\d.]+)\s*mi/.exec(d || ''); return m ? parseFloat(m[1]) : null; };
  const maxMiles = (d) => { const m = /(\d+)/.exec(d || ''); return m ? parseInt(m[1], 10) : null; };

  /* Enhanced Profile disclosure: castleconnolly.com's own wording. */
  const ENHANCED_NOTE = 'Enhanced Profile: profile upgrade purchased by a doctor to display additional information; this does not impact Top Doctor or Top Dentist selection.';

  function ProviderCard({ p, activeInsurance, dentist, ctx }) {
    const [disclose, setDisclose] = useState(false);
    const c = ctx || {};
    const program = (p.programs || []).find((x) => x === 'Top Doctor' || x === 'Top Dentist');
    const user = !!c.user;
    const plan = user && c.profile ? (dentist && c.profile.family && c.profile.family[0] && c.profile.family[0].dentalPlan ? c.profile.family[0].dentalPlan : c.profile.plan) : null;
    const inNetwork = plan ? (p.insurance || []).includes(plan) : null;
    const isSaved = user && c.saved && c.saved.has(p.id);
    const featured = p.featured || p.enhancedProfile;
    const profileUrl = p.profileUrl || 'https://www.castleconnolly.com/';
    return (
      <article className={'pcard' + (featured ? ' pcard--featured' : '')} aria-label={p.name}>
        {featured && (
          <>
            <button type="button" className={'pcard__featured' + (disclose ? ' is-open' : '')} aria-expanded={disclose} onClick={() => setDisclose((d) => !d)}>Enhanced Profile <span className="pcard__featured-i">{Icon.Info()}</span></button>
            {disclose && <div className="pcard__disclose" role="note">{ENHANCED_NOTE}</div>}
          </>
        )}
        <div className="pcard__head">
          <div className="pcard__avatar" aria-hidden="true">{p.initials}</div>
          <div className="pcard__id">
            <h3 className="pcard__name">{p.name}</h3>
            <div className="pcard__spec">{p.specialty}{p.subspecialty ? ' · ' + p.subspecialty : ''}</div>
            <div className="pcard__badges">
              {program && <Badge kind="top" title={(program === 'Top Doctor' ? 'Castle Connolly Top Doctor: about 7% of U.S. physicians, peer nominated and research vetted' : 'Castle Connolly Top Dentist: top 10% of dentists nationwide') + (p.boardCertified ? '. Board certified.' : '')}><TopMark />{program}{p.topDoctorSince ? ' since ' + p.topDoctorSince : ''}</Badge>}
              {inNetwork === true && <Badge kind="net" title={'Accepts your plan (' + plan + ')'}>{Icon.ShieldCheck()}In network · {plan}</Badge>}
              {inNetwork === false && <Badge kind="out" title={'Does not list your plan (' + plan + ')'}>Out of network</Badge>}
              {(p.acceptingNew || p.newPatients) && <Badge kind="ok">Accepting new patients</Badge>}
              {(p.programs || []).includes('Accolades') && <Badge kind="soft" title="Practice holds a Castle Connolly Accolade for concentration of Top Doctors">Accolades</Badge>}
            </div>
          </div>
        </div>
        <dl className="pcard__lines">
          <div><dt>{Icon.MapPin()}</dt><dd title={[p.address, p.city + ', ' + p.state + ' ' + p.zip].filter(Boolean).join(', ')}><strong>{p.hospital || p.practice}</strong>{p.practice && p.hospital ? <span className="pcard__muted"> · {p.practice}</span> : null}{p.distance ? <span className="pcard__dist" title={user && c.profile ? 'Distance from your home ZIP, ' + (c.profile.zip || '02116') : 'Distance from Boston, MA 02116'}>{p.distance}{user && c.profile ? ' from home' : ' away'}</span> : null}</dd></div>
          {inNetwork !== true && <div><dt>{Icon.Shield()}</dt><dd>{insuranceSummary(p.insurance, activeInsurance || plan)}</dd></div>}
          <div><dt>{Icon.Calendar()}</dt><dd>{p.nextAvailable ? <>Next available <strong>{p.nextAvailable}</strong></> : 'Call for availability'}<span className="pcard__muted">{[p.telehealth ? 'Telehealth' : null, p.education || null, p.yearsExperience ? p.yearsExperience + ' yrs' : null, (p.languages || []).filter((l) => l !== 'English').length ? 'Speaks ' + (p.languages || []).filter((l) => l !== 'English').join(', ') : null].filter(Boolean).map((x) => ' · ' + x).join('')}</span></dd></div>
        </dl>
        <div className="pcard__actions">
          {user && p.bookOnline && <a className="btn btn--primary" href={profileUrl} target="_blank" rel="noreferrer">{Icon.Calendar()}<span>Book online</span></a>}
          <a className={'btn' + (user && p.bookOnline ? '' : ' btn--primary')} href={profileUrl} target="_blank" rel="noreferrer">View profile</a>
          {user
            ? <button type="button" className={'btn' + (isSaved ? ' is-on' : '')} aria-pressed={!!isSaved} title={isSaved ? 'Remove from Saved' : 'Save this ' + (dentist ? 'dentist' : 'doctor')} onClick={() => c.toggleSave && c.toggleSave(p.id)}>{Icon.Bookmark()}<span>{isSaved ? 'Saved' : 'Save'}</span></button>
            : <button type="button" className="btn" onClick={c.requireSignIn} title="Sign in to save this doctor and see if they take your plan">{Icon.Bookmark()}<span>Save · sign in</span></button>}
          {p.phone && <a className="btn btn--quiet pcard__call" href={'tel:' + p.phone.replace(/[^0-9+]/g, '')} title={p.phone} aria-label={'Call ' + p.phone}>{Icon.Phone()}<span>Call</span></a>}
        </div>
      </article>
    );
  }

  const SPECIALTIES = ['Cardiology', 'Dermatology', 'Endocrinology', 'General Dentistry', 'Pediatric Dentistry', 'Primary Care', 'Orthopedics', 'Oncology'];
  const INSURERS = ['Aetna', 'Blue Cross Blue Shield of MA', 'Cigna', 'Harvard Pilgrim', 'Medicare', 'Tufts Health Plan', 'UnitedHealthcare', 'Delta Dental of Massachusetts', 'MetLife', 'Guardian'];

  function collect(items, key) {
    const s = new Set();
    (items || []).forEach((p) => (Array.isArray(p[key]) ? p[key] : [p[key]]).forEach((v) => v && s.add(v)));
    return [...s].sort();
  }

  /* Facet chips shared by the landing finder and the results filter bar: a pill that toggles, or opens a small menu. */
  function useFacetMenu() {
    const [menu, setMenu] = useState(null);
    const wrap = React.useRef(null);
    React.useEffect(() => { if (!menu) return; const close = (e) => { if (wrap.current && !wrap.current.contains(e.target)) setMenu(null); }; document.addEventListener('mousedown', close); return () => document.removeEventListener('mousedown', close); }, [menu]);
    return { menu, setMenu, wrap };
  }
  function FacetChip({ id, label, value, icon, children, on, onClick, menu, setMenu, wide }) {
    const open = menu === id;
    return (
      <div className="smart__chipwrap">
        <button type="button" className={'smart__chip' + (on ? ' is-on' : '') + (open ? ' is-open' : '')} onClick={onClick || (() => setMenu((m) => (m === id ? null : id)))} aria-expanded={children ? open : undefined} aria-pressed={onClick ? !!on : undefined}>{on && onClick ? Icon.Check() : icon || null}<span>{label}</span>{value ? <b>{value}</b> : null}{children ? Icon.ChevronDown() : null}</button>
        {children && open && <div className={'smart__menu' + (wide ? ' smart__menu--more' : '')} role="menu">{children}</div>}
      </div>
    );
  }
  function FacetOpt({ on, label, onPick }) {
    return <button type="button" role="menuitemradio" aria-checked={on} className={'smart__opt' + (on ? ' is-on' : '')} onClick={onPick}>{on ? Icon.Check() : <span className="smart__optgap" />}<span>{label}</span></button>;
  }
  function FacetMore({ groups, values, onSet }) {
    return groups.map(([k, label, opts]) => (
      <div key={k} className="smart__more"><div className="smart__morelabel">{label}</div><div className="smart__moreopts">{opts.map((v) => <button key={v} type="button" className={'smart__pill' + ((values[k] || opts[0]) === v ? ' is-on' : '')} onClick={() => onSet(k, v)}>{v}</button>)}</div></div>
    ));
  }

  const DISTANCES = ['5 mi', '10 mi', '25 mi', '50 mi', 'Any distance'];
  /* Results filter bar: the same card and chip row as the landing finder, with Sort by where the Find button was. Every control acts on the list live. */
  function FilterBar({ items, filters, onChange, dentist }) {
    const specialties = useMemo(() => collect(items, 'specialty'), [items]);
    const insurers = useMemo(() => collect(items, 'insurance'), [items]);
    const languages = useMemo(() => collect(items, 'languages').filter((l) => l !== 'English'), [items]);
    const hospitals = useMemo(() => collect(items, 'hospital'), [items]);
    const { menu, setMenu, wrap } = useFacetMenu();
    const set = (k, v) => onChange({ ...filters, [k]: v });
    const specialtyValue = filters.specialty || (specialties.length === 1 ? specialties[0] : '');
    const from = filters.__from || {};
    const tag = (t, title) => <em title={title}>{t}</em>;
    const insTag = from.insurance === 'family' ? tag('family plan', 'From your profile: family member\u2019s plan') : from.insurance === 'card' ? tag('card', 'From your insurance card') : from.insurance ? tag('profile', 'From your profile') : null;
    const locTag = from.location === 'device' ? tag('device', 'From your device') : from.location ? tag('profile', 'From your profile') : tag('near you', 'Boston, MA 02116');
    const isAny = (k) => !filters[k] || filters[k] === 'Any';
    const moreOn = !isAny('gender') || !isAny('language') || !isAny('hospital');
    const base = filters.__base || {};
    const dirty = ['specialty', 'insurance', 'topOnly', 'acceptingNew', 'bookOnline', 'telehealth', 'distance', 'sort'].some((k) => (filters[k] || '') !== (base[k] || '')) || moreOn;
    const chip = (props) => <FacetChip menu={menu} setMenu={setMenu} {...props} />;
    return (
      <div className="finder-wrap finder-wrap--filters" role="group" aria-label="Refine providers" ref={wrap}>
        <div className="finder finder--filters">
          <label className="finder__cell finder__cell--q">
            <span className="finder__label">Specialty</span>
            <span className="finder__inputwrap">{Icon.Search()}<select value={specialtyValue} onChange={(e) => set('specialty', e.target.value)}><option value="">All specialties</option>{specialties.map((s) => <option key={s} value={s}>{s}</option>)}</select></span>
          </label>
          <label className="finder__cell finder__cell--loc">
            <span className="finder__label">Location{locTag}</span>
            <span className="finder__inputwrap">{Icon.MapPin()}<input value={filters.location || ''} onChange={(e) => set('location', e.target.value)} placeholder="City or ZIP" /></span>
          </label>
          <label className="finder__cell finder__cell--ins">
            <span className="finder__label">Insurance{insTag}</span>
            <span className="finder__inputwrap">{Icon.Shield()}<select value={filters.insurance || ''} onChange={(e) => set('insurance', e.target.value)}><option value="">Any insurance</option>{insurers.map((s) => <option key={s} value={s}>{s}</option>)}</select></span>
          </label>
          <label className="finder__cell finder__cell--sort">
            <span className="finder__label">Sort by</span>
            <span className="finder__inputwrap">{Icon.Sliders()}<select value={filters.sort || 'top'} onChange={(e) => set('sort', e.target.value)}><option value="top">Most relevant</option><option value="distance">Closest</option><option value="available">Next available</option></select></span>
          </label>
        </div>
        <div className="finder__chips">
          {chip({ id: 'distance', label: 'Distance', value: filters.distance || 'Any distance', children: DISTANCES.map((d) => <FacetOpt key={d} label={d} on={(filters.distance || 'Any distance') === d} onPick={() => { set('distance', d); setMenu(null); }} />) })}
          {chip({ id: 'new', label: 'Accepting new patients', on: !!filters.acceptingNew, onClick: () => set('acceptingNew', !filters.acceptingNew) })}
          {chip({ id: 'book', label: 'Online booking', on: !!filters.bookOnline, onClick: () => set('bookOnline', !filters.bookOnline) })}
          {chip({ id: 'tele', label: 'Telehealth', on: !!filters.telehealth, onClick: () => set('telehealth', !filters.telehealth) })}
          {chip({ id: 'top', label: dentist ? 'Top Dentists only' : 'Top Doctors only', on: !!filters.topOnly, onClick: () => set('topOnly', !filters.topOnly) })}
          {chip({ id: 'more', label: 'More filters', icon: Icon.Sliders(), on: moreOn, wide: true, children: <FacetMore groups={[['gender', 'Gender', ['Any', 'Woman / Female', 'Man / Male']], ['language', 'Language', ['Any', ...languages]], ['hospital', 'Affiliated hospital', ['Any', ...hospitals]]]} values={filters} onSet={set} /> })}
          {dirty && <button type="button" className="filter__clear" onClick={() => onChange({ ...base, location: filters.location, __base: filters.__base, __from: filters.__from })}>Reset</button>}
        </div>
      </div>
    );
  }

  function applyFilters(items, f) {
    return (items || []).filter((p) => {
      if (f.specialty && p.specialty !== f.specialty) return false;
      if (f.insurance && !(p.insurance || []).includes(f.insurance)) return false;
      if (f.topOnly && !(p.programs || []).some((x) => x === 'Top Doctor' || x === 'Top Dentist')) return false;
      if (f.acceptingNew && !(p.acceptingNew || p.newPatients)) return false;
      if (f.bookOnline && !p.bookOnline) return false;
      if (f.telehealth && !p.telehealth) return false;
      if (f.gender && f.gender !== 'Any' && p.gender !== (f.gender === 'Woman / Female' ? 'F' : 'M')) return false;
      if (f.language && f.language !== 'Any' && !(p.languages || []).includes(f.language)) return false;
      if (f.hospital && f.hospital !== 'Any' && (p.hospital || p.practice) !== f.hospital) return false;
      const mx = maxMiles(f.distance), mi = milesOf(p.distance);
      if (mx && mi !== null && mi > mx) return false;
      return true;
    }).sort((a, b) => (b.featured ? 1 : 0) - (a.featured ? 1 : 0));
  }

  const SORT_LABELS = { top: (d) => 'Sorted by ' + (d ? 'Top Dentist' : 'Top Doctor') + ' status, then distance', distance: () => 'Sorted by distance', available: () => 'Sorted by next available appointment' };
  const availDate = (p) => { const t = Date.parse((p.nextAvailable || '') + ' 2026'); return isNaN(t) ? Infinity : t; };
  const isTop = (p) => (p.programs || []).some((x) => x === 'Top Doctor' || x === 'Top Dentist') ? 0 : 1;
  /* Search preferences (account concept): the chosen sort reorders every provider list live; "book online first" floats bookable doctors. */
  function sortByPrefs(items, prefs) {
    if (!prefs) return items;
    const custom = prefs.sort && prefs.sort !== 'top';
    if (!custom && !prefs.bookOnlineFirst) return items;
    const key = (p) => prefs.sort === 'distance' ? (milesOf(p.distance) === null ? Infinity : milesOf(p.distance)) : prefs.sort === 'available' ? availDate(p) : isTop(p) * 1000 + (milesOf(p.distance) || 0);
    return [...items].sort((a, b) => (prefs.bookOnlineFirst ? ((b.bookOnline ? 1 : 0) - (a.bookOnline ? 1 : 0)) : 0) || key(a) - key(b));
  }
  function ProvidersSection({ section, filters, onFilters, dentist, ctx }) {
    const f = filters || {};
    const prefs = f.sort ? { ...(ctx && ctx.prefs) || {}, sort: f.sort } : (ctx && ctx.prefs);
    const prefSort = !!(prefs && ((prefs.sort && prefs.sort !== 'top') || prefs.bookOnlineFirst));
    const shown = sortByPrefs(applyFilters(section.items, f), prefs);
    const prefNote = prefSort ? (SORT_LABELS[prefs.sort] || SORT_LABELS.top)(dentist) + (prefs.bookOnlineFirst ? ', doctors who book online first' : '') : null;
    const base = (f.__base || {});
    const refined = ['specialty', 'insurance', 'topOnly', 'acceptingNew', 'distance'].some((k) => (f[k] || '') !== (base[k] || ''));
    const noun = dentist ? 'dentists' : 'doctors';
    return (
      <div className="providers">
        <FilterBar items={section.items} filters={f} onChange={onFilters} dentist={dentist} />
        <div className="providers__count">
          {refined
            ? <>Showing <strong>{shown.length}</strong> of the <strong>{section.items.length}</strong> nearest {noun} that match your filters{f.insurance ? ' (' + f.insurance + ')' : ''}</>
            : <>Showing <strong>{section.quick ? 'the top ' : ''}{shown.length}</strong> of <strong>{section.count || section.items.length}</strong> {noun}{f.insurance ? ' who accept ' + f.insurance : ''}{f.location ? ' near ' + f.location : ''}</>}
          {prefNote ? <> · {prefNote}{!f.sort && <em className="providers__pref" title="From your Search preferences">your preference</em>}</> : section.note ? <> · {cite(section.note)}</> : (section.items && section.items[0] && section.items[0].sourceNum ? <> · Directory records<Cite n={section.items[0].sourceNum} /></> : null)}
        </div>
        {shown.length === 0 && <div className="providers__empty">No providers match every filter. Widen the distance, clear insurance, or reset the filters.</div>}
        <div className="providers__grid">
          {shown.map((p) => <ProviderCard key={p.id} p={p} activeInsurance={f.insurance} dentist={dentist} ctx={ctx} />)}
        </div>
      </div>
    );
  }

  function SaveButton({ id, kind, label, ctx, compact }) {
    const c = ctx || {};
    if (!c.user) return <button type="button" className={'btn' + (compact ? ' btn--quiet' : '')} onClick={c.requireSignIn} title={'Sign in to save this ' + label}>{Icon.Bookmark()}<span>Save · sign in</span></button>;
    const set = kind === 'hospital' ? c.savedH : c.savedT;
    const on = !!(set && set.has(id));
    const toggle = kind === 'hospital' ? c.toggleSaveH : c.toggleSaveT;
    return <button type="button" className={'btn' + (compact ? ' btn--quiet' : '') + (on ? ' is-on' : '')} aria-pressed={on} onClick={() => toggle && toggle(id)} title={on ? 'Remove from Saved' : 'Save this ' + label}>{Icon.Bookmark()}<span>{on ? 'Saved' : 'Save'}</span></button>;
  }
  function HospitalsSection({ section, ctx }) {
    return (
      <div className="hospitals">
        {section.items.map((h) => (
          <article key={h.id} className="hcard">
            <div className="hcard__icon">{Icon.MapPin()}</div>
            <div className="hcard__body">
              <h3 className="hcard__name">{h.name}<Cite n={h.sourceNum} /></h3>
              <div className="pcard__muted">{h.city}, {h.state} · {h.distance}{h.phone ? ' · ' + h.phone : ''}</div>
              <div className="pcard__badges">
                {(h.programs || []).includes('Top Hospital') && <Badge kind="top" title="Castle Connolly Top Hospital, ranked by procedure or service line"><TopMark />Top Hospital{h.tier ? ' · ' + h.tier : ''}</Badge>}
                {(h.programs || []).includes('Accolades') && <Badge kind="soft" title="Castle Connolly Accolade: highest concentration of Top Doctors">Accolades</Badge>}
                {h.topDoctorsCount ? <Badge kind="soft">{h.topDoctorsCount} Top Doctors on staff</Badge> : null}
                {h.type ? <Badge kind="soft">{h.type}</Badge> : null}
              </div>
              {h.note ? <p className="hcard__note">{cite(h.note)}</p> : null}
              {h.rankings && h.rankings.length > 0 && (
                <ul className="hcard__ranks">
                  {h.rankings.map((r, i) => <li key={i}><strong>{typeof r.rank === 'number' ? '#' + r.rank + ' ' + (r.scope || 'National') : r.rank}</strong> · {r.procedure}</li>)}
                </ul>
              )}
            </div>
            <div className="hcard__actions"><a className="btn btn--primary" href={h.url || 'https://www.castleconnolly.com/'} target="_blank" rel="noreferrer">View hospital</a><button className="btn">Directions</button><SaveButton id={h.id} kind="hospital" label="hospital" ctx={ctx} /></div>
          </article>
        ))}
      </div>
    );
  }

  function ArticlesSection({ section, ctx }) {
    const c = ctx || {};
    return (
      <div className="articles">
        {section.items.map((a) => (
          <div key={a.id} className="acard-wrap">
          <button type="button" className={'acard__save' + (c.user && c.savedT && c.savedT.has(a.id) ? ' is-on' : '')} aria-pressed={!!(c.user && c.savedT && c.savedT.has(a.id))} onClick={() => (c.user ? c.toggleSaveT && c.toggleSaveT(a.id) : c.requireSignIn && c.requireSignIn())} title={c.user ? (c.savedT && c.savedT.has(a.id) ? 'Remove from Saved' : 'Save this guide') : 'Sign in to save this guide'}>{Icon.Bookmark()}</button>
          <a className="acard" href={a.url} target="_blank" rel="noreferrer">
            {a.image && <img className="acard__img" src={a.image} alt="" loading="lazy" />}
            <div className="acard__body">
              <span className="acard__cat">{a.category}</span>
              <h3 className="acard__title">{a.title}</h3>
              <div className="acard__meta">{a.reviewer && <span>{a.reviewer}</span>}<span className="acard__date">{[a.date, a.readTime].filter(Boolean).join(' · ')}</span></div>
            </div>
          </a>
          </div>
        ))}
      </div>
    );
  }

  function ToolsSection({ section }) {
    return (
      <div className="tools-row">
        {section.items.map((t) => (
          <a key={t.id} className="tcard" href={t.url} target="_blank" rel="noreferrer">
            {t.image && <img className="tcard__img" src={t.image} alt="" loading="lazy" />}
            <div className="tcard__body">
              <span className="acard__cat">Tool</span>
              <h3 className="tcard__name">{t.name}</h3>
              <p className="tcard__desc">{t.desc}</p>
              <span className="btn btn--primary tcard__cta">{t.cta || 'Open tool'}{Icon.ArrowRight()}</span>
            </div>
          </a>
        ))}
      </div>
    );
  }

  function ConditionSection({ section }) {
    const c = section.item;
    return (
      <div className="cond">
        {c.name && c.name !== section.title ? <h3 className="cond__name">{c.name}</h3> : null}
        {(c.overview || []).map((p, i) => <p key={i}>{cite(p)}</p>)}
        {c.keyFacts && c.keyFacts.length > 0 && (
          <div className="facts">
            {c.keyFacts.map((k, i) => <div key={i} className="fact"><div className="fact__value">{k.value}<Cite n={k.cite} /></div><div className="fact__label">{k.label}</div></div>)}
          </div>
        )}
        {c.treatments && c.treatments.length > 0 && (
          <ul className="bullet-list">
            {c.treatments.map((t, i) => <li key={i}><div className="bullet-list__label">{t.name}</div><div className="bullet-list__desc">{cite(t.desc)}{t.cite ? <Cite n={t.cite} /> : null}</div></li>)}
          </ul>
        )}
        {c.url && <p className="cond__more"><a className="cond__link" href={c.url} target="_blank" rel="noreferrer">Read the full {c.name} guide {Icon.ArrowRight()}</a></p>}
      </div>
    );
  }

  function ProseSection({ section }) {
    return (
      <>
        {(section.paragraphs || []).map((p, i) => <p key={i}>{cite(p)}</p>)}
        {section.bullets && section.bullets.length > 0 && (
          <ul className="bullet-list">
            {section.bullets.map((b, i) => <li key={i}><div className="bullet-list__label">{b.label}</div><div className="bullet-list__desc">{cite(b.desc)}{b.cite ? <Cite n={b.cite} /> : null}</div></li>)}
          </ul>
        )}
        {section.callout && (
          <div className="callout">
            <div className="callout__icon">{Icon.Sparkle()}</div>
            <div><p className="callout__title">{section.callout.title}</p><p className="callout__body">{cite(section.callout.body)}</p></div>
          </div>
        )}
      </>
    );
  }


  /* Side-by-side recognition table. Values are strings with [n] markers; no adjectives, no ranking. */
  function CompareSection({ section, ctx }) {
    const pool = (window.EHData && window.EHData.PROVIDERS) || {};
    const c = ctx || {};
    const cols = (section.columns || []).map((col) => ({ ...col, p: pool[col.providerId] || (section.items || []).find((x) => x.id === col.providerId) || {} }));
    return (
      <div className="compare">
        {section.intro && <p className="compare__intro">{cite(section.intro)}</p>}
        <div className="compare__table" role="table">
          <div className="compare__row compare__row--head" role="row">
            <div className="compare__label" role="columnheader"><span className="sr-only">Attribute</span></div>
            {cols.map((col) => {
              const saved = c.user && c.saved && c.saved.has(col.p.id);
              return (
                <div key={col.providerId} className="compare__col" role="columnheader">
                  {col.label && <div className="compare__kicker">{col.label}</div>}
                  <div className="compare__who"><span className="pcard__avatar">{col.p.initials}</span><div><div className="compare__name">{col.p.name}{saved && <Badge kind="saved">{Icon.Bookmark()}Saved</Badge>}</div><div className="pcard__muted">{col.p.hospital}</div></div></div>
                </div>
              );
            })}
          </div>
          {(section.rows || []).map((r, i) => (
            <div key={i} className="compare__row" role="row">
              <div className="compare__label" role="rowheader">{r.label}</div>
              {r.values.map((v, j) => <div key={j} className="compare__cell" role="cell">{cite(v)}</div>)}
            </div>
          ))}
          <div className="compare__row compare__row--actions" role="row">
            <div className="compare__label" role="rowheader"><span className="sr-only">Actions</span></div>
            {cols.map((col) => (
              <div key={col.providerId} className="compare__cell compare__actions" role="cell">
                {c.user && col.p.bookOnline ? <a className="btn btn--primary" href={col.p.profileUrl || '#'} target="_blank" rel="noreferrer">{Icon.Calendar()}<span>Book online</span></a> : <a className="btn btn--primary" href={col.p.profileUrl || '#'} target="_blank" rel="noreferrer">View profile</a>}
                <button type="button" className={'btn btn--save' + (c.user && c.saved && c.saved.has(col.p.id) ? ' is-on' : '')} onClick={() => c.toggleSave && c.toggleSave(col.p.id)}>{Icon.Bookmark()}<span>{c.user && c.saved && c.saved.has(col.p.id) ? 'Saved' : 'Save'}</span></button>
              </div>
            ))}
          </div>
        </div>
        {section.footnote && <p className="compare__foot">{cite(section.footnote)}</p>}
      </div>
    );
  }

  function SectionBody({ section, ctx }) {
    const filters = (ctx && ctx.filters) || {};
    const onFilters = (ctx && ctx.setFilters) || (() => {});
    switch (section.kind) {
      case 'providers': return <ProvidersSection section={section} filters={filters} onFilters={onFilters} ctx={ctx} dentist={(section.items || []).some((p) => (p.programs || []).includes('Top Dentist') || /dentist/i.test(p.specialty || ''))} />;
      case 'compare': return <CompareSection section={section} ctx={ctx} />;
      case 'hospitals': return <HospitalsSection section={section} ctx={ctx} />;
      case 'articles': return <ArticlesSection section={section} ctx={ctx} />;
      case 'tools': return <ToolsSection section={section} />;
      case 'condition': return <ConditionSection section={section} />;
      default: return <ProseSection section={section} />;
    }
  }

  function SourcesList({ sources }) {
    const [open, setOpen] = useState(false);
    if (!sources || !sources.length) return null;
    const shown = open ? sources : sources.slice(0, 4);
    return (
      <section className="sources fade-in" aria-label="Sources">
        <header className="section__head"><h2 className="section__title">Sources</h2><span className="sources__count">{sources.length}</span></header>
        <ol className="sources__list">
          {shown.map((s) => (
            <li key={s.num} id={'src-' + s.num} className="source">
              <span className="source__num">{s.num}</span>
              <a className="source__body" href={s.url || '#'} target="_blank" rel="noreferrer">
                <span className="source__name"><span className={'source__fav source__fav--' + (s.type || 'external')}>{(s.name || '?')[0]}</span>{s.name}</span>
                <span className="source__title">{s.title}</span>
                <span className="source__date">{s.date}</span>
              </a>
            </li>
          ))}
        </ol>
        {sources.length > 4 && <button className="sources__toggle" onClick={() => setOpen((o) => !o)}>{open ? 'Show fewer' : 'Show all ' + sources.length + ' sources'}</button>}
      </section>
    );
  }

  /* Merge duplicate sources (same url + title) and renumber 1..n, rewriting every cite, sourceNum and "[n]" marker. */
  function normalizeSources(t) {
    const sources = t.sources || [];
    const firstByKey = new Map(); const remap = new Map(); const kept = [];
    sources.forEach((s) => { const k = (s.url || '') + '|' + (s.title || ''); if (firstByKey.has(k)) remap.set(s.num, firstByKey.get(k)); else { firstByKey.set(k, s.num); kept.push(s); } });
    kept.forEach((s, i) => { remap.set(s.num, i + 1); });
    const map = (n) => (remap.has(n) ? remap.get(n) : n);
    const fix = (v) => {
      if (Array.isArray(v)) return v.map(fix);
      if (v && typeof v === 'object') { const o = {}; for (const k in v) o[k] = (k === 'cite' && typeof v[k] === 'number') || k === 'sourceNum' ? map(v[k]) : k === 'cite' && Array.isArray(v[k]) ? v[k].map(map) : fix(v[k]); return o; }
      if (typeof v === 'string') return v.replace(/\[(\d+)\]/g, (m, n) => '[' + map(Number(n)) + ']');
      return v;
    };
    const out = fix({ ...t, sources: kept.map((s, i) => ({ ...s, num: i + 1 })) });
    out.sources = kept.map((s, i) => ({ ...s, num: i + 1 }));
    return out;
  }

  /* Convert a plain data template into the engine's template shape (sections need a body()). */
  function toEngineTemplate(raw) {
    const t = normalizeSources(raw);
    return {
      ...t,
      tabs: [],
      sections: (t.sections || []).map((s) => ({ ...s, tab: s.scope || 'ask', icon: 'Sparkle', body: (ctx) => <SectionBody section={s} ctx={ctx} /> })),
    };
  }

  window.EHCards = { cite, Cite, ProviderCard, FilterBar, useFacetMenu, FacetChip, FacetOpt, FacetMore, applyFilters, SectionBody, SourcesList, toEngineTemplate, CompareSection, SPECIALTIES, INSURERS };
})();
