// Grants & Scholarships Database — app.jsx
// React app using Supabase for auth + data. No build step needed.

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

// ── Supabase client ──────────────────────────────────────────
const SUPABASE_URL = 'https://hiqjquqdntoxacxvltfr.supabase.co';
const SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImhpcWpxdXFkbnRveGFjeHZsdGZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3ODA3MTIwMzAsImV4cCI6MjA5NjI4ODAzMH0.YPxeqE-afpIlKIoAMGhq0pUu1_sX04fzJhhpvanAptg';
const db = supabase.createClient(SUPABASE_URL, SUPABASE_ANON_KEY);

// ── Constants ────────────────────────────────────────────────
const STATUSES = ['Not started','In progress','Submitted','Awarded','Rejected','Not applicable'];
const STATUS_COLORS = {
  'Not started':    'status-not-started',
  'In progress':    'status-in-progress',
  'Submitted':      'status-submitted',
  'Awarded':        'status-awarded',
  'Rejected':       'status-rejected',
  'Not applicable': 'status-not-applicable',
};
const KANBAN_COLS = ['Not started','In progress','Submitted','Awarded','Rejected'];
const DOW = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
const MONTHS = ['January','February','March','April','May','June',
                'July','August','September','October','November','December'];

// ── Helpers ──────────────────────────────────────────────────
function daysUntil(dateStr) {
  if (!dateStr) return null;
  const diff = Math.ceil((new Date(dateStr) - new Date()) / 86400000);
  return diff;
}

function urgencyClass(days) {
  if (days === null) return 'urgency-future';
  if (days < 0)  return 'urgency-future';
  if (days <= 1) return 'urgency-critical';
  if (days <= 7) return 'urgency-high';
  if (days <= 14) return 'urgency-medium';
  if (days <= 30) return 'urgency-low';
  return 'urgency-future';
}

function urgencyLabel(days) {
  if (days === null || days < 0) return 'No deadline';
  if (days === 0) return 'Due today!';
  if (days === 1) return 'Due tomorrow!';
  if (days <= 7) return `${days}d left`;
  if (days <= 30) return `${days}d left`;
  return `${days}d`;
}

function calDeadlineClass(days) {
  if (days <= 1) return 'deadline-critical';
  if (days <= 7) return 'deadline-high';
  if (days <= 14) return 'deadline-medium';
  return 'deadline-low';
}

function fmtDate(dateStr) {
  if (!dateStr) return '—';
  return new Date(dateStr + 'T00:00:00').toLocaleDateString('en-GB', {
    day:'numeric', month:'short', year:'numeric'
  });
}

function fmtAmount(grant) {
  if (!grant.amount_min && !grant.amount_max) return '—';
  const cur = grant.amount_currency || 'GBP';
  const sym = cur === 'GBP' ? '£' : cur === 'EUR' ? '€' : cur === 'USD' ? '$' : cur + ' ';
  if (grant.amount_min && grant.amount_max && grant.amount_min !== grant.amount_max)
    return `${sym}${grant.amount_min.toLocaleString()}–${sym}${grant.amount_max.toLocaleString()}`;
  const amt = grant.amount_max || grant.amount_min;
  return `${sym}${amt.toLocaleString()}`;
}

function estimateNextDeadline(deadlineStr) {
  if (!deadlineStr) return null;
  const d = new Date(deadlineStr + 'T00:00:00');
  d.setFullYear(d.getFullYear() + 1);
  return d.toISOString().split('T')[0];
}

// ── Auth Screen ──────────────────────────────────────────────
function AuthScreen({ onLogin }) {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState('');
  const [mode, setMode] = useState('login'); // login | signup

  const handleSubmit = async (e) => {
    e.preventDefault();
    setLoading(true);
    setError('');
    try {
      let result;
      if (mode === 'login') {
        result = await db.auth.signInWithPassword({ email, password });
      } else {
        result = await db.auth.signUp({ email, password });
        if (!result.error) {
          setError('');
          alert('Check your email to confirm your account, then sign in.');
          setMode('login');
          setLoading(false);
          return;
        }
      }
      if (result.error) throw result.error;
      onLogin(result.data.session);
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="auth-wrap">
      <div className="auth-card">
        <div className="auth-logo">
          <h1>Grants &amp; Scholarships<br/>Database</h1>
          <div className="auth-divider" />
          <p>Archaeological Research Funding</p>
        </div>

        {error && <div className="alert alert-error">{error}</div>}

        <form onSubmit={handleSubmit}>
          <div className="field">
            <label>Email</label>
            <input type="email" value={email}
              onChange={e => setEmail(e.target.value)} required
              placeholder="you@example.com" />
          </div>
          <div className="field">
            <label>Password</label>
            <input type="password" value={password}
              onChange={e => setPassword(e.target.value)} required
              placeholder="••••••••" />
          </div>
          <button className="btn btn-primary btn-full" type="submit" disabled={loading}>
            {loading ? 'Please wait…' : mode === 'login' ? 'Sign In' : 'Create Account'}
          </button>
        </form>

        <p style={{textAlign:'center', marginTop:'20px', fontSize:'13px', color:'var(--text-mid)'}}>
          {mode === 'login' ? (
            <>New user? <button className="btn btn-ghost btn-sm" onClick={() => setMode('signup')}>Sign up</button></>
          ) : (
            <>Have an account? <button className="btn btn-ghost btn-sm" onClick={() => setMode('login')}>Sign in</button></>
          )}
        </p>
      </div>
    </div>
  );
}

// ── Status Badge ─────────────────────────────────────────────
function StatusBadge({ status }) {
  return (
    <span className={`status-badge ${STATUS_COLORS[status] || 'status-not-started'}`}>
      {status}
    </span>
  );
}

// ── Urgency Badge ────────────────────────────────────────────
function UrgencyBadge({ deadline }) {
  const days = daysUntil(deadline);
  const cls  = urgencyClass(days);
  const lbl  = urgencyLabel(days);
  return (
    <span className={`urgency ${cls}`}>
      <span className="urgency-dot" />
      {lbl}
    </span>
  );
}

// ── Grant Form ───────────────────────────────────────────────
function GrantForm({ grant, onSave, onClose, userRole }) {
  const blank = {
    name:'', funding_body:'', amount_min:'', amount_max:'',
    amount_currency:'GBP', amount_notes:'', open_date:'', deadline:'',
    is_recurring: false, recurrence_interval:'annual', next_estimated_deadline:'',
    eligibility:'', research_areas:'', career_stage:'', status:'Not started',
    notes:'', requirements:'', website_url:'', contacts:''
  };

  const toForm = (g) => ({
    ...blank,
    ...g,
    research_areas: g?.research_areas ? g.research_areas.join(', ') : '',
    amount_min: g?.amount_min ?? '',
    amount_max: g?.amount_max ?? '',
    next_estimated_deadline: g?.next_estimated_deadline || '',
  });

  const [form, setForm] = useState(toForm(grant));
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState('');

  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));

  // Auto-estimate next deadline when deadline + recurring changes
  useEffect(() => {
    if (form.is_recurring && form.deadline && !form.next_estimated_deadline) {
      set('next_estimated_deadline', estimateNextDeadline(form.deadline));
    }
  }, [form.is_recurring, form.deadline]);

  const handleSave = async () => {
    if (!form.name.trim()) { setError('Grant name is required.'); return; }
    if (!form.funding_body.trim()) { setError('Funding body is required.'); return; }
    setSaving(true); setError('');
    try {
      const payload = {
        ...form,
        research_areas: form.research_areas
          ? form.research_areas.split(',').map(s => s.trim()).filter(Boolean)
          : [],
        amount_min: form.amount_min !== '' ? parseFloat(form.amount_min) : null,
        amount_max: form.amount_max !== '' ? parseFloat(form.amount_max) : null,
        open_date: form.open_date || null,
        deadline: form.deadline || null,
        next_estimated_deadline: form.next_estimated_deadline || null,
      };
      delete payload.id;
      delete payload.created_at;
      delete payload.updated_at;
      delete payload.created_by;

      let result;
      if (grant?.id) {
        result = await db.from('grants').update(payload).eq('id', grant.id).select().single();
      } else {
        const { data: { user } } = await db.auth.getUser();
        result = await db.from('grants').insert({ ...payload, created_by: user?.id }).select().single();
      }
      if (result.error) throw result.error;
      onSave(result.data);
    } catch (err) {
      setError(err.message);
    } finally {
      setSaving(false);
    }
  };

  const canEdit = userRole === 'admin' || userRole === 'editor';

  return (
    <div className="modal-backdrop" onClick={e => e.target === e.currentTarget && onClose()}>
      <div className="modal">
        <div className="modal-header">
          <h2>{grant?.id ? 'Edit Grant' : 'Add Grant'}</h2>
          <button className="btn btn-ghost" style={{color:'var(--mist)'}} onClick={onClose}>✕</button>
        </div>
        <div className="modal-body">
          {error && <div className="alert alert-error">{error}</div>}

          <div className="form-grid">
            <div className="field form-row">
              <label>Grant / Scholarship Name *</label>
              <input value={form.name} onChange={e => set('name', e.target.value)}
                placeholder="e.g. Society of Antiquaries Fellowship" disabled={!canEdit} />
            </div>
            <div className="field">
              <label>Funding Body / Organisation *</label>
              <input value={form.funding_body} onChange={e => set('funding_body', e.target.value)}
                placeholder="e.g. British Academy" disabled={!canEdit} />
            </div>
            <div className="field">
              <label>Career Stage</label>
              <input value={form.career_stage} onChange={e => set('career_stage', e.target.value)}
                placeholder="e.g. Early career, PhD, Any" disabled={!canEdit} />
            </div>

            <div className="field">
              <label>Amount Min (number only)</label>
              <input type="number" value={form.amount_min}
                onChange={e => set('amount_min', e.target.value)}
                placeholder="e.g. 1000" disabled={!canEdit} />
            </div>
            <div className="field">
              <label>Amount Max (number only)</label>
              <input type="number" value={form.amount_max}
                onChange={e => set('amount_max', e.target.value)}
                placeholder="e.g. 10000" disabled={!canEdit} />
            </div>
            <div className="field">
              <label>Currency</label>
              <select value={form.amount_currency}
                onChange={e => set('amount_currency', e.target.value)} disabled={!canEdit}>
                <option>GBP</option><option>EUR</option><option>USD</option><option>Other</option>
              </select>
            </div>
            <div className="field">
              <label>Amount Notes</label>
              <input value={form.amount_notes} onChange={e => set('amount_notes', e.target.value)}
                placeholder="e.g. up to £5k travel only" disabled={!canEdit} />
            </div>

            <div className="field">
              <label>Open Date</label>
              <input type="date" value={form.open_date} onChange={e => set('open_date', e.target.value)}
                disabled={!canEdit} />
            </div>
            <div className="field">
              <label>Deadline</label>
              <input type="date" value={form.deadline} onChange={e => set('deadline', e.target.value)}
                disabled={!canEdit} />
            </div>

            <div className="field">
              <label>Status</label>
              <select value={form.status} onChange={e => set('status', e.target.value)}
                disabled={!canEdit}>
                {STATUSES.map(s => <option key={s}>{s}</option>)}
              </select>
            </div>
            <div className="field" style={{display:'flex', flexDirection:'column', justifyContent:'flex-end'}}>
              <label style={{display:'flex', alignItems:'center', gap:'10px', cursor: canEdit ? 'pointer' : 'default'}}>
                <input type="checkbox" checked={form.is_recurring}
                  onChange={e => set('is_recurring', e.target.checked)} disabled={!canEdit} />
                Recurring grant
              </label>
              {form.is_recurring && (
                <div style={{marginTop:'8px'}}>
                  <select value={form.recurrence_interval}
                    onChange={e => set('recurrence_interval', e.target.value)} disabled={!canEdit}>
                    <option value="annual">Annual</option>
                    <option value="biennial">Biennial</option>
                    <option value="other">Other</option>
                  </select>
                </div>
              )}
            </div>

            {form.is_recurring && (
              <div className="field form-row">
                <label>Next Estimated Deadline (recurring)</label>
                <input type="date" value={form.next_estimated_deadline}
                  onChange={e => set('next_estimated_deadline', e.target.value)}
                  disabled={!canEdit} />
              </div>
            )}

            <div className="field form-row">
              <label>Research Areas (comma-separated)</label>
              <input value={form.research_areas} onChange={e => set('research_areas', e.target.value)}
                placeholder="e.g. Bronze Age, Zooarchaeology, Post-medieval" disabled={!canEdit} />
            </div>

            <div className="field form-row">
              <label>Eligibility</label>
              <textarea value={form.eligibility} onChange={e => set('eligibility', e.target.value)}
                placeholder="Who can apply? Nationality, career stage, institutional affiliation…"
                disabled={!canEdit} />
            </div>

            <div className="field form-row">
              <label>Requirements / Notes</label>
              <textarea value={form.requirements} onChange={e => set('requirements', e.target.value)}
                placeholder="Documents needed, word limits, referee requirements…"
                disabled={!canEdit} />
            </div>

            <div className="field form-row">
              <label>Notes</label>
              <textarea value={form.notes} onChange={e => set('notes', e.target.value)}
                placeholder="Any other notes about this grant…" disabled={!canEdit} />
            </div>

            <div className="field">
              <label>Website / Application URL</label>
              <input type="url" value={form.website_url} onChange={e => set('website_url', e.target.value)}
                placeholder="https://…" disabled={!canEdit} />
            </div>
            <div className="field">
              <label>Contacts / References</label>
              <input value={form.contacts} onChange={e => set('contacts', e.target.value)}
                placeholder="Names, emails, notes" disabled={!canEdit} />
            </div>
          </div>
        </div>
        <div className="modal-footer">
          <button className="btn btn-secondary" onClick={onClose}>Cancel</button>
          {canEdit && (
            <button className="btn btn-primary" onClick={handleSave} disabled={saving}>
              {saving ? 'Saving…' : 'Save Grant'}
            </button>
          )}
        </div>
      </div>
    </div>
  );
}

// ── Grant Detail Modal ───────────────────────────────────────
function GrantDetail({ grant, onEdit, onDelete, onClose, userRole }) {
  const days = daysUntil(grant.deadline);
  const canDelete = userRole === 'admin';
  const canEdit   = userRole === 'admin' || userRole === 'editor';

  return (
    <div className="modal-backdrop" onClick={e => e.target === e.currentTarget && onClose()}>
      <div className="modal">
        <div className="modal-header">
          <div>
            <h2 style={{marginBottom:'4px'}}>{grant.name}</h2>
            <span style={{fontSize:'13px', color:'var(--mist)', opacity:0.7}}>{grant.funding_body}</span>
          </div>
          <button className="btn btn-ghost" style={{color:'var(--mist)'}} onClick={onClose}>✕</button>
        </div>
        <div className="modal-body">
          {/* Status + urgency row */}
          <div style={{display:'flex', gap:'10px', flexWrap:'wrap', marginBottom:'20px'}}>
            <StatusBadge status={grant.status} />
            {grant.deadline && <UrgencyBadge deadline={grant.deadline} />}
            {grant.is_recurring && <span className="recurring-tag">↻ {grant.recurrence_interval}</span>}
          </div>

          <div className="detail-section">
            <h4>Funding</h4>
            <div className="detail-grid">
              <div className="detail-item">
                <label>Amount</label>
                <p>{fmtAmount(grant)}</p>
              </div>
              {grant.amount_notes && (
                <div className="detail-item">
                  <label>Amount notes</label>
                  <p>{grant.amount_notes}</p>
                </div>
              )}
              <div className="detail-item">
                <label>Career stage</label>
                <p>{grant.career_stage || '—'}</p>
              </div>
            </div>
          </div>

          <div className="detail-section">
            <h4>Dates</h4>
            <div className="detail-grid">
              <div className="detail-item">
                <label>Opens</label>
                <p>{fmtDate(grant.open_date)}</p>
              </div>
              <div className="detail-item">
                <label>Deadline</label>
                <p style={{color: days !== null && days <= 7 ? 'var(--ember)' : undefined}}>
                  {fmtDate(grant.deadline)}
                  {days !== null && days >= 0 && (
                    <span style={{marginLeft:'8px', fontSize:'12px', color:'var(--text-light)'}}>
                      ({days === 0 ? 'today' : `${days}d`})
                    </span>
                  )}
                </p>
              </div>
              {grant.is_recurring && grant.next_estimated_deadline && (
                <div className="detail-item">
                  <label>Next estimated deadline</label>
                  <p>{fmtDate(grant.next_estimated_deadline)}</p>
                </div>
              )}
            </div>
          </div>

          {(grant.research_areas?.length > 0 || grant.eligibility) && (
            <div className="detail-section">
              <h4>Scope &amp; Eligibility</h4>
              {grant.research_areas?.length > 0 && (
                <div style={{marginBottom:'12px'}}>
                  <div className="tags-list">
                    {grant.research_areas.map(a => <span key={a} className="tag">{a}</span>)}
                  </div>
                </div>
              )}
              {grant.eligibility && <p style={{fontSize:'14px', color:'var(--text)', lineHeight:'1.6'}}>{grant.eligibility}</p>}
            </div>
          )}

          {grant.requirements && (
            <div className="detail-section">
              <h4>Requirements</h4>
              <p style={{fontSize:'14px', color:'var(--text)', lineHeight:'1.6', whiteSpace:'pre-wrap'}}>{grant.requirements}</p>
            </div>
          )}

          {grant.notes && (
            <div className="detail-section">
              <h4>Notes</h4>
              <p style={{fontSize:'14px', color:'var(--text)', lineHeight:'1.6', whiteSpace:'pre-wrap'}}>{grant.notes}</p>
            </div>
          )}

          {(grant.website_url || grant.contacts) && (
            <div className="detail-section">
              <h4>Links &amp; Contacts</h4>
              <div className="detail-grid">
                {grant.website_url && (
                  <div className="detail-item">
                    <label>Website</label>
                    <a href={grant.website_url} target="_blank" rel="noreferrer" className="detail-link">
                      Apply / More info ↗
                    </a>
                  </div>
                )}
                {grant.contacts && (
                  <div className="detail-item">
                    <label>Contacts</label>
                    <p>{grant.contacts}</p>
                  </div>
                )}
              </div>
            </div>
          )}

          <p style={{fontSize:'11px', color:'var(--text-light)', marginTop:'8px'}}>
            Added {fmtDate(grant.created_at?.split('T')[0])}
            {grant.updated_at !== grant.created_at && ` · Updated ${fmtDate(grant.updated_at?.split('T')[0])}`}
          </p>
        </div>
        <div className="modal-footer" style={{justifyContent:'space-between'}}>
          <div>
            {canDelete && (
              <button className="btn btn-danger btn-sm" onClick={() => {
                if (confirm(`Delete "${grant.name}"? This cannot be undone.`)) onDelete(grant.id);
              }}>Delete</button>
            )}
          </div>
          <div style={{display:'flex', gap:'10px'}}>
            <button className="btn btn-secondary" onClick={onClose}>Close</button>
            {canEdit && <button className="btn btn-primary" onClick={() => onEdit(grant)}>Edit</button>}
          </div>
        </div>
      </div>
    </div>
  );
}

// ── Table View ───────────────────────────────────────────────
function TableView({ grants, onSelect }) {
  const [sortKey, setSortKey] = useState('deadline');
  const [sortDir, setSortDir] = useState('asc');

  const toggleSort = (key) => {
    if (sortKey === key) setSortDir(d => d === 'asc' ? 'desc' : 'asc');
    else { setSortKey(key); setSortDir('asc'); }
  };

  const sorted = useMemo(() => {
    return [...grants].sort((a, b) => {
      let va = a[sortKey], vb = b[sortKey];
      if (va === null || va === undefined) va = '';
      if (vb === null || vb === undefined) vb = '';
      const cmp = String(va).localeCompare(String(vb), undefined, { numeric: true });
      return sortDir === 'asc' ? cmp : -cmp;
    });
  }, [grants, sortKey, sortDir]);

  const SortTh = ({ label, field }) => (
    <th className="sortable" onClick={() => toggleSort(field)}>
      {label} {sortKey === field ? (sortDir === 'asc' ? '↑' : '↓') : ''}
    </th>
  );

  if (!grants.length) {
    return (
      <div className="table-wrap">
        <div className="empty-state">
          <p>No grants match your filters.</p>
        </div>
      </div>
    );
  }

  return (
    <div className="table-wrap">
      <table className="grants-table">
        <thead>
          <tr>
            <SortTh label="Grant" field="name" />
            <SortTh label="Funding Body" field="funding_body" />
            <SortTh label="Amount" field="amount_max" />
            <SortTh label="Opens" field="open_date" />
            <SortTh label="Deadline" field="deadline" />
            <th>Urgency</th>
            <SortTh label="Status" field="status" />
            <th>Areas</th>
          </tr>
        </thead>
        <tbody>
          {sorted.map(g => {
            const days = daysUntil(g.deadline);
            return (
              <tr key={g.id} onClick={() => onSelect(g)}>
                <td>
                  <div className="grant-name">
                    {g.name}
                    {g.is_recurring && <span className="recurring-tag">↻</span>}
                  </div>
                </td>
                <td><span className="grant-body">{g.funding_body}</span></td>
                <td><span className="grant-amount">{fmtAmount(g)}</span></td>
                <td style={{fontSize:'13px', color:'var(--text-mid)'}}>{fmtDate(g.open_date)}</td>
                <td style={{fontSize:'13px', color:'var(--text-mid)', whiteSpace:'nowrap'}}>{fmtDate(g.deadline)}</td>
                <td>{g.deadline ? <UrgencyBadge deadline={g.deadline} /> : <span style={{color:'var(--text-light)', fontSize:'12px'}}>—</span>}</td>
                <td><StatusBadge status={g.status} /></td>
                <td>
                  <div className="tags-list">
                    {(g.research_areas || []).slice(0,2).map(a => (
                      <span key={a} className="tag" style={{fontSize:'11px'}}>{a}</span>
                    ))}
                    {(g.research_areas || []).length > 2 && (
                      <span style={{fontSize:'11px', color:'var(--text-light)'}}>+{g.research_areas.length - 2}</span>
                    )}
                  </div>
                </td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
}

// ── Kanban View ──────────────────────────────────────────────
function KanbanView({ grants, onSelect }) {
  return (
    <div className="kanban-board">
      {KANBAN_COLS.map(col => {
        const cards = grants.filter(g => g.status === col);
        return (
          <div key={col} className="kanban-col">
            <div className="kanban-col-header">
              <span className="kanban-col-title">{col}</span>
              <span className="kanban-count">{cards.length}</span>
            </div>
            {cards.length === 0 && (
              <div style={{fontSize:'12px', color:'var(--text-light)', padding:'12px 0', textAlign:'center'}}>
                —
              </div>
            )}
            {cards.map(g => {
              const days = daysUntil(g.deadline);
              return (
                <div key={g.id} className="kanban-card" onClick={() => onSelect(g)}>
                  <div className="kanban-card-name">{g.name}</div>
                  <div className="kanban-card-body">{g.funding_body}</div>
                  <div className="kanban-card-footer">
                    <span style={{fontSize:'12px', color:'var(--text-light)'}}>{fmtAmount(g)}</span>
                    {g.deadline
                      ? <UrgencyBadge deadline={g.deadline} />
                      : <span style={{fontSize:'11px', color:'var(--text-light)'}}>No deadline</span>
                    }
                  </div>
                  {g.is_recurring && <span className="recurring-tag" style={{marginTop:'6px', display:'inline-block'}}>↻ recurring</span>}
                </div>
              );
            })}
          </div>
        );
      })}
    </div>
  );
}

// ── Calendar View ────────────────────────────────────────────
function CalendarView({ grants, onSelect }) {
  const today = new Date();
  const [year, setYear]   = useState(today.getFullYear());
  const [month, setMonth] = useState(today.getMonth());

  const prevMonth = () => { if (month === 0) { setMonth(11); setYear(y => y-1); } else setMonth(m => m-1); };
  const nextMonth = () => { if (month === 11) { setMonth(0); setYear(y => y+1); } else setMonth(m => m+1); };

  // Build day grid
  const firstDay  = new Date(year, month, 1).getDay();
  const daysInMo  = new Date(year, month + 1, 0).getDate();
  const daysInPrev = new Date(year, month, 0).getDate();
  const cells = [];

  for (let i = firstDay - 1; i >= 0; i--)
    cells.push({ day: daysInPrev - i, current: false });
  for (let d = 1; d <= daysInMo; d++)
    cells.push({ day: d, current: true });
  const rem = 42 - cells.length;
  for (let d = 1; d <= rem; d++)
    cells.push({ day: d, current: false });

  // Build event map
  const eventMap = {};
  const addEvent = (dateStr, ev) => {
    if (!dateStr) return;
    const d = new Date(dateStr + 'T00:00:00');
    if (d.getFullYear() !== year || d.getMonth() !== month) return;
    const k = d.getDate();
    if (!eventMap[k]) eventMap[k] = [];
    eventMap[k].push(ev);
  };

  grants.forEach(g => {
    if (g.open_date)  addEvent(g.open_date,  { type:'open',     label:`📂 ${g.name}`, grant:g });
    if (g.deadline) {
      const days = daysUntil(g.deadline);
      const cls = days !== null ? calDeadlineClass(days) : 'deadline-low';
      addEvent(g.deadline, { type:'deadline', cls, label:`⏰ ${g.name}`, grant:g });
    }
    if (g.is_recurring && g.next_estimated_deadline) {
      addEvent(g.next_estimated_deadline, { type:'deadline', cls:'deadline-low', label:`↻ ${g.name}`, grant:g });
    }
  });

  const todayStr = `${today.getFullYear()}-${String(today.getMonth()+1).padStart(2,'0')}-${String(today.getDate()).padStart(2,'0')}`;

  return (
    <div>
      <div className="urgency-legend">
        <span style={{fontSize:'12px', color:'var(--text-mid)', fontWeight:500}}>Deadline urgency:</span>
        {[
          ['deadline-critical','≤1d'],
          ['deadline-high','≤7d'],
          ['deadline-medium','≤14d'],
          ['deadline-low','>14d'],
          ['open-date','Opens'],
        ].map(([cls, lbl]) => (
          <span key={cls} className="urgency-legend-item">
            <span className={`urgency-legend-dot cal-event ${cls}`} style={{width:'10px',height:'10px',borderRadius:'50%',display:'inline-block'}} />
            {lbl}
          </span>
        ))}
      </div>
      <div className="calendar-wrap">
        <div className="calendar-header">
          <h3>{MONTHS[month]} {year}</h3>
          <div className="calendar-nav">
            <button onClick={prevMonth}>‹</button>
            <button onClick={() => { setYear(today.getFullYear()); setMonth(today.getMonth()); }}>
              Today
            </button>
            <button onClick={nextMonth}>›</button>
          </div>
        </div>
        <div className="calendar-grid">
          {DOW.map(d => <div key={d} className="calendar-dow">{d}</div>)}
          {cells.map((cell, i) => {
            const isToday = cell.current &&
              today.getDate() === cell.day &&
              today.getMonth() === month &&
              today.getFullYear() === year;
            const events = cell.current ? (eventMap[cell.day] || []) : [];
            return (
              <div key={i} className={`calendar-day ${!cell.current ? 'other-month' : ''} ${isToday ? 'today' : ''}`}>
                <div className="calendar-day-num">{cell.day}</div>
                {events.map((ev, j) => (
                  <div key={j}
                    className={`cal-event ${ev.type === 'open' ? 'open-date' : ev.cls}`}
                    onClick={() => onSelect(ev.grant)}
                    title={ev.label}
                  >
                    {ev.label}
                  </div>
                ))}
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
}

// ── Main App ─────────────────────────────────────────────────
function App() {
  const [session,  setSession]  = useState(null);
  const [profile,  setProfile]  = useState(null);
  const [grants,   setGrants]   = useState([]);
  const [loading,  setLoading]  = useState(true);
  const [view,     setView]     = useState('table'); // table | kanban | calendar
  const [selected, setSelected] = useState(null);    // grant to view
  const [editing,  setEditing]  = useState(null);    // grant to edit (null = new)
  const [showForm, setShowForm] = useState(false);

  // Filters
  const [search,      setSearch]      = useState('');
  const [filterStatus, setFilterStatus] = useState('');
  const [filterBody,   setFilterBody]   = useState('');
  const [filterArea,   setFilterArea]   = useState('');
  const [filterUrgent, setFilterUrgent] = useState(false);

  // Init auth
  useEffect(() => {
    db.auth.getSession().then(({ data }) => {
      setSession(data.session);
      if (!data.session) setLoading(false);
    });
    const { data: listener } = db.auth.onAuthStateChange((_ev, sess) => {
      setSession(sess);
      if (!sess) { setLoading(false); setProfile(null); setGrants([]); }
    });
    return () => listener.subscription.unsubscribe();
  }, []);

  // Load profile + grants when session changes
  useEffect(() => {
    if (!session) return;
    loadAll();
  }, [session]);

  const loadAll = async () => {
    setLoading(true);
    try {
      const [{ data: prof }, { data: grs }] = await Promise.all([
        db.from('profiles').select('*').eq('id', session.user.id).single(),
        db.from('grants').select('*').order('deadline', { ascending: true, nullsFirst: false }),
      ]);
      setProfile(prof);
      setGrants(grs || []);
    } catch (e) {
      console.error(e);
    } finally {
      setLoading(false);
    }
  };

  const handleLogin = (sess) => setSession(sess);
  const handleLogout = () => db.auth.signOut();

  const handleSave = (saved) => {
    setGrants(gs => {
      const idx = gs.findIndex(g => g.id === saved.id);
      if (idx >= 0) { const n = [...gs]; n[idx] = saved; return n; }
      return [...gs, saved];
    });
    setShowForm(false);
    setEditing(null);
    setSelected(saved);
  };

  const handleDelete = async (id) => {
    const { error } = await db.from('grants').delete().eq('id', id);
    if (error) { alert(error.message); return; }
    setGrants(gs => gs.filter(g => g.id !== id));
    setSelected(null);
  };

  const handleEdit = (grant) => {
    setEditing(grant);
    setSelected(null);
    setShowForm(true);
  };

  // Derived filter values for dropdowns
  const allBodies = useMemo(() => [...new Set(grants.map(g => g.funding_body))].sort(), [grants]);
  const allAreas  = useMemo(() => {
    const s = new Set();
    grants.forEach(g => (g.research_areas || []).forEach(a => s.add(a)));
    return [...s].sort();
  }, [grants]);

  // Filtered grants
  const filtered = useMemo(() => {
    return grants.filter(g => {
      if (filterStatus && g.status !== filterStatus) return false;
      if (filterBody && g.funding_body !== filterBody) return false;
      if (filterArea && !(g.research_areas || []).includes(filterArea)) return false;
      if (filterUrgent) {
        const days = daysUntil(g.deadline);
        if (days === null || days > 14) return false;
      }
      if (search) {
        const q = search.toLowerCase();
        return (
          g.name.toLowerCase().includes(q) ||
          g.funding_body.toLowerCase().includes(q) ||
          (g.notes || '').toLowerCase().includes(q) ||
          (g.research_areas || []).some(a => a.toLowerCase().includes(q))
        );
      }
      return true;
    });
  }, [grants, filterStatus, filterBody, filterArea, filterUrgent, search]);

  // Stats
  const stats = useMemo(() => ({
    total:      grants.length,
    inProgress: grants.filter(g => g.status === 'In progress').length,
    submitted:  grants.filter(g => g.status === 'Submitted').length,
    awarded:    grants.filter(g => g.status === 'Awarded').length,
    urgent:     grants.filter(g => { const d = daysUntil(g.deadline); return d !== null && d >= 0 && d <= 14; }).length,
  }), [grants]);

  if (!session) return <AuthScreen onLogin={handleLogin} />;
  if (loading)  return <div className="app-shell"><div className="main"><span className="spinner" /></div></div>;

  const userRole = profile?.role || 'viewer';
  const canEdit = userRole === 'admin' || userRole === 'editor';

  return (
    <div className="app-shell">
      {/* Header */}
      <header className="header">
        <div className="header-brand">
          <h1>Grants &amp; Scholarships</h1>
          {userRole !== 'viewer' && <span>{userRole}</span>}
        </div>
        <div style={{flex:1, display:'flex', justifyContent:'center'}}>
          <div className="view-tabs">
            {[['table','Table'],['calendar','Calendar'],['kanban','Board']].map(([v, l]) => (
              <button key={v} className={`view-tab ${view === v ? 'active' : ''}`}
                onClick={() => setView(v)}>{l}</button>
            ))}
          </div>
        </div>
        <div className="header-right">
          <span className="header-user">{profile?.email}</span>
          {canEdit && (
            <button className="btn btn-primary btn-sm"
              onClick={() => { setEditing(null); setShowForm(true); }}>
              + Add Grant
            </button>
          )}
          <button className="btn btn-ghost btn-sm" style={{color:'var(--mist)'}} onClick={handleLogout}>
            Sign out
          </button>
        </div>
      </header>

      <main className="main">
        {/* Stats */}
        <div className="stats-bar">
          <div className="stat-pill">
            <span className="stat-count">{stats.total}</span>
            <span className="stat-label">Total</span>
          </div>
          {stats.inProgress > 0 && (
            <div className="stat-pill">
              <span className="stat-dot" style={{background:'#3d5af1'}} />
              <span className="stat-count">{stats.inProgress}</span>
              <span className="stat-label">In progress</span>
            </div>
          )}
          {stats.submitted > 0 && (
            <div className="stat-pill">
              <span className="stat-dot" style={{background:'var(--sage)'}} />
              <span className="stat-count">{stats.submitted}</span>
              <span className="stat-label">Submitted</span>
            </div>
          )}
          {stats.awarded > 0 && (
            <div className="stat-pill">
              <span className="stat-dot" style={{background:'#1a7a4a'}} />
              <span className="stat-count">{stats.awarded}</span>
              <span className="stat-label">Awarded</span>
            </div>
          )}
          {stats.urgent > 0 && (
            <div className="stat-pill" style={{borderColor:'var(--ember)'}}>
              <span className="stat-dot" style={{background:'var(--ember)'}} />
              <span className="stat-count" style={{color:'var(--ember)'}}>{stats.urgent}</span>
              <span className="stat-label">Urgent (≤14d)</span>
            </div>
          )}
        </div>

        {/* Filters */}
        <div className="filter-bar">
          <input type="search" placeholder="Search grants…"
            value={search} onChange={e => setSearch(e.target.value)} />
          <select value={filterStatus} onChange={e => setFilterStatus(e.target.value)}>
            <option value="">All statuses</option>
            {STATUSES.map(s => <option key={s}>{s}</option>)}
          </select>
          <select value={filterBody} onChange={e => setFilterBody(e.target.value)}>
            <option value="">All funders</option>
            {allBodies.map(b => <option key={b}>{b}</option>)}
          </select>
          <select value={filterArea} onChange={e => setFilterArea(e.target.value)}>
            <option value="">All research areas</option>
            {allAreas.map(a => <option key={a}>{a}</option>)}
          </select>
          <label style={{display:'flex', alignItems:'center', gap:'6px', fontSize:'13px', color:'var(--text-mid)', cursor:'pointer', whiteSpace:'nowrap'}}>
            <input type="checkbox" checked={filterUrgent}
              onChange={e => setFilterUrgent(e.target.checked)} />
            Urgent only (≤14d)
          </label>
          {(search || filterStatus || filterBody || filterArea || filterUrgent) && (
            <button className="btn btn-ghost btn-sm" onClick={() => {
              setSearch(''); setFilterStatus(''); setFilterBody(''); setFilterArea(''); setFilterUrgent(false);
            }}>Clear filters</button>
          )}
          <span style={{marginLeft:'auto', fontSize:'13px', color:'var(--text-light)'}}>
            {filtered.length} of {grants.length}
          </span>
        </div>

        {/* Mobile view switcher */}
        <div style={{display:'flex', gap:'8px', marginBottom:'16px', flexWrap:'wrap'}} className="mobile-view-tabs">
          <style>{`.mobile-view-tabs { display: none; } @media(max-width:768px){.mobile-view-tabs{display:flex;}}`}</style>
          {[['table','Table'],['calendar','Calendar'],['kanban','Board']].map(([v, l]) => (
            <button key={v} className={`btn btn-sm ${view === v ? 'btn-primary' : 'btn-secondary'}`}
              onClick={() => setView(v)}>{l}</button>
          ))}
        </div>

        {/* Views */}
        {view === 'table'    && <TableView    grants={filtered} onSelect={setSelected} />}
        {view === 'kanban'   && <KanbanView   grants={filtered} onSelect={setSelected} />}
        {view === 'calendar' && <CalendarView grants={filtered} onSelect={setSelected} />}
      </main>

      {/* Detail modal */}
      {selected && (
        <GrantDetail
          grant={selected}
          userRole={userRole}
          onEdit={handleEdit}
          onDelete={handleDelete}
          onClose={() => setSelected(null)}
        />
      )}

      {/* Add/Edit form */}
      {showForm && (
        <GrantForm
          grant={editing}
          userRole={userRole}
          onSave={handleSave}
          onClose={() => { setShowForm(false); setEditing(null); }}
        />
      )}
    </div>
  );
}

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