/**
 * Upheld — Indian Legal Research Platform
 * frontend/src/App.jsx
 *
 * Aesthetic: Editorial/authoritative. Ink-on-paper meets modern research tool.
 * Playfair Display headings, Source Serif 4 body, JetBrains Mono for citations.
 * Warm parchment (#f7f5f0), deep ink (#1a1814), muted gold accent (#8b6914).
 */

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

const API = '';

async function apiFetch(path, opts = {}) {
  const r = await fetch(API + path, {
    headers: { 'Content-Type': 'application/json' },
    ...opts,
  });
  if (!r.ok) throw new Error(`${r.status} ${r.statusText}`);
  return r.json();
}

// ── Styles ────────────────────────────────────────────────────────────────────
const CSS = `
:root {
  --ink: #1a1814; --paper: #f7f5f0; --warm: #ede9e0; --border: #d4cfc4;
  --muted: #7a7468; --gold: #8b6914; --gold-lt: #b8960f;
  --red: #8b1a1a; --green: #1a5c2a; --blue: #1a3a5c;
  --serif: 'Source Serif 4',Georgia,serif;
  --display: 'Playfair Display',Georgia,serif;
  --mono: 'JetBrains Mono','Courier New',monospace;
  --max: 900px; --trans: 0.15s ease;
}
.app { min-height:100vh; display:flex; flex-direction:column; }

/* Header */
.hdr { border-bottom:1px solid var(--border); background:var(--paper);
  position:sticky; top:0; z-index:100; }
.hdr-in { max-width:var(--max); margin:0 auto; padding:0 24px;
  display:flex; align-items:center; gap:20px; height:54px; }
.logo { font-family:var(--display); font-size:1.3rem; font-weight:700;
  color:var(--ink); cursor:pointer; letter-spacing:-0.01em; flex-shrink:0; }
.logo em { color:var(--gold); font-style:normal; }
.hdr-q { flex:1; }
.hdr-q input { width:100%; padding:7px 13px; border:1px solid var(--border);
  background:var(--warm); border-radius:2px; font-family:var(--serif);
  font-size:0.9rem; color:var(--ink); outline:none;
  transition:border-color var(--trans),box-shadow var(--trans); }
.hdr-q input:focus { border-color:var(--gold);
  box-shadow:0 0 0 2px rgba(139,105,20,.12); }

/* Home */
.home { flex:1; display:flex; flex-direction:column; align-items:center;
  justify-content:center; padding:80px 24px 120px; }
.home-logo { font-family:var(--display); font-size:3.2rem; font-weight:700;
  color:var(--ink); letter-spacing:-0.025em; margin-bottom:4px; }
.home-logo em { color:var(--gold); font-style:normal; }
.home-tag { font-family:var(--serif); font-size:1rem; color:var(--muted);
  font-style:italic; margin-bottom:44px; font-weight:300; }
.home-wrap { width:100%; max-width:620px; }
.home-box { display:flex; border:2px solid var(--ink); border-radius:2px;
  overflow:hidden; box-shadow:5px 5px 0 var(--ink);
  transition:box-shadow var(--trans); }
.home-box:focus-within { box-shadow:5px 5px 0 var(--gold); }
.home-box input { flex:1; padding:14px 18px; border:none; background:var(--paper);
  font-family:var(--serif); font-size:1.05rem; color:var(--ink); outline:none; }
.home-box input::placeholder { color:var(--muted); font-style:italic; }
.home-box button { padding:14px 22px; background:var(--ink); color:var(--paper);
  border:none; font-family:var(--serif); font-size:0.95rem; font-weight:600;
  cursor:pointer; transition:background var(--trans); letter-spacing:0.02em; }
.home-box button:hover { background:var(--gold); }
.home-ex { margin-top:18px; display:flex; flex-wrap:wrap; gap:7px; }
.home-chip { font-family:var(--mono); font-size:0.75rem; color:var(--muted);
  padding:4px 10px; border:1px solid var(--border); border-radius:2px;
  cursor:pointer; background:var(--paper); transition:color var(--trans),border-color var(--trans); }
.home-chip:hover { color:var(--gold); border-color:var(--gold); }
.home-stats { margin-top:52px; display:flex; gap:48px; text-align:center; }
.stat-n { font-family:var(--display); font-size:2.1rem; font-weight:700;
  color:var(--ink); display:block; }
.stat-l { font-family:var(--serif); font-size:0.76rem; color:var(--muted);
  text-transform:uppercase; letter-spacing:0.08em; }

/* Page */
.page { max-width:var(--max); margin:0 auto; padding:28px 24px; flex:1; }

/* Filters */
.filters { display:flex; flex-wrap:wrap; gap:10px; margin-bottom:22px;
  padding-bottom:18px; border-bottom:1px solid var(--border); }
.fl { display:flex; align-items:center; gap:6px; }
.fl-lbl { font-family:var(--serif); font-size:0.76rem; color:var(--muted);
  text-transform:uppercase; letter-spacing:0.06em; white-space:nowrap; }
.fl-sel, .fl-inp { padding:5px 10px; border:1px solid var(--border);
  border-radius:2px; background:var(--paper); font-family:var(--serif);
  font-size:0.84rem; color:var(--ink); outline:none; cursor:pointer; }
.fl-inp { width:80px; }
.fl-sel:focus, .fl-inp:focus { border-color:var(--gold); }
.fl-clr { font-family:var(--serif); font-size:0.8rem; color:var(--muted);
  background:none; border:none; cursor:pointer; text-decoration:underline; }
.fl-clr:hover { color:var(--red); }

/* Results meta */
.r-meta { display:flex; align-items:baseline; justify-content:space-between;
  margin-bottom:18px; }
.r-count { font-family:var(--serif); font-size:0.86rem; color:var(--muted); }
.r-count strong { color:var(--ink); font-weight:600; }

/* Result card */
.card { border:1px solid var(--border); border-radius:2px; padding:19px 21px;
  margin-bottom:11px; background:var(--paper); cursor:pointer;
  transition:border-color var(--trans),box-shadow var(--trans),transform var(--trans); }
.card:hover { border-color:var(--ink); box-shadow:3px 3px 0 var(--ink);
  transform:translate(-1px,-1px); }
.card-top { display:flex; align-items:flex-start; justify-content:space-between;
  gap:12px; margin-bottom:8px; }
.card-name { font-family:var(--display); font-size:1.03rem; font-weight:600;
  color:var(--ink); line-height:1.3; }
.card-unc { font-family:var(--mono); font-size:0.7rem; color:var(--gold);
  white-space:nowrap; padding:2px 7px; border:1px solid var(--border);
  border-radius:2px; background:var(--warm); flex-shrink:0; }
.card-row { display:flex; flex-wrap:wrap; gap:14px; margin-bottom:8px; }
.card-m { font-family:var(--serif); font-size:0.81rem; color:var(--muted); }
.card-m strong { color:var(--ink); font-weight:600; }
.bench-b { font-family:var(--serif); font-size:0.74rem; font-weight:600;
  padding:2px 8px; border-radius:2px; }
.b1 { background:var(--warm); color:var(--muted); }
.b2 { background:#e8f0e8; color:var(--green); }
.b3 { background:#e8ecf4; color:var(--blue); }
.b5 { background:#f5ede0; color:var(--gold); }
.card-snip { font-family:var(--serif); font-size:0.86rem; color:var(--muted);
  line-height:1.5; font-style:italic; border-left:2px solid var(--border);
  padding-left:11px; }
.card-foot { display:flex; align-items:center; justify-content:space-between;
  margin-top:11px; padding-top:9px; border-top:1px solid var(--border); }
.card-stats { display:flex; gap:14px; }
.card-stat { font-family:var(--serif); font-size:0.77rem; color:var(--muted); }
.card-stat strong { color:var(--ink); }

/* Copy button */
.cpbtn { font-family:var(--mono); font-size:0.7rem; color:var(--muted);
  background:none; border:1px solid var(--border); border-radius:2px;
  padding:3px 8px; cursor:pointer;
  transition:color var(--trans),border-color var(--trans); }
.cpbtn:hover { color:var(--gold); border-color:var(--gold); }
.cpbtn.ok { color:var(--green); border-color:var(--green); }

/* Judgment */
.j-hdr { margin-bottom:28px; padding-bottom:22px; border-bottom:2px solid var(--ink); }
.j-unc { font-family:var(--mono); font-size:0.88rem; color:var(--gold);
  display:inline-block; margin-bottom:12px;
  border-bottom:1px dashed var(--gold); padding-bottom:2px; }
.j-title { font-family:var(--display); font-size:1.65rem; font-weight:700;
  color:var(--ink); line-height:1.25; margin-bottom:16px; letter-spacing:-0.01em; }
.j-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
  gap:14px; margin-bottom:18px; }
.j-item { display:flex; flex-direction:column; gap:2px; }
.j-key { font-family:var(--serif); font-size:0.7rem; color:var(--muted);
  text-transform:uppercase; letter-spacing:0.08em; }
.j-val { font-family:var(--serif); font-size:0.9rem; color:var(--ink); font-weight:600; }
.j-acts { display:flex; gap:10px; flex-wrap:wrap; }
.btn { font-family:var(--serif); font-size:0.83rem; padding:7px 14px;
  border-radius:2px; cursor:pointer; border:1.5px solid var(--ink);
  background:var(--paper); color:var(--ink);
  transition:background var(--trans),color var(--trans); font-weight:600; }
.btn:hover { background:var(--ink); color:var(--paper); }

/* AI Summary */
.ai-box { border:1px solid var(--border); border-radius:2px; padding:18px 20px;
  background:var(--warm); margin-bottom:26px; }
.ai-hd { font-family:var(--serif); font-size:0.73rem; font-weight:600;
  color:var(--muted); text-transform:uppercase; letter-spacing:0.08em;
  margin-bottom:10px; display:flex; align-items:center; gap:8px; }
.ai-badge { background:var(--ink); color:var(--paper); font-family:var(--mono);
  font-size:0.63rem; padding:2px 6px; border-radius:2px; }
.ai-ratio { font-family:var(--display); font-size:1.03rem; font-style:italic;
  color:var(--ink); line-height:1.5; margin-bottom:8px; }
.ai-held { font-family:var(--serif); font-size:0.86rem; color:var(--muted);
  border-left:2px solid var(--gold); padding-left:11px; }

/* Body grid */
.j-body { display:grid; grid-template-columns:1fr 270px; gap:28px; }
@media(max-width:700px){ .j-body{ grid-template-columns:1fr; } }
.j-text { font-family:var(--serif); font-size:0.93rem; line-height:1.78; color:var(--ink); }
.j-text p { margin-bottom:1em; }

/* Citation rendering */
.citation-link { color:var(--gold); text-decoration:underline;
  text-decoration-style:dotted; text-underline-offset:2px; cursor:pointer; }
.citation-link:hover { color:var(--gold-lt); }
.citation-unresolved { color:var(--muted); }
.citation-foreign { color:var(--blue); font-style:italic; }
.citation-badge { font-family:var(--mono); font-size:0.63rem; padding:1px 5px;
  border-radius:2px; margin-left:3px; vertical-align:middle; }
.badge-followed     { background:#e8f0e8; color:var(--green); }
.badge-distinguished{ background:#e8ecf4; color:var(--blue); }
.badge-doubted      { background:#f5ede0; color:var(--gold); }
.badge-per-incuriam,.badge-not-followed { background:#fde8e8; color:var(--red); }
.badge-explained,.badge-referred { background:var(--warm); color:var(--muted); }

/* Sidebar */
.sidebar { display:flex; flex-direction:column; gap:22px; }
.sb-hd { font-family:var(--serif); font-size:0.7rem; font-weight:600;
  color:var(--muted); text-transform:uppercase; letter-spacing:0.08em;
  margin-bottom:10px; padding-bottom:7px; border-bottom:1px solid var(--border); }
.cit-item { padding:7px 0; border-bottom:1px solid var(--border);
  font-family:var(--serif); font-size:0.81rem; }
.cit-item:last-child { border-bottom:none; }
.cit-link { color:var(--gold); font-family:var(--mono); font-size:0.73rem;
  cursor:pointer; text-decoration:underline; text-underline-offset:2px;
  text-decoration-style:dotted; }
.cit-link:hover { color:var(--gold-lt); }
.cit-unres { color:var(--muted); font-family:var(--mono); font-size:0.73rem; }

/* Loading */
.loading { text-align:center; padding:56px 24px; font-family:var(--serif);
  color:var(--muted); font-style:italic; }
.dot { display:inline-block; width:6px; height:6px; border-radius:50%;
  background:var(--gold); margin:0 3px;
  animation:blink 1.2s ease-in-out infinite; }
.dot:nth-child(2){ animation-delay:.2s; }
.dot:nth-child(3){ animation-delay:.4s; }
@keyframes blink{ 0%,80%,100%{ opacity:.2; transform:scale(.8); }
  40%{ opacity:1; transform:scale(1); } }
.err { border:1px solid var(--red); border-radius:2px; padding:14px 18px;
  background:#fdf5f5; font-family:var(--serif); font-size:0.88rem;
  color:var(--red); margin:18px 0; }

/* Pagination */
.pages { display:flex; align-items:center; gap:8px; margin-top:28px;
  padding-top:22px; border-top:1px solid var(--border); justify-content:center; }
.pg-btn { font-family:var(--serif); font-size:0.83rem; padding:6px 12px;
  border:1px solid var(--border); border-radius:2px; cursor:pointer;
  background:var(--paper); color:var(--ink);
  transition:background var(--trans),border-color var(--trans); }
.pg-btn:hover:not(:disabled){ background:var(--warm); border-color:var(--ink); }
.pg-btn:disabled{ opacity:.4; cursor:default; }
.pg-info { font-family:var(--serif); font-size:0.8rem; color:var(--muted); }

/* Empty */
.empty { text-align:center; padding:56px 24px; }
.empty-t { font-family:var(--display); font-size:1.25rem; color:var(--ink); margin-bottom:7px; }
.empty-s { font-family:var(--serif); font-size:0.88rem; color:var(--muted); font-style:italic; }

/* Footer */
.ftr { border-top:1px solid var(--border); padding:18px 24px;
  font-family:var(--serif); font-size:0.76rem; color:var(--muted);
  display:flex; align-items:center; justify-content:space-between;
  flex-wrap:wrap; gap:8px; }
.ftr a { color:var(--muted); text-decoration:underline; }
.ftr a:hover { color:var(--gold); }

.fade { animation:fadeUp .18s ease; }
@keyframes fadeUp{ from{ opacity:0; transform:translateY(5px); } to{ opacity:1; } }
`;

function injectCSS() {
  if (document.getElementById('upheld-css')) return;
  const s = document.createElement('style');
  s.id = 'upheld-css';
  s.textContent = CSS;
  document.head.appendChild(s);
}

// ── Helpers ───────────────────────────────────────────────────────────────────

function benchCls(n) {
  if (!n) return 'b1';
  if (n >= 5) return 'b5';
  if (n >= 3) return 'b3';
  if (n >= 2) return 'b2';
  return 'b1';
}

function CopyBtn({ text, label = 'Copy citation' }) {
  const [ok, setOk] = useState(false);
  if (!text) return null;
  return (
    <button
      className={`cpbtn${ok ? ' ok' : ''}`}
      onClick={e => {
        e.stopPropagation();
        navigator.clipboard.writeText(text).then(() => {
          setOk(true); setTimeout(() => setOk(false), 1800);
        });
      }}
      title={text}
    >{ok ? '✓ Copied' : label}</button>
  );
}

function Loader() {
  return (
    <div className="loading">
      <span className="dot"/><span className="dot"/><span className="dot"/>
      <span style={{display:'block',marginTop:12}}>Searching…</span>
    </div>
  );
}

function Err({ msg }) {
  return <div className="err">⚠ {msg}</div>;
}

function TreatBadge({ t }) {
  if (!t || t === 'unknown' || t === 'referred') return null;
  const labels = { followed:'Followed', distinguished:'Distinguished',
    doubted:'Doubted', explained:'Explained',
    not_followed:'Not Followed', per_incuriam:'Per Incuriam' };
  return <span className={`citation-badge badge-${t.replace('_','-')}`}>{labels[t]||t}</span>;
}

// ── Result card ───────────────────────────────────────────────────────────────

function Card({ r, onOpen }) {
  return (
    <div className="card fade" onClick={() => onOpen(r.doc_id)}>
      <div className="card-top">
        <div className="card-name">{r.case_number || r.doc_id}</div>
        {r.unc_display && <span className="card-unc">{r.unc_display}</span>}
      </div>
      <div className="card-row">
        {r.court_code && <span className="card-m"><strong>{r.court_code}</strong></span>}
        {r.decision_date && <span className="card-m">{r.decision_date.slice(0,7)}</span>}
        {r.bench_strength && (
          <span className={`bench-b ${benchCls(r.bench_strength)}`}>
            {r.bench_display || `${r.bench_strength}-Judge`}
          </span>
        )}
      </div>
      {r.snippet && <div className="card-snip">…{r.snippet}…</div>}
      <div className="card-foot">
        <div className="card-stats">
          <span className="card-stat"><strong>{r.cited_by_count||0}</strong> citing</span>
          <span className="card-stat"><strong>{r.cites_count||0}</strong> cited</span>
        </div>
        <CopyBtn text={r.copy_citation}/>
      </div>
    </div>
  );
}

// ── Filters ───────────────────────────────────────────────────────────────────

const COURTS = [
  {v:'',l:'All courts'},{v:'SC',l:'Supreme Court'},
  {v:'HC-DEL',l:'Delhi HC'},{v:'HC-BOM',l:'Bombay HC'},
  {v:'HC-MAD',l:'Madras HC'},{v:'HC-CAL',l:'Calcutta HC'},
  {v:'HC-KER',l:'Kerala HC'},{v:'HC-KAR',l:'Karnataka HC'},
  {v:'HC-ALL',l:'Allahabad HC'},{v:'HC-GUJ',l:'Gujarat HC'},
  {v:'HC-MP',l:'MP HC'},{v:'HC-PH',l:'Punjab & Haryana HC'},
  {v:'HC-RAJ',l:'Rajasthan HC'},{v:'HC-PAT',l:'Patna HC'},
  {v:'ITAT',l:'ITAT'},{v:'NCDRC',l:'NCDRC'},
];

function Filters({ f, onChange, onClear }) {
  const set = k => e => onChange({...f, [k]: e.target.value||null});
  const setN = k => e => onChange({...f, [k]: e.target.value ? parseInt(e.target.value) : null});
  return (
    <div className="filters">
      <div className="fl">
        <span className="fl-lbl">Court</span>
        <select className="fl-sel" value={f.court_code||''} onChange={set('court_code')}>
          {COURTS.map(c=><option key={c.v} value={c.v}>{c.l}</option>)}
        </select>
      </div>
      <div className="fl">
        <span className="fl-lbl">Bench</span>
        <select className="fl-sel" value={f.bench_strength_min||''} onChange={setN('bench_strength_min')}>
          <option value="">Any bench</option>
          <option value="2">2+ judges</option>
          <option value="3">3+ judges</option>
          <option value="5">Constitution Bench+</option>
        </select>
      </div>
      <div className="fl">
        <span className="fl-lbl">From</span>
        <input type="number" className="fl-inp" placeholder="Year" min="1947" max="2026"
          value={f.year_from||''} onChange={setN('year_from')}/>
      </div>
      <div className="fl">
        <span className="fl-lbl">To</span>
        <input type="number" className="fl-inp" placeholder="Year" min="1947" max="2026"
          value={f.year_to||''} onChange={setN('year_to')}/>
      </div>
      <button className="fl-clr" onClick={onClear}>Clear</button>
    </div>
  );
}

// ── Home ──────────────────────────────────────────────────────────────────────

const EXAMPLES = [
  'AIR 2010 SC 1', 'Section 138 NI Act', 'reservation promotion quantifiable data',
  'anticipatory bail conditions', '(2017) 10 SCC 1',
];

function Home({ onSearch, stats }) {
  const [q, setQ] = useState('');
  const ref = useRef(null);
  useEffect(() => { ref.current?.focus(); }, []);
  const go = () => q.trim() && onSearch(q.trim());
  return (
    <div className="home">
      <div className="home-logo">UPHELD<em>.</em></div>
      <div className="home-tag">Accuracy-first Indian legal research. Every judgment, permanently cited.</div>
      <div className="home-wrap">
        <div className="home-box">
          <input ref={ref} value={q} onChange={e=>setQ(e.target.value)}
            onKeyDown={e=>e.key==='Enter'&&go()}
            placeholder="Search judgments, citations, legal principles…"/>
          <button onClick={go}>Search</button>
        </div>
        <div className="home-ex">
          {EXAMPLES.map(ex=>(
            <span key={ex} className="home-chip" onClick={()=>onSearch(ex)}>{ex}</span>
          ))}
        </div>
      </div>
      {stats && (
        <div className="home-stats">
          <div>
            <span className="stat-n">{(stats.judgments_indexed||0).toLocaleString()}</span>
            <span className="stat-l">Judgments indexed</span>
          </div>
          <div>
            <span className="stat-n">{(stats.unc_total||0).toLocaleString()}</span>
            <span className="stat-l">Permanent citations</span>
          </div>
        </div>
      )}
    </div>
  );
}

// ── Results ───────────────────────────────────────────────────────────────────

function Results({ query, onOpen }) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  const [filters, setFilters] = useState({});
  const [page, setPage] = useState(1);
  const PER = 20;

  const search = useCallback(async () => {
    setLoading(true); setError(null);
    try {
      const p = new URLSearchParams({query, page, per_page: PER});
      if (filters.court_code)         p.set('court_code', filters.court_code);
      if (filters.bench_strength_min) p.set('bench_strength_min', filters.bench_strength_min);
      if (filters.year_from)          p.set('year_from', filters.year_from);
      if (filters.year_to)            p.set('year_to', filters.year_to);
      const d = await apiFetch(`/search?${p}`);
      setData(d);
      if (d.type === 'direct' && d.results.length === 1) { onOpen(d.results[0].doc_id); return; }
    } catch(e) { setError(e.message); }
    finally { setLoading(false); }
  }, [query, page, filters]);

  useEffect(() => { search(); }, [search]);

  const total = data?.total || 0;
  const pages = Math.ceil(total / PER);

  return (
    <div className="page fade">
      <Filters f={filters} onChange={f=>{setFilters(f);setPage(1);}} onClear={()=>{setFilters({});setPage(1);}}/>
      {loading && <Loader/>}
      {error && <Err msg={error}/>}
      {!loading && data && (
        <>
          <div className="r-meta">
            <div className="r-count"><strong>{total.toLocaleString()}</strong> judgments found</div>
          </div>
          {data.results.length===0 && (
            <div className="empty">
              <div className="empty-t">No judgments found</div>
              <div className="empty-s">
                {data.total === 0
                  ? 'The corpus is not yet indexed. Run the EC2 pipeline to populate the database.'
                  : 'Try broader terms, or paste a citation directly (e.g. AIR 2010 SC 1)'}
              </div>
            </div>
          )}
          {data.results.map(r=><Card key={r.doc_id} r={r} onOpen={onOpen}/>)}
          {pages>1 && (
            <div className="pages">
              <button className="pg-btn" disabled={page===1} onClick={()=>setPage(p=>p-1)}>← Prev</button>
              <span className="pg-info">Page {page} of {pages}</span>
              <button className="pg-btn" disabled={page>=pages} onClick={()=>setPage(p=>p+1)}>Next →</button>
            </div>
          )}
        </>
      )}
    </div>
  );
}

// ── Judgment ──────────────────────────────────────────────────────────────────

function Judgment({ docId, onNav }) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    setLoading(true); setError(null); setData(null);
    apiFetch(`/judgment/${docId}`)
      .then(setData).catch(e=>setError(e.message))
      .finally(()=>setLoading(false));
  }, [docId]);

  if (loading) return <div className="page"><Loader/></div>;
  if (error)   return <div className="page"><Err msg={error}/></div>;
  if (!data)   return null;

  const {meta, full_text, full_text_html, citations, cited_by, ai_summary} = data;

  return (
    <div className="page fade">
      {/* Header */}
      <div className="j-hdr">
        {meta.unc_display && <div className="j-unc">{meta.unc_display}</div>}
        <h1 className="j-title">{meta.case_number||meta.doc_id}</h1>
        <div className="j-grid">
          <div className="j-item">
            <span className="j-key">Court</span>
            <span className="j-val">{meta.court_code||meta.court||'—'}</span>
          </div>
          <div className="j-item">
            <span className="j-key">Date</span>
            <span className="j-val">{meta.decision_date||'—'}</span>
          </div>
          <div className="j-item">
            <span className="j-key">Bench</span>
            <span className="j-val">{meta.bench_display||meta.bench_type||'—'}</span>
          </div>
          <div className="j-item">
            <span className="j-key">Cited by</span>
            <span className="j-val">{meta.cited_by_count} judgments</span>
          </div>
          {meta.pages && (
            <div className="j-item">
              <span className="j-key">Pages</span>
              <span className="j-val">{meta.pages}</span>
            </div>
          )}
          {meta.coram && (
            <div className="j-item" style={{gridColumn:'1/-1'}}>
              <span className="j-key">Coram</span>
              <span className="j-val" style={{fontWeight:400,fontSize:'0.86rem'}}>{meta.coram}</span>
            </div>
          )}
        </div>
        <div className="j-acts">
          {meta.copy_citation && <CopyBtn text={meta.copy_citation} label="Copy with citation"/>}
        </div>
      </div>

      {/* AI Summary */}
      {ai_summary && (
        <div className="ai-box">
          <div className="ai-hd"><span className="ai-badge">AI</span> AI Summary</div>
          {ai_summary.ratio && <div className="ai-ratio">"{ai_summary.ratio}"</div>}
          {ai_summary.held  && <div className="ai-held">{ai_summary.held}</div>}
        </div>
      )}

      {/* Body */}
      <div className="j-body">
        <div>
          {(full_text_html||full_text) ? (
            <div className="j-text"
              dangerouslySetInnerHTML={{__html: full_text_html||full_text.replace(/\n\n/g,'</p><p>')}}/>
          ) : (
            <div className="empty">
              <div className="empty-t">Text not yet indexed</div>
              <div className="empty-s">Metadata available. Full text indexing pending.</div>
            </div>
          )}
        </div>

        <div className="sidebar">
          {citations?.length>0 && (
            <div>
              <div className="sb-hd">Citations ({citations.length})</div>
              {citations.slice(0,25).map((c,i)=>(
                <div key={i} className="cit-item">
                  {c.resolved
                    ? <span className="cit-link" onClick={()=>onNav('judgment',c.cited_doc_id)}>{c.raw_text}</span>
                    : <span className="cit-unres">{c.raw_text}</span>
                  }
                  <TreatBadge t={c.treatment}/>
                </div>
              ))}
              {citations.length>25 && (
                <div style={{fontSize:'0.78rem',color:'var(--muted)',marginTop:7,fontStyle:'italic'}}>
                  +{citations.length-25} more
                </div>
              )}
            </div>
          )}
          {cited_by?.length>0 && (
            <div>
              <div className="sb-hd">Cited by ({meta.cited_by_count})</div>
              {cited_by.slice(0,15).map((c,i)=>(
                <div key={i} className="cit-item">
                  <span className="cit-link" onClick={()=>onNav('judgment',c.citing_doc_id)}>
                    {c.case_number||c.citing_doc_id}
                  </span>
                  {c.decision_date && (
                    <span style={{fontSize:'0.7rem',color:'var(--muted)',marginLeft:6}}>
                      {c.decision_date.slice(0,4)}
                    </span>
                  )}
                </div>
              ))}
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

// ── App ───────────────────────────────────────────────────────────────────────

function App() {
  const [view, setView]   = useState('home');
  const [query, setQuery] = useState('');
  const [docId, setDocId] = useState(null);
  const [stats, setStats] = useState(null);
  const [hq, setHq]       = useState('');

  useEffect(() => { injectCSS(); }, []);
  useEffect(() => {
    if (view==='home') apiFetch('/health').then(setStats).catch(()=>{});
  }, [view]);

  const goHome = () => { setView('home'); setQuery(''); setDocId(null); setHq(''); };
  const doSearch = q => { setQuery(q); setHq(q); setView('results'); };
  const openDoc  = id => { setDocId(id); setView('judgment'); };
  const nav = (v, id) => { if(v==='judgment') openDoc(id); };

  return (
    <div className="app">
      {view !== 'home' && (
        <header className="hdr">
          <div className="hdr-in">
            <span className="logo" onClick={goHome}>UPHELD<em>.</em></span>
            <div className="hdr-q">
              <input value={hq} onChange={e=>setHq(e.target.value)}
                onKeyDown={e=>e.key==='Enter'&&hq.trim()&&doSearch(hq.trim())}
                placeholder="Search judgments, citations…"/>
            </div>
          </div>
        </header>
      )}

      {view==='home'     && <Home onSearch={doSearch} stats={stats}/>}
      {view==='results'  && <Results key={query} query={query} onOpen={openDoc}/>}
      {view==='judgment' && docId && <Judgment key={docId} docId={docId} onNav={nav}/>}

      <footer className="ftr">
        <span>© 2025 Upheld · Data: AWS Open Data (CC-BY-4.0) · <a href="/docs" target="_blank">API docs</a></span>
        <span style={{fontSize:'0.72rem'}}>UPHELD citations are permanent stable identifiers, not court-accepted citations.</span>
      </footer>
    </div>
  );
}

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