// BVI Timeline — Form and Sidebar Components
// Exported to window for use in Timeline.html

const { useState, useEffect, useRef, useMemo } = React;

// ── EVENT FORM MODAL ──────────────────────────────────────────────────────────

function EventForm({ event, onSave, onClose }) {
  const isNew = !event || !event.id;
  const [cal,      setCal]      = useState(event?.cal      || 'greg');
  const [year,     setYear]     = useState(event?.year     || '');
  const [month,    setMonth]    = useState(event?.month    || '');
  const [day,      setDay]      = useState(event?.day      || '');
  const [precision,setPrecision]= useState(event?.precision|| 'day');
  const [title,    setTitle]    = useState(event?.title    || '');
  const [desc,     setDesc]     = useState(event?.desc     || '');
  const [category, setCategory] = useState(event?.category || 'LEGAL');
  const [power,    setPower]    = useState(event?.power    || 'GBR');
  const [source,   setSource]   = useState(event?.source   || '');
  const [citation, setCitation] = useState(event?.citation || '');
  const [verified, setVerified] = useState(event?.verified || false);
  const [notes,    setNotes]    = useState(event?.notes    || '');

  const computedJDN = useMemo(() => {
    const y = parseInt(year), m = parseInt(month)||1, d = parseInt(day)||1;
    if (!y || isNaN(y)) return null;
    return cal === 'julian' ? TU.julianToJDN(y,m,d) : TU.gregorianToJDN(y,m,d);
  }, [cal, year, month, day]);

  const gregEquiv = computedJDN ? TU.jdnToGregorian(computedJDN) : null;
  const julEquiv  = computedJDN ? TU.jdnToJulian(computedJDN)    : null;

  function handleSave() {
    if (!title.trim())       { alert('Title required'); return; }
    if (!year || isNaN(parseInt(year))) { alert('Year required'); return; }
    if (!computedJDN)        { alert('Invalid date'); return; }
    const saved = {
      id: event?.id || ('u_' + Date.now()),
      jdn: computedJDN, cal, year: parseInt(year),
      month: parseInt(month)||1, day: parseInt(day)||1,
      precision, title: title.trim(), desc, category, power,
      source, citation, verified, notes,
      seedData: false, addedAt: event?.addedAt || Date.now(),
    };
    onSave(saved);
  }

  const labelStyle = { display:'block', fontSize:11, fontWeight:600, letterSpacing:'0.06em', textTransform:'uppercase', color:'#7a6a55', marginBottom:4 };
  const inputStyle = { width:'100%', padding:'6px 8px', border:'1px solid #d4c8b8', borderRadius:4, fontSize:13, background:'#fdf9f4', boxSizing:'border-box' };
  const rowStyle   = { display:'grid', gridTemplateColumns:'1fr 1fr', gap:12 };

  return (
    <div style={{position:'fixed',inset:0,background:'rgba(0,0,0,0.55)',zIndex:200,display:'flex',alignItems:'center',justifyContent:'center'}}
         onClick={e => e.target===e.currentTarget && onClose()}>
      <div style={{background:'#fdf9f4',borderRadius:8,padding:'28px 32px',width:560,maxHeight:'90vh',overflowY:'auto',boxShadow:'0 8px 40px rgba(0,0,0,0.3)'}}>
        <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:20}}>
          <h2 style={{margin:0,fontFamily:'Georgia,serif',fontSize:18,color:'#2d1f0e'}}>{isNew ? 'Add Event' : 'Edit Event'}</h2>
          <button onClick={onClose} style={{background:'none',border:'none',fontSize:20,cursor:'pointer',color:'#888',lineHeight:1}}>×</button>
        </div>

        {/* DATE */}
        <div style={{background:'#f0ebe3',borderRadius:6,padding:'12px 16px',marginBottom:16}}>
          <div style={{display:'flex',gap:8,marginBottom:10,alignItems:'center'}}>
            <span style={{...labelStyle,margin:0}}>Calendar:</span>
            {['greg','julian'].map(c => (
              <button key={c} onClick={() => setCal(c)}
                style={{padding:'3px 10px',borderRadius:4,border:'1px solid #c4b49a',fontSize:12,cursor:'pointer',
                        background: cal===c ? '#8b1a1a':'#fff', color: cal===c ? '#fff':'#555'}}>
                {c==='greg' ? 'Gregorian (N.S.)' : 'Julian (O.S.)'}
              </button>
            ))}
          </div>
          <div style={{display:'grid',gridTemplateColumns:'80px 80px 80px 1fr',gap:8,alignItems:'end'}}>
            <div>
              <label style={labelStyle}>Year *</label>
              <input style={inputStyle} type="number" value={year} onChange={e=>setYear(e.target.value)} placeholder="1627"/>
            </div>
            <div>
              <label style={labelStyle}>Month</label>
              <input style={inputStyle} type="number" min="1" max="12" value={month} onChange={e=>setMonth(e.target.value)} placeholder="1–12"/>
            </div>
            <div>
              <label style={labelStyle}>Day</label>
              <input style={inputStyle} type="number" min="1" max="31" value={day} onChange={e=>setDay(e.target.value)} placeholder="1–31"/>
            </div>
            <div>
              <label style={labelStyle}>Precision</label>
              <select style={inputStyle} value={precision} onChange={e=>setPrecision(e.target.value)}>
                <option value="day">Day</option>
                <option value="month">Month</option>
                <option value="year">Year only</option>
              </select>
            </div>
          </div>
          {computedJDN && (
            <div style={{marginTop:8,fontSize:11,color:'#6b5a45',lineHeight:1.8}}>
              <strong>JDN:</strong> {computedJDN} &nbsp;|&nbsp;
              <strong>Gregorian:</strong> {TU.formatDate(gregEquiv)}
              {cal==='julian' && <> &nbsp;|&nbsp; <strong>Julian (O.S.):</strong> {TU.formatDate(julEquiv)}</>}
              {cal==='greg' && julEquiv && parseInt(year) <= 1752 && <> &nbsp;|&nbsp; <strong>Julian (O.S.):</strong> {TU.formatDate(julEquiv)}</>}
            </div>
          )}
        </div>

        {/* TITLE & DESCRIPTION */}
        <div style={{marginBottom:12}}>
          <label style={labelStyle}>Title *</label>
          <input style={inputStyle} value={title} onChange={e=>setTitle(e.target.value)} placeholder="e.g. Carlisle Letters Patent"/>
        </div>
        <div style={{marginBottom:12}}>
          <label style={labelStyle}>Description</label>
          <textarea style={{...inputStyle,height:80,resize:'vertical',fontFamily:'inherit'}}
            value={desc} onChange={e=>setDesc(e.target.value)}/>
        </div>

        {/* CATEGORY & POWER */}
        <div style={{...rowStyle,marginBottom:12}}>
          <div>
            <label style={labelStyle}>Category</label>
            <select style={inputStyle} value={category} onChange={e=>setCategory(e.target.value)}>
              {Object.entries(EVENT_CATEGORIES).map(([k,v])=>(
                <option key={k} value={k}>{v.label}</option>
              ))}
            </select>
          </div>
          <div>
            <label style={labelStyle}>Colonial Power</label>
            <select style={inputStyle} value={power} onChange={e=>setPower(e.target.value)}>
              {Object.entries(POWERS).map(([k,v])=>(
                <option key={k} value={k}>{v.label}</option>
              ))}
            </select>
          </div>
        </div>

        {/* SOURCE */}
        <div style={{...rowStyle,marginBottom:12}}>
          <div>
            <label style={labelStyle}>Source</label>
            <input style={inputStyle} value={source} onChange={e=>setSource(e.target.value)} placeholder="e.g. Letters Patent 1627"/>
          </div>
          <div>
            <label style={labelStyle}>Citation</label>
            <input style={inputStyle} value={citation} onChange={e=>setCitation(e.target.value)} placeholder="e.g. TNA ref. or statute ref."/>
          </div>
        </div>

        {/* NOTES */}
        <div style={{marginBottom:12}}>
          <label style={labelStyle}>Research Notes</label>
          <textarea style={{...inputStyle,height:60,resize:'vertical',fontFamily:'inherit'}}
            value={notes} onChange={e=>setNotes(e.target.value)} placeholder="Personal notes, cross-references, questions…"/>
        </div>

        {/* VERIFIED */}
        <label style={{display:'flex',alignItems:'center',gap:8,cursor:'pointer',marginBottom:20,fontSize:13,color:'#3a2e1e'}}>
          <input type="checkbox" checked={verified} onChange={e=>setVerified(e.target.checked)}
            style={{width:16,height:16,accentColor:'#2e7d32'}}/>
          <span>I have verified this against the primary source</span>
        </label>

        <div style={{display:'flex',gap:10,justifyContent:'flex-end'}}>
          <button onClick={onClose} style={{padding:'8px 20px',border:'1px solid #c4b49a',borderRadius:4,background:'#fff',cursor:'pointer',fontSize:13}}>Cancel</button>
          <button onClick={handleSave} style={{padding:'8px 20px',border:'none',borderRadius:4,background:'#8b1a1a',color:'#fff',cursor:'pointer',fontSize:13,fontWeight:600}}>
            {isNew ? 'Add Event' : 'Save Changes'}
          </button>
        </div>
      </div>
    </div>
  );
}

// ── EVENT DETAIL PANEL ────────────────────────────────────────────────────────

function EventDetail({ event, onEdit, onDelete, onClose }) {
  if (!event) return null;
  const g = TU.jdnToGregorian(event.jdn);
  const j = TU.jdnToJulian(event.jdn);
  const cat = EVENT_CATEGORIES[event.category] || {};
  const pow = POWERS[event.power] || {};
  const badge = (label, color) => (
    <span style={{display:'inline-block',padding:'2px 8px',borderRadius:3,fontSize:11,fontWeight:600,background:color+'22',color,border:`1px solid ${color}55`,marginRight:6}}>
      {label}
    </span>
  );
  return (
    <div style={{background:'#fdf9f4',border:'1px solid #d4c8b8',borderRadius:6,padding:'16px 20px',marginTop:12,position:'relative'}}>
      <button onClick={onClose} title="Close" style={{position:'absolute',top:10,right:12,background:'none',border:'none',fontSize:18,cursor:'pointer',color:'#aaa'}}>×</button>
      <div style={{marginBottom:8}}>
        {badge(cat.label || event.category, cat.color || '#888')}
        {badge(pow.label || event.power, pow.color || '#888')}
        {event.seedData && !event.verified && badge('⚠ Needs verification','#b45309')}
        {event.verified && badge('✓ Verified','#2e7d32')}
      </div>
      <h3 style={{margin:'0 0 6px',fontFamily:'Georgia,serif',fontSize:17,color:'#2d1f0e'}}>{event.title}</h3>
      <div style={{fontSize:12,color:'#7a6a55',marginBottom:10,fontFamily:'monospace'}}>
        JDN {event.jdn} · {TU.formatDate(g, 'long')} (Gregorian)
        {event.year <= 1752 && ` · O.S. ${TU.formatDate(j, 'long')}`}
      </div>
      {event.desc && <p style={{fontSize:13,color:'#3a2e1e',lineHeight:1.7,margin:'0 0 10px'}}>{event.desc}</p>}
      {event.source && (
        <div style={{fontSize:12,borderTop:'1px solid #e8e0d5',paddingTop:8,marginTop:8}}>
          <strong style={{color:'#6b5a45'}}>Source:</strong> <span style={{color:'#3a2e1e'}}>{event.source}</span>
          {event.citation && <><br/><strong style={{color:'#6b5a45'}}>Citation:</strong> <span style={{color:'#3a2e1e',fontFamily:'monospace',fontSize:11}}>{event.citation}</span></>}
        </div>
      )}
      {event.notes && (
        <div style={{fontSize:12,borderTop:'1px solid #e8e0d5',paddingTop:8,marginTop:8}}>
          <strong style={{color:'#6b5a45'}}>Notes:</strong> <span style={{color:'#3a2e1e',fontStyle:'italic'}}>{event.notes}</span>
        </div>
      )}
      <div style={{display:'flex',gap:8,marginTop:14}}>
        <button onClick={() => onEdit(event)} style={{padding:'5px 14px',fontSize:12,border:'1px solid #c4b49a',borderRadius:4,background:'#fff',cursor:'pointer'}}>Edit</button>
        <button onClick={() => { if(confirm('Delete this event?')) onDelete(event.id); }}
          style={{padding:'5px 14px',fontSize:12,border:'1px solid #e8b4b4',borderRadius:4,background:'#fff',color:'#8b1a1a',cursor:'pointer'}}>Delete</button>
      </div>
    </div>
  );
}

// ── FILTER SIDEBAR ────────────────────────────────────────────────────────────

function FilterSidebar({ filters, onChange, eventCount, totalCount }) {
  const tog = (set, key) => {
    const next = new Set(set);
    next.has(key) ? next.delete(key) : next.add(key);
    return next;
  };

  const sectionTitle = txt => (
    <div style={{fontSize:10,fontWeight:700,letterSpacing:'0.1em',textTransform:'uppercase',color:'#a09080',marginTop:18,marginBottom:8}}>{txt}</div>
  );

  const chip = (key, label, color, active, onClick) => (
    <div key={key} onClick={onClick} style={{
      display:'flex',alignItems:'center',gap:6,padding:'4px 8px',borderRadius:4,cursor:'pointer',
      marginBottom:4,fontSize:12,
      background: active ? color+'33' : 'transparent',
      color: active ? '#f0e8d8' : '#a09080',
      border: `1px solid ${active ? color+'66' : 'transparent'}`,
    }}>
      <span style={{width:8,height:8,borderRadius:'50%',background:color,flexShrink:0}}></span>
      {label}
    </div>
  );

  return (
    <div style={{width:190,flexShrink:0,background:'#1e1810',padding:'16px 14px',overflowY:'auto',height:'100%',boxSizing:'border-box'}}>
      <div style={{fontSize:11,color:'#7a6a55',marginBottom:12}}>
        Showing <strong style={{color:'#c4a87a'}}>{eventCount}</strong> of {totalCount} events
      </div>

      {/* Search */}
      <input
        placeholder="Search…"
        value={filters.search}
        onChange={e => onChange({...filters, search: e.target.value})}
        style={{width:'100%',boxSizing:'border-box',padding:'6px 8px',borderRadius:4,border:'1px solid #3a2e1e',background:'#140f08',color:'#e8dcc8',fontSize:12,outline:'none'}}
      />

      {sectionTitle('Colonial Power')}
      {Object.entries(POWERS).map(([k,v]) =>
        chip(k, v.label, v.color, filters.powers.has(k), () => onChange({...filters, powers: tog(filters.powers, k)}))
      )}

      {sectionTitle('Category')}
      {Object.entries(EVENT_CATEGORIES).map(([k,v]) =>
        chip(k, v.label, v.color, filters.categories.has(k), () => onChange({...filters, categories: tog(filters.categories, k)}))
      )}

      {sectionTitle('Status')}
      <div onClick={() => onChange({...filters, showSeed: !filters.showSeed})}
        style={{display:'flex',alignItems:'center',gap:8,cursor:'pointer',fontSize:12,color: filters.showSeed ? '#f0e8d8':'#a09080',padding:'4px 0'}}>
        <span style={{width:14,height:14,border:`2px solid ${filters.showSeed?'#c4a87a':'#4a3a28'}`,borderRadius:3,background: filters.showSeed?'#c4a87a':'transparent',display:'inline-block',flexShrink:0}}></span>
        Show seed events
      </div>
      <div onClick={() => onChange({...filters, showVerified: !filters.showVerified})}
        style={{display:'flex',alignItems:'center',gap:8,cursor:'pointer',fontSize:12,color: filters.showVerified ? '#f0e8d8':'#a09080',padding:'4px 0'}}>
        <span style={{width:14,height:14,border:`2px solid ${filters.showVerified?'#2e7d32':'#4a3a28'}`,borderRadius:3,background: filters.showVerified?'#2e7d32':'transparent',display:'inline-block',flexShrink:0}}></span>
        Verified only
      </div>

      <div style={{marginTop:24,paddingTop:16,borderTop:'1px solid #2a1e12',fontSize:10,color:'#5a4a38',lineHeight:1.8}}>
        <div>⚠ = Seed / unverified</div>
        <div>✓ = Source verified</div>
        <div>O.S. = Old Style (Julian)</div>
        <div>N.S. = New Style (Gregorian)</div>
      </div>
    </div>
  );
}

// ── PERSON DETAIL CARD ───────────────────────────────────────────────────
function PersonDetailCard({ person, roleTags, monarchs, viGovs, liGovs, events, onSelectEvent, onClose }) {
  if (!person) return null;
  const dispDate = (y,m,d,cal) => {
    if (!y) return null;
    const s = `${d?d+' ':''}${m?TU.MONTHS_SHORT[m-1]+' ':''}${y}`;
    return cal==='julian' ? s+' (O.S.)' : s;
  };
  const born = dispDate(person.birthYear,person.birthMonth,person.birthDay,person.birthCal);
  const died = dispDate(person.deathYear,person.deathMonth,person.deathDay,person.deathCal);
  const linkedEvents = (events||[]).filter(e => (e.personIds||[]).includes(person.id)).sort((a,b)=>a.jdn-b.jdn);
  const monarchRoles = (monarchs||[]).filter(m => m.personId===person.id);
  const liRoles      = (liGovs||[]).filter(g => g.personId===person.id);
  const viRoles      = (viGovs||[]).filter(g => g.personId===person.id);
  return (
    <div style={{background:'#fdf9f4',border:'1px solid #d4c8b8',borderRadius:6,padding:'18px 22px',position:'relative'}}>
      {onClose && <button onClick={onClose} title="Close" style={{position:'absolute',top:10,right:12,background:'none',border:'none',fontSize:18,cursor:'pointer',color:'#aaa'}}>×</button>}
      <h3 style={{margin:'0 0 4px',fontFamily:'Georgia,serif',fontSize:19,color:'#2d1f0e'}}>{person.name}</h3>
      {person.aliases && <div style={{fontSize:12,color:'#8a7a68',fontStyle:'italic',marginBottom:6}}>{person.aliases}</div>}
      <div style={{fontSize:12,color:'#7a6a55',marginBottom:10,fontFamily:'monospace'}}>
        {born ? `b. ${born}` : 'b. ?'} — {died ? `d. ${died}` : 'd. ?'}
        {person.seedData && !person.verified && <span style={{marginLeft:8,color:'#b45309'}}>⚠ unverified</span>}
        {person.verified && <span style={{marginLeft:8,color:'#2e7d32'}}>✓ verified</span>}
      </div>
      <div style={{display:'flex',flexWrap:'wrap',gap:5,marginBottom:12}}>
        {(person.roleTags||[]).map(tid => {
          const t = roleTags.find(r=>r.id===tid);
          if (!t) return null;
          return <span key={tid} style={{padding:'2px 8px',borderRadius:3,fontSize:11,fontWeight:600,background:t.color+'22',color:t.color,border:`1px solid ${t.color}55`}}>{t.label}</span>;
        })}
      </div>
      {person.biography && <p style={{fontSize:13,color:'#3a2e1e',lineHeight:1.7,margin:'0 0 12px'}}>{person.biography}</p>}
      {(monarchRoles.length+liRoles.length+viRoles.length) > 0 && (
        <div style={{borderTop:'1px solid #e8e0d5',paddingTop:10,marginBottom:10}}>
          <div style={{fontSize:10,fontWeight:700,letterSpacing:'0.06em',textTransform:'uppercase',color:'#7a6a55',marginBottom:6}}>Offices held</div>
          {monarchRoles.map((m,i)=><div key={'m'+i} style={{fontSize:12,color:'#3a2e1e',padding:'2px 0'}}>· Monarch — {m.sYear||'?'} to {m.eYear||'?'}</div>)}
          {liRoles.map((g,i)=><div key={'l'+i} style={{fontSize:12,color:'#3a2e1e',padding:'2px 0'}}>· Leeward Islands {g.title||'Governor'} — {g.startYear||'?'} to {g.endYear||'?'}</div>)}
          {viRoles.map((g,i)=><div key={'v'+i} style={{fontSize:12,color:'#3a2e1e',padding:'2px 0'}}>· Virgin Islands {g.title||'Governor'} — {g.startYear||'?'} to {g.endYear||'?'}</div>)}
        </div>
      )}
      {linkedEvents.length > 0 && (
        <div style={{borderTop:'1px solid #e8e0d5',paddingTop:10,marginBottom:10}}>
          <div style={{fontSize:10,fontWeight:700,letterSpacing:'0.06em',textTransform:'uppercase',color:'#7a6a55',marginBottom:6}}>Related events</div>
          {linkedEvents.map(e => (
            <div key={e.id} onClick={()=>onSelectEvent && onSelectEvent(e)} style={{fontSize:12,color:'#1a3a8b',padding:'3px 0',cursor:onSelectEvent?'pointer':'default',textDecoration:onSelectEvent?'underline':'none'}}>
              → {e.title} <span style={{color:'#8a7a68'}}>({TU.jdnToDisplay(e.jdn,'year')})</span>
            </div>
          ))}
        </div>
      )}
      {person.source && (
        <div style={{fontSize:12,borderTop:'1px solid #e8e0d5',paddingTop:8,marginTop:8}}>
          <strong style={{color:'#6b5a45'}}>Source:</strong> <span style={{color:'#3a2e1e'}}>{person.source}</span>
          {person.citation && <><br/><strong style={{color:'#6b5a45'}}>Citation:</strong> <span style={{color:'#3a2e1e',fontFamily:'monospace',fontSize:11}}>{person.citation}</span></>}
        </div>
      )}
      {person.notes && <div style={{fontSize:12,borderTop:'1px solid #e8e0d5',paddingTop:8,marginTop:8}}><strong style={{color:'#6b5a45'}}>Notes:</strong> <span style={{color:'#3a2e1e',fontStyle:'italic'}}>{person.notes}</span></div>}
    </div>
  );
}

// ── PEOPLE LIST ────────────────────────────────────────────────────────────
function PeopleList({ people, roleTags, onSelect, selected }) {
  const [search, setSearch] = useState('');
  const [tagFilter, setTagFilter] = useState(null);
  const filtered = useMemo(()=>{
    return people.filter(p => {
      if (tagFilter && !(p.roleTags||[]).includes(tagFilter)) return false;
      if (search) {
        const q = search.toLowerCase();
        if (!(p.name+(p.aliases||'')+(p.biography||'')+(p.notes||'')).toLowerCase().includes(q)) return false;
      }
      return true;
    }).sort((a,b)=>(a.birthYear||9999)-(b.birthYear||9999));
  },[people,search,tagFilter]);
  return (
    <div>
      <div style={{display:'flex',gap:8,alignItems:'center',marginBottom:12,flexWrap:'wrap'}}>
        <input placeholder="Search people…" value={search} onChange={e=>setSearch(e.target.value)} style={{padding:'5px 8px',fontSize:12,border:'1px solid #c4b49a',borderRadius:4,background:'#fdf9f4',width:200}}/>
        <select value={tagFilter||''} onChange={e=>setTagFilter(e.target.value||null)} style={{padding:'5px 8px',fontSize:12,border:'1px solid #c4b49a',borderRadius:4,background:'#fdf9f4'}}>
          <option value="">All roles</option>
          {roleTags.map(t => <option key={t.id} value={t.id}>{t.label}</option>)}
        </select>
        <span style={{fontSize:11,color:'#7a6a55'}}>{filtered.length} of {people.length}</span>
      </div>
      <div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill,minmax(280px,1fr))',gap:10}}>
        {filtered.map(p => {
          const isSel = selected?.id===p.id;
          return (
            <div key={p.id} onClick={()=>onSelect(p)} style={{padding:'10px 14px',background:isSel?'#fff':'#fdf9f4',border:`1px solid ${isSel?'#8b1a1a':'#d4c8b8'}`,borderRadius:6,cursor:'pointer',transition:'all 0.1s'}}>
              <div style={{fontFamily:'Georgia,serif',fontSize:14,fontWeight:600,color:'#2d1f0e'}}>{p.name}</div>
              <div style={{fontSize:10,color:'#7a6a55',fontFamily:'monospace',marginTop:2}}>{p.birthYear||'?'} – {p.deathYear||'?'}</div>
              <div style={{display:'flex',flexWrap:'wrap',gap:3,marginTop:6}}>
                {(p.roleTags||[]).map(tid => {
                  const t = roleTags.find(r=>r.id===tid);
                  if (!t) return null;
                  return <span key={tid} style={{padding:'1px 6px',borderRadius:3,fontSize:9,background:t.color+'22',color:t.color}}>{t.label}</span>;
                })}
              </div>
            </div>
          );
        })}
      </div>
      {filtered.length===0 && <div style={{padding:30,textAlign:'center',color:'#a09080',fontSize:13}}>No people match.</div>}
    </div>
  );
}

Object.assign(window, { EventForm, EventDetail, FilterSidebar, PersonDetailCard, PeopleList });
