/* B2B Scorecard — Booking to Billing
 * Single React app, no external runtime
 */
const { useState, useEffect, useMemo, useRef } = React;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "showPriorCommitments": true,
  "showProtocol": true,
  "compactDeals": false,
  "stageMode": "tabs"
}/*EDITMODE-END*/;

const COLORS = ["GREEN","YELLOW","RED","NA"];
const COLOR_LABELS = { GREEN:"On Track", YELLOW:"Drifting", RED:"Stuck", NA:"Archived" };

// ────────────────────────────────────────────────────────────
// Persistence
// ────────────────────────────────────────────────────────────
const STORAGE_KEY = "b2b-scorecard-2026-04-27-v2";
function usePersistentState(initial){
  const [state, setState] = useState(() => {
    try {
      const saved = localStorage.getItem(STORAGE_KEY);
      if (saved) return { ...initial, ...JSON.parse(saved) };
    } catch(e){}
    return initial;
  });
  useEffect(() => {
    try {
      localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
    } catch(e){}
  }, [state]);
  return [state, setState];
}

// ────────────────────────────────────────────────────────────
// Tweaks (lightweight inline panel)
// ────────────────────────────────────────────────────────────
function useTweaks(){
  const [tweaks, setTweaks] = useState(TWEAK_DEFAULTS);
  const [open, setOpen] = useState(false);

  useEffect(() => {
    const onMsg = (e) => {
      const d = e.data || {};
      if (d.type === '__activate_edit_mode') setOpen(true);
      else if (d.type === '__deactivate_edit_mode') setOpen(false);
    };
    window.addEventListener('message', onMsg);
    window.parent.postMessage({type:'__edit_mode_available'}, '*');
    return () => window.removeEventListener('message', onMsg);
  }, []);

  const set = (k, v) => {
    setTweaks(prev => {
      const next = { ...prev, [k]: v };
      window.parent.postMessage({type:'__edit_mode_set_keys', edits:{[k]:v}}, '*');
      return next;
    });
  };

  const close = () => {
    setOpen(false);
    window.parent.postMessage({type:'__edit_mode_dismissed'}, '*');
  };

  return { tweaks, set, open, close };
}

function TweaksPanel({ tweaks, set, open, close }){
  if (!open) return null;
  return (
    <div style={{
      position:'fixed', bottom:24, right:24, zIndex:100,
      width: 340, background: 'var(--mdc-white)',
      border:'1px solid var(--mdc-line-strong)',
      borderRadius:'var(--mdc-radius-lg)',
      boxShadow:'var(--mdc-shadow-lg)',
      fontFamily:'var(--mdc-font-sans)'
    }}>
      <div style={{
        display:'flex', justifyContent:'space-between', alignItems:'center',
        padding:'14px 18px', borderBottom:'1px solid var(--mdc-line)'
      }}>
        <span style={{
          fontFamily:'var(--mdc-font-mono)', fontSize:11, letterSpacing:'.16em',
          textTransform:'uppercase', fontWeight:700, color:'var(--mdc-ink)'
        }}>Tweaks</span>
        <button onClick={close} style={{
          background:'transparent', border:'none', cursor:'pointer',
          color:'var(--mdc-ink-soft)', fontSize:18, lineHeight:1
        }}>×</button>
      </div>
      <div style={{ padding: 18, display:'flex', flexDirection:'column', gap:14 }}>
        <Toggle label="Show prior commitments" value={tweaks.showPriorCommitments} onChange={v=>set('showPriorCommitments', v)} />
        <Toggle label="Show protocol legend" value={tweaks.showProtocol} onChange={v=>set('showProtocol', v)} />
        <Toggle label="Compact deal cards" value={tweaks.compactDeals} onChange={v=>set('compactDeals', v)} />
        <Radio label="Stage navigation"
          value={tweaks.stageMode}
          options={[{v:'tabs',l:'Tabs (one stage per view)'},{v:'all',l:'Single scrolling list'}]}
          onChange={v=>set('stageMode', v)} />
        <button onClick={() => {
          if (confirm('Reset all meeting state? This clears commitments, IDS, color calls.')) {
            localStorage.removeItem(STORAGE_KEY);
            location.reload();
          }
        }} style={{
          marginTop:6, padding:'10px 14px', background:'var(--b2b-red)',
          color:'white', border:'none', borderRadius:'var(--mdc-radius-sm)',
          fontFamily:'var(--mdc-font-mono)', fontSize:11, letterSpacing:'.14em',
          textTransform:'uppercase', fontWeight:600, cursor:'pointer'
        }}>Reset Meeting State</button>
      </div>
    </div>
  );
}

function Toggle({ label, value, onChange }){
  return (
    <label style={{ display:'flex', justifyContent:'space-between', alignItems:'center', cursor:'pointer', fontSize:14 }}>
      <span style={{ color:'var(--mdc-ink)' }}>{label}</span>
      <button onClick={() => onChange(!value)} style={{
        width:40, height:22, borderRadius:11, border:'none', cursor:'pointer',
        background: value ? 'var(--mdc-blue)' : 'var(--mdc-line-strong)',
        position:'relative', transition:'background .15s'
      }}>
        <span style={{
          position:'absolute', top:2, left: value ? 20 : 2, width:18, height:18,
          background:'white', borderRadius:'50%', transition:'left .15s',
          boxShadow:'0 1px 2px rgba(0,0,0,.2)'
        }}/>
      </button>
    </label>
  );
}

function Radio({ label, value, options, onChange }){
  return (
    <div style={{ display:'flex', flexDirection:'column', gap:8 }}>
      <span style={{ fontSize:14, color:'var(--mdc-ink)' }}>{label}</span>
      <div style={{ display:'flex', gap:6 }}>
        {options.map(o => (
          <button key={o.v} onClick={() => onChange(o.v)} style={{
            flex:1, padding:'8px 10px', fontSize:11, fontFamily:'var(--mdc-font-mono)',
            letterSpacing:'.08em', textTransform:'uppercase', fontWeight:600,
            border:'1px solid', borderColor: value === o.v ? 'var(--mdc-blue)' : 'var(--mdc-line)',
            background: value === o.v ? 'var(--mdc-blue-tint)' : 'var(--mdc-white)',
            color: value === o.v ? 'var(--mdc-blue)' : 'var(--mdc-ink-soft)',
            borderRadius:'var(--mdc-radius-sm)', cursor:'pointer'
          }}>{o.l}</button>
        ))}
      </div>
    </div>
  );
}

// ────────────────────────────────────────────────────────────
// Helpers
// ────────────────────────────────────────────────────────────
function dealsForStage(state, stageId) {
  return state.deals.filter(d => d.stage === stageId);
}
function countsForStage(state, stageId) {
  const ds = dealsForStage(state, stageId);
  return {
    GREEN: ds.filter(d=>d.color==='GREEN').length,
    YELLOW: ds.filter(d=>d.color==='YELLOW').length,
    RED: ds.filter(d=>d.color==='RED').length,
    NA: ds.filter(d=>d.color==='NA').length,
    total: ds.length
  };
}
function fmtCurrency(raw){
  if (raw == null || raw === '') return '';
  const digits = String(raw).replace(/[^0-9]/g, '');
  if (!digits) return '';
  const n = parseInt(digits, 10);
  if (isNaN(n)) return '';
  return '$' + n.toLocaleString('en-US');
}
function totalCounts(state) {
  return {
    GREEN: state.deals.filter(d=>d.color==='GREEN').length,
    YELLOW: state.deals.filter(d=>d.color==='YELLOW').length,
    RED: state.deals.filter(d=>d.color==='RED').length,
    NA: state.deals.filter(d=>d.color==='NA').length,
  };
}

// ────────────────────────────────────────────────────────────
// Topnav + Tabs
// ────────────────────────────────────────────────────────────
function TopNav({ meta, hubspot, asana }){
  const [now, setNow] = useState(new Date());
  useEffect(() => {
    const t = setInterval(() => setNow(new Date()), 30000);
    return () => clearInterval(t);
  }, []);
  return (
    <header className="topnav" data-screen-label="topnav">
      <div className="brand">
        <img src={(window.__resources && window.__resources.brandLogo) || "brand/logo-color.png"} alt="MDC" />
        <div className="brand-meta">
          <div className="t">{meta.title} <span style={{color:'var(--mdc-ink-soft)', fontWeight:400}}>· Scorecard</span></div>
          <div className="s">{meta.team}</div>
        </div>
      </div>
      <div className="actions">
        {hubspot && (
          <button className="conn-pill" data-on={hubspot.connected} title={`Last sync ${hubspot.lastSync} · ${hubspot.dealsPulled} deals`}>
            <span className="conn-dot"/>
            <span className="conn-name">HubSpot</span>
            <span className="conn-meta">forecast=<b>{hubspot.forecastCategory}</b></span>
          </button>
        )}
        {asana && (
          <button className="conn-pill" data-on={asana.connected} title={`Project: ${asana.project} · Last post ${asana.lastPost}`}>
            <span className="conn-dot"/>
            <span className="conn-name">Asana</span>
            <span className="conn-meta">{asana.project.split(' · ').pop()}</span>
          </button>
        )}
        <span className="clock">{meta.dateISO} · WK17</span>
        <button className="btn btn-secondary" onClick={()=>window.print()}>Export</button>
        <button className="btn btn-primary" onClick={()=>{
          if (confirm('Mark meeting complete and snapshot state?')) {
            const blob = new Blob([JSON.stringify(JSON.parse(localStorage.getItem(STORAGE_KEY)||'{}'), null, 2)], {type:'application/json'});
            const a = document.createElement('a');
            a.href = URL.createObjectURL(blob);
            a.download = `b2b-${meta.dateISO}.json`;
            a.click();
          }
        }}>End Meeting</button>
      </div>
    </header>
  );
}

function TabRail({ tabs, current, onSelect, state }){
  // Top rail shows only non-stage tabs. Stage navigation lives in the
  // PipelineStrip cards which are richer (counts, owner, subtitle).
  const railTabs = tabs.filter(t => t.kind !== 'stage');
  return (
    <nav className="rail" role="tablist">
      {railTabs.map((t, i) => {
        return (
          <div key={t.id}
            className="tab"
            role="tab"
            aria-current={current === t.id}
            onClick={() => onSelect(t.id)}
            data-screen-label={`tab-${t.id}`}>
            <span className="tab-idx">{String(i).padStart(2,'0')}</span>
            <span className="tab-name">{t.name}</span>
          </div>
        );
      })}
    </nav>
  );
}

// ────────────────────────────────────────────────────────────
// Views
// ────────────────────────────────────────────────────────────
function CoverView({ state }){
  return (
    <div className="view cover" data-screen-label="00-cover">
      <div className="cover-inner">
        <p className="eyebrow">
          <span className="idx">00</span>{state.meta.team}<span className="sep">·</span>{state.meta.dateISO}
        </p>
        <h1>Booking<br/><b>to Billing.</b></h1>
        <div className="arrow-flow">
          <span className="step first">1 · Sold</span>
          <span className="ar">→</span>
          <span className="step">2 · In Delivery</span>
          <span className="ar">→</span>
          <span className="step">3 · Delivered / RFS</span>
          <span className="ar">→</span>
          <span className="step last">4 · Billing</span>
        </div>
        <div className="date-row">
          <div><span>Date</span><br/><b>{state.meta.date}</b></div>
          <div><span>Cadence</span><br/><b>Weekly · 60 min</b></div>
          <div><span>Owner</span><br/><b>Juan Pamanes</b></div>
        </div>
      </div>
    </div>
  );
}

function PurposeView({ state, tweaks }){
  return (
    <div className="view purpose" data-screen-label="01-purpose">
      <p className="eyebrow"><span className="idx">01</span>Purpose &amp; Protocol</p>
      <div className="purpose-grid">
        <div>
          <blockquote>
            Every booked deal must reach <b>billed.</b>
          </blockquote>
          <p className="caption">{state.purpose.replace(/^Every booked deal must reach billed\.\s*/,'')}</p>
          <div className="note"><b>Today's verdict.</b> {state.meta.priorMeetingNote}</div>
        </div>
        {tweaks.showProtocol && (
          <div className="protocol">
            <h3>Color-call protocol</h3>
            {state.protocol.map(p => (
              <div key={p.key} className="proto-row">
                <div className={"swatch " + p.key}>{p.label}</div>
                <div>
                  <div className="cue">{p.cue}</div>
                  <div className="action">{p.action}</div>
                </div>
                <div className="budget">{p.budget}</div>
              </div>
            ))}
          </div>
        )}
      </div>
    </div>
  );
}

function PipelineStrip({ state, current, onSelect }){
  return (
    <div className="pipeline-strip">
      {state.stages.map(s => {
        const c = countsForStage(state, s.id);
        const worst = c.RED ? 'RED' : c.YELLOW ? 'YELLOW' : c.GREEN ? 'GREEN' : 'NA';
        return (
          <div key={s.id} className="ps-stage"
               data-active={current === `stage-${s.id}`}
               onClick={() => onSelect(`stage-${s.id}`)}>
            <span className="ps-label">Stage {s.stageNum}</span>
            <span className="ps-name">{s.name}</span>
            <span className="ps-sub">{s.subtitle}</span>
            <div className="ps-count">
              <span className="dot" style={{background:`var(--b2b-${worst.toLowerCase()})`}}/>
              <span>{c.total} deal{c.total!==1?'s':''}</span>
              {c.RED > 0 && <span className="red-pill">{c.RED} red</span>}
            </div>
            <span className="ps-owner"><span>{s.owner}</span></span>
          </div>
        );
      })}
    </div>
  );
}

function OverviewView({ state, current, onSelect, tweaks }){
  const counts = totalCounts(state);
  return (
    <div className="view" data-screen-label="03-overview">
      <p className="eyebrow"><span className="idx">03</span>Review · Pipeline Health</p>
      <PipelineStrip state={state} current={current} onSelect={onSelect} />
      <div className="ov-counts">
        {COLORS.map(c => (
          <div key={c} className={"ov-stat " + c}>
            <span className="bar"/>
            <div className="n">{counts[c]}</div>
            <div className="l">{COLOR_LABELS[c]}</div>
          </div>
        ))}
      </div>
      {tweaks.showPriorCommitments && (
        <div className="ov-carryover-link" onClick={() => onSelect && onSelect('carryover')}>
          <div className="ovcl-icon">↩</div>
          <div className="ovcl-body">
            <div className="ovcl-lbl">JUMP BACK TO CARRY-OVER</div>
            <div className="ovcl-sub">{state.priorCommitments.length} prior commitments from last meeting</div>
          </div>
          <div className="ovcl-cta">Open →</div>
        </div>
      )}
    </div>
  );
}

function FunnelChart({ state }){
  const max = Math.max(...state.stages.map(s => countsForStage(state, s.id).total)) || 1;
  return (
    <div className="funnel">
      <div className="funnel-head">
        <h3>Stage distribution</h3>
        <div className="sub">Deals per stage · color = current call</div>
      </div>
      <div className="funnel-bars">
        {state.stages.map(s => {
          const c = countsForStage(state, s.id);
          const totalRatio = c.total / max;
          const heightPx = Math.max(10, totalRatio * 200);
          return (
            <div key={s.id} className="funnel-col">
              <div className="ftot">{c.total}</div>
              <div className="stack" style={{height: heightPx, flex: 'none'}}>
                {['GREEN','YELLOW','RED'].map(col => (
                  c[col] > 0 && <div key={col} className={"funnel-seg " + col} style={{flex: c[col]}}/>
                ))}
              </div>
              <div className="flbl">{s.name}</div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

function PriorCommitments({ state }){
  return (
    <div>
      <p className="eyebrow" style={{marginTop:12}}>Carry-over · Last meeting's commitments</p>
      <div className="prior-list">
        {state.priorCommitments.map((p,i) => (
          <div key={i} className="prior-row" data-status={p.status}>
            <span className="powner">{p.owner}</span>
            <span className="paction">{p.action}</span>
            <span className="pdue">DUE {p.due}</span>
            <span className="pstatus">{p.status}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

// ────────────────────────────────────────────────────────────
// Carry-over view — its own page. First thing the team reviews:
// "what did we say last time, and did it happen?"
// ────────────────────────────────────────────────────────────
function CarryoverView({ state, setState }){
  const items = state.priorCommitments || [];
  const [groupBy, setGroupBy] = useState('status'); // 'status' | 'owner'

  const completed = items.filter(p => p.status === 'completed').length;
  const missed    = items.filter(p => p.status === 'missed').length;
  const open      = items.filter(p => !p.status || p.status === 'open').length;
  const total     = items.length;

  const updateItem = (i, patch) => setState(prev => ({
    ...prev,
    priorCommitments: prev.priorCommitments.map((p, idx) => idx === i ? { ...p, ...patch } : p)
  }));
  const removeItem = (i) => setState(prev => ({
    ...prev,
    priorCommitments: prev.priorCommitments.filter((_, idx) => idx !== i)
  }));

  const cycleStatus = (i) => {
    const order = ['open','completed','missed'];
    const cur = items[i].status || 'open';
    const next = order[(order.indexOf(cur) + 1) % order.length];
    updateItem(i, { status: next });
  };

  // Roll a missed item into today's Action List, then mark it resolved here.
  const rollIntoActionList = (i) => {
    const it = items[i];
    setState(prev => ({
      ...prev,
      commitments: [...prev.commitments, {
        action: it.action,
        owner: it.owner,
        due: '',
        dealId: it.dealId || '',
        dealName: it.dealName || '',
        customer: it.customer || '',
        done: false,
        asanaPosted: false,
        carriedFrom: 'last meeting'
      }],
      priorCommitments: prev.priorCommitments.map((p, idx) => idx === i ? { ...p, status: 'rolled' } : p)
    }));
  };

  // Group items
  const groups = useMemo(() => {
    if (groupBy === 'status') {
      const buckets = { open: [], missed: [], completed: [], rolled: [] };
      items.forEach((it, _i) => {
        const s = it.status || 'open';
        (buckets[s] || buckets.open).push({ ...it, _i });
      });
      const order = [
        ['open',     'Open · still on the hook'],
        ['missed',   'Missed · what slipped'],
        ['rolled',   'Rolled forward · now on Action List'],
        ['completed','Completed · done last cycle'],
      ];
      return order.map(([k, label]) => [k, label, buckets[k] || []]).filter(([,,arr]) => arr.length > 0);
    }
    if (groupBy === 'owner') {
      const m = new Map();
      items.forEach((it, _i) => {
        const k = (it.owner || 'UNASSIGNED').trim();
        if (!m.has(k)) m.set(k, []);
        m.get(k).push({ ...it, _i });
      });
      const SENTINELS = new Set(['UNASSIGNED']);
      return [...m.entries()].sort((a, b) => {
        if (SENTINELS.has(a[0]) && !SENTINELS.has(b[0])) return 1;
        if (!SENTINELS.has(a[0]) && SENTINELS.has(b[0])) return -1;
        return a[0].localeCompare(b[0]);
      }).map(([k, arr]) => [k, k, arr]);
    }
    return [['all', 'All', items.map((it, _i) => ({ ...it, _i }))]];
  }, [items, groupBy]);

  return (
    <div className="view" data-screen-label="carryover">
      <p className="eyebrow"><span className="idx">02</span>Carry-over · Last meeting's commitments</p>

      <div className="commits-hero">
        <div className="commits-hero-left">
          <h2>What did we <b>say</b>. Did it <b>happen.</b></h2>
          <p className="sub">First thing on the agenda. Walk every prior commitment, mark each one. Missed items roll into today's action list.</p>
        </div>
        <div className="commits-hero-right">
          <div className="commits-bigcount">
            <div className="bigcount-num">{total}</div>
            <div className="bigcount-lbl">PRIOR<br/>COMMITMENTS</div>
          </div>
          <div className="commits-pills">
            <div className="commits-pill"><b>{open}</b><span>OPEN</span></div>
            <div className="commits-pill"><b>{completed}</b><span>DONE</span></div>
            <div className={"commits-pill " + (missed > 0 ? 'closed-pill' : '')}><b>{missed}</b><span>MISSED</span></div>
            <div className="commits-pill"><b>{total}</b><span>TOTAL</span></div>
          </div>
        </div>
      </div>

      {items.length === 0 && (
        <div className="composer-card" style={{padding:48,textAlign:'center',color:'var(--mdc-ink-faint)',fontStyle:'italic'}}>
          No prior commitments — fresh start.
        </div>
      )}

      {items.length > 0 && (
        <>
          <div className="groupby-bar">
            <span className="groupby-lbl">GROUP BY</span>
            {[
              { id:'status', label:'STATUS' },
              { id:'owner',  label:'OWNER'  },
            ].map(opt => (
              <button key={opt.id}
                      className="groupby-btn"
                      data-on={groupBy === opt.id}
                      onClick={() => setGroupBy(opt.id)}>{opt.label}</button>
            ))}
            <span className="groupby-tip">Click status to cycle <b>open → done → missed</b>. Roll missed forward to Action List.</span>
          </div>

          <div className="commit-groups">
            {groups.map(([key, label, list]) => (
              <section key={key} className={"cg cg-status-" + key}>
                <header className="cg-head">
                  <h3>{label}</h3>
                  <span className="cg-count">{list.length} {list.length === 1 ? 'ITEM' : 'ITEMS'}</span>
                </header>
                <div className="cg-rows">
                  {list.map((it, n) => {
                    const status = it.status || 'open';
                    return (
                      <div key={it._i} className={"cg-row carry-row carry-row-" + status}>
                        <button
                          className={"carry-status-btn carry-status-" + status}
                          onClick={() => cycleStatus(it._i)}
                          title="Click to cycle: open → done → missed">
                          {status === 'completed' && '✓'}
                          {status === 'missed' && '✗'}
                          {status === 'open' && '○'}
                          {status === 'rolled' && '↪'}
                        </button>
                        <span className="cg-num">{String(n+1).padStart(2,'0')}</span>
                        <div className="cg-body">
                          <div className="cg-customer-line">
                            {groupBy !== 'owner' && it.owner && (
                              <span className="cg-customer">{it.owner}</span>
                            )}
                            {it.dealName && <span className="cg-dealid">{it.dealName}</span>}
                          </div>
                          <input
                            className="cg-action-edit"
                            defaultValue={it.action}
                            placeholder="(no action set)"
                            onBlur={e => updateItem(it._i, { action: e.target.value })}
                          />
                          <div className="cg-sub">
                            {groupBy !== 'owner' && (
                              <>
                                <span className="cg-meta-lbl">OWNER</span>
                                <input className="cg-owner-edit" defaultValue={it.owner || ''} placeholder="—"
                                  onBlur={e => updateItem(it._i, { owner: e.target.value })}/>
                              </>
                            )}
                          </div>
                        </div>
                        <div className="cg-due-cell">
                          <span className="carry-due">DUE {it.due || '—'}</span>
                        </div>
                        {(status === 'missed' || status === 'open') ? (
                          <button
                            className="carry-roll"
                            onClick={() => rollIntoActionList(it._i)}
                            title="Move to today's Action List">
                            <span className="carry-roll-arrow">↪</span>
                            <span className="carry-roll-lbl">ROLL</span>
                          </button>
                        ) : (
                          <span
                            className={"carry-status-tag carry-status-" + status}>
                            {status === 'completed' && 'DONE'}
                            {status === 'rolled' && 'ROLLED'}
                          </span>
                        )}
                        <button className="cg-rm" onClick={() => removeItem(it._i)} aria-label="Remove">×</button>
                      </div>
                    );
                  })}
                </div>
              </section>
            ))}
          </div>
        </>
      )}
    </div>
  );
}

// ────────────────────────────────────────────────────────────
// Stage view (per-stage drilldown)
// ────────────────────────────────────────────────────────────
function StageView({ state, setState, stage, current, onSelect, tweaks, idsAdd, commitAdd, commitUpdate, commitRemove }){
  const deals = dealsForStage(state, stage.id);
  const counts = countsForStage(state, stage.id);

  // Single open deal at a time. Default to first red, then first yellow, else none.
  const defaultOpen = useMemo(() => {
    const red = deals.find(d => d.color === 'RED');
    if (red) return red.id;
    const yel = deals.find(d => d.color === 'YELLOW');
    if (yel) return yel.id;
    return null;
  }, [stage.id]); // only recompute when switching stages
  const [openDealId, setOpenDealId] = useState(defaultOpen);
  useEffect(() => { setOpenDealId(defaultOpen); }, [defaultOpen]);

  const updateDeal = (id, patch) => {
    setState(prev => ({
      ...prev,
      deals: prev.deals.map(d => d.id === id ? { ...d, ...patch } : d)
    }));
  };

  const addDeal = ({ id, name, client, color, mrc, nrc }) => {
    setState(prev => ({
      ...prev,
      deals: [...prev.deals, {
        id: id || `MDC-${String(2900 + prev.deals.length).padStart(4,'0')}`,
        name: name || 'New deal',
        client: client || '',
        stage: stage.id,
        color: color || 'YELLOW',
        stageOwner: stage.owner,
        mrc: mrc || '', nrc: nrc || '', netRunRate: '',
        dateSold: '', salesToDeliveryHandoff: '',
        handoffAccepted: false, targetRFS: '',
        technicalPresentationReceived: false, facilitiesAcceptance: false,
        handoffAcceptedCSPtoFinance: false,
        nrcHandoffToBilling: '', nrcFinanceAcceptance: false, nrcInvoice: '',
        mrcHandoffToBilling: '', mrcFinanceAcceptance: false,
        mismatchFlag: false, billingStart: '',
        serviceOrderId: '', serviceOrderLocation: '',
        specialDisposition: '',
        blocker: '', nextAction: '', nextBy: '', consequence: ''
      }]
    }));
  };

  return (
    <div className="view" data-screen-label={`stage-${stage.id}`}>
      <p className="eyebrow">
        <span className="idx">{String(state.stages.findIndex(s=>s.id===stage.id)+3).padStart(2,'0')}</span>
        Stage · {stage.name}
      </p>
      <PipelineStrip state={state} current={current} onSelect={onSelect} />
      <div className="stage-head">
        <div>
          <h2><b>Stage {stage.stageNum} · {stage.name}.</b></h2>
          <p className="sub">{stage.subtitle}</p>
        </div>
        <div className="meta">
          <div>OWNER · <b>{stage.owner.toUpperCase()}</b></div>
          <div className="sla">EXIT: {stage.exitCriteria}</div>
          <div style={{marginTop:8}}>
            <b style={{color:'var(--b2b-red)'}}>{counts.RED}</b> red ·{' '}
            <b style={{color:'var(--b2b-yellow)'}}>{counts.YELLOW}</b> yellow ·{' '}
            <b style={{color:'var(--b2b-green)'}}>{counts.GREEN}</b> green
          </div>
        </div>
      </div>
      <div className="deals-grid">
        <AddDealRow stage={stage} onAdd={addDeal}/>
        {deals.length === 0 && (
          <div style={{
            padding:'32px', textAlign:'center', color:'var(--mdc-ink-faint)',
            border:'1px dashed var(--mdc-line-strong)', borderRadius:'var(--mdc-radius-lg)',
            fontStyle:'italic'
          }}>No deals yet — use "+ ADD DEAL" above to drop one in.</div>
        )}
        {deals.map(d => (
          <DealCard key={d.id} deal={d} stage={stage} update={(p)=>updateDeal(d.id,p)}
                    compact={tweaks.compactDeals} idsAdd={idsAdd} commitAdd={commitAdd}
                    commitUpdate={commitUpdate} commitRemove={commitRemove}
                    commitments={state.commitments}
                    expanded={openDealId === d.id}
                    onToggle={() => setOpenDealId(prev => prev === d.id ? null : d.id)}
                    stages={state.stages}/>
        ))}
      </div>
    </div>
  );
}

function AddDealRow({ stage, onAdd }){
  const [open, setOpen] = useState(false);
  const [draft, setDraft] = useState({ id:'', name:'', client:'', color:'YELLOW', mrc:'', nrc:'' });
  const firstRef = useRef(null);

  const reset = () => setDraft({ id:'', name:'', client:'', color:'YELLOW', mrc:'', nrc:'' });
  const submit = () => {
    if (!draft.client.trim()) { firstRef.current && firstRef.current.focus(); return; }
    if (!draft.name.trim())   { firstRef.current && firstRef.current.focus(); return; }
    onAdd({ ...draft, name: draft.name.trim(), client: draft.client.trim(), id: draft.id.trim() });
    reset();
    setTimeout(() => firstRef.current && firstRef.current.focus(), 0);
  };
  const onKey = (e) => { if (e.key === 'Enter') { e.preventDefault(); submit(); } };

  if (!open) {
    return (
      <button className="add-deal-toggle" onClick={() => { setOpen(true); setTimeout(()=>firstRef.current&&firstRef.current.focus(),0); }}>
        <span className="add-deal-plus">＋</span>
        <span className="add-deal-lbl">ADD DEAL TO {stage.name.toUpperCase()}</span>
        <span className="add-deal-hint">— drop a new deal into this stage</span>
      </button>
    );
  }

  return (
    <div className="add-deal-form">
      <div className="adf-head">
        <span className="adf-title">NEW DEAL · {stage.name.toUpperCase()}</span>
        <button className="adf-close" onClick={() => { setOpen(false); reset(); }}>×</button>
      </div>
      <div className="adf-grid">
        <div className="adf-field adf-name">
          <span className="adf-lbl">CLIENT</span>
          <input ref={firstRef} value={draft.client} onKeyDown={onKey}
                 onChange={e => setDraft({ ...draft, client: e.target.value })}
                 placeholder="Acme"/>
        </div>
        <div className="adf-field adf-name">
          <span className="adf-lbl">DEAL NAME</span>
          <input value={draft.name} onKeyDown={onKey}
                 onChange={e => setDraft({ ...draft, name: e.target.value })}
                 placeholder="e.g. Colocation in Laredo"/>
        </div>
        <div className="adf-field">
          <span className="adf-lbl">SO ID</span>
          <input value={draft.id} onKeyDown={onKey}
                 onChange={e => setDraft({ ...draft, id: e.target.value })}
                 placeholder="auto"/>
        </div>
        <div className="adf-field">
          <span className="adf-lbl">MRC</span>
          <input value={draft.mrc} onKeyDown={onKey}
                 onChange={e => setDraft({ ...draft, mrc: e.target.value })}
                 onBlur={e => setDraft(d => ({ ...d, mrc: fmtCurrency(e.target.value) }))}
                 placeholder="$0"/>
        </div>
        <div className="adf-field">
          <span className="adf-lbl">NRC</span>
          <input value={draft.nrc} onKeyDown={onKey}
                 onChange={e => setDraft({ ...draft, nrc: e.target.value })}
                 onBlur={e => setDraft(d => ({ ...d, nrc: fmtCurrency(e.target.value) }))}
                 placeholder="$0"/>
        </div>
        <div className="adf-field">
          <span className="adf-lbl">COLOR</span>
          <div className="adf-colors">
            {['GREEN','YELLOW','RED'].map(c => (
              <button key={c} type="button"
                      className={"adf-color " + c.toLowerCase() + (draft.color===c?' on':'')}
                      onClick={() => setDraft({ ...draft, color: c })}>{c}</button>
            ))}
          </div>
        </div>
      </div>
      <div className="adf-foot">
        <button className="adf-cancel" onClick={() => { setOpen(false); reset(); }}>Cancel</button>
        <button className="adf-submit" onClick={submit}>+ ADD DEAL</button>
      </div>
    </div>
  );
}

function DealCard({ deal, stage, update, compact, idsAdd, commitAdd, commitUpdate, commitRemove, commitments, expanded, onToggle, stages }){
  const [dispoState, setDispoState] = useState(deal.disposition || null);
  const [stageMenuOpen, setStageMenuOpen] = useState(false);

  const setDispo = (k) => {
    const next = dispoState === k ? null : k;
    setDispoState(next);
    update({ disposition: next });
    if (next === 'committed') {
      // Inline tasks for this deal already drive the Action List; only auto-add
      // a fallback commitment if there are zero tasks yet AND a nextAction exists.
      const hasInline = (commitments || []).some(c => c.dealId === deal.id);
      if (!hasInline && deal.nextAction) {
        commitAdd({
          action: deal.nextAction,
          owner: deal.stageOwner,
          due: deal.nextBy || 'TBD',
          dealId: deal.id,
          customer: deal.client,
          dealName: `${deal.id} · ${deal.name}`,
          done: false,
          asanaPosted: false
        });
      }
    } else if (next === 'IDS') {
      idsAdd({
        dealId: deal.id,
        dealName: `${deal.id} · ${deal.name}`,
        question: deal.blocker || '',
        owner: deal.stageOwner,
        due: ''
      });
    }
  };

  const setColor = (c) => update({ color: c });

  return (
    <div className="deal" data-color={deal.color} data-screen-label={`deal-${deal.id}`}>
      <div className="deal-head"
           onClick={(e) => {
             // Toggle expansion when clicking anywhere on the head EXCEPT
             // interactive controls (color buttons + the explicit More/Less toggle).
             if (e.target.closest('button')) return;
             onToggle && onToggle();
           }}
           style={{cursor:'pointer'}}>
        <span className="deal-bar"/>
        <div className="deal-name">
          <div className="deal-title-stack">
            <div className="deal-client-row">
              <span className="deal-client">{deal.client || 'Unnamed customer'}</span>
              {deal.specialConditions && (
                <span className="special-badge" title={deal.specialConditions}>
                  ⚑ Special Conditions
                </span>
              )}
              {deal.mismatchFlag && <span className="mismatch-pill">⚠ Mismatch</span>}
            </div>
            <div className="deal-desc-row">
              <span className="deal-desc">{deal.name}</span>
              <span className="deal-id">{deal.id}</span>
            </div>
          </div>
        </div>
        <div className="deal-money" onClick={(e) => e.stopPropagation()}>
          <div><span className="lbl">MRC</span> <b>{fmtCurrency(deal.mrc) || '—'}</b></div>
          <div><span className="lbl">NRC</span> <b>{fmtCurrency(deal.nrc) || '—'}</b></div>
          <div><span className="lbl">NRR</span> <b>{fmtCurrency(deal.netRunRate) || '—'}</b></div>
        </div>
        <div className="deal-stage-picker" onClick={(e) => e.stopPropagation()}>
          <span className="lbl">Stage</span>
          <button className="dsp-current" onClick={() => setStageMenuOpen(v => !v)} data-open={stageMenuOpen}>
            <span className="dsp-num">{stage.stageNum}</span>
            <span className="dsp-name">{stage.name}</span>
            <span className="dsp-chev">▾</span>
          </button>
          {stageMenuOpen && (
            <>
              <div className="dsp-backdrop" onClick={() => setStageMenuOpen(false)}/>
              <div className="dsp-menu" role="menu">
                <div className="dsp-menu-head">Move to stage</div>
                {stages && stages.map(s => (
                  <button key={s.id}
                          className="dsp-opt"
                          data-active={s.id === deal.stage}
                          disabled={s.id === deal.stage}
                          onClick={() => {
                            update({ stage: s.id });
                            setStageMenuOpen(false);
                          }}>
                    <span className="dsp-opt-num">{s.stageNum}</span>
                    <span className="dsp-opt-name">{s.name}</span>
                    {s.id === deal.stage && <span className="dsp-opt-here">CURRENT</span>}
                  </button>
                ))}
                <div className="dsp-menu-foot">Owner: <b>{stage.owner}</b></div>
              </div>
            </>
          )}
        </div>
        <div className="color-call-inline">
          {COLORS.map(c => (
            <button key={c} className="cci-btn" data-on={deal.color === c} data-k={c}
                    onClick={() => setColor(c)}>{c}</button>
          ))}
        </div>
        <button className="deal-toggle" onClick={() => onToggle && onToggle()}>
          {expanded ? '▴ Less' : '▾ More'}
        </button>
      </div>

      {expanded && (
        <div className="deal-properties">
          {deal.specialConditions && (
            <div className="special-banner">
              <div className="sb-head">
                <span className="sb-flag">⚑</span>
                <span className="sb-title">SPECIAL CONDITIONS</span>
                {deal.specialConditionsRule && (
                  <span className="sb-rule">overrides → <b>{deal.specialConditionsRule.replace(/^Default rule overridden:\s*/,'')}</b></span>
                )}
              </div>
              <div className="sb-body">{deal.specialConditions}</div>
            </div>
          )}

          <NRCStatusBanner deal={deal} update={update}/>

          <WorkflowTimeline deal={deal} sla={(window.__B2B_STATE && window.__B2B_STATE.slaConfig) || {}}/>

          {stage.id === 'billing' && <CloseTheLoopStrip deal={deal}/>}

          <div className="prop-sections">
            <section className="prop-section">
              <h4 className="ps-title"><span className="ps-num">01</span> Identity</h4>
              <div className="ps-grid">
                <Prop label="Client" value={deal.client} fkey="client" update={update} icon="building"/>
                <Prop label="Service Order ID" value={deal.serviceOrderId} fkey="serviceOrderId" update={update} icon="hash"/>
                <Prop label="Location" value={deal.serviceOrderLocation} fkey="serviceOrderLocation" update={update} icon="pin"/>
              </div>
            </section>

            <section className="prop-section">
              <h4 className="ps-title"><span className="ps-num">02</span> Money</h4>
              <div className="ps-grid">
                <Prop label="MRC" value={deal.mrc} fkey="mrc" update={update} kind="currency" icon="cash"/>
                <Prop label="NRC" value={deal.nrc} fkey="nrc" update={update} kind="currency" icon="cash"/>
                <Prop label="NRC received notes" value={deal.nrcReceivedNotes} fkey="nrcReceivedNotes" update={update} wide icon="note"/>
              </div>
            </section>

            <section className="prop-section">
              <h4 className="ps-title"><span className="ps-num">03</span> Sales → Delivery handoff</h4>
              <div className="ps-grid">
                <Prop label="Date Sold" value={deal.dateSold} fkey="dateSold" update={update} kind="date" icon="calendar"/>
                <Prop label="Sales→Delivery Handoff" value={deal.salesToDeliveryHandoff} fkey="salesToDeliveryHandoff" update={update} kind="date" icon="handshake"/>
                <Prop label="Handoff Accepted" value={deal.handoffAccepted} fkey="handoffAccepted" update={update} kind="check" icon="handshake"/>
              </div>
            </section>

            <section className="prop-section ps-nrc">
              <h4 className="ps-title"><span className="ps-num">04</span> NRC billing <span className="ps-tag">starts at handoff</span></h4>
              <div className="ps-grid">
                <Prop label="NRC Handoff to Billing" value={deal.nrcHandoffToBilling} fkey="nrcHandoffToBilling" update={update} kind="date" icon="calendar"/>
                <Prop label="NRC Finance Acceptance" value={deal.nrcFinanceAcceptance} fkey="nrcFinanceAcceptance" update={update} kind="check" icon="cash"/>
                <Prop label="NRC Invoice #" value={deal.nrcInvoice} fkey="nrcInvoice" update={update} icon="receipt"/>
                <Prop label="NRC Ticket Ref" value={deal.nrcTicketRef} fkey="nrcTicketRef" update={update} mono icon="hash" placeholder="(awaits portal NRC category)"/>
              </div>
            </section>

            <section className="prop-section">
              <h4 className="ps-title"><span className="ps-num">05</span> Delivery → RFS</h4>
              <div className="ps-grid">
                <Prop label="Target RFS" value={deal.targetRFS} fkey="targetRFS" update={update} kind="date" highlight icon="play"/>
                <Prop label="RFS Date (actual)" value={deal.rfsDate} fkey="rfsDate" update={update} kind="date" highlight icon="play"/>
                <Prop label="Tech Presentation Recvd" value={deal.technicalPresentationReceived} fkey="technicalPresentationReceived" update={update} kind="check" icon="presentation"/>
                <Prop label="Facilities Acceptance" value={deal.facilitiesAcceptance} fkey="facilitiesAcceptance" update={update} kind="check" icon="facility"/>
              </div>
            </section>

            <section className="prop-section">
              <h4 className="ps-title"><span className="ps-num">06</span> CSP → Finance handoff</h4>
              <div className="ps-grid">
                <Prop label="CSP→Finance Accepted" value={deal.handoffAcceptedCSPtoFinance} fkey="handoffAcceptedCSPtoFinance" update={update} kind="check" icon="flow"/>
                <Prop label="MRC Handoff to Billing" value={deal.mrcHandoffToBilling} fkey="mrcHandoffToBilling" update={update} kind="date" icon="calendar"/>
              </div>
            </section>

            <section className="prop-section">
              <h4 className="ps-title"><span className="ps-num">07</span> MRC billing</h4>
              <div className="ps-grid">
                <Prop label="MRC Finance Acceptance" value={deal.mrcFinanceAcceptance} fkey="mrcFinanceAcceptance" update={update} kind="check" icon="bank"/>
                <Prop label="MRC Invoice #" value={deal.mrcInvoice} fkey="mrcInvoice" update={update} icon="receipt"/>
                <Prop label="Billing Start" value={deal.billingStart} fkey="billingStart" update={update} kind="date" highlight icon="play"/>
              </div>
            </section>

            <section className="prop-section ps-flag">
              <h4 className="ps-title"><span className="ps-num">08</span> Exceptions</h4>
              <div className="ps-grid">
                <Prop label="Mismatch Flag" value={deal.mismatchFlag} fkey="mismatchFlag" update={update} kind="check" warn icon="warn"/>
                <Prop label="Special Conditions" value={deal.specialConditions} fkey="specialConditions" update={update} wide icon="flag" placeholder="e.g. 'MRC starts 90 days after RFS, not on RFS'"/>
              </div>
            </section>
          </div>

          <div className="dispo-zone dispo-zone-tasks">
            <div className="dispo-cell blocker-cell">
              <span className="lbl">
                <span className="lbl-dot" aria-hidden="true">⚑</span>
                Blocker / Why stuck
              </span>
              <textarea className="field blocker"
                defaultValue={deal.blocker}
                onBlur={e => update({ blocker: e.target.value })}
                placeholder="What's keeping this from advancing?"/>
            </div>
            <div className="dispo-cell dispo-cell-tasks">
              <DealTasks deal={deal}
                         commitments={commitments || []}
                         commitAdd={commitAdd}
                         commitUpdate={commitUpdate}
                         commitRemove={commitRemove}/>
            </div>
          </div>
        </div>
      )}

      {(deal.color === 'RED' || deal.color === 'YELLOW') && (
        <div className="disposition-bar">
          <span className="dispo-lbl">Force Disposition</span>
          <div className="dispo-status">
            <button className="dispo-btn" data-k="committed"
                    data-on={dispoState==='committed'}
                    onClick={() => setDispo('committed')}>✓ Committed (→ Action List)</button>
            <button className="dispo-btn" data-k="archive"
                    data-on={dispoState==='archive'}
                    onClick={() => {
                      setDispo('archive');
                      update({ color: 'NA' });
                    }}>✗ Archive (→ Out of pipeline)</button>
          </div>
          <span style={{fontFamily:'var(--mdc-font-mono)', fontSize:11, color:'var(--mdc-ink-faint)'}}>
            {dispoState ? `→ ${dispoState}` : 'pending'}
          </span>
        </div>
      )}
    </div>
  );
}

// ────────────────────────────────────────────────────────────
// DueDatePicker — popover with quick day chips + "later date"
// Used by DealTasks rows and CommitmentsView. Matches the design:
// trigger reads "DUE MON 5/4 ▾", popover header reads "DUE BY NEXT WBR"
// and shows TODAY + next 7 day chips. Stores due as "Mon May 4" so
// existing display elsewhere keeps working.
// ────────────────────────────────────────────────────────────
const DAY_NAMES = ['SUN','MON','TUE','WED','THU','FRI','SAT'];
const MONTH_NAMES = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];

function getReviewToday(){
  // Pull the simulated review date from state seed; fall back to real today.
  try {
    const iso = (window.__B2B_STATE && window.__B2B_STATE.meta && window.__B2B_STATE.meta.dateISO) || '';
    if (iso) {
      const [y,m,d] = iso.split('-').map(Number);
      return new Date(y, m-1, d);
    }
  } catch (e) { /* fall through */ }
  return new Date();
}
function fmtDueLabel(date){
  return `${DAY_NAMES[date.getDay()].charAt(0)}${DAY_NAMES[date.getDay()].slice(1).toLowerCase()} ${MONTH_NAMES[date.getMonth()]} ${date.getDate()}`;
}
function fmtTriggerShort(str){
  // Display value compactly: "Mon Apr 28" -> "MON 4/28"
  if (!str) return '';
  const t = str.trim();
  // Try parse "Mon Apr 28" or "Apr 28" patterns
  const parts = t.replace(/,/g,'').split(/\s+/);
  let day = '', mo = '', dnum = '';
  for (const p of parts) {
    if (DAY_NAMES.some(n => n.toLowerCase().startsWith(p.slice(0,3).toLowerCase()))) day = p.slice(0,3).toUpperCase();
    else if (MONTH_NAMES.some(n => n.toLowerCase() === p.slice(0,3).toLowerCase())) mo = MONTH_NAMES.findIndex(n => n.toLowerCase() === p.slice(0,3).toLowerCase()) + 1;
    else if (/^\d+$/.test(p)) dnum = p;
  }
  if (day && mo && dnum) return `${day} ${mo}/${dnum}`;
  return t.toUpperCase();
}

function DueDatePicker({ value, onChange, compact, align }){
  const [open, setOpen] = useState(false);
  const [showLater, setShowLater] = useState(false);
  const wrapRef = useRef(null);

  useEffect(() => {
    if (!open) return;
    const onDoc = (e) => {
      if (wrapRef.current && !wrapRef.current.contains(e.target)) {
        setOpen(false); setShowLater(false);
      }
    };
    const onEsc = (e) => { if (e.key === 'Escape') { setOpen(false); setShowLater(false); } };
    document.addEventListener('mousedown', onDoc);
    document.addEventListener('keydown', onEsc);
    return () => { document.removeEventListener('mousedown', onDoc); document.removeEventListener('keydown', onEsc); };
  }, [open]);

  const today = useMemo(() => getReviewToday(), []);
  const days = useMemo(() => {
    const out = [];
    for (let i = 0; i < 8; i++) {
      const d = new Date(today);
      d.setDate(today.getDate() + i);
      out.push(d);
    }
    return out;
  }, [today]);

  const valueLabel = (value || '').trim();
  const triggerText = valueLabel ? fmtTriggerShort(valueLabel) : 'SET DATE';

  const pick = (d) => {
    onChange(fmtDueLabel(d));
    setOpen(false); setShowLater(false);
  };
  const pickLater = (isoStr) => {
    if (!isoStr) return;
    const [y,m,da] = isoStr.split('-').map(Number);
    const d = new Date(y, m-1, da);
    onChange(fmtDueLabel(d));
    setOpen(false); setShowLater(false);
  };
  const clear = () => { onChange(''); setOpen(false); setShowLater(false); };

  const sameDay = (a, b) =>
    a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();

  // Match selected to a day in the picker
  const selectedDate = useMemo(() => {
    if (!valueLabel) return null;
    // Try parsing "Mon May 4" or "May 4"
    const parts = valueLabel.replace(/,/g,'').split(/\s+/);
    let mo = -1, dnum = -1;
    for (const p of parts) {
      const i = MONTH_NAMES.findIndex(n => n.toLowerCase() === p.slice(0,3).toLowerCase());
      if (i >= 0) mo = i;
      else if (/^\d+$/.test(p)) dnum = Number(p);
    }
    if (mo < 0 || dnum < 0) return null;
    // Choose year that lands within today..today+365
    let y = today.getFullYear();
    let candidate = new Date(y, mo, dnum);
    if (candidate < new Date(today.getFullYear(), today.getMonth(), today.getDate() - 1)) {
      candidate = new Date(y + 1, mo, dnum);
    }
    return candidate;
  }, [valueLabel, today]);

  return (
    <div className={"ddp-wrap " + (compact?'compact ':'') + (align==='right'?'align-right ':'')} ref={wrapRef}>
      <button type="button"
              className={"ddp-trigger " + (valueLabel?'has-value':'empty')}
              onClick={() => setOpen(o => !o)}>
        <span className="ddp-trigger-text">{triggerText}</span>
        <span className="ddp-caret">▾</span>
      </button>
      {open && (
        <div className="ddp-pop" role="dialog">
          <div className="ddp-head">DUE BY NEXT WBR</div>
          <div className="ddp-grid">
            {days.map((d, i) => {
              const isToday = i === 0;
              const isTmrw = i === 1;
              const top = isToday ? 'TODAY' : isTmrw ? 'TMRW' : DAY_NAMES[d.getDay()];
              const num = d.getDate();
              const sel = selectedDate && sameDay(selectedDate, d);
              return (
                <button key={i} type="button"
                        className={"ddp-day " + (sel ? 'on' : '')}
                        onClick={() => pick(d)}>
                  <span className="ddp-day-top">{top}</span>
                  <span className="ddp-day-num">{num}</span>
                </button>
              );
            })}
          </div>
          <div className="ddp-foot">
            {showLater ? (
              <input type="date" autoFocus className="ddp-later-input"
                     onChange={e => pickLater(e.target.value)}/>
            ) : (
              <button type="button" className="ddp-later" onClick={() => setShowLater(true)}>
                PICK LATER DATE
              </button>
            )}
            <button type="button" className="ddp-clear" onClick={clear}>CLEAR</button>
          </div>
        </div>
      )}
    </div>
  );
}

function DealTasks({ deal, commitments, commitAdd, commitUpdate, commitRemove }){
  // Inline composer scoped to this deal. Each row is a real entry in
  // state.commitments — same store the final Action List reads from. Items
  // are tagged with dealId so they can be filtered/grouped on that page.
  const [draft, setDraft] = useState({ action:'', owner: deal.stageOwner || '', due:'' });
  const actionRef = useRef(null);

  // Map indices from the global commitments list -> tasks for THIS deal.
  const myTasks = useMemo(() => commitments
    .map((c, i) => ({ ...c, _i: i }))
    .filter(c => c.dealId === deal.id), [commitments, deal.id]);

  const add = () => {
    if (!draft.action.trim()) { actionRef.current && actionRef.current.focus(); return; }
    commitAdd && commitAdd({
      action: draft.action.trim(),
      owner: draft.owner.trim() || deal.stageOwner,
      due: draft.due.trim(),
      dealId: deal.id,
      customer: deal.client,
      dealName: `${deal.id} · ${deal.name}`,
      done: false,
      asanaPosted: false
    });
    setDraft({ action:'', owner: draft.owner, due:'' });
    setTimeout(() => actionRef.current && actionRef.current.focus(), 0);
  };
  const onKey = (e) => {
    if (e.key === 'Enter' || ((e.metaKey || e.ctrlKey) && e.key === 'Enter')) {
      e.preventDefault(); add();
    }
  };

  const total = myTasks.length;
  const closed = myTasks.filter(t => t.done).length;

  return (
    <div className="deal-tasks">
      <div className="dt-head">
        <span className="dt-title">ACTIONS COMMITTED ({total})</span>
        <span className="dt-hint">
          {total === 0 ? 'No actions yet — add the bite-sized next steps below.'
                       : `${closed}/${total} done · click ✓ to mark complete`}
        </span>
      </div>

      {myTasks.length > 0 && (
        <div className="dt-list">
          {myTasks.map((t, n) => (
            <div key={t._i} className={"dt-row " + (t.done ? 'done' : '')}>
              <button className="dt-check"
                      onClick={() => commitUpdate(t._i, { done: !t.done })}
                      aria-label="Toggle done">{t.done ? '✓' : ''}</button>
              <div className="dt-body">
                <input className="dt-action" defaultValue={t.action}
                       onBlur={e => commitUpdate(t._i, { action: e.target.value })}
                       placeholder="Action…"/>
                <div className="dt-meta">
                  <input className="dt-owner" defaultValue={t.owner}
                         onBlur={e => commitUpdate(t._i, { owner: e.target.value })}
                         placeholder="OWNER"/>
                  <span className="dt-sep">·</span>
                  <span className="dt-due-lbl">DUE</span>
                  <DueDatePicker value={t.due}
                                 onChange={(v) => commitUpdate(t._i, { due: v })}
                                 compact/>
                </div>
              </div>
              <button className="dt-x" onClick={() => commitRemove(t._i)} aria-label="Remove">×</button>
            </div>
          ))}
        </div>
      )}

      {/* Inline composer */}
      <div className="dt-composer">
        <input ref={actionRef}
               className="dt-action-input"
               value={draft.action}
               onChange={e => setDraft({ ...draft, action: e.target.value })}
               onKeyDown={onKey}
               placeholder="Type action, press Enter…"/>
        <div className="dt-composer-meta">
          <input className="dt-owner-input"
                 value={draft.owner}
                 onChange={e => setDraft({ ...draft, owner: e.target.value })}
                 onKeyDown={onKey}
                 placeholder="OWNER"/>
          <DueDatePicker value={draft.due}
                         onChange={(v) => setDraft(prev => ({ ...prev, due: v }))}
                         compact align="right"/>
          <button className="dt-add" onClick={add}>+ ADD</button>
        </div>
      </div>
    </div>
  );
}

// ────────────────────────────────────────────────────────────
// NRC Status Banner — fixes the "Service Delivery has no
// visibility into Finance NRC billing" problem. Sits at the top
// of every expanded deal card so the SD team sees NRC state
// before they look at anything else.
// ────────────────────────────────────────────────────────────
function NRCStatusBanner({ deal, update }){
  // No NRC charged on this deal → nothing to show.
  const hasNRC = deal.nrc && deal.nrc.replace(/[^0-9.]/g,'').length > 0 && deal.nrc !== '$0.00';
  if (!hasNRC) {
    return (
      <div className="nrc-banner nrc-banner--none">
        <div className="nrc-icon">○</div>
        <div className="nrc-body">
          <div className="nrc-line1">No NRC on this deal</div>
          <div className="nrc-line2">Recurring-only — skip NRC step.</div>
        </div>
      </div>
    );
  }

  // Determine status. Order matters: blocker > invoiced+accepted > invoiced > handoff > pending.
  const handoffDate = deal.nrcHandoffToBilling;
  const invoiced = !!deal.nrcInvoice;
  const accepted = !!deal.nrcFinanceAcceptance;
  const handed = !!handoffDate;
  const ticket = deal.nrcTicketRef;

  let state, line1, line2;
  if (invoiced && accepted) {
    state = 'paid';
    line1 = 'NRC INVOICED & ACCEPTED';
    line2 = `${deal.nrcInvoice} · accepted by Finance`;
  } else if (invoiced && !accepted) {
    state = 'invoiced';
    line1 = 'NRC INVOICED — awaiting Finance acceptance';
    line2 = `${deal.nrcInvoice} · ${handoffDate ? `handed off ${handoffDate}` : 'no handoff date'}`;
  } else if (handed && !invoiced) {
    state = 'pending';
    line1 = 'NRC HANDED OFF — awaiting invoice';
    line2 = `Sent to Finance ${handoffDate}${ticket ? ` · ${ticket}` : ' · no ticket ref'}`;
  } else if (!handed) {
    // The actual visibility problem: SD doesn't know if Finance has been told.
    state = 'blocked';
    line1 = 'NRC NOT YET HANDED TO BILLING';
    line2 = 'Service Delivery: confirm NRC was sent to Finance after Sales→Delivery handoff.';
  } else {
    state = 'pending';
    line1 = 'NRC pending';
    line2 = 'Status unclear';
  }

  // Steps shown as inline progress chain.
  const steps = [
    { key:'handoff',  label:'Handed off',  done: handed,    detail: handoffDate || '—' },
    { key:'invoice',  label:'Invoice cut', done: invoiced,  detail: deal.nrcInvoice || '—' },
    { key:'accepted', label:'Accepted',    done: accepted,  detail: accepted ? '✓' : 'pending' },
  ];

  // Quick action: SD pings Finance.
  const ping = () => {
    if (!confirm('Notify Finance that NRC needs handoff/invoice?')) return;
    // Mark a notify timestamp the user can see; real wiring would post to Asana.
    update({ nrcLastPing: new Date().toLocaleDateString('en-US', { month:'short', day:'numeric' }) });
    alert('Asana ticket created in B2B Scorecard · WK17 → assigned to Farah (Finance).');
  };

  return (
    <div className={"nrc-banner nrc-banner--" + state}>
      <div className="nrc-icon">
        {state === 'paid' && '✓'}
        {state === 'invoiced' && '◐'}
        {state === 'pending' && '◔'}
        {state === 'blocked' && '⚠'}
      </div>
      <div className="nrc-body">
        <div className="nrc-line1">{line1}</div>
        <div className="nrc-line2">{line2}</div>
        <div className="nrc-steps">
          {steps.map((s, i) => (
            <React.Fragment key={s.key}>
              <div className={"nrc-step " + (s.done ? 'done' : 'pending')}>
                <span className="nrc-step-dot">{s.done ? '●' : '○'}</span>
                <span className="nrc-step-lbl">{s.label}</span>
                <span className="nrc-step-val">{s.detail}</span>
              </div>
              {i < steps.length - 1 && <span className="nrc-step-arrow">→</span>}
            </React.Fragment>
          ))}
        </div>
      </div>
      {(state === 'blocked' || state === 'pending') && (
        <button className="nrc-action" onClick={ping}>
          {deal.nrcLastPing ? `Re-ping Finance · last ${deal.nrcLastPing}` : 'Ping Finance'}
        </button>
      )}
    </div>
  );
}

// ────────────────────────────────────────────────────────────
// Workflow timeline — milestone strip with auto date-diffs + SLA flags
// ────────────────────────────────────────────────────────────
function daysBetween(a, b){
  if (!a || !b) return null;
  const ms = (new Date(b) - new Date(a)) / 86400000;
  return Math.round(ms);
}
function WorkflowTimeline({ deal, sla }){
  const steps = [
    { key:'dateSold',              label:'Sold',           date: deal.dateSold,              done: !!deal.dateSold },
    { key:'salesToDeliveryHandoff',label:'Sales→Delivery', date: deal.salesToDeliveryHandoff,done: !!deal.handoffAccepted },
    { key:'nrcHandoffToBilling',   label:'NRC → Billing',  date: deal.nrcHandoffToBilling,   done: !!deal.nrcHandoffToBilling, parallel: true },
    { key:'rfsDate',               label:'RFS',            date: deal.rfsDate,               done: !!deal.rfsDate, target: deal.targetRFS },
    { key:'mrcHandoffToBilling',   label:'MRC → Billing',  date: deal.mrcHandoffToBilling,   done: !!deal.mrcHandoffToBilling },
    { key:'billingStart',          label:'Billing Start',  date: deal.billingStart,          done: !!deal.billingStart },
  ];

  // Compute gaps + SLA flags
  const gaps = [];
  for (let i = 0; i < steps.length - 1; i++) {
    const a = steps[i].date, b = steps[i+1].date;
    const d = daysBetween(a, b);
    let warn = false, slaLabel = null;
    if (d !== null) {
      // Apply SLA rules where defined (sla object keyed by from-to)
      const ruleKey = `${steps[i].key}__${steps[i+1].key}`;
      const max = sla && sla[ruleKey];
      if (max && d > max) { warn = true; slaLabel = `> ${max}d SLA`; }
    }
    gaps.push({ days: d, warn, slaLabel });
  }

  // RFS slip vs target
  const rfsSlip = deal.targetRFS && deal.rfsDate ? daysBetween(deal.targetRFS, deal.rfsDate) : null;

  return (
    <div className="wf-timeline">
      <div className="wf-track">
        {steps.map((s, i) => (
          <React.Fragment key={s.key}>
            <div className={"wf-node " + (s.done ? 'done' : 'pending')}>
              <div className="wf-dot">{s.done ? '●' : '○'}</div>
              <div className="wf-lbl">{s.label}</div>
              <div className="wf-date">{s.date || '—'}</div>
              {s.key === 'rfsDate' && rfsSlip !== null && rfsSlip !== 0 && (
                <div className={"wf-slip " + (rfsSlip > 0 ? 'late' : 'early')}>
                  {rfsSlip > 0 ? `+${rfsSlip}d slip` : `${rfsSlip}d early`}
                </div>
              )}
            </div>
            {i < steps.length - 1 && (
              <div className={"wf-gap " + (gaps[i].warn ? 'warn' : '')}>
                <div className="wf-line"/>
                {gaps[i].days !== null && (
                  <div className="wf-gap-lbl">
                    {gaps[i].days}d
                    {gaps[i].slaLabel && <span className="wf-sla">{gaps[i].slaLabel}</span>}
                  </div>
                )}
              </div>
            )}
          </React.Fragment>
        ))}
      </div>
    </div>
  );
}

// ────────────────────────────────────────────────────────────
// Close-the-loop strip — Stage 4 invoice readiness
// ────────────────────────────────────────────────────────────
function CloseTheLoopStrip({ deal }){
  const items = [
    { label:'NRC Invoice', val: deal.nrcInvoice, accept: deal.nrcFinanceAcceptance },
    { label:'MRC Invoice', val: deal.mrcInvoice, accept: deal.mrcFinanceAcceptance },
    { label:'Billing Start', val: deal.billingStart, accept: !!deal.billingStart },
  ];
  const closed = items.every(i => i.val && i.accept);
  return (
    <div className={"ctl-strip " + (closed ? 'closed' : 'open')}>
      <div className="ctl-title">
        <span className="ctl-icon">{closed ? '✓' : '◔'}</span>
        Close the Loop
        <span className="ctl-sub">{closed ? 'All invoiced & accepted' : 'Outstanding'}</span>
      </div>
      <div className="ctl-items">
        {items.map(i => (
          <div key={i.label} className={"ctl-item " + (i.val && i.accept ? 'ok' : 'pending')}>
            <span className="ctl-check">{i.val && i.accept ? '☑' : '☐'}</span>
            <span className="ctl-lbl">{i.label}</span>
            <span className="ctl-val">{i.val || '—'}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

function Prop({ label, value, fkey, update, mono, kind, highlight, warn, wide, icon, placeholder }){
  // Editable property cell. Checkboxes toggle on click; text/date fields use
  // a contenteditable span so the cell visual matches the read-only state.
  if (kind === 'check') {
    const on = !!value;
    const toggle = () => update && fkey && update({ [fkey]: !on });
    return (
      <div className={"prop " + (wide?'prop-wide':'')}>
        <span className="prop-lbl">
          {icon && <PropIcon name={icon}/>}
          {label}
        </span>
        <button type="button"
                onClick={toggle}
                className={"prop-check editable " + (on ? 'on' : 'off') + (warn && on ? ' warn' : '')}>
          {on ? '✓' : '○'} {on ? 'Yes' : 'No'}
        </button>
      </div>
    );
  }

  if (kind === 'currency') {
    // Autoformat on blur: strip non-digits, render as $X,XXX (no decimals).
    const display = fmtCurrency(value);
    const isEmpty = !display;
    const cls = "prop-val editable mono " + (highlight?'highlight ':'') + (isEmpty?'empty':'');
    const onBlur = (e) => {
      const next = fmtCurrency(e.currentTarget.textContent);
      e.currentTarget.textContent = next;
      if (next !== (value || '') && update && fkey) update({ [fkey]: next });
    };
    const onKey = (e) => {
      if (e.key === 'Enter') { e.preventDefault(); e.currentTarget.blur(); }
      if (e.key === 'Escape') { e.currentTarget.textContent = display; e.currentTarget.blur(); }
    };
    return (
      <div className={"prop " + (wide?'prop-wide':'')}>
        <span className="prop-lbl">
          {icon && <PropIcon name={icon}/>}
          {label}
        </span>
        <span className={cls}
              contentEditable
              suppressContentEditableWarning
              spellCheck={false}
              onBlur={onBlur}
              onKeyDown={onKey}
              data-placeholder={placeholder || "$0"}>{display}</span>
      </div>
    );
  }

  if (kind === 'date') {
    const isEmpty = !value || value === '';
    const pickerRef = useRef(null);
    const [draft, setDraft] = useState(value || '');
    // Keep draft in sync if value changes externally
    useEffect(() => { setDraft(value || ''); }, [value]);

    const isValid = (v) => v === '' || /^\d{4}-\d{2}-\d{2}$/.test(v);

    const commit = (next) => {
      if (next === (value || '')) return;
      if (!isValid(next)) {
        // Revert to last good value silently
        setDraft(value || '');
        return;
      }
      if (update && fkey) update({ [fkey]: next });
    };

    const onTextBlur = (e) => {
      const next = e.currentTarget.value.trim();
      commit(next);
    };
    const onTextKey = (e) => {
      if (e.key === 'Enter') { e.preventDefault(); e.currentTarget.blur(); }
      if (e.key === 'Escape') { setDraft(value || ''); e.currentTarget.blur(); }
    };
    const onPickerChange = (e) => {
      const next = e.target.value;
      setDraft(next);
      commit(next);
    };
    const openPicker = (e) => {
      e.stopPropagation();
      const el = pickerRef.current;
      if (!el) return;
      if (el.showPicker) el.showPicker();
      else el.click();
    };

    return (
      <div className={"prop " + (wide?'prop-wide':'')}>
        <span className="prop-lbl">
          {icon && <PropIcon name={icon}/>}
          {label}
        </span>
        <span className={"prop-val date-input mono " + (highlight?'highlight ':'') + (isEmpty && !draft?'empty':'')}>
          <input
            type="text"
            className="date-text"
            value={draft}
            placeholder="YYYY-MM-DD"
            spellCheck={false}
            onChange={(e) => setDraft(e.target.value)}
            onBlur={onTextBlur}
            onKeyDown={onTextKey}
            inputMode="numeric"
            pattern="\d{4}-\d{2}-\d{2}"
          />
          <input
            ref={pickerRef}
            type="date"
            className="date-hidden-picker"
            value={value || ''}
            onChange={onPickerChange}
            tabIndex={-1}
            aria-hidden="true"
          />
          <button
            type="button"
            className="date-cal-btn"
            onClick={openPicker}
            title="Open calendar"
            aria-label="Open calendar">
            <PropIcon name="calendar"/>
          </button>
        </span>
      </div>
    );
  }

  const isEmpty = !value || value === '';
  const cls = "prop-val editable " +
              (mono?'mono ':'') +
              (highlight?'highlight ':'') +
              (isEmpty?'empty':'');

  const onBlur = (e) => {
    const next = e.currentTarget.textContent.trim();
    if (next !== (value || '') && update && fkey) update({ [fkey]: next });
  };
  const onKey = (e) => {
    if (e.key === 'Enter') { e.preventDefault(); e.currentTarget.blur(); }
    if (e.key === 'Escape') { e.currentTarget.textContent = value || ''; e.currentTarget.blur(); }
  };

  return (
    <div className={"prop " + (wide?'prop-wide':'')}>
      <span className="prop-lbl">
        {icon && <PropIcon name={icon}/>}
        {label}
      </span>
      <span className={cls}
            contentEditable
            suppressContentEditableWarning
            spellCheck={false}
            onBlur={onBlur}
            onKeyDown={onKey}
            data-placeholder={placeholder || "—"}>{value || ''}</span>
    </div>
  );
}

// Compact stroke icons for property labels — outline style, 14px, currentColor.
function PropIcon({ name }){
  const common = { width:14, height:14, viewBox:"0 0 24 24", fill:"none", stroke:"currentColor",
                   strokeWidth:1.8, strokeLinecap:"round", strokeLinejoin:"round",
                   className:"prop-icon" };
  const paths = {
    building: <><path d="M4 21h16"/><path d="M6 21V5a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v16"/><path d="M9 8h.01M15 8h.01M9 12h.01M15 12h.01M9 16h.01M15 16h.01"/></>,
    hash:     <><path d="M4 9h16M4 15h16M10 3 8 21M16 3l-2 18"/></>,
    pin:      <><path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 1 1 16 0Z"/><circle cx="12" cy="10" r="3"/></>,
    calendar: <><rect x="3" y="5" width="18" height="16" rx="2"/><path d="M8 3v4M16 3v4M3 11h18"/></>,
    handshake:<><path d="M8 12 5 9 2 12l4 5 3-2"/><path d="m16 12 3-3 3 3-4 5-3-2"/><path d="m9 15 3 3 3-3"/><path d="M12 18v-6"/></>,
    presentation:<><rect x="3" y="4" width="18" height="12" rx="1"/><path d="m8 20 4-4 4 4M12 16v4"/></>,
    facility: <><path d="M3 21h18"/><path d="M5 21V8l7-5 7 5v13"/><rect x="9" y="13" width="6" height="8"/></>,
    receipt:  <><path d="M6 2h12v20l-3-2-3 2-3-2-3 2z"/><path d="M9 7h6M9 11h6M9 15h4"/></>,
    cash:     <><rect x="2" y="6" width="20" height="12" rx="2"/><circle cx="12" cy="12" r="3"/><path d="M6 10v.01M18 14v.01"/></>,
    bank:     <><path d="M3 21h18M3 10h18M5 6l7-3 7 3M6 10v11M10 10v11M14 10v11M18 10v11"/></>,
    flow:     <><circle cx="5" cy="6" r="2"/><circle cx="19" cy="18" r="2"/><path d="M7 6h7a3 3 0 0 1 3 3v6"/></>,
    flag:     <><path d="M5 3v18"/><path d="M5 4h12l-3 4 3 4H5"/></>,
    warn:     <><path d="m12 3 10 18H2L12 3Z"/><path d="M12 10v5M12 18h.01"/></>,
    note:     <><path d="M5 4h11l3 3v13a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1Z"/><path d="M9 9h6M9 13h6M9 17h4"/></>,
    play:     <><circle cx="12" cy="12" r="9"/><path d="m10 8 6 4-6 4Z"/></>,
  };
  return <svg {...common}>{paths[name] || null}</svg>;
}

// ────────────────────────────────────────────────────────────
// IDS Queue
// ────────────────────────────────────────────────────────────
function IDSView({ state, setState }){
  const queue = state.idsQueue;

  const updateItem = (i, patch) => {
    setState(prev => ({
      ...prev,
      idsQueue: prev.idsQueue.map((q, idx) => idx === i ? { ...q, ...patch } : q)
    }));
  };

  const remove = (i) => {
    setState(prev => ({
      ...prev,
      idsQueue: prev.idsQueue.filter((_, idx) => idx !== i)
    }));
  };

  return (
    <div className="view" data-screen-label="ids">
      <p className="eyebrow"><span className="idx">{String(state.stages.length+3).padStart(2,'0')}</span>IDS · Issues, Discussion, Solve</p>
      <div className="ids-head">
        <div>
          <h2><b>What are we missing.</b></h2>
          <p className="sub">Anything that needs more than 5 minutes goes here. Drilled deeper at the end of the meeting — or moved to async if it doesn't fit.</p>
        </div>
        <div className="count">In queue · <b>{queue.length}</b></div>
      </div>

      {state.missing && state.missing.prompts && (
        <div>
          <p className="eyebrow" style={{fontSize:13, color:'var(--mdc-ink-soft)'}}>Prompts to surface a missing issue</p>
          <ul className="missing-prompts">
            {state.missing.prompts.map((p,i) => <li key={i}>{p}</li>)}
          </ul>
        </div>
      )}

      <p className="eyebrow" style={{fontSize:13, color:'var(--mdc-ink-soft)', marginTop:24}}>Issues queue</p>
      <div className="ids-grid">
        {queue.length === 0 && (
          <div style={{
            gridColumn:'1 / -1', padding:'48px', textAlign:'center',
            color:'var(--mdc-ink-faint)', border:'1px dashed var(--mdc-line-strong)',
            borderRadius:'var(--mdc-radius-lg)', fontStyle:'italic', fontSize:18
          }}>
            No issues in queue. Add via "? Needs IDS" on a deal card, or capture missing issues here.
          </div>
        )}
        {queue.map((q, i) => (
          <div key={i} className="ids-slot">
            <div style={{display:'flex', justifyContent:'space-between', alignItems:'center'}}>
              <span className="num">Item {String(i+1).padStart(2,'0')}</span>
              <button onClick={() => remove(i)} style={{
                background:'transparent', border:'none', cursor:'pointer',
                color:'var(--mdc-ink-faint)', fontFamily:'var(--mdc-font-mono)',
                fontSize:11, letterSpacing:'.1em', textTransform:'uppercase'
              }}>remove</button>
            </div>
            {q.dealName && <div className="deal-ref">{q.dealName}</div>}
            <textarea className="question-input"
              defaultValue={q.question}
              placeholder="What's the issue? What needs to be solved?"
              onBlur={e => updateItem(i, { question: e.target.value })}/>
            <div className="ids-foot">
              <input className="owner" defaultValue={q.owner}
                placeholder="OWNER"
                onBlur={e => updateItem(i, { owner: e.target.value })}/>
              <input defaultValue={q.outcome}
                placeholder="Solve / Decision"
                onBlur={e => updateItem(i, { outcome: e.target.value })}/>
              <input defaultValue={q.due}
                placeholder="By when"
                onBlur={e => updateItem(i, { due: e.target.value })}/>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

// ────────────────────────────────────────────────────────────
// Commitments
// ────────────────────────────────────────────────────────────
function CommitmentsView({ state, setState }){
  const c = state.commitments;
  const [groupBy, setGroupBy] = useState('owner'); // 'owner' | 'deal' | 'due'
  const [draft, setDraft] = useState({ action:'', owner:'', due:'' });
  const actionRef = useRef(null);

  const update = (i, patch) => {
    setState(prev => ({
      ...prev,
      commitments: prev.commitments.map((it, idx) => idx === i ? { ...it, ...patch } : it)
    }));
  };
  const remove = (i) => {
    setState(prev => ({
      ...prev,
      commitments: prev.commitments.filter((_, idx) => idx !== i)
    }));
  };
  const addDraft = () => {
    if (!draft.action.trim()) {
      actionRef.current && actionRef.current.focus();
      return;
    }
    setState(prev => ({
      ...prev,
      commitments: [...prev.commitments, {
        action: draft.action.trim(),
        owner: draft.owner.trim(),
        due: draft.due.trim(),
        dealName: '', done: false, asanaPosted: false
      }]
    }));
    setDraft({ action:'', owner: draft.owner, due:'' }); // keep owner sticky
    setTimeout(() => actionRef.current && actionRef.current.focus(), 0);
  };
  const onComposerKey = (e) => {
    if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') { e.preventDefault(); addDraft(); }
  };

  // Stats — task/owner/open/closed counters
  const total = c.length;
  const closed = c.filter(it => it.done).length;
  const open = total - closed;
  const ownerCount = useMemo(() => {
    const s = new Set();
    c.forEach(it => { const o = (it.owner || '').trim(); if (o) s.add(o); });
    return s.size;
  }, [c]);

  // Grouping
  const groups = useMemo(() => {
    const m = new Map();
    const keyFor = (it) => {
      if (groupBy === 'owner') return (it.owner || '').trim() || 'UNASSIGNED';
      if (groupBy === 'deal')  return it.dealName || 'NO DEAL';
      if (groupBy === 'due')   return (it.due || '').trim() || 'NO DUE DATE';
      return 'OTHER';
    };
    c.forEach((it, idx) => {
      const k = keyFor(it);
      if (!m.has(k)) m.set(k, []);
      m.get(k).push({ ...it, _i: idx });
    });
    const SENTINELS = new Set(['UNASSIGNED','NO DEAL','NO DUE DATE']);
    return [...m.entries()].sort((a, b) => {
      const aS = SENTINELS.has(a[0]), bS = SENTINELS.has(b[0]);
      if (aS && !bS) return 1;
      if (!aS && bS) return -1;
      return a[0].localeCompare(b[0]);
    });
  }, [c, groupBy]);

  return (
    <div className="view" data-screen-label="commitments">
      <p className="eyebrow"><span className="idx">04</span>Action List · Walking Out</p>

      <div className="commits-hero">
        <div className="commits-hero-left">
          <h2>Who owns <b>what</b>, by <b>when.</b></h2>
          <p className="sub">Every action committed during this walk-through. Read aloud, confirm out loud, then lock.</p>
        </div>
        <div className="commits-hero-right">
          <div className="commits-bigcount">
            <div className="bigcount-num">{total}</div>
            <div className="bigcount-lbl">COMMITMENTS<br/>LOGGED</div>
          </div>
          <div className="commits-pills">
            <div className="commits-pill"><b>{total}</b><span>TASKS</span></div>
            <div className="commits-pill"><b>{ownerCount}</b><span>OWNERS</span></div>
            <div className="commits-pill"><b>{open}</b><span>OPEN</span></div>
            <div className={"commits-pill " + (closed > 0 ? 'closed-pill' : '')}><b>{closed}</b><span>CLOSED</span></div>
          </div>
        </div>
      </div>

      {/* Composer card */}
      <div className="composer-card">
        <div className="composer-head">
          <span className="composer-title">ACTIONS COMMITTED ({total})</span>
          <span className="composer-hint">Add each bite-sized action. Click ✓ when done. Posted to Asana on End Meeting.</span>
        </div>

        {/* Inline composer */}
        <div className="composer-input-row">
          <input ref={actionRef}
                 className="composer-action"
                 value={draft.action}
                 onChange={e => setDraft({ ...draft, action: e.target.value })}
                 onKeyDown={onComposerKey}
                 placeholder="Type an action, then press + Add (or Cmd+Enter)."/>
        </div>
        <div className="composer-fields">
          <label className="composer-field">
            <span>Owner</span>
            <input value={draft.owner}
                   onChange={e => setDraft({ ...draft, owner: e.target.value })}
                   onKeyDown={onComposerKey}
                   placeholder="Name"/>
          </label>
          <label className="composer-field">
            <span>Due</span>
            <DueDatePicker value={draft.due}
                           onChange={(v) => setDraft(prev => ({ ...prev, due: v }))}
                           compact/>
          </label>
          <button className="composer-add" onClick={addDraft}>+ ADD ACTION</button>
        </div>
      </div>

      {/* Group-by tabs + summary list */}
      {c.length > 0 && (
        <>
          <div className="groupby-bar">
            <span className="groupby-lbl">GROUP BY</span>
            {[
              { id:'owner', label:'OWNER' },
              { id:'deal',  label:'DEAL'  },
              { id:'due',   label:'DUE DATE' },
            ].map(opt => (
              <button key={opt.id}
                      className="groupby-btn"
                      data-on={groupBy === opt.id}
                      onClick={() => setGroupBy(opt.id)}>{opt.label}</button>
            ))}
            <span className="groupby-tip">Use <b>↑/↓</b> to step or <b>Enter</b> to confirm.</span>
          </div>

          <div className="commit-groups">
            {groups.map(([key, items]) => {
              const isSentinel = ['UNASSIGNED','NO DEAL','NO DUE DATE'].includes(key);
              const display = key === 'UNASSIGNED' ? 'Unassigned'
                            : key === 'NO DEAL' ? 'No deal linked'
                            : key === 'NO DUE DATE' ? 'No due date'
                            : key;
              return (
                <section key={key} className={"cg " + (isSentinel ? 'cg-sentinel' : '')}>
                  <header className="cg-head">
                    <h3>{display}</h3>
                    <span className="cg-count">{items.length} {items.length === 1 ? 'ACTION' : 'ACTIONS'}</span>
                  </header>
                  <div className="cg-rows">
                    {items.map((it, n) => {
                      const dealId = it.dealId || (it.dealName || '').match(/MDC-\d+/)?.[0] || '';
                      const customerName = it.customer
                        || ((it.dealName || '').replace(dealId, '').replace(/[·\-]/g,'').trim())
                        || '';
                      return (
                        <div key={it._i} className={"cg-row " + (it.done ? 'done' : '')}>
                          <button className="cg-check" onClick={() => update(it._i, { done: !it.done })} aria-label="Toggle done">
                            {it.done ? '✓' : ''}
                          </button>
                          <span className="cg-num">{String(n+1).padStart(2,'0')}</span>
                          <div className="cg-body">
                            <div className="cg-customer-line">
                              {groupBy !== 'deal' && customerName && (
                                <span className="cg-customer">{customerName}</span>
                              )}
                              {dealId && <span className="cg-dealid">{dealId}</span>}
                            </div>
                            <input
                              className="cg-action-edit"
                              defaultValue={it.action}
                              placeholder="(no action set)"
                              onBlur={e => update(it._i, { action: e.target.value })}
                            />
                            <div className="cg-sub">
                              {groupBy !== 'owner' && (
                                <>
                                  <span className="cg-meta-lbl">OWNER</span>
                                  <input className="cg-owner-edit" defaultValue={it.owner||''} placeholder="—"
                                    onBlur={e => update(it._i, { owner: e.target.value })}/>
                                </>
                              )}
                            </div>
                          </div>
                          <div className="cg-due-cell">
                            <DueDatePicker value={it.due}
                                           onChange={(v) => update(it._i, { due: v })}
                                           compact/>
                          </div>
                          <button
                            className={"cg-asana " + (it.asanaPosted ? 'on' : 'off')}
                            onClick={() => update(it._i, { asanaPosted: !it.asanaPosted })}
                            title={it.asanaPosted ? 'Posted to Asana' : 'Click to mark as posted'}>
                            <span className="cg-asana-dot"/>
                            <span className="cg-asana-lbl">{it.asanaPosted ? 'POSTED' : 'PENDING'}</span>
                          </button>
                          <button className="cg-rm" onClick={() => remove(it._i)} aria-label="Remove">×</button>
                        </div>
                      );
                    })}
                  </div>
                </section>
              );
            })}
          </div>
        </>
      )}
    </div>
  );
}

// ────────────────────────────────────────────────────────────
// App
// ────────────────────────────────────────────────────────────
function App(){
  const initialState = window.__B2B_STATE;
  const [state, setState] = usePersistentState(initialState);
  const [current, setCurrent] = useState('cover');
  const tw = useTweaks();

  const tabs = useMemo(() => [
    { id:'cover', name:'Cover', kind:'cover' },
    { id:'purpose', name:'Purpose', kind:'meta' },
    { id:'carryover', name:'Carry-over', kind:'meta' },
    { id:'overview', name:'Review', kind:'meta' },
    ...state.stages.map(s => ({ id:`stage-${s.id}`, name:s.name, kind:'stage', stageId:s.id })),
    { id:'commitments', name:'Action List', kind:'meta' },
  ], [state.stages]);

  // Inline IDS / commit add helpers
  const idsAdd = (item) => setState(prev => ({ ...prev, idsQueue: [...prev.idsQueue, item] }));
  const commitAdd = (item) => setState(prev => ({
    ...prev,
    commitments: [...prev.commitments, item]
  }));
  const commitUpdate = (idx, patch) => setState(prev => ({
    ...prev,
    commitments: prev.commitments.map((c, i) => i === idx ? { ...c, ...patch } : c)
  }));
  const commitRemove = (idx) => setState(prev => ({
    ...prev,
    commitments: prev.commitments.filter((_, i) => i !== idx)
  }));

  let viewEl = null;
  if (current === 'cover') viewEl = <CoverView state={state}/>;
  else if (current === 'purpose') viewEl = <PurposeView state={state} tweaks={tw.tweaks}/>;
  else if (current === 'overview') viewEl = <OverviewView state={state} current={current} onSelect={setCurrent} tweaks={tw.tweaks}/>;
  else if (current === 'carryover') viewEl = <CarryoverView state={state} setState={setState}/>;
  else if (current === 'commitments') viewEl = <CommitmentsView state={state} setState={setState}/>;
  else if (current.startsWith('stage-')) {
    const stageId = current.slice(6);
    const stage = state.stages.find(s => s.id === stageId);
    if (stage) viewEl = <StageView state={state} setState={setState} stage={stage}
                                   current={current} onSelect={setCurrent}
                                   tweaks={tw.tweaks} idsAdd={idsAdd} commitAdd={commitAdd}
                                   commitUpdate={commitUpdate} commitRemove={commitRemove}/>;
  }

  return (
    <div className="app">
      <TopNav meta={state.meta} hubspot={state.hubspot} asana={state.asana}/>
      <TabRail tabs={tabs} current={current} onSelect={setCurrent} state={state}/>
      <div className="stage">{viewEl}</div>
      <TweaksPanel {...tw}/>
    </div>
  );
}

function __b2bMount(){ ReactDOM.createRoot(document.getElementById('root')).render(<App/>); }
if (window.__B2B_READY) __b2bMount();
else document.addEventListener('b2b:ready', __b2bMount, { once: true });
