// 1Radar v3 — Math calculators (ecom / online business)

// ── Number helpers ────────────────────────────────────────────────────────────
const _n = (v) => parseFloat(v) || 0;
const _fmt = (n, d = 2) => {
  if (!isFinite(n)) return '—';
  const abs = Math.abs(n).toFixed(d).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
  return n < 0 ? `−${abs}` : abs;
};
const fmtE = (n) => { if (!isFinite(n)) return '—'; return `${n < 0 ? '−€' : '€'}${_fmt(Math.abs(n))}`; };
const fmtP = (n) => (!isFinite(n) ? '—' : `${_fmt(n, 1)}%`);
const fmtX = (n) => (!isFinite(n) ? '—' : `${_fmt(n, 2)}×`);
const fmtI = (n) => (!isFinite(n) ? '—' : Math.round(Math.abs(n)).toLocaleString('en-US'));

// ── Shared UI ─────────────────────────────────────────────────────────────────

const CalcInput = ({ label, value, onChange, pre, suf, step = 'any', placeholder = '0', hint }) => (
  <div style={{ display: 'flex', flexDirection: 'column', gap: 5 }}>
    <label style={{ fontSize: 12, fontWeight: 500, color: 'var(--text-muted)' }}>{label}</label>
    <div style={{ position: 'relative', display: 'flex', alignItems: 'center' }}>
      {pre && (
        <span style={{
          position: 'absolute', left: 10, fontSize: 13,
          color: 'var(--text-faint)', pointerEvents: 'none', zIndex: 1,
        }}>{pre}</span>
      )}
      <input
        className="fld"
        type="number"
        value={value}
        onChange={e => onChange(e.target.value)}
        step={step}
        placeholder={placeholder}
        style={{ width: '100%', paddingLeft: pre ? 26 : 12, paddingRight: suf ? 46 : 12 }}
      />
      {suf && (
        <span style={{
          position: 'absolute', right: 10, fontSize: 12,
          color: 'var(--text-faint)', pointerEvents: 'none', whiteSpace: 'nowrap',
        }}>{suf}</span>
      )}
    </div>
    {hint && <p style={{ margin: 0, fontSize: 11, color: 'var(--text-faint)', lineHeight: 1.4 }}>{hint}</p>}
  </div>
);

// grow=true makes the card take up equal share of flex column height
const Kpi = ({ label, value, sub, tone, hero, grow }) => {
  const color = { good: 'var(--success)', warn: 'var(--warn)', bad: 'var(--danger)' }[tone] ?? 'var(--text)';
  return (
    <div className="card" style={{
      padding: '14px 16px', display: 'flex', flexDirection: 'column', gap: 3,
      ...(grow ? { flex: 1 } : {}),
    }}>
      <div style={{ fontSize: 11, color: 'var(--text-faint)', textTransform: 'uppercase', letterSpacing: '0.05em', fontWeight: 500 }}>{label}</div>
      <div className="font-display" style={{ fontWeight: 700, fontSize: hero ? 32 : 22, color, lineHeight: 1.1 }}>{value}</div>
      {sub && <div style={{ fontSize: 11, color: 'var(--text-faint)', marginTop: 2 }}>{sub}</div>}
    </div>
  );
};

// Wraps the right column — clones every Kpi child with grow=true so cards share the height equally
const KpiCol = ({ children }) => (
  <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
    {React.Children.map(children, child =>
      child && child.type === Kpi ? React.cloneElement(child, { grow: true }) : child
    )}
  </div>
);

const Panel = ({ title, children, cols }) => (
  <div className="card" style={{ padding: 20, display: 'flex', flexDirection: 'column', gap: 14 }}>
    {title && <div style={{ fontSize: 13, fontWeight: 600, fontFamily: 'var(--font-display)', color: 'var(--text-2)' }}>{title}</div>}
    <div style={cols ? { display: 'grid', gridTemplateColumns: `repeat(${cols}, 1fr)`, gap: 10 } : { display: 'flex', flexDirection: 'column', gap: 12 }}>
      {children}
    </div>
  </div>
);

const Note = ({ children }) => (
  <p style={{ margin: 0, fontSize: 12, color: 'var(--text-faint)', lineHeight: 1.65 }}>{children}</p>
);

// Two columns that stretch to the same height
const TwoCol = ({ children }) => (
  <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16, alignItems: 'stretch' }}>
    {children}
  </div>
);

// ── Ask AI section ────────────────────────────────────────────────────────────
const AskAICard = ({ questions, onAskAI }) => (
  <div style={{
    borderRadius: 'var(--r-xl)',
    border: '1px solid var(--border)',
    background: 'var(--bg-surface)',
    padding: 20,
    display: 'flex',
    flexDirection: 'column',
    gap: 14,
  }}>
    <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
      <div style={{
        width: 32, height: 32, borderRadius: 10,
        background: 'var(--accent-soft)', color: 'var(--accent)',
        display: 'grid', placeItems: 'center', flexShrink: 0,
      }}>
        <Icon name="sparkles" size={16}/>
      </div>
      <div>
        <div className="font-display" style={{ fontWeight: 600, fontSize: 14 }}>Ask AI</div>
        <div style={{ fontSize: 12, color: 'var(--text-faint)' }}>Get more context, examples, or benchmark data</div>
      </div>
    </div>

    <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
      {questions.map((q, i) => (
        <button
          key={i}
          onClick={() => onAskAI && onAskAI(q)}
          style={{
            display: 'flex', alignItems: 'flex-start', gap: 10,
            background: 'var(--bg-soft)', border: '1px solid var(--border-soft)',
            borderRadius: 'var(--r-lg)', padding: '9px 12px',
            cursor: 'pointer', textAlign: 'left', width: '100%',
            color: 'var(--text-2)', fontSize: 13, lineHeight: 1.45,
            transition: 'background 0.15s',
          }}
          onMouseEnter={e => e.currentTarget.style.background = 'var(--accent-soft)'}
          onMouseLeave={e => e.currentTarget.style.background = 'var(--bg-soft)'}
        >
          <Icon name="send" size={13} style={{ color: 'var(--accent)', marginTop: 2, flexShrink: 0 }}/>
          {q}
        </button>
      ))}
    </div>
  </div>
);

// ── Layout wrapper ────────────────────────────────────────────────────────────
const CalcLayout = ({ id, onBack, onAskAI, children }) => {
  const meta = CALC_META[id] || {};
  return (
    <>
      <div style={{
        padding: '0 24px', height: 56, flexShrink: 0,
        display: 'flex', alignItems: 'center', gap: 12,
        borderBottom: '1px solid var(--border-soft)',
        background: 'var(--bg-surface)',
      }}>
        <button className="btn btn-stroke btn-sm" onClick={onBack}>
          <Icon name="arrow-left-s" size={14}/> All tools
        </button>
        <div style={{ width: 1, height: 16, background: 'var(--border)' }}/>
        <div style={{
          width: 28, height: 28, borderRadius: 8,
          background: 'var(--accent-soft)', color: 'var(--accent)',
          display: 'grid', placeItems: 'center', flexShrink: 0,
        }}>
          <Icon name={meta.icon} size={14}/>
        </div>
        <div>
          <div className="font-display" style={{ fontWeight: 600, fontSize: 14, lineHeight: 1.2 }}>{meta.name}</div>
          <div style={{ fontSize: 11, color: 'var(--text-faint)' }}>{meta.subtitle}</div>
        </div>
      </div>
      <div style={{ flex: 1, padding: 24, overflow: 'auto' }} className="scroll">
        <div style={{ maxWidth: 880, margin: '0 auto', display: 'flex', flexDirection: 'column', gap: 16 }}>
          {children}
          {meta.questions && (
            <AskAICard questions={meta.questions} onAskAI={onAskAI}/>
          )}
        </div>
      </div>
    </>
  );
};

// ─────────────────────────────────────────────────────────────────────────────
// 1. Customer LTV Calculator
// ─────────────────────────────────────────────────────────────────────────────
const LTVCalc = () => {
  const [aov,    setAov]    = React.useState('80');
  const [freq,   setFreq]   = React.useState('3');
  const [life,   setLife]   = React.useState('2');
  const [margin, setMargin] = React.useState('40');

  const r = React.useMemo(() => {
    const a = _n(aov), f = _n(freq), l = _n(life), m = _n(margin) / 100;
    const annualVal = a * f;
    const grossLTV  = annualVal * l;
    const netLTV    = grossLTV * m;
    return { annualVal, grossLTV, netLTV, cac30: netLTV * 0.30, cac50: netLTV * 0.50, cac100: netLTV };
  }, [aov, freq, life, margin]);

  return (
    <>
      <TwoCol>
        <Panel title="Inputs">
          <CalcInput label="Average Order Value (AOV)" value={aov}    onChange={setAov}    pre="€" placeholder="80"/>
          <CalcInput label="Purchase frequency"        value={freq}   onChange={setFreq}   suf="orders / yr" placeholder="3"/>
          <CalcInput label="Customer lifespan"         value={life}   onChange={setLife}   suf="years" placeholder="2"/>
          <CalcInput label="Gross margin"              value={margin} onChange={setMargin} suf="%" placeholder="40" hint="(Revenue − COGS) ÷ Revenue"/>
        </Panel>
        <KpiCol>
          <Kpi label="Net LTV"               value={fmtE(r.netLTV)}    hero tone={r.netLTV > 0 ? 'good' : 'bad'} sub="Lifetime revenue × gross margin"/>
          <Kpi label="Gross LTV"             value={fmtE(r.grossLTV)}  sub="Before cost of goods"/>
          <Kpi label="Annual customer value" value={fmtE(r.annualVal)} sub={`€${aov} AOV × ${freq} orders/yr`}/>
        </KpiCol>
      </TwoCol>

      <Panel title="Max Customer Acquisition Cost (CAC)">
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 10 }}>
          <Kpi label="Conservative — 30% of Net LTV" value={fmtE(r.cac30)}  tone="warn"/>
          <Kpi label="Aggressive — 50% of Net LTV"   value={fmtE(r.cac50)}  tone="warn"/>
          <Kpi label="Break-even — 100% of Net LTV"  value={fmtE(r.cac100)} tone="bad"/>
        </div>
        <Note>Industry rule: CAC ≤ 1/3 of Net LTV for healthy unit economics. A 3:1 LTV:CAC ratio leaves room for payback period and operating costs.</Note>
      </Panel>
    </>
  );
};

// ─────────────────────────────────────────────────────────────────────────────
// 2. ROAS Break-Even Calculator
// ─────────────────────────────────────────────────────────────────────────────
const RoasBeCalc = () => {
  const [aov,   setAov]   = React.useState('80');
  const [cogs,  setCogs]  = React.useState('25');
  const [ship,  setShip]  = React.useState('5');
  const [fee,   setFee]   = React.useState('2.9');
  const [other, setOther] = React.useState('0');

  const r = React.useMemo(() => {
    const a = _n(aov), c = _n(cogs), s = _n(ship), f = _n(fee) / 100, o = _n(other);
    const txFee     = a * f;
    const totalCost = c + s + txFee + o;
    const profit    = a - totalCost;
    const marginPct = a > 0 ? (profit / a) * 100 : 0;
    const beROAS    = profit > 0 ? a / profit : Infinity;
    return { profit, marginPct, beROAS, beCPA: profit, txFee, totalCost };
  }, [aov, cogs, ship, fee, other]);

  const roasTone = r.beROAS < 2 ? 'good' : r.beROAS < 4 ? 'warn' : 'bad';

  return (
    <>
      <TwoCol>
        <Panel title="Your order economics">
          <CalcInput label="Selling price / AOV"   value={aov}   onChange={setAov}   pre="€" placeholder="80"/>
          <CalcInput label="Product cost (COGS)"   value={cogs}  onChange={setCogs}  pre="€" placeholder="25"/>
          <CalcInput label="Shipping cost"         value={ship}  onChange={setShip}  pre="€" placeholder="5"/>
          <CalcInput label="Payment processing"    value={fee}   onChange={setFee}   suf="%" placeholder="2.9" hint="Stripe: 1.5–2.9% · Shopify Payments: 1.5–2%"/>
          <CalcInput label="Other variable costs"  value={other} onChange={setOther} pre="€" placeholder="0" hint="Returns, packaging, etc."/>
        </Panel>
        <KpiCol>
          <Kpi label="Break-even ROAS"     value={isFinite(r.beROAS) ? fmtX(r.beROAS) : '∞'} hero tone={r.profit > 0 ? roasTone : 'bad'} sub="Minimum ROAS to avoid losses"/>
          <Kpi label="Break-even CPA"      value={fmtE(r.beCPA)}     tone={r.beCPA > 0 ? 'good' : 'bad'} sub="Max cost per conversion"/>
          <Kpi label="Gross profit/order"  value={fmtE(r.profit)}    tone={r.profit > 0 ? 'good' : 'bad'} sub={`Margin: ${fmtP(r.marginPct)}`}/>
          <Kpi label="Total costs/order"   value={fmtE(r.totalCost)} sub={`Incl. €${_fmt(_n(fee) / 100 * _n(aov))} payment fee`}/>
        </KpiCol>
      </TwoCol>

      {r.profit > 0 && (
        <Panel title="ROAS targets with safety cushion">
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 10 }}>
            {[{ mult: 1.5, label: '1.5×' }, { mult: 2, label: '2×' }, { mult: 3, label: '3×' }].map(({ mult, label }) => (
              <Kpi key={mult} label={`${label} cushion — ${fmtX(r.beROAS * mult)} ROAS`} value={fmtE(_n(aov) / (r.beROAS * mult))} sub="Target CPA"/>
            ))}
          </div>
          <Note>BE ROAS = AOV ÷ Gross Profit. A 2× cushion means you stay profitable even if ROAS drops 50% — good baseline for volatile ad accounts.</Note>
        </Panel>
      )}
    </>
  );
};

// ─────────────────────────────────────────────────────────────────────────────
// 3. Profit Margin Calculator
// ─────────────────────────────────────────────────────────────────────────────
const ProfitMarginCalc = () => {
  const [rev,  setRev]  = React.useState('10000');
  const [cogs, setCogs] = React.useState('4000');
  const [opex, setOpex] = React.useState('2000');
  const [ads,  setAds]  = React.useState('1000');

  const r = React.useMemo(() => {
    const rv = _n(rev), c = _n(cogs), o = _n(opex), a = _n(ads);
    const gp  = rv - c;
    const op  = gp - o;
    const net = op - a;
    return {
      grossProfit: gp,  grossMargin: rv > 0 ? gp  / rv * 100 : 0,
      opProfit:    op,  opMargin:    rv > 0 ? op   / rv * 100 : 0,
      netProfit:   net, netMargin:   rv > 0 ? net  / rv * 100 : 0,
      markup: c > 0 ? gp / c * 100 : 0,
      mer:    a > 0 ? rv / a : 0,
    };
  }, [rev, cogs, opex, ads]);

  const tone = (v) => v > 15 ? 'good' : v > 0 ? 'warn' : 'bad';

  return (
    <>
      <TwoCol>
        <Panel title="Inputs">
          <CalcInput label="Total revenue"             value={rev}  onChange={setRev}  pre="€" placeholder="10 000"/>
          <CalcInput label="Cost of goods (COGS)"      value={cogs} onChange={setCogs} pre="€" placeholder="4 000"/>
          <CalcInput label="Operating expenses (OPEX)" value={opex} onChange={setOpex} pre="€" placeholder="2 000" hint="Salaries, tools, rent, etc."/>
          <CalcInput label="Total ad spend"            value={ads}  onChange={setAds}  pre="€" placeholder="1 000"/>
        </Panel>
        <KpiCol>
          <Kpi label="Net profit"       value={fmtE(r.netProfit)}   hero tone={tone(r.netMargin)}   sub={`Net margin: ${fmtP(r.netMargin)}`}/>
          <Kpi label="Gross profit"     value={fmtE(r.grossProfit)} tone={tone(r.grossMargin)} sub={`Gross margin: ${fmtP(r.grossMargin)}`}/>
          <Kpi label="Operating profit" value={fmtE(r.opProfit)}    tone={tone(r.opMargin)}   sub={`Operating margin: ${fmtP(r.opMargin)}`}/>
        </KpiCol>
      </TwoCol>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
        <Kpi label="Markup"             value={fmtP(r.markup)} sub="Gross profit ÷ COGS"/>
        <Kpi label="MER (blended ROAS)" value={fmtX(r.mer)} tone={r.mer > 3 ? 'good' : r.mer > 1.5 ? 'warn' : 'bad'} sub="Total revenue ÷ total ad spend"/>
      </div>
    </>
  );
};

// ─────────────────────────────────────────────────────────────────────────────
// 4. Shopify Plan Calculator
// ─────────────────────────────────────────────────────────────────────────────
const SHOPIFY_PLANS = [
  { name: 'Basic',    monthly: 25,   txFee: 0.0200, cardRate: 0.0200, cardFixed: 0.30, commitment: null },
  { name: 'Grow',     monthly: 65,   txFee: 0.0100, cardRate: 0.0170, cardFixed: 0.30, commitment: null },
  { name: 'Advanced', monthly: 399,  txFee: 0.0060, cardRate: 0.0150, cardFixed: 0.30, commitment: null },
  { name: 'Plus',     monthly: 2500, monthly3y: 2300, txFee: 0.0020, cardRate: 0.0130, cardFixed: 0.30, commitment: '1-year min.' },
];

const ShopifyPlanCalc = () => {
  const [rev,    setRev]    = React.useState('30000');
  const [orders, setOrders] = React.useState('375');
  const [useSP,  setUseSP]  = React.useState(true);

  const results = React.useMemo(() => {
    const r = _n(rev), o = _n(orders);
    return SHOPIFY_PLANS.map(plan => {
      const varCost = useSP ? r * plan.cardRate + o * plan.cardFixed : r * plan.txFee;
      return { ...plan, varCost, total: plan.monthly + varCost };
    });
  }, [rev, orders, useSP]);

  const cheapestIdx = results.reduce((best, p, i) => p.total < results[best].total ? i : best, 0);

  const beRevenues = SHOPIFY_PLANS.slice(0, 3).map((plan, i) => {
    const next = SHOPIFY_PLANS[i + 1];
    const diff = next.monthly - plan.monthly;
    const rate = useSP ? (plan.cardRate - next.cardRate) : (plan.txFee - next.txFee);
    return rate > 0 ? diff / rate : Infinity;
  });

  return (
    <>
      <Panel title="Your store">
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 12 }}>
          <CalcInput label="Monthly revenue" value={rev}    onChange={setRev}    pre="€" placeholder="30 000"/>
          <CalcInput label="Monthly orders"  value={orders} onChange={setOrders} placeholder="375" hint="Used for per-transaction fee"/>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 5 }}>
            <label style={{ fontSize: 12, fontWeight: 500, color: 'var(--text-muted)' }}>Payment gateway</label>
            <div style={{ display: 'flex', gap: 6 }}>
              {[{ label: 'Shopify Payments', val: true }, { label: 'Third-party', val: false }].map(opt => (
                <button key={String(opt.val)} className={`btn btn-sm ${useSP === opt.val ? 'btn-night' : 'btn-stroke'}`}
                  style={{ flex: 1, fontSize: 12 }} onClick={() => setUseSP(opt.val)}>{opt.label}</button>
              ))}
            </div>
            <p style={{ margin: 0, fontSize: 11, color: 'var(--text-faint)', lineHeight: 1.4 }}>
              {useSP ? 'Card processing: 2.0% → 1.3% + $0.30/order' : 'Extra transaction fee: 2.0% → 0.15% of revenue'}
            </p>
          </div>
        </div>
      </Panel>

      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 10 }}>
        {results.map((plan, i) => {
          const isBest = i === cheapestIdx;
          return (
            <div key={plan.name} className="card" style={{
              padding: 16, position: 'relative',
              border: isBest ? '2px solid var(--accent)' : '1px solid var(--border)',
              background: isBest ? 'var(--accent-soft)' : 'var(--bg-surface)',
              display: 'flex', flexDirection: 'column', gap: 8,
            }}>
              {isBest && <span className="badge badge-blue" style={{ position: 'absolute', top: 10, right: 10, fontSize: 10 }}>Best value</span>}
              <div style={{ display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
                <span style={{ fontSize: 11, fontWeight: 600, color: 'var(--text-faint)', textTransform: 'uppercase', letterSpacing: '0.05em' }}>{plan.name}</span>
                {plan.commitment && <span className="badge" style={{ fontSize: 10, background: 'var(--bg-soft)', color: 'var(--text-faint)', border: '1px solid var(--border)' }}>{plan.commitment}</span>}
              </div>
              <div className="font-display" style={{ fontWeight: 700, fontSize: 22, color: isBest ? 'var(--accent)' : 'var(--text)' }}>
                ${_fmt(plan.total, 0)}<span style={{ fontSize: 12, fontWeight: 400, color: 'var(--text-faint)' }}>/mo</span>
              </div>
              <div style={{ fontSize: 11, color: 'var(--text-faint)', display: 'flex', flexDirection: 'column', gap: 3, marginTop: 2 }}>
                <div>Plan: ${plan.monthly}/mo</div>
                {plan.monthly3y && (
                  <div style={{ color: 'var(--success)' }}>${plan.monthly3y}/mo on 3-year term</div>
                )}
                <div>Fees: ${_fmt(plan.varCost, 0)}/mo</div>
                <div style={{ marginTop: 2, color: 'var(--text-muted)' }}>
                  {useSP ? `${(plan.cardRate * 100).toFixed(1)}% + $${plan.cardFixed}/order` : `${(plan.txFee * 100).toFixed(2)}% on revenue`}
                </div>
              </div>
            </div>
          );
        })}
      </div>

      <Panel title="Upgrade break-even points">
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 10 }}>
          {SHOPIFY_PLANS.slice(0, 3).map((plan, i) => {
            const next = SHOPIFY_PLANS[i + 1];
            const be   = beRevenues[i];
            const past = _n(rev) >= be;
            return (
              <Kpi key={plan.name} label={`${plan.name} → ${next.name}`}
                value={isFinite(be) ? `$${fmtI(be)}/mo` : '—'}
                sub={past ? '✓ You should upgrade' : 'Revenue needed to justify upgrade'}
                tone={past ? 'good' : undefined}/>
            );
          })}
        </div>
        <Note>Plans billed in USD. Plus requires a minimum 1-year commitment. Green = your revenue has passed the break-even threshold for this upgrade.</Note>
      </Panel>
    </>
  );
};

// ─────────────────────────────────────────────────────────────────────────────
// 5. Ad Budget Calculator
// ─────────────────────────────────────────────────────────────────────────────
const AdBudgetCalc = () => {
  const [targetRev,  setTargetRev]  = React.useState('50000');
  const [targetROAS, setTargetROAS] = React.useState('3');
  const [margin,     setMargin]     = React.useState('40');

  const r = React.useMemo(() => {
    const rv = _n(targetRev), roas = _n(targetROAS), m = _n(margin) / 100;
    const adSpend    = roas > 0 ? rv / roas : 0;
    const grossProfit = rv * m;
    const netProfit  = grossProfit - adSpend;
    return {
      adSpend, grossProfit, netProfit,
      beROAS:      m > 0 ? 1 / m : 0,
      netMargin:   rv > 0 ? netProfit / rv * 100 : 0,
      spendShare:  rv > 0 ? adSpend / rv * 100 : 0,
    };
  }, [targetRev, targetROAS, margin]);

  return (
    <TwoCol>
      <Panel title="Goals">
        <CalcInput label="Target monthly revenue" value={targetRev}  onChange={setTargetRev}  pre="€" placeholder="50 000"/>
        <CalcInput label="Target ROAS"            value={targetROAS} onChange={setTargetROAS} suf="×" placeholder="3.0"/>
        <CalcInput label="Gross margin"           value={margin}     onChange={setMargin}     suf="%" placeholder="40" hint="Revenue minus COGS, as a percentage"/>
      </Panel>
      <KpiCol>
        <Kpi label="Required ad budget"   value={fmtE(r.adSpend)}     hero tone="warn" sub={`${fmtP(r.spendShare)} of target revenue`}/>
        <Kpi label="Net profit after ads" value={fmtE(r.netProfit)}   tone={r.netProfit > 0 ? 'good' : 'bad'} sub={`Net margin: ${fmtP(r.netMargin)}`}/>
        <Kpi label="Gross profit"         value={fmtE(r.grossProfit)} sub="Before ad spend"/>
        <Kpi label="Break-even ROAS"      value={fmtX(r.beROAS)}      sub="Minimum to not lose money"/>
      </KpiCol>
    </TwoCol>
  );
};

// ─────────────────────────────────────────────────────────────────────────────
// 6. Break-Even Units Calculator
// ─────────────────────────────────────────────────────────────────────────────
const BreakevenCalc = () => {
  const [price,   setPrice]   = React.useState('80');
  const [varCost, setVarCost] = React.useState('35');
  const [fixed,   setFixed]   = React.useState('5000');

  const r = React.useMemo(() => {
    const p = _n(price), v = _n(varCost), f = _n(fixed);
    const contribution = p - v;
    const contribPct   = p > 0 ? contribution / p * 100 : 0;
    const beUnits      = contribution > 0 ? Math.ceil(f / contribution) : Infinity;
    const beRevenue    = isFinite(beUnits) ? beUnits * p : Infinity;
    return { contribution, contribPct, beUnits, beRevenue };
  }, [price, varCost, fixed]);

  return (
    <TwoCol>
      <Panel title="Inputs">
        <CalcInput label="Selling price / unit"  value={price}   onChange={setPrice}   pre="€" placeholder="80"/>
        <CalcInput label="Variable cost / unit"  value={varCost} onChange={setVarCost} pre="€" placeholder="35" hint="COGS + shipping + payment fees"/>
        <CalcInput label="Monthly fixed costs"   value={fixed}   onChange={setFixed}   pre="€" placeholder="5 000" hint="Salaries, rent, software, etc."/>
      </Panel>
      <KpiCol>
        <Kpi label="Break-even units/mo"   value={isFinite(r.beUnits) ? fmtI(r.beUnits) : '∞'}     hero tone={isFinite(r.beUnits) ? 'warn' : 'bad'}/>
        <Kpi label="Break-even revenue/mo" value={isFinite(r.beRevenue) ? fmtE(r.beRevenue) : '∞'} sub="Revenue to cover all costs"/>
        <Kpi label="Contribution/unit"     value={fmtE(r.contribution)} tone={r.contribution > 0 ? 'good' : 'bad'} sub={`Contribution margin: ${fmtP(r.contribPct)}`}/>
      </KpiCol>
    </TwoCol>
  );
};

// ─────────────────────────────────────────────────────────────────────────────
// 7. Markup ↔ Margin Converter
// ─────────────────────────────────────────────────────────────────────────────
const MarkupCalc = () => {
  const [cost,  setCost]  = React.useState('30');
  const [price, setPrice] = React.useState('80');

  const r = React.useMemo(() => {
    const c = _n(cost), p = _n(price);
    const profit = p - c;
    return {
      profit,
      margin: p > 0 ? profit / p * 100 : 0,
      markup: c > 0 ? profit / c * 100 : 0,
    };
  }, [cost, price]);

  const quickRef = [
    { mk: 25, mg: 20 }, { mk: 33, mg: 25 }, { mk: 50, mg: 33.3 },
    { mk: 100, mg: 50 }, { mk: 200, mg: 66.7 }, { mk: 400, mg: 80 },
  ];

  return (
    <>
      <TwoCol>
        <Panel title="Inputs">
          <CalcInput label="Cost price (COGS)" value={cost}  onChange={setCost}  pre="€" placeholder="30"/>
          <CalcInput label="Selling price"     value={price} onChange={setPrice} pre="€" placeholder="80"/>
        </Panel>
        <KpiCol>
          <Kpi label="Gross margin"      value={fmtP(r.margin)} hero tone={r.margin > 50 ? 'good' : r.margin > 25 ? 'warn' : 'bad'} sub="Profit ÷ Selling price"/>
          <Kpi label="Markup"            value={fmtP(r.markup)}      sub="Profit ÷ Cost price"/>
          <Kpi label="Gross profit/unit" value={fmtE(r.profit)} tone={r.profit > 0 ? 'good' : 'bad'}/>
        </KpiCol>
      </TwoCol>

      <Panel title="Markup → Margin reference table">
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 8 }}>
          {quickRef.map(({ mk, mg }) => {
            const sellPrice = _n(cost) * (1 + mk / 100);
            const isActive  = Math.abs(r.markup - mk) < 1;
            return (
              <div key={mk} className="card" style={{
                padding: '10px 14px', display: 'flex', justifyContent: 'space-between', alignItems: 'center',
                background: isActive ? 'var(--accent-soft)' : 'var(--bg-surface)',
                border: isActive ? '1.5px solid var(--accent)' : '1px solid var(--border)',
              }}>
                <div>
                  <div style={{ fontSize: 13, fontWeight: 600 }}>{mk}% markup</div>
                  <div style={{ fontSize: 11, color: 'var(--text-faint)' }}>{mg}% margin</div>
                </div>
                <div style={{ fontSize: 13, fontWeight: 500, fontFamily: 'var(--font-mono)' }}>€{_fmt(sellPrice)}</div>
              </div>
            );
          })}
        </div>
        <Note>Margin and markup are not the same: 50% markup on a €30 cost = €45 sell price = 33% margin. Always clarify which one you mean when talking to investors or ad agencies.</Note>
      </Panel>
    </>
  );
};

// ─────────────────────────────────────────────────────────────────────────────
// 8. Facebook Comment Generator
// ─────────────────────────────────────────────────────────────────────────────

const _fbMkId = () => Math.random().toString(36).slice(2, 8);

const FB_LABELS = {
  en: { like: 'Like', reply: 'Reply', write: 'Write a comment...', comments: 'Comments' },
  fr: { like: "J'aime", reply: 'Répondre', write: 'Écrire un commentaire...', comments: 'Commentaires' },
  de: { like: 'Gefällt mir', reply: 'Antworten', write: 'Kommentar schreiben...', comments: 'Kommentare' },
  es: { like: 'Me gusta', reply: 'Responder', write: 'Escribe un comentario...', comments: 'Comentarios' },
  pt: { like: 'Curtir', reply: 'Responder', write: 'Escreva um comentário...', comments: 'Comentários' },
};

const FB_TEMPLATES = {
  weightloss: [
    { id: 'w1', name: 'Eder Dionízio',      text: 'Omg, for real? Now I gotta try it!',                           time: '5 d', likes: 4,  parentId: null },
    { id: 'w2', name: 'Lucia Helena',        text: 'I might try it too 😄',                                        time: '5 d', likes: 11, parentId: 'w1' },
    { id: 'w3', name: 'Roberval Callegari', text: 'No suffering at all 💪',                                        time: '4 d', likes: 6,  parentId: 'w2' },
    { id: 'w4', name: 'Ligia dos Santos',   text: 'I finally fit into my old clothes again! Best feeling ever 🤩', time: '3 d', likes: 3,  parentId: null },
    { id: 'w5', name: 'Marcelo Essado',     text: 'Makes all the difference!',                                     time: '2 d', likes: 4,  parentId: 'w4' },
  ],
  skincare: [
    { id: 's1', name: 'Sophie Martin',  text: 'My skin has never looked this good, absolutely love it! ✨',  time: '3 d', likes: 14, parentId: null },
    { id: 's2', name: 'Julien Berger',  text: 'Same here! My dark spots are almost gone 😮',                 time: '3 d', likes: 7,  parentId: 's1' },
    { id: 's3', name: 'Clara Voss',     text: 'How long did it take to see results?',                        time: '2 d', likes: 2,  parentId: 's1' },
    { id: 's4', name: 'Tom Leclerc',    text: "Just ordered, can't wait to try it!",                         time: '1 d', likes: 5,  parentId: null },
    { id: 's5', name: 'Emma Dubois',    text: 'I saw results in just 2 weeks 🌟',                            time: '2 d', likes: 9,  parentId: 's3' },
  ],
  fitness: [
    { id: 'f1', name: 'Jake Rivera',   text: 'Down 8kg in 6 weeks, this program is no joke 🔥',              time: '4 d', likes: 22, parentId: null },
    { id: 'f2', name: 'Priya Sharma',  text: 'Which plan are you on? Results look incredible!',               time: '4 d', likes: 8,  parentId: 'f1' },
    { id: 'f3', name: 'Jake Rivera',   text: 'The 12-week shred — totally worth it 💯',                       time: '3 d', likes: 5,  parentId: 'f2' },
    { id: 'f4', name: 'Maria Costa',   text: 'Signed up yesterday, so pumped to start!',                      time: '2 d', likes: 3,  parentId: null },
    { id: 'f5', name: 'Ben Okafor',    text: "Let's gooo 💪 we can do this together",                         time: '1 d', likes: 6,  parentId: 'f4' },
  ],
};

const FB_TIME_OPTS = ['just now','1 m','5 m','30 m','1 h','2 h','6 h','12 h','1 d','2 d','3 d','4 d','5 d','1 w','2 w','1 m'];

const FB_REACTIONS = [
  { id: 'like',  emoji: '👍', label: 'Like'  },
  { id: 'love',  emoji: '❤️', label: 'Love'  },
  { id: 'care',  emoji: '🥰', label: 'Care'  },
  { id: 'haha',  emoji: '😂', label: 'Haha'  },
  { id: 'wow',   emoji: '😯', label: 'Wow'   },
  { id: 'sad',   emoji: '😢', label: 'Sad'   },
  { id: 'angry', emoji: '😡', label: 'Angry' },
];

const FB_COMMENT_LIBRARY = [
  // young / white / man
  { file: 'comment_library/4e71e94d1265172d5397490d62fc0e43b2eb8c8b74db0834c0b4be4d2790a9ad.jpeg.png', age: 'young', eth: 'white', gender: 'man' },
  { file: 'comment_library/5dfe68651c88c1e529a5cf53a7dd81e055f7ece559beaabcac1c6180ea577928.jpeg.png', age: 'young', eth: 'white', gender: 'man' },
  { file: 'comment_library/unnamed_file_from_thispersondoesnotexist.com.png(3).png',    age: 'young', eth: 'white', gender: 'man' },
  { file: 'comment_library/unnamed_file_from_thispersondoesnotexist.com.png(4).png',    age: 'young', eth: 'white', gender: 'man' },
  { file: 'comment_library/unnamed_file_from_thispersondoesnotexist.com.png(6).png',    age: 'young', eth: 'white', gender: 'man' },
  { file: 'comment_library/unnamed_file_from_thispersondoesnotexist.com.png(8).png',    age: 'young', eth: 'white', gender: 'man' },
  { file: 'comment_library/unnamed_file_from_thispersondoesnotexist.com.png(9).png',    age: 'young', eth: 'white', gender: 'man' },
  { file: 'comment_library/unnamed_file_from_thispersondoesnotexist.com.png(10).png',   age: 'young', eth: 'white', gender: 'man' },
  // young / white / woman
  { file: 'comment_library/7f30c6d51ce9ae762d56e166c88c8e62ce17cae95663e49bd280aa946687930b.jpeg.png', age: 'young', eth: 'white', gender: 'woman' },
  { file: 'comment_library/844abd2082aec24c109ed33018c407a8cacaab081678be8450dba5123a2127f4.jpeg.png', age: 'young', eth: 'white', gender: 'woman' },
  { file: 'comment_library/unnamed_file_from_thispersondoesnotexist.com.png.png',       age: 'young', eth: 'white', gender: 'woman' },
  { file: 'comment_library/unnamed_file_from_thispersondoesnotexist.com.png(7).png',    age: 'young', eth: 'white', gender: 'woman' },
  // young / black / man
  { file: 'comment_library/262f49eb956597608c92f22250edf21540a4d326f3d1b6b2599bf5b2cbbb35ce.jpeg.png', age: 'young', eth: 'black', gender: 'man' },
  { file: 'comment_library/534f54fdc8e725ab8f5568f15706c6ddc5d58e1eb822dfaff3c9c9ee516b9b2d.jpeg.png', age: 'young', eth: 'black', gender: 'man' },
  { file: 'comment_library/a58135d2c5ac9b169004c276864afabd70f463c8bee705a6ee4ae1e93c15c784.jpeg.png', age: 'young', eth: 'black', gender: 'man' },
  // young / mixed / man
  { file: 'comment_library/34a5272515e7ef265af5daa5c966cccf1fe1e38b085e8fbae58f5a33351f1124.jpeg.png', age: 'young', eth: 'mixed', gender: 'man' },
  { file: 'comment_library/51d29da1b20980b2a70e62870baac482c7bc3847e808934f3dda2e1580158080.jpeg.png', age: 'young', eth: 'mixed', gender: 'man' },
  { file: 'comment_library/unnamed_file_from_thispersondoesnotexist.com.png(5).png',    age: 'young', eth: 'mixed', gender: 'man' },
  // young / mixed / woman
  { file: 'comment_library/unnamed_file_from_thispersondoesnotexist.com.png(2).png',    age: 'young', eth: 'mixed', gender: 'woman' },
  // mid / white / man
  { file: 'comment_library/a133aa1f9c25e3f98029cd82a56d0e2d496d7e7fc2ad06f6ec4671529f899ae3.jpeg.png', age: 'mid', eth: 'white', gender: 'man' },
  { file: 'comment_library/ca8a1e7c42148abdfd7b5ee18a267a43c398b2471af70fed0cbf4f08238f99da.jpeg.png', age: 'mid', eth: 'white', gender: 'man' },
  // mid / white / woman
  { file: 'comment_library/66e94563265ccaa6125b86f563709b101c1b9cfeebdb44683a67e12ede27df99.jpeg.png', age: 'mid', eth: 'white', gender: 'woman' },
  { file: 'comment_library/d751a7600831e22245eeb28252fa422eddd1e5eb8ee3b3f7625d855350ac033e.jpeg.png', age: 'mid', eth: 'white', gender: 'woman' },
  { file: 'comment_library/e30be59e21d978f514560b813758cfc876c77978b4741b5d9c732f5e19037d3b.bin.png',  age: 'mid', eth: 'white', gender: 'woman' },
  // mid / black / man
  { file: 'comment_library/6df2969782ec0116f6cb532717e9aaab4975370c453c044dffe4144cbd121443.jpeg.png', age: 'mid', eth: 'black', gender: 'man' },
  { file: 'comment_library/bd80a385ba4465677fdefe6ac8c5f9dd896b0f6eb83f2c5baf8424d2185f4da6.jpeg.png', age: 'mid', eth: 'black', gender: 'man' },
  // mid / black / woman
  { file: 'comment_library/3311a465e7b3c9ee3dd614ea299b31d227e4af75aad29b80fe01dc1bfb91e5ac.jpeg.png', age: 'mid', eth: 'black', gender: 'woman' },
  { file: 'comment_library/4efbae4e00645c76b803d31be12cc941cb4b227d6d3899a54945f8a046bcc081.jpeg.png', age: 'mid', eth: 'black', gender: 'woman' },
  { file: 'comment_library/9f224a13a33cb6c99ed38d86acfc3abf58c4b899431149e15c31670d4bd3fa24.jpeg.png', age: 'mid', eth: 'black', gender: 'woman' },
  // mid / mixed / woman
  { file: 'comment_library/unnamed_file_from_thispersondoesnotexist.com.png(1).png',    age: 'mid', eth: 'mixed', gender: 'woman' },
  // old / white / man
  { file: 'comment_library/923cfb8340648033c43d0076a953c59b57eb0c3cee817914b6868b940bbf3078.jpeg.png', age: 'old', eth: 'white', gender: 'man' },
  { file: 'comment_library/94b00c9a7170c2da884d81d7bff9d80aa67560e2944fc50adf27704f3e0900f7.jpeg.png', age: 'old', eth: 'white', gender: 'man' },
  // old / white / woman
  { file: 'comment_library/190e78dec898e75ae26aa5c51d41c974b7eb17ac41f2561d278702bbfb1f1add.jpeg.png', age: 'old', eth: 'white', gender: 'woman' },
  { file: 'comment_library/2317d895d9d84c6ad6b2eb131e06b0662867747baf136be03f25a6b13d94dcd2.jpeg.png', age: 'old', eth: 'white', gender: 'woman' },
  { file: 'comment_library/5a44382cdfd884b0fee5f0ee8154912054b7ed7c06d7f829a9193f24309d2246.jpeg.png', age: 'old', eth: 'white', gender: 'woman' },
  // old / black / man
  { file: 'comment_library/0e1d24bb910c255e2a676462682fc94c395ce5db736dee6a45f3b7fee9c77c53.jpeg.png', age: 'old', eth: 'black', gender: 'man' },
  { file: 'comment_library/1fb0f63defb09e4305e4359136f5ab19b0f4384e7657b1234568103f01988224.jpeg.png', age: 'old', eth: 'black', gender: 'man' },
  { file: 'comment_library/56d8f2ad28e2ad2a520f76d159aaea903d0c006fec3b51a83f175887277e3f21.jpeg.png', age: 'old', eth: 'black', gender: 'man' },
  // old / black / woman
  { file: 'comment_library/556325111437461090bf4218d34f1203c3694bf400904529c518890abc3144c4.jpeg.png', age: 'old', eth: 'black', gender: 'woman' },
  { file: 'comment_library/d5e0ea7f366fe0c57865c9f048abf64222051bdc2815aeb89a720ca601e3d590.jpeg.png', age: 'old', eth: 'black', gender: 'woman' },
  { file: 'comment_library/ed261941b86c49aaef1dfd849016938b8734934efeffd577aadfcdbd61ffdb07.jpeg.png', age: 'old', eth: 'black', gender: 'woman' },
];

const FbAvatar = ({ name, size = 40, avatarUrl }) => {
  const [err, setErr] = React.useState(false);
  React.useEffect(() => setErr(false), [avatarUrl]);
  const COLORS = ['#1877f2','#42b72a','#f02849','#f7b928','#8b5cf6','#ec4899','#06b6d4','#f97316'];
  const n = name || '?';
  const hash = n.split('').reduce((a, c) => a + c.charCodeAt(0), 0);
  if (avatarUrl && !err) {
    return (
      <img
        src={avatarUrl} alt={n}
        onError={() => setErr(true)}
        style={{ width: size, height: size, borderRadius: '50%', flexShrink: 0, objectFit: 'cover', display: 'block' }}
      />
    );
  }
  return (
    <div style={{
      width: size, height: size, borderRadius: '50%', flexShrink: 0,
      background: COLORS[hash % COLORS.length], color: '#fff',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      fontSize: Math.round(size * 0.36), fontWeight: 700, userSelect: 'none',
      fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
    }}>
      {n.trim().split(/\s+/).map(w => w[0]).join('').slice(0, 2).toUpperCase()}
    </div>
  );
};

// FbCommentRow: handles both read-only display and inline editing
const FbCommentRow = ({
  comment, depth, labels, isEditMode,
  isEditing, editDraft, setEditDraft, onStartEdit, onSaveEdit, onCancelEdit, onDelete,
  isReplying, replyText, setReplyText, onStartReply, onCancelReply, onSubmitReply,
  onSaveAvatar,
}) => {
  const avSz = depth === 0 ? 40 : 32;
  const textareaRef  = React.useRef(null);
  const fileInputRef = React.useRef(null);
  const reactionEmoji = FB_REACTIONS.find(r => r.id === (comment.reaction || 'like'))?.emoji || '👍';

  // Avatar popup state (local, independent of text-edit mode)
  const [avatarOpen,  setAvatarOpen]  = React.useState(false);
  const [avatarDraft, setAvatarDraft] = React.useState('');
  const [avatarFile,  setAvatarFile]  = React.useState(''); // base64, kept out of the input
  const [libAge,      setLibAge]      = React.useState('all');
  const [libEth,      setLibEth]      = React.useState('all');
  const [libGender,   setLibGender]   = React.useState('all');

  React.useEffect(() => {
    if (isEditing && textareaRef.current) {
      textareaRef.current.focus();
      textareaRef.current.selectionStart = textareaRef.current.value.length;
    }
  }, [isEditing]);

  // Close avatar popup on outside click
  React.useEffect(() => {
    if (!avatarOpen) return;
    const handler = () => setAvatarOpen(false);
    document.addEventListener('mousedown', handler);
    return () => document.removeEventListener('mousedown', handler);
  }, [avatarOpen]);

  const openAvatarPopup = () => {
    const existing = comment.avatar || '';
    if (existing.startsWith('data:')) {
      setAvatarFile(existing);
      setAvatarDraft('');
    } else {
      setAvatarFile('');
      setAvatarDraft(existing);
    }
    setAvatarOpen(true);
  };

  const applyAvatar = () => {
    onSaveAvatar(comment.id, avatarFile || avatarDraft);
    setAvatarOpen(false);
  };

  return (
    <div>
      <div style={{ display: 'flex', gap: 8 }}>

        {/* Avatar — click opens photo popup in edit mode */}
        <div style={{ position: 'relative', flexShrink: 0 }}>
          <div
            style={{ cursor: isEditMode ? 'pointer' : 'default', position: 'relative', display: 'inline-block' }}
            onClick={() => isEditMode && openAvatarPopup()}
          >
            <FbAvatar name={isEditing ? (editDraft.name || '?') : comment.name} size={avSz} avatarUrl={comment.avatar}/>
            {isEditMode && (
              <div style={{ position: 'absolute', bottom: -1, right: -1, width: 16, height: 16, borderRadius: '50%', background: '#1877f2', border: '2px solid #fff', display: 'flex', alignItems: 'center', justifyContent: 'center', pointerEvents: 'none' }}>
                <Icon name="edit" size={8} style={{ color: '#fff' }}/>
              </div>
            )}
          </div>

          {/* Avatar popup */}
          {avatarOpen && (
            <div
              onMouseDown={e => e.stopPropagation()}
              style={{ position: 'absolute', top: avSz + 6, left: 0, zIndex: 200, background: 'var(--bg-surface)', border: '1px solid var(--border)', borderRadius: 'var(--r-xl)', boxShadow: 'var(--shadow-pop)', padding: 16, width: 320, display: 'flex', flexDirection: 'column', gap: 10 }}
            >
              {/* Preview + header */}
              <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                <FbAvatar name={comment.name} size={44} avatarUrl={avatarFile || avatarDraft || undefined}/>
                <div>
                  <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--text)', fontFamily: 'var(--font-display)' }}>Profile photo</div>
                  <div style={{ fontSize: 11, color: 'var(--text-faint)', marginTop: 2 }}>URL, file, or library</div>
                </div>
              </div>

              {/* URL input + file upload */}
              <div style={{ display: 'flex', gap: 6 }}>
                <input
                  autoFocus
                  value={avatarDraft}
                  onChange={e => { setAvatarDraft(e.target.value); setAvatarFile(''); }}
                  onKeyDown={e => { if (e.key === 'Enter') applyAvatar(); if (e.key === 'Escape') setAvatarOpen(false); }}
                  placeholder="https://…"
                  className="fld"
                  style={{ flex: 1, fontSize: 12, padding: '6px 10px' }}
                />
                <input type="file" accept="image/*" ref={fileInputRef} style={{ display: 'none' }}
                  onChange={e => {
                    const file = e.target.files[0];
                    if (!file) return;
                    const reader = new FileReader();
                    reader.onload = ev => { setAvatarFile(ev.target.result); setAvatarDraft(file.name); };
                    reader.readAsDataURL(file);
                    e.target.value = '';
                  }}
                />
                <button className="btn btn-stroke btn-sm" onClick={() => fileInputRef.current && fileInputRef.current.click()} title="Upload from file">
                  <Icon name="upload" size={13}/>
                </button>
                <button className="btn btn-night btn-sm" onClick={applyAvatar}>Apply</button>
              </div>

              {/* Library */}
              <div style={{ borderTop: '1px solid var(--border-soft)', paddingTop: 10, display: 'flex', flexDirection: 'column', gap: 8 }}>
                <div style={{ fontSize: 10, fontWeight: 600, color: 'var(--text-faint)', textTransform: 'uppercase', letterSpacing: '0.06em' }}>Library</div>

                {/* Filters — 3 rows */}
                {[
                  { label: 'Age',    opts: [['all','All'],['young','Young'],['mid','Mid'],['old','Old']],           val: libAge,    set: setLibAge },
                  { label: 'Eth',    opts: [['all','All'],['white','White'],['black','Black'],['mixed','Mixed']],    val: libEth,    set: setLibEth },
                  { label: 'Gender', opts: [['all','All'],['man','Man'],['woman','Woman']],                          val: libGender, set: setLibGender },
                ].map(({ label, opts, val, set }) => (
                  <div key={label} style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
                    <span style={{ fontSize: 10, color: 'var(--text-faint)', width: 38, flexShrink: 0 }}>{label}</span>
                    <div style={{ display: 'flex', gap: 3 }}>
                      {opts.map(([v, l]) => (
                        <button key={v} onClick={() => set(v)} style={{ fontSize: 10, padding: '2px 8px', borderRadius: 20, border: '1px solid', borderColor: val === v ? 'var(--accent)' : 'var(--border)', background: val === v ? 'var(--accent-soft)' : 'transparent', color: val === v ? 'var(--accent)' : 'var(--text-muted)', cursor: 'pointer', fontWeight: val === v ? 600 : 400, whiteSpace: 'nowrap' }}>{l}</button>
                      ))}
                    </div>
                  </div>
                ))}

                {/* Grid */}
                <div style={{ display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 5, maxHeight: 200, overflowY: 'auto' }}>
                  {FB_COMMENT_LIBRARY
                    .filter(p =>
                      (libAge    === 'all' || p.age    === libAge)    &&
                      (libEth    === 'all' || p.eth    === libEth)    &&
                      (libGender === 'all' || p.gender === libGender)
                    )
                    .map((p, i) => (
                      <img key={i} src={p.file} alt=""
                        onClick={() => { onSaveAvatar(comment.id, p.file); setAvatarOpen(false); }}
                        style={{ width: '100%', aspectRatio: '1 / 1', objectFit: 'cover', borderRadius: 8, cursor: 'pointer', border: '2px solid', borderColor: comment.avatar === p.file ? '#1877f2' : 'transparent', boxSizing: 'border-box' }}
                      />
                    ))
                  }
                </div>
              </div>

              {/* Close */}
              <button className="btn btn-ghost btn-sm" onClick={() => setAvatarOpen(false)} style={{ alignSelf: 'flex-end' }}>
                <Icon name="close" size={13}/> Close
              </button>
            </div>
          )}
        </div>

        <div style={{ flex: 1, minWidth: 0 }}>
          {isEditing ? (
            /* ── Inline edit mode ── */
            <>
              <div style={{ background: '#f0f2f5', borderRadius: 18, padding: '8px 14px', border: '2px solid #1877f2' }}>
                <input
                  value={editDraft.name}
                  onChange={e => setEditDraft(d => ({ ...d, name: e.target.value }))}
                  style={{ fontWeight: 700, fontSize: 13, color: '#050505', border: 'none', background: 'transparent', outline: 'none', width: '100%', padding: 0, fontFamily: 'inherit', display: 'block', marginBottom: 4 }}
                />
                <textarea
                  ref={textareaRef}
                  value={editDraft.text}
                  onChange={e => setEditDraft(d => ({ ...d, text: e.target.value }))}
                  rows={2}
                  style={{ fontSize: 14, color: '#050505', lineHeight: 1.45, border: 'none', background: 'transparent', outline: 'none', width: '100%', resize: 'none', fontFamily: 'inherit', padding: 0, display: 'block' }}
                />
              </div>

              {/* Reaction picker + Likes + Time + Actions */}
              <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginTop: 6, paddingLeft: 2, flexWrap: 'wrap' }}>
                <div style={{ display: 'flex', gap: 1 }}>
                  {FB_REACTIONS.map(r => (
                    <button key={r.id} onClick={() => setEditDraft(d => ({ ...d, reaction: r.id }))} title={r.label}
                      style={{ fontSize: 15, width: 28, height: 28, display: 'flex', alignItems: 'center', justifyContent: 'center', border: '2px solid', borderColor: editDraft.reaction === r.id ? '#1877f2' : 'transparent', borderRadius: 6, cursor: 'pointer', background: editDraft.reaction === r.id ? '#e7f0fd' : 'transparent' }}>
                      {r.emoji}
                    </button>
                  ))}
                </div>
                <input type="number" min="0" value={editDraft.likes}
                  onChange={e => setEditDraft(d => ({ ...d, likes: parseInt(e.target.value) || 0 }))}
                  className="fld" style={{ fontSize: 12, padding: '3px 6px', width: 52, height: 28 }}/>
                <select value={editDraft.time} onChange={e => setEditDraft(d => ({ ...d, time: e.target.value }))}
                  className="fld" style={{ fontSize: 12, padding: '3px 6px', height: 28 }}>
                  {FB_TIME_OPTS.map(t => <option key={t} value={t}>{t}</option>)}
                </select>
                <div style={{ flex: 1 }}/>
                <button onClick={onSaveEdit} title="Save" style={{ width: 28, height: 28, borderRadius: '50%', background: '#1877f2', border: 'none', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                  <Icon name="check" size={13} stroke={2.5} style={{ color: '#fff' }}/>
                </button>
                <button onClick={onCancelEdit} title="Cancel" style={{ width: 28, height: 28, borderRadius: '50%', background: '#e4e6eb', border: 'none', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                  <Icon name="close" size={13} stroke={2} style={{ color: '#444' }}/>
                </button>
                <button onClick={() => { onDelete(comment.id); onCancelEdit(); }} title="Delete" style={{ width: 28, height: 28, borderRadius: '50%', background: '#fee2e2', border: 'none', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                  <Icon name="trash" size={13} style={{ color: '#dc2626' }}/>
                </button>
              </div>
            </>
          ) : (
            /* ── Display mode ── */
            <>
              <div
                onClick={() => isEditMode && onStartEdit(comment)}
                onMouseEnter={e => { if (isEditMode) e.currentTarget.style.background = '#e4e6eb'; }}
                onMouseLeave={e => { e.currentTarget.style.background = '#f0f2f5'; }}
                style={{ background: '#f0f2f5', borderRadius: 18, padding: '8px 14px', display: 'inline-block', maxWidth: '100%', cursor: isEditMode ? 'pointer' : 'default', transition: 'background 0.12s' }}
              >
                <div style={{ fontWeight: 700, fontSize: 13, color: '#050505', marginBottom: 2 }}>{comment.name}</div>
                <div style={{ fontSize: 14, color: '#050505', lineHeight: 1.45, wordBreak: 'break-word' }}>{comment.text}</div>
              </div>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginTop: 4, paddingLeft: 8 }}>
                <span style={{ fontSize: 12, color: '#65676b' }}>{comment.time}</span>
                <button style={{ fontSize: 12, fontWeight: 700, color: '#65676b', background: 'none', border: 'none', cursor: 'default', padding: 0 }}>{labels.like}</button>
                <button
                  onClick={() => isEditMode && (isReplying ? onCancelReply() : onStartReply(comment.id))}
                  style={{ fontSize: 12, fontWeight: 700, color: isReplying ? '#1877f2' : '#65676b', background: 'none', border: 'none', cursor: isEditMode ? 'pointer' : 'default', padding: 0 }}
                >{labels.reply}</button>
                {comment.likes > 0 && (
                  <div style={{ display: 'flex', alignItems: 'center', gap: 3, marginLeft: 'auto' }}>
                    <span style={{ fontSize: 12, color: '#65676b' }}>{comment.likes}</span>
                    <span style={{ fontSize: 15, lineHeight: 1 }}>{reactionEmoji}</span>
                  </div>
                )}
              </div>
            </>
          )}
        </div>
      </div>

      {/* Reply input */}
      {isReplying && (
        <div style={{ display: 'flex', gap: 8, marginTop: 10, marginLeft: 48, alignItems: 'center' }}>
          <div style={{ width: 32, height: 32, borderRadius: '50%', background: '#1877f2', flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
            <Icon name="user" size={14} style={{ color: '#fff' }}/>
          </div>
          <div style={{ flex: 1, position: 'relative' }}>
            <input
              autoFocus
              value={replyText}
              onChange={e => setReplyText(e.target.value)}
              onKeyDown={e => {
                if (e.key === 'Enter' && replyText.trim()) onSubmitReply(comment.id);
                if (e.key === 'Escape') onCancelReply();
              }}
              placeholder={`${labels.reply} ${comment.name}…`}
              style={{ width: '100%', background: '#f0f2f5', border: '1.5px solid #1877f2', borderRadius: 20, padding: '7px 44px 7px 16px', fontSize: 14, outline: 'none', fontFamily: 'inherit', boxSizing: 'border-box' }}
            />
            <button
              onClick={() => replyText.trim() && onSubmitReply(comment.id)}
              style={{ position: 'absolute', right: 8, top: '50%', transform: 'translateY(-50%)', background: 'none', border: 'none', cursor: 'pointer', color: replyText.trim() ? '#1877f2' : '#ccd0d5', padding: 4 }}
            ><Icon name="send" size={15}/></button>
          </div>
        </div>
      )}
    </div>
  );
};

const FbCommentGen = () => {
  const [mode, setMode]           = React.useState('edit');
  const [lang, setLang]           = React.useState('en');
  const [tplKey, setTplKey]       = React.useState('weightloss');
  const [comments, setComments]   = React.useState(FB_TEMPLATES.weightloss);
  const [showTpl, setShowTpl]     = React.useState(false);
  const [editingId, setEditingId] = React.useState(null);
  const [editDraft, setEditDraft] = React.useState({});
  const [replyingTo, setReplyingTo] = React.useState(null);
  const [replyText, setReplyText]   = React.useState('');
  const [newText, setNewText]       = React.useState('');
  const [addingNew, setAddingNew]   = React.useState(false);
  const [exporting, setExporting]   = React.useState(false);
  const [exportHeader, setExportHeader]     = React.useState(true);
  const [exportWriteBar, setExportWriteBar] = React.useState(false);
  const [showExportModal, setShowExportModal] = React.useState(false);
  const [exportFormat, setExportFormat]       = React.useState('png');
  const [exportPreviewUrl, setExportPreviewUrl]       = React.useState(null);
  const [exportPreviewLoading, setExportPreviewLoading] = React.useState(false);
  const previewRef = React.useRef(null);

  // Connector measurements: one L-rect per child, placed in the parent wrapper.
  // threadWrapperRefs: parentId -> { el, avSz }
  // threadChildRefs:   parentId -> Map<idx, childEl>
  const threadWrapperRefs = React.useRef(new Map());
  const threadChildRefs   = React.useRef(new Map());
  const [childConnectorHeights, setChildConnectorHeights] = React.useState({});

  React.useLayoutEffect(() => {
    const next = {};
    threadWrapperRefs.current.forEach(({ avSz }, parentId) => {
      const childMap = threadChildRefs.current.get(parentId);
      if (!childMap || childMap.size === 0) return;
      // armCenter = half of child avSz (always 32 at depth >= 1)
      next[parentId] = [...childMap.keys()].sort((a, b) => a - b)
        .map(idx => Math.max(1, childMap.get(idx).offsetTop + 16 - avSz));
    });
    setChildConnectorHeights(prev => {
      const keys = new Set([...Object.keys(prev), ...Object.keys(next)]);
      for (const k of keys) {
        const a = prev[k], b = next[k];
        if (!a || !b || a.length !== b.length || a.some((v, i) => v !== b[i])) return next;
      }
      return prev;
    });
  });

  const buildClone = (options = {}) => {
    const { header = exportHeader, writeBar = exportWriteBar } = options;
    const source = previewRef.current;
    const clone  = source.cloneNode(true);
    if (!header)   clone.querySelectorAll('[data-export-header]').forEach(n => n.remove());
    if (!writeBar) clone.querySelectorAll('[data-export-ignore]').forEach(n => n.remove());
    Object.assign(clone.style, {
      position: 'fixed', top: '-100000px', left: '-100000px',
      width: source.offsetWidth + 'px', background: '#ffffff', margin: '0',
    });
    document.body.appendChild(clone);
    return clone;
  };

  const generatePreview = async (options) => {
    if (!previewRef.current || typeof domtoimage === 'undefined') return;
    setExportPreviewLoading(true);
    const clone = buildClone(options);
    try {
      const url = await domtoimage.toPng(clone, {
        scale: 1, bgcolor: '#ffffff',
        width: clone.offsetWidth, height: clone.scrollHeight,
      });
      setExportPreviewUrl(url);
    } finally {
      document.body.removeChild(clone);
      setExportPreviewLoading(false);
    }
  };

  const handleDownload = async () => {
    if (!previewRef.current || typeof domtoimage === 'undefined') return;
    setExporting(true);
    const clone = buildClone();
    try {
      const render = exportFormat === 'jpeg'
        ? domtoimage.toJpeg(clone, { quality: 0.95, bgcolor: '#ffffff', scale: 2, width: clone.offsetWidth, height: clone.scrollHeight })
        : domtoimage.toPng(clone,  { bgcolor: '#ffffff', scale: 2, width: clone.offsetWidth, height: clone.scrollHeight });
      const dataUrl = await render;
      const link = document.createElement('a');
      link.download = `fb-comments-${Date.now()}.${exportFormat}`;
      link.href = dataUrl;
      link.click();
      setShowExportModal(false);
    } finally {
      document.body.removeChild(clone);
      setExporting(false);
    }
  };

  React.useEffect(() => {
    if (!showExportModal) { setExportPreviewUrl(null); return; }
    generatePreview();
  }, [showExportModal]);

  React.useEffect(() => {
    if (!showExportModal) return;
    generatePreview();
  }, [exportHeader, exportWriteBar]);

  const labels     = FB_LABELS[lang] || FB_LABELS.en;
  const isEditMode = mode === 'edit';

  const rootComments = React.useMemo(
    () => comments.filter(c => c.parentId === null),
    [comments]
  );

  const renderThread = (comment, depth) => {
    const avSz     = depth === 0 ? 40 : 32;
    const hPad     = avSz / 2 + 12;
    const children = comments.filter(x => x.parentId === comment.id);
    const hasChildren = children.length > 0;
    const heights  = childConnectorHeights[comment.id] || [];
    return (
      <div key={comment.id}
        ref={el => {
          if (el) threadWrapperRefs.current.set(comment.id, { el, avSz });
          else    threadWrapperRefs.current.delete(comment.id);
        }}
        style={{ position: 'relative' }}>
        {/* One L-rect per child: borderLeft = vertical leg, borderBottom = horizontal arm.
            Rendered first so content stacks on top without needing z-index. */}
        {children.map((child, idx) => (
          <div key={`connector-${child.id}`} style={{
            position: 'absolute',
            left: avSz / 2 - 1,
            top: avSz,
            width: hPad + 2,
            height: heights[idx] ?? 1,
            borderLeft: '2px solid #ccd0d5',
            borderBottom: '2px solid #ccd0d5',
            borderBottomLeftRadius: 10,
            boxSizing: 'border-box',
            pointerEvents: 'none',
          }}/>
        ))}
        <FbCommentRow
          comment={comment} depth={depth} labels={labels} isEditMode={isEditMode}
          isEditing={isEditMode && editingId === comment.id}
          editDraft={editDraft} setEditDraft={setEditDraft}
          onStartEdit={startEdit} onSaveEdit={saveEdit} onCancelEdit={cancelEdit} onDelete={deleteComment}
          isReplying={isEditMode && replyingTo === comment.id}
          replyText={replyText} setReplyText={setReplyText}
          onStartReply={startReply} onCancelReply={cancelReply} onSubmitReply={submitReply}
          onSaveAvatar={saveAvatar}
        />
        {hasChildren && (
          <div style={{
            marginLeft: avSz / 2 - 1,
            paddingLeft: hPad,
            borderLeft: '2px solid transparent',
            paddingTop: 4,
            display: 'flex',
            flexDirection: 'column',
            gap: 12,
          }}>
            {children.map((child, idx) => (
              <div key={child.id}
                ref={el => {
                  if (!threadChildRefs.current.has(comment.id))
                    threadChildRefs.current.set(comment.id, new Map());
                  const m = threadChildRefs.current.get(comment.id);
                  if (el) m.set(idx, el); else m.delete(idx);
                }}
                style={{ position: 'relative' }}>
                {renderThread(child, depth + 1)}
              </div>
            ))}
          </div>
        )}
      </div>
    );
  };

  // ── Edit handlers ─────────────────────────────────────────────────────────
  const startEdit = (comment) => {
    setReplyingTo(null); setReplyText(''); setAddingNew(false); setNewText('');
    setEditingId(comment.id);
    setEditDraft({ name: comment.name, text: comment.text, time: comment.time, likes: comment.likes, reaction: comment.reaction || 'like' });
  };
  const saveEdit = () => {
    setComments(cs => cs.map(c => c.id === editingId ? { ...c, ...editDraft } : c));
    setEditingId(null);
  };
  const saveAvatar = React.useCallback((id, url) => {
    setComments(cs => cs.map(c => c.id === id ? { ...c, avatar: url } : c));
  }, []);
  const cancelEdit = () => setEditingId(null);
  const deleteComment = (id) => { setComments(cs => cs.filter(c => c.id !== id && c.parentId !== id)); setEditingId(null); };

  // ── Reply handlers ────────────────────────────────────────────────────────
  const startReply = (parentId) => {
    setEditingId(null); setAddingNew(false); setNewText('');
    setReplyingTo(parentId); setReplyText('');
  };
  const cancelReply  = () => { setReplyingTo(null); setReplyText(''); };
  const submitReply  = (parentId) => {
    if (!replyText.trim()) return;
    setComments(cs => [...cs, { id: _fbMkId(), name: 'New User', text: replyText.trim(), time: 'just now', likes: 0, avatar: '', reaction: 'like', parentId }]);
    setReplyingTo(null); setReplyText('');
  };

  // ── New comment handlers ──────────────────────────────────────────────────
  const activateNew = () => { setEditingId(null); setReplyingTo(null); setAddingNew(true); setNewText(''); };
  const cancelNew   = () => { setAddingNew(false); setNewText(''); };
  const submitNew   = () => {
    if (!newText.trim()) return;
    setComments(cs => [...cs, { id: _fbMkId(), name: 'New User', text: newText.trim(), time: 'just now', likes: 0, avatar: '', reaction: 'like', parentId: null }]);
    setAddingNew(false); setNewText('');
  };

  // Reset interactive state when switching modes
  const switchMode = (m) => {
    setMode(m); setEditingId(null); setReplyingTo(null); setAddingNew(false); setReplyText(''); setNewText('');
  };

  React.useEffect(() => {
    if (!showTpl) return;
    const close = () => setShowTpl(false);
    document.addEventListener('click', close);
    return () => document.removeEventListener('click', close);
  }, [showTpl]);

  return (
    <>
      {/* ── Toolbar ─────────────────────────────────────────────────────────── */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 16, flexWrap: 'wrap' }}>

        {/* Templates */}
        <div style={{ position: 'relative' }}>
          <button className="btn btn-stroke btn-sm" onClick={e => { e.stopPropagation(); setShowTpl(v => !v); }} style={{ gap: 6 }}>
            <Icon name="folder" size={14}/> Templates <Icon name="chevron-down" size={12}/>
          </button>
          {showTpl && (
            <div onClick={e => e.stopPropagation()} style={{
              position: 'absolute', top: 'calc(100% + 6px)', left: 0, zIndex: 50,
              background: 'var(--bg-surface)', border: '1px solid var(--border)',
              borderRadius: 'var(--r-lg)', boxShadow: 'var(--shadow-pop)', minWidth: 160, overflow: 'hidden',
            }}>
              {[['weightloss','Weight loss'],['skincare','Skincare'],['fitness','Fitness']].map(([key, label]) => (
                <button key={key}
                  onClick={() => { setComments(FB_TEMPLATES[key]); setTplKey(key); setShowTpl(false); }}
                  style={{ display: 'block', width: '100%', textAlign: 'left', padding: '9px 14px', fontSize: 13, border: 'none', cursor: 'pointer', background: tplKey === key ? 'var(--accent-soft)' : 'transparent', color: tplKey === key ? 'var(--accent)' : 'var(--text)' }}>
                  {label}
                </button>
              ))}
            </div>
          )}
        </div>

        {/* Edit / Preview toggle */}
        <div style={{ flex: 1, display: 'flex', justifyContent: 'center' }}>
          <div style={{ display: 'flex', padding: 3, background: 'var(--bg-soft)', borderRadius: 'var(--r-pill)', border: '1px solid var(--border)' }}>
            {['edit','preview'].map(m => (
              <button key={m} onClick={() => switchMode(m)} style={{
                padding: '5px 18px', borderRadius: 'var(--r-pill)',
                fontSize: 13, fontWeight: 600, border: 'none', cursor: 'pointer',
                background: mode === m ? '#0e121b' : 'transparent',
                color: mode === m ? '#fff' : 'var(--text-muted)',
                transition: 'all 0.15s',
              }}>{m.charAt(0).toUpperCase() + m.slice(1)}</button>
            ))}
          </div>
        </div>

        {/* Lang */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 6, background: 'var(--bg-surface)', border: '1px solid var(--border)', borderRadius: 'var(--r-pill)', padding: '5px 12px' }}>
          <span style={{ fontSize: 11, color: 'var(--text-faint)', fontWeight: 500, textTransform: 'uppercase', letterSpacing: '0.04em' }}>Lang</span>
          <select value={lang} onChange={e => setLang(e.target.value)} style={{ border: 'none', background: 'none', fontSize: 13, fontWeight: 600, color: 'var(--text)', cursor: 'pointer', outline: 'none', padding: 0 }}>
            {[['en','EN'],['fr','FR'],['de','DE'],['es','ES'],['pt','PT']].map(([v,l]) => <option key={v} value={v}>{l}</option>)}
          </select>
        </div>

        {/* Export */}
        <button className="btn btn-night btn-sm" onClick={() => setShowExportModal(true)} style={{ gap: 6 }}>
          <Icon name="export" size={14}/> Export PNG
        </button>
      </div>

      {/* ── FB preview (shared by both modes; edit mode adds interactivity) ── */}
      <div style={{ background: 'var(--bg-surface)', borderRadius: 'var(--r-xl)', border: '1px solid var(--border)', padding: 24 }}>
        {isEditMode && (
          <p style={{ margin: '0 0 12px', fontSize: 12, color: 'var(--text-faint)' }}>
            Click a comment to edit it · Click <strong>Reply</strong> to add a reply · Click the input below to add a comment
          </p>
        )}
        <div ref={previewRef} style={{ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif' }}>
          <h2 data-export-header="true" style={{ fontSize: 24, fontWeight: 800, color: 'var(--text)', margin: '0 0 16px 0' }}>
            {labels.comments} ({comments.length})
          </h2>
          <div style={{ background: '#fff', borderRadius: 12, border: '1px solid #e4e6eb', padding: '16px 16px 12px', display: 'flex', flexDirection: 'column', gap: 16 }}>
            {rootComments.map(c => renderThread(c, 0))}

            {/* Write a comment bar */}
            <div data-export-ignore="true" style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 4 }}>
              <div style={{ width: 36, height: 36, borderRadius: '50%', background: '#1877f2', flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                <Icon name="user" size={16} style={{ color: '#fff' }}/>
              </div>
              {isEditMode && addingNew ? (
                <div style={{ flex: 1, position: 'relative' }}>
                  <input
                    autoFocus
                    value={newText}
                    onChange={e => setNewText(e.target.value)}
                    onKeyDown={e => { if (e.key === 'Enter' && newText.trim()) submitNew(); if (e.key === 'Escape') cancelNew(); }}
                    placeholder={labels.write}
                    style={{ width: '100%', background: '#f0f2f5', border: '1.5px solid #1877f2', borderRadius: 20, padding: '7px 44px 7px 16px', fontSize: 14, outline: 'none', fontFamily: 'inherit', boxSizing: 'border-box' }}
                  />
                  <button onClick={submitNew} style={{ position: 'absolute', right: 8, top: '50%', transform: 'translateY(-50%)', background: 'none', border: 'none', cursor: 'pointer', color: newText.trim() ? '#1877f2' : '#ccd0d5', padding: 4 }}>
                    <Icon name="send" size={15}/>
                  </button>
                </div>
              ) : (
                <div
                  onClick={isEditMode ? activateNew : undefined}
                  style={{ flex: 1, background: '#f0f2f5', borderRadius: 20, padding: '8px 16px', fontSize: 14, color: '#65676b', cursor: isEditMode ? 'pointer' : 'default', userSelect: 'none' }}
                >{labels.write}</div>
              )}
            </div>
          </div>
        </div>
      </div>

      {/* ── Export Modal ──────────────────────────────────────────────────────── */}
      {showExportModal && (
        <div
          onClick={() => setShowExportModal(false)}
          style={{ position: 'fixed', inset: 0, zIndex: 1000, background: 'rgba(0,0,0,0.55)', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 24 }}
        >
          <div onClick={e => e.stopPropagation()} style={{
            background: 'var(--bg-surface)', borderRadius: 'var(--r-xl)',
            border: '1px solid var(--border)', boxShadow: 'var(--shadow-pop)',
            width: '100%', maxWidth: 560, display: 'flex', flexDirection: 'column', overflow: 'hidden',
          }}>
            {/* Header */}
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '16px 20px', borderBottom: '1px solid var(--border-soft)' }}>
              <div style={{ fontWeight: 700, fontSize: 15, color: 'var(--text)' }}>Export image</div>
              <button onClick={() => setShowExportModal(false)} style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--text-muted)', padding: 4, borderRadius: 6, display: 'flex' }}>
                <Icon name="close" size={16}/>
              </button>
            </div>

            {/* Preview */}
            <div style={{ padding: '20px 20px 0', maxHeight: 380, overflowY: 'auto' }}>
              {exportPreviewLoading ? (
                <div style={{ height: 180, display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'var(--bg-soft)', borderRadius: 10, color: 'var(--text-faint)', fontSize: 13 }}>
                  Génération de la preview…
                </div>
              ) : exportPreviewUrl ? (
                <img src={exportPreviewUrl} alt="preview" style={{ width: '100%', borderRadius: 10, border: '1px solid var(--border)', display: 'block' }}/>
              ) : null}
            </div>

            {/* Options */}
            <div style={{ padding: '18px 20px', display: 'flex', flexDirection: 'column', gap: 14 }}>
              {/* Format */}
              <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
                <span style={{ fontSize: 12, fontWeight: 600, color: 'var(--text-muted)', minWidth: 64 }}>Format</span>
                <div style={{ display: 'flex', gap: 6 }}>
                  {[['png','PNG'],['jpeg','JPEG']].map(([v,l]) => (
                    <button key={v} onClick={() => setExportFormat(v)} style={{
                      padding: '5px 16px', fontSize: 12, fontWeight: 600, borderRadius: 'var(--r-pill)',
                      border: '1px solid', cursor: 'pointer', transition: 'all 0.15s',
                      borderColor: exportFormat === v ? 'var(--accent)' : 'var(--border)',
                      background: exportFormat === v ? 'var(--accent-soft)' : 'transparent',
                      color: exportFormat === v ? 'var(--accent)' : 'var(--text-muted)',
                    }}>{l}</button>
                  ))}
                </div>
              </div>
              {/* Include */}
              <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
                <span style={{ fontSize: 12, fontWeight: 600, color: 'var(--text-muted)', minWidth: 64 }}>Inclure</span>
                <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
                  {[
                    ['header',   'Header Comments',      exportHeader,   setExportHeader],
                    ['writebar', 'Barre de commentaire', exportWriteBar, setExportWriteBar],
                  ].map(([key, label, val, set]) => (
                    <button key={key} onClick={() => set(v => !v)} style={{
                      display: 'flex', alignItems: 'center', gap: 5,
                      padding: '5px 12px', fontSize: 12, fontWeight: 500, borderRadius: 'var(--r-pill)',
                      border: '1px solid', cursor: 'pointer', transition: 'all 0.15s',
                      borderColor: val ? 'var(--accent)' : 'var(--border)',
                      background: val ? 'var(--accent-soft)' : 'transparent',
                      color: val ? 'var(--accent)' : 'var(--text-muted)',
                    }}>
                      <span style={{ fontSize: 10 }}>{val ? '✓' : '○'}</span> {label}
                    </button>
                  ))}
                </div>
              </div>
            </div>

            {/* Footer */}
            <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, padding: '12px 20px 20px', borderTop: '1px solid var(--border-soft)' }}>
              <button className="btn btn-stroke btn-sm" onClick={() => setShowExportModal(false)}>Annuler</button>
              <button className="btn btn-night btn-sm" onClick={handleDownload} disabled={exporting} style={{ gap: 6 }}>
                <Icon name="export" size={14}/> {exporting ? 'Téléchargement…' : `Télécharger en ${exportFormat.toUpperCase()}`}
              </button>
            </div>
          </div>
        </div>
      )}
    </>
  );
};

// ─────────────────────────────────────────────────────────────────────────────
// Metadata + Router
// ─────────────────────────────────────────────────────────────────────────────
const CALC_META = {
  ltv: {
    name: 'Customer LTV', icon: 'user', subtitle: 'Lifetime value & max CAC per customer',
    questions: [
      'What is a healthy LTV:CAC ratio for a DTC brand?',
      'What retention strategies can increase customer lifespan?',
      'How does LTV change with subscription vs one-time purchases?',
    ],
  },
  roas: {
    name: 'ROAS Break-Even', icon: 'wallet', subtitle: 'Min ROAS & max CPA to stay profitable',
    questions: [
      'What is a realistic target ROAS for Meta ads in ecom?',
      'How should I adjust my ROAS target as I scale ad spend?',
      'Should I optimize for ROAS or CPA — and when to switch?',
    ],
  },
  margin: {
    name: 'Profit Margin', icon: 'graph', subtitle: 'Gross, operating & net margin in one view',
    questions: [
      'What is a healthy net margin for an ecommerce store?',
      'How do I calculate MER and why does it matter more than ROAS?',
      'What costs belong in OPEX vs COGS for an ecom P&L?',
    ],
  },
  shopify: {
    name: 'Shopify Plan', icon: 'shop', subtitle: 'Find the most cost-effective plan for your GMV',
    questions: [
      'When does it make sense to switch from Basic to Shopify plan?',
      'Is Shopify Payments available and worth it in Europe?',
      'What hidden fees should I account for on Shopify?',
    ],
  },
  adbudget: {
    name: 'Ad Budget', icon: 'trend-up', subtitle: 'Required ad spend to hit revenue targets',
    questions: [
      'What percentage of revenue should I spend on ads at different stages?',
      'How do I scale my ad budget without tanking ROAS?',
      'What is a good MER (blended ROAS) benchmark for Shopify stores?',
    ],
  },
  breakeven: {
    name: 'Break-Even Units', icon: 'package', subtitle: 'Units needed per month to cover fixed costs',
    questions: [
      'How can I lower my break-even point without cutting price?',
      'What is contribution margin and how does it relate to profitability?',
      'Which fixed costs should I include when calculating break-even?',
    ],
  },
  markup: {
    name: 'Markup / Margin', icon: 'tag', subtitle: 'Convert between markup % and margin %',
    questions: [
      'What is the difference between markup and margin — and why does it matter?',
      'What markup multiple is typical for Shopify dropshipping vs private label?',
      'How should I price my product to achieve a 60% gross margin?',
    ],
  },
  fbcomment: {
    name: 'FB Comment Generator', icon: 'meta', subtitle: 'Design realistic Facebook comment sections',
  },
};

const CALC_COMPONENTS = {
  ltv: LTVCalc, roas: RoasBeCalc, margin: ProfitMarginCalc, shopify: ShopifyPlanCalc,
  adbudget: AdBudgetCalc, breakeven: BreakevenCalc, markup: MarkupCalc,
  fbcomment: FbCommentGen,
};

const ToolCalcRouter = ({ id, onBack, onNav, onAskAI }) => {
  const Comp = CALC_COMPONENTS[id];
  return (
    <CalcLayout id={id} onBack={onBack} onAskAI={onAskAI}>
      {Comp ? <Comp /> : <div style={{ color: 'var(--text-faint)' }}>Calculator not found.</div>}
    </CalcLayout>
  );
};

Object.assign(window, { ToolCalcRouter, CALC_META });
