// Mella — Studio Dashboard (desktop-first), interactive workspace
// Phase 1: functional nav + Requests inbox + Accept→board + cross-surface sync toasts.
// Status palette (cohesion audit): teal = in progress, sienna = attention,
// olive = done, ochre = money. Money: sans display, mono ledger. Built on mella-kit.jsx.

const { useState, useEffect } = React;
const { hexToRgba, ACCENTS, MONO, PALS, ART, resolveTheme, Painting, Field, inp, ta } = window;

const ATTN = '#B05536';   // sienna — attention
const DONE = '#5C6536';   // olive — done
const MONEYC = '#9A6B2E'; // ochre — money

const P_BODHI = ['#D9D4C6', '#9AA083', '#5C6347', '#2c3020'];
const P_MOCHI = ['#E6D6BE', '#C3A37E', '#7C6046', '#3a2d1e'];

// Generated commission imagery (Gemini). `photo` = the client's reference snapshot
// of the live pet (shown small in the dashboard). WIP stage shots stay gradient
// placeholders here; the real progression lives in the client portal. Final = the
// real oil (co.art). Each carries a focal point + a fallback palette while it loads.
const MAPLE_PHOTO   = { src:'art/stbernard-head-photo.jpg', pos:'center 28%', pal:PALS.maple };
const JUNIPER_PHOTO = { src:'art/affenpinscher-photo.jpg',  pos:'center 32%', pal:PALS.juniper };
const OTTO_PHOTO    = { src:'art/terrier-up-photo.jpg',     pos:'center 26%', pal:PALS.otto };
const DAISY_PHOTO   = { src:'art/stbernard-pup-photo.jpg',  pos:'center 35%', pal:PALS.daisy };
const BODHI_PHOTO   = { src:'art/leonberger-head-photo.jpg',pos:'center 30%', pal:P_BODHI };
// Mochi — a two-pup request: one shot of both pups + a matching solo of each
// (the solos generated img2img from crops of the together shot, so it's the same two dogs).
const MOCHI_TOGETHER = { src:'art/mochi-together.jpg', pos:'center 62%', pal:P_MOCHI, ratio:0.806 };
const MOCHI_A        = { src:'art/mochi-a.jpg',        pos:'center 42%', pal:P_MOCHI, ratio:0.806 };
const MOCHI_B        = { src:'art/mochi-b.jpg',        pos:'center 42%', pal:P_MOCHI, ratio:0.806 };

/* commissions + requests ----------------------------------------------- */
const C = {
  maple:   { id:'maple',  name:'Maple',          client:'Sarah M.',  spec:'memorial, Saint Bernard, 11×14', pal:PALS.maple,   art:ART.stbHead,   total:260, dep:'paid', bal:'due on approval', due:'Jul 18', token:'xK9mP2',
             photo:MAPLE_PHOTO,
             brief:{ subject:'Saint Bernard', email:'sarah.m@gmail.com', refs:[MAPLE_PHOTO], note:"Maple was our girl for 13 years, we lost her this spring. This is the last portrait we have of her. I'd love to capture that softness in her eyes." } },
  juniper: { id:'juniper',name:'Juniper',        client:'Dana R.',   spec:'scruffy terrier, 9×12',          pal:PALS.juniper, art:ART.affen,     total:340, dep:'paid', bal:'due on approval', due:'Jul 24', token:'pQ7nL4',
             photo:JUNIPER_PHOTO,
             brief:{ subject:'Terrier mix', email:'dana.r@gmail.com', refs:[JUNIPER_PHOTO], note:"Juniper is the most photogenic dog alive and has the most soulful eyes. Would love her on a 9×12 for the living room." } },
  otto:    { id:'otto',   name:'Otto',           client:'Marcus T.', spec:'Maltese, 8×10',                  pal:PALS.otto,    art:ART.terrierUp, total:190, dep:'paid', bal:'due on approval', due:'Jul 12', token:'rT2bW8', sentFinal:true,
             photo:OTTO_PHOTO,
             brief:{ subject:'Maltese', email:'marcus.t@gmail.com', refs:[OTTO_PHOTO], note:"Otto turns 5 this month. I want to surprise my partner with a little portrait of our fluffy white boy. He loves to sit up and beg just like this." } },
  daisy:   { id:'daisy',  name:'Daisy',          client:'Priya S.',  spec:'Saint Bernard pup, 11×14',       pal:PALS.daisy,   art:ART.stbPup,    total:300, dep:'paid', bal:'paid', due:'shipped Jul 9', token:'kM5xZ1',
             photo:DAISY_PHOTO,
             brief:{ subject:'Saint Bernard', email:'priya.s@gmail.com', refs:[DAISY_PHOTO], note:"Daisy is our Saint Bernard pup, such a sweet face. An 11×14 would be perfect above the stairs." } },
  // incoming requests (also become board cards on accept)
  bodhi:   { id:'bodhi',  name:'Bodhi',          client:'Tom H.',    spec:'Leonberger, 11×14',              pal:P_BODHI,      art:ART.leonberger,total:320, dep:'paid', bal:'paid', due:'shipped Jul 2', token:'wQ1aa2',
             photo:BODHI_PHOTO,
             req:{ when:'Jun 16', subject:'Leonberger', size:'11×14', options:['Memorial','Single pet'], timeline:'No deadline', email:'tomh@gmail.com', refs:[BODHI_PHOTO], note:"Bodhi was my hiking buddy for twelve years. I had you paint him from this photo up on the ridge. Thank you for capturing him." } },
  mochi:   { id:'mochi',  name:'Mochi & Yuzu',   client:'Aly P.',    spec:'two pets, 12×16',                pal:P_MOCHI,      art:ART.litterDoor,total:380, dep:'unbilled', bal:'on approval', due:'Aug 11', token:'eR4bb5',
             req:{ when:'yesterday', subject:'Two pups', size:'12×16', options:['Two pets'], timeline:'No rush', email:'aly.p@gmail.com', refs:[MOCHI_TOGETHER, MOCHI_A, MOCHI_B], note:"Could you do both of my pups on one canvas? They're very different colors. Not in a rush at all." } },
};
const COLUMNS = [
  { key:'open',  label:'Open',            tint:(c)=>c.faint },
  { key:'wip',   label:'In progress',     tint:(c)=>c.a },
  { key:'await', label:'Awaiting client', tint:()=>ATTN },
  { key:'done',  label:'Done',            tint:()=>DONE },
];
const INITIAL = { open:[], wip:['juniper'], await:['maple','otto'], done:['daisy','bodhi'] };

// invoices — one row per deposit / balance event
const INVOICES = [
  { key:'i1', co:'otto',    kind:'Balance', amt:95,  state:'overdue', when:'invoice sent 6 days ago' },
  { key:'i2', co:'maple',   kind:'Balance', amt:130, state:'pending', when:'final in review with client' },
  { key:'i3', co:'bodhi',   kind:'Balance', amt:160, state:'paid',    when:'paid Jul 2' },
  { key:'i4', co:'daisy',   kind:'Balance', amt:150, state:'paid',    when:'paid Jul 1' },
  { key:'i5', co:'otto',    kind:'Deposit', amt:95,  state:'paid',    when:'paid Jun 28' },
  { key:'i6', co:'juniper', kind:'Deposit', amt:170, state:'paid',    when:'paid Jun 26' },
  { key:'i7', co:'maple',   kind:'Deposit', amt:130, state:'paid',    when:'paid Jun 24' },
  { key:'i8', co:'bodhi',   kind:'Deposit', amt:160, state:'paid',    when:'paid Jun 20' },
  { key:'i9', co:'daisy',   kind:'Deposit', amt:150, state:'paid',    when:'paid Jun 18' },
];

// Commission thumbnail rule: prefer the client's reference PHOTO of the live pet
// whenever we have one — that's the cover we want on the board. With no photo, a
// DONE piece shows the real finished oil; otherwise fall back to the gradient.
// Standardized placeholder gradient: every filler with no real image renders
// this one sage/green (Bodhi's palette), so empty thumbnails look consistent.
const FILLER = P_BODHI;
const pieceImg = (co, done) => {
  if (co.photo)       return { pal: co.photo.pal, src: co.photo.src, pos: co.photo.pos };
  if (done && co.art) return { pal: co.art.pal, src: co.art.src, pos: co.art.pos };
  return { pal: FILLER };
};
// Normalize a reference/stage entry (palette array OR {src,pos,pal}) for <Painting>.
// Real images (with src) pass through; any palette-only filler standardizes to FILLER.
const refProps = (r) => (r && r.src) ? { pal: r.pal, src: r.src, pos: r.pos } : { pal: FILLER };

/* ============================================================== dashboard */
function Dashboard({ t }) {
  const c = resolveTheme(t);
  const { tier } = window.useViewport();
  const compact = tier === 'tablet';   // iPad portrait (834): stacked layout
  const isNew = t.accountState === 'new';
  const [view, setView] = useState('queue');
  const [cols, setCols] = useState(INITIAL);
  const [requests, setRequests] = useState(['mochi']);
  const [setupDone, setSetupDone] = useState({});
  const [drag, setDrag] = useState(null);
  const [over, setOver] = useState(null);
  const [detail, setDetail] = useState(null);
  const [posting, setPosting] = useState(null);
  const [adding, setAdding] = useState(false);
  const [confirm, setConfirm] = useState(null);
  const [toast, setToast] = useState(null);
  const askConfirm = (opts) => setConfirm(opts);
  useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') { setDetail(null); setAdding(false); setConfirm(null); } };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, []);
  useEffect(() => {
    setCols(isNew ? { open:[], wip:[], await:[], done:[] } : INITIAL);
    setRequests(isNew ? [] : ['mochi']);
    setView('queue');
  }, [t.accountState]);

  const flash = (msg) => { setToast(msg); clearTimeout(window.__mt); window.__mt = setTimeout(() => setToast(null), 3000); };
  const openCount = Math.max(0, 5 - (cols.wip.length + cols.await.length));
  const inflight = cols.wip.concat(cols.await).reduce((s, id) => s + C[id].total, 0);
  const owedDue = INVOICES.filter(i => i.state==='overdue' || i.state==='pending');
  const owedSum = owedDue.reduce((s,i) => s + i.amt, 0);

  const dropCard = (to) => {
    if (!drag) { setOver(null); return; }
    if (to === 'open') { setDrag(null); setOver(null); return; } // Open holds empty slots, not commissions
    const id = drag;
    setCols(prev => { const n = {}; Object.keys(prev).forEach(k => n[k] = prev[k].filter(x => x !== id)); n[to] = [...n[to], id]; return n; });
    if (to === 'done') {
      const willOpen = Math.max(0, 5 - (cols.wip.filter(x => x !== id).length + cols.await.filter(x => x !== id).length));
      flash(`${C[id].name} marked done. Your public page now shows ${willOpen} of 5 open.`);
    }
    setDrag(null); setOver(null);
  };
  const performAccept = (id) => {
    C[id].dep = 'sent';   // deposit invoice goes out the moment you accept
    setRequests(r => r.filter(x => x !== id));
    setCols(prev => ({ ...prev, wip: [...prev.wip, id] }));
    const willOpen = Math.max(0, 5 - (cols.wip.length + 1 + cols.await.length));
    flash(`Accepted ${C[id].name}. Deposit invoice sent to ${C[id].client}. Public page now shows ${willOpen} of 5 open.`);
    setView('queue');
  };
  const accept = (id) => askConfirm({ title:'Accept this request?', body:`This reserves a slot and emails ${C[id].client} a deposit invoice for $${C[id].total/2}.`, label:'Accept and send', onYes:() => performAccept(id) });
  const performDecline = (id) => { setRequests(r => r.filter(x => x !== id)); flash(`Declined ${C[id].name}. ${C[id].client} gets a kind note.`); };
  const decline = (id) => askConfirm({ title:'Decline this request?', body:`${C[id].client} will get a kind, automatic note. This can't be undone.`, label:'Decline', danger:true, onYes:() => performDecline(id) });
  const uploadWip = (co) => { setPosting(co.id); };
  const postUpdate = (co, data) => { setPosting(null); flash(`${data.stage} posted to ${co.name}. ${co.client} just got a gentle heads-up to take a look.`); };
  const remind = (inv) => flash(`Friendly reminder sent to ${C[inv.co].client} for the $${inv.amt} balance.`);
  const addCommission = (data) => {
    const id = 'c' + Date.now();
    C[id] = { id, name:data.name, client:data.client, spec:data.spec, pal:PALS.juniper, total:Number(data.price)||0,
      dep:data.depPaid?'paid':'sent', bal:'on approval', due:'in progress', token:Math.random().toString(36).slice(2,8),
      brief:{ subject:data.spec, email:'', refs:[PALS.juniper], note:'Added by you while migrating your queue into Mella.' } };
    setCols(prev => ({ ...prev, [data.stage]: [...prev[data.stage], id] }));
    setAdding(false);
    flash(`${data.name} added to your board.`);
  };

  const NAV = [['queue','Queue',0],['requests','Requests',requests.length],['payments','Payments',0],['settings','Settings',0]];

  return (
    <div style={{ position:'relative', display:'flex', flexDirection:'column', height:'100%', fontFamily:c.f.body, color:c.text, background:c.bg }}>

      {/* ---- top bar ---- */}
      <div style={{ display:'flex', alignItems:'center', gap:18, padding:'0 22px', height:56, borderBottom:`1px solid ${c.rule}`, flexShrink:0 }}>
        <span style={{ fontFamily:"'Instrument Serif', serif", fontSize:23, color:c.text }}>mella<span style={{ color:c.a }}>.</span></span>
        <div style={{ width:1, height:22, background:c.rule }} />
        <span style={{ fontSize:13.5, color:c.soft, fontWeight:600 }}>Iris's Studio</span>
        <nav style={{ display:'flex', gap:4, marginLeft:14 }}>
          {NAV.map(([key,label,badge]) => {
            const act = view === key;
            return (
              <span key={key} onClick={() => setView(key)} style={{ position:'relative', fontSize:13, padding:'7px 11px', borderRadius:7,
                color: act?c.text:c.muted, background: act?c.sunk:'transparent', fontWeight: act?600:500, cursor:'pointer' }}>
                {label}
                {badge>0 && <span style={{ marginLeft:6, fontFamily:MONO, fontSize:9, color:'#fff', background:ATTN, borderRadius:99, padding:'1px 5px' }}>{badge}</span>}
              </span>
            );
          })}
        </nav>
        <div style={{ marginLeft:'auto', display:'flex', alignItems:'center', gap:12 }}>
          <a href="Mella Public Queue.html" target="_blank" rel="noopener" style={{ fontFamily:MONO, fontSize:11.5, color:c.muted, textDecoration:'none', display:'flex', alignItems:'center', gap:7, border:`1px solid ${c.rule}`, borderRadius:8, padding:'7px 11px' }}>
            <span style={{ width:6, height:6, borderRadius:'50%', background:c.a }} /> mella.studio/iris ↗
          </a>
          <Painting pal={ART.leonberger.pal} src={ART.leonberger.src} pos={ART.leonberger.pos} radius={'50%'} style={{ width:30, height:30 }} />
        </div>
      </div>

      {/* ---- body ---- */}
      <div style={{ flex:1, display:'flex', minHeight:0 }}>
        {view === 'settings' ? <SettingsShell c={c} flash={flash} askConfirm={askConfirm} />
         : isNew ? <NewArtistView view={view} c={c} flash={flash} setView={setView} setupDone={setupDone} setSetupDone={setSetupDone} />
         : (
        <React.Fragment>
        {compact && view === 'queue' ? (
          <CompactQueue c={c} cols={cols} openCount={openCount} inflight={inflight} owedSum={owedSum} requests={requests} setDetail={setDetail} setAdding={setAdding} setView={setView} flash={flash} />
        ) : (
        <React.Fragment>
        <div style={{ flex:1, display:'flex', flexDirection:'column', minWidth:0, padding:'20px 22px 0' }}>
          {view === 'queue' && (
            <React.Fragment>
              <div style={{ display:'flex', gap:12, marginBottom:18 }}>
                <Stat c={c} label="Open slots" value={`${openCount} / 5`} />
                <Stat c={c} label="In progress" value={cols.wip.length} dot={c.a} />
                <Stat c={c} label="Awaiting client" value={cols.await.length} dot={ATTN} />
                <Stat c={c} label="Active work" value={`$${inflight.toLocaleString()}`} />
              </div>
              <div style={{ flex:1, display:'flex', gap:14, minHeight:0, overflow:'hidden' }}>
                {COLUMNS.map(col => {
                  const ids = cols[col.key]; const tint = col.tint(c);
                  return (
                    <div key={col.key}
                      onDragOver={(e) => { if (col.key !== 'open') { e.preventDefault(); setOver(col.key); } }}
                      onDragLeave={() => setOver(o => o === col.key ? null : o)}
                      onDrop={() => dropCard(col.key)}
                      style={{ flex:1, minWidth:0, display:'flex', flexDirection:'column', background: over===col.key ? c.aSoft : 'transparent', borderRadius:12, transition:'background .15s', padding:4 }}>
                      <div style={{ display:'flex', alignItems:'center', gap:8, padding:'2px 6px 10px' }}>
                        <span style={{ width:8, height:8, borderRadius:'50%', background:tint }} />
                        <span style={{ fontSize:12.5, fontWeight:600, color:c.text }}>{col.label}</span>
                        <span style={{ fontFamily:MONO, fontSize:11, color:c.faint }}>{col.key==='open' ? openCount : ids.length}</span>
                      </div>
                      <div style={{ display:'flex', flexDirection:'column', gap:9, overflowY:'auto', paddingRight:2 }}>
                        {col.key==='open'
                          ? Array.from({length: openCount}).map((_,i) => <OpenSlot key={i} c={c} onClick={() => setAdding(true)} />)
                          : ids.map(id => <Card key={id} c={c} co={C[id]} tint={tint} colKey={col.key} dragging={drag===id} onDragStart={() => setDrag(id)} onDragEnd={() => { setDrag(null); setOver(null); }} onClick={() => setDetail(id)} />)}
                        {col.key!=='open' && ids.length===0 && <div style={{ fontSize:11.5, color:c.faint, padding:'10px 6px', textAlign:'center' }}>—</div>}
                      </div>
                    </div>
                  );
                })}
              </div>
            </React.Fragment>
          )}

          {view === 'requests' && <RequestsInbox c={c} ids={requests} onAccept={accept} onDecline={decline} />}
          {view === 'payments' && <PaymentsView c={c} onRemind={remind} />}
        </div>

        {/* right rail */}
        <div style={{ width:300, flexShrink:0, borderLeft:`1px solid ${c.rule}`, padding:'20px 20px', display:'flex', flexDirection:'column', gap:18, overflowY:'auto', background:c.sunk }}>
          <RailCard c={c} title="Your public page">
            <div style={{ display:'flex', alignItems:'center', gap:8, fontFamily:MONO, fontSize:12, color:c.soft }}>
              <span style={{ width:6, height:6, borderRadius:'50%', background:c.a }} /> mella.studio/iris
            </div>
            <div style={{ display:'flex', gap:8, marginTop:11 }}>
              <a href="Mella Public Queue.html" target="_blank" rel="noopener" style={miniBtn(c,true)}>Open</a>
              <span style={miniBtn(c)} onClick={() => flash('Link copied to clipboard.')}>Copy link</span>
              <span style={miniBtn(c)} onClick={() => setView('settings')}>Edit page</span>
            </div>
            <div style={{ marginTop:12, paddingTop:12, borderTop:`1px solid ${c.rule}`, display:'flex', justifyContent:'space-between', fontSize:12.5, color:c.muted }}>
              <span>Slots showing</span><span style={{ color:c.text }}>{openCount} of 5 open</span>
            </div>
          </RailCard>

          <RailCard c={c} title="Payments">
            <Row c={c} label="Stripe" value={<span style={{ color:DONE, display:'flex', alignItems:'center', gap:6 }}><span>✓</span> Connected</span>} />
            <Row c={c} label="Active work" value={<span style={{ fontFamily:c.f.body, fontWeight:600 }}>${inflight.toLocaleString()}.00</span>} />
            <Row c={c} label="Balances due" value={<span style={{ fontFamily:c.f.body, fontWeight:600, color:ATTN }}>${owedSum}.00</span>} />
            <div style={{ marginTop:8, fontSize:11, color:c.faint, lineHeight:1.45 }}>You keep your own payouts. Mella never holds your money.</div>
          </RailCard>

          <RailCard c={c} title="Lately">
            {[['Maple','final in review with Sarah', ATTN],['Otto','balance reminder sent', MONEYC],[`${requests.length} new request${requests.length===1?'':'s'}`,'from your public page', c.a]].map((r,i) => (
              <div key={i} style={{ display:'flex', gap:9, padding:'7px 0', borderTop: i? `1px solid ${c.rule}`:'none' }}>
                <span style={{ width:6, height:6, borderRadius:'50%', background:r[2], marginTop:5, flexShrink:0 }} />
                <div style={{ fontSize:12.5, color:c.text }}>{r[0]}<span style={{ color:c.muted }}>, {r[1]}</span></div>
              </div>
            ))}
          </RailCard>
        </div>
        </React.Fragment>
        )}
        </React.Fragment>
        )}
      </div>

      {detail && <DetailDrawer c={c} co={C[detail]} colKey={Object.keys(cols).find(k => cols[k].includes(detail))} onClose={() => setDetail(null)} onUpload={uploadWip} />}
      {posting && <PostUpdate c={c} co={C[posting]} onClose={() => setPosting(null)} onPost={(data) => postUpdate(C[posting], data)} />}
      {adding && <AddCommission c={c} onClose={() => setAdding(false)} onAdd={addCommission} />}
      {confirm && <Confirm c={c} {...confirm} onClose={() => setConfirm(null)} />}
      {toast && <Toast c={c} msg={toast} />}
    </div>
  );
}

/* ---- compact (iPad portrait) queue: stacked sections, 2-up cards, tap to open ---- */
function CompactQueue({ c, cols, openCount, inflight, owedSum, requests, setDetail, setAdding, setView, flash }) {
  const sec = { marginBottom: 24 };
  const secHead = { display:'flex', alignItems:'center', gap:9, padding:'0 2px 11px' };
  return (
    <div style={{ flex:1, overflowY:'auto', padding:'18px 20px 28px' }}>
      {/* stat strip */}
      <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:10, marginBottom:22 }}>
        <Stat c={c} label="Open slots" value={`${openCount} / 5`} />
        <Stat c={c} label="In progress" value={cols.wip.length} dot={c.a} />
        <Stat c={c} label="Awaiting client" value={cols.await.length} dot={ATTN} />
        <Stat c={c} label="Active work" value={`$${inflight.toLocaleString()}`} />
      </div>

      {/* board as stacked sections */}
      {COLUMNS.map(col => {
        const ids = cols[col.key]; const tint = col.tint(c);
        const count = col.key==='open' ? openCount : ids.length;
        return (
          <div key={col.key} style={sec}>
            <div style={secHead}>
              <span style={{ width:8, height:8, borderRadius:'50%', background:tint }} />
              <span style={{ fontSize:14, fontWeight:600, color:c.text }}>{col.label}</span>
              <span style={{ fontFamily:MONO, fontSize:11.5, color:c.faint }}>{count}</span>
            </div>
            <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:10 }}>
              {col.key==='open'
                ? Array.from({length: openCount}).map((_,i) => <OpenSlot key={i} c={c} onClick={() => setAdding(true)} />)
                : ids.map(id => <Card key={id} c={c} co={C[id]} tint={tint} colKey={col.key} onClick={() => setDetail(id)} />)}
              {col.key!=='open' && ids.length===0 && <div style={{ gridColumn:'1 / -1', fontSize:12, color:c.faint, padding:'12px 4px', textAlign:'center' }}>Nothing here yet</div>}
            </div>
          </div>
        );
      })}

      {/* rail info, folded into sections below */}
      <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:14, marginTop:6 }}>
        <RailCard c={c} title="Your public page">
          <div style={{ display:'flex', alignItems:'center', gap:8, fontFamily:MONO, fontSize:12, color:c.soft }}>
            <span style={{ width:6, height:6, borderRadius:'50%', background:c.a }} /> mella.studio/iris
          </div>
          <div style={{ display:'flex', gap:8, marginTop:11, flexWrap:'wrap' }}>
            <a href="Mella Public Queue.html" target="_blank" rel="noopener" style={miniBtn(c,true)}>Open</a>
            <span style={miniBtn(c)} onClick={() => flash('Link copied to clipboard.')}>Copy link</span>
            <span style={miniBtn(c)} onClick={() => setView('settings')}>Edit page</span>
          </div>
          <div style={{ marginTop:12, paddingTop:12, borderTop:`1px solid ${c.rule}`, display:'flex', justifyContent:'space-between', fontSize:12.5, color:c.muted }}>
            <span>Slots showing</span><span style={{ color:c.text }}>{openCount} of 5 open</span>
          </div>
        </RailCard>
        <RailCard c={c} title="Payments">
          <Row c={c} label="Stripe" value={<span style={{ color:DONE, display:'flex', alignItems:'center', gap:6 }}><span>✓</span> Connected</span>} />
          <Row c={c} label="Active work" value={<span style={{ fontFamily:c.f.body, fontWeight:600 }}>${inflight.toLocaleString()}.00</span>} />
          <Row c={c} label="Balances due" value={<span style={{ fontFamily:c.f.body, fontWeight:600, color:ATTN }}>${owedSum}.00</span>} />
        </RailCard>
      </div>
    </div>
  );
}

/* ---- toast ---- */
function Toast({ c, msg }) {
  return (
    <div style={{ position:'absolute', left:'50%', bottom:22, transform:'translateX(-50%)', zIndex:60,
      background:c.dark?'#000':c.text, color:c.bg, fontSize:12.5, padding:'11px 18px', borderRadius:10,
      boxShadow:'0 16px 40px -16px rgba(0,0,0,0.5)', animation:'mq-rise .25s ease', maxWidth:560, textAlign:'center',
      display:'flex', alignItems:'center', gap:9 }}>
      <span style={{ width:6, height:6, borderRadius:'50%', background:c.a, flexShrink:0 }} />{msg}
    </div>
  );
}

/* ---- new artist: onboarding + empty states ---- */
function NewArtistView({ view, c, flash, setView, setupDone, setSetupDone }) {
  if (view === 'queue') return <Onboarding c={c} flash={flash} setView={setView} setupDone={setupDone} setSetupDone={setSetupDone} />;
  const empty = {
    requests: ['No requests yet', 'When someone fills out your public page, their request lands here. Share your link to get the first one.'],
    payments: ['No payments yet', 'Once you accept a commission and take a deposit, your money shows up here.'],
  }[view] || ['Nothing here yet', ''];
  return (
    <div style={{ flex:1, display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center', textAlign:'center', color:c.faint, padding:'40px' }}>
      <div style={{ width:46, height:46, borderRadius:12, border:`1.5px dashed ${c.rule}`, display:'flex', alignItems:'center', justifyContent:'center', fontSize:20, color:hexToRgba(c.a,0.7), marginBottom:15 }}>◌</div>
      <div style={{ fontFamily:c.f.head, fontWeight:c.f.hw, fontSize:23, letterSpacing:c.f.kern, color:c.text }}>{empty[0]}</div>
      <div style={{ fontSize:13.5, marginTop:7, maxWidth:'40ch', lineHeight:1.55 }}>{empty[1]}</div>
      <button onClick={() => setView('queue')} style={{ ...btn(c), background:c.a, color:c.aOn, padding:'11px 18px', marginTop:18 }}>Back to setup</button>
    </div>
  );
}

function Onboarding({ c, flash, setView, setupDone, setSetupDone }) {
  const mark = (k) => setSetupDone(p => ({ ...p, [k]: true }));
  const STEPS = [
    { k:'stripe', title:'Connect Stripe',        sub:'So you can take deposits and balances. About 2 minutes.',        cta:'Connect',   run:() => { mark('stripe'); /* STRIPE: launch Stripe Connect onboarding (OAuth/Account Link); on return, store the connected account id. See STRIPE-HANDOFF.md → "Connect". */ flash('Stripe connected. You can take payments now.'); } },
    { k:'page',   title:'Build your public page', sub:'Your art and portfolio, your prices, your colors, and your link.', cta:'Set up',    run:() => { mark('page'); setView('settings'); } },
    { k:'share',  title:'Share your link',        sub:'Put mella.studio/iris in your bio and start taking work.',        cta:'Copy link', run:() => { mark('share'); flash('Link copied. Paste it in your Instagram or Bluesky bio.'); } },
  ];
  const doneCount = STEPS.filter(s => setupDone[s.k]).length;
  const allDone = doneCount === STEPS.length;

  return (
    <div style={{ flex:1, overflowY:'auto', display:'flex', justifyContent:'center', padding:'40px 24px' }}>
      <div style={{ width:'100%', maxWidth:560 }}>
        <div style={{ fontFamily:MONO, fontSize:10, letterSpacing:'0.16em', textTransform:'uppercase', color:c.a }}>Welcome to Mella</div>
        <h1 style={{ fontFamily:c.f.head, fontWeight:c.f.hw, fontSize:34, letterSpacing:c.f.kern, color:c.text, margin:'10px 0 0' }}>Let's get your studio ready, Iris.</h1>
        <p style={{ fontSize:14.5, color:c.muted, lineHeight:1.55, margin:'10px 0 0', maxWidth:'46ch' }}>Three quick steps and your public page is live. You can change any of it later in Settings.</p>

        {/* progress */}
        <div style={{ display:'flex', alignItems:'center', gap:12, margin:'22px 0 18px' }}>
          <div style={{ flex:1, height:6, borderRadius:99, background:c.sunk, overflow:'hidden' }}>
            <div style={{ width:`${(doneCount/STEPS.length)*100}%`, height:'100%', background:c.a, borderRadius:99, transition:'width .3s' }} />
          </div>
          <span style={{ fontFamily:MONO, fontSize:11, color:c.faint }}>{doneCount} of {STEPS.length}</span>
        </div>

        {allDone && (
          <div style={{ background:c.aSoft, border:`1px solid ${hexToRgba(c.a,0.3)}`, borderRadius:12, padding:'16px 18px', marginBottom:16, display:'flex', alignItems:'center', gap:13 }}>
            <span style={{ width:30, height:30, borderRadius:'50%', background:c.a, color:c.aOn, display:'flex', alignItems:'center', justifyContent:'center', fontSize:16, flexShrink:0 }}>✓</span>
            <div>
              <div style={{ fontSize:16, fontWeight:600, color:c.text }}>You're all set.</div>
              <div style={{ fontSize:12.5, color:c.soft, marginTop:1 }}>Your page is live. Your first request will land in this dashboard.</div>
            </div>
          </div>
        )}

        {/* steps */}
        <div style={{ display:'flex', flexDirection:'column', gap:10 }}>
          {STEPS.map((s, i) => {
            const done = !!setupDone[s.k];
            return (
              <div key={s.k} style={{ display:'flex', alignItems:'center', gap:14, background:c.surface, border:`1px solid ${done ? hexToRgba(c.a,0.3) : c.rule}`, borderRadius:12, padding:'15px 17px' }}>
                <span style={{ width:26, height:26, borderRadius:'50%', flexShrink:0, display:'flex', alignItems:'center', justifyContent:'center', fontFamily:MONO, fontSize:12,
                  background: done ? c.a : 'transparent', color: done ? c.aOn : c.faint, border: done ? 'none' : `1.5px solid ${c.rule}` }}>{done ? '✓' : i+1}</span>
                <div style={{ flex:1, minWidth:0 }}>
                  <div style={{ fontSize:14, fontWeight:600, color:c.text }}>{s.title}</div>
                  <div style={{ fontSize:12, color:c.muted, marginTop:2 }}>{s.sub}</div>
                </div>
                <button onClick={s.run} style={{ ...btn(c), flexShrink:0, padding:'8px 14px', fontSize:12.5,
                  background: done ? 'transparent' : c.a, color: done ? c.muted : c.aOn, border: done ? `1px solid ${c.rule}` : 'none' }}>
                  {done ? 'Done' : s.cta}
                </button>
              </div>
            );
          })}
        </div>

        <div style={{ marginTop:20, fontSize:12, color:c.faint, textAlign:'center' }}>Need a hand? <span style={{ color:c.a, cursor:'pointer' }}>Read the quick start guide</span></div>
      </div>
    </div>
  );
}

/* ---- requests inbox ---- */
function Fact({ c, label, value }) {
  return (
    <div>
      <div style={{ fontFamily:MONO, fontSize:8.5, letterSpacing:'0.1em', textTransform:'uppercase', color:c.faint }}>{label}</div>
      <div style={{ fontSize:13, color:c.text, marginTop:3 }}>{value}</div>
    </div>
  );
}
function Chip({ c, children, accent }) {
  return <span style={{ fontFamily:MONO, fontSize:9, letterSpacing:'0.06em', textTransform:'uppercase', color: accent?c.a:c.muted, background: accent?c.aSoft:c.sunk, border:`1px solid ${accent?'transparent':c.rule}`, borderRadius:99, padding:'4px 9px' }}>{children}</span>;
}
function RequestsInbox({ c, ids, onAccept, onDecline }) {
  const [open, setOpen] = useState(null);   // request id being reviewed
  const [viewer, setViewer] = useState(null);
  return (
    <div style={{ maxWidth:680 }}>
      <div style={{ marginBottom:4, fontFamily:c.f.body, fontWeight:600, fontSize:24, letterSpacing:'-0.01em', color:c.text }}>Requests</div>
      <div style={{ fontSize:13, color:c.muted, marginBottom:18 }}>New commission requests from your public page. Open one to read the full brief, then accept or decline.</div>
      {ids.length === 0 ? (
        <div style={{ border:`1.5px dashed ${c.rule}`, borderRadius:12, padding:'40px', textAlign:'center', color:c.faint }}>
          <div style={{ fontSize:18, fontWeight:600, color:c.text }}>All caught up.</div>
          <div style={{ fontSize:13, marginTop:4 }}>New requests from mella.studio/iris will land here.</div>
        </div>
      ) : (
        <div style={{ display:'flex', flexDirection:'column', gap:10 }}>
          {ids.map(id => {
            const co = C[id];
            const nRefs = co.req.refs.length;
            return (
              <div key={id} onClick={() => setOpen(id)} style={{ background:c.surface, border:`1px solid ${c.rule}`, borderRadius:12, padding:13, display:'flex', gap:15, alignItems:'center', cursor:'pointer', transition:'border-color .15s' }}
                onMouseEnter={e=>e.currentTarget.style.borderColor=hexToRgba(c.a,0.5)} onMouseLeave={e=>e.currentTarget.style.borderColor=c.rule}>
                <div style={{ position:'relative', flexShrink:0 }}>
                  <Painting {...refProps(co.req.refs[0])} radius={9} style={{ width:64, height:64 }} />
                  {nRefs > 1 && <div style={{ position:'absolute', right:4, bottom:4, fontFamily:MONO, fontSize:8, color:'#fff', background:hexToRgba('#000',0.55), borderRadius:99, padding:'2px 6px' }}>{nRefs}</div>}
                </div>
                <div style={{ flex:1, minWidth:0 }}>
                  <div style={{ display:'flex', alignItems:'baseline', gap:9 }}>
                    <span style={{ fontWeight:600, fontSize:15.5, letterSpacing:'-0.01em', color:c.text }}>{co.name}</span>
                    <span style={{ fontSize:12.5, color:c.muted }}>{co.client}</span>
                    <span style={{ fontFamily:MONO, fontSize:10, color:c.faint, marginLeft:'auto' }}>{co.req.when}</span>
                  </div>
                  <div style={{ fontSize:12.5, color:c.muted, marginTop:4 }}>{co.req.subject}, {co.req.size}, {co.req.options[0]}</div>
                  <p style={{ margin:'7px 0 0', fontSize:13, lineHeight:1.45, color:c.soft, display:'-webkit-box', WebkitLineClamp:1, WebkitBoxOrient:'vertical', overflow:'hidden' }}>{co.req.note}</p>
                </div>
                <div style={{ flexShrink:0, textAlign:'right', paddingLeft:8 }}>
                  <div style={{ fontFamily:c.f.body, fontSize:18, fontWeight:600, letterSpacing:'-0.01em', color:c.text }}>${co.total}</div>
                  <div style={{ fontFamily:MONO, fontSize:9, color:c.faint, marginTop:1 }}>est. quote</div>
                  <div style={{ fontFamily:MONO, fontSize:10, color:c.a, marginTop:8 }}>Review →</div>
                </div>
              </div>
            );
          })}
        </div>
      )}
      {open && <RequestModal c={c} co={C[open]} onViewPhotos={() => setViewer(open)}
        onAccept={() => { onAccept(open); setOpen(null); }} onDecline={() => { onDecline(open); setOpen(null); }} onClose={() => setOpen(null)} />}
      {viewer && <PhotoViewer c={c} co={C[viewer]} onClose={() => setViewer(null)} />}
    </div>
  );
}

/* ---- request review modal (full brief + decision) ---- */
function RequestModal({ c, co, onViewPhotos, onAccept, onDecline, onClose }) {
  const refs = co.req.refs;
  return (
    <div onClick={onClose} style={{ position:'absolute', inset:0, zIndex:60, background:hexToRgba('#000',0.4), display:'flex', alignItems:'center', justifyContent:'center', animation:'mq-fade .18s ease' }}>
      <div onClick={e=>e.stopPropagation()} style={{ width:480, maxHeight:'90%', overflowY:'auto', background:c.bg, borderRadius:16, boxShadow:'0 40px 90px -30px rgba(0,0,0,0.6)' }}>
        {/* header */}
        <div style={{ padding:'18px 22px', borderBottom:`1px solid ${c.rule}`, display:'flex', alignItems:'center', gap:8 }}>
          <div style={{ fontFamily:MONO, fontSize:10, letterSpacing:'0.14em', textTransform:'uppercase', color:c.a }}>New request, {co.req.when}</div>
          <span onClick={onClose} style={{ marginLeft:'auto', cursor:'pointer', color:c.faint, fontSize:20, lineHeight:1 }}>×</span>
        </div>

        <div style={{ padding:'20px 22px' }}>
          {/* who + what */}
          <div style={{ fontSize:22, fontWeight:600, letterSpacing:'-0.01em', color:c.text }}>{co.name}</div>
          <div style={{ fontSize:13, color:c.muted, marginTop:2 }}>from {co.client}</div>

          {/* reference photos */}
          <div style={{ display:'flex', gap:8, marginTop:16 }}>
            {refs.map((r,idx) => (
              <div key={idx} onClick={onViewPhotos} style={{ flex:1, cursor:'pointer' }}>
                <Painting {...refProps(r)} radius={9} style={{ width:'100%', height:96 }} />
              </div>
            ))}
          </div>
          <div onClick={onViewPhotos} style={{ fontFamily:MONO, fontSize:10, color:c.a, marginTop:7, cursor:'pointer' }}>View {refs.length} reference photos →</div>

          {/* facts */}
          <div style={{ display:'flex', gap:24, marginTop:18, padding:'14px 0', borderTop:`1px solid ${c.rule}`, borderBottom:`1px solid ${c.rule}` }}>
            <Fact c={c} label="Subject" value={co.req.subject} />
            <Fact c={c} label="Size" value={co.req.size} />
            <Fact c={c} label="Type" value={co.req.options.join(', ')} />
            <Fact c={c} label="Timeline" value={co.req.timeline} />
          </div>

          {/* full note */}
          <div style={{ ...lbl(c), marginTop:16 }}>In their words</div>
          <p style={{ margin:0, fontSize:14, lineHeight:1.6, color:c.text }}>{co.req.note}</p>
          <div style={{ marginTop:12, fontFamily:MONO, fontSize:11, color:c.a }}>{co.req.email}</div>

          {/* quote + decision */}
          <div style={{ display:'flex', alignItems:'center', gap:14, marginTop:20, padding:'14px 16px', background:c.sunk, borderRadius:11 }}>
            <div>
              <div style={{ fontFamily:MONO, fontSize:9, letterSpacing:'0.1em', textTransform:'uppercase', color:c.faint }}>Est. quote</div>
              <div style={{ fontSize:22, fontWeight:600, letterSpacing:'-0.01em', color:c.text, marginTop:2 }}>${co.total}</div>
              <div style={{ fontFamily:c.f.body, fontSize:9.5, color:c.faint }}>${co.total/2} deposit to start</div>
            </div>
            <button onClick={onDecline} style={{ ...btn(c), marginLeft:'auto', background:'transparent', color:c.muted, border:`1px solid ${c.rule}`, padding:'11px 16px' }}>Decline</button>
            <button onClick={onAccept} style={{ ...btn(c), background:c.a, color:c.aOn, padding:'11px 18px' }}>Accept &amp; send invoice</button>
          </div>
        </div>
      </div>
    </div>
  );
}

/* ---- reference photo viewer ---- */
function PhotoViewer({ c, co, onClose }) {
  const [i, setI] = useState(0);
  const refs = co.req.refs;
  return (
    <div onClick={onClose} style={{ position:'absolute', inset:0, zIndex:60, background:hexToRgba('#000', c.dark?0.7:0.5), display:'flex', alignItems:'center', justifyContent:'center', animation:'mq-fade .18s ease' }}>
      <div onClick={e => e.stopPropagation()} style={{ width:440, background:c.bg, borderRadius:16, overflow:'hidden', boxShadow:'0 40px 90px -30px rgba(0,0,0,0.6)' }}>
        <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between', padding:'14px 18px', borderBottom:`1px solid ${c.rule}` }}>
          <div>
            <span style={{ fontWeight:600, fontSize:16, letterSpacing:'-0.01em', color:c.text }}>{co.name}</span>
            <span style={{ fontSize:12, color:c.muted, marginLeft:8 }}>{co.client}'s references</span>
          </div>
          <span onClick={onClose} style={{ cursor:'pointer', color:c.faint, fontSize:20, lineHeight:1 }}>×</span>
        </div>
        <div style={{ padding:18 }}>
          <Painting {...refProps(refs[i])} radius={10} style={{ width:'100%', aspectRatio: String((refs[i] && refs[i].ratio) || 0.8) }} />
          <div style={{ display:'flex', gap:8, marginTop:12 }}>
            {refs.map((r, idx) => (
              <div key={idx} onClick={() => setI(idx)} style={{ cursor:'pointer', borderRadius:7, padding:2, border:`2px solid ${idx===i ? c.a : 'transparent'}` }}>
                <Painting {...refProps(r)} radius={5} style={{ width:54, height:54 }} />
              </div>
            ))}
            <div style={{ marginLeft:'auto', alignSelf:'center', fontFamily:MONO, fontSize:10.5, color:c.faint }}>{i+1} of {refs.length}</div>
          </div>
        </div>
      </div>
    </div>
  );
}

/* ---- payments ---- */
function PaymentsView({ c, onRemind }) {
  const owed = INVOICES.filter(i => i.state==='overdue' || i.state==='pending').reduce((s,i)=>s+i.amt,0);
  const inflight = ['maple','juniper','otto'].reduce((s,id)=>s+C[id].total/2,0);
  const awaiting = INVOICES.filter(i => i.state==='overdue' || i.state==='pending');
  const paid = INVOICES.filter(i => i.state==='paid');

  const SummaryCard = ({ label, value, tint, sub }) => (
    <div style={{ flex:1, background:c.surface, border:`1px solid ${c.rule}`, borderRadius:12, padding:'15px 17px' }}>
      <div style={{ fontFamily:MONO, fontSize:9, letterSpacing:'0.1em', textTransform:'uppercase', color:c.faint, display:'flex', alignItems:'center', gap:6 }}>
        {tint && <span style={{ width:6, height:6, borderRadius:'50%', background:tint }} />}{label}
      </div>
      <div style={{ fontFamily:c.f.body, fontSize:26, fontWeight:600, color: tint || c.text, letterSpacing:'-0.015em', marginTop:7 }}>{value}</div>
      <div style={{ fontSize:11.5, color:c.muted, marginTop:3 }}>{sub}</div>
    </div>
  );

  return (
    <div style={{ maxWidth:760, paddingBottom:30 }}>
      <div style={{ marginBottom:4, fontFamily:c.f.body, fontWeight:600, fontSize:24, letterSpacing:'-0.015em', color:c.text }}>Payments</div>
      <div style={{ fontSize:13, color:c.muted, marginBottom:18 }}>What you are owed, what is overdue, and what you have earned. Paid out to your bank directly, Mella never holds your money.</div>

      <div style={{ display:'flex', gap:12, marginBottom:24 }}>
        <SummaryCard label="Owed to you" value={`$${owed}`} tint={ATTN} sub="2 balances outstanding" />
        <SummaryCard label="Deposits in flight" value={`$${inflight}`} sub="3 commissions underway" />
        <SummaryCard label="Earned this year" value="$4,820" tint={DONE} sub="18 portraits delivered" />
      </div>

      {/* awaiting balance */}
      <SectionLabel c={c}>Awaiting balance</SectionLabel>
      <div style={{ border:`1px solid ${c.rule}`, borderRadius:12, overflow:'hidden', marginBottom:22 }}>
        {awaiting.map((inv, i) => <InvoiceRow key={inv.key} c={c} inv={inv} first={i===0} onRemind={onRemind} />)}
      </div>

      {/* paid */}
      <SectionLabel c={c}>Recently paid</SectionLabel>
      <div style={{ border:`1px solid ${c.rule}`, borderRadius:12, overflow:'hidden' }}>
        {paid.map((inv, i) => <InvoiceRow key={inv.key} c={c} inv={inv} first={i===0} onRemind={onRemind} />)}
      </div>

      {/* stripe strip */}
      <div style={{ marginTop:22, background:c.sunk, border:`1px solid ${c.rule}`, borderRadius:12, padding:'14px 16px', display:'flex', alignItems:'center', gap:12 }}>
        <span style={{ width:30, height:30, borderRadius:7, background:c.a, flexShrink:0 }} />
        <div style={{ flex:1 }}>
          <div style={{ fontSize:13, color:c.text, fontWeight:600, display:'flex', alignItems:'center', gap:7 }}>Stripe connected <span style={{ color:DONE }}>✓</span></div>
          <div style={{ fontSize:11.5, color:c.muted, marginTop:1 }}>Payouts to your bank ending 4291, usually within 2 days.</div>
        </div>
        <span style={{ ...miniBtn(c), padding:'8px 13px' }}>Manage</span>
      </div>
    </div>
  );
}
function SectionLabel({ c, children }) {
  return <div style={{ fontFamily:MONO, fontSize:9.5, letterSpacing:'0.14em', textTransform:'uppercase', color:c.faint, fontWeight:500, marginBottom:9 }}>{children}</div>;
}
function InvoiceRow({ c, inv, first, onRemind }) {
  const co = C[inv.co];
  const palette = { paid:DONE, overdue:ATTN, pending:MONEYC }[inv.state];
  const label = { paid:'Paid', overdue:'Overdue', pending:'Pending approval' }[inv.state];
  return (
    <div style={{ display:'flex', alignItems:'center', gap:13, padding:'12px 15px', background:c.surface, borderTop: first?'none':`1px solid ${c.rule}` }}>
      <Painting {...pieceImg(co, co.bal==='paid')} radius={7} style={{ width:38, height:38, flexShrink:0 }} />
      <div style={{ flex:1, minWidth:0 }}>
        <div style={{ fontSize:13.5, color:c.text, fontWeight:600 }}>{co.name} <span style={{ color:c.muted, fontWeight:400 }}>for {co.client}</span></div>
        <div style={{ fontSize:11.5, color:c.muted, marginTop:1 }}>{inv.kind} payment, {inv.when}</div>
      </div>
      <span style={{ fontFamily:MONO, fontSize:9, letterSpacing:'0.04em', textTransform:'uppercase', color:palette, background:hexToRgba(palette, c.dark?0.2:0.11), borderRadius:99, padding:'4px 8px', flexShrink:0 }}>{label}</span>
      <div style={{ width:74, textAlign:'right', fontFamily:c.f.body, fontWeight:600, fontSize:14, color:c.text, flexShrink:0 }}>${inv.amt}</div>
      <div style={{ width:118, textAlign:'right', flexShrink:0 }}>
        {inv.state==='overdue' && <button onClick={() => onRemind(inv)} style={{ ...btn(c), background:c.a, color:c.aOn, padding:'8px 13px', fontSize:12 }}>Send reminder</button>}
        {inv.state==='pending' && <span style={{ fontFamily:MONO, fontSize:10, color:c.faint }}>waiting on client</span>}
      </div>
    </div>
  );
}

/* ---- settings shell: sub-nav + panels ---- */
function SettingsShell({ c, flash, askConfirm }) {
  const [tab, setTab] = useState('public');
  const TABS = [['public','Public page'],['commission','Commission setup'],['payments','Payment settings'],['account','Account'],['plan','Plan & billing']];
  return (
    <div style={{ flex:1, display:'flex', minHeight:0 }}>
      <div style={{ width:196, flexShrink:0, borderRight:`1px solid ${c.rule}`, padding:'22px 14px', background:c.sunk, overflowY:'auto' }}>
        <div style={{ fontFamily:MONO, fontSize:10.5, fontWeight:500, letterSpacing:'0.14em', textTransform:'uppercase', color:c.faint, padding:'2px 10px 16px' }}>Settings</div>
        {TABS.map(([k,label]) => {
          const on = tab===k;
          return <div key={k} onClick={() => setTab(k)} style={{ fontSize:13, padding:'9px 10px', borderRadius:8, marginBottom:2, cursor:'pointer', color:on?c.text:c.muted, background:on?c.surface:'transparent', fontWeight:on?600:500, border:`1px solid ${on?c.rule:'transparent'}` }}>{label}</div>;
        })}
      </div>
      <div style={{ flex:1, minWidth:0, display:'flex', minHeight:0 }}>
        {tab==='public' && <PublicPagePanel c={c} flash={flash} />}
        {tab==='commission' && <CommissionPanel c={c} flash={flash} />}
        {tab==='payments' && <PaymentSettingsPanel c={c} flash={flash} />}
        {tab==='account' && <AccountPanel c={c} flash={flash} askConfirm={askConfirm} />}
        {tab==='plan' && <PlanPanel c={c} flash={flash} askConfirm={askConfirm} />}
      </div>
    </div>
  );
}
function SettingsStub({ c, title, body }) {
  return (
    <div style={{ flex:1, padding:'22px 26px' }}>
      <div style={{ fontFamily:c.f.body, fontWeight:600, fontSize:24, letterSpacing:'-0.015em', color:c.text }}>{title}</div>
      <div style={{ fontSize:13.5, color:c.muted, marginTop:8, maxWidth:'46ch', lineHeight:1.55 }}>{body}</div>
      <div style={{ marginTop:20, border:`1.5px dashed ${c.rule}`, borderRadius:12, padding:'34px', textAlign:'center', color:c.faint, maxWidth:560 }}>
        <div style={{ width:42, height:42, borderRadius:11, border:`1.5px dashed ${c.rule}`, display:'flex', alignItems:'center', justifyContent:'center', fontSize:18, color:hexToRgba(c.a,0.7), margin:'0 auto 12px' }}>◌</div>
        <div style={{ fontSize:13, color:c.muted }}>We build this panel in a later pass.</div>
      </div>
    </div>
  );
}

/* ---- settings: customize public page (the wedge) ---- */
function PublicPagePanel({ c, flash }) {
  const [s, setS] = useState({
    name:'Iris Sandoval', specialty:'Oil portraits', tagline:'Hand-painted portraits of your pet, in oil on canvas, from your favorite photo.',
    location:'Portland, OR', handle:'iris', price:'260', statusNote:'',
    commissionsNote:"Every piece is quoted by hand. Size, number of pets, and detail all matter. Tell me what you have in mind and I'll send a price.",
    accent:'teal', tone:'warm', type:'editorial', showNames:false, showTiers:false, visibility:'public',
  });
  const set = (k, v) => setS(p => ({ ...p, [k]: v }));
  const [portfolio, setPortfolio] = useState([ART.stbHead, ART.stbPup, ART.terrierUp, ART.leonberger]);
  const ART_LIST = Object.values(ART);
  const addWork = () => setPortfolio(p => [...p, ART_LIST[p.length % ART_LIST.length]]);
  const pc = resolveTheme(s); // preview theme, independent of dashboard chrome
  const { tier } = window.useViewport();
  const showPreview = tier === 'desktop';   // inline preview only where it fits; else "Open live page"

  const grp = { marginBottom:22 };
  const lab = { fontFamily:MONO, fontSize:9.5, letterSpacing:'0.12em', textTransform:'uppercase', color:c.faint, marginBottom:10 };
  const field = (k, label, placeholder) => (
    <label style={{ display:'block', marginBottom:12 }}>
      <span style={{ fontSize:12, color:c.muted, display:'block', marginBottom:5 }}>{label}</span>
      <input value={s[k]} placeholder={placeholder} onChange={e => set(k, e.target.value)}
        style={{ width:'100%', fontFamily:c.f.body, fontSize:13.5, color:c.text, background:c.surface, border:`1px solid ${c.rule}`, borderRadius:9, padding:'9px 11px', outline:'none' }} />
    </label>
  );
  const Seg = ({ k, opts }) => (
    <div style={{ display:'flex', gap:6 }}>
      {opts.map(([val,txt]) => {
        const on = s[k]===val;
        return <button key={val} onClick={() => set(k,val)} style={{ flex:1, fontFamily:c.f.body, fontSize:12, fontWeight: on?600:500, padding:'8px 6px', borderRadius:8, cursor:'pointer',
          color: on?c.text:c.muted, background: on?c.surface:'transparent', border:`1px solid ${on?c.rule:'transparent'}`, boxShadow: on?`0 1px 2px ${hexToRgba('#000',0.04)}`:'none' }}>{txt}</button>;
      })}
    </div>
  );
  const Toggle = ({ k }) => (
    <div onClick={() => set(k,!s[k])} style={{ width:40, height:24, borderRadius:99, background: s[k]?c.a:c.rule, position:'relative', cursor:'pointer', flexShrink:0, transition:'background .15s' }}>
      <div style={{ position:'absolute', top:3, left: s[k]?19:3, width:18, height:18, borderRadius:'50%', background:'#fff', transition:'left .15s', boxShadow:'0 1px 2px rgba(0,0,0,0.25)' }} />
    </div>
  );
  const ToggleRow = ({ k, title, sub }) => (
    <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between', gap:12, padding:'10px 0' }}>
      <div><div style={{ fontSize:13, color:c.text }}>{title}</div><div style={{ fontSize:11.5, color:c.muted, marginTop:1 }}>{sub}</div></div>
      <Toggle k={k} />
    </div>
  );

  return (
    <div style={{ flex:1, display:'flex', gap:26, minWidth:0, minHeight:0, padding:'22px 26px' }}>
      {/* controls */}
      <div style={{ flex:'1 1 0', minWidth:0, maxWidth: showPreview ? 400 : 560, overflowY:'auto', paddingRight:6, paddingBottom:30 }}>
        <div style={{ display:'flex', alignItems:'flex-start', justifyContent:'space-between', gap:12, marginBottom:4 }}>
          <div style={{ marginBottom:4, fontFamily:c.f.body, fontWeight:600, fontSize:24, letterSpacing:'-0.015em', color:c.text }}>Your public page</div>
          {!showPreview && <a href="Mella Public Queue.html" target="_blank" rel="noopener" style={{ ...miniBtn(c,true), padding:'8px 13px', whiteSpace:'nowrap', flexShrink:0 }}>Open live page ↗</a>}
        </div>
        <div style={{ fontSize:13, color:c.muted, marginBottom:20 }}>This is the page clients see at your link. Make it feel like yours.</div>

        <div style={grp}>
          <div style={lab}>Visibility</div>
          <Seg k="visibility" opts={[['public','Public'],['unlisted','Unlisted']]} />
          <div style={{ fontSize:11.5, color:c.muted, marginTop:8, lineHeight:1.45 }}>
            {s.visibility==='public' ? 'Anyone with your link can request a slot.' : 'Hidden from search. Only people you share the link with can see it, good for a private waitlist.'}
          </div>
          <div style={{ marginTop:11, display:'flex', alignItems:'center', gap:8, fontFamily:MONO, fontSize:12, color:c.soft, background:c.sunk, border:`1px solid ${c.rule}`, borderRadius:8, padding:'8px 11px' }}>
            <span style={{ color:c.faint }}>mella.studio/</span>
            <input value={s.handle} onChange={e => set('handle', e.target.value.replace(/[^a-z0-9]/gi,'').toLowerCase())}
              style={{ flex:1, fontFamily:MONO, fontSize:12, color:c.text, background:'transparent', border:'none', outline:'none', padding:0 }} />
          </div>
        </div>

        <div style={grp}>
          <div style={lab}>Artwork</div>
          <div style={{ display:'flex', gap:12, alignItems:'center', marginBottom:11 }}>
            <Painting pal={ART.leonberger.pal} src={ART.leonberger.src} pos={ART.leonberger.pos} radius={'50%'} style={{ width:44, height:44, flexShrink:0 }} />
            <div style={{ flex:1 }}>
              <div style={{ fontSize:13, color:c.text }}>Profile photo</div>
              <div style={{ fontSize:11.5, color:c.muted, marginTop:1 }}>A small portrait of you</div>
            </div>
            <span style={{ ...miniBtn(c), padding:'7px 12px' }}>Change</span>
          </div>
          <div style={{ display:'flex', gap:12, alignItems:'center' }}>
            <Painting pal={ART.twoPointers.pal} src={ART.twoPointers.src} pos="center 38%" radius={7} style={{ width:66, height:40, flexShrink:0 }} />
            <div style={{ flex:1 }}>
              <div style={{ fontSize:13, color:c.text }}>Banner</div>
              <div style={{ fontSize:11.5, color:c.muted, marginTop:1 }}>A piece of your work, shown wide up top</div>
            </div>
            <span style={{ ...miniBtn(c), padding:'7px 12px' }}>Change</span>
          </div>
        </div>

        <div style={grp}>
          <div style={lab}>Recent work</div>
          <div style={{ fontSize:11.5, color:c.faint, marginBottom:10, lineHeight:1.45 }}>Your portfolio gallery. Add past pieces so new clients can see your work.</div>
          <div style={{ display:'grid', gridTemplateColumns:'repeat(4, 1fr)', gap:7 }}>
            {portfolio.map((p,i) => (
              <div key={i} style={{ position:'relative' }}>
                <Painting pal={p.pal} src={p.src} pos={p.pos} radius={7} style={{ width:'100%', height:58 }} />
                <span onClick={() => setPortfolio(arr => arr.filter((_,idx)=>idx!==i))} title="Remove"
                  style={{ position:'absolute', top:3, right:3, width:16, height:16, borderRadius:'50%', background:hexToRgba('#000',0.5), color:'#fff', fontSize:10, lineHeight:'16px', textAlign:'center', cursor:'pointer' }}>×</span>
              </div>
            ))}
            <div onClick={addWork} style={{ height:58, borderRadius:7, border:`1.5px dashed ${c.rule}`, display:'flex', alignItems:'center', justifyContent:'center', color:c.a, fontSize:18, cursor:'pointer', background:c.sunk }}>+</div>
          </div>
        </div>

        <div style={grp}>
          <div style={lab}>Identity</div>
          {field('name','Display name','Your name')}
          <div style={{ fontSize:11, color:c.faint, marginTop:-6, marginBottom:12, lineHeight:1.4 }}>The name clients see. It can differ from your account name.</div>
          {field('specialty','What you make','Oil portraits')}
          {field('tagline','Tagline','One warm line about your work')}
          {field('location','Location','City, State')}
        </div>

        <div style={grp}>
          <div style={lab}>Brand color</div>
          <div style={{ display:'flex', gap:9, flexWrap:'wrap' }}>
            {Object.entries(ACCENTS).map(([key,hex]) => (
              <div key={key} onClick={() => set('accent',key)} style={{ width:30, height:30, borderRadius:'50%', background:hex, cursor:'pointer',
                boxShadow: s.accent===key ? `0 0 0 2px ${c.bg}, 0 0 0 4px ${hex}` : `inset 0 0 0 1px ${hexToRgba('#000',0.08)}` }} />
            ))}
          </div>
        </div>

        <div style={grp}>
          <div style={lab}>Background</div>
          <Seg k="tone" opts={[['warm','Warm'],['paper','Paper'],['dusk','Dusk']]} />
        </div>
        <div style={grp}>
          <div style={lab}>Typeface</div>
          <Seg k="type" opts={[['editorial','Editorial'],['classic','Classic'],['clean','Clean']]} />
        </div>

        <div style={grp}>
          <div style={lab}>Commissions</div>
          <div style={{ fontSize:11.5, color:c.faint, marginBottom:10, lineHeight:1.45 }}>The <span style={{ color:c.a }}>"Starting at $X"</span> price comes from your lowest tier in <span style={{ color:c.a }}>Commission setup</span>. Write the line that sits beneath it in your own words.</div>
          <label style={{ display:'block' }}>
            <span style={{ fontSize:12, color:c.muted, display:'block', marginBottom:5 }}>Your commissions blurb</span>
            <textarea value={s.commissionsNote} rows={3} onChange={e => set('commissionsNote', e.target.value)}
              style={{ width:'100%', fontFamily:c.f.body, fontSize:13.5, color:c.text, background:c.surface, border:`1px solid ${c.rule}`, borderRadius:9, padding:'9px 11px', outline:'none', resize:'vertical', lineHeight:1.5 }} />
          </label>
        </div>

        <div style={grp}>
          <div style={lab}>Page options</div>
          <div style={{ fontSize:11.5, color:c.faint, marginBottom:10, lineHeight:1.45 }}>Prices come from your <span style={{ color:c.a }}>Commission setup</span>. The page shows your lowest as "Starting at".</div>
          <div style={{ borderTop:`1px solid ${c.rule}`, marginTop:4 }}>
            <ToggleRow k="showNames" title="Show client names" sub="Off shows anonymous slots" />
            <ToggleRow k="showTiers" title="Show price tiers" sub="A typical-sizes breakdown" />
          </div>
        </div>

        <button onClick={() => flash('Public page saved. Your live link is updated.')} style={{ ...btn(c), background:c.a, color:c.aOn, width:'100%', padding:'12px' }}>Save changes</button>
      </div>

      {/* live preview */}
      {showPreview && (
      <div style={{ flexShrink:0, width:300, display:'flex', flexDirection:'column', alignItems:'center', position:'sticky', top:0, alignSelf:'flex-start' }}>
        <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between', width:'100%', maxWidth:300, marginBottom:12 }}>
          <span style={{ fontFamily:MONO, fontSize:10, letterSpacing:'0.1em', textTransform:'uppercase', color:c.faint }}>Live preview</span>
          <a href="Mella Public Queue.html" target="_blank" rel="noopener" style={{ fontFamily:MONO, fontSize:10.5, color:c.a, textDecoration:'none' }}>Open live page ↗</a>
        </div>
        <div style={{ width:300, height:600, borderRadius:30, overflow:'hidden', background:pc.bg, border:`1px solid ${c.rule}`, boxShadow:`0 30px 60px -30px ${hexToRgba('#000',0.4)}`, position:'relative' }}>
          {/* mini url chrome */}
          <div style={{ background:pc.chrome, padding:'8px 0', textAlign:'center' }}>
            <span style={{ fontFamily:MONO, fontSize:10, color:pc.soft }}>🔒 mella.studio/{s.handle}{s.visibility==='unlisted' && <span style={{ color:pc.faint }}>  (unlisted)</span>}</span>
          </div>
          <PreviewPage pc={pc} s={s} />
        </div>
      </div>
      )}
    </div>
  );
}

// condensed, faithful preview of the public queue hero, driven live by settings
function PreviewPage({ pc, s }) {
  const eyebrow = { fontFamily:MONO, fontSize:8.5, letterSpacing:'0.16em', textTransform:'uppercase', color:pc.faint };
  return (
    <div style={{ height:566, overflowY:'auto' }}>
      <div style={{ position:'relative' }}>
        <Painting pal={ART.twoPointers.pal} src={ART.twoPointers.src} pos="center 20%" radius={0} style={{ height:96, width:'100%' }} />
        <div style={{ position:'absolute', inset:0, background:`linear-gradient(180deg, ${hexToRgba(pc.bg,0)} 40%, ${pc.bg} 100%)` }} />
      </div>
      <div style={{ padding:'0 18px', marginTop:-26, position:'relative' }}>
        <Painting pal={ART.leonberger.pal} src={ART.leonberger.src} pos={ART.leonberger.pos} radius={'50%'} style={{ width:52, height:52, border:`3px solid ${pc.bg}` }} />
        <div style={{ ...eyebrow, color:pc.a, marginTop:9 }}>{s.specialty}</div>
        <div style={{ fontFamily:pc.f.head, fontWeight:pc.f.hw, fontSize: 28*pc.f.big+4, letterSpacing:pc.f.kern, color:pc.text, lineHeight:1.05, marginTop:5 }}>{s.name}</div>
        <p style={{ margin:'6px 0 0', fontSize:11.5, lineHeight:1.45, color:pc.soft }}>{s.tagline}</p>
        <div style={{ ...eyebrow, marginTop:8, color:pc.faint }}>{s.location}</div>

        <div style={{ marginTop:14, background:pc.surface, border:`1px solid ${pc.rule}`, borderRadius:10, padding:'11px 13px' }}>
          <div style={{ display:'flex', alignItems:'center', gap:8 }}>
            <span style={{ width:7, height:7, borderRadius:'50%', background:pc.a, flexShrink:0 }} />
            <span style={{ fontFamily:pc.f.body, fontWeight:500, fontSize:15, letterSpacing:'-0.01em', color:pc.text, whiteSpace:'nowrap' }}>2 of 5 slots open</span>
          </div>
          <div style={{ marginLeft:15, marginTop:2, fontSize:11, color:pc.muted }}>{s.statusNote && s.statusNote.trim() ? s.statusNote : 'booking into July'}</div>
        </div>

        <div style={{ display:'flex', gap:5, marginTop:14 }}>
          {[0,1,2,3,4].map(i => <div key={i} style={{ flex:1, height:6, borderRadius:99, background: i<3?pc.a:'transparent', border: i<3?'none':`1.5px dashed ${pc.rule}`, opacity:i<3?1-i*0.08:1 }} />)}
        </div>
        <div style={{ marginTop:12, display:'flex', flexDirection:'column', gap:7 }}>
          {[0,1,2].map(i => (
            <div key={i} style={{ display:'flex', alignItems:'center', gap:9, background:pc.surface, border:`1px solid ${pc.rule}`, borderRadius:9, padding:'7px 9px' }}>
              {s.showNames
                ? <Painting pal={[ART.stbHead,ART.terrierUp,ART.affen][i].pal} src={[ART.stbHead,ART.terrierUp,ART.affen][i].src} pos={[ART.stbHead,ART.terrierUp,ART.affen][i].pos} radius={5} style={{ width:32, height:32, flexShrink:0 }} />
                : <div style={{ width:32, height:32, flexShrink:0, borderRadius:5, background:pc.sunk, border:`1px solid ${pc.rule}`, display:'flex', alignItems:'center', justifyContent:'center', fontFamily:MONO, fontSize:10, color:pc.faint }}>0{i+1}</div>}
              <div style={{ flex:1 }}>
                <div style={{ fontFamily:pc.f.body, fontWeight:500, fontSize:13, letterSpacing:'-0.005em', color:pc.text }}>{s.showNames ? ['Maple','Otto','Juniper'][i] : 'Slot 0'+(i+1)}</div>
                <div style={{ fontSize:9.5, color:pc.muted }}>{s.showNames ? ['memorial portrait','Maltese, 8x10','scruffy terrier, 9x12'][i] : 'in the studio'}</div>
              </div>
            </div>
          ))}
        </div>

        <div style={{ marginTop:14, paddingBottom:18 }}>
          <button style={{ width:'100%', background:pc.a, color:'#fff', border:'none', borderRadius:9, padding:'11px', fontFamily:pc.f.body, fontSize:13, fontWeight:600, cursor:'pointer' }}>Request a slot</button>
        </div>
      </div>
    </div>
  );
}

/* ---- settings: commission setup ---- */
function CommissionPanel({ c, flash }) {
  const [sizes, setSizes] = useState([
    { size:'8 × 10',  label:'single pet',      price:'260' },
    { size:'11 × 14', label:'single pet',      price:'340' },
    { size:'11 × 14', label:'two pets',        price:'440' },
    { size:'16 × 20', label:'statement piece', price:'620' },
  ]);
  const [types, setTypes] = useState(['Memorial','Single pet','Two pets','Personal']);
  const [newType, setNewType] = useState('');
  const [deposit, setDeposit] = useState('50');
  const [turnaround, setTurnaround] = useState('2 to 3 weeks');
  const [fields, setFields] = useState({ words:true, photo:true, timeline:true, budget:false });

  const lab = { fontFamily:MONO, fontSize:9.5, letterSpacing:'0.12em', textTransform:'uppercase', color:c.faint, marginBottom:4 };
  const card = { background:c.surface, border:`1px solid ${c.rule}`, borderRadius:12, padding:'16px 18px', marginBottom:18, maxWidth:620 };
  const small = { fontFamily:c.f.body, fontSize:13, color:c.text, background:c.bg, border:`1px solid ${c.rule}`, borderRadius:8, padding:'8px 10px', outline:'none' };
  const setSize = (i,k,v) => setSizes(p => p.map((r,idx)=> idx===i ? {...r,[k]:v} : r));

  const Toggle = ({ on, onClick }) => (
    <div onClick={onClick} style={{ width:40, height:24, borderRadius:99, background:on?c.a:c.rule, position:'relative', cursor:'pointer', flexShrink:0 }}>
      <div style={{ position:'absolute', top:3, left:on?19:3, width:18, height:18, borderRadius:'50%', background:'#fff', boxShadow:'0 1px 2px rgba(0,0,0,0.25)' }} />
    </div>
  );

  return (
    <div style={{ flex:1, overflowY:'auto', padding:'22px 26px' }}>
      <div style={{ fontFamily:c.f.body, fontWeight:600, fontSize:24, letterSpacing:'-0.015em', color:c.text }}>Commission setup</div>
      <div style={{ fontSize:13.5, color:c.muted, marginTop:8, marginBottom:22, maxWidth:'52ch', lineHeight:1.55 }}>
        The sizes, types, and questions you set here become the options clients pick from on your public page, and the tags you see on every request.
      </div>

      {/* sizes & pricing */}
      <div style={lab}>Sizes &amp; pricing</div>
      <div style={card}>
        <div style={{ display:'flex', gap:10, fontFamily:MONO, fontSize:9, letterSpacing:'0.08em', textTransform:'uppercase', color:c.faint, paddingBottom:8 }}>
          <span style={{ width:88 }}>Size</span><span style={{ flex:1 }}>Description</span><span style={{ width:84 }}>From</span><span style={{ width:24 }} />
        </div>
        {sizes.map((r,i) => (
          <div key={i} style={{ display:'flex', gap:10, alignItems:'center', paddingTop:8, borderTop:`1px solid ${c.rule}` }}>
            <input value={r.size} onChange={e=>setSize(i,'size',e.target.value)} style={{ ...small, width:88 }} />
            <input value={r.label} onChange={e=>setSize(i,'label',e.target.value)} style={{ ...small, flex:1 }} />
            <div style={{ width:84, display:'flex', alignItems:'center', gap:3 }}>
              <span style={{ color:c.faint, fontSize:13 }}>$</span>
              <input value={r.price} onChange={e=>setSize(i,'price',e.target.value.replace(/[^0-9]/g,''))} style={{ ...small, width:'100%', fontFamily:MONO }} />
            </div>
            <span onClick={()=>setSizes(p=>p.filter((_,idx)=>idx!==i))} style={{ width:24, textAlign:'center', cursor:'pointer', color:c.faint, fontSize:16 }}>×</span>
          </div>
        ))}
        <button onClick={()=>setSizes(p=>[...p,{size:'',label:'',price:''}])} style={{ marginTop:12, background:'transparent', border:'none', color:c.a, fontFamily:c.f.body, fontSize:12.5, fontWeight:600, cursor:'pointer', padding:0 }}>+ Add a size</button>
      </div>

      {/* commission types (the tags) */}
      <div style={lab}>Commission types</div>
      <div style={card}>
        <div style={{ fontSize:12.5, color:c.muted, marginBottom:12 }}>Clients choose these when they request. They show as the tags on each request.</div>
        <div style={{ display:'flex', flexWrap:'wrap', gap:7 }}>
          {types.map(tg => (
            <span key={tg} style={{ display:'flex', alignItems:'center', gap:7, fontFamily:MONO, fontSize:10.5, letterSpacing:'0.04em', color:c.a, background:c.aSoft, borderRadius:99, padding:'6px 10px' }}>
              {tg}<span onClick={()=>setTypes(p=>p.filter(x=>x!==tg))} style={{ cursor:'pointer', opacity:0.6 }}>×</span>
            </span>
          ))}
          <input value={newType} onChange={e=>setNewType(e.target.value)}
            onKeyDown={e=>{ if(e.key==='Enter' && newType.trim()){ setTypes(p=>[...p,newType.trim()]); setNewType(''); } }}
            placeholder="add a type, press enter" style={{ ...small, width:170, fontSize:12 }} />
        </div>
      </div>

      {/* deposit + turnaround */}
      <div style={{ display:'flex', gap:18, maxWidth:620 }}>
        <div style={{ flex:1 }}>
          <div style={lab}>Deposit to start</div>
          <div style={{ ...card, maxWidth:'none' }}>
            <div style={{ display:'flex', gap:6 }}>
              {['30','50','100'].map(d => (
                <button key={d} onClick={()=>setDeposit(d)} style={{ flex:1, fontFamily:c.f.body, fontSize:13, fontWeight:deposit===d?600:500, padding:'9px', borderRadius:8, cursor:'pointer', color:deposit===d?c.text:c.muted, background:deposit===d?c.bg:'transparent', border:`1px solid ${deposit===d?c.rule:'transparent'}` }}>{d}%</button>
              ))}
            </div>
            <div style={{ fontSize:11.5, color:c.muted, marginTop:9 }}>Clients pay {deposit}% up front, the rest when the piece is approved.</div>
          </div>
        </div>
        <div style={{ flex:1 }}>
          <div style={lab}>Typical turnaround</div>
          <div style={{ ...card, maxWidth:'none' }}>
            <input value={turnaround} onChange={e=>setTurnaround(e.target.value)} style={{ ...small, width:'100%' }} />
            <div style={{ fontSize:11.5, color:c.muted, marginTop:9 }}>Shown to clients as a gentle estimate, never a hard promise.</div>
          </div>
        </div>
      </div>

      {/* intake fields */}
      <div style={lab}>Your intake form asks for</div>
      <div style={{ ...card }}>
        {[['words','Their pet, in their words','Always on, the heart of the request',true],
          ['photo','A reference photo','Recommended for accurate work',false],
          ['timeline','Any timeline or deadline','Helps you plan the queue',false],
          ['budget','Budget range','Some artists prefer to quote instead',false]].map(([k,title,sub,locked]) => (
          <div key={k} style={{ display:'flex', alignItems:'center', justifyContent:'space-between', gap:12, padding:'10px 0', borderTop: k==='words'?'none':`1px solid ${c.rule}` }}>
            <div><div style={{ fontSize:13, color:c.text }}>{title}</div><div style={{ fontSize:11.5, color:c.muted, marginTop:1 }}>{sub}</div></div>
            {locked
              ? <span style={{ fontFamily:MONO, fontSize:9, letterSpacing:'0.08em', textTransform:'uppercase', color:c.faint }}>always</span>
              : <Toggle on={fields[k]} onClick={()=>setFields(p=>({...p,[k]:!p[k]}))} />}
          </div>
        ))}
      </div>

      <button onClick={()=>flash('Commission setup saved. Your public page and intake form are updated.')} style={{ ...btn(c), background:c.a, color:c.aOn, padding:'12px 20px', marginBottom:20 }}>Save changes</button>
    </div>
  );
}

/* ---- settings: payment settings ---- */
function PaymentSettingsPanel({ c, flash }) {
  const [autoRemind, setAutoRemind] = useState(true);
  const [firstDays, setFirstDays] = useState('3');
  const [repeatDays, setRepeatDays] = useState('7');
  const [currency, setCurrency] = useState('USD');
  const [copyInvoice, setCopyInvoice] = useState(true);
  const [showAddress, setShowAddress] = useState(false);

  const lab = { fontFamily:MONO, fontSize:9.5, letterSpacing:'0.12em', textTransform:'uppercase', color:c.faint, marginBottom:10 };
  const card = { background:c.surface, border:`1px solid ${c.rule}`, borderRadius:12, padding:'16px 18px', marginBottom:18, maxWidth:620 };
  const numIn = { fontFamily:MONO, fontSize:13, color:c.text, background:c.bg, border:`1px solid ${c.rule}`, borderRadius:7, padding:'5px 8px', width:42, textAlign:'center', outline:'none' };

  const Toggle = ({ on, onClick }) => (
    <div onClick={onClick} style={{ width:40, height:24, borderRadius:99, background:on?c.a:c.rule, position:'relative', cursor:'pointer', flexShrink:0 }}>
      <div style={{ position:'absolute', top:3, left:on?19:3, width:18, height:18, borderRadius:'50%', background:'#fff', boxShadow:'0 1px 2px rgba(0,0,0,0.25)' }} />
    </div>
  );
  const ToggleRow = ({ title, sub, on, onClick, first }) => (
    <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between', gap:12, padding:'11px 0', borderTop: first?'none':`1px solid ${c.rule}` }}>
      <div><div style={{ fontSize:13, color:c.text }}>{title}</div><div style={{ fontSize:11.5, color:c.muted, marginTop:1 }}>{sub}</div></div>
      <Toggle on={on} onClick={onClick} />
    </div>
  );

  return (
    <div style={{ flex:1, overflowY:'auto', padding:'22px 26px' }}>
      <div style={{ fontFamily:c.f.body, fontWeight:600, fontSize:24, letterSpacing:'-0.015em', color:c.text }}>Payment settings</div>
      <div style={{ fontSize:13.5, color:c.muted, marginTop:8, marginBottom:22, maxWidth:'52ch', lineHeight:1.55 }}>
        How you get paid, and how Mella chases balances for you so you never have to send an awkward email.
      </div>

      {/* stripe hero */}
      <div style={{ ...card, display:'flex', gap:15, alignItems:'flex-start' }}>
        <span style={{ width:42, height:42, borderRadius:10, background:c.a, flexShrink:0 }} />
        <div style={{ flex:1 }}>
          <div style={{ fontSize:14.5, fontWeight:600, color:c.text, display:'flex', alignItems:'center', gap:8 }}>Stripe <span style={{ fontFamily:MONO, fontSize:9.5, letterSpacing:'0.06em', textTransform:'uppercase', color:DONE, background:hexToRgba(DONE,c.dark?0.2:0.12), borderRadius:99, padding:'3px 8px' }}>Connected</span></div>
          <div style={{ fontSize:12.5, color:c.muted, marginTop:5, lineHeight:1.5 }}>Deposits and balances go straight to your bank. Mella never holds your money or takes a cut of your sales.</div>
        </div>
        <span style={{ ...miniBtn(c), padding:'8px 13px', whiteSpace:'nowrap' }}>Manage on Stripe</span>
      </div>

      {/* getting paid */}
      <div style={lab}>Getting paid</div>
      <div style={card}>
        <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between' }}>
          <div>
            <div style={{ fontSize:13, color:c.text }}>Currency</div>
            <div style={{ fontSize:12, color:c.muted, marginTop:2 }}>Shown to clients at checkout. Payouts are managed in Stripe.</div>
          </div>
          <select value={currency} onChange={e=>setCurrency(e.target.value)} style={{ fontFamily:c.f.body, fontSize:13, color:c.text, background:c.bg, border:`1px solid ${c.rule}`, borderRadius:7, padding:'7px 10px', outline:'none', cursor:'pointer' }}>
            {['USD','CAD','EUR','GBP','AUD'].map(x=> <option key={x}>{x}</option>)}
          </select>
        </div>
      </div>

      {/* automatic reminders — the dunning centerpiece */}
      <div style={lab}>Automatic balance reminders</div>
      <div style={card}>
        <ToggleRow first title="Chase unpaid balances for me" sub="Mella emails the client a gentle nudge so you do not have to" on={autoRemind} onClick={()=>setAutoRemind(v=>!v)} />
        {autoRemind && (
          <div style={{ marginTop:6, paddingTop:14, borderTop:`1px solid ${c.rule}` }}>
            <div style={{ fontSize:12, color:c.muted, marginBottom:9 }}>How soon after a balance is due should the first nudge go out?</div>
            <div style={{ display:'flex', gap:6 }}>
              {[['3','In 3 days'],['7','In a week'],['14','In 2 weeks']].map(([v,txt]) => {
                const on = firstDays===v;
                return <button key={v} onClick={()=>setFirstDays(v)} style={{ flex:1, fontFamily:c.f.body, fontSize:12.5, fontWeight:on?600:500, padding:'9px 6px', borderRadius:8, cursor:'pointer', color:on?c.text:c.muted, background:on?c.bg:'transparent', border:`1px solid ${on?c.rule:'transparent'}` }}>{txt}</button>;
              })}
            </div>
            <div style={{ fontSize:12, color:c.faint, marginTop:11, lineHeight:1.5 }}>Reminders stop the moment they pay. Send one yourself any time from Payments.</div>
          </div>
        )}
      </div>

      {/* invoices */}
      <div style={lab}>Invoices</div>
      <div style={card}>
        <ToggleRow first title="Email me a copy of every invoice" sub="A receipt for your own records" on={copyInvoice} onClick={()=>setCopyInvoice(v=>!v)} />
        <ToggleRow title="Show my studio address on invoices" sub="Some clients need this for their records" on={showAddress} onClick={()=>setShowAddress(v=>!v)} />
      </div>

      <button onClick={()=>flash('Payment settings saved.')} style={{ ...btn(c), background:c.a, color:c.aOn, padding:'12px 20px', marginBottom:20 }}>Save changes</button>
    </div>
  );
}

/* ---- settings: account ---- */
function AccountPanel({ c, flash, askConfirm }) {
  const [email, setEmail] = useState('iris@irissandoval.com');
  const [editingEmail, setEditingEmail] = useState(false);
  const [draftEmail, setDraftEmail] = useState('');
  const [notif, setNotif] = useState({ request:true, approve:true, paid:true, weekly:false });

  const lab = { fontFamily:MONO, fontSize:9.5, letterSpacing:'0.12em', textTransform:'uppercase', color:c.faint, marginBottom:10 };
  const card = { background:c.surface, border:`1px solid ${c.rule}`, borderRadius:12, padding:'16px 18px', marginBottom:18, maxWidth:620 };
  const field = (val, setVal, label, type) => (
    <label style={{ display:'block' }}>
      <span style={{ fontSize:12, color:c.muted, display:'block', marginBottom:5 }}>{label}</span>
      <input value={val} type={type||'text'} onChange={e=>setVal(e.target.value)}
        style={{ width:'100%', fontFamily:c.f.body, fontSize:13.5, color:c.text, background:c.bg, border:`1px solid ${c.rule}`, borderRadius:9, padding:'9px 11px', outline:'none' }} />
    </label>
  );
  const Toggle = ({ on, onClick }) => (
    <div onClick={onClick} style={{ width:40, height:24, borderRadius:99, background:on?c.a:c.rule, position:'relative', cursor:'pointer', flexShrink:0 }}>
      <div style={{ position:'absolute', top:3, left:on?19:3, width:18, height:18, borderRadius:'50%', background:'#fff', boxShadow:'0 1px 2px rgba(0,0,0,0.25)' }} />
    </div>
  );
  const NotifRow = ({ k, title, first }) => (
    <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between', gap:12, padding:'11px 0', borderTop: first?'none':`1px solid ${c.rule}` }}>
      <div style={{ fontSize:13, color:c.text }}>{title}</div>
      <Toggle on={notif[k]} onClick={()=>setNotif(p=>({...p,[k]:!p[k]}))} />
    </div>
  );

  return (
    <div style={{ flex:1, overflowY:'auto', padding:'22px 26px' }}>
      <div style={{ fontFamily:c.f.body, fontWeight:600, fontSize:24, letterSpacing:'-0.015em', color:c.text }}>Account</div>
      <div style={{ fontSize:13.5, color:c.muted, marginTop:8, marginBottom:22, maxWidth:'52ch', lineHeight:1.55 }}>
        Your details and how Mella reaches you. This is just for you, none of it shows on your public page.
      </div>

      {/* contact + sign in */}
      <div style={lab}>Account &amp; sign in</div>
      <div style={card}>
        <div style={{ display:'flex', alignItems:'flex-start', justifyContent:'space-between', gap:14, paddingBottom:14, borderBottom:`1px solid ${c.rule}` }}>
          <div style={{ flex:1, minWidth:0 }}>
            <div style={{ fontSize:13, color:c.text }}>Email</div>
            {editingEmail ? (
              <React.Fragment>
                <input autoFocus value={draftEmail} onChange={e=>setDraftEmail(e.target.value)} placeholder="new@email.com"
                  style={{ width:'100%', maxWidth:300, marginTop:7, fontFamily:c.f.body, fontSize:13.5, color:c.text, background:c.surface, border:`1px solid ${c.rule}`, borderRadius:9, padding:'9px 11px', outline:'none' }} />
                <div style={{ fontSize:11, color:c.faint, marginTop:6, lineHeight:1.5 }}>We'll email a confirmation link to the new address. Your login stays the same until you confirm.</div>
              </React.Fragment>
            ) : (
              <React.Fragment>
                <div style={{ fontFamily:MONO, fontSize:12, color:c.muted, marginTop:3 }}>{email}</div>
                <div style={{ fontSize:11, color:c.faint, marginTop:5, lineHeight:1.5 }}>Your login, and where Mella sends new requests and approvals.</div>
              </React.Fragment>
            )}
          </div>
          {editingEmail ? (
            <div style={{ display:'flex', gap:7, flexShrink:0 }}>
              <span onClick={()=>{ setEditingEmail(false); setDraftEmail(''); }} style={{ ...miniBtn(c), padding:'7px 11px' }}>Cancel</span>
              <span onClick={()=>{
                const next = draftEmail.trim();
                if (!next || !/.+@.+\..+/.test(next)) { flash('Enter a valid email address.'); return; }
                askConfirm({ title:'Send confirmation?', body:`We'll send a confirmation link to ${next}. Your login email changes only after you click it.`, label:'Send confirmation', onYes:()=>{ setEditingEmail(false); setDraftEmail(''); flash(`Confirmation link sent to ${next}.`); } });
              }} style={{ ...miniBtn(c, true), padding:'7px 11px', whiteSpace:'nowrap' }}>Send confirmation</span>
            </div>
          ) : (
            <span onClick={()=>{ setDraftEmail(email); setEditingEmail(true); }} style={{ ...miniBtn(c), padding:'7px 12px', whiteSpace:'nowrap', flexShrink:0 }}>Change email</span>
          )}
        </div>
        <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between', paddingTop:14 }}>
          <div>
            <div style={{ fontSize:13, color:c.text }}>Password</div>
            <div style={{ fontSize:12, color:c.muted, marginTop:2 }}>Last changed 4 months ago</div>
          </div>
          <span style={{ ...miniBtn(c), padding:'7px 12px' }}>Change password</span>
        </div>
      </div>

      {/* notifications */}
      <div style={lab}>Email me when</div>
      <div style={card}>
        <NotifRow first k="request" title="A new commission request comes in" />
        <NotifRow k="approve" title="A client approves a piece" />
        <NotifRow k="paid" title="A balance gets paid" />
        <NotifRow k="weekly" title="My weekly studio summary is ready" />
      </div>

      <div style={{ display:'flex', alignItems:'center', gap:16, maxWidth:620 }}>
        <button onClick={()=>flash('Account saved.')} style={{ ...btn(c), background:c.a, color:c.aOn, padding:'12px 20px' }}>Save changes</button>
        <span onClick={()=>flash('Signed out.')} style={{ fontSize:13, color:c.muted, cursor:'pointer' }}>Sign out</span>
        <span onClick={()=>askConfirm({ title:'Delete your account?', body:'This permanently removes your studio, public page, and client portals. This cannot be undone.', label:'Delete account', danger:true, onYes:()=>flash('Account scheduled for deletion.') })} style={{ fontSize:13, color:ATTN, cursor:'pointer', marginLeft:'auto', marginRight:4 }}>Delete account</span>
      </div>
      <div style={{ height:24 }} />
    </div>
  );
}

/* ---- settings: plan & billing ---- */
function PlanPanel({ c, flash, askConfirm }) {
  const [cycle, setCycle] = useState('monthly');
  const price = cycle==='monthly' ? '19' : '15';
  const lab = { fontFamily:MONO, fontSize:9.5, letterSpacing:'0.12em', textTransform:'uppercase', color:c.faint, marginBottom:10 };
  const card = { background:c.surface, border:`1px solid ${c.rule}`, borderRadius:12, padding:'16px 18px', marginBottom:18, maxWidth:620 };
  const included = ['Unlimited commissions and clients','Your public queue page','A private portal for every client','Stripe payments, deposits and balances','Automatic balance reminders'];

  return (
    <div style={{ flex:1, overflowY:'auto', padding:'22px 26px' }}>
      <div style={{ fontFamily:c.f.body, fontWeight:600, fontSize:24, letterSpacing:'-0.015em', color:c.text }}>Plan &amp; billing</div>
      <div style={{ fontSize:13.5, color:c.muted, marginTop:8, marginBottom:22, maxWidth:'52ch', lineHeight:1.55 }}>
        Your Mella subscription. One flat price, no fees on your sales, ever.
      </div>

      {/* current plan hero */}
      <div style={{ ...card, padding:0, overflow:'hidden' }}>
        <div style={{ padding:'18px 20px', display:'flex', alignItems:'flex-start', gap:16 }}>
          <div style={{ flex:1 }}>
            <div style={{ display:'flex', alignItems:'center', gap:9 }}>
              <span style={{ fontFamily:c.f.body, fontWeight:600, fontSize:19, letterSpacing:'-0.01em', color:c.text }}>Studio</span>
              <span style={{ fontFamily:MONO, fontSize:9, letterSpacing:'0.06em', textTransform:'uppercase', color:c.a, background:c.aSoft, borderRadius:99, padding:'3px 8px' }}>Current plan</span>
            </div>
            <div style={{ fontSize:12.5, color:c.muted, marginTop:6 }}>Renews August 1. You can cancel any time.</div>
          </div>
          <div style={{ textAlign:'right' }}>
            <div><span style={{ fontFamily:c.f.body, fontSize:30, fontWeight:600, color:c.text, letterSpacing:'-0.02em' }}>${price}</span><span style={{ fontSize:13, color:c.muted }}> / month</span></div>
            {cycle==='annual' && <div style={{ fontFamily:c.f.body, fontSize:10, color:DONE, marginTop:2 }}>billed $180 a year</div>}
          </div>
        </div>
        {/* cycle toggle */}
        <div style={{ display:'flex', gap:6, padding:'0 20px 16px' }}>
          {[['monthly','Monthly'],['annual','Annual, save 20%']].map(([v,txt]) => {
            const on = cycle===v;
            return <button key={v} onClick={()=>setCycle(v)} style={{ flex:1, fontFamily:c.f.body, fontSize:12.5, fontWeight:on?600:500, padding:'9px', borderRadius:8, cursor:'pointer', color:on?c.text:c.muted, background:on?c.bg:'transparent', border:`1px solid ${on?c.rule:'transparent'}` }}>{txt}</button>;
          })}
        </div>
        {/* included */}
        <div style={{ borderTop:`1px solid ${c.rule}`, background:c.sunk, padding:'14px 20px' }}>
          <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:'7px 16px' }}>
            {included.map(x => (
              <div key={x} style={{ fontSize:12.5, color:c.soft, display:'flex', alignItems:'flex-start', gap:8 }}>
                <span style={{ color:DONE, flexShrink:0 }}>✓</span>{x}
              </div>
            ))}
          </div>
        </div>
      </div>

      {/* payment method */}
      <div style={lab}>Payment method</div>
      {/* === STRIPE INTEGRATION POINT: subscription payment method ===
          This is Mella's OWN subscription billing (Stripe Billing on the platform
          account, NOT Connect). Do NOT collect card data here. Use Stripe Billing's
          hosted "Customer Portal" or a Setup Element. The "Update" button should
          launch the Stripe Customer Portal session. See STRIPE-HANDOFF.md → "Plan & billing". */}
      <div data-stripe-slot="subscription-card" style={card}>
        <div style={{ display:'flex', alignItems:'center', gap:12 }}>
          <span style={{ width:34, height:22, borderRadius:4, background:c.a, opacity:0.85, flexShrink:0 }} />
          <div style={{ flex:1 }}>
            <div style={{ fontSize:13, color:c.text, fontFamily:MONO }}>Visa ending 4242</div>
            <div style={{ fontSize:11.5, color:c.muted, marginTop:1 }}>Expires 09 / 27</div>
          </div>
          <span data-stripe-action="open-billing-portal" style={{ ...miniBtn(c), padding:'7px 12px' }}>Manage in Stripe</span>
        </div>
        <div style={{ fontSize:11, color:c.faint, marginTop:11, lineHeight:1.5, paddingTop:11, borderTop:`1px solid ${c.rule}` }}>Billing is handled securely by Stripe. We never store your card on Mella.</div>
      </div>

      {/* billing history */}
      <div style={lab}>Billing history</div>
      <div style={{ border:`1px solid ${c.rule}`, borderRadius:12, overflow:'hidden', maxWidth:620, marginBottom:20 }}>
        {[['Jul 1, 2026','$19.00'],['Jun 1, 2026','$19.00'],['May 1, 2026','$19.00']].map((r,i) => (
          <div key={i} style={{ display:'flex', alignItems:'center', gap:12, padding:'11px 16px', background:c.surface, borderTop: i?`1px solid ${c.rule}`:'none' }}>
            <span style={{ fontSize:13, color:c.text, flex:1 }}>{r[0]}</span>
            <span style={{ fontFamily:MONO, fontSize:9, letterSpacing:'0.04em', textTransform:'uppercase', color:DONE, background:hexToRgba(DONE,c.dark?0.2:0.11), borderRadius:99, padding:'3px 8px' }}>Paid</span>
            <span style={{ fontFamily:MONO, fontSize:13, color:c.text, width:64, textAlign:'right' }}>{r[1]}</span>
            <span style={{ fontFamily:MONO, fontSize:11, color:c.a, cursor:'pointer', width:60, textAlign:'right' }}>Receipt</span>
          </div>
        ))}
      </div>

      <div style={{ display:'flex', alignItems:'center', gap:16, maxWidth:620 }}>
        <button onClick={()=>flash('Plan updated.')} style={{ ...btn(c), background:c.a, color:c.aOn, padding:'12px 20px' }}>Save changes</button>
        <span onClick={()=>askConfirm({ title:'Cancel your plan?', body:'Your public page and client portals stay live until the end of this billing period, then go offline.', label:'Cancel plan', danger:true, onYes:()=>flash('Plan cancelled. Active until Aug 1.') })} style={{ fontSize:13, color:c.muted, cursor:'pointer', marginLeft:'auto', marginRight:4 }}>Cancel plan</span>
      </div>
      <div style={{ height:24 }} />
    </div>
  );
}

/* ---- stub (future phases) ---- */
function Stub({ c, label }) {
  return (
    <div style={{ height:'90%', display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center', textAlign:'center', color:c.faint }}>
      <div style={{ width:46, height:46, borderRadius:12, border:`1.5px dashed ${c.rule}`, display:'flex', alignItems:'center', justifyContent:'center', fontSize:20, color:hexToRgba(c.a,0.7), marginBottom:14 }}>◌</div>
      <div style={{ fontFamily:c.f.head, fontWeight:c.f.hw, fontSize:22, color:c.text, textTransform:'capitalize' }}>{label}</div>
      <div style={{ fontSize:13, marginTop:5, maxWidth:'34ch', lineHeight:1.5 }}>We build this in the next pass. {label==='settings' ? 'It is where the public-page customizer and your tiers will live.' : 'The ledger, deposits, and balance reminders.'}</div>
    </div>
  );
}

/* ---- shared pieces ---- */
function Stat({ c, label, value, dot, mono }) {
  return (
    <div style={{ flex:1, background:c.surface, border:`1px solid ${c.rule}`, borderRadius:12, padding:'13px 15px' }}>
      <div style={{ fontFamily:MONO, fontSize:9, letterSpacing:'0.1em', textTransform:'uppercase', color:c.faint, display:'flex', alignItems:'center', gap:6 }}>
        {dot && <span style={{ width:6, height:6, borderRadius:'50%', background:dot }} />}{label}
      </div>
      <div style={{ fontFamily: mono ? MONO : c.f.body, fontSize: mono?20:24, fontWeight:600, color:c.text, marginTop:6, letterSpacing: mono?0:'-0.01em' }}>{value}</div>
    </div>
  );
}
/* ---- confirm dialog ---- */
function Confirm({ c, title, body, label, danger, onYes, onClose }) {
  const accent = danger ? ATTN : c.a;
  return (
    <div onClick={onClose} style={{ position:'absolute', inset:0, zIndex:70, background:hexToRgba('#000',0.42), display:'flex', alignItems:'center', justifyContent:'center', animation:'mq-fade .15s ease' }}>
      <div onClick={e=>e.stopPropagation()} style={{ width:380, background:c.bg, borderRadius:14, padding:'22px 24px', boxShadow:'0 40px 90px -30px rgba(0,0,0,0.6)' }}>
        <div style={{ fontSize:18, fontWeight:600, letterSpacing:'-0.01em', color:c.text }}>{title}</div>
        <p style={{ fontSize:13.5, color:c.muted, lineHeight:1.55, margin:'9px 0 20px' }}>{body}</p>
        <div style={{ display:'flex', gap:10, justifyContent:'flex-end' }}>
          <button onClick={onClose} style={{ ...btn(c), background:'transparent', color:c.muted, border:`1px solid ${c.rule}` }}>Cancel</button>
          <button onClick={() => { onYes(); onClose(); }} style={{ ...btn(c), background:accent, color:'#fff' }}>{label}</button>
        </div>
      </div>
    </div>
  );
}

/* ---- confirm dialog end ---- */
function Card({ c, co, tint, colKey, dragging, onDragStart, onDragEnd, onClick }) {
  const balColor = co.bal==='paid' ? DONE : (colKey==='await' ? ATTN : (co.dep==='sent' ? MONEYC : c.faint));
  const balText  = co.bal==='paid' ? 'Paid' : (colKey==='await' ? 'Balance due' : (co.dep==='sent' ? 'Deposit sent' : 'Deposit paid'));
  return (
    <div draggable onDragStart={onDragStart} onDragEnd={onDragEnd} onClick={onClick} style={{
      background:c.surface, border:`1px solid ${c.rule}`, borderLeft:`3px solid ${tint}`, borderRadius:9,
      padding:'10px 11px', cursor:'pointer', boxShadow:`0 1px 2px ${hexToRgba('#000',c.dark?0.3:0.03)}`,
      opacity: dragging ? 0.4 : 1, transition:'opacity .15s',
    }}>
      <div style={{ display:'flex', gap:10 }}>
        <Painting {...pieceImg(co, colKey==='done')} radius={6} style={{ width:42, height:42, flexShrink:0 }} />
        <div style={{ flex:1, minWidth:0 }}>
          <div style={{ display:'flex', justifyContent:'space-between', alignItems:'baseline', gap:6 }}>
            <span style={{ fontSize:13.5, fontWeight:600, color:c.text, whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>{co.name}</span>
            <span style={{ fontFamily:c.f.body, fontWeight:600, fontSize:10, color:c.faint, flexShrink:0 }}>${co.total}</span>
          </div>
          <div style={{ fontFamily:MONO, fontSize:9.5, color:c.faint, marginTop:2, whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>{co.spec}</div>
        </div>
      </div>
      <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between', marginTop:9 }}>
        <span style={{ fontFamily:MONO, fontSize:9, letterSpacing:'0.04em', textTransform:'uppercase', color:balColor, background:hexToRgba(balColor,c.dark?0.2:0.11), borderRadius:99, padding:'3px 7px' }}>{balText}</span>
        <span style={{ fontFamily:MONO, fontSize:10.5, color:c.muted }}>{co.due}</span>
      </div>
    </div>
  );
}
function OpenSlot({ c, onClick }) {
  return (
    <div onClick={onClick} title="Add a commission" style={{ border:`1.5px dashed ${c.rule}`, borderRadius:9, padding:'14px 11px', textAlign:'center', color:c.faint, fontSize:11.5, cursor:'pointer' }}>
      <div style={{ fontSize:16, color:hexToRgba(c.a,0.6), lineHeight:1 }}>+</div>
      <div style={{ marginTop:4 }}>add a commission</div>
    </div>
  );
}
function AddCommission({ c, onClose, onAdd }) {
  const [d, setD] = useState({ name:'', client:'', spec:'', price:'', stage:'wip', depPaid:true });
  const set = (k,v) => setD(p => ({ ...p, [k]:v }));
  const inp2 = { width:'100%', fontFamily:c.f.body, fontSize:13.5, color:c.text, background:c.bg, border:`1px solid ${c.rule}`, borderRadius:9, padding:'9px 11px', outline:'none' };
  const lab2 = { fontSize:12, color:c.muted, display:'block', marginBottom:5 };
  const ok = d.name.trim() && d.client.trim();
  return (
    <div onClick={onClose} style={{ position:'absolute', inset:0, zIndex:55, background:hexToRgba('#000',0.4), display:'flex', alignItems:'center', justifyContent:'center', animation:'mq-fade .18s ease' }}>
      <div onClick={e=>e.stopPropagation()} style={{ width:440, background:c.bg, borderRadius:16, overflow:'hidden', boxShadow:'0 40px 90px -30px rgba(0,0,0,0.6)' }}>
        <div style={{ padding:'18px 22px 0' }}>
          <div style={{ fontFamily:MONO, fontSize:10, letterSpacing:'0.14em', textTransform:'uppercase', color:c.a }}>Add a commission</div>
          <h3 style={{ fontFamily:c.f.body, fontWeight:600, fontSize:21, letterSpacing:'-0.01em', color:c.text, margin:'8px 0 0' }}>Fill this slot</h3>
          <p style={{ fontSize:12.5, color:c.muted, margin:'6px 0 0', lineHeight:1.5 }}>Adding work you already have going? Drop it in here so your board and public page stay accurate. You can add progress photos from the card once it is on the board.</p>
        </div>
        <div style={{ padding:'18px 22px' }}>
          <div style={{ display:'flex', gap:10, marginBottom:12 }}>
            <label style={{ flex:1 }}><span style={lab2}>Piece</span><input value={d.name} placeholder="Maple" onChange={e=>set('name',e.target.value)} style={inp2} /></label>
            <label style={{ flex:1 }}><span style={lab2}>Client</span><input value={d.client} placeholder="Sarah M." onChange={e=>set('client',e.target.value)} style={inp2} /></label>
          </div>
          <div style={{ display:'flex', gap:10, marginBottom:12 }}>
            <label style={{ flex:2 }}><span style={lab2}>What it is</span><input value={d.spec} placeholder="Saint Bernard, 11x14" onChange={e=>set('spec',e.target.value)} style={inp2} /></label>
            <label style={{ flex:1 }}><span style={lab2}>Price</span><input value={d.price} placeholder="260" onChange={e=>set('price',e.target.value.replace(/[^0-9]/g,''))} style={{ ...inp2, fontFamily:MONO }} /></label>
          </div>
          <span style={lab2}>Reference photos</span>
          <div style={{ border:`1.5px dashed ${c.rule}`, borderRadius:10, padding:'14px', textAlign:'center', color:c.faint, fontSize:12.5, background:c.sunk, cursor:'pointer', marginBottom:14 }}>
            <span style={{ color:c.a, fontSize:16, display:'block', marginBottom:3 }}>↑</span>
            Drag the client's photos here, or tap to add
          </div>
          <span style={lab2}>Where is it now?</span>
          <div style={{ display:'flex', gap:6, marginBottom:14 }}>
            {[['wip','In progress'],['await','Awaiting client'],['done','Done']].map(([v,txt]) => {
              const on = d.stage===v;
              return <button key={v} onClick={()=>set('stage',v)} style={{ flex:1, fontFamily:c.f.body, fontSize:12, fontWeight:on?600:500, padding:'9px 6px', borderRadius:8, cursor:'pointer', color:on?c.text:c.muted, background:on?c.surface:'transparent', border:`1px solid ${on?c.rule:'transparent'}` }}>{txt}</button>;
            })}
          </div>
          <div onClick={()=>set('depPaid',!d.depPaid)} style={{ display:'flex', alignItems:'center', justifyContent:'space-between', cursor:'pointer', marginBottom:18 }}>
            <span style={{ fontSize:13, color:c.text }}>Deposit already paid</span>
            <div style={{ width:40, height:24, borderRadius:99, background:d.depPaid?c.a:c.rule, position:'relative' }}>
              <div style={{ position:'absolute', top:3, left:d.depPaid?19:3, width:18, height:18, borderRadius:'50%', background:'#fff', boxShadow:'0 1px 2px rgba(0,0,0,0.25)' }} />
            </div>
          </div>
          <div style={{ display:'flex', gap:10 }}>
            <button onClick={onClose} style={{ ...btn(c), flex:1, background:'transparent', color:c.muted, border:`1px solid ${c.rule}` }}>Cancel</button>
            <button onClick={()=> ok && onAdd(d)} style={{ ...btn(c), flex:2, background: ok?c.a:c.rule, color: ok?c.aOn:c.faint, cursor: ok?'pointer':'default' }}>Add to board</button>
          </div>
        </div>
      </div>
    </div>
  );
}
function RailCard({ c, title, children }) {
  return (
    <div>
      <div style={{ fontFamily:MONO, fontSize:9.5, letterSpacing:'0.14em', textTransform:'uppercase', color:c.faint, fontWeight:500, marginBottom:10 }}>{title}</div>
      <div style={{ background:c.surface, border:`1px solid ${c.rule}`, borderRadius:12, padding:'14px 15px' }}>{children}</div>
    </div>
  );
}
function Row({ c, label, value }) {
  return (
    <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', fontSize:12.5, padding:'5px 0', color:c.muted }}>
      <span>{label}</span><span style={{ color:c.text }}>{value}</span>
    </div>
  );
}
const miniBtn = (c, solid) => ({
  fontFamily:c.f.body, fontSize:11.5, fontWeight:500, padding:'7px 11px', borderRadius:7, cursor:'pointer', textAlign:'center', textDecoration:'none',
  color: solid?c.aOn:c.text, background: solid?c.a:'transparent', border: solid?'none':`1px solid ${c.rule}`,
});
const btn = (c) => ({ fontFamily:c.f.body, fontSize:13, fontWeight:600, padding:'10px 16px', borderRadius:9, cursor:'pointer', border:'none' });

/* ---- post an update (artist composes a WIP) ---- */
const STAGE_CAPTIONS = {
  'Sketch': "Here's the first drawing. Take a look at the pose and proportions before I commit to paint.",
  'Underpainting': "Blocking in the colors and the warm light now. She's starting to come through.",
  'Final': "She's finished. Take your time, and approve when she feels right to you.",
  'Custom': "",
};
function PostUpdate({ c, co, onClose, onPost }) {
  const [stage, setStage] = React.useState('Underpainting');
  const [custom, setCustom] = React.useState('');
  const [caption, setCaption] = React.useState('');
  const [touched, setTouched] = React.useState(false);
  const [photos, setPhotos] = React.useState(1);
  const [isFinal, setIsFinal] = React.useState(false);
  const pickStage = (s) => {
    setStage(s);
    setIsFinal(s === 'Final');
  };
  const label = stage === 'Custom' ? (custom.trim() || 'Update') : stage;
  return (
    <div onClick={onClose} style={{ position:'absolute', inset:0, zIndex:60, background:hexToRgba('#000',0.4), display:'flex', alignItems:'center', justifyContent:'center', animation:'mq-fade .18s ease' }}>
      <div onClick={e=>e.stopPropagation()} style={{ width:460, maxHeight:'90%', overflowY:'auto', background:c.bg, borderRadius:16, boxShadow:'0 40px 90px -30px rgba(0,0,0,0.6)' }}>
        <div style={{ padding:'18px 22px 0', display:'flex', alignItems:'flex-start', gap:12 }}>
          <Painting pal={FILLER} radius={8} style={{ width:40, height:40, flexShrink:0 }} />
          <div style={{ flex:1 }}>
            <div style={{ fontFamily:MONO, fontSize:10, letterSpacing:'0.14em', textTransform:'uppercase', color:c.a }}>Post an update</div>
            <h3 style={{ fontSize:19, fontWeight:600, color:c.text, margin:'4px 0 0' }}>{co.name}</h3>
          </div>
          <span onClick={onClose} style={{ cursor:'pointer', color:c.faint, fontSize:20, lineHeight:1 }}>×</span>
        </div>

        <div style={{ padding:'16px 22px 22px' }}>
          {/* stage */}
          <div style={{ fontSize:12, color:c.muted, marginBottom:6 }}>Which stage is this?</div>
          <div style={{ display:'flex', gap:6, marginBottom: stage==='Custom'?9:16 }}>
            {['Sketch','Underpainting','Final','Custom'].map(s => {
              const on = stage===s;
              return <button key={s} onClick={()=>pickStage(s)} style={{ flex:1, fontFamily:c.f.body, fontSize:11.5, fontWeight:on?600:500, padding:'9px 4px', borderRadius:8, cursor:'pointer', color:on?c.text:c.muted, background:on?c.surface:'transparent', border:`1px solid ${on?c.rule:'transparent'}` }}>{s}</button>;
            })}
          </div>
          {stage==='Custom' && (
            <input autoFocus value={custom} onChange={e=>setCustom(e.target.value)} placeholder="Name this stage (e.g. Detail pass)"
              style={{ width:'100%', marginBottom:16, fontFamily:c.f.body, fontSize:13.5, color:c.text, background:c.surface, border:`1px solid ${c.rule}`, borderRadius:9, padding:'9px 11px', outline:'none' }} />
          )}

          {/* photos */}
          <div style={{ fontSize:12, color:c.muted, marginBottom:6 }}>Photos <span style={{ color:c.faint }}>add a few detail shots if you like</span></div>
          <div style={{ display:'flex', gap:8, marginBottom:16 }}>
            {Array.from({length:photos}).map((_,i)=>(
              <div key={i} style={{ position:'relative' }}>
                <Painting pal={FILLER} radius={8} style={{ width:64, height:64 }} />
                <span onClick={()=>setPhotos(p=>Math.max(1,p-1))} style={{ position:'absolute', top:3, right:3, width:16, height:16, borderRadius:'50%', background:hexToRgba('#000',0.5), color:'#fff', fontSize:10, lineHeight:'16px', textAlign:'center', cursor:'pointer' }}>×</span>
              </div>
            ))}
            <div onClick={()=>setPhotos(p=>p+1)} style={{ width:64, height:64, borderRadius:8, border:`1.5px dashed ${c.rule}`, background:c.sunk, display:'flex', alignItems:'center', justifyContent:'center', color:c.a, fontSize:20, cursor:'pointer' }}>+</div>
          </div>

          {/* caption */}
          <div style={{ fontSize:12, color:c.muted, marginBottom:6 }}>A note to go with it</div>
          <textarea value={caption} onChange={e=>{ setCaption(e.target.value); setTouched(true); }} rows={3} placeholder={`A line for ${co.client.split(' ')[0]} about this stage…`}
            style={{ width:'100%', fontFamily:c.f.body, fontSize:13.5, color:c.text, background:c.surface, border:`1px solid ${c.rule}`, borderRadius:9, padding:'10px 12px', outline:'none', resize:'vertical', lineHeight:1.5 }} />

          {/* is this the final */}
          <div onClick={()=>setIsFinal(v=>!v)} style={{ display:'flex', alignItems:'center', justifyContent:'space-between', cursor:'pointer', marginTop:16, padding:'12px 14px', background: isFinal?c.aSoft:c.sunk, border:`1px solid ${isFinal?hexToRgba(c.a,0.3):c.rule}`, borderRadius:10 }}>
            <div style={{ paddingRight:12 }}>
              <div style={{ fontSize:13, color:c.text, fontWeight:600 }}>This is the finished piece</div>
              <div style={{ fontSize:11.5, color:c.muted, marginTop:1, lineHeight:1.4 }}>Approving it asks {co.client.split(' ')[0]} for the balance, then it ships.</div>
            </div>
            <div style={{ width:40, height:24, borderRadius:99, background:isFinal?c.a:c.rule, position:'relative', flexShrink:0 }}>
              <div style={{ position:'absolute', top:3, left:isFinal?19:3, width:18, height:18, borderRadius:'50%', background:'#fff', boxShadow:'0 1px 2px rgba(0,0,0,0.25)' }} />
            </div>
          </div>

          {/* post */}
          <div style={{ display:'flex', gap:10, marginTop:18 }}>
            <button onClick={onClose} style={{ ...btn(c), flex:1, background:'transparent', color:c.muted, border:`1px solid ${c.rule}` }}>Cancel</button>
            <button onClick={()=>onPost({ stage: label, photos, caption, isFinal })} style={{ ...btn(c), flex:2, background:c.a, color:c.aOn }}>Post to {co.client.split(' ')[0]}'s page</button>
          </div>
        </div>
      </div>
    </div>
  );
}

/* ---- detail drawer ---- */
function stagesFor(colKey) {
  if (colKey==='done')  return [{s:'Sketch',d:'Jun 24',st:'approved'},{s:'Underpainting',d:'Jul 2',st:'approved'},{s:'Final',d:'Jul 9',st:'approved'}];
  if (colKey==='await') return [{s:'Sketch',d:'Jun 24',st:'approved'},{s:'Underpainting',d:'Jul 2',st:'approved'},{s:'Final',d:'Jul 9',st:'awaiting'}];
  if (colKey==='wip')   return [{s:'Sketch',d:'Jun 24',st:'approved'},{s:'Underpainting',d:'Jul 2',st:'wip'}];
  return [];
}
function activityFor(colKey, co, fn) {
  const dep = {t:'Deposit paid', sub:'$'+(co.total/2), d:'Jun 24'};
  const acc = {t:'Request accepted', d:'Jun 22'};
  if (colKey==='done')  return [{t:'Marked shipped', d: co.due.replace('shipped ','').replace('ships ','')},{t:'Balance paid', sub:'$'+(co.total/2), d:'Jul 11'},{t:`Final approved by ${fn}`, d:'Jul 10'},{t:'Final posted', d:'Jul 9'}, dep, acc];
  if (colKey==='await') return [{t:`Final posted, waiting on ${fn}`, d:'Jul 9'},{t:'Underpainting approved', d:'Jul 3'},{t:'Underpainting posted', d:'Jul 2'},{t:'Sketch approved', d:'Jun 25'}, dep, acc];
  if (colKey==='wip')   return [{t:'Underpainting posted', d:'Jul 2'},{t:`Sketch approved by ${fn}`, d:'Jun 25'},{t:'Sketch posted', d:'Jun 24'}, dep, acc];
  return [dep, acc];
}
function actDot(t, c) {
  if (/paid|approved|shipped/.test(t)) return DONE;
  if (/waiting/.test(t)) return ATTN;
  if (/accepted/.test(t)) return c.faint;
  return c.a;
}
const NOTES_BY = {
  maple:   [{ stage:'Sketch', date:'Jun 25', text:'Could her head tilt up just a little? Otherwise I love how she\u2019s looking.' }],
  juniper: [{ stage:'Underpainting', date:'Jul 3', text:'The grey and tan in her coat is perfect. Could her scruffy beard be a touch fuller?' }],
};
const MSGS_BY = {
  maple:   [{ from:'client', date:'Jun 21', text:'She used to rest her chin on my knee every evening. That gentle look is what I\u2019d most love to see.' },
             { from:'iris', date:'Jun 22', text:'That\u2019s exactly the kind of detail that makes a portrait feel like her.' }],
  otto:    [{ from:'client', date:'Jul 9', text:'Just wanted to say thank you. This means a lot to our family.' }],
};
function DetailDrawer({ c, co, colKey, onClose, onUpload }) {
  const fn = co.client.split(' ')[0];
  const STATUS = { wip:['In progress', c.a], await:['Awaiting client', ATTN], done:['Done', DONE], open:['Open', c.faint] };
  const status = STATUS[colKey] || STATUS.wip;
  const stages = stagesFor(colKey);
  const activity = activityFor(colKey, co, fn);
  const active = colKey==='wip' || colKey==='await';
  const shippedAlready = colKey==='done' && /shipped/.test(co.due);
  const readyToShip = colKey==='done' && /ships/.test(co.due);
  const [ship, setShip] = React.useState({ done:false, tracking:'' });
  React.useEffect(() => { setShip({ done:false, tracking:'' }); }, [co.id]);
  const brief = co.brief || (co.req ? { subject:co.req.subject, email:co.req.email, refs:co.req.refs, note:co.req.note } : null);
  const refPhotos = brief ? brief.refs.filter(r => r && r.src) : [];   // real reference photos only
  const [viewer, setViewer] = React.useState(false);                   // header photo -> lightbox
  const [handled, setHandled] = React.useState({});
  const clientNotes = NOTES_BY[co.id] || [];
  const [thread, setThread] = React.useState(MSGS_BY[co.id] || []);
  const [reply, setReply] = React.useState('');
  React.useEffect(() => { setThread(MSGS_BY[co.id] || []); setReply(''); setViewer(false); }, [co.id]);
  const sendReply = () => { const t = reply.trim(); if (!t) return; setThread(m => [...m, { from:'iris', date:'just now', text:t }]); setReply(''); };
  const stState = { approved:['✓ approved', DONE], awaiting:['awaiting client', ATTN], wip:['in progress', c.a] };

  return (
    <div onClick={onClose} style={{ position:'absolute', inset:0, zIndex:50, background:hexToRgba('#000',0.34), display:'flex', justifyContent:'flex-end', animation:'mq-fade .18s ease' }}>
      {viewer && refPhotos.length > 0 && (
        <div onClick={(e) => { e.stopPropagation(); setViewer(false); }} style={{ position:'absolute', inset:0, zIndex:60, background:hexToRgba('#000',0.82), display:'flex', alignItems:'center', justifyContent:'center', gap:14, padding:'5vh 5vw', cursor:'zoom-out', animation:'mq-fade .18s ease' }}>
          {refPhotos.map((r,i) => <Painting key={i} {...refProps(r)} radius={12} style={{ width: refPhotos.length>1 ? `${Math.floor(86/refPhotos.length)}vw` : 'min(92vw, 560px)', aspectRatio:String((r && r.ratio) || 0.75), maxHeight:'88vh' }} />)}
        </div>
      )}
      <div onClick={e=>e.stopPropagation()} style={{ width:430, height:'100%', background:c.bg, borderLeft:`1px solid ${c.rule}`, overflowY:'auto', animation:'mq-slidein .26s cubic-bezier(.2,.8,.2,1)' }}>
        {/* header */}
        <div style={{ padding:'18px 22px', borderBottom:`1px solid ${c.rule}`, display:'flex', alignItems:'flex-start', gap:14 }}>
          <div onClick={() => refPhotos.length && setViewer(true)} title={refPhotos.length ? 'View reference photo' : undefined}
               style={{ flexShrink:0, lineHeight:0, borderRadius:8, cursor: refPhotos.length ? 'zoom-in' : 'default' }}>
            <Painting {...pieceImg(co, colKey==='done')} radius={8} style={{ width:52, height:52 }} />
          </div>
          <div style={{ flex:1, minWidth:0 }}>
            <div style={{ fontFamily:c.f.body, fontWeight:600, fontSize:21, letterSpacing:'-0.01em', color:c.text }}>{co.name}</div>
            <div style={{ fontFamily:MONO, fontSize:10, color:c.faint, marginTop:2 }}>{co.spec}</div>
            <div style={{ display:'flex', alignItems:'center', gap:9, marginTop:8 }}>
              <span style={{ fontFamily:MONO, fontSize:9, letterSpacing:'0.06em', textTransform:'uppercase', color:status[1], background:hexToRgba(status[1], c.dark?0.2:0.12), borderRadius:99, padding:'3px 8px' }}>{status[0]}</span>
              <span style={{ fontFamily:MONO, fontSize:10.5, color:c.faint }}>{active ? `due ${co.due}` : co.due}</span>
            </div>
          </div>
          <span onClick={onClose} style={{ cursor:'pointer', color:c.faint, fontSize:20, lineHeight:1 }}>×</span>
        </div>

        <div style={{ padding:'20px 22px' }}>
          {/* the brief — tap the header photo to enlarge */}
          {brief && (
            <React.Fragment>
              <div style={lbl(c)}>The request</div>
              <div style={{ background:c.surface, border:`1px solid ${c.rule}`, borderRadius:10, padding:'14px', marginBottom:18 }}>
                <p style={{ margin:0, fontSize:13.5, lineHeight:1.5, color:c.soft }}>{brief.note}</p>
                <div style={{ marginTop:11, paddingTop:11, borderTop:`1px solid ${c.rule}`, display:'flex', alignItems:'center', gap:14, fontFamily:MONO, fontSize:10.5 }}>
                  <span style={{ color:c.faint }}>{brief.subject}</span>
                  <span style={{ color:c.a, marginLeft:'auto' }}>{brief.email}</span>
                </div>
              </div>
            </React.Fragment>
          )}

          {/* work in progress */}
          {stages.length > 0 && (
            <React.Fragment>
              <div style={{ ...lbl(c), display:'flex', justifyContent:'space-between' }}><span>Work in progress</span><span style={{ color:c.faint }}>client sees these</span></div>
              <div style={{ display:'flex', gap:9, marginBottom:12 }}>
                {stages.map((s,i) => {
                  const ss = stState[s.st];
                  return (
                    <div key={i} style={{ flex:1 }}>
                      <Painting {...refProps(co.wip && co.wip[s.s] ? co.wip[s.s] : co.pal)} radius={7} style={{ width:'100%', height:72 }} />
                      <div style={{ fontSize:10.5, color:c.text, marginTop:5, fontWeight:600 }}>{s.s}</div>
                      <div style={{ fontFamily:MONO, fontSize:8.5, color:ss[1] }}>{ss[0]}</div>
                    </div>
                  );
                })}
              </div>
            </React.Fragment>
          )}
          {active ? (
            <div onClick={() => onUpload(co)} style={{ border:`1.5px dashed ${c.rule}`, borderRadius:10, padding:'15px', textAlign:'center', color:c.faint, fontSize:12.5, background:c.sunk, cursor:'pointer', marginBottom:18 }}>
              <span style={{ color:c.a, fontSize:17, display:'block', marginBottom:3 }}>↑</span>
              Post the next update, {fn} gets a gentle heads-up
            </div>
          ) : shippedAlready || (readyToShip && ship.done) ? (
            <div style={{ borderRadius:10, padding:'13px 15px', fontSize:12.5, background:c.sunk, color:DONE, marginBottom:18, display:'flex', alignItems:'center', gap:9 }}>
              <span style={{ fontSize:14 }}>✓</span>
              <div>
                <div style={{ fontWeight:600 }}>Shipped{shippedAlready ? ' ' + co.due.replace('shipped ','') : ' just now'}</div>
                <div style={{ fontFamily:MONO, fontSize:10.5, color:c.muted, marginTop:2 }}>Tracking {shippedAlready ? '1Z 999 AA1 01' : (ship.tracking.trim() || 'added')}</div>
              </div>
            </div>
          ) : readyToShip ? (
            <div style={{ border:`1px solid ${hexToRgba(c.a,0.35)}`, background:c.aSoft, borderRadius:11, padding:'15px', marginBottom:18 }}>
              <div style={{ fontWeight:600, fontSize:14, color:c.text }}>Paid and ready to ship</div>
              <div style={{ fontSize:12, color:c.muted, marginTop:2, lineHeight:1.45 }}>{fn} has paid in full. Add a tracking number and let them know she's on her way.</div>
              <input value={ship.tracking} onChange={e=>setShip(s=>({ ...s, tracking:e.target.value }))} placeholder="Tracking number"
                style={{ width:'100%', marginTop:11, fontFamily:MONO, fontSize:12.5, color:c.text, background:c.bg, border:`1px solid ${c.rule}`, borderRadius:9, padding:'9px 11px', outline:'none' }} />
              <button onClick={() => setShip(s=>({ ...s, done:true }))} style={{ ...btn(c), width:'100%', marginTop:10, background:c.a, color:c.aOn }}>Mark as shipped</button>
              <div style={{ fontSize:11, color:c.faint, textAlign:'center', marginTop:8 }}>{fn}'s page updates to “Shipped” with the tracking link.</div>
            </div>
          ) : (
            <div style={{ borderRadius:10, padding:'13px', textAlign:'center', fontSize:12.5, background:c.sunk, color:DONE, marginBottom:18 }}>✓ Delivered, all photos approved</div>
          )}

          {/* === stream 1: client notes (the review action queue) === */}
          <div style={{ ...lbl(c), display:'flex', justifyContent:'space-between' }}><span>Client notes</span><span style={{ color:c.faint }}>feedback to act on</span></div>
          {clientNotes.length === 0 ? (
            <div style={{ fontSize:12.5, color:c.faint, marginBottom:18, padding:'2px 0' }}>No change requests on this piece.</div>
          ) : (
            <div style={{ display:'flex', flexDirection:'column', gap:9, marginBottom:18 }}>
              {clientNotes.map((n,i) => {
                const done = handled[co.id+i];
                return (
                  <div key={i} style={{ background:c.surface, border:`1px solid ${done ? c.rule : hexToRgba(ATTN,0.4)}`, borderRadius:10, padding:'11px 13px', opacity: done?0.6:1 }}>
                    <div style={{ display:'flex', alignItems:'center', gap:7, marginBottom:6 }}>
                      <span style={{ fontFamily:MONO, fontSize:8.5, letterSpacing:'0.06em', textTransform:'uppercase', color: done?c.faint:ATTN, background:hexToRgba(done?c.faint:ATTN, 0.12), borderRadius:99, padding:'2px 7px' }}>on the {n.stage}</span>
                      <span style={{ fontFamily:MONO, fontSize:9.5, color:c.faint }}>{n.date}</span>
                    </div>
                    <p style={{ margin:0, fontSize:13, lineHeight:1.5, color: done?c.muted:c.text }}>{n.text}</p>
                    <div onClick={()=>setHandled(p=>({ ...p, [co.id+i]: !p[co.id+i] }))} style={{ marginTop:9, display:'flex', alignItems:'center', gap:7, cursor:'pointer', fontSize:11.5, color: done?DONE:c.a, fontWeight:600 }}>
                      <span style={{ width:15, height:15, borderRadius:4, border:`1.5px solid ${done?DONE:c.rule}`, background: done?DONE:'transparent', color:'#fff', display:'flex', alignItems:'center', justifyContent:'center', fontSize:10 }}>{done?'✓':''}</span>
                      {done ? 'Addressed' : 'Mark as addressed'}
                    </div>
                  </div>
                );
              })}
            </div>
          )}

          {/* === stream 2: messages (the relationship channel) === */}
          <div style={{ ...lbl(c), display:'flex', justifyContent:'space-between' }}><span>Messages</span><span style={{ color:c.faint }}>{thread.length ? 'a quieter word' : ''}</span></div>
          <div style={{ background:c.surface, border:`1px solid ${c.rule}`, borderRadius:10, padding:'12px 14px', marginBottom:18 }}>
            {thread.length === 0 ? (
              <div style={{ fontSize:12.5, color:c.faint, padding:'2px 0 10px' }}>No messages yet. Say hello when you like.</div>
            ) : (
              thread.map((m,i) => {
                const mine = m.from === 'iris';
                return (
                  <div key={i} style={{ borderLeft:`2px solid ${mine ? c.rule : c.a}`, paddingLeft:11, marginBottom:11 }}>
                    <div style={{ display:'flex', alignItems:'baseline', gap:7, marginBottom:3 }}>
                      <span style={{ fontSize:12, fontWeight:600, color: mine?c.muted:c.text }}>{mine ? 'You' : fn}</span>
                      <span style={{ fontFamily:MONO, fontSize:9, color:c.faint }}>{m.date}</span>
                    </div>
                    <p style={{ margin:0, fontSize:12.5, lineHeight:1.5, color: mine?c.muted:c.text }}>{m.text}</p>
                  </div>
                );
              })
            )}
            {/* inline reply composer */}
            <div style={{ borderTop:`1px solid ${c.rule}`, paddingTop:11, marginTop: thread.length?0:2 }}>
              <textarea value={reply} onChange={e=>setReply(e.target.value)} rows={2} placeholder={`Reply to ${fn}…`}
                style={{ width:'100%', fontFamily:c.f.body, fontSize:12.5, color:c.text, background:c.bg, border:`1px solid ${c.rule}`, borderRadius:8, padding:'8px 10px', outline:'none', resize:'vertical', lineHeight:1.5 }} />
              <div style={{ display:'flex', alignItems:'center', justifyContent:'flex-end', marginTop:8 }}>
                <button onClick={sendReply} disabled={!reply.trim()} style={{ background: reply.trim()?c.a:c.rule, color: reply.trim()?c.aOn:c.faint, border:'none', borderRadius:8, padding:'8px 16px', fontFamily:c.f.body, fontSize:12.5, fontWeight:600, cursor: reply.trim()?'pointer':'default' }}>Send</button>
              </div>
            </div>
          </div>

          {/* payment */}
          <div style={lbl(c)}>Payment</div>
          <div style={{ display:'flex', gap:10, marginBottom:18 }}>
            <div style={{ flex:1, background:c.surface, border:`1px solid ${c.rule}`, borderRadius:10, padding:'11px 13px' }}>
              <div style={{ fontSize:11, color:c.muted }}>Deposit</div>
              <div style={{ fontFamily:c.f.body, fontWeight:600, fontSize:14, color:c.text, marginTop:3 }}>${(co.total/2).toFixed(0)} <span style={{ fontSize:10, color: co.dep==='sent'?MONEYC:DONE }}>{co.dep==='sent'?'sent':'paid'}</span></div>
            </div>
            <div style={{ flex:1, background:c.surface, border:`1px solid ${c.rule}`, borderRadius:10, padding:'11px 13px' }}>
              <div style={{ fontSize:11, color:c.muted }}>Balance</div>
              <div style={{ fontFamily:c.f.body, fontWeight:600, fontSize:14, color:c.text, marginTop:3 }}>${(co.total/2).toFixed(0)} <span style={{ fontSize:10, color: co.bal==='paid'?DONE:c.faint }}>{co.bal==='paid'?'paid':'on approval'}</span></div>
            </div>
          </div>

          {/* client + portal */}
          <div style={lbl(c)}>Client</div>
          <div style={{ background:c.surface, border:`1px solid ${c.rule}`, borderRadius:10, padding:'12px 14px', marginBottom:18 }}>
            <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center' }}>
              <span style={{ fontSize:14, fontWeight:600, color:c.text }}>{co.client}</span>
              <a href="Mella Client Portal.html" target="_blank" rel="noopener" style={{ fontFamily:MONO, fontSize:10.5, color:c.a, textDecoration:'underline' }}>view as client ↗</a>
            </div>
            <div style={{ display:'flex', alignItems:'center', gap:7, marginTop:8, fontFamily:MONO, fontSize:11, color:c.muted }}><span>🔒</span> mella.studio/c/{co.token}</div>
          </div>

          {/* activity */}
          <div style={lbl(c)}>Activity</div>
          <div>
            {activity.map((a,i) => {
              const last = i===activity.length-1;
              const dot = actDot(a.t, c);
              return (
                <div key={i} style={{ display:'flex', gap:12 }}>
                  <div style={{ display:'flex', flexDirection:'column', alignItems:'center', width:9, flexShrink:0 }}>
                    <span style={{ width:8, height:8, borderRadius:'50%', background:dot, marginTop:4, flexShrink:0 }} />
                    {!last && <div style={{ flex:1, width:1.5, background:c.rule, marginTop:3 }} />}
                  </div>
                  <div style={{ flex:1, paddingBottom: last?0:14, display:'flex', justifyContent:'space-between', gap:10 }}>
                    <span style={{ fontSize:12.5, color:c.text }}>{a.t}{a.sub && <span style={{ color:c.muted }}> {a.sub}</span>}</span>
                    <span style={{ fontFamily:MONO, fontSize:10, color:c.faint, flexShrink:0 }}>{a.d}</span>
                  </div>
                </div>
              );
            })}
          </div>
        </div>
      </div>
    </div>
  );
}
const lbl = (c) => ({ fontFamily:MONO, fontSize:9.5, letterSpacing:'0.12em', textTransform:'uppercase', color:c.faint, marginBottom:9 });

/* ============================================================ desktop frame */
function DesktopFrame({ t, children }) {
  const c = resolveTheme(t);
  const [scale, setScale] = useState(1);
  const [narrow, setNarrow] = useState(false);
  useEffect(() => {
    const fit = () => {
      setScale(Math.min(1, (window.innerWidth - 28) / 1280, (window.innerHeight - 28) / 820));
      setNarrow(window.innerWidth < 760);
    };
    fit(); window.addEventListener('resize', fit); return () => window.removeEventListener('resize', fit);
  }, []);
  const embed = typeof window !== 'undefined' && /[?&]embed=1/.test(window.location.search);
  if (narrow && !embed) return <PhoneGuard c={c} />;
  return (
    <DashFrameInner c={c} scale={scale}>{children}</DashFrameInner>
  );
}
function PhoneGuard({ c }) {
  const card = { background:c.surface, border:`1px solid ${c.rule}`, borderRadius:14, padding:'14px 15px', display:'flex', gap:13, alignItems:'flex-start' };
  return (
    <div style={{ minHeight:'100vh', background:c.bg, fontFamily:c.f.body, color:c.text, display:'flex', flexDirection:'column', padding:'0 22px 40px' }}>
      <div style={{ paddingTop:48, marginBottom:6, fontFamily:"'Instrument Serif',serif", fontSize:26, letterSpacing:'-0.01em' }}>mella<span style={{ color:c.a }}>.</span></div>
      <div style={{ fontFamily:MONO, fontSize:10.5, letterSpacing:'0.16em', textTransform:'uppercase', color:c.a, marginTop:26, display:'flex', alignItems:'center', gap:10 }}>
        <span style={{ width:6, height:6, borderRadius:'50%', background:c.a }} /> Your studio
      </div>
      <h1 style={{ fontFamily:c.f.head, fontWeight:c.f.hw, fontSize:34, lineHeight:1.08, letterSpacing:'-0.015em', margin:'14px 0 0', maxWidth:'14ch' }}>Best seen on a bigger screen.</h1>
      <p style={{ fontSize:15.5, lineHeight:1.6, color:c.soft, margin:'16px 0 0', maxWidth:'42ch' }}>
        The Studio dashboard, your queue, requests, and payments, is built for a laptop or iPad. Open this same link there and you'll have the full thing.
      </p>
      <div style={{ marginTop:26, fontFamily:MONO, fontSize:10.5, letterSpacing:'0.12em', textTransform:'uppercase', color:c.faint }}>What lives here</div>
      <div style={{ display:'flex', flexDirection:'column', gap:10, marginTop:13 }}>
        {[
          ['A board for every commission', 'Open, in progress, awaiting your client, done.'],
          ['New requests, in one place', 'Accept to send a deposit, or decline kindly.'],
          ['Progress photos and approvals', 'Post an update, your client sees it on their page.'],
          ['Payments and reminders', 'Deposits, balances, and gentle nudges, all in view.'],
        ].map((r,i) => (
          <div key={i} style={card}>
            <span style={{ fontFamily:MONO, fontSize:11, color:c.a, marginTop:2 }}>{String(i+1).padStart(2,'0')}</span>
            <div>
              <div style={{ fontSize:14, fontWeight:600, color:c.text }}>{r[0]}</div>
              <div style={{ fontSize:12.5, color:c.muted, marginTop:2, lineHeight:1.45 }}>{r[1]}</div>
            </div>
          </div>
        ))}
      </div>
      <div style={{ marginTop:26, display:'flex', flexDirection:'column', gap:11 }}>
        <a href="Mella Public Queue.html" style={{ textAlign:'center', background:c.a, color:c.aOn, borderRadius:12, padding:'15px', fontSize:15, fontWeight:600, textDecoration:'none' }}>See your public page instead →</a>
        <a href="Mella Client Portal.html" style={{ textAlign:'center', background:'transparent', color:c.text, border:`1px solid ${c.rule}`, borderRadius:12, padding:'14px', fontSize:14.5, fontWeight:500, textDecoration:'none' }}>Or what your client sees →</a>
      </div>
      <div style={{ marginTop:30, textAlign:'center', fontFamily:"'Instrument Serif',serif", fontSize:18, color:c.faint }}>on mella<span style={{ color:c.a }}>.</span></div>
    </div>
  );
}
function DashFrameInner({ c, scale, children }) {
  const embed = typeof window !== 'undefined' && /[?&]embed=1/.test(window.location.search);
  if (embed) {
    // frameless: fills the showcase's device bezel at native size, inner flex layout reflows
    return <div style={{ width:'100vw', height:'100vh', overflow:'hidden', background:c.bg, display:'flex', flexDirection:'column' }}>{children}</div>;
  }
  return (
    <div style={{ minHeight:'100vh', display:'flex', alignItems:'center', justifyContent:'center' }}>
      <div style={{ width:1280, height:820, transform:`scale(${scale})`, transformOrigin:'center', borderRadius:14, overflow:'hidden', background:c.bg, position:'relative', boxShadow:'0 60px 120px -40px rgba(40,32,24,0.55), 0 0 0 1px rgba(0,0,0,0.05)' }}>
        <div style={{ height:42, background:c.chrome, borderBottom:`1px solid ${c.rule}`, display:'flex', alignItems:'center', gap:8, padding:'0 16px' }}>
          <div style={{ display:'flex', gap:7 }}>{[0,1,2].map(i=><span key={i} style={{ width:11, height:11, borderRadius:'50%', background:hexToRgba('#000',0.08) }} />)}</div>
          <div style={{ margin:'0 auto', background:c.bg, border:`1px solid ${c.rule}`, borderRadius:7, padding:'5px 16px', fontFamily:MONO, fontSize:11.5, color:c.soft, display:'flex', alignItems:'center', gap:7 }}>
            <span style={{ fontSize:9, color:c.faint }}>🔒</span> app.mella.studio/studio
          </div>
        </div>
        <div style={{ height:778 }}>{children}</div>
      </div>
    </div>
  );
}

/* ============================================================ app + tweaks */
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "accent": "teal",
  "tone": "warm",
  "type": "editorial",
  "accountState": "active"
}/*EDITMODE-END*/;

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  return (
    <React.Fragment>
      <DesktopFrame t={t}><Dashboard t={t} /></DesktopFrame>
      <TweaksPanel title="Tweaks">
        <TweakSection label="Theme" />
        <TweakColor label="Accent" value={ACCENTS[t.accent]} options={Object.values(ACCENTS)}
          onChange={(v) => setTweak('accent', Object.keys(ACCENTS).find(k => ACCENTS[k] === v) || 'teal')} />
        <TweakRadio label="Background" value={t.tone} options={['warm','paper','dusk']} onChange={(v) => setTweak('tone', v)} />
        <TweakSelect label="Type" value={t.type} options={[['editorial','Editorial serif'],['classic','Classic serif'],['clean','Clean sans']]} onChange={(v) => setTweak('type', v)} />
        <TweakSection label="Demo state" />
        <TweakRadio label="Account" value={t.accountState} options={[{value:'active',label:'Established'},{value:'new',label:'Brand new'}]} onChange={(v) => setTweak('accountState', v)} />
      </TweaksPanel>
    </React.Fragment>
  );
}

(function(){ const s=document.createElement('style'); s.textContent='@keyframes mq-slidein{from{transform:translateX(100%)}to{transform:translateX(0)}}'; document.head.appendChild(s); })();

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