/* Mandera Portal — Client app shell + sections (live on MData). */

function StatusPill({ status }) {
  const { Badge } = window.ManderaDesignSystem_8327ce;
  const map = {
    Confirmed: ['positive', 'soft'], Scheduled: ['positive', 'soft'], Pending: ['caution', 'outline'],
    Declined: ['critical', 'outline'], Posted: ['neutral', 'soft'],
    Draft: ['neutral', 'outline'], 'In progress': ['accent', 'soft'], Planning: ['caution', 'outline'],
    done: ['positive', 'soft'], 'in-progress': ['accent', 'soft'], open: ['caution', 'outline'],
  };
  const [tone, variant] = map[status] || ['neutral', 'outline'];
  const label = status === 'in-progress' ? 'In progress' : status.charAt(0).toUpperCase() + status.slice(1);
  return <Badge tone={tone} variant={variant}>{label}</Badge>;
}

function SectionWrap({ children, pad = 32 }) {
  return <div className="mp-section" style={{ padding: pad, display: 'flex', flexDirection: 'column', gap: 20 }}>{children}</div>;
}
function Empty({ icon, text }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12, padding: '60px 0', color: 'var(--text-faint)' }}>
      <window.PIcon n={icon} s={32} color="var(--text-faint)" stroke={1.4} />
      <span style={{ fontFamily: 'var(--font-sans)', fontSize: 14 }}>{text}</span>
    </div>
  );
}
const fmtDate = (iso) => { try { return new Date(iso + 'T00:00').toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' }); } catch (e) { return iso; } };
const _todayISO = () => new Date().toISOString().slice(0, 10);
function dayLabel(iso) {
  const t = new Date(_todayISO() + 'T00:00'); const d = new Date(iso + 'T00:00');
  const diff = Math.round((d - t) / 86400000);
  if (diff === 0) return 'Today'; if (diff === 1) return 'Tomorrow';
  if (diff > 1 && diff < 7) return new Date(iso + 'T00:00').toLocaleDateString('en-US', { weekday: 'long' });
  return new Date(iso + 'T00:00').toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' });
}
/* Build an upcoming agenda (today onward) from calendar posts + shoots.
   Pass clientId to scope to one client, or null for every client (owner view). */
function buildAgenda(clientId) {
  const today = _todayISO();
  const cal = (clientId ? window.MData.forClient('calendar', clientId) : window.MData.get('calendar'))
    .filter((c) => c.date && c.date >= today)
    .map((c) => ({ id: c.id, kind: 'post', date: c.date, time: c.time || '', title: c.title, meta: c.platform, status: c.status, clientId: c.clientId, media: c.media }));
  const shoots = (clientId ? window.MData.forClient('shoots', clientId) : window.MData.get('shoots'))
    .filter((s) => s.date && s.date >= today)
    .map((s) => ({ id: s.id, kind: 'shoot', date: s.date, time: s.time || '', title: s.title, meta: s.location, status: s.status, clientId: s.clientId }));
  const all = cal.concat(shoots).sort((a, b) => (a.date + (a.time || '')).localeCompare(b.date + (b.time || '')));
  const byDate = {};
  all.forEach((it) => { (byDate[it.date] = byDate[it.date] || []).push(it); });
  return Object.keys(byDate).sort().map((d) => ({ date: d, items: byDate[d] }));
}
function AgendaRow({ it, showClient }) {
  const client = showClient && it.clientId ? window.MData.client(it.clientId) : null;
  const isImg = it.media && it.media.type && it.media.type.startsWith('image');
  return (
    <div style={{ display: 'flex', gap: 14, alignItems: 'center', padding: '13px 16px', background: 'var(--surface-card)', border: '1px solid var(--border-subtle)', borderRadius: 'var(--radius-md)' }}>
      {isImg
        ? <img src={it.media.url || it.media.dataUrl} alt="" style={{ width: 42, height: 42, flex: 'none', borderRadius: 'var(--radius-sm)', objectFit: 'cover', border: '1px solid var(--border-subtle)' }} />
        : <span style={{ width: 42, height: 42, flex: 'none', borderRadius: 'var(--radius-sm)', background: it.kind === 'shoot' ? 'rgba(110,170,240,0.12)' : 'var(--accent-soft)', border: '1px solid var(--border-subtle)', display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}><window.PIcon n={it.kind === 'shoot' ? 'camera' : 'send'} s={18} color={it.kind === 'shoot' ? 'var(--azure-400)' : 'var(--positive)'} /></span>}
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontFamily: 'var(--font-sans)', fontSize: 14.5, fontWeight: 600, color: 'var(--text-strong)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{it.title}</div>
        <div style={{ fontFamily: 'var(--font-sans)', fontSize: 12.5, color: 'var(--text-muted)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
          {client ? client.brand + ' · ' : ''}{it.kind === 'shoot' ? 'Shoot' : it.meta}{it.time ? ' · ' + it.time : ''}
        </div>
      </div>
      <StatusPill status={it.status} />
    </div>
  );
}
function Agenda({ clientId, showClient, emptyText }) {
  const days = buildAgenda(clientId);
  if (!days.length) return <Empty icon="calendar-clock" text={emptyText || 'Nothing scheduled coming up'} />;
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
      {days.map((g) => (
        <div key={g.date} style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
          <div style={{ display: 'flex', alignItems: 'baseline', gap: 10 }}>
            <span style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 17, color: 'var(--text-strong)' }}>{dayLabel(g.date)}</span>
            <span style={{ fontFamily: 'var(--font-sans)', fontSize: 12.5, color: 'var(--text-faint)' }}>{fmtDate(g.date)}</span>
            <span style={{ flex: 1, height: 1, background: 'var(--border-subtle)' }} />
            <span style={{ fontFamily: 'var(--font-sans)', fontSize: 12, color: 'var(--text-faint)' }}>{g.items.length} item{g.items.length > 1 ? 's' : ''}</span>
          </div>
          {g.items.map((it) => <AgendaRow key={it.kind + it.id} it={it} showClient={showClient} />)}
        </div>
      ))}
    </div>
  );
}

/* Items scheduled for TODAY (shoots + posts), across one client or all. */
function todayItems(clientId) {
  const today = _todayISO();
  const cal = (clientId ? window.MData.forClient('calendar', clientId) : window.MData.get('calendar'))
    .filter((c) => c.date === today)
    .map((c) => ({ id: c.id, kind: 'post', date: c.date, time: c.time || '', title: c.title, meta: c.platform, status: c.status, clientId: c.clientId, media: c.media }));
  const shoots = (clientId ? window.MData.forClient('shoots', clientId) : window.MData.get('shoots'))
    .filter((s) => s.date === today)
    .map((s) => ({ id: s.id, kind: 'shoot', date: s.date, time: s.time || '', title: s.title, meta: s.location, status: s.status, clientId: s.clientId }));
  return cal.concat(shoots).sort((a, b) => (a.time || '').localeCompare(b.time || ''));
}
/* A "Today" card — the current date + everything happening on it. Used on
   both the client dashboard and the owner overview so they match. */
function TodayPanel({ clientId, showClient }) {
  const now = new Date();
  const weekday = now.toLocaleDateString('en-US', { weekday: 'long' });
  const rest = now.toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' });
  const items = todayItems(clientId || null);
  return (
    <div style={{ borderRadius: 'var(--radius-xl)', border: '1px solid var(--border-subtle)', background: 'var(--surface-card)', padding: '22px 24px', display: 'flex', flexDirection: 'column', gap: 14 }}>
      <div style={{ display: 'flex', alignItems: 'baseline', gap: 12, flexWrap: 'wrap' }}>
        <window.PIcon n="calendar-days" s={19} color="var(--accent-text)" stroke={1.7} />
        <span style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 23, color: 'var(--text-strong)' }}>Today</span>
        <span style={{ fontFamily: 'var(--font-sans)', fontSize: 14, color: 'var(--text-muted)' }}>{weekday}, {rest}</span>
        <span style={{ flex: 1 }} />
        <span style={{ fontFamily: 'var(--font-sans)', fontSize: 12.5, color: 'var(--text-faint)' }}>{items.length} scheduled</span>
      </div>
      {items.length
        ? <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>{items.map((it) => <AgendaRow key={it.kind + it.id} it={it} showClient={showClient} />)}</div>
        : <div style={{ padding: '10px 0', fontFamily: 'var(--font-sans)', fontSize: 14, color: 'var(--text-faint)' }}>Nothing scheduled for today.</div>}
    </div>
  );
}

/* ---------------- Dashboard ---------------- */
function ClientDashboard({ clientId, client, acc, go }) {
  const { Button, Card } = window.ManderaDesignSystem_8327ce;
  const shoots = window.MData.forClient('shoots', clientId).slice().sort((a, b) => a.date.localeCompare(b.date));
  const calendar = window.MData.forClient('calendar', clientId).slice().sort((a, b) => a.date.localeCompare(b.date));
  const projects = window.MData.forClient('projects', clientId);
  const timeline = window.MData.forClient('timeline', clientId);
  const nextShoot = shoots[0];
  const nextPost = calendar[0];
  return (
    <SectionWrap>
      <div style={{ position: 'relative', overflow: 'hidden', borderRadius: 'var(--radius-xl)', border: '1px solid var(--border-subtle)', background: 'var(--ink-900)', padding: '32px 34px' }}>
        <window.MarkWash opacity={0.16} size={420} pos={{ top: '-120px', right: '-80px' }} />
        <div style={{ position: 'relative' }}>
          <span className="mandera-eyebrow">Welcome back · {client.brand}</span>
          <h2 style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 38, color: 'var(--text-strong)', margin: '12px 0 8px', letterSpacing: '-0.01em' }}>
            Good to see you, {(acc.name || '').split(' ')[0]}.
          </h2>
          <p style={{ fontFamily: 'var(--font-sans)', fontSize: 16, color: 'var(--text-secondary)', margin: 0, maxWidth: 540 }}>
            Your account lead is <b style={{ color: 'var(--text-strong)' }}>{client.manager}</b>. Here’s where things stand across your projects.
          </p>
          <div style={{ display: 'flex', gap: 12, marginTop: 22, flexWrap: 'wrap' }}>
            <Button variant="primary" onClick={() => go('shoots')} iconRight={<window.PIcon n="arrow-right" s={16} color="var(--ink-950)" />}>Upcoming shoots</Button>
            <Button variant="outline" onClick={() => go('messages')} iconLeft={<window.PIcon n="message-square" s={16} color="var(--accent-text)" />}>Message your team</Button>
          </div>
        </div>
      </div>

      <TodayPanel clientId={clientId} />

      <div className="mp-grid3" style={{ display: 'grid', gridTemplateColumns: 'repeat(3,1fr)', gap: 18 }}>
        {[
          { ic: 'camera', lbl: 'Next shoot', val: nextShoot ? fmtDate(nextShoot.date) : '—', sub: nextShoot ? nextShoot.title : 'Nothing booked', color: 'var(--azure-400)' },
          { ic: 'send', lbl: 'Next post', val: nextPost ? fmtDate(nextPost.date) : '—', sub: nextPost ? nextPost.title : 'Nothing scheduled', color: 'var(--positive)' },
          { ic: 'folder-kanban', lbl: 'Active projects', val: projects.length + '', sub: projects.map((p) => p.name).join(' · ') || '—', color: 'var(--accent-text)' },
        ].map((s) => (
          <Card key={s.lbl}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 14 }}>
              <window.PIcon n={s.ic} s={18} color={s.color} stroke={1.7} />
              <span style={{ fontFamily: 'var(--font-sans)', fontSize: 13, color: 'var(--text-muted)' }}>{s.lbl}</span>
            </div>
            <div style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 28, color: 'var(--text-strong)', lineHeight: 1 }}>{s.val}</div>
            <div style={{ fontFamily: 'var(--font-sans)', fontSize: 13, color: 'var(--text-secondary)', marginTop: 8, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{s.sub}</div>
          </Card>
        ))}
      </div>

      <div>
        <h3 style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 20, color: 'var(--text-strong)', margin: '0 0 12px' }}>Coming up</h3>
        <Agenda clientId={clientId} emptyText="Nothing scheduled coming up — your team will add posts and shoots here." />
      </div>

      <div className="mp-grid2" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 18 }}>
        <div>
          <h3 style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 20, color: 'var(--text-strong)', margin: '0 0 12px' }}>Upcoming shoots</h3>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
            {shoots.slice(0, 3).map((s) => (
              <div key={s.id} style={{ display: 'flex', gap: 14, alignItems: 'center', padding: '14px 16px', background: 'var(--surface-card)', border: '1px solid var(--border-subtle)', borderRadius: 'var(--radius-md)' }}>
                <window.PIcon n="camera" s={18} color="var(--azure-400)" />
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontFamily: 'var(--font-sans)', fontSize: 14, fontWeight: 600, color: 'var(--text-strong)' }}>{s.title}</div>
                  <div style={{ fontFamily: 'var(--font-sans)', fontSize: 12.5, color: 'var(--text-muted)' }}>{fmtDate(s.date)} · {s.time}</div>
                </div>
                <StatusPill status={s.status} />
              </div>
            ))}
            {!shoots.length && <Empty icon="camera" text="No shoots booked yet" />}
          </div>
        </div>
        <div>
          <h3 style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 20, color: 'var(--text-strong)', margin: '0 0 12px' }}>Project timeline</h3>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 0 }}>
            {timeline.slice(0, 5).map((t, i) => (
              <div key={t.id} style={{ display: 'flex', gap: 12, alignItems: 'flex-start', padding: '8px 0' }}>
                <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', alignSelf: 'stretch' }}>
                  <window.PIcon n={t.status === 'completed' ? 'check-circle' : t.status === 'current' ? 'circle-dot' : 'circle'} s={16} color={t.status === 'completed' ? 'var(--positive)' : t.status === 'current' ? 'var(--accent-bright)' : 'var(--text-faint)'} />
                  {i < Math.min(timeline.length, 5) - 1 && <span style={{ flex: 1, width: 1, background: 'var(--border-subtle)', marginTop: 2 }} />}
                </div>
                <div style={{ paddingBottom: 8 }}>
                  <div style={{ fontFamily: 'var(--font-sans)', fontSize: 14, fontWeight: t.status === 'current' ? 600 : 500, color: t.status === 'upcoming' ? 'var(--text-muted)' : 'var(--text-strong)' }}>{t.task}</div>
                  <div style={{ fontFamily: 'var(--font-sans)', fontSize: 12, color: 'var(--text-faint)' }}>{fmtDate(t.date)}</div>
                </div>
              </div>
            ))}
            {!timeline.length && <Empty icon="git-commit" text="Timeline coming soon" />}
          </div>
        </div>
      </div>
    </SectionWrap>
  );
}

/* ---------------- Shoots ---------------- */
function ClientShoots({ clientId, client, acc, focus, bump }) {
  const { Button } = window.ManderaDesignSystem_8327ce;
  const shoots = window.MData.forClient('shoots', clientId).slice().sort((a, b) => a.date.localeCompare(b.date));
  const [openForm, setOpenForm] = React.useState(null);
  const [note, setNote] = React.useState('');
  const brand = client ? client.brand : 'A client';
  const teamMail = (subject, intro, rows) => { try { fetch('../api/portal-mail.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'notify_team', subject, intro, rows }) }).catch(() => {}); } catch (e) {} };
  const respond = (s, ok) => {
    window.MData.update('shoots', s.id, { status: ok ? 'Confirmed' : 'Declined', respondedBy: acc.name, respondedAt: Date.now() });
    window.MData.notify({ audience: 'team', type: 'shoot', text: brand + (ok ? ' approved' : ' declined') + ' the shoot “' + s.title + '” · ' + fmtDate(s.date), link: 'clients', refId: clientId });
    teamMail((ok ? 'Shoot approved' : 'Shoot declined') + ' — ' + brand, brand + (ok ? ' approved' : ' declined') + ' a scheduled shoot.', [['Shoot', s.title], ['Date', fmtDate(s.date)], ['Time', s.time || '—'], ['Location', s.location || '—'], ['Response', ok ? 'Approved' : 'Declined']]);
    bump();
  };
  const requestChange = (s) => {
    const t = note.trim(); if (!t) return;
    window.MData.update('shoots', s.id, { changeRequest: { note: t, by: acc.name, ts: Date.now() } });
    window.MData.notify({ audience: 'team', type: 'shootchange', text: brand + ' requested a change to the shoot “' + s.title + '”: “' + t + '”', link: 'clients', refId: clientId });
    teamMail('Shoot change request — ' + brand, brand + ' asked to change a scheduled shoot.', [['Shoot', s.title], ['Currently', fmtDate(s.date) + (s.time ? ' · ' + s.time : '')], ['Location', s.location || '—'], ['Request', t]]);
    setOpenForm(null); setNote(''); bump();
  };
  const chipBtn = (color, bg, border) => ({ appearance: 'none', display: 'inline-flex', alignItems: 'center', gap: 7, cursor: 'pointer', fontFamily: 'var(--font-sans)', fontSize: 12.5, fontWeight: 600, color, background: bg, border: '1px solid ' + border, borderRadius: 'var(--radius-pill)', padding: '7px 14px' });
  return (
    <SectionWrap>
      {shoots.map((s) => {
        const hot = focus && focus === s.id;
        return (
        <div key={s.id} style={{ display: 'flex', gap: 18, padding: '20px 22px', background: 'var(--surface-card)', border: '1px solid', borderColor: hot ? 'var(--border-accent)' : 'var(--border-subtle)', borderRadius: 'var(--radius-lg)', boxShadow: hot ? 'var(--glow-accent)' : 'none', transition: 'box-shadow var(--dur-base) var(--ease-out)' }}>
          <div style={{ width: 56, height: 56, flex: 'none', borderRadius: 'var(--radius-md)', background: 'var(--accent-soft)', border: '1px solid var(--border-accent)', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center' }}>
            <window.PIcon n="camera" s={20} color="var(--accent-text)" stroke={1.6} />
          </div>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 12 }}>
              <h3 style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 20, color: 'var(--text-strong)', margin: 0 }}>{s.title}</h3>
              <StatusPill status={s.status} />
            </div>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 18, marginTop: 12 }}>
              {[['calendar', fmtDate(s.date)], ['clock', s.time], ['map-pin', s.location]].map(([ic, v]) => (
                <span key={ic} style={{ display: 'inline-flex', alignItems: 'center', gap: 7, fontFamily: 'var(--font-sans)', fontSize: 13.5, color: 'var(--text-secondary)' }}>
                  <window.PIcon n={ic} s={15} color="var(--text-muted)" /> {v}
                </span>
              ))}
            </div>
            {s.notes && <p style={{ fontFamily: 'var(--font-sans)', fontSize: 13.5, color: 'var(--text-muted)', margin: '12px 0 0', paddingTop: 12, borderTop: '1px solid var(--border-subtle)' }}>{s.notes}</p>}
            {s.changeRequest && (
              <div style={{ marginTop: 12, padding: '10px 14px', borderRadius: 'var(--radius-md)', background: 'rgba(224,163,62,0.08)', border: '1px solid rgba(224,163,62,0.35)', fontFamily: 'var(--font-sans)', fontSize: 13, color: '#e0a33e', lineHeight: 1.5 }}>
                Change requested: “{s.changeRequest.note}” — your Mandera team has been emailed and will confirm the new details.
              </div>
            )}
            {s.status !== 'Declined' && (
              <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap', marginTop: 14, paddingTop: 14, borderTop: '1px solid var(--border-subtle)' }}>
                {s.status === 'Pending' && (<React.Fragment>
                  <button onClick={() => respond(s, true)} style={chipBtn('var(--positive)', 'rgba(47,170,110,0.1)', 'rgba(47,170,110,0.4)')}><window.PIcon n="check" s={14} color="var(--positive)" /> Approve this shoot</button>
                  <button onClick={() => respond(s, false)} style={chipBtn('var(--critical)', 'rgba(224,82,82,0.08)', 'rgba(224,82,82,0.35)')}><window.PIcon n="x" s={14} color="var(--critical)" /> Decline</button>
                </React.Fragment>)}
                {!s.changeRequest && (
                  <button onClick={() => { setOpenForm(openForm === s.id ? null : s.id); setNote(''); }} style={chipBtn('var(--text-secondary)', 'var(--surface-raised)', 'var(--border-default)')}><window.PIcon n="calendar-clock" s={14} /> Request a change</button>
                )}
              </div>
            )}
            {openForm === s.id && (
              <form onSubmit={(e) => { e.preventDefault(); requestChange(s); }} style={{ display: 'flex', gap: 8, marginTop: 12 }}>
                <input autoFocus value={note} onChange={(e) => setNote(e.target.value)} placeholder="What would you like to change? e.g. Can we move this to Thursday afternoon?"
                  style={{ flex: 1, minWidth: 0, height: 40, padding: '0 12px', background: 'var(--surface-sunken)', border: '1px solid var(--border-default)', borderRadius: 'var(--radius-md)', color: 'var(--text-strong)', fontFamily: 'var(--font-sans)', fontSize: 13.5, outline: 'none' }} />
                <Button variant="primary" size="sm" type="submit">Send request</Button>
              </form>
            )}
          </div>
        </div>
      ); })}
      {!shoots.length && <Empty icon="camera" text="No shoots booked yet" />}
    </SectionWrap>
  );
}

/* ---------------- Content calendar ---------------- */
/* Approve / request changes on a post. “Request changes” asks for a note,
   which lands in the item's note thread so it shows under the content on
   both sides. Works for Draft AND Scheduled items. */
function ApprovalRow({ it, clientId, acc, bump }) {
  const [open, setOpen] = React.useState(false);
  const [note, setNote] = React.useState('');
  const cl = window.MData.client(clientId);
  const brand = cl ? cl.brand : 'A client';
  const approve = () => {
    window.MData.update('calendar', it.id, { approval: 'approved' });
    window.MData.notify({ audience: 'team', type: 'approval', text: brand + ' approved “' + it.title + '”', link: 'clients', refId: clientId });
    bump && bump();
  };
  const sendChanges = (e) => {
    e.preventDefault();
    const t = note.trim(); if (!t) return;
    const notes = (Array.isArray(it.notes) ? it.notes : []).concat([{ id: 'n' + Date.now().toString(36), who: acc ? acc.name : 'Client', role: 'client', text: t, ts: Date.now() }]);
    window.MData.update('calendar', it.id, { approval: 'changes', approvalNote: t, notes });
    window.MData.notify({ audience: 'team', type: 'approval', text: brand + ' requested changes on “' + it.title + '”: “' + t + '”', link: 'clients', refId: clientId });
    setOpen(false); setNote(''); bump && bump();
  };
  const chip = (icon, color, bg, border, label) => (
    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 7, fontFamily: 'var(--font-sans)', fontSize: 12.5, fontWeight: 600, color, background: bg, border: '1px solid ' + border, borderRadius: 'var(--radius-pill)', padding: '5px 12px' }}>
      <window.PIcon n={icon} s={13} color={color} /> {label}
    </span>
  );
  if (it.approval === 'approved') return <div style={{ display: 'flex' }}>{chip('check', 'var(--positive)', 'rgba(47,170,110,0.1)', 'rgba(47,170,110,0.4)', 'Approved by you')}</div>;
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
      <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap', alignItems: 'center' }}>
        {it.approval === 'changes' && chip('pencil', '#e0a33e', 'rgba(224,163,62,0.1)', 'rgba(224,163,62,0.4)', 'Changes requested')}
        {it.status !== 'Posted' && (
          <button onClick={approve} style={{ appearance: 'none', display: 'inline-flex', alignItems: 'center', gap: 7, cursor: 'pointer', fontFamily: 'var(--font-sans)', fontSize: 12.5, fontWeight: 600, color: 'var(--positive)', background: 'rgba(47,170,110,0.1)', border: '1px solid rgba(47,170,110,0.4)', borderRadius: 'var(--radius-pill)', padding: '7px 14px' }}>
            <window.PIcon n="check" s={14} color="var(--positive)" /> Approve
          </button>
        )}
        {it.status !== 'Posted' && it.approval !== 'changes' && (
          <button onClick={() => setOpen(!open)} style={{ appearance: 'none', display: 'inline-flex', alignItems: 'center', gap: 7, cursor: 'pointer', fontFamily: 'var(--font-sans)', fontSize: 12.5, fontWeight: 600, color: 'var(--text-secondary)', background: 'var(--surface-raised)', border: '1px solid var(--border-default)', borderRadius: 'var(--radius-pill)', padding: '7px 14px' }}>
            <window.PIcon n="pencil" s={13} /> Request changes
          </button>
        )}
      </div>
      {open && (
        <form onSubmit={sendChanges} style={{ display: 'flex', gap: 8 }}>
          <input autoFocus value={note} onChange={(e) => setNote(e.target.value)} placeholder="What should change?"
            style={{ flex: 1, minWidth: 0, height: 38, padding: '0 12px', background: 'var(--surface-sunken)', border: '1px solid var(--border-default)', borderRadius: 'var(--radius-md)', color: 'var(--text-strong)', fontFamily: 'var(--font-sans)', fontSize: 13.5, outline: 'none' }} />
          <button type="submit" style={{ appearance: 'none', cursor: 'pointer', fontFamily: 'var(--font-sans)', fontSize: 12.5, fontWeight: 700, color: 'var(--ink-950)', background: 'var(--accent)', border: 'none', borderRadius: 'var(--radius-md)', padding: '0 16px' }}>Send</button>
        </form>
      )}
    </div>
  );
}

function ClientCalendar({ clientId, acc, bump }) {
  const items = window.MData.forClient('calendar', clientId).slice().sort((a, b) => (a.date + a.time).localeCompare(b.date + b.time));
  const platIcon = { Instagram: 'instagram', TikTok: 'music', Email: 'mail', Facebook: 'facebook', YouTube: 'youtube' };
  const isImg = (m) => m && m.type && m.type.startsWith('image');
  const isVid = (m) => m && m.type && m.type.startsWith('video');
  const byDate = {};
  items.forEach((it) => { (byDate[it.date] = byDate[it.date] || []).push(it); });
  return (
    <SectionWrap>
      {Object.keys(byDate).sort().map((d) => (
        <div key={d} style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
            <span style={{ fontFamily: 'var(--font-sans)', fontSize: 13, fontWeight: 600, letterSpacing: '0.06em', textTransform: 'uppercase', color: 'var(--text-secondary)', whiteSpace: 'nowrap' }}>{fmtDate(d)}</span>
            <span style={{ flex: 1, height: 1, background: 'var(--border-subtle)' }} />
          </div>
          {byDate[d].map((it) => (
            <div key={it.id} style={{ display: 'flex', flexDirection: 'column', gap: 12, padding: '14px 16px', background: 'var(--surface-card)', border: '1px solid var(--border-subtle)', borderRadius: 'var(--radius-md)' }}>
              <div style={{ display: 'flex', gap: 14, alignItems: 'center' }}>
              <div style={{ fontFamily: 'var(--font-mono)', fontSize: 13, color: 'var(--text-secondary)', width: 56 }}>{it.time}</div>
              {it.media ? (
                isImg(it.media)
                  ? <a href={it.media.url || it.media.dataUrl} target="_blank" rel="noreferrer" title="View full size" style={{ display: 'inline-flex', flex: 'none' }}><img src={it.media.url || it.media.dataUrl} alt="" style={{ width: 52, height: 52, borderRadius: 'var(--radius-sm)', objectFit: 'cover', border: '1px solid var(--border-subtle)' }} /></a>
                  : <div style={{ width: 52, height: 52, flex: 'none', borderRadius: 'var(--radius-sm)', background: 'var(--surface-sunken)', border: '1px solid var(--border-subtle)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}><window.PIcon n={isVid(it.media) ? 'film' : 'file'} s={20} color="var(--positive)" /></div>
              ) : (
                <window.PIcon n={platIcon[it.platform] || 'globe'} s={18} color="var(--positive)" />
              )}
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontFamily: 'var(--font-sans)', fontSize: 14.5, fontWeight: 600, color: 'var(--text-strong)' }}>{it.title}</div>
                <div style={{ fontFamily: 'var(--font-sans)', fontSize: 12.5, color: 'var(--text-muted)' }}>{it.platform}{it.media ? ' · ' + it.media.name : ''}</div>
              </div>
              {it.media && <a href={it.media.url || it.media.dataUrl} download={it.media.name} title="Download" style={{ display: 'inline-flex', color: 'var(--positive)', padding: 4 }}><window.PIcon n="download" s={16} color="var(--positive)" /></a>}
                <StatusPill status={it.status} />
              </div>
              <ApprovalRow it={it} clientId={clientId} acc={acc} bump={bump} />
              <window.PNotes coll="calendar" item={it} me={{ name: acc.name, role: 'client' }} clientId={clientId} link="calendar" onChange={bump} placeholder="Write a note about this post…" />
            </div>
          ))}
        </div>
      ))}
      {!items.length && <Empty icon="calendar-days" text="No content scheduled yet" />}
    </SectionWrap>
  );
}

/* ---------------- Timeline ---------------- */
function ClientTimeline({ clientId, acc, bump }) {
  const tl = window.MData.forClient('timeline', clientId);
  const groups = [['current', 'In progress'], ['upcoming', 'Upcoming'], ['completed', 'Completed']];
  return (
    <SectionWrap>
      {groups.map(([key, label]) => {
        const items = tl.filter((t) => t.status === key);
        if (!items.length) return null;
        return (
          <div key={key} style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
            <h3 style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 20, color: 'var(--text-strong)', margin: 0 }}>{label}</h3>
            {items.map((t) => (
              <div key={t.id} style={{ display: 'flex', flexDirection: 'column', gap: 12, padding: '14px 16px', background: 'var(--surface-card)', border: '1px solid var(--border-subtle)', borderRadius: 'var(--radius-md)' }}>
                <div style={{ display: 'flex', gap: 14, alignItems: 'center' }}>
                  <window.PIcon n={key === 'completed' ? 'check-circle' : key === 'current' ? 'circle-dot' : 'circle'} s={18} color={key === 'completed' ? 'var(--positive)' : key === 'current' ? 'var(--accent-bright)' : 'var(--text-faint)'} />
                  <span style={{ flex: 1, fontFamily: 'var(--font-sans)', fontSize: 14.5, fontWeight: 500, color: 'var(--text-strong)' }}>{t.task}</span>
                  <span style={{ fontFamily: 'var(--font-sans)', fontSize: 13, color: 'var(--text-muted)' }}>{fmtDate(t.date)}</span>
                </div>
                <window.PNotesBox coll="timeline" item={t} me={{ name: acc.name, role: 'client' }} clientId={clientId} link="timeline" what={'“' + t.task + '” on the project timeline'} onChange={bump} placeholder="Leave a comment on this step…" />
              </div>
            ))}
          </div>
        );
      })}
      {!tl.length && <Empty icon="git-commit" text="Timeline coming soon" />}
    </SectionWrap>
  );
}

/* ---------------- Files ---------------- */
function ClientFiles({ clientId, client, acc, bump }) {
  const { Button } = window.ManderaDesignSystem_8327ce;
  const files = window.MData.forClient('files', clientId);
  const fileRef = React.useRef(null);
  const [busy, setBusy] = React.useState(false);
  const kindIcon = { pdf: 'file-text', doc: 'file-text', zip: 'file-archive', img: 'image', video: 'file-video' };
  const onUpload = async (e) => {
    const fl = e.target.files && e.target.files[0]; if (!fl) return;
    e.target.value = '';
    const ext = (fl.name.split('.').pop() || '').toLowerCase();
    const kind = ['pdf'].includes(ext) ? 'pdf' : ['doc', 'docx', 'txt'].includes(ext) ? 'doc' : ['zip', 'rar'].includes(ext) ? 'zip' : ['png', 'jpg', 'jpeg', 'gif', 'webp'].includes(ext) ? 'img' : ['mp4', 'mov', 'webm'].includes(ext) ? 'video' : 'doc';
    setBusy(true);
    const up = await window.mUploadFile(fl);
    const dataUrl = up ? null : await window.mReadSmall(fl, 2.5);
    setBusy(false);
    if (!up && !dataUrl) { alert('Could not upload “' + fl.name + '” — the server is unreachable right now and the file is too large to keep locally. Please try again.'); return; }
    window.MData.add('files', { clientId, name: fl.name, kind, size: (fl.size / 1048576).toFixed(1) + ' MB', by: acc.name, date: new Date().toISOString().slice(0, 10), url: up ? up.url : null, dataUrl });
    window.MData.notify({ audience: 'team', type: 'file', text: (client ? client.brand : 'A client') + ' uploaded a file: ' + fl.name, link: 'clients', refId: clientId });
    bump();
  };
  return (
    <SectionWrap>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
        <p style={{ fontFamily: 'var(--font-sans)', fontSize: 14.5, color: 'var(--text-muted)', margin: 0 }}>Shared assets, briefs and deliverables.</p>
        <Button variant="primary" size="sm" disabled={busy} onClick={() => fileRef.current && fileRef.current.click()} iconLeft={<window.PIcon n="upload" s={15} color="var(--ink-950)" />}>{busy ? 'Uploading…' : 'Upload file'}</Button>
        <input ref={fileRef} type="file" style={{ display: 'none' }} onChange={onUpload} />
      </div>
      <div style={{ border: '1px solid var(--border-subtle)', borderRadius: 'var(--radius-lg)', overflow: 'hidden' }}>
        {files.map((fl, i) => (
          <div key={fl.id} style={{ display: 'flex', alignItems: 'center', gap: 14, padding: '14px 18px', background: 'var(--surface-card)', borderTop: i ? '1px solid var(--border-subtle)' : 'none' }}>
            <window.PIcon n={kindIcon[fl.kind] || 'file'} s={20} color="var(--accent-text)" stroke={1.6} />
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontFamily: 'var(--font-sans)', fontSize: 14.5, fontWeight: 600, color: 'var(--text-strong)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{fl.name}</div>
              <div style={{ fontFamily: 'var(--font-sans)', fontSize: 12.5, color: 'var(--text-muted)' }}>{fl.size} · {fl.by} · {fmtDate(fl.date)}</div>
            </div>
            {(fl.url || fl.dataUrl)
              ? <a href={fl.url || fl.dataUrl} download={fl.name} title="Download" style={{ display: 'inline-flex', border: '1px solid var(--border-default)', background: 'var(--surface-raised)', borderRadius: 'var(--radius-sm)', color: 'var(--text-secondary)', padding: 8 }}><window.PIcon n="download" s={16} /></a>
              : <button title="Download" onClick={() => alert('“' + fl.name + '” was added before file storage was connected — ask your Mandera team to re-upload it.')} style={{ appearance: 'none', border: '1px solid var(--border-default)', background: 'var(--surface-raised)', borderRadius: 'var(--radius-sm)', cursor: 'pointer', color: 'var(--text-faint)', display: 'inline-flex', padding: 8 }}>
                  <window.PIcon n="download" s={16} />
                </button>}
          </div>
        ))}
        {!files.length && <Empty icon="folder" text="No files yet" />}
      </div>
    </SectionWrap>
  );
}

/* ---------------- Revisions ---------------- */
function ClientRevisions({ clientId, bump }) {
  const { Button, Input } = window.ManderaDesignSystem_8327ce;
  const revs = window.MData.forClient('revisions', clientId);
  const [title, setTitle] = React.useState('');
  const [detail, setDetail] = React.useState('');
  const submit = (e) => { e.preventDefault(); if (!title.trim()) return; window.MData.add('revisions', { clientId, title: title.trim(), detail: detail.trim(), status: 'open', date: new Date().toISOString().slice(0, 10) }); const cl = window.MData.client(clientId); window.MData.notify({ audience: 'team', type: 'revision', text: (cl ? cl.brand : 'A client') + ' requested a change: ' + title.trim(), link: 'clients', refId: clientId }); setTitle(''); setDetail(''); bump(); };
  return (
    <SectionWrap>
      <div className="mp-grid2" style={{ display: 'grid', gridTemplateColumns: '360px 1fr', gap: 28, alignItems: 'start' }}>
        <form onSubmit={submit} style={{ background: 'var(--surface-card)', border: '1px solid var(--border-subtle)', borderRadius: 'var(--radius-xl)', padding: 24, display: 'flex', flexDirection: 'column', gap: 16 }}>
          <h3 style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 22, color: 'var(--text-strong)', margin: 0 }}>Request a change</h3>
          <Input label="Summary" placeholder="What needs adjusting?" value={title} onChange={(e) => setTitle(e.target.value)} />
          <window.OnbField label="Details">
            <textarea rows={4} value={detail} onChange={(e) => setDetail(e.target.value)} placeholder="Add any specifics…"
              style={{ resize: 'vertical', padding: '12px 14px', background: 'var(--surface-sunken)', border: '1px solid var(--border-default)', borderRadius: 'var(--radius-md)', color: 'var(--text-strong)', fontFamily: 'var(--font-sans)', fontSize: 15, outline: 'none' }} />
          </window.OnbField>
          <Button variant="primary" type="submit" fullWidth iconRight={<window.PIcon n="send" s={16} color="var(--ink-950)" />}>Submit request</Button>
        </form>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
          {revs.map((r) => (
            <div key={r.id} style={{ padding: '16px 18px', background: 'var(--surface-card)', border: '1px solid var(--border-subtle)', borderRadius: 'var(--radius-md)' }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', gap: 12, alignItems: 'flex-start' }}>
                <span style={{ fontFamily: 'var(--font-sans)', fontSize: 15, fontWeight: 600, color: 'var(--text-strong)' }}>{r.title}</span>
                <StatusPill status={r.status} />
              </div>
              {r.detail && <p style={{ fontFamily: 'var(--font-sans)', fontSize: 13.5, color: 'var(--text-muted)', margin: '8px 0 0' }}>{r.detail}</p>}
              <div style={{ fontFamily: 'var(--font-sans)', fontSize: 12, color: 'var(--text-faint)', marginTop: 8 }}>{fmtDate(r.date)}</div>
            </div>
          ))}
          {!revs.length && <Empty icon="git-pull-request" text="No revision requests yet" />}
        </div>
      </div>
    </SectionWrap>
  );
}

/* ---------------- Messages ---------------- */
function ClientMessages({ clientId, acc, bump }) {
  const { Button } = window.ManderaDesignSystem_8327ce;
  const msgs = window.MData.forClient('messages', clientId).slice().sort((a, b) => a.ts - b.ts);
  const [text, setText] = React.useState('');
  const endRef = React.useRef(null);
  React.useEffect(() => { if (endRef.current) endRef.current.scrollTop = endRef.current.scrollHeight; });
  const send = (e) => { e.preventDefault(); if (!text.trim()) return; window.MData.add('messages', { clientId, fromRole: 'client', fromId: acc.id, fromName: (acc.name || 'You').split(' ')[0], text: text.trim(), ts: Date.now(), read: true }); const cl = window.MData.client(clientId); window.MData.notify({ audience: 'team', type: 'message', text: (cl ? cl.brand : 'A client') + ' sent you a message', link: 'messages', refId: clientId }); setText(''); bump(); };
  return (
    <div className="mp-chat" style={{ padding: 32, height: '100%', display: 'flex', flexDirection: 'column' }}>
      <div ref={endRef} style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 16, overflowY: 'auto', paddingBottom: 16 }} className="mandera-scroll">
        {msgs.map((m) => {
          const mine = m.fromId ? m.fromId === acc.id : m.fromRole === 'client';
          const av = !mine ? window.mAvatarOf(m.fromId) : null;
          return (
            <div key={m.id} style={{ display: 'flex', justifyContent: mine ? 'flex-end' : 'flex-start', gap: 10 }}>
              {!mine && <window.Avatar name={m.fromName} src={av} size={34} accent={!av} />}
              <div style={{ maxWidth: 460 }}>
                <div style={{ padding: '12px 16px', borderRadius: 'var(--radius-lg)', fontFamily: 'var(--font-sans)', fontSize: 14.5, lineHeight: 1.5, background: mine ? 'var(--accent)' : 'var(--surface-card)', color: mine ? 'var(--ink-950)' : 'var(--text-body)', border: mine ? 'none' : '1px solid var(--border-subtle)', borderBottomRightRadius: mine ? 4 : 'var(--radius-lg)', borderBottomLeftRadius: mine ? 'var(--radius-lg)' : 4 }}>{m.text}</div>
                <div style={{ fontFamily: 'var(--font-sans)', fontSize: 11, color: 'var(--text-faint)', marginTop: 4, textAlign: mine ? 'right' : 'left' }}>{m.fromName} · {window.mTimeAgo(m.ts)}</div>
              </div>
            </div>
          );
        })}
        {!msgs.length && <Empty icon="message-square" text="No messages yet — say hello" />}
      </div>
      <form onSubmit={send} style={{ display: 'flex', gap: 12, alignItems: 'center', paddingTop: 16, borderTop: '1px solid var(--border-subtle)' }}>
        <input value={text} onChange={(e) => setText(e.target.value)} placeholder="Write a message…" style={{ flex: 1, height: 46, padding: '0 16px', background: 'var(--surface-sunken)', border: '1px solid var(--border-default)', borderRadius: 'var(--radius-md)', color: 'var(--text-strong)', fontFamily: 'var(--font-sans)', fontSize: 15, outline: 'none' }}
          onFocus={(e) => e.target.style.borderColor = 'var(--accent)'} onBlur={(e) => e.target.style.borderColor = 'var(--border-default)'} />
        <Button variant="primary" type="submit" iconRight={<window.PIcon n="send" s={16} color="var(--ink-950)" />}>Send</Button>
      </form>
    </div>
  );
}

/* ---------------- Notifications ---------------- */
function ClientNotifications({ clientId, bump, go }) {
  const { Button } = window.ManderaDesignSystem_8327ce;
  const notes = window.MData.forClient('notifications', clientId).slice().sort((a, b) => b.ts - a.ts);
  const icon = { shoot: 'camera', file: 'file-text', message: 'message-square', schedule: 'calendar-days', invoice: 'receipt', note: 'message-circle', shootchange: 'calendar-clock', approval: 'check-check' };
  const markAll = () => { notes.forEach((n) => window.MData.update('notifications', n.id, { read: true })); bump(); };
  const open = (n) => { window.MData.update('notifications', n.id, { read: true }); if (n.link) go(n.link, n.refId); else bump(); };
  return (
    <SectionWrap>
      <div style={{ display: 'flex', justifyContent: 'flex-end' }}>
        <Button variant="ghost" size="sm" onClick={markAll} iconLeft={<window.PIcon n="check-check" s={15} />}>Mark all read</Button>
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
        {notes.map((n) => (
          <div key={n.id} onClick={() => open(n)} style={{ display: 'flex', gap: 14, alignItems: 'center', padding: '14px 16px', background: n.read ? 'var(--surface-card)' : 'var(--accent-soft)', border: '1px solid', borderColor: n.read ? 'var(--border-subtle)' : 'var(--border-accent)', borderRadius: 'var(--radius-md)', cursor: 'pointer' }}>
            <window.PIcon n={icon[n.type] || 'bell'} s={18} color={n.read ? 'var(--text-muted)' : 'var(--accent-text)'} />
            <span style={{ flex: 1, fontFamily: 'var(--font-sans)', fontSize: 14, color: 'var(--text-body)' }}>{n.text}</span>
            {n.link && <window.PIcon n="chevron-right" s={15} color="var(--text-faint)" />}
            <span style={{ fontFamily: 'var(--font-sans)', fontSize: 12, color: 'var(--text-faint)' }}>{window.mTimeAgo(n.ts)}</span>
          </div>
        ))}
        {!notes.length && <Empty icon="bell" text="You’re all caught up" />}
      </div>
    </SectionWrap>
  );
}

/* ---------------- Invoices ---------------- */
function ClientInvoices({ clientId, client }) {
  const { Badge } = window.ManderaDesignSystem_8327ce;
  const rows = window.MData.forClient('invoices', clientId).slice().sort((a, b) => (b.date || '').localeCompare(a.date || ''));
  return (
    <SectionWrap>
      <p style={{ fontFamily: 'var(--font-sans)', fontSize: 14.5, color: 'var(--text-muted)', margin: 0 }}>Billing history for {client.brand}. Tap an invoice to download.</p>
      <div style={{ border: '1px solid var(--border-subtle)', borderRadius: 'var(--radius-lg)', overflow: 'hidden' }}>
        {rows.map((r, i) => (
          <div key={r.id} style={{ display: 'flex', gap: 14, alignItems: 'center', padding: '15px 18px', background: 'var(--surface-card)', borderTop: i ? '1px solid var(--border-subtle)' : 'none' }}>
            <window.PIcon n="receipt" s={20} color="var(--accent-text)" stroke={1.6} />
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontFamily: 'var(--font-mono)', fontSize: 13.5, color: 'var(--text-strong)' }}>{r.no || r.name}</div>
              <div style={{ fontFamily: 'var(--font-sans)', fontSize: 12.5, color: 'var(--text-muted)' }}>{r.date ? fmtDate(r.date) : ''}{r.amount ? ' · ' + r.amount : ''}</div>
            </div>
            <Badge tone={r.status === 'Paid' ? 'positive' : 'caution'} variant={r.status === 'Paid' ? 'soft' : 'outline'}>{r.status || 'Due'}</Badge>
            {(r.url || r.dataUrl)
              ? <a href={r.url || r.dataUrl} download={r.fileName || ((r.no || 'invoice') + '.pdf')} title="Download" style={{ display: 'inline-flex', color: 'var(--accent-text)', border: '1px solid var(--border-default)', background: 'var(--surface-raised)', borderRadius: 'var(--radius-sm)', padding: 8 }}><window.PIcon n="download" s={16} color="var(--accent-text)" /></a>
              : <span style={{ fontFamily: 'var(--font-sans)', fontSize: 12, color: 'var(--text-faint)' }}>No file</span>}
          </div>
        ))}
        {!rows.length && <Empty icon="receipt" text="No invoices yet" />}
      </div>
    </SectionWrap>
  );
}

/* ---------------- Settings ---------------- */
function ClientSettings({ clientId, client, acc, bump }) {
  const { Button, Input } = window.ManderaDesignSystem_8327ce;
  const [cur, setCur] = React.useState('');
  const [n1, setN1] = React.useState('');
  const [n2, setN2] = React.useState('');
  const [err, setErr] = React.useState('');
  const [ok, setOk] = React.useState(false);
  const avRef = React.useRef(null);
  const onb = window.MData.onboardingOf(clientId);
  const submit = (e) => {
    e.preventDefault(); setOk(false);
    if (n1 !== n2) { setErr('The two new passwords don’t match.'); return; }
    const res = window.MAuth.changePassword(acc.id, cur, n1);
    if (res.error) { setErr(res.error); return; }
    setErr(''); setOk(true); setCur(''); setN1(''); setN2(''); bump();
  };
  return (
    <SectionWrap>
      <div className="mp-grid2" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 24, alignItems: 'start' }}>
        <section style={{ background: 'var(--surface-card)', border: '1px solid var(--border-subtle)', borderRadius: 'var(--radius-xl)', padding: 26, display: 'flex', flexDirection: 'column', gap: 16 }}>
          <h3 style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 22, color: 'var(--text-strong)', margin: 0 }}>Profile</h3>
          <div style={{ display: 'flex', alignItems: 'center', gap: 14, flexWrap: 'wrap' }}>
            <window.Avatar name={acc.name} src={acc.avatar} size={56} accent={!acc.avatar} fontSize={20} />
            <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
              <Button variant="outline" size="sm" onClick={() => avRef.current && avRef.current.click()} iconLeft={<window.PIcon n="camera" s={14} color="var(--accent-text)" />}>{acc.avatar ? 'Change photo' : 'Add your photo'}</Button>
              {acc.avatar && <Button variant="ghost" size="sm" onClick={() => { window.MData.update('accounts', acc.id, { avatar: null }); bump(); }}>Remove</Button>}
            </div>
            <input ref={avRef} type="file" accept="image/*" style={{ display: 'none' }} onChange={(e) => { const f = e.target.files && e.target.files[0]; if (f) window.mReadAvatar(f, (d) => { window.MData.update('accounts', acc.id, { avatar: d }); bump(); }); e.target.value = ''; }} />
          </div>
          <Input label="Contact name" value={acc.name} readOnly />
          <Input label="Brand" value={client.brand} readOnly />
          <Input label="Email" value={acc.email} readOnly />
          <p style={{ fontFamily: 'var(--font-sans)', fontSize: 12.5, color: 'var(--text-faint)', margin: 0 }}>To change your name, brand or email, message your Mandera team.</p>
        </section>
        <section style={{ background: 'var(--surface-card)', border: '1px solid var(--border-subtle)', borderRadius: 'var(--radius-xl)', padding: 26 }}>
          <h3 style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 22, color: 'var(--text-strong)', margin: '0 0 16px' }}>Change password</h3>
          <form onSubmit={submit} style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 7 }}>
              <label style={{ fontFamily: 'var(--font-sans)', fontSize: 13.5, fontWeight: 600, color: 'var(--text-secondary)' }}>Current password</label>
              <window.PwInput value={cur} onChange={(e) => { setCur(e.target.value); setErr(''); }} err={err} />
            </div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 7 }}>
              <label style={{ fontFamily: 'var(--font-sans)', fontSize: 13.5, fontWeight: 600, color: 'var(--text-secondary)' }}>New password</label>
              <window.PwInput value={n1} onChange={(e) => { setN1(e.target.value); setErr(''); }} placeholder="At least 6 characters" err={err} />
            </div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 7 }}>
              <label style={{ fontFamily: 'var(--font-sans)', fontSize: 13.5, fontWeight: 600, color: 'var(--text-secondary)' }}>Confirm new password</label>
              <window.PwInput value={n2} onChange={(e) => { setN2(e.target.value); setErr(''); }} err={err} />
            </div>
            {err && <span style={{ fontFamily: 'var(--font-sans)', fontSize: 12.5, color: 'var(--critical)' }}>{err}</span>}
            {ok && <span style={{ fontFamily: 'var(--font-sans)', fontSize: 12.5, color: 'var(--positive)' }}>Password updated.</span>}
            <Button variant="primary" type="submit" iconRight={<window.PIcon n="check" s={16} color="var(--ink-950)" />}>Update password</Button>
          </form>
        </section>
      </div>
    </SectionWrap>
  );
}

/* ---------------- Client shell ---------------- */
const CLIENT_NAV = [
  { id: 'dashboard', label: 'Dashboard', icon: 'layout-dashboard' },
  { id: 'shoots', label: 'Upcoming Shoots', icon: 'camera' },
  { id: 'calendar', label: 'Content Calendar', icon: 'calendar-days' },
  { id: 'timeline', label: 'Project Timeline', icon: 'git-commit' },
  { id: 'files', label: 'File Center', icon: 'folder' },
  { id: 'revisions', label: 'Revisions', icon: 'git-pull-request' },
  { id: 'messages', label: 'Messages', icon: 'message-square' },
  { id: 'notifications', label: 'Notifications', icon: 'bell' },
  { id: 'invoices', label: 'Invoices', icon: 'receipt' },
  { id: 'settings', label: 'Settings', icon: 'settings' },
];
const CLIENT_TITLES = {
  dashboard: ['Dashboard', 'Your partnership at a glance'],
  shoots: ['Upcoming Shoots', 'Dates, times, locations & notes'],
  calendar: ['Content Calendar', 'Scheduled posts & publishing dates'],
  timeline: ['Project Timeline', 'What’s done, current and next'],
  files: ['File Center', 'Download assets & upload files'],
  revisions: ['Revisions', 'Request changes & track status'],
  messages: ['Messages', 'Talk directly to your Mandera team'],
  notifications: ['Notifications', 'Reminders & updates'],
  invoices: ['Invoices', 'Billing history'],
  settings: ['Settings', 'Profile & password'],
};

function AppShell({ initial }) {
  const [nav, setNav] = React.useState(initial || 'dashboard');
  const [focus, setFocus] = React.useState(null);
  const [menuOpen, setMenuOpen] = React.useState(false);
  const [, setTick] = React.useState(0);
  const bump = () => setTick((t) => t + 1);
  const acc = window.MAuth.current();
  const clientId = acc && acc.clientId;
  const client = clientId ? window.MData.client(clientId) : null;
  if (!acc || !client) return <div style={{ padding: 40, color: 'var(--text-muted)', fontFamily: 'var(--font-sans)' }}>No client session. <a href="#" onClick={(e) => { e.preventDefault(); window.location.reload(); }} style={{ color: 'var(--accent-text)' }}>Reload</a></div>;

  const go = (id, refId) => { setNav(id); setFocus(refId || null); setMenuOpen(false); };
  React.useEffect(() => { window.__mpView = nav; }, [nav]);
  const [title, sub] = CLIENT_TITLES[nav];
  const unread = window.MData.forClient('notifications', clientId).filter((n) => !n.read).length;
  const unreadMsg = window.MData.forClient('messages', clientId).filter((m) => m.fromRole !== 'client' && !m.read).length;
  const nav2 = CLIENT_NAV.map((n) => n.id === 'notifications' && unread ? { ...n, badge: unread } : n.id === 'messages' && unreadMsg ? { ...n, badge: unreadMsg } : n);

  const footer = <window.UserCard name={acc.name} sub={client.brand} src={acc.avatar} onAvatar={(d) => { window.MData.update('accounts', acc.id, { avatar: d }); bump(); }} />;
  const sectionProps = { clientId, client, acc, go, bump, focus };
  return (
    <div className="mp-shell" style={{ display: 'flex', height: '100vh', background: 'var(--bg-canvas)', overflow: 'hidden' }}>
      {menuOpen && <div className="mp-overlay" onClick={() => setMenuOpen(false)} />}
      <window.Sidebar nav={nav2} active={nav} go={go} label="Client Portal" footer={footer} mobileOpen={menuOpen} onSignOut={() => { window.MAuth.signOut(); go('dashboard'); window.__portalGo && window.__portalGo('login'); }} />
      <div style={{ flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0 }}>
        <window.Topbar title={title} sub={sub} user={{ name: acc.name, sub: client.brand, src: acc.avatar }} notifCount={unread} onBell={() => go('notifications')} onMenu={() => setMenuOpen(true)} />
        <main style={{ flex: 1, overflowY: 'auto' }} className="mandera-scroll">
          {nav === 'dashboard' && <ClientDashboard {...sectionProps} />}
          {nav === 'shoots' && <ClientShoots {...sectionProps} />}
          {nav === 'calendar' && <ClientCalendar {...sectionProps} />}
          {nav === 'timeline' && <ClientTimeline {...sectionProps} />}
          {nav === 'files' && <ClientFiles {...sectionProps} />}
          {nav === 'revisions' && <ClientRevisions {...sectionProps} />}
          {nav === 'messages' && <ClientMessages {...sectionProps} />}
          {nav === 'notifications' && <ClientNotifications {...sectionProps} />}
          {nav === 'invoices' && <ClientInvoices {...sectionProps} />}
          {nav === 'settings' && <ClientSettings {...sectionProps} />}
        </main>
      </div>
    </div>
  );
}

Object.assign(window, { AppShell, StatusPill, mFmtDate: fmtDate, Agenda, TodayPanel });
