// Slate — Book-level Dashboard, client directory, per-client workspace shell.

const {
  Icons, fmt, POSITIONS, ACCOUNTS, HOUSEHOLD, HOUSEHOLD_BRIEFING, STATEMENTS, INSIGHTS,
  BOOK_SUMMARY, CLIENT_BOOK: slateClientBook, DASHBOARD_TASKS, MARKET_CALENDAR,
} = window.Slate;
const { HouseholdScreen, InsightsScreen, HoldingDetail } = window.Slate.Screens;
const AltsScreen = window.Slate.AltsStructured.AltsScreen;

const HH_TABS = ['summary', 'holdings', 'diversification', 'accounts', 'cashflow'];

function currentMode() {
  const hash = location.hash.slice(1);
  const slash = hash.indexOf('/');
  if (slash >= 0) {
    const prefix = hash.slice(0, slash);
    if (prefix === 'plan' || prefix === 'meet') return prefix;
  }
  return 'plan';
}

const WORKSPACE_TABS = [
  { id: 'portfolio', label: 'Aggregated portfolio' },
  { id: 'insights', label: 'Insights' },
  { id: 'events', label: 'Upcoming events' },
  { id: 'documents', label: 'Documents' },
  { id: 'status', label: 'Overall status' },
  { id: 'alts', label: 'Alternatives' },
  { id: 'refresh', label: 'Refresh data' },
];

/** Map legacy household/insights paths into clients/:id/… hashes. */
function clientScopedNavigate(clientId, path) {
  const mode = currentMode();
  if (path.startsWith('household/')) {
    location.hash = `${mode}/clients/${clientId}/holding/${path.slice(10)}`;
    return;
  }
  if (path.startsWith('household')) {
    const raw = path.split('?')[1] || 'summary';
    const sub = HH_TABS.includes(raw) ? raw : 'summary';
    location.hash = sub === 'summary' ? `${mode}/clients/${clientId}/portfolio` : `${mode}/clients/${clientId}/portfolio?sub=${sub}`;
    return;
  }
  if (path.startsWith('insights/')) {
    location.hash = `${mode}/clients/${clientId}/insights/${path.slice(9)}`;
    return;
  }
  if (path === 'insights') {
    location.hash = `${mode}/clients/${clientId}/insights`;
    return;
  }
  if (path === 'alts') {
    location.hash = `${mode}/clients/${clientId}/alts`;
    return;
  }
  if (path.startsWith('clients/')) {
    location.hash = `${mode}/${path}`;
    return;
  }
  location.hash = `${mode}/${path}`;
}

function fmtShort(d) {
  return new Date(d).toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
}

function getMergedClientBook() {
  const Store = window.Slate.ClientStore;
  const custom = Store ? Store.list() : [];
  return [...custom, ...slateClientBook];
}

function resolveBookRow(clientId) {
  const Store = window.Slate.ClientStore;
  if (Store) {
    const c = Store.get(clientId);
    if (c) return c;
  }
  return slateClientBook.find(x => x.id === clientId) || null;
}

/** Start global intake (upload → parse → review) attributed to a WM-created household. */
function startClientPortfolioIntake(clientId, displayName) {
  try {
    sessionStorage.setItem('slate-intake-client-id', clientId);
    if (displayName) sessionStorage.setItem('slate-intake-client-name', displayName);
    sessionStorage.removeItem('slate-post-review-hash');
  } catch (e) { /* ignore */ }
  location.hash = 'meet/upload';
}

function clientAum(row) {
  if (row.isCustom) {
    if (!row.portfolioIngested) return 0;
    if (row.displayAum != null) return row.displayAum;
    return POSITIONS.reduce((a, p) => a + p.mv, 0);
  }
  if (row.demoFull) return POSITIONS.reduce((a, p) => a + p.mv, 0);
  return row.aum || 0;
}

function clientAumSplit(row) {
  const total = clientAum(row);
  const frac = row.aumAdvisoryFrac != null ? row.aumAdvisoryFrac : 1;
  return { advisory: total * frac, nonAdvisory: total * (1 - frac) };
}

const DOC_TYPE_OPTS = [
  { value: 'kyc', label: 'KYC' },
  { value: 'kyb', label: 'KYB' },
  { value: 'statement', label: 'Statement' },
];

const ADD_CLIENT_TYPES = ['HNW', 'UHNW', 'FO', 'Family office'];
const ADD_CLIENT_CUSTODIANS = ['Schwab', 'Fidelity', 'Pershing', 'JPM Private', 'BNY', 'Morgan Stanley'];

/** After parse review, land here (demo: full Marchetti workspace). */
const SLATE_POST_REVIEW_HASH_KEY = 'slate-post-review-hash';
const SLATE_INTAKE_CLIENT_NAME_KEY = 'slate-intake-client-name';
const SLATE_DEMO_AFTER_INTAKE_HASH = 'clients/marchetti/portfolio';

function clearSlateIntakeLandingPrefs() {
  try {
    sessionStorage.removeItem(SLATE_POST_REVIEW_HASH_KEY);
    sessionStorage.removeItem(SLATE_INTAKE_CLIENT_NAME_KEY);
  } catch (e) { /* ignore */ }
}

function uid() {
  return 'd' + Math.random().toString(36).slice(2, 11);
}

const ADD_CLIENT_EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

/** @returns {string|null} Error message or null if valid. */
function validateAddClientFields({ firstName, lastName, email, relationshipSince, custodian }) {
  if (!firstName.trim()) return 'First name is required.';
  if (!lastName.trim()) return 'Last name is required.';
  const em = email.trim();
  if (!em) return 'Email is required.';
  if (!ADD_CLIENT_EMAIL_RE.test(em)) return 'Enter a valid email address.';
  if (!relationshipSince.trim()) return 'Relationship since is required.';
  if (!custodian.trim()) return 'Primary custodian is required.';
  return null;
}

/** WM-created client saved locally; open their workspace to import statements. */
function AddClientModal({ open, onClose, onComplete }) {
  const fileInputRef = React.useRef(null);
  const [firstName, setFirstName] = React.useState('');
  const [lastName, setLastName] = React.useState('');
  const [clientType, setClientType] = React.useState('HNW');
  const [relationshipSince, setRelationshipSince] = React.useState(String(new Date().getFullYear()));
  const [email, setEmail] = React.useState('');
  const [custodian, setCustodian] = React.useState('');
  const [estAum, setEstAum] = React.useState('');
  const [notes, setNotes] = React.useState('');
  /** @type {[{ id: string, fileName: string, docType: string }]} */
  const [docs, setDocs] = React.useState([]);
  const [formError, setFormError] = React.useState(null);

  React.useEffect(() => {
    if (!open) return;
    setFirstName('');
    setLastName('');
    setClientType('HNW');
    setRelationshipSince(String(new Date().getFullYear()));
    setEmail('');
    setCustodian('');
    setEstAum('');
    setNotes('');
    setDocs([]);
    setFormError(null);
  }, [open]);

  if (!open) return null;

  const addFiles = (fileList, defaultType = 'statement') => {
    const next = Array.from(fileList || []).map(f => ({ id: uid(), fileName: f.name, docType: defaultType }));
    if (next.length) setDocs(d => [...d, ...next]);
  };

  const removeDoc = id => setDocs(d => d.filter(x => x.id !== id));

  const setDocType = (id, docType) => setDocs(d => d.map(x => (x.id === id ? { ...x, docType } : x)));

  const handleCreate = () => {
    const err = validateAddClientFields({ firstName, lastName, email, relationshipSince, custodian });
    if (err) {
      setFormError(err);
      return;
    }
    setFormError(null);
    const payload = {
      firstName: firstName.trim(),
      lastName: lastName.trim(),
      clientType,
      relationshipSince: relationshipSince.trim(),
      email: email.trim(),
      custodian: custodian.trim(),
      estAum: estAum.trim(),
      notes: notes.trim(),
      docs,
    };
    const Store = window.Slate.ClientStore;
    const rec = Store ? Store.addClient(payload) : null;
    onComplete && onComplete(rec || payload);
    onClose();
    if (rec) location.hash = currentMode() + '/clients/' + rec.id + '/portfolio';
  };

  return (
    <div className="modal-backdrop" role="presentation" onClick={onClose}>
      <div className="modal" role="dialog" aria-modal="true" aria-labelledby="add-client-title" onClick={e => e.stopPropagation()}>
        <div className="modal-head">
          <div>
            <h2 id="add-client-title" className="modal-title serif">Add new client</h2>
            <p className="modal-sub text-2">Fields marked <span style={{ color: 'var(--neg)' }}>*</span> are required. Then import statements from the workspace to build the portfolio and insights.</p>
          </div>
          <button type="button" className="btn ghost icon modal-close" onClick={onClose} aria-label="Close">
            <span dangerouslySetInnerHTML={{ __html: Icons.close }} />
          </button>
        </div>

        <div className="modal-body">
          <div className="modal-grid-2">
            <div>
              <label className="modal-label" htmlFor="add-client-first">First name <span className="text-3" style={{ color: 'var(--neg)' }} aria-hidden="true">*</span></label>
              <input id="add-client-first" className="modal-input" value={firstName} onChange={e => setFirstName(e.target.value)} placeholder="Marco" required autoComplete="given-name"/>
            </div>
            <div>
              <label className="modal-label" htmlFor="add-client-last">Last name <span className="text-3" style={{ color: 'var(--neg)' }} aria-hidden="true">*</span></label>
              <input id="add-client-last" className="modal-input" value={lastName} onChange={e => setLastName(e.target.value)} placeholder="Marchetti" required autoComplete="family-name"/>
            </div>
            <div>
              <label className="modal-label" htmlFor="add-client-type">Client type</label>
              <select id="add-client-type" className="modal-input" value={clientType} onChange={e => setClientType(e.target.value)}>
                {ADD_CLIENT_TYPES.map(t => <option key={t} value={t}>{t}</option>)}
              </select>
            </div>
            <div>
              <label className="modal-label" htmlFor="add-client-since">Relationship since <span className="text-3" style={{ color: 'var(--neg)' }} aria-hidden="true">*</span></label>
              <input id="add-client-since" className="modal-input" value={relationshipSince} onChange={e => setRelationshipSince(e.target.value)} placeholder="2026" required/>
            </div>
          </div>
          <div className="mt-12">
            <label className="modal-label" htmlFor="add-client-email">Email <span className="text-3" style={{ color: 'var(--neg)' }} aria-hidden="true">*</span></label>
            <input id="add-client-email" type="email" className="modal-input" value={email} onChange={e => setEmail(e.target.value)} placeholder="marco@example.com" required autoComplete="email"/>
          </div>
          <div className="modal-grid-2 mt-12">
            <div>
              <label className="modal-label" htmlFor="add-client-custodian">Primary custodian <span className="text-3" style={{ color: 'var(--neg)' }} aria-hidden="true">*</span></label>
              <select id="add-client-custodian" className="modal-input" value={custodian} onChange={e => setCustodian(e.target.value)} required>
                <option value="">Select custodian…</option>
                {ADD_CLIENT_CUSTODIANS.map(c => <option key={c} value={c}>{c}</option>)}
              </select>
            </div>
            <div>
              <label className="modal-label">Estimated AUM</label>
              <input className="modal-input" value={estAum} onChange={e => setEstAum(e.target.value)} placeholder="$50M"/>
            </div>
          </div>
          <div className="mt-12">
            <label className="modal-label">Notes</label>
            <textarea className="modal-input modal-textarea" value={notes} onChange={e => setNotes(e.target.value)} rows={3} placeholder="Investment objectives, risk profile, key concerns…"/>
          </div>

          <div className="mt-20">
            <div className="label" style={{ marginBottom: 8 }}>Upload documents (optional)</div>
            <p className="text-3" style={{ fontSize: 12, marginBottom: 12 }}>You can add more later from the client&apos;s <strong>Documents</strong> or <strong>Refresh data</strong> tabs.</p>
            <div
              className={`modal-dropzone ${docs.length ? 'has-files' : ''}`}
              onDragOver={e => { e.preventDefault(); }}
              onDrop={e => { e.preventDefault(); addFiles(e.dataTransfer.files, 'statement'); }}
            >
              <span dangerouslySetInnerHTML={{ __html: Icons.upload }} style={{ color: 'var(--accent)' }} />
              <div className="serif" style={{ fontSize: 16, fontWeight: 600 }}>Drop PDF statements, Investment Policy Statement, or KYC / KYB here</div>
              <div className="text-3" style={{ fontSize: 12 }}>
                or <button type="button" className="modal-link" onClick={() => fileInputRef.current && fileInputRef.current.click()}>browse</button>
                · set type per file below
              </div>
              <div className="text-3 mt-8" style={{ fontSize: 11 }}>Schwab · Fidelity · Pershing · JPM · BNY · Morgan Stanley</div>
              <input ref={fileInputRef} type="file" multiple accept=".pdf,application/pdf" className="modal-file-input" onChange={e => { addFiles(e.target.files, 'statement'); e.target.value = ''; }}/>
            </div>

            {docs.length > 0 && (
              <div className="modal-doc-list stack stack-2 mt-12">
                {docs.map(d => (
                  <div key={d.id} className="modal-doc-row row gap-10" style={{ alignItems: 'center', flexWrap: 'wrap' }}>
                    <select className="modal-input modal-input-sm" value={d.docType} onChange={e => setDocType(d.id, e.target.value)} aria-label="Document type">
                      {DOC_TYPE_OPTS.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
                    </select>
                    <span className="mono text-3" style={{ fontSize: 12, flex: 1, minWidth: 120 }}>{d.fileName}</span>
                    <button type="button" className="btn ghost sm" onClick={() => removeDoc(d.id)}>Remove</button>
                  </div>
                ))}
              </div>
            )}

            <div className="row gap-10 mt-12" style={{ flexWrap: 'wrap', alignItems: 'center' }}>
              <button type="button" className="btn ghost sm" onClick={() => fileInputRef.current && fileInputRef.current.click()}>
                + Add more PDFs
              </button>
              <span className="text-3" style={{ fontSize: 11 }}>Each file gets a type column — use multiple statements for each custodian PDF.</span>
            </div>
          </div>

          {formError && (
            <div className="card" role="alert" style={{ padding: 12, marginTop: 16, borderColor: 'var(--neg)', background: 'var(--neg-soft)', fontSize: 13 }}>
              {formError}
            </div>
          )}
        </div>

        <div className="modal-actions row gap-10" style={{ justifyContent: 'flex-end', flexWrap: 'wrap' }}>
          <button type="button" className="btn ghost" onClick={onClose}>Cancel</button>
          <button type="button" className="btn primary" onClick={handleCreate}>
            Create client <span dangerouslySetInnerHTML={{ __html: Icons.arrow }} />
          </button>
        </div>
      </div>
    </div>
  );
}

function DashboardScreen({ navigate }) {
  const [addClientOpen, setAddClientOpen] = React.useState(false);
  const [customN, setCustomN] = React.useState(() => (window.Slate.ClientStore ? window.Slate.ClientStore.list().length : 0));

  React.useEffect(() => {
    const Store = window.Slate.ClientStore;
    if (!Store) return undefined;
    const on = () => setCustomN(Store.list().length);
    window.addEventListener(Store.EVENT_CHANGED, on);
    return () => window.removeEventListener(Store.EVENT_CHANGED, on);
  }, []);

  const totalHouseholds = BOOK_SUMMARY.activeClients + customN;

  const greetingHour = new Date().getHours();
  const greet = greetingHour < 12 ? 'Good morning' : greetingHour < 17 ? 'Good afternoon' : 'Good evening';

  return (
    <div className="page">
      <AddClientModal
        open={addClientOpen}
        onClose={() => setAddClientOpen(false)}
        onComplete={() => {}}
      />

      <div className="page-header">
        <div>
          <div className="eyebrow">{new Date().toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' })} · book overview</div>
          <h1 className="serif">{greet}, Christine</h1>
          <p className="text-2 mt-8" style={{ maxWidth: 620 }}>
            {DASHBOARD_TASKS.filter(t => t.tone === 'urgent').length} items need attention this week · {totalHouseholds} households in directory
          </p>
        </div>
        <div className="row gap-10" style={{ flexWrap: 'wrap' }}>
          <button type="button" className="btn primary" onClick={() => setAddClientOpen(true)}>
            + Add client
          </button>
          <button type="button" className="btn ghost" onClick={() => navigate('clients')}>View all clients</button>
        </div>
      </div>

      <div className="kpis">
        <div className="kpi">
          <span className="label">Total AUM</span>
          <span className="v lg">{fmt.money(BOOK_SUMMARY.totalAum, { compact: true })}</span>
          <span className="text-3" style={{ fontSize: 12, color: 'var(--pos)' }}>▲ {fmt.pct(100 * BOOK_SUMMARY.aumYtdPct, { dp: 1 })} YTD</span>
        </div>
        <div className="kpi">
          <span className="label">Active clients</span>
          <span className="v lg">{totalHouseholds}</span>
          <span className="text-3" style={{ fontSize: 12 }}>{BOOK_SUMMARY.foCount} FO · {BOOK_SUMMARY.uhnwCount} UHNW · {BOOK_SUMMARY.hnwCount} HNW</span>
        </div>
        <div className="kpi">
          <span className="label">Portfolios flagged</span>
          <span className="v lg" style={{ color: 'var(--warn)' }}>{BOOK_SUMMARY.flaggedPortfolios}</span>
          <span className="text-3" style={{ fontSize: 12 }}>Investment Policy Statement drift or review due</span>
        </div>
        <div className="kpi">
          <span className="label">Revenue YTD</span>
          <span className="v lg">{fmt.money(BOOK_SUMMARY.revenueYtd, { compact: true })}</span>
          <span className="text-3" style={{ fontSize: 12, color: 'var(--pos)' }}>▲ {fmt.pct(100 * BOOK_SUMMARY.revenueYtdVsLastYear, { dp: 1 })} vs Last Year</span>
        </div>
      </div>

      <div className="section grid-2" style={{ gap: 20, marginTop: 24 }}>
        <div className="card" style={{ padding: 22 }}>
          <div className="row" style={{ justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 12 }}>
            <h2 className="serif" style={{ margin: 0 }}>This week</h2>
            <span className="text-3" style={{ fontSize: 11 }}>Deadline</span>
          </div>
          <div className="stack">
            {DASHBOARD_TASKS.map(t => (
              <div key={t.id} className="row" style={{ justifyContent: 'space-between', alignItems: 'center', minHeight: 44, borderBottom: '1px dashed var(--rule-2)' }}>
                <span style={{ fontSize: 13, flex: 1, minWidth: 0, overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis', marginRight: 10 }}>{t.label}</span>
                <span className={`tag ${t.tone === 'urgent' ? 'neg' : t.tone === 'warn' ? 'warn' : ''}`}>{t.when}</span>
              </div>
            ))}
          </div>
        </div>
        <div className="card" style={{ padding: 22 }}>
          <div className="row" style={{ justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 12 }}>
            <h2 className="serif" style={{ margin: 0 }}>Market calendar</h2>
            <span className="text-3" style={{ fontSize: 11 }}>Issuance</span>
          </div>
          <div className="stack">
            {MARKET_CALENDAR.map(m => (
              <div key={m.id} className="row gap-12" style={{ alignItems: 'center', minHeight: 44, borderBottom: '1px dashed var(--rule-2)', fontSize: 13 }}>
                <span className="mono" style={{ fontSize: 11, width: 48, color: 'var(--ink-3)' }}>{m.ticker}</span>
                <span style={{ flex: 1, minWidth: 0 }}>{m.label}</span>
                <span className="text-3 mono" style={{ fontSize: 11 }}>{fmtShort(m.date)}</span>
                <span className="num" style={{ fontSize: 12 }}>{fmt.money(m.value, { compact: true })}</span>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
}

function ClientListScreen({ navigate, hasBook }) {
  const Store = window.Slate.ClientStore;
  const [addClientOpen, setAddClientOpen] = React.useState(false);
  const [tick, setTick] = React.useState(0);

  React.useEffect(() => {
    if (!Store) return undefined;
    const on = () => setTick(t => t + 1);
    window.addEventListener(Store.EVENT_CHANGED, on);
    return () => window.removeEventListener(Store.EVENT_CHANGED, on);
  }, [Store]);

  const merged = getMergedClientBook();
  const customCount = Store ? Store.list().length : 0;
  const dirHouseholds = BOOK_SUMMARY.activeClients + customCount;

  const canBrowse = merged.length > 0;

  return (
    <div className="page">
      <AddClientModal
        open={addClientOpen}
        onClose={() => setAddClientOpen(false)}
        onComplete={() => {}}
      />
      <div className="page-header">
        <div>
          <div className="eyebrow">Client book · {dirHouseholds} households</div>
          <h1 className="serif">Clients</h1>
        </div>
        <button type="button" className="btn primary" onClick={() => setAddClientOpen(true)}>+ Add client</button>
      </div>
      {!canBrowse ? (
        <p className="text-2">Add a client to list them here alongside the sample book.</p>
      ) : (
        <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
          <table className="tbl client-book-tbl">
            <thead>
              <tr className="col-group-row">
                <th rowSpan={2} className="col-anchor">Client</th>
                <th rowSpan={2} className="col-anchor">Type</th>
                <th colSpan={4} className="col-group-advisory col-group-start-advisory">Advisory</th>
                <th colSpan={3} className="col-group-nonadvisory col-group-start-nonadvisory col-group-end-nonadvisory">Non-Advisory</th>
                <th rowSpan={2} className="col-anchor num">Total AUM</th>
                <th rowSpan={2} className="col-anchor col-review">Review</th>
              </tr>
              <tr>
                <th className="num col-group-advisory col-group-start-advisory">AUM</th>
                <th className="num col-group-advisory">YTD</th>
                <th className="col-group-advisory">Drift</th>
                <th className="col-group-advisory">Fee</th>
                <th className="num col-group-nonadvisory col-group-start-nonadvisory">AUM</th>
                <th className="num col-group-nonadvisory">YTD</th>
                <th className="col-group-nonadvisory col-group-end-nonadvisory">Fee</th>
              </tr>
            </thead>
            <tbody key={'book-' + tick}>
              {merged.map(row => {
                const { advisory, nonAdvisory } = clientAumSplit(row);
                const isPendingCustom = row.isCustom && !row.portfolioIngested;
                const hasData = !isPendingCustom && (row.demoFull || row.portfolioIngested || row.aum > 0);
                const open = () => {
                  if (row.isCustom) navigate(`clients/${row.id}/portfolio`);
                  else if (row.demoFull) navigate(`clients/${row.id}/portfolio`);
                };
                const fmtYtd = pct => pct != null ? fmt.pct(100 * pct, { dp: 1 }) : '—';
                const fmtFee = fee => fee ? fee.map((line, i) => (
                  <React.Fragment key={i}>
                    {i > 0 && <><span style={{ color: 'var(--ink-4)' }}>;</span><br/></>}
                    {line === '—' ? <span className="text-3">{line}</span> : line}
                  </React.Fragment>
                )) : <span className="text-3">—</span>;
                return (
                  <tr key={row.id} onClick={open} style={{ cursor: (row.isCustom || row.demoFull) ? 'pointer' : 'default', opacity: (row.isCustom || row.demoFull) ? 1 : 0.65 }}>
                    <td>
                      <div style={{ fontWeight: 600 }}>{row.name}{isPendingCustom ? <span className="text-3" style={{ fontSize: 11, marginLeft: 8 }}>· Awaiting import</span> : null}</div>
                      <div className="text-3" style={{ fontSize: 11 }}>{row.custodians}</div>
                    </td>
                    <td><span className="tag">{row.familyType}</span></td>
                    <td className="num col-group-advisory col-group-start-advisory">{hasData ? fmt.money(advisory, { compact: true }) : '—'}</td>
                    <td className="num col-group-advisory" style={{ color: 'var(--pos)' }}>{hasData ? fmtYtd(row.ytdAdvisoryPct) : '—'}</td>
                    <td className="text-3 col-group-advisory" style={{ fontSize: 12 }}>{row.driftLabel}</td>
                    <td className="text-3 col-group-advisory" style={{ fontSize: 12, whiteSpace: 'nowrap' }}>{fmtFee(row.feeAdvisory)}</td>
                    <td className="num col-group-nonadvisory col-group-start-nonadvisory">{hasData ? (nonAdvisory > 0 ? fmt.money(nonAdvisory, { compact: true }) : <span className="text-3">—</span>) : '—'}</td>
                    <td className="num col-group-nonadvisory" style={{ color: 'var(--pos)' }}>{hasData && row.ytdNonAdvisoryPct != null ? fmtYtd(row.ytdNonAdvisoryPct) : <span className="text-3">—</span>}</td>
                    <td className="text-3 col-group-nonadvisory col-group-end-nonadvisory" style={{ fontSize: 12, whiteSpace: 'nowrap' }}>{fmtFee(row.feeNonAdvisory)}</td>
                    <td className="num">{hasData ? fmt.money(advisory + nonAdvisory, { compact: true }) : '—'}</td>
                    <td className="col-review"><span className={`tag ${row.reviewTone === 'urgent' ? 'neg' : row.reviewTone === 'warn' ? 'warn' : ''}`}>{row.reviewInDays < 0 ? 'Overdue' : `${row.reviewInDays}d`}</span></td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      )}
      <p className="text-3 mt-16" style={{ fontSize: 12 }}>Sample households are illustrative except Marchetti. Clients you add appear at the top until you import their statements.</p>
    </div>
  );
}

function ClientEventsTab() {
  const b = HOUSEHOLD_BRIEFING;
  return (
    <div className="stack stack-3" style={{ marginTop: 8 }}>
      <div>
        <h3 className="serif" style={{ fontSize: 16, marginBottom: 8 }}>Relationship &amp; compliance</h3>
        <div className="card" style={{ padding: 0 }}>
          {b.upcoming.slice(0, 8).map((e, i) => (
            <div key={i} className="row" style={{ padding: '12px 16px', borderBottom: i < 7 ? '1px solid var(--rule-2)' : 'none', gap: 12 }}>
              <span className="mono text-3" style={{ width: 84 }}>{fmtShort(e.date)}</span>
              <span style={{ flex: 1, fontSize: 13 }}>{e.label}</span>
              <span className="tag">{e.kind}</span>
            </div>
          ))}
        </div>
      </div>
      <div>
        <h3 className="serif" style={{ fontSize: 16, marginBottom: 8 }}>Structured &amp; trading</h3>
        <div className="card" style={{ padding: 0 }}>
          {MARKET_CALENDAR.map((m, i) => (
            <div key={m.id} className="row" style={{ padding: '12px 16px', borderBottom: i < MARKET_CALENDAR.length - 1 ? '1px solid var(--rule-2)' : 'none', gap: 12, fontSize: 13 }}>
              <span className="mono">{m.ticker}</span>
              <span style={{ flex: 1 }}>{m.label}</span>
              <span className="text-3">{fmtShort(m.date)}</span>
              <span className="tag accent">{m.kind}</span>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

function ClientDocumentsTab({ navigate, clientId }) {
  return (
    <div style={{ marginTop: 8 }}>
      <p className="text-2 mb-12" style={{ fontSize: 13 }}>Statements on file for {HOUSEHOLD.name}. Upload more from <strong>Refresh data</strong> or add a client.</p>
      <div className="table-wrap">
        <table className="tbl">
          <thead>
            <tr><th>File</th><th>Custodian</th><th className="num">Pages</th><th className="num">Extracted MV</th></tr>
          </thead>
          <tbody>
            {STATEMENTS.map(s => {
              const acct = ACCOUNTS.find(a => a.id === s.account);
              return (
                <tr key={s.id}>
                  <td className="mono" style={{ fontSize: 12 }}>{s.file}</td>
                  <td>{acct ? acct.custodian : '—'}</td>
                  <td className="num">{s.pages}</td>
                  <td className="num">{fmt.money(s.value, { compact: true })}</td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
      <button type="button" className="btn ghost mt-16" onClick={() => navigate(`clients/${clientId}/refresh`)}>Upload additional statements →</button>
    </div>
  );
}

function ClientStatusTab({ navigate, clientId }) {
  const b = HOUSEHOLD_BRIEFING;
  const nw = POSITIONS.reduce((a, p) => a + p.mv, 0);
  const flagged = POSITIONS.filter(p => p.flag).length;
  return (
    <div className="grid-2" style={{ gap: 16, marginTop: 8 }}>
      <div className="card" style={{ padding: 20 }}>
        <div className="label">Relationship snapshot</div>
        <div className="serif" style={{ fontSize: 20, fontWeight: 600, marginTop: 6 }}>{HOUSEHOLD.name}</div>
        <p className="text-2 mt-8" style={{ fontSize: 13, lineHeight: 1.55 }}>{HOUSEHOLD.segment} · since {HOUSEHOLD.founded} · RM {HOUSEHOLD.rm}</p>
        <hr className="divider dashed" style={{ margin: '14px 0' }}/>
        <div className="label">Next meeting</div>
        <div style={{ fontSize: 14, marginTop: 4 }}>{b.nextMeeting.type} · {fmtShort(b.nextMeeting.date)}</div>
      </div>
      <div className="card" style={{ padding: 20 }}>
        <div className="label">Data health</div>
        <div className="kpis" style={{ marginTop: 12, gridTemplateColumns: '1fr 1fr' }}>
          <div className="kpi"><span className="label">Net worth (parsed)</span><span className="v lg">{fmt.money(nw, { compact: true })}</span></div>
          <div className="kpi"><span className="label">Last ingest</span><span className="v lg" style={{ fontSize: 16 }}>6 May 2026</span></div>
          <div className="kpi"><span className="label">Parser confidence</span><span className="v lg" style={{ color: 'var(--pos)', fontSize: 18 }}>94%</span></div>
          <div className="kpi"><span className="label">Flagged positions</span><span className="v lg" style={{ color: flagged ? 'var(--warn)' : 'var(--pos)' }}>{flagged}</span></div>
        </div>
        <button type="button" className="btn sm mt-16" onClick={() => navigate(`clients/${clientId}/insights`)}>View insights</button>
      </div>
    </div>
  );
}

function ClientRefreshTab({ navigate, clientId, clientName }) {
  const [mode, setMode] = React.useState('merge');
  const onUploadClick = () => {
    const Store = window.Slate.ClientStore;
    if (Store && Store.get(clientId)) {
      startClientPortfolioIntake(clientId, clientName);
    } else {
      clearSlateIntakeLandingPrefs();
      navigate('upload');
    }
  };
  return (
    <div className="card client-refresh-card" style={{ padding: 24, marginTop: 8 }}>
      <h3 className="serif">Refresh from statements</h3>
      <p className="client-refresh-lede">
        Upload new PDFs to extend positions and tax lots, or run a clean refresh (rebuild from scratch — demo only).
      </p>
      <div className="client-refresh-options">
        <label className="client-refresh-option">
          <input type="radio" name="ingest" checked={mode === 'merge'} onChange={() => setMode('merge')} />
          <span><strong>Merge / append</strong> — add new periods and reconcile with existing holdings.</span>
        </label>
        <label className="client-refresh-option">
          <input type="radio" name="ingest" checked={mode === 'clean'} onChange={() => setMode('clean')} />
          <span><strong>Clean refresh</strong> — replace modeled book from new uploads (confirmation in production).</span>
        </label>
      </div>
      <div className="client-refresh-actions">
        <button type="button" className="btn primary" onClick={onUploadClick}>
          <span dangerouslySetInnerHTML={{ __html: Icons.upload }} /> Upload statements…
        </button>
        <button type="button" className="btn ghost" onClick={() => navigate(`clients/${clientId}/documents`)}>View documents</button>
      </div>
    </div>
  );
}

function ClientWorkspacePending({ clientId, row, navigate, activeTab }) {
  const importNow = () => startClientPortfolioIntake(clientId, row.name);
  const emptyCard = (title, body, showImport = true) => (
    <div className="card" style={{ padding: 28, marginTop: 8 }}>
      <h3 className="serif" style={{ marginTop: 0 }}>{title}</h3>
      <p className="text-2" style={{ fontSize: 13, lineHeight: 1.55 }}>{body}</p>
      {showImport ? (
        <button type="button" className="btn primary mt-16" onClick={importNow}>
          <span dangerouslySetInnerHTML={{ __html: Icons.upload }} /> Import portfolio files
        </button>
      ) : null}
    </div>
  );

  return (
    <React.Fragment>
      {activeTab === 'portfolio' && (
        <div className="stack stack-3" style={{ marginTop: 8 }}>
          {emptyCard(
            'No portfolio on file yet',
            'Upload PDF brokerage statements to parse positions, reconcile custodians, aggregate the household book, and generate insights. This demo uses the same pipeline as the Marchetti book; after you confirm parse review, charts and ideas populate here.',
          )}
        </div>
      )}
      {activeTab === 'insights' && emptyCard(
        'Insights after first import',
        'Slate builds concentration, tax, cash-drag, and Investment Policy Statement drift insights once statements are parsed and the book is aggregated.',
      )}
      {activeTab === 'events' && emptyCard(
        'Upcoming events',
        'Relationship and compliance milestones will appear here once the household has an active portfolio.',
        false,
      )}
      {activeTab === 'documents' && emptyCard(
        'No statements on file',
        'Import custodian PDFs from the Portfolio tab or via Refresh data. Document inventory grows with each ingest.',
      )}
      {activeTab === 'status' && (
        <div className="grid-2" style={{ gap: 16, marginTop: 8 }}>
          <div className="card" style={{ padding: 20 }}>
            <div className="label">Household</div>
            <div className="serif" style={{ fontSize: 20, fontWeight: 600, marginTop: 6 }}>{row.name}</div>
            <p className="text-2 mt-8" style={{ fontSize: 13 }}>
              {row.familyType}{row.email ? ` · ${row.email}` : ''}{row.relationshipSince ? ` · since ${row.relationshipSince}` : ''}
            </p>
            {row.custodian ? <p className="text-3 mt-8" style={{ fontSize: 12 }}>Primary custodian · {row.custodian}</p> : null}
            {row.notes ? <p className="text-2 mt-12" style={{ fontSize: 12 }}>{row.notes}</p> : null}
          </div>
          <div className="card" style={{ padding: 20 }}>
            <div className="label">Parsed book</div>
            <p className="text-2 mt-8" style={{ fontSize: 13 }}>No ingest yet. Net worth, parser confidence, and review cadence will appear after the first successful import.</p>
            <button type="button" className="btn primary mt-16" onClick={importNow}>Import portfolio files</button>
          </div>
        </div>
      )}
      {activeTab === 'alts' && emptyCard(
        'Alternatives',
        'Private funds and structured products surface here once positions are parsed and classified.',
      )}
      {activeTab === 'refresh' && (
        <ClientRefreshTab navigate={navigate} clientId={clientId} clientName={row.name} />
      )}
    </React.Fragment>
  );
}

function ClientWorkspaceScreen({ clientId, tab, navigate, accountIds }) {
  const row = resolveBookRow(clientId);
  const wrapNav = React.useCallback(p => clientScopedNavigate(clientId, p), [clientId]);
  const portfolioBase = `clients/${clientId}/portfolio`;

  if (!row) {
    return (
      <div className="page">
        <h1 className="serif">Client not found</h1>
        <button type="button" className="btn" onClick={() => navigate('clients')}>Back to clients</button>
      </div>
    );
  }

  const activeTab = WORKSPACE_TABS.some(t => t.id === tab) ? tab : 'portfolio';
  const isCustomPending = row.isCustom && !row.portfolioIngested;

  if (isCustomPending) {
    return (
      <div className="page">
        <div className="page-header">
          <div>
            <div className="eyebrow">New client · {row.familyType}</div>
            <h1 className="serif">{row.name}</h1>
            <p className="text-2 mt-8" style={{ fontSize: 13 }}>No portfolio data yet · primary custodian {row.custodians || '—'}</p>
          </div>
          <button type="button" className="btn ghost" onClick={() => navigate('clients')}>← All clients</button>
        </div>
        <div className="tabs client-workspace-tabs" style={{ flexWrap: 'wrap' }}>
          {WORKSPACE_TABS.map(t => (
            <button type="button" key={t.id} className={activeTab === t.id ? 'active' : ''} onClick={() => navigate(`clients/${clientId}/${t.id}`)}>
              {t.label}
            </button>
          ))}
        </div>
        <ClientWorkspacePending clientId={clientId} row={row} navigate={navigate} activeTab={activeTab} />
      </div>
    );
  }

  if (!row.isCustom && !row.demoFull) {
    return (
      <div className="page">
        <div className="page-header">
          <h1 className="serif">{row.name}</h1>
          <p className="text-2">Illustrative household — open <button type="button" className="btn linkish" style={{ padding: 0, border: 0, background: 'none', color: 'var(--accent)', cursor: 'pointer', textDecoration: 'underline' }} onClick={() => navigate('clients/marchetti/portfolio')}>The Marchetti Family</button> for the full demo.</p>
        </div>
        <button type="button" className="btn" onClick={() => navigate('clients')}>All clients</button>
      </div>
    );
  }

  return (
    <div className="page">
      <div className="page-header">
        <div>
          <div className="eyebrow">Client workspace · {row.familyType}</div>
          <h1 className="serif">{row.name}</h1>
          <p className="text-2 mt-8" style={{ fontSize: 13 }}>{row.acctCount} accounts · {row.custodians}</p>
        </div>
        <button type="button" className="btn ghost" onClick={() => navigate('clients')}>← All clients</button>
      </div>

      <div className="tabs client-workspace-tabs" style={{ flexWrap: 'wrap' }}>
        {WORKSPACE_TABS.map(t => (
          <button type="button" key={t.id} className={activeTab === t.id ? 'active' : ''} onClick={() => navigate(`clients/${clientId}/${t.id}`)}>
            {t.label}
          </button>
        ))}
      </div>

      {activeTab === 'portfolio' && (
        <HouseholdScreen navigate={wrapNav} accountIds={accountIds} portfolioBase={portfolioBase} embedded />
      )}
      {activeTab === 'insights' && (
        <InsightsScreen navigate={wrapNav} accountIds={accountIds} embedded />
      )}
      {activeTab === 'events' && <ClientEventsTab />}
      {activeTab === 'documents' && <ClientDocumentsTab navigate={navigate} clientId={clientId} />}
      {activeTab === 'status' && <ClientStatusTab navigate={navigate} clientId={clientId} />}
      {activeTab === 'alts' && (
        <div className="client-alts-embed">
          <AltsScreen navigate={wrapNav} />
        </div>
      )}
      {activeTab === 'refresh' && <ClientRefreshTab navigate={navigate} clientId={clientId} clientName={row.name} />}
    </div>
  );
}

function parseClientRoute(route) {
  const pathOnly = route.split('?')[0];
  if (pathOnly === 'clients') return { kind: 'list' };
  if (!pathOnly.startsWith('clients/')) return null;
  const segs = pathOnly.split('/').filter(Boolean);
  if (segs.length < 2) return { kind: 'list' };
  const id = segs[1];
  if (segs.length === 2) return { kind: 'workspace', clientId: id, tab: 'portfolio' };
  const third = segs[2];
  if (third === 'holding' && segs[3]) return { kind: 'holding', clientId: id, symbol: decodeURIComponent(segs[3]) };
  if (third === 'insights' && segs[3]) return { kind: 'insightDetail', clientId: id, insightId: segs[3] };
  if (third === 'portfolio') return { kind: 'workspace', clientId: id, tab: 'portfolio' };
  if (WORKSPACE_TABS.some(t => t.id === third)) return { kind: 'workspace', clientId: id, tab: third };
  return { kind: 'workspace', clientId: id, tab: 'portfolio' };
}

window.Slate.Book = {
  DashboardScreen,
  ClientListScreen,
  ClientWorkspaceScreen,
  ClientEventsTab,
  clientScopedNavigate,
  parseClientRoute,
  WORKSPACE_TABS,
  SLATE_POST_REVIEW_HASH_KEY,
  SLATE_DEMO_AFTER_INTAKE_HASH,
  clearSlateIntakeLandingPrefs,
  startClientPortfolioIntake,
  resolveBookRow,
  getMergedClientBook,
};
