// PatientForm.jsx — Cover → Write → Preview → (confirm popup) → Thank you.
// One submission per serial. Every screen fades/slides in. Write screen fits with no scroll.
const { useState: useStatePF, useRef: useRefPF } = React;

// ── compact attachment tile (inline, no scroll) ──
function FileTile({ label, hint, accept, items, onAdd, onRemove, icon, addedLabel }) {
  const T = window.TOK;
  const ref = useRefPF(null);
  const n = items.length;
  return (
    <div style={{ flex: 1, border: `1px solid ${n ? T.accent : T.line}`, borderRadius: 16, background: n ? T.accentSoft : T.surface, transition: 'all .15s' }}>
      <button onClick={() => ref.current?.click()} style={{
        width: '100%', display: 'flex', flexDirection: 'column', gap: 6, padding: '13px 14px',
        background: 'none', border: 'none', cursor: 'pointer', fontFamily: 'inherit', textAlign: 'left',
      }}>
        <span style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
          <span style={{ width: 38, height: 38, borderRadius: 11, background: n ? '#fff' : T.accentSoft, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>{icon}</span>
          {n > 0 && <span style={{ fontSize: 13, fontWeight: 700, color: T.accentDeep }}>{n} {addedLabel || 'added'}</span>}
        </span>
        <span style={{ display: 'block', fontSize: 15.5, fontWeight: 600, color: T.ink }}>{label}</span>
        <span style={{ display: 'block', fontSize: 12.5, color: T.faint }}>{hint}</span>
      </button>
      <input ref={ref} type="file" accept={accept} multiple style={{ display: 'none' }}
        onChange={e => { onAdd(Array.from(e.target.files)); e.target.value = ''; }} />
    </div>
  );
}

function FileChip({ f, onRemove }) {
  const T = window.TOK;
  return (
    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8, padding: '7px 9px 7px 8px', background: T.surface, border: `1px solid ${T.line}`, borderRadius: 10 }}>
      {f.url && f.kind === 'image'
        ? <img src={f.url} style={{ width: 30, height: 30, borderRadius: 7, objectFit: 'cover' }} />
        : <span style={{ width: 30, height: 30, borderRadius: 7, background: T.boneDeep, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 15 }}>{f.kind === 'video' ? '🎬' : '📄'}</span>}
      <span style={{ fontSize: 13.5, color: T.inkSoft, maxWidth: 150, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{f.name}</span>
      {onRemove && <button onClick={onRemove} style={{ border: 'none', background: 'none', cursor: 'pointer', color: T.faint, fontSize: 19, lineHeight: 1, padding: '0 2px' }}>×</button>}
    </span>
  );
}

function Field({ children, label, grow, width }) {
  const T = window.TOK;
  return (
    <div style={{ flex: grow ? 1 : undefined, width, display: 'flex', flexDirection: 'column', gap: 7 }}>
      <span style={{ fontSize: 13, fontWeight: 600, color: T.inkSoft, paddingLeft: 2 }}>{label}</span>
      {children}
    </div>
  );
}

// transition-based screen entrance (robust under html-to-image capture)
function ScreenFade({ children }) {
  const [on, setOn] = useStatePF(false);
  React.useEffect(() => { const r = requestAnimationFrame(() => setOn(true)); return () => cancelAnimationFrame(r); }, []);
  return (
    <div style={{ height: '100%', opacity: on ? 1 : 0, transform: on ? 'none' : 'translateY(16px)', transition: 'opacity .44s cubic-bezier(.4,.02,.2,1), transform .44s cubic-bezier(.4,.02,.2,1)' }}>{children}</div>
  );
}

function PatientForm({ onSubmit, submittedPhones = [], online, onStaffLogin, lang = 'en', onLang, onScreenChange }) {
  const T = window.TOK, B = window.BRAND, UI = window.UI, D = window.dict(lang), API = window.API;
  const [screen, setScreen] = useStatePF('cover');   // cover | lookup | register | write | preview | done
  React.useEffect(() => { onScreenChange && onScreenChange(screen); }, [screen]);
  const [modal, setModal] = useStatePF(null);        // null | 'confirm' | 'dup'
  const [d, setD] = useStatePF({ name: '', age: '', phone: '', address: '', complaint: '', reports: [], media: [] });
  const [phoneErr, setPhoneErr] = useStatePF('');
  const [busy, setBusy] = useStatePF(false);         // uploading + submitting (online)
  const [doneSerial, setDoneSerial] = useStatePF(null);
  const [editing, setEditing] = useStatePF(null);    // null | { id, serial } — editing an existing submission
  const [lockUntil, setLockUntil] = useStatePF(null);// ISO string when a phone is locked
  const [lookupPhone, setLookupPhone] = useStatePF('');
  const [lookupErr, setLookupErr] = useStatePF('');
  const set = (k, v) => setD(p => ({ ...p, [k]: v }));

  const hoursLeft = (iso) => Math.max(1, Math.round((Date.parse(iso) - Date.now()) / 3600000));

  // split a stored submission's flat file list back into the two tiles
  const splitFiles = (files = []) => ({
    reports: files.filter(f => f.type === 'pdf').map(f => ({ name: f.name, kind: 'pdf', url: f.url, file: null })),
    media: files.filter(f => f.type === 'image' || f.type === 'video').map(f => ({ name: f.name, kind: f.type, url: f.url, file: null })),
  });

  // load a submission into the form for viewing/editing, then jump to preview
  const loadForEdit = (sub) => {
    const { reports, media } = splitFiles(sub.files);
    setD({ name: sub.name || '', age: sub.age || '', phone: sub.phone || '', address: sub.address || '', complaint: sub.complaint || '', reports, media });
    setEditing({ id: sub.id, serial: sub.serial });
    setPhoneErr(''); setLookupErr(''); setModal(null); setScreen('preview');
  };

  // look up an existing submission by phone (the patient self-service entry)
  const findSubmission = async (phone) => {
    if (busy) return;
    const p = normPhone(phone);
    if (!/^01\d{9}$/.test(p)) { setLookupErr(D.phone_bad); return; }
    setBusy(true); setLookupErr('');
    try {
      const r = await API.mySubmission(p);
      if (r && r.submission) loadForEdit(r.submission);
      else setLookupErr(D.lookup_none);
    } catch (e) { setLookupErr(D.lookup_none); }
    finally { setBusy(false); }
  };

  const addFiles = (key, files) => {
    const mapped = files.map(f => {
      const isImg = f.type.startsWith('image'), isVid = f.type.startsWith('video');
      return { name: f.name, kind: isImg ? 'image' : isVid ? 'video' : 'pdf', url: isImg ? URL.createObjectURL(f) : null, file: f };
    });
    set(key, [...d[key], ...mapped]);
  };
  const removeFile = (key, i) => set(key, d[key].filter((_, j) => j !== i));

  const normPhone = (s) => (s || '').replace(/[\s-]/g, '');
  const phoneDigits = normPhone(d.phone);
  const phoneValid = /^01\d{9}$/.test(phoneDigits);          // 11-digit BD mobile (0 1 + 9)
  const regReady = d.name.trim() && phoneValid;
  const phoneUsed = () => submittedPhones.map(normPhone).includes(phoneDigits);

  const goFromRegister = async () => {
    if (!d.name.trim() || busy) return;
    if (!phoneDigits) { setPhoneErr(D.phone_req); return; }
    if (!phoneValid) { setPhoneErr(D.phone_bad); return; }
    if (editing) { setPhoneErr(''); setScreen('write'); return; }   // own submission — no dedupe
    if (online && API) {
      setBusy(true);
      try { const r = await API.checkPhone(phoneDigits); if (!r.available) { setLockUntil(r.lockedUntil); setModal('dup'); return; } }
      catch (e) { /* network hiccup — let the final-send guard catch a true duplicate */ }
      finally { setBusy(false); }
    } else if (phoneUsed()) { setModal('dup'); return; }
    setPhoneErr(''); setScreen('write');
  };

  const confirmSubmit = async () => {
    if (busy) return;
    // ── online: upload new attachments, then create or update on the server ──
    if (online && API) {
      setBusy(true);
      try {
        const upload = async (f) => f.file ? API.uploadFile(f.file) : { type: f.kind, name: f.name, url: f.url };
        const files = await Promise.all([...d.reports, ...d.media].map(upload));
        const payload = { name: d.name, phone: phoneDigits, age: d.age, address: d.address, complaint: d.complaint, files };
        const r = editing ? await API.updateSubmission(editing.id, payload) : await API.createSubmission(payload);
        setDoneSerial(r.serial); setModal(null); setScreen('done');
      } catch (e) {
        if (e && (e.status === 409 || e.message === 'duplicate')) { setLockUntil(e.data && e.data.lockedUntil); setModal('dup'); }
        else { setPhoneErr(D.send_failed || 'Could not send — please try again.'); setModal(null); }
      } finally { setBusy(false); }
      return;
    }
    // ── offline: in-memory prototype path ──
    if (phoneUsed()) { setModal('dup'); return; }
    onSubmit && onSubmit({
      id: 'new_' + Date.now(),
      name: d.name, age: d.age, phone: phoneDigits, address: d.address, sex: '', status: 'submitted', complaint: d.complaint,
      files: [...d.reports, ...d.media].map(f => ({ type: f.kind, name: f.name, url: f.url })),
      submittedAt: new Date().toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }), isNew: true,
    });
    setModal(null); setScreen('done');
  };

  const reset = () => {
    setD({ name: '', age: '', phone: '', address: '', complaint: '', reports: [], media: [] });
    setPhoneErr(''); setDoneSerial(null); setEditing(null); setLockUntil(null);
    setLookupPhone(''); setLookupErr(''); setScreen('cover');
  };

  // icons
  const icoReport = <svg width="20" height="20" viewBox="0 0 24 24" fill="none"><path d="M6 2h8l4 4v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2Z" stroke={T.accent} strokeWidth="1.7"/><path d="M14 2v4h4M8 13h8M8 17h5" stroke={T.accent} strokeWidth="1.7" strokeLinecap="round"/></svg>;
  const icoMedia = <svg width="20" height="20" viewBox="0 0 24 24" fill="none"><rect x="3" y="5" width="18" height="14" rx="2.5" stroke={T.accent} strokeWidth="1.7"/><circle cx="8.5" cy="10" r="1.7" fill={T.accent}/><path d="M4 17l4.5-4.5L13 16l3-2.5L20 17" stroke={T.accent} strokeWidth="1.7" strokeLinejoin="round"/></svg>;

  const inputStyle = { width: '100%', boxSizing: 'border-box', height: 50, padding: '0 15px', fontFamily: 'inherit', fontSize: 16.5, color: T.ink, background: T.surface, border: `1px solid ${T.line}`, borderRadius: 13, outline: 'none' };
  const onFocus = e => e.target.style.borderColor = T.accent;
  const onBlur = e => e.target.style.borderColor = T.line;

  // ─────────────────────────── screen bodies ───────────────────────────
  let body = null;

  if (screen === 'cover') body = <window.Cover onStart={() => setScreen('choose')} onStaffLogin={onStaffLogin} />;

  // CHOOSE — register new vs. update an existing submission
  if (screen === 'choose') {
    const startNew = () => { setEditing(null); setD({ name: '', age: '', phone: '', address: '', complaint: '', reports: [], media: [] }); setPhoneErr(''); setScreen('register'); };
    const ChoiceCard = ({ onClick, icon, title, sub, primary }) => (
      <button onClick={onClick} style={{
        width: '100%', textAlign: 'left', cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 16,
        padding: '20px 18px', borderRadius: 18, fontFamily: 'inherit',
        background: primary ? T.accent : T.surface, border: `1px solid ${primary ? T.accent : T.line}`,
        boxShadow: primary ? '0 14px 30px rgba(28,119,206,0.28)' : '0 1px 2px rgba(17,42,65,0.04)',
      }}>
        <span style={{ width: 52, height: 52, borderRadius: 14, flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', background: primary ? 'rgba(255,255,255,0.18)' : T.accentSoft }}>{icon}</span>
        <span style={{ flex: 1, minWidth: 0 }}>
          <span style={{ display: 'block', fontFamily: "'Anek Bangla', 'Hind Siliguri', sans-serif", fontWeight: 600, fontSize: 19, color: primary ? '#fff' : T.ink, lineHeight: 1.2 }}>{title}</span>
          <span style={{ display: 'block', fontSize: 13.5, color: primary ? 'rgba(255,255,255,0.85)' : T.muted, marginTop: 4, lineHeight: 1.4 }}>{sub}</span>
        </span>
        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" style={{ flexShrink: 0 }}><path d="M5 12h14M13 6l6 6-6 6" stroke={primary ? '#fff' : T.faint} strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"/></svg>
      </button>
    );
    body = (
      <div style={{ height: '100%', display: 'flex', flexDirection: 'column', background: T.bone, fontFamily: "'Hanken Grotesk', sans-serif" }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '56px 22px 10px', flexShrink: 0 }}>
          <button onClick={() => setScreen('cover')} style={{ width: 40, height: 40, borderRadius: 11, border: `1px solid ${T.line}`, background: T.surface, cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none"><path d="M15 6l-6 6 6 6" stroke={T.inkSoft} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/></svg>
          </button>
          <div style={{ fontFamily: "'Anek Bangla', 'Hind Siliguri', sans-serif", fontWeight: 600, fontSize: 23, color: T.ink, lineHeight: 1.15 }}>{D.choose_title}</div>
          <window.LangToggle lang={lang} onLang={onLang} style={{ marginLeft: 'auto' }} />
        </div>
        <div style={{ flex: 1, display: 'flex', flexDirection: 'column', justifyContent: 'center', gap: 16, padding: '0 22px 40px' }}>
          <ChoiceCard primary onClick={startNew} title={D.choose_new} sub={D.choose_new_sub}
            icon={<svg width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M12 5v14M5 12h14" stroke="#fff" strokeWidth="2.4" strokeLinecap="round"/></svg>} />
          <ChoiceCard onClick={() => { setLookupPhone(''); setLookupErr(''); setScreen('lookup'); }} title={D.choose_update} sub={D.choose_update_sub}
            icon={<svg width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M4 20h4l10-10-4-4L4 16v4Z" stroke={T.accent} strokeWidth="1.9" strokeLinejoin="round"/></svg>} />
        </div>
      </div>
    );
  }

  // LOOKUP — phone verification → load the patient's submission for editing
  if (screen === 'lookup') {
    const lp = normPhone(lookupPhone);
    const ready = /^01\d{9}$/.test(lp);
    body = (
      <div style={{ height: '100%', display: 'flex', flexDirection: 'column', background: T.bone, fontFamily: "'Hanken Grotesk', sans-serif" }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '56px 22px 10px', flexShrink: 0 }}>
          <button onClick={() => setScreen('choose')} style={{ width: 40, height: 40, borderRadius: 11, border: `1px solid ${T.line}`, background: T.surface, cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none"><path d="M15 6l-6 6 6 6" stroke={T.inkSoft} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/></svg>
          </button>
          <div style={{ fontFamily: "'Anek Bangla', 'Hind Siliguri', sans-serif", fontWeight: 600, fontSize: 23, color: T.ink, lineHeight: 1.15 }}>{D.lookup_title}</div>
          <window.LangToggle lang={lang} onLang={onLang} style={{ marginLeft: 'auto' }} />
        </div>
        <div style={{ fontSize: 14, color: T.muted, padding: '0 22px 22px', flexShrink: 0 }}>{D.lookup_sub}</div>
        <div style={{ flex: 1, minHeight: 0, padding: '4px 22px 0' }}>
          <Field label={D.phone}>
            <input value={lookupPhone} onChange={e => { setLookupPhone(e.target.value); if (lookupErr) setLookupErr(''); }} placeholder={D.phone_ph} inputMode="tel" autoFocus
              onKeyDown={e => { if (e.key === 'Enter' && ready) findSubmission(lookupPhone); }}
              style={{ ...inputStyle, borderColor: lookupErr ? '#E0A094' : T.line }} onFocus={onFocus} onBlur={onBlur} />
          </Field>
          {lookupErr && <div style={{ fontSize: 13, color: '#C0492B', background: '#FBEAE5', padding: '9px 12px', borderRadius: 10, marginTop: 12 }}>{lookupErr}</div>}
        </div>
        <div style={{ flexShrink: 0, padding: '12px 22px calc(env(safe-area-inset-bottom) + 24px)' }}>
          <button onClick={() => findSubmission(lookupPhone)} disabled={!ready || busy} style={{
            width: '100%', height: 56, cursor: ready && !busy ? 'pointer' : 'not-allowed', fontFamily: 'inherit',
            fontSize: 18, fontWeight: 600, color: ready ? '#fff' : T.faint,
            background: ready ? T.accent : T.boneDeep, border: 'none', borderRadius: 16,
            boxShadow: ready ? '0 12px 28px rgba(28,119,206,0.3)' : 'none',
            display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 9,
          }}>{busy ? '…' : D.lookup_btn}</button>
        </div>
      </div>
    );
  }

  // REGISTER — name, age, serial, address
  if (screen === 'register') {
    body = (
      <div style={{ height: '100%', display: 'flex', flexDirection: 'column', background: T.bone, fontFamily: "'Hanken Grotesk', sans-serif" }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '56px 22px 10px', flexShrink: 0 }}>
          <button onClick={() => setScreen(editing ? 'preview' : 'choose')} style={{ width: 40, height: 40, borderRadius: 11, border: `1px solid ${T.line}`, background: T.surface, cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none"><path d="M15 6l-6 6 6 6" stroke={T.inkSoft} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/></svg>
          </button>
          <div style={{ fontFamily: "'Anek Bangla', 'Hind Siliguri', sans-serif", fontWeight: 600, fontSize: 23, color: T.ink, lineHeight: 1.15, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', minWidth: 0 }}>{D.reg_title}</div>
          <window.LangToggle lang={lang} onLang={onLang} style={{ marginLeft: 'auto' }} />
        </div>
        {editing
          ? <div style={{ margin: '0 22px 14px', padding: '8px 12px', background: T.accentSoft, borderRadius: 10, fontSize: 13, fontWeight: 600, color: T.accentDeep, flexShrink: 0, display: 'flex', alignItems: 'center', gap: 7 }}>
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none"><path d="M4 20h4l10-10-4-4L4 16v4Z" stroke={T.accentDeep} strokeWidth="2" strokeLinejoin="round"/></svg>{D.editing_banner} · {D.serial} {editing.serial}
            </div>
          : <div style={{ fontSize: 14, color: T.muted, padding: '0 22px 18px', flexShrink: 0 }}>{D.reg_sub}</div>}

        <div style={{ flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column', gap: 16, padding: '4px 22px 0' }}>
          <Field label={D.f_name}>
            <input value={d.name} onChange={e => set('name', e.target.value)} placeholder={D.name_ph} style={inputStyle} onFocus={onFocus} onBlur={onBlur} />
          </Field>
          <div style={{ display: 'flex', gap: 12 }}>
            <Field label={D.phone} grow>
              <input value={d.phone} onChange={e => { set('phone', e.target.value); if (phoneErr) setPhoneErr(''); }} placeholder={D.phone_ph} inputMode="tel" disabled={!!editing} style={{ ...inputStyle, borderColor: phoneErr ? '#E0A094' : T.line, background: editing ? T.lineSoft : T.surface, color: editing ? T.muted : T.ink }} onFocus={onFocus} onBlur={onBlur} />
            </Field>
            <Field label={D.age} width={96}>
              <input value={d.age} onChange={e => set('age', e.target.value)} placeholder="—" inputMode="numeric" style={inputStyle} onFocus={onFocus} onBlur={onBlur} />
            </Field>
          </div>
          {phoneErr && <div style={{ fontSize: 13, color: '#C0492B', background: '#FBEAE5', padding: '9px 12px', borderRadius: 10, marginTop: -6 }}>{phoneErr}</div>}
          <Field label={D.address}>
            <textarea value={d.address} onChange={e => set('address', e.target.value)} placeholder={D.address_ph} rows={3}
              style={{ width: '100%', boxSizing: 'border-box', padding: '13px 15px', resize: 'none', fontFamily: 'inherit', fontSize: 16.5, lineHeight: 1.5, color: T.ink, background: T.surface, border: `1px solid ${T.line}`, borderRadius: 13, outline: 'none' }}
              onFocus={e => e.target.style.borderColor = T.accent} onBlur={e => e.target.style.borderColor = T.line} />
          </Field>
        </div>

        <div style={{ flexShrink: 0, padding: '12px 22px calc(env(safe-area-inset-bottom) + 24px)' }}>
          <button onClick={goFromRegister} disabled={!d.name.trim()} style={{
            width: '100%', height: 56, cursor: d.name.trim() ? 'pointer' : 'not-allowed', fontFamily: 'inherit',
            fontSize: 18, fontWeight: 600, color: d.name.trim() ? '#fff' : T.faint,
            background: d.name.trim() ? T.accent : T.boneDeep, border: 'none', borderRadius: 16,
            boxShadow: d.name.trim() ? '0 12px 28px rgba(28,119,206,0.3)' : 'none', transition: 'all .15s',
            display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 9,
          }}>
            {D.continue}
            {regReady && <svg width="18" height="18" viewBox="0 0 24 24" fill="none"><path d="M5 12h14M13 6l6 6-6 6" stroke="#fff" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"/></svg>}
          </button>
        </div>
      </div>
    );
  }

  // WRITE — fixed height, no scroll
  if (screen === 'write') {
    body = (
      <div style={{ height: '100%', display: 'flex', flexDirection: 'column', background: T.bone, fontFamily: "'Hanken Grotesk', sans-serif" }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '56px 22px 12px', flexShrink: 0 }}>
          <button onClick={() => setScreen('register')} style={{ width: 40, height: 40, borderRadius: 11, border: `1px solid ${T.line}`, background: T.surface, cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none"><path d="M15 6l-6 6 6 6" stroke={T.inkSoft} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/></svg>
          </button>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontFamily: "'Anek Bangla', 'Hind Siliguri', sans-serif", fontWeight: 600, fontSize: 20, color: T.ink, lineHeight: 1.1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{d.name || B.name}</div>
            <div style={{ fontSize: 12.5, color: T.muted, marginTop: 2 }}>{d.phone || ''}</div>
          </div>
          <window.LangToggle lang={lang} onLang={onLang} />
        </div>

        <div style={{ flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column', gap: 14, padding: '8px 22px 0' }}>
          {/* the one big field — grows to fill */}
          <div style={{ flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column' }}>
            <div style={{ fontFamily: "'Anek Bangla', 'Hind Siliguri', sans-serif", fontWeight: 600, fontSize: 25, color: T.ink, lineHeight: 1.2, marginBottom: 4, flexShrink: 0 }}>{D.complaint_label}</div>
            <div style={{ fontSize: 13.5, color: T.muted, marginBottom: 10, flexShrink: 0 }}>{D.complaint_hint}</div>
            <textarea value={d.complaint} onChange={e => set('complaint', e.target.value)} placeholder={D.complaint_ph}
              style={{ flex: 1, minHeight: 96, width: '100%', boxSizing: 'border-box', padding: 16, resize: 'none', fontFamily: 'inherit', fontSize: 16.5, lineHeight: 1.6, color: T.ink, background: T.surface, border: `1px solid ${T.line}`, borderRadius: 16, outline: 'none' }}
              onFocus={e => e.target.style.borderColor = T.accent} onBlur={e => e.target.style.borderColor = T.line} />
          </div>

          {/* attachments — inline, side by side */}
          <div style={{ display: 'flex', gap: 10, flexShrink: 0 }}>
            <FileTile label={D.reports} hint={D.reports_sub} addedLabel={D.added} accept="application/pdf,.pdf" icon={icoReport} items={d.reports} onAdd={f => addFiles('reports', f)} onRemove={i => removeFile('reports', i)} />
            <FileTile label={D.media} hint={D.media_sub} addedLabel={D.added} accept="image/*,video/*" icon={icoMedia} items={d.media} onAdd={f => addFiles('media', f)} onRemove={i => removeFile('media', i)} />
          </div>
        </div>

        {/* footer → Preview */}
        <div style={{ flexShrink: 0, padding: '12px 22px calc(env(safe-area-inset-bottom) + 24px)' }}>
          <button onClick={() => d.complaint.trim() && setScreen('preview')} disabled={!d.complaint.trim()} style={{
            width: '100%', height: 56, cursor: d.complaint.trim() ? 'pointer' : 'not-allowed', fontFamily: 'inherit',
            fontSize: 18, fontWeight: 600, color: d.complaint.trim() ? '#fff' : T.faint,
            background: d.complaint.trim() ? T.accent : T.boneDeep, border: 'none', borderRadius: 16,
            boxShadow: d.complaint.trim() ? '0 12px 28px rgba(28,119,206,0.3)' : 'none', transition: 'all .15s',
            display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 9,
          }}>
            {D.preview}
            {d.complaint.trim() && <svg width="18" height="18" viewBox="0 0 24 24" fill="none"><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z" stroke="#fff" strokeWidth="2"/><circle cx="12" cy="12" r="2.6" stroke="#fff" strokeWidth="2"/></svg>}
          </button>
        </div>
      </div>
    );
  }

  // PREVIEW — review, edit, delete, submit
  if (screen === 'preview') {
    const Section = ({ label, onEdit, children }) => (
      <div style={{ background: T.surface, border: `1px solid ${T.line}`, borderRadius: 16, padding: '14px 16px', marginBottom: 12 }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
          <span style={{ fontSize: 13, fontWeight: 700, color: T.faint }}>{label}</span>
          <button onClick={onEdit} style={{ border: 'none', background: 'none', cursor: 'pointer', color: T.accent, fontFamily: 'inherit', fontSize: 14, fontWeight: 600, display: 'flex', alignItems: 'center', gap: 4 }}>
            <svg width="13" height="13" viewBox="0 0 24 24" fill="none"><path d="M4 20h4l10-10-4-4L4 16v4Z" stroke={T.accent} strokeWidth="2" strokeLinejoin="round"/></svg>{D.edit}
          </button>
        </div>
        {children}
      </div>
    );
    const allFiles = [...d.reports.map((f, i) => ({ f, key: 'reports', i })), ...d.media.map((f, i) => ({ f, key: 'media', i }))];
    body = (
      <div style={{ height: '100%', display: 'flex', flexDirection: 'column', background: T.bone, fontFamily: "'Hanken Grotesk', sans-serif" }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '56px 22px 10px', flexShrink: 0 }}>
          <button onClick={() => setScreen('write')} style={{ width: 40, height: 40, borderRadius: 11, border: `1px solid ${T.line}`, background: T.surface, cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none"><path d="M15 6l-6 6 6 6" stroke={T.inkSoft} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/></svg>
          </button>
          <div>
            <div style={{ fontFamily: "'Anek Bangla', 'Hind Siliguri', sans-serif", fontWeight: 600, fontSize: 24, color: T.ink, lineHeight: 1.15 }}>{D.preview_title}</div>
          </div>
          <window.LangToggle lang={lang} onLang={onLang} style={{ marginLeft: 'auto' }} />
        </div>
        <div style={{ fontSize: 13.5, color: T.muted, padding: '0 22px 12px', flexShrink: 0 }}>{D.preview_sub}</div>

        <div style={{ flex: 1, overflowY: 'auto', padding: '0 22px 6px' }}>
          <Section label={D.sec_patient} onEdit={() => setScreen('register')}>
            <div style={{ fontSize: 17, color: T.ink, fontWeight: 600 }}>{d.name || <em style={{ color: T.faint, fontWeight: 400 }}>{D.empty}</em>}</div>
            <div style={{ fontSize: 14.5, color: T.muted, marginTop: 2 }}>{d.phone || '—'}{d.age ? ' · ' + d.age + ' ' + D.years : ''}</div>
            {d.address && <div style={{ fontSize: 14.5, color: T.muted, marginTop: 4, lineHeight: 1.45 }}>{d.address}</div>}
          </Section>
          <Section label={D.sec_problem} onEdit={() => setScreen('write')}>
            <div style={{ fontSize: 16, color: T.ink, lineHeight: 1.6, whiteSpace: 'pre-wrap' }}>{d.complaint || <em style={{ color: T.faint }}>{D.empty}</em>}</div>
          </Section>
          <Section label={D.sec_files} onEdit={() => setScreen('write')}>
            {allFiles.length === 0
              ? <div style={{ fontSize: 14.5, color: T.faint }}>{D.no_files}</div>
              : <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
                  {allFiles.map(({ f, key, i }) => <FileChip key={key + i} f={f} onRemove={() => removeFile(key, i)} />)}
                </div>}
          </Section>
        </div>

        <div style={{ flexShrink: 0, padding: '12px 22px calc(env(safe-area-inset-bottom) + 24px)', display: 'flex', gap: 12 }}>
          <button onClick={() => setScreen('write')} style={{ height: 56, padding: '0 22px', cursor: 'pointer', fontFamily: 'inherit', fontSize: 17, fontWeight: 600, color: T.inkSoft, background: T.surface, border: `1px solid ${T.line}`, borderRadius: 16 }}>{D.edit}</button>
          <button onClick={() => setModal('confirm')} style={{
            flex: 1, height: 56, cursor: 'pointer', fontFamily: 'inherit', fontSize: 18, fontWeight: 600, color: '#fff',
            background: T.accent, border: 'none', borderRadius: 16, boxShadow: '0 12px 28px rgba(28,119,206,0.3)',
            display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 9,
          }}>
            {editing ? D.update : D.submit}
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none"><path d="M5 12h14M13 6l6 6-6 6" stroke="#fff" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"/></svg>
          </button>
        </div>
      </div>
    );
  }

  // DONE — graceful thank you
  if (screen === 'done') {
    body = (
      <div style={{ height: '100%', display: 'flex', flexDirection: 'column', fontFamily: "'Hanken Grotesk', sans-serif",
        background: 'linear-gradient(172deg, #1C77CE 0%, #155EA8 100%)' }}>
        <div style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', textAlign: 'center', padding: '54px 34px 20px' }}>
          <div style={{ width: 86, height: 86, borderRadius: '50%', background: 'rgba(255,255,255,0.16)', display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: 26 }}>
            <div style={{ width: 58, height: 58, borderRadius: '50%', background: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
              <svg width="30" height="30" viewBox="0 0 24 24" fill="none"><path d="M5 13l4 4L19 7" stroke={T.accent} strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"/></svg>
            </div>
          </div>
          <div style={{ fontFamily: "'Instrument Serif', serif", fontSize: 46, color: '#fff', lineHeight: 1, marginBottom: 16 }}>{editing ? D.done_updated_title : UI.done_title}</div>
          {doneSerial != null && (
            <div style={{ display: 'inline-flex', flexDirection: 'column', alignItems: 'center', background: 'rgba(255,255,255,0.16)', border: '1px solid rgba(255,255,255,0.3)', borderRadius: 16, padding: '12px 26px', marginBottom: 18 }}>
              <span style={{ fontSize: 12.5, letterSpacing: 1.4, textTransform: 'uppercase', color: 'rgba(255,255,255,0.8)' }}>{UI.done_serial}</span>
              <span style={{ fontFamily: "'Instrument Serif', serif", fontSize: 44, color: '#fff', lineHeight: 1.05 }}>{doneSerial}</span>
            </div>
          )}
          <div style={{ fontSize: 16.5, color: 'rgba(255,255,255,0.92)', lineHeight: 1.7, maxWidth: 300 }}>{editing ? D.done_updated_body : UI.done_body}</div>
          <div style={{ fontFamily: "'Instrument Serif', serif", fontStyle: 'italic', fontSize: 19, color: 'rgba(255,255,255,0.9)', marginTop: 30 }}>{UI.done_note}</div>
        </div>
        <div style={{ padding: '0 26px calc(env(safe-area-inset-bottom) + 26px)' }}>
          <button onClick={reset} style={{ width: '100%', height: 52, cursor: 'pointer', fontFamily: 'inherit', fontSize: 16, fontWeight: 600, color: '#fff', background: 'rgba(255,255,255,0.16)', border: '1.5px solid rgba(255,255,255,0.34)', borderRadius: 14 }}>{UI.done_again}</button>
        </div>
      </div>
    );
  }

  // ─────────────────────────── modals ───────────────────────────
  let overlay = null;
  if (modal === 'confirm') {
    overlay = (
      <ModalShell onClose={() => setModal(null)}>
        <div style={{ width: 56, height: 56, borderRadius: 16, background: T.accentSoft, display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '0 auto 16px' }}>
          <svg width="28" height="28" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="9" stroke={T.accent} strokeWidth="2"/><path d="M12 8v5" stroke={T.accent} strokeWidth="2.2" strokeLinecap="round"/><circle cx="12" cy="16.5" r="1.2" fill={T.accent}/></svg>
        </div>
        <div style={{ fontFamily: "'Anek Bangla', 'Hind Siliguri', sans-serif", fontWeight: 600, fontSize: 24, color: T.ink, textAlign: 'center', marginBottom: 10 }}>{editing ? D.confirm_update_title : D.confirm_title}</div>
        <div style={{ fontSize: 15.5, color: T.muted, textAlign: 'center', lineHeight: 1.6, marginBottom: 22 }}>{editing ? D.confirm_update_body : D.confirm_body}</div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
          <button onClick={confirmSubmit} disabled={busy} style={{ height: 54, cursor: busy ? 'wait' : 'pointer', opacity: busy ? 0.75 : 1, fontFamily: 'inherit', fontSize: 17, fontWeight: 600, color: '#fff', background: T.accent, border: 'none', borderRadius: 14, boxShadow: '0 10px 24px rgba(28,119,206,0.3)' }}>{busy ? '…' : (editing ? D.confirm_update_yes : D.confirm_yes)}</button>
          <button onClick={() => setModal(null)} style={{ height: 52, cursor: 'pointer', fontFamily: 'inherit', fontSize: 16, fontWeight: 600, color: T.inkSoft, background: T.bone, border: 'none', borderRadius: 14 }}>{D.confirm_cancel}</button>
        </div>
      </ModalShell>
    );
  }
  if (modal === 'dup') {
    overlay = (
      <ModalShell onClose={() => setModal(null)}>
        <div style={{ width: 56, height: 56, borderRadius: 16, background: '#FDEBE7', display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '0 auto 16px' }}>
          <svg width="28" height="28" viewBox="0 0 24 24" fill="none"><path d="M12 3l9 16H3l9-16Z" stroke="#D9512C" strokeWidth="2" strokeLinejoin="round"/><path d="M12 10v4" stroke="#D9512C" strokeWidth="2.2" strokeLinecap="round"/><circle cx="12" cy="16.8" r="1.1" fill="#D9512C"/></svg>
        </div>
        <div style={{ fontFamily: "'Anek Bangla', 'Hind Siliguri', sans-serif", fontWeight: 600, fontSize: 24, color: T.ink, textAlign: 'center', marginBottom: 10 }}>{D.dup_title}</div>
        <div style={{ fontSize: 15.5, color: T.muted, textAlign: 'center', lineHeight: 1.6, marginBottom: 22 }}>{D.dup_body}</div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
          {online && (
            <button onClick={() => findSubmission(phoneDigits)} disabled={busy} style={{ width: '100%', height: 54, cursor: busy ? 'wait' : 'pointer', opacity: busy ? 0.75 : 1, fontFamily: 'inherit', fontSize: 17, fontWeight: 600, color: '#fff', background: T.accent, border: 'none', borderRadius: 14, boxShadow: '0 10px 24px rgba(28,119,206,0.3)', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8 }}>
              <svg width="17" height="17" viewBox="0 0 24 24" fill="none"><path d="M4 20h4l10-10-4-4L4 16v4Z" stroke="#fff" strokeWidth="2" strokeLinejoin="round"/></svg>{busy ? '…' : D.dup_edit}
            </button>
          )}
          <button onClick={() => { setModal(null); setScreen('register'); }} style={{ width: '100%', height: 52, cursor: 'pointer', fontFamily: 'inherit', fontSize: 16, fontWeight: 600, color: online ? T.inkSoft : '#fff', background: online ? T.bone : T.accent, border: 'none', borderRadius: 14 }}>{D.dup_ok}</button>
        </div>
      </ModalShell>
    );
  }

  return (
    <div style={{ position: 'relative', height: '100%', overflow: 'hidden' }}>
      <ScreenFade key={screen}>{body}</ScreenFade>
      {overlay}
    </div>
  );
}

// bottom-sheet modal shell with backdrop (transition-based)
function ModalShell({ children, onClose }) {
  const T = window.TOK;
  const [on, setOn] = useStatePF(false);
  React.useEffect(() => { const r = requestAnimationFrame(() => setOn(true)); return () => cancelAnimationFrame(r); }, []);
  return (
    <div onClick={onClose} style={{ position: 'absolute', inset: 0, zIndex: 80, background: on ? 'rgba(17,42,65,0.42)' : 'rgba(17,42,65,0)', transition: 'background .26s ease', display: 'flex', alignItems: 'flex-end' }}>
      <div onClick={e => e.stopPropagation()} style={{ width: '100%', background: T.surface, borderRadius: '26px 26px 0 0', padding: '26px 24px calc(env(safe-area-inset-bottom) + 30px)', boxShadow: '0 -16px 50px rgba(17,42,65,0.22)', transform: on ? 'translateY(0)' : 'translateY(100%)', transition: 'transform .4s cubic-bezier(.3,.72,.18,1)' }}>
        <div style={{ width: 40, height: 5, borderRadius: 100, background: T.line, margin: '0 auto 20px' }} />
        {children}
      </div>
    </div>
  );
}

window.PatientForm = PatientForm;
