const PROJECT_FIELDS = [
  { key: 'LOG TITLE',         label: 'Log Title' },
  { key: 'HOLE NO',           label: 'Hole No.' },
  { key: 'PROJECT NO',        label: 'Project No.' },
  { key: 'PROJECT',           label: 'Project' },
  { key: 'LOCATION',          label: 'Location' },
  { key: 'STARTED',           label: 'Started' },
  { key: 'FINISHED',          label: 'Finished' },
  { key: 'DRILL METHOD',      label: 'Drill Method' },
  { key: 'GROUND ELEV. (M)',  label: 'Ground Elev. (m)', type: 'number' },
  { key: 'COORDINATES (M) N', label: 'N (m)' },
  { key: 'COORDINATES (M) E', label: 'E (m)' },
  { key: 'LOGGED BY',         label: 'Logged By' },
  { key: 'CHECKED BY',        label: 'Checked By' },
  { key: 'WATER LEVEL (M)',   label: 'Water Level (m)', type: 'number' },
];

const SOIL_TYPES = ['', 'ASPHALT', 'CLAY', 'GRAVEL', 'SAND', 'BACKFILL'];

const Field = ({ label, value, type = 'text', onChange }) => (
  <div className="field">
    <label>{label}</label>
    <input
      type={type}
      value={value ?? ''}
      onChange={e => onChange(type === 'number' && e.target.value !== '' ? Number(e.target.value) : e.target.value)}
    />
  </div>
);

const Checkbox = ({ label, value, onChange }) => (
  <div className="field checkbox">
    <input type="checkbox" checked={!!value} onChange={e => onChange(e.target.checked)} />
    <label>{label}</label>
  </div>
);

const ProjectForm = ({ payload, setPayload }) => {
  if (!payload) return null;
  const info = payload.info || {};
  const setInfo = (key, value) =>
    setPayload(p => ({ ...p, info: { ...p.info, [key]: value } }));
  const setTop = (key, value) =>
    setPayload(p => ({ ...p, [key]: value }));
  return (
    <>
      <h2>Project</h2>
      {PROJECT_FIELDS.map(f => (
        <Field
          key={f.key}
          label={f.label}
          type={f.type || 'text'}
          value={info[f.key]}
          onChange={v => setInfo(f.key, v)}
        />
      ))}
    </>
  );
};

const SettingsForm = ({ payload, setPayload }) => {
  if (!payload) return null;
  const setTop = (key, value) =>
    setPayload(p => ({ ...p, [key]: value }));
  return (
    <>
      <h2>Settings</h2>
      <Field
        label="Depth / Sheet (m)"
        type="number"
        value={payload.depth_per_sheet_m}
        onChange={v => setTop('depth_per_sheet_m', v)}
      />
    </>
  );
};

const LayersForm = ({ payload, setPayload }) => {
  if (!payload) return null;
  const layers = payload.layers || [];

  const updateLayer = (i, key, value) =>
    setPayload(p => ({
      ...p,
      layers: p.layers.map((l, idx) => idx === i ? { ...l, [key]: value } : l),
    }));

  const addLayer = () => {
    const last = layers[layers.length - 1];
    const newTop = last ? last.depth_end_m : 0;
    setPayload(p => ({
      ...p,
      layers: [...(p.layers || []), {
        depth_start_m: newTop,
        depth_end_m: newTop + 1,
        desc: '',
        soil_type: 'SAND',
      }],
    }));
  };

  const removeLayer = (i) =>
    setPayload(p => ({ ...p, layers: p.layers.filter((_, idx) => idx !== i) }));

  return (
    <>
      <h2>Layers</h2>
      {layers.map((l, i) => (
        <div className="row" key={i}>
          <div className="row-header">
            <span>#{i + 1}</span>
            <button className="del" onClick={() => removeLayer(i)} title="Remove">×</button>
          </div>
          <div className="row-fields">
            <span className="row-label">Depth (m): top — bottom</span>
            <div className="field-pair">
              <input
                type="number"
                step="0.01"
                value={l.depth_start_m ?? ''}
                onChange={e => updateLayer(i, 'depth_start_m', e.target.value === '' ? '' : Number(e.target.value))}
                placeholder="top"
              />
              <input
                type="number"
                step="0.01"
                value={l.depth_end_m ?? ''}
                onChange={e => updateLayer(i, 'depth_end_m', e.target.value === '' ? '' : Number(e.target.value))}
                placeholder="bot"
              />
            </div>
            <span className="row-label">Soil Type</span>
            <select
              value={l.soil_type || ''}
              onChange={e => updateLayer(i, 'soil_type', e.target.value || null)}
            >
              {SOIL_TYPES.map(t => <option key={t} value={t}>{t || '(none)'}</option>)}
            </select>
            <span className="row-label">Description</span>
            <textarea
              value={l.desc || ''}
              onChange={e => updateLayer(i, 'desc', e.target.value)}
              placeholder="description"
              rows={2}
            />
          </div>
        </div>
      ))}
      <button className="add" onClick={addLayer}>+ Add Layer</button>
    </>
  );
};

const TEST_TYPES = ['SPT', 'SHELBY', 'UC2', 'VANE', ''];

const SamplesForm = ({ payload, setPayload }) => {
  const [expanded, setExpanded] = React.useState(() => new Set());
  if (!payload) return null;
  const tests = payload.tests || [];

  const toggleExpand = (i) => {
    setExpanded(prev => {
      const next = new Set(prev);
      next.has(i) ? next.delete(i) : next.add(i);
      return next;
    });
  };

  const updateTest = (i, key, value) =>
    setPayload(p => ({
      ...p,
      tests: p.tests.map((t, idx) => idx === i ? { ...t, [key]: value } : t),
    }));

  const num = (v) => (v === '' ? null : Number(v));

  const addTest = () => {
    const last = tests[tests.length - 1];
    const top = last ? (last.depth_bot_m ?? 0) + 0.4 : 1;
    setPayload(p => ({
      ...p,
      tests: [...(p.tests || []), {
        no: (last?.no || 0) + 1,
        test: 'SPT',
        depth_top_m: top,
        depth_bot_m: top + 0.6,
        recovery: null,
        spt1: null, spt2: null, spt3: null,
        wp_pct: null, w_pct: null, wl_pct: null, fines_pct: null,
        su_kPa: null, su_kind: null,
      }],
    }));
  };

  const removeTest = (i) =>
    setPayload(p => ({ ...p, tests: p.tests.filter((_, idx) => idx !== i) }));

  return (
    <>
      <h2>Samples</h2>
      {tests.map((t, i) => {
        const isOpen = expanded.has(i);
        return (
          <div className="row" key={i}>
            <div className="row-header">
              <span>#{t.no ?? i + 1}</span>
              <button className="del" onClick={() => removeTest(i)} title="Remove">×</button>
            </div>
            <div className="row-fields">
              <span className="row-label">No. — Test Type</span>
              <div className="field-pair">
                <input
                  type="number"
                  value={t.no ?? ''}
                  onChange={e => updateTest(i, 'no', num(e.target.value))}
                  placeholder="no"
                />
                <select
                  value={t.test || ''}
                  onChange={e => updateTest(i, 'test', e.target.value || null)}
                >
                  {TEST_TYPES.map(x => <option key={x} value={x}>{x || '(none)'}</option>)}
                </select>
              </div>
              <span className="row-label">Depth (m): top — bottom</span>
              <div className="field-pair">
                <input
                  type="number" step="0.01"
                  value={t.depth_top_m ?? ''}
                  onChange={e => updateTest(i, 'depth_top_m', num(e.target.value))}
                  placeholder="top"
                />
                <input
                  type="number" step="0.01"
                  value={t.depth_bot_m ?? ''}
                  onChange={e => updateTest(i, 'depth_bot_m', num(e.target.value))}
                  placeholder="bot"
                />
              </div>
              <span className="row-label">Recovery %</span>
              <input
                type="number"
                value={t.recovery ?? ''}
                onChange={e => updateTest(i, 'recovery', num(e.target.value))}
                placeholder="recovery"
              />
              <button className="expand-toggle" onClick={() => toggleExpand(i)}>
                {isOpen ? '▾ Hide' : '▸ Show more'}
              </button>
              {isOpen && (
                <>
                  <span className="row-label">SPT: 1 / 2 / 3</span>
                  <div className="field-triple">
                    <input
                      type="number"
                      value={t.spt1 ?? ''}
                      onChange={e => updateTest(i, 'spt1', num(e.target.value))}
                      placeholder="SPT 1"
                    />
                    <input
                      type="number"
                      value={t.spt2 ?? ''}
                      onChange={e => updateTest(i, 'spt2', num(e.target.value))}
                      placeholder="SPT 2"
                    />
                    <input
                      type="number"
                      value={t.spt3 ?? ''}
                      onChange={e => updateTest(i, 'spt3', num(e.target.value))}
                      placeholder="SPT 3"
                    />
                  </div>
                  <span className="row-label">Wp % / W % / Wl %</span>
                  <div className="field-triple">
                    <input
                      type="number"
                      value={t.wp_pct ?? ''}
                      onChange={e => updateTest(i, 'wp_pct', num(e.target.value))}
                      placeholder="Wp"
                    />
                    <input
                      type="number"
                      value={t.w_pct ?? ''}
                      onChange={e => updateTest(i, 'w_pct', num(e.target.value))}
                      placeholder="W"
                    />
                    <input
                      type="number"
                      value={t.wl_pct ?? ''}
                      onChange={e => updateTest(i, 'wl_pct', num(e.target.value))}
                      placeholder="Wl"
                    />
                  </div>
                  <span className="row-label">Fines %</span>
                  <input
                    type="number"
                    value={t.fines_pct ?? ''}
                    onChange={e => updateTest(i, 'fines_pct', num(e.target.value))}
                    placeholder="fines"
                  />
                  <span className="row-label">Su (kPa)</span>
                  <div className="field-pair">
                    <input
                      type="number"
                      value={t.su_kPa ?? ''}
                      onChange={e => updateTest(i, 'su_kPa', num(e.target.value))}
                      placeholder="Su"
                    />
                    <select
                      value={t.su_kind || ''}
                      onChange={e => updateTest(i, 'su_kind', e.target.value || null)}
                    >
                      <option value="">(none)</option>
                      {SU_KINDS.map(k => <option key={k} value={k}>{k}</option>)}
                    </select>
                  </div>
                </>
              )}
            </div>
          </div>
        );
      })}
      <button className="add" onClick={addTest}>+ Add Sample</button>
    </>
  );
};

const SU_KINDS = ['field_peak', 'field_remold', 'lab_peak', 'lab_remold', 'uc2_peak', 'uc2_remold'];

const SuPointsForm = ({ payload, setPayload }) => {
  if (!payload) return null;
  const pts = payload.su_points || [];
  const num = (v) => (v === '' ? null : Number(v));

  const update = (i, key, value) =>
    setPayload(p => ({
      ...p,
      su_points: p.su_points.map((x, idx) => idx === i ? { ...x, [key]: value } : x),
    }));

  const add = () =>
    setPayload(p => ({
      ...p,
      su_points: [...(p.su_points || []), { depth_m: 0, su_kPa: 0, su_kind: 'field_peak' }],
    }));

  const remove = (i) =>
    setPayload(p => ({ ...p, su_points: p.su_points.filter((_, idx) => idx !== i) }));

  return (
    <>
      <h2>Su Points</h2>
      {pts.map((s, i) => (
        <div className="row" key={i}>
          <div className="row-header">
            <span>#{i + 1}</span>
            <button className="del" onClick={() => remove(i)} title="Remove">×</button>
          </div>
          <div className="row-fields">
            <span className="row-label">Depth (m) — Su (kPa)</span>
            <div className="field-pair">
              <input
                type="number" step="0.01"
                value={s.depth_m ?? ''}
                onChange={e => update(i, 'depth_m', num(e.target.value))}
                placeholder="depth"
              />
              <input
                type="number"
                value={s.su_kPa ?? ''}
                onChange={e => update(i, 'su_kPa', num(e.target.value))}
                placeholder="Su"
              />
            </div>
            <span className="row-label">Kind</span>
            <select
              value={s.su_kind || ''}
              onChange={e => update(i, 'su_kind', e.target.value)}
            >
              {SU_KINDS.map(k => <option key={k} value={k}>{k}</option>)}
            </select>
          </div>
        </div>
      ))}
      <button className="add" onClick={add}>+ Add Su Point</button>
    </>
  );
};

const PiezoForm = ({ payload, setPayload }) => {
  if (!payload) return null;
  const piezo = payload.piezo || {};
  const set = (key, v) =>
    setPayload(p => ({ ...p, piezo: { ...p.piezo, [key]: v } }));
  return (
    <>
      <h2>Piezometer</h2>
      <Field
        label="Hole Depth (m)"
        type="number"
        value={piezo.bottom_depth_m}
        onChange={v => set('bottom_depth_m', v === '' ? null : v)}
      />
      <Field
        label="Piezo Bottom (m)"
        type="number"
        value={piezo.bottom_piezo_m}
        onChange={v => set('bottom_piezo_m', v === '' ? null : v)}
      />
      <Field
        label="Screen Top (m)"
        type="number"
        value={piezo.top_screen_m}
        onChange={v => set('top_screen_m', v === '' ? null : v)}
      />
      <Field
        label="Screen Bottom (m)"
        type="number"
        value={piezo.bottom_screen_m}
        onChange={v => set('bottom_screen_m', v === '' ? null : v)}
      />
    </>
  );
};

const NotesForm = ({ payload, setPayload }) => {
  if (!payload) return null;
  return (
    <>
      <h2>Notes</h2>
      <textarea
        className="notes"
        rows={6}
        value={payload.notes || ''}
        onChange={e => setPayload(p => ({ ...p, notes: e.target.value }))}
        placeholder="Notes appear on the last page"
      />
    </>
  );
};

const fmtValidationError = (detail) => {
  if (!Array.isArray(detail)) return JSON.stringify(detail);
  return detail.map(d => `${(d.loc || []).join('.')}: ${d.msg}`).join(' | ');
};

const STORAGE_KEY = 'soil_log_payload_v1';
const WIDTH_KEY = 'soil_log_left_width_v1';
const MIN_LEFT = 240;
const MAX_LEFT = 600;

const EMPTY_PAYLOAD = {
  depth_per_sheet_m: 20,
  info: {},
  layers: [],
  tests: [],
  piezo: {},
  su_points: [],
  notes: '',
};

const TABS = [
  { id: 'project',  label: 'Project' },
  { id: 'layers',   label: 'Layers' },
  { id: 'samples',  label: 'Samples' },
  { id: 'piezo',    label: 'Piezo' },
  { id: 'notes',    label: 'Notes' },
  { id: 'settings', label: 'Settings' },
];

const FileMenu = ({ items, disabled }) => {
  const [open, setOpen] = React.useState(false);
  const ref = React.useRef(null);
  React.useEffect(() => {
    if (!open) return;
    const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    const onKey = (e) => { if (e.key === 'Escape') setOpen(false); };
    document.addEventListener('mousedown', onDoc);
    document.addEventListener('keydown', onKey);
    return () => {
      document.removeEventListener('mousedown', onDoc);
      document.removeEventListener('keydown', onKey);
    };
  }, [open]);
  return (
    <div className="filemenu" ref={ref}>
      <button onClick={() => setOpen(o => !o)} disabled={disabled} aria-haspopup="menu" aria-expanded={open}>
        File ▾
      </button>
      {open && (
        <div className="filemenu-list" role="menu">
          {items.map((it, i) => (
            <button
              key={i}
              role="menuitem"
              disabled={it.disabled}
              onClick={() => { setOpen(false); it.onClick(); }}
            >
              {it.label}
            </button>
          ))}
        </div>
      )}
    </div>
  );
};

const App = () => {
  const [payload, setPayload] = React.useState(null);
  const [pageUrls, setPageUrls] = React.useState([]);
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState('');
  const [tab, setTab] = React.useState('project');
  const [zoom, setZoom] = React.useState(1);
  const [leftWidth, setLeftWidth] = React.useState(() => {
    const v = parseInt(localStorage.getItem(WIDTH_KEY) || '', 10);
    return Number.isFinite(v) && v >= MIN_LEFT && v <= MAX_LEFT ? v : 440;
  });
  const fileInputRef = React.useRef(null);

  React.useEffect(() => {
    try { localStorage.setItem(WIDTH_KEY, String(leftWidth)); } catch (_) {}
  }, [leftWidth]);

  const startDrag = (e) => {
    e.preventDefault();
    const onMove = (ev) => {
      const w = Math.max(MIN_LEFT, Math.min(MAX_LEFT, ev.clientX));
      setLeftWidth(w);
    };
    const onUp = () => {
      window.removeEventListener('mousemove', onMove);
      window.removeEventListener('mouseup', onUp);
      document.body.style.cursor = '';
      document.body.style.userSelect = '';
    };
    document.body.style.cursor = 'col-resize';
    document.body.style.userSelect = 'none';
    window.addEventListener('mousemove', onMove);
    window.addEventListener('mouseup', onUp);
  };

  const isEmptyPayload = (p) =>
    !p ||
    ((p.layers?.length ?? 0) === 0 &&
     (p.tests?.length ?? 0) === 0 &&
     (p.su_points?.length ?? 0) === 0 &&
     Object.keys(p.info || {}).length === 0 &&
     !p.notes);

  const loadSample = () => {
    fetch('/api/sample')
      .then(r => r.json())
      .then(data => {
        setPayload(data);
        refresh(data);
      })
      .catch(e => setErr('Sample load failed: ' + e.message));
  };

  // 9a: Load from localStorage (if non-empty) or /api/sample on mount
  React.useEffect(() => {
    try {
      const saved = localStorage.getItem(STORAGE_KEY);
      if (saved) {
        const parsed = JSON.parse(saved);
        if (!isEmptyPayload(parsed)) {
          setPayload(parsed);
          return;
        }
      }
    } catch (_) {}
    fetch('/api/sample')
      .then(r => r.json())
      .then(setPayload)
      .catch(e => setErr('Sample load failed: ' + e.message));
  }, []);

  // 9a: Auto-save on every payload change
  React.useEffect(() => {
    if (payload) {
      try { localStorage.setItem(STORAGE_KEY, JSON.stringify(payload)); }
      catch (_) {}
    }
  }, [payload]);

  const fetchPage = async (n, body) => {
    let json;
    try {
      json = JSON.stringify(body);
    } catch (e) {
      throw { status: 0, text: 'Payload not serializable. Clearing saved state — please reload.' };
    }
    const r = await fetch(`/api/preview?page=${n}`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: json,
    });
    if (!r.ok) throw { status: r.status, text: await r.text() };
    const total = parseInt(r.headers.get('X-Page-Total') || '1', 10);
    const blob = await r.blob();
    return { url: URL.createObjectURL(blob), total };
  };

  const refresh = async (overridePayload) => {
    const p = overridePayload || payload;
    if (!p) return;
    setBusy(true);
    setErr('');
    try {
      const first = await fetchPage(1, p);
      const rest = await Promise.all(
        Array.from({ length: first.total - 1 }, (_, i) => fetchPage(i + 2, p))
      );
      const urls = [first.url, ...rest.map(r => r.url)];
      setPageUrls(prev => {
        prev.forEach(URL.revokeObjectURL);
        return urls;
      });
    } catch (e) {
      if (e.status === 422) {
        try { setErr('Validation: ' + fmtValidationError(JSON.parse(e.text).detail)); }
        catch { setErr('Validation: ' + e.text); }
      } else if (e.status === 0) {
        try { localStorage.removeItem(STORAGE_KEY); } catch (_) {}
        setErr(e.text);
      } else {
        setErr('Preview error: ' + (e.message || e.text || e));
      }
    } finally {
      setBusy(false);
    }
  };

  const downloadPDF = async () => {
    if (!payload) return;
    setBusy(true);
    setErr('');
    try {
      const r = await fetch('/api/generate', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(payload),
      });
      if (r.status === 422) {
        const body = await r.json();
        setErr('Validation: ' + fmtValidationError(body.detail));
        return;
      }
      if (!r.ok) {
        setErr(`PDF failed: ${r.status} ${await r.text()}`);
        return;
      }
      const cd = r.headers.get('Content-Disposition') || '';
      const m = cd.match(/filename="([^"]+)"/);
      const filename = m ? m[1] : 'soil_log.pdf';
      const blob = await r.blob();
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = filename;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      URL.revokeObjectURL(url);
    } catch (e) {
      setErr('PDF error: ' + e.message);
    } finally {
      setBusy(false);
    }
  };

  // 9b: Save JSON to file
  const saveJSON = () => {
    if (!payload) return;
    const hole = (payload.info?.['HOLE NO'] || 'soil_log').replace(/[^\w\-]+/g, '_');
    const blob = new Blob([JSON.stringify(payload, null, 2)], { type: 'application/json' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `${hole}.json`;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
  };

  // 9b: Load JSON from file
  const loadJSON = (e) => {
    const file = e.target.files?.[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = (ev) => {
      try {
        const data = JSON.parse(ev.target.result);
        setPayload(data);
        setErr('');
        refresh(data);
      } catch (err) {
        setErr('Load failed: ' + err.message);
      }
    };
    reader.readAsText(file);
    e.target.value = '';
  };

  // 9c: New blank log + auto refresh
  const newLog = () => {
    if (!confirm('Clear all data and start new log?')) return;
    const empty = { ...EMPTY_PAYLOAD };
    setPayload(empty);
    refresh(empty);
  };

  const initialized = React.useRef(false);
  React.useEffect(() => {
    if (payload && !initialized.current) {
      initialized.current = true;
      refresh();
    }
  }, [payload]);

  // Hotkey: Ctrl+Enter (or Cmd+Enter on Mac) → Preview
  React.useEffect(() => {
    const onKey = (e) => {
      if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
        e.preventDefault();
        if (!busy && payload) refresh();
      }
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [busy, payload]);

  return (
    <div className="app">
      <header className="toolbar">
        <h1>Soil Log Builder</h1>
        <div className="toolbar-actions">
          <FileMenu
            disabled={busy}
            items={[
              { label: 'New',       onClick: newLog },
              { label: 'Sample',    onClick: loadSample },
              { label: 'Load JSON', onClick: () => fileInputRef.current?.click() },
              { label: 'Save JSON', onClick: saveJSON, disabled: !payload },
            ]}
          />
          <input
            type="file"
            accept="application/json,.json"
            ref={fileInputRef}
            onChange={loadJSON}
            style={{ display: 'none' }}
          />
          <button onClick={() => refresh()} disabled={busy || !payload} title="Preview (Ctrl+Enter)">
            {busy ? 'Rendering…' : 'Preview'}
          </button>
          <button className="primary" onClick={downloadPDF} disabled={busy || !payload}>
            Download
          </button>
        </div>
      </header>
      {err && <div className="error">{err}</div>}
      <div className="body" style={{ gridTemplateColumns: `${leftWidth}px 4px 1fr` }}>
        <aside className="left">
          <div className="tabs">
            {TABS.map(t => (
              <button
                key={t.id}
                className={`tab ${tab === t.id ? 'active' : ''}`}
                onClick={() => setTab(t.id)}
              >
                {t.label}
              </button>
            ))}
          </div>
          <div className="form">
            {tab === 'project' && <ProjectForm payload={payload} setPayload={setPayload} />}
            {tab === 'layers'  && <LayersForm  payload={payload} setPayload={setPayload} />}
            {tab === 'samples' && (
              <>
                <SamplesForm  payload={payload} setPayload={setPayload} />
                <SuPointsForm payload={payload} setPayload={setPayload} />
              </>
            )}
            {tab === 'piezo'   && <PiezoForm    payload={payload} setPayload={setPayload} />}
            {tab === 'notes'    && <NotesForm    payload={payload} setPayload={setPayload} />}
            {tab === 'settings' && <SettingsForm payload={payload} setPayload={setPayload} />}
          </div>
        </aside>
        <div className="splitter" onMouseDown={startDrag} title="Drag to resize" />
        <div className="preview-pane">
          <div className="preview-toolbar">
            <span className="page-info">
              {pageUrls.length === 0 ? '—' : `${pageUrls.length} page${pageUrls.length === 1 ? '' : 's'}`}
            </span>
            <div className="zoom">
              <button onClick={() => setZoom(z => Math.max(0.4, +(z - 0.1).toFixed(2)))} title="Zoom out">−</button>
              <span>{Math.round(zoom * 100)}%</span>
              <button onClick={() => setZoom(z => Math.min(2.0, +(z + 0.1).toFixed(2)))} title="Zoom in">+</button>
            </div>
          </div>
          <div className="preview-canvas">
            {pageUrls.length === 0 ? (
              <div className="placeholder">Loading…</div>
            ) : (
              <div className="pages" style={{ transform: `scale(${zoom})`, transformOrigin: 'top center' }}>
                {pageUrls.map((url, i) => (
                  <img key={i} src={url} alt={`page ${i + 1}`} />
                ))}
              </div>
            )}
          </div>
        </div>
      </div>
    </div>
  );
};

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