/* coopmgr.jsx — RaDaConnect COOP-MANAGER (mobile-first PWA surface).
 * Web-first per the 2026-07-02 coop-demo brief: coop managers triage + oversee
 * from a phone/desk (farmers + agents stay native on Aegis). Screens: Connect
 * (home = wireframes 1a+1b merged), Wanachama (roster), Ramani (off demo path),
 * Kazi (tasks), Mimi (profile). Farm -> Point/Spot Kuza drill lands in Phase 2.
 * Data: window.COOP (coop_data.js) — Othaya FCS identity + a flagged demo
 * portfolio. Primitives reused from core.jsx. P001: pictures over numbers, names
 * stay names, status ramp for readings, all copy via the COOP dictionary. */
const { useState, useMemo, useEffect } = React;
const C = window.COOP;
const DIRECTORY_URL = 'https://europe-west1-root-range-465417-m6.cloudfunctions.net/coop_directory';
const COOP_DASH_URL = 'https://get-coop-dashboard-5bsfvjejza-ew.a.run.app';
const FB_CONFIG = { apiKey: "AIzaSyAFQ37zvZAD4vWIE5nwuCxzHUeCY8P-m5s", authDomain: "root-range-465417-m6.firebaseapp.com", projectId: "root-range-465417-m6", storageBucket: "root-range-465417-m6.firebasestorage.app", messagingSenderId: "6353794009", appId: "1:6353794009:web:09a6a1ffa464dfea445fc8" };
/* normalise to one canonical digit form: 07XX/7XX -> 2547XX (matches provision_coop_manager.py) */
const phoneToEmail = phone => {
  let d = String(phone).replace(/\D/g, '');
  if (d.length === 10 && d[0] === '0') d = '254' + d.slice(1);
  else if (d.length === 9 && (d[0] === '7' || d[0] === '1')) d = '254' + d;
  return d + '@coops.radaconnect.app';
};
/* the warehouse stores 'Rada ...'; the brand is RaDa */
const radaBrand = v => v == null ? v : String(v).replace(/\bRada\b/g, 'RaDa');
const MIX_COLORS = ['var(--forest)', 'var(--ink-mute)', 'var(--orange)', 'var(--blue)'];

/* live get_coop_dashboard payload -> the app's portfolio shape (same fields as demo) */
function buildLiveP(ld) {
  const farms = (ld.farms || []).map(f => ({
    id: f.farm_id, farmer: f.name || f.farm_id,
    name: (f.crop || '—') + (f.county ? ' · ' + f.county : ''),
    ward: f.county || '—', factory: null, area: f.area_ha,
    status: f.status, driver: f.driver, ndvi: f.ndvi, ndmi: f.ndmi, crop: f.crop || null,
    trend: (f.trend || []).filter(t => t != null), cohort: null,
    score: f.ndvi != null ? Math.round(f.ndvi * 100) : null,
    verified: !!f.verified, hasData: !!f.has_data, live: true, stage: null,
    lat: (f.lat != null ? f.lat : null), lng: (f.lng != null ? f.lng : null),
  }));
  const need = farms.filter(f => f.status === 'red' && f.hasData);
  const driverCounts = need.reduce((a, f) => { a[f.driver] = (a[f.driver] || 0) + 1; return a; }, {});
  const mixCount = {};
  farms.forEach(f => { const c = (f.name || '').split(' · ')[0]; mixCount[c] = (mixCount[c] || 0) + 1; });
  const mixTop = Object.entries(mixCount).sort((a, b) => b[1] - a[1]).slice(0, 4);
  const s = ld.summary || {};
  return {
    live: true, FARMS: farms, needAttention: need, driverCounts,
    health: { green: s.green || 0, amber: s.amber || 0, red: s.red || 0 },
    noData: s.no_data || 0, verifiedCount: s.verified || 0,
    members: s.farms || farms.length, hectares: s.hectares,
    cropMix: mixTop.map(([crop, n], i) => ({ crop, cropSw: crop, pct: Math.round(100 * n / (farms.length || 1)), color: MIX_COLORS[i % MIX_COLORS.length] })),
    asOf: s.as_of || null, method: ld.method || '',
    thresholds: ld.thresholds || { ndvi_green: 0.5, ndvi_amber: 0.32, ndmi_dry: 0.25 },
  };
}
/* live CRM directory -> switcher entries. "~11,000 members" -> 11000; short name from the long one. */
function dirToCoop(d) {
  const digits = String(d.members || '').replace(/[^\d]/g, '');
  const short = String(d.name || '').replace(/\s*\(.*\)\s*/g, ' ').replace(/co-?operative|society|union|limited|ltd\.?/gi, '').trim().split(/\s+/).slice(0, 2).join(' ');
  return { id: 'dir_' + d.id, short: short || d.name, name: d.name, county: d.county || '—', crop: d.crops || d.type || '', cropSw: d.crops || d.type || '', membersTotal: digits ? +digits : 0 };
}
const RAMP = { green: 'var(--green)', amber: 'var(--amber)', red: 'var(--red)', no_data: 'var(--ink-mute)', nodata: 'var(--ink-mute)' };
const SOFT = { green: 'var(--green-soft)', amber: 'var(--amber-soft)', red: 'var(--red-soft)', no_data: 'var(--bg-cream-dim)', nodata: 'var(--bg-cream-dim)' };
const INK = { green: 'var(--green-deep)', amber: 'var(--amber-ink)', red: 'var(--red-ink)', no_data: 'var(--ink-soft)', nodata: 'var(--ink-soft)' };
let deferredPrompt = null; // PWA install prompt (captured for the Mimi install button)
window.addEventListener('beforeinstallprompt', e => { e.preventDefault(); deferredPrompt = e; });

function s(css) {
  const o = {};
  String(css).split(';').forEach(d => { const i = d.indexOf(':'); if (i < 0) return; const k = d.slice(0, i).trim(); if (!k) return; o[k.startsWith('--') ? k : k.replace(/-([a-z])/g, (_, c) => c.toUpperCase())] = d.slice(i + 1).trim(); });
  return o;
}
const merge = (css, x) => Object.assign(s(css), x || {});
const initials = name => name.replace(/[^A-Za-z. ]/g, '').split(/[ .]+/).filter(Boolean).slice(0, 2).map(w => w[0]).join('').toUpperCase();
/* no containers — open blocks on the ground, a single hairline above */
const CARD = "background:transparent;border-top:1px solid var(--line);border-radius:0;padding:14px 2px 4px";

/* ---- small building blocks ---- */
function Avatar({ name, size = 34 }) {
  return <span style={merge("display:inline-flex;align-items:center;justify-content:center;border-radius:50%;background:var(--bg-cream-dim);border:1px solid var(--line);font-family:var(--font-mono);color:var(--ink-soft);flex:0 0 auto", { width: size, height: size, fontSize: size * 0.34 })}>{initials(name)}</span>;
}
function Priority({ st, t }) {
  const lbl = st === 'red' ? t('risk') : st === 'amber' ? t('watch') : (st === 'no_data' || st === 'nodata') ? t('noRecent') : t('good');
  return <span className="schip" style={{ background: SOFT[st] || SOFT.nodata, color: INK[st] || INK.nodata }}><span className="sdot" style={{ width: 8, height: 8, background: RAMP[st] || RAMP.nodata }} />{lbl}</span>;
}
function DriverLine({ f, t, lang }) {
  const d = C.DRIVERS[f.driver] || C.DRIVERS.healthy;
  return <span style={s("display:inline-flex;align-items:center;gap:6px;font-size:12.5px;color:var(--ink)")}><span style={s("font-size:14px")}>{d.glyph}</span>{lang === 'sw' ? d.sw : d.en}</span>;
}
function Donut({ segs, size = 78 }) { // segs: [{v,color}]
  const total = segs.reduce((a, x) => a + x.v, 0) || 1; let acc = 0;
  const stops = segs.map(x => { const a = acc / total * 100, b = (acc += x.v) / total * 100; return `${x.color} ${a}% ${b}%`; }).join(',');
  return <span style={merge("position:relative;display:inline-block;border-radius:50%;flex:0 0 auto", { width: size, height: size, background: `conic-gradient(${stops})` })}><span style={merge("position:absolute;background:var(--bg-paper);border-radius:50%", { inset: size * 0.22 })} /></span>;
}
function SegBar({ segs, h = 12, onSeg }) {
  return <div style={merge("display:flex;width:100%;border-radius:7px;overflow:hidden;border:1px solid var(--line)", { height: h })}>
    {segs.map((x, i) => <div key={i} onClick={onSeg ? () => onSeg(x.key) : undefined} title={x.label} style={merge("height:100%", { flex: x.v, background: x.color, cursor: onSeg ? 'pointer' : 'default' })} />)}
  </div>;
}
function Section({ title, right }) {
  return <div style={s("display:flex;justify-content:space-between;align-items:baseline;margin-top:4px")}>
    <span className="eyebrow">{title}</span>{right}</div>;
}

/* ---- member card (triage feed) ---- */
function MemberCard({ f, t, lang, onOpen, onAssign }) {
  return <div style={s(CARD + ";display:flex;flex-direction:column;gap:9px")}>
    <div onClick={() => onOpen(f.id)} style={s("display:flex;justify-content:space-between;align-items:flex-start;cursor:pointer;gap:8px")}>
      <div style={s("display:flex;gap:9px;align-items:center;min-width:0")}>
        <Avatar name={f.farmer} />
        <div style={s("min-width:0")}><div style={s("font-size:13.5px;font-weight:700;color:var(--ink);line-height:1.1")}>{f.farmer}</div>
          <div style={s("font-size:11.5px;color:var(--ink-mute)")}>{f.name}{!f.live ? ' · ' + (lang === 'sw' ? C.COOP.cropSw : C.COOP.crop) : ''}{f.area != null ? ' · ' + f.area + ' ha' : ''}</div></div>
      </div>
      <Priority st={f.status} t={t} />
    </div>
    <div style={s("display:flex;justify-content:space-between;align-items:center;gap:8px")}>
      <DriverLine f={f} t={t} lang={lang} />
      {f.trend && f.trend.length > 1 && <div style={s("width:64px")}><Sparkline series={f.trend} baseline={f.cohort || undefined} w={64} h={20} color={RAMP[f.status]} fill={false} dot /></div>}
    </div>
    <div style={s("display:flex;gap:6px")}>
      <button className="btn" style={s("flex:1;font-size:11.5px;padding:6px 8px")} onClick={() => onAssign(f)}>{t('assign')}</button>
      <button className="btn" style={s("flex:1;font-size:11.5px;padding:6px 8px")} onClick={() => onOpen(f.id)}>{t('flag')}</button>
      <button className="btn" style={s("flex:1;font-size:11.5px;padding:6px 8px")}>{t('message')}</button>
    </div>
  </div>;
}

/* ---- Connect (home) — 1a + 1b merged ---- */
function Connect({ P, t, lang, onOpen, onAssign, filter, setFilter, goRoster, onOpenEudr }) {
  const H = P.health, need = P.needAttention;
  const dc = P.driverCounts;
  const chips = Object.entries(dc).sort((a, b) => b[1] - a[1]).filter(x => x[1] && C.DRIVERS[x[0]]);
  const shown = filter === 'all' ? need : need.filter(f => f.driver === filter);
  const ymax = Math.max.apply(null, C.yieldSeries);
  return <div style={s("display:flex;flex-direction:column;gap:13px")}>
    {P.live && <div style={s("display:flex;align-items:center;gap:9px;background:var(--green-soft);border:1px solid var(--line);border-radius:10px;padding:8px 12px")}>
      <span className="sdot" style={{ width: 9, height: 9, background: 'var(--green)' }} />
      <span style={s("font-size:11.5px;color:var(--green-deep);line-height:1.4")}>{t('liveNote')}</span>
    </div>}
    {/* overview strip */}
    <div style={s("display:grid;grid-template-columns:1fr 1fr;gap:9px")}>
      <div style={s(CARD)}><div style={s("font-size:22px;font-weight:700;color:var(--ink)")}>{P.members}</div><div style={s("font-size:11.5px;color:var(--ink-mute)")}>{t('members')}</div></div>
      <div style={s(CARD)}><div style={s("font-size:22px;font-weight:700;color:var(--ink)")}>{P.hectares != null ? P.hectares : '—'}</div><div style={s("font-size:11.5px;color:var(--ink-mute)")}>{t('ha')}</div></div>
      <div style={s(CARD + ";grid-column:1 / -1;display:flex;flex-direction:column;gap:7px")}>
        <Section title={t('healthDist')} />
        <SegBar segs={[{ key: 'green', v: H.green, color: RAMP.green, label: t('good') }, { key: 'amber', v: H.amber, color: RAMP.amber, label: t('watch') }, { key: 'red', v: H.red, color: RAMP.red, label: t('risk') }]} onSeg={goRoster} />
        <div style={s("display:flex;gap:14px;flex-wrap:wrap")}>
          <span style={s("font-size:11.5px;color:var(--ink-soft)")}><span className="sdot" style={{ width: 8, height: 8, background: RAMP.green, marginRight: 5 }} />{H.green} {t('good')}</span>
          <span style={s("font-size:11.5px;color:var(--ink-soft)")}><span className="sdot" style={{ width: 8, height: 8, background: RAMP.amber, marginRight: 5 }} />{H.amber} {t('watch')}</span>
          <span style={s("font-size:11.5px;color:var(--ink-soft)")}><span className="sdot" style={{ width: 8, height: 8, background: RAMP.red, marginRight: 5 }} />{H.red} {t('risk')}</span>
        </div>
      </div>
    </div>

    {/* yield forecast — demo only (no defensible live method yet; Mariia owns it) */}
    {!P.live && <div style={s(CARD + ";display:flex;flex-direction:column;gap:8px")}>
      <Section title={t('yieldTitle')} right={<span style={s("font-size:11px;color:var(--green-deep);font-weight:700")}>+12% {t('vsSeason')}</span>} />
      <div style={s("display:flex;align-items:flex-end;gap:5px;height:54px")}>
        {C.yieldSeries.map((v, i) => <div key={i} style={merge("flex:1;border-radius:3px 3px 0 0", { height: Math.round(v / ymax * 100) + '%', background: i >= 5 ? 'var(--orange)' : 'var(--bg-cream-dim)' })} />)}
      </div>
      <div style={s("display:flex;justify-content:space-between")}><span style={s("font-size:10.5px;color:var(--ink-mute)")}>{t('now')}</span><span style={s("font-size:10.5px;color:var(--orange-deep)")}>{t('forecast')} →</span></div>
    </div>}

    {/* crop mix (live = computed from the real member farms) */}
    {P.cropMix && P.cropMix.length > 0 && <div style={s(CARD + ";display:flex;gap:14px;align-items:center")}>
      <Donut segs={P.cropMix.map(c => ({ v: c.pct, color: c.color }))} />
      <div style={s("display:flex;flex-direction:column;gap:6px")}>
        <span className="eyebrow">{t('cropMix')}</span>
        {P.cropMix.map((c, i) => <span key={i} style={s("font-size:12px;color:var(--ink-soft)")}><span className="sdot" style={{ width: 8, height: 8, background: c.color, marginRight: 6 }} />{lang === 'sw' ? c.cropSw : c.crop} · {c.pct}%</span>)}
      </div>
    </div>}

    {/* market access / EUDR (coffee coop -> export proof) */}
    <div onClick={onOpenEudr} style={s(CARD + ";display:flex;align-items:center;gap:11px;cursor:pointer;border-left:3px solid var(--green)")}>
      <span style={s("width:38px;height:38px;border-radius:9px;background:var(--green-soft);display:flex;align-items:center;justify-content:center;flex:0 0 auto")}><Icon d="globe" size={20} color="var(--green-deep)" /></span>
      <div style={s("flex:1;min-width:0")}><div style={s("font-size:13.5px;font-weight:700;color:var(--ink)")}>{t('marketAccess')}</div><div style={s("font-size:11.5px;color:var(--ink-mute)")}>{t('marketSub')}</div></div>
      <Icon d="chevron" size={16} color="var(--ink-mute)" />
    </div>

    {/* triage feed */}
    <Section title={t('needHelp') + ' · ' + need.length} right={<span style={s("font-size:11px;color:var(--orange-deep)")}>{t('sortUrgent')} ↓</span>} />
    <div style={s("display:flex;gap:6px;flex-wrap:wrap")}>
      <button className={"tag"} onClick={() => setFilter('all')} style={merge("cursor:pointer;border:1px solid var(--line)", filter === 'all' ? { background: 'var(--orange)', color: '#0A0C10' } : {})}>{t('all')} {need.length}</button>
      {chips.map(([k, n], i) => { const d = C.DRIVERS[k]; return <button key={i} className="tag" onClick={() => setFilter(k)} style={merge("cursor:pointer;border:1px solid var(--line)", filter === k ? { background: 'var(--orange)', color: '#0A0C10' } : {})}>{d.glyph} {n}</button>; })}
    </div>
    {shown.map(f => <MemberCard key={f.id} f={f} t={t} lang={lang} onOpen={onOpen} onAssign={onAssign} />)}
    <div style={s("font-size:11px;color:var(--ink-mute);font-style:italic;text-align:center;padding:2px 8px")}>↳ {t('tapFarm')}</div>
  </div>;
}

/* ---- Wanachama (roster) — 1c ---- */
function Roster({ P, active, t, lang, onOpen }) {
  const [q, setQ] = useState('');
  const [flt, setFlt] = useState('all');
  const [cropF, setCropF] = useState('');
  const [srt, setSrt] = useState('worst');
  const cname = f => cropDisp(f, lang === 'sw' ? active.cropSw : active.crop, lang);
  const cropCounts = {};
  P.FARMS.forEach(f => { const c = cname(f); if (c !== '—') cropCounts[c] = (cropCounts[c] || 0) + 1; });
  const chips = Object.entries(cropCounts).sort((a, b) => b[1] - a[1]).slice(0, 6);
  const order = { red: 0, amber: 1, green: 2, no_data: 3, nodata: 3 };
  const ord = st => order[st] !== undefined ? order[st] : 4;
  let rows = P.FARMS.slice();
  if (cropF) rows = rows.filter(f => cname(f) === cropF);
  const H = { green: 0, amber: 0, red: 0 };
  rows.forEach(f => { if (H[f.status] !== undefined) H[f.status]++; });
  if (flt !== 'all') rows = rows.filter(f => f.status === flt);
  if (q) rows = rows.filter(f => (farmLabel(f) + ' ' + (f.name || '') + ' ' + (f.ward || '')).toLowerCase().includes(q.toLowerCase()));
  if (srt === 'worst') rows.sort((a, b) => (ord(a.status) - ord(b.status)) || ((a.ndvi != null ? a.ndvi : 9) - (b.ndvi != null ? b.ndvi : 9)));
  else if (srt === 'low') rows.sort((a, b) => (a.ndvi != null ? a.ndvi : 9) - (b.ndvi != null ? b.ndvi : 9));
  else if (srt === 'high') rows.sort((a, b) => (b.ndvi != null ? b.ndvi : -9) - (a.ndvi != null ? a.ndvi : -9));
  else rows.sort((a, b) => farmLabel(a).localeCompare(farmLabel(b)));
  const SORTS = lang === 'sw'
    ? [['worst', 'Mbaya kwanza'], ['low', 'NDVI chini'], ['high', 'NDVI juu'], ['name', 'Jina']]
    : [['worst', 'Worst first'], ['low', 'NDVI low'], ['high', 'NDVI high'], ['name', 'Name']];
  return <div style={s("display:flex;flex-direction:column;gap:11px")}>
    <SegBar h={13} segs={[{ key: 'green', v: H.green, color: RAMP.green }, { key: 'amber', v: H.amber, color: RAMP.amber }, { key: 'red', v: H.red, color: RAMP.red }]} onSeg={k => setFlt(flt === k ? 'all' : k)} />
    <div style={s("display:flex;gap:12px")}>
      {['green', 'amber', 'red'].map(k => <button key={k} className="tag" onClick={() => setFlt(flt === k ? 'all' : k)} style={merge("cursor:pointer;border:1px solid var(--line)", flt === k ? { background: RAMP[k], color: '#fff', borderColor: RAMP[k] } : {})}>{H[k]} {t(k === 'green' ? 'good' : k === 'amber' ? 'watch' : 'risk')}</button>)}
    </div>
    {chips.length > 1 && <div className="fchips" style={s("margin-top:0")}>
      <button className={'fchip' + (cropF === '' ? ' on' : '')} onClick={() => setCropF('')}>{lang === 'sw' ? 'Zote' : 'All'} · {P.FARMS.length}</button>
      {chips.map(c => <button key={c[0]} className={'fchip' + (cropF === c[0] ? ' on' : '')} onClick={() => setCropF(cropF === c[0] ? '' : c[0])}>{c[0]} · {c[1]}</button>)}
    </div>}
    <div style={s("display:flex;gap:9px;align-items:center")}>
      <div className="schip" style={s("flex:1;display:flex;align-items:center;gap:8px;border:0;border-radius:0;padding:8px 2px;background:transparent;text-transform:none;letter-spacing:0")}>
        <Icon d="search" size={15} color="var(--ink-mute)" />
        <input value={q} onChange={e => setQ(e.target.value)} placeholder={t('search')} style={s("border:none;outline:none;background:transparent;font-family:inherit;font-size:13px;color:var(--ink);width:100%")} />
      </div>
      <select value={srt} onChange={e => setSrt(e.target.value)}
        style={s("padding:8px 10px;font-size:12.5px;font-family:inherit;border:1px solid var(--line);border-radius:9px;background:var(--bg-cream-dim);color:var(--ink)")}>
        {SORTS.map(o => <option key={o[0]} value={o[0]}>{o[1]}</option>)}
      </select>
    </div>
    <div style={s(CARD + ";padding:0;overflow:hidden")}>
      {rows.map((f, i) => <div key={f.id} onClick={() => onOpen(f.id)} style={s("display:flex;align-items:center;gap:10px;padding:10px 13px;border-top:1px solid var(--line-soft);cursor:pointer")}>
        <span style={s("font-family:var(--font-mono);font-size:11px;color:var(--ink-mute);width:16px;text-align:center")}>{i + 1}</span>
        <Avatar name={farmLabel(f)} size={30} />
        <div style={s("flex:1;min-width:0")}><div style={s("font-size:13px;font-weight:700;color:var(--ink);line-height:1.1")}>{farmLabel(f)}</div><div style={s("font-size:11px;color:var(--ink-mute)")}>{cname(f)}{f.ward ? ' · ' + f.ward : ''}{f.area ? ' · ' + f.area + ' ha' : ''}</div></div>
        <span style={s("font-family:var(--font-mono);font-size:12px;color:var(--ink-soft);min-width:36px;text-align:right")}>{f.ndvi != null ? f.ndvi.toFixed(2) : '—'}</span>
        <span className="sdot" style={{ width: 10, height: 10, background: RAMP[f.status] || 'var(--ink-mute)' }}></span>
      </div>)}
    </div>
  </div>;
}
/* ---- Farm detail drill (Phase 1 plain-language; Kuza Spot grid = Phase 2) ---- */
function FarmView({ f, P, fbUser, t, lang, onBack, onAssign }) {
  const d = C.DRIVERS[f.driver] || C.DRIVERS.healthy;
  const rec = (C.REC[f.driver] || C.REC.healthy)[lang] || (C.REC[f.driver] || C.REC.healthy).en;
  const noData = f.live && f.hasData === false;
  // live verdicts use the CF's own rule_v1 thresholds (shipped in the payload) and LEVEL
  // words (good/moderate/low) - "normal" is a baseline claim only the demo cohort supports
  const TH = (f.live && P && P.thresholds) || null;
  const gVerd = TH
    ? (f.ndvi >= TH.ndvi_green ? ['lvlGood', 'green'] : f.ndvi >= TH.ndvi_amber ? ['lvlMid', 'amber'] : ['lvlLow', 'red'])
    : (f.ndvi >= 0.68 ? ['aboveNormal', 'green'] : f.ndvi >= 0.55 ? ['atNormal', 'amber'] : ['belowNormal', 'red']);
  const mVerd = TH
    ? ((f.ndmi || 0) < TH.ndmi_dry ? ['lvlLow', 'red'] : (f.ndmi || 0) < 0.35 ? ['lvlMid', 'amber'] : ['lvlGood', 'green'])
    : ((f.ndmi || 0) >= 0.4 ? ['aboveNormal', 'green'] : (f.ndmi || 0) >= 0.28 ? ['atNormal', 'amber'] : ['belowNormal', 'red']);
  const stg = f.stage ? (C.STAGES.find(x => x.key === f.stage) || null) : null;
  const [sel, setSel] = useState(null);
  const grid = f.live ? null : C.kuzaGrid(f);
  /* live mode: real Kuza spot feed for this member farm (gsg_gold_eu views via the CF) */
  const [liveSpots, setLiveSpots] = useState(null);
  useEffect(() => {
    if (!f.live || !fbUser) return;
    let alive = true;
    fbUser.getIdToken()
      .then(tok => fetch(COOP_DASH_URL + '?farm=' + encodeURIComponent(f.id), { headers: { Authorization: 'Bearer ' + tok } }))
      .then(r => r.ok ? r.json() : null)
      .then(j => { if (alive && j) setLiveSpots(j.spots || []); })
      .catch(() => { if (alive) setLiveSpots([]); });
    return () => { alive = false; };
  }, [f.id, f.live, fbUser]);
  const livePoints = useMemo(() => {
    if (!liveSpots || !liveSpots.length) return null;
    const by = {};
    liveSpots.forEach(sp => { (by[sp.point_label] = by[sp.point_label] || { label: sp.point_label, rank: sp.point_rank || 0, spots: [] }).spots.push(sp); });
    const pts = Object.values(by).sort((a, b) => a.rank - b.rank);
    pts.forEach(p => p.spots.sort((a, b) => (a.spot_number || 0) - (b.spot_number || 0)));
    return pts;
  }, [liveSpots]);
  return <div style={s("display:flex;flex-direction:column;gap:13px")}>
    <button className="btn" onClick={onBack} style={s("align-self:flex-start;font-size:12px;padding:7px 12px;display:inline-flex;align-items:center;gap:6px")}><Icon d="chevron" size={14} style={{ transform: 'rotate(180deg)' }} />{t('backToList')}</button>
    <div style={s(CARD + ";display:flex;gap:11px;align-items:center")}>
      <Avatar name={f.farmer} size={44} />
      <div style={s("flex:1;min-width:0")}>
        <div style={s("font-size:16px;font-weight:700;color:var(--ink)")}>{f.farmer}</div>
        <div style={s("font-size:12px;color:var(--ink-mute)")}>{f.name}{f.ward && f.ward !== '—' ? ' · ' + f.ward : ''}{f.factory ? ' · ' + t('factory') + ': ' + f.factory : ''}</div>
      </div>
      <Priority st={f.status} t={t} />
    </div>
    <div style={s(CARD + ";display:flex;flex-direction:column;gap:11px")}>
      {noData
        ? <div style={s("font-size:12.5px;color:var(--ink-mute);font-style:italic")}>{(C.DRIVERS.no_data[lang] || C.DRIVERS.no_data.en)} — {t('noRecent')}</div>
        : <React.Fragment>
          <div><div style={s("display:flex;justify-content:space-between;margin-bottom:4px")}><span style={s("font-size:12.5px;color:var(--ink)")}>{t('greenness')}</span><span style={s("font-size:11.5px;color:" + RAMP[gVerd[1]])}>{t(gVerd[0])}</span></div><Bar pct={(f.ndvi || 0) * 100} s={gVerd[1]} /></div>
          <div><div style={s("display:flex;justify-content:space-between;margin-bottom:4px")}><span style={s("font-size:12.5px;color:var(--ink)")}>{t('moisture')}</span><span style={s("font-size:11.5px;color:" + RAMP[mVerd[1]])}>{t(mVerd[0])}</span></div><Bar pct={(f.ndmi || 0) * 140} s={mVerd[1]} /></div>
        </React.Fragment>}
      <div style={s("display:flex;justify-content:space-between;align-items:center")}>
        {stg ? <span style={s("font-size:12px;color:var(--ink-mute)")}>{t('stage')}: <b style={s("color:var(--ink)")}>{lang === 'sw' ? stg.sw : stg.en}</b></span>
             : <span style={s("font-size:11px;color:var(--ink-mute)")}>{f.hasData === false ? t('noRecent') : (f.verified ? t('verified') : t('provisional'))}</span>}
        <span style={s("font-size:11px;color:var(--ink-mute)")}>{f.live ? (liveSpots ? liveSpots.length + ' ' + t('spots') : '…') : f.spots + ' ' + t('spots')}</span>
      </div>
    </div>
    {(!f.live || (liveSpots && liveSpots.length > 0)) && <div style={s(CARD + ";display:flex;flex-direction:column;gap:9px")}>
      <Section title={t('kuzaTitle')} right={<span style={s("font-family:var(--font-mono);font-size:9px;color:var(--ink-mute)")}>{t('unitLegend')}</span>} />
      {!f.live && <div style={merge("border:2px solid var(--blue);border-radius:10px;padding:6px;display:grid;gap:5px", { gridTemplateColumns: 'repeat(' + grid.pointCols + ',1fr)' })}>
        {grid.points.map(pt => <div key={pt.letter} style={s("border:1.5px solid var(--green);border-radius:6px;padding:4px 4px 3px;position:relative")}>
          <span style={s("position:absolute;top:2px;left:4px;font-family:var(--font-mono);font-size:8px;font-weight:700;color:var(--green-deep)")}>{pt.letter}</span>
          <div style={s("display:grid;grid-template-columns:1fr 1fr;gap:2px;margin-top:9px")}>
            {pt.spots.map(sp => { const on = sel && sel.letter === pt.letter && sel.n === sp.n; return <div key={sp.n} onClick={() => setSel({ letter: pt.letter, n: sp.n, status: sp.status })} style={merge("border-radius:2px;cursor:pointer", { aspectRatio: '1', background: RAMP[sp.status], boxShadow: on ? '0 0 0 2px var(--orange)' : 'inset 0 0 0 1px rgba(222,153,76,.55)' })} />; })}
          </div>
        </div>)}
      </div>}
      {f.live && livePoints && <div style={merge("border:2px solid var(--blue);border-radius:10px;padding:6px;display:grid;gap:5px", { gridTemplateColumns: 'repeat(' + Math.min(4, Math.max(2, Math.ceil(Math.sqrt(livePoints.length)))) + ',1fr)' })}>
        {livePoints.map(pt => <div key={pt.label} style={s("border:1.5px solid var(--green);border-radius:6px;padding:4px 4px 3px;position:relative")}>
          <span style={s("position:absolute;top:2px;left:4px;font-family:var(--font-mono);font-size:8px;font-weight:700;color:var(--green-deep)")}>{pt.label}</span>
          <div style={s("display:grid;grid-template-columns:1fr 1fr;gap:2px;margin-top:9px")}>
            {pt.spots.map(sp => { const k = sp.primary_status_key || 'status.no_data'; const ks = C.KUZA_STATUS[k] || C.KUZA_STATUS['status.no_data']; const on = sel && sel.addr === sp.spot_address; return <div key={sp.spot_address} title={sp.spot_address} onClick={() => setSel({ addr: sp.spot_address, letter: pt.label, n: sp.spot_number, key: k })} style={merge("border-radius:2px;cursor:pointer", { aspectRatio: '1', background: ks.color, boxShadow: on ? '0 0 0 2px var(--orange)' : 'inset 0 0 0 1px rgba(222,153,76,.55)' })} />; })}
          </div>
        </div>)}
      </div>}
      <div style={s("display:flex;gap:13px;flex-wrap:wrap")}>
        <span style={s("font-size:10.5px;color:var(--ink-soft)")}><span style={s("display:inline-block;width:9px;height:9px;border:2px solid var(--blue);border-radius:2px;margin-right:5px;vertical-align:middle")} />{t('acre')}</span>
        <span style={s("font-size:10.5px;color:var(--ink-soft)")}><span style={s("display:inline-block;width:9px;height:9px;border:2px solid var(--green);border-radius:2px;margin-right:5px;vertical-align:middle")} />Point</span>
        <span style={s("font-size:10.5px;color:var(--ink-soft)")}><span style={s("display:inline-block;width:9px;height:9px;border:2px solid var(--orange);border-radius:2px;margin-right:5px;vertical-align:middle")} />Spot</span>
      </div>
      <div style={s("background:var(--bg-cream-dim);border-radius:9px;padding:9px 11px;font-size:12.5px;color:var(--ink)")}>
        {sel ? (sel.key
          ? <span>{f.farmer} · <b>Point {sel.letter} · Spot {sel.n}</b> · {t('hali')}: <b style={{ color: (C.KUZA_STATUS[sel.key] || {}).color }}>{((C.KUZA_STATUS[sel.key] || {})[lang]) || sel.key}</b></span>
          : <span>{f.name} · <b>Point {sel.letter} · Spot {sel.n}</b> · {t('hali')}: <b style={{ color: RAMP[sel.status] }}>{C.spotWord(sel.status, f.driver, lang)}</b></span>)
          : <span style={s("color:var(--ink-mute);font-style:italic")}>{t('tapSpot')}</span>}
      </div>
    </div>}
    {f.trend && f.trend.length > 1 && <div style={s(CARD)}>
      <Section title={t('trend')} />
      <div style={s("margin-top:8px")}><Sparkline series={f.trend} baseline={f.cohort || undefined} w={300} h={72} color={RAMP[f.status]} /></div>
    </div>}
    <div style={s(CARD + ";border-left:3px solid " + RAMP[f.status] + ";display:flex;flex-direction:column;gap:7px")}>
      <span className="eyebrow" style={{ color: INK[f.status] }}>{d.glyph} {lang === 'sw' ? d.sw : d.en}</span>
      <div style={s("font-size:13px;color:var(--ink);line-height:1.5")}><b>{t('recAction')}:</b> {rec}</div>
      <button className="btn orange" onClick={() => onAssign(f)} style={s("margin-top:4px;font-size:13px;padding:9px;border:none;background:var(--orange);color:#fff;font-weight:700;border-radius:10px;cursor:pointer")}>{t('assign')}</button>
    </div>
    <div style={s("font-size:10.5px;color:var(--ink-mute);font-style:italic;text-align:center")}>{f.live ? t('liveNote') : t('demoNote')}</div>
  </div>;
}

/* ---- Ramani / Kazi / Mimi (light for Phase 1) ---- */
/* Google dark base — byte-identical to the officials portal (official.jsx GMAP_DARK)
 * so the two surfaces read as one system. */
const GMAP_DARK = [
  { elementType: 'geometry', stylers: [{ color: '#1b2127' }] },
  { elementType: 'labels.text.stroke', stylers: [{ color: '#0b0d11' }] },
  { elementType: 'labels.text.fill', stylers: [{ color: '#b9c4c1' }] },
  { featureType: 'administrative', elementType: 'geometry', stylers: [{ color: '#3a464d' }] },
  { featureType: 'administrative.country', elementType: 'labels.text.fill', stylers: [{ color: '#dbe3e0' }] },
  { featureType: 'administrative.province', elementType: 'geometry.stroke', stylers: [{ color: '#54636b' }, { weight: 1.1 }] },
  { featureType: 'administrative.locality', elementType: 'labels.text.fill', stylers: [{ color: '#e9cfa4' }] },
  { featureType: 'administrative.neighborhood', elementType: 'labels.text.fill', stylers: [{ color: '#9aa6a3' }] },
  { featureType: 'poi', stylers: [{ visibility: 'off' }] },
  { featureType: 'road', elementType: 'geometry', stylers: [{ color: '#2b343b' }] },
  { featureType: 'road', elementType: 'labels.text.fill', stylers: [{ color: '#94a09d' }] },
  { featureType: 'road.highway', elementType: 'geometry', stylers: [{ color: '#3e4a53' }] },
  { featureType: 'road.highway', elementType: 'labels.text.fill', stylers: [{ color: '#c7cfcc' }] },
  { featureType: 'transit', stylers: [{ visibility: 'off' }] },
  { featureType: 'water', elementType: 'geometry', stylers: [{ color: '#0e1a24' }] },
  { featureType: 'landscape.natural', elementType: 'geometry', stylers: [{ color: '#1e262c' }] },
];

/* Ramani — ships as an ALL-FARMS heat map (same canvas overlay technique as the
 * officials portal: HeatmapLayer was removed in Maps JS v3.65, each blob is one
 * real farm centroid coloured by its status — never a fabricated field, P001).
 * Selecting a shamba from the picker switches to its real 10 m spot rectangles;
 * clearing the pick returns to the heat. */
const hexA = (hex, a) => { const n = String(hex).replace('#', ''); const r = parseInt(n.slice(0, 2), 16), g = parseInt(n.slice(2, 4), 16), b = parseInt(n.slice(4, 6), 16); return 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; };
function makeHeatOverlay(google) {
  class Heat extends google.maps.OverlayView {
    constructor() { super(); this.canvas = null; this.points = []; this.opts = {}; }
    setData(points, opts) { this.points = points || []; if (opts) this.opts = opts; this.draw(); }
    onAdd() {
      const c = document.createElement('canvas');
      c.style.position = 'absolute'; c.style.left = '0'; c.style.top = '0'; c.style.pointerEvents = 'none';
      this.canvas = c; this.getPanes().overlayLayer.appendChild(c);
    }
    onRemove() { if (this.canvas && this.canvas.parentNode) this.canvas.parentNode.removeChild(this.canvas); this.canvas = null; }
    draw() {
      const proj = this.getProjection(), map = this.getMap(), c = this.canvas;
      if (!proj || !map || !c) return;
      const b = map.getBounds(); if (!b) return;
      const ne = b.getNorthEast(), sw = b.getSouthWest();
      const tl = proj.fromLatLngToDivPixel(new google.maps.LatLng(ne.lat(), sw.lng()));
      const br = proj.fromLatLngToDivPixel(new google.maps.LatLng(sw.lat(), ne.lng()));
      const pad = 80;
      const w = Math.max(1, Math.round(Math.abs(br.x - tl.x)) + pad * 2), h = Math.max(1, Math.round(Math.abs(br.y - tl.y)) + pad * 2);
      const ox = Math.min(tl.x, br.x) - pad, oy = Math.min(tl.y, br.y) - pad;
      c.width = w; c.height = h; c.style.left = ox + 'px'; c.style.top = oy + 'px';
      const ctx = c.getContext('2d'); ctx.clearRect(0, 0, w, h);
      const R = this.opts.radius || 44, op = this.opts.opacity != null ? this.opts.opacity : 0.6, colorFor = this.opts.colorFor || (() => '#DE994C');
      for (const p of this.points) {
        const px = proj.fromLatLngToDivPixel(new google.maps.LatLng(p.lat, p.lng));
        const x = px.x - ox, y = px.y - oy;
        if (x < -R || y < -R || x > w + R || y > h + R) continue;
        const col = colorFor(p.weight);
        const g = ctx.createRadialGradient(x, y, 0, x, y, R);
        g.addColorStop(0, hexA(col, op)); g.addColorStop(0.55, hexA(col, op * 0.5)); g.addColorStop(1, hexA(col, 0));
        ctx.fillStyle = g; ctx.beginPath(); ctx.arc(x, y, R, 0, Math.PI * 2); ctx.fill();
      }
    }
  }
  return new Heat();
}
const HEATCOL = { red: '#C06848', amber: '#D69020', green: '#6FA23F', no_data: '#7C8783', nodata: '#7C8783' };
function Ramani({ t, lang, P, isLive, fbUser }) {
  const mapDiv = React.useRef(null), mapObj = React.useRef(null), cellsRef = React.useRef([]), heatRef = React.useRef(null), fitH = React.useRef(false);
  const [base, setBase] = useState('dark');
  const [ready, setReady] = useState(false);
  const [slow, setSlow] = useState(false);
  const [farmId, setFarmId] = useState(null);
  const [spots, setSpots] = useState(null);
  const [err, setErr] = useState('');
  const farms = P.FARMS || [];
  const farm = farms.find(f => f.id === farmId) || null;
  const pts = farms.filter(f => f.lat != null && f.lng != null).map(f => ({ lat: f.lat, lng: f.lng, weight: f.status || 'no_data' }));

  const paintHeat = () => {
    const g = window.google, map = mapObj.current;
    if (!g || !map || !heatRef.current) return;
    if (farmId) { heatRef.current.setData([]); return; }
    heatRef.current.setData(pts, { radius: 42, opacity: .55, colorFor: w => HEATCOL[w] || '#DE994C' });
    if (pts.length && !fitH.current) {
      try { const b = new g.maps.LatLngBounds(); pts.forEach(p => b.extend(p)); map.fitBounds(b, 46); fitH.current = true; } catch (e) {}
    }
  };

  useEffect(() => {
    let dead = false, n = 0;
    const init = () => {
      if (dead) return;
      if (!window.google || !window.google.maps) { if (n++ < 120) setTimeout(init, 150); return; }
      if (mapObj.current || !mapDiv.current) return;
      const g = window.google;
      const opts = {
        center: { lat: 0.52, lng: 35.27 }, zoom: 7, minZoom: 5, maxZoom: 18,
        mapTypeId: 'roadmap', styles: GMAP_DARK, backgroundColor: '#0B0D11',
        disableDefaultUI: true, zoomControl: true, gestureHandling: 'greedy', clickableIcons: false,
      };
      try { if (g.maps.RenderingType && g.maps.RenderingType.RASTER) opts.renderingType = g.maps.RenderingType.RASTER; } catch (e) {}
      mapObj.current = new g.maps.Map(mapDiv.current, opts);
      heatRef.current = makeHeatOverlay(g);
      heatRef.current.setMap(mapObj.current);
      g.maps.event.addListenerOnce(mapObj.current, 'tilesloaded', () => { if (!dead) setReady(true); });
      paintHeat();
      [90, 350, 800].forEach(ms => setTimeout(() => { if (dead || !mapObj.current) return; try { g.maps.event.trigger(mapObj.current, 'resize'); } catch (e) {} fitH.current = false; paintHeat(); }, ms));
    };
    init();
    const slowT = setTimeout(() => { if (!dead) setSlow(true); }, 8000);
    return () => { dead = true; clearTimeout(slowT); cellsRef.current.forEach(c => { try { c.setMap(null); } catch (e) {} }); cellsRef.current = []; try { if (heatRef.current) heatRef.current.setMap(null); } catch (e) {} heatRef.current = null; mapObj.current = null; fitH.current = false; };
  }, []);

  useEffect(() => {
    const g = window.google, map = mapObj.current; if (!g || !map) return;
    if (base === 'satellite') { map.setMapTypeId('hybrid'); map.setOptions({ styles: null }); }
    else { map.setMapTypeId('roadmap'); map.setOptions({ styles: GMAP_DARK }); }
  }, [base]);

  useEffect(() => {
    if (!farmId || !isLive || !fbUser) { setSpots(null); return; }
    let alive = true; setErr(''); setSpots(null);
    fbUser.getIdToken().then(tok => fetch(COOP_DASH_URL + '?farm=' + encodeURIComponent(farmId), { headers: { Authorization: 'Bearer ' + tok } }))
      .then(r => r.ok ? r.json() : Promise.reject(r.status))
      .then(j => { if (alive) setSpots(j.spots || []); })
      .catch(() => { if (alive) setErr(lang === 'sw' ? 'Imeshindikana kupakia vipande.' : 'Could not load spots.'); });
    return () => { alive = false; };
  }, [farmId, isLive, fbUser]);

  // spot rectangles when a farm is chosen; heat when not
  useEffect(() => {
    const g = window.google, map = mapObj.current; if (!g || !map) return;
    cellsRef.current.forEach(c => { try { c.setMap(null); } catch (e) {} }); cellsRef.current = [];
    paintHeat();
    if (!farmId || !spots || !spots.length) return;
    const D = 0.000045;
    const b = new g.maps.LatLngBounds();
    spots.forEach(sp => {
      if (sp.lat == null || sp.lng == null) return;
      const ks = C.KUZA_STATUS[sp.primary_status_key || 'status.no_data'] || C.KUZA_STATUS['status.no_data'];
      const col = ks.color.indexOf('var(') === 0
        ? getComputedStyle(document.documentElement).getPropertyValue(ks.color.slice(4, -1)).trim() || '#6FA23F'
        : ks.color;
      const r = new g.maps.Rectangle({
        bounds: { north: sp.lat + D, south: sp.lat - D, east: sp.lng + D, west: sp.lng - D },
        strokeColor: '#DE994C', strokeOpacity: .5, strokeWeight: .6,
        fillColor: col, fillOpacity: .72, map, clickable: false,
      });
      cellsRef.current.push(r);
      b.extend({ lat: sp.lat, lng: sp.lng });
    });
    try { map.fitBounds(b, 40); } catch (e) {}
  }, [spots, farmId, P]);

  const gmReady = !!(window.google && window.google.maps);
  const L = lang === 'sw'
    ? { pick: 'Ushirika mzima · ramani ya joto', none: 'Ramani ya joto ya mashamba yote — chagua shamba kuona vipande vyake.', noHeat: 'Ramani ya joto inakuja na sasisho lijalo la data — kwa sasa chagua shamba.', demo: 'Ramani ya moja kwa moja inahitaji kuingia — portfolio ya mfano haina viwianishi.', loading: 'Inapakia ramani…', spots: 'vipande', dark: 'Giza', sat: 'Setilaiti', noRender: 'Ramani haikuweza kuonekana kwenye kivinjari hiki', noRenderSub: 'Data ya vipande iko sawa. Kwa ramani, fungua kwenye Chrome, Edge au Firefox ya sasa.' }
    : { pick: 'Whole co-op · heat map', none: 'Heat map of every shamba — pick one to see its 10 m spots.', noHeat: 'The heat view arrives with the next data update — pick a shamba meanwhile.', demo: 'The live map needs sign-in — the sample portfolio carries no coordinates.', loading: 'Map engine loading…', spots: 'spots', dark: 'Dark', sat: 'Satellite', noRender: 'The map could not render in this browser', noRenderSub: 'Your spot readings are fine. For the map view, open this in a current Chrome, Edge or Firefox.' };

  return <div style={s("display:flex;flex-direction:column;gap:11px")}>
    <div style={s("display:flex;gap:9px;align-items:center;flex-wrap:wrap")}>
      <select value={farmId || ''} onChange={e => setFarmId(e.target.value || null)} disabled={!isLive}
        style={s("flex:1;min-width:180px;padding:9px 11px;font-size:13px;font-family:inherit;border:1px solid var(--line);border-radius:9px;background:var(--bg-cream-dim);color:var(--ink)")}>
        <option value="">{L.pick}</option>
        {farms.map(f => <option key={f.id} value={f.id}>{farmLabel(f)}{f.ward ? ' · ' + f.ward : ''}</option>)}
      </select>
      {spots && farmId && <span className="tag">{spots.length} {L.spots}</span>}
    </div>
    <div style={s("position:relative;border-radius:14px;overflow:hidden;border:1px solid var(--line)")}>
      <div ref={mapDiv} style={{ height: 460, width: '100%', background: '#0B0D11' }} />
      {!gmReady && !slow && <div style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#8A9490', fontSize: 13 }}>{L.loading}</div>}
      {gmReady && !ready && slow && <div style={{ position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', gap: 6, alignItems: 'center', justifyContent: 'center', textAlign: 'center', padding: 24, color: '#C7D0CD', background: 'rgba(11,13,17,.92)' }}>
        <div style={{ fontSize: 22 }}>🗺️</div>
        <div style={{ fontWeight: 600, fontSize: 13, color: '#E8EEEC' }}>{L.noRender}</div>
        <div style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: '#8A9490', maxWidth: 330, lineHeight: 1.5 }}>{L.noRenderSub}</div>
      </div>}
      {gmReady && !farmId && <div style={{ position: 'absolute', left: 10, bottom: 10, zIndex: 5, fontFamily: 'var(--font-mono)', fontSize: 10, color: '#fff', background: 'rgba(11,13,17,.8)', border: '1px solid rgba(255,255,255,.14)', padding: '6px 10px', borderRadius: 7, maxWidth: '72%' }}>{isLive ? (pts.length ? L.none : L.noHeat) : L.demo}</div>}
      {err && <div style={{ position: 'absolute', left: 10, bottom: 10, zIndex: 5, fontSize: 12, color: 'var(--red-ink)', background: 'rgba(11,13,17,.85)', padding: '6px 10px', borderRadius: 7 }}>{err}</div>}
      <div style={{ position: 'absolute', right: 10, top: 10, zIndex: 5, display: 'flex', border: '1px solid rgba(255,255,255,.18)', borderRadius: 8, overflow: 'hidden', background: 'rgba(11,13,17,.72)' }}>
        {[['dark', L.dark], ['satellite', L.sat]].map(([k, lab]) => <button key={k} onClick={() => setBase(k)}
          style={{ fontFamily: 'var(--font-mono)', fontSize: 10, fontWeight: 700, letterSpacing: .4, padding: '6px 11px', border: 'none', cursor: 'pointer', background: base === k ? 'var(--orange,#DE994C)' : 'transparent', color: base === k ? '#0B0D11' : '#C7D0CD' }}>{lab}</button>)}
      </div>
      <div style={{ position: 'absolute', left: 10, top: 10, zIndex: 5, fontFamily: 'var(--font-mono)', fontSize: 9.5, fontWeight: 700, color: '#fff', background: 'rgba(11,13,17,.80)', padding: '5px 10px', borderRadius: 6, border: '1px solid rgba(255,255,255,.14)' }}>
        {farm ? farmLabel(farm).toUpperCase() + ' · 10 M SPOTS' : (pts.length ? pts.length + ' SHAMBA · HEAT' : '')}
      </div>
    </div>
  </div>;
}
function Kazi({ t, lang, tasks, setTasks }) {
  if (!tasks.length) return <div style={s(CARD + ";text-align:center;padding:34px 18px;color:var(--ink-mute)")}><Icon d="task" size={26} color="var(--ink-mute)" /><div style={s("margin-top:10px;font-size:13px")}>{t('tasks_empty')}</div></div>;
  return <div style={s("display:flex;flex-direction:column;gap:9px")}>
    <div style={s("font-family:var(--font-mono);font-size:9.5px;letter-spacing:.6px;color:var(--ink-mute);border:1px dashed var(--line);border-radius:8px;padding:7px 10px")}>{lang === 'sw' ? DK.sw.kaziLocal : DK.en.kaziLocal}</div>
    {tasks.map((k, i) => { const d = C.DRIVERS[k.driver] || C.DRIVERS.healthy; return <div key={i} style={s(CARD + ";display:flex;gap:10px;align-items:center")}>
      <span style={s("font-size:18px")}>{d.glyph}</span>
      <div style={s("flex:1;min-width:0")}><div style={s("font-size:13px;font-weight:700;color:var(--ink)")}>{k.farmer}</div><div style={s("font-size:11px;color:var(--ink-mute)")}>{k.name} · {lang === 'sw' ? d.sw : d.en}</div></div>
      <span className="tag">{t('assigned')}</span>
    </div>; })}
  </div>;
}
/* ---- EUDR export stub (coffee coop -> market access) ---- */
function EudrView({ active, P, t, lang, onBack }) {
  const farms = P.FARMS, v = P.verifiedCount, p = farms.length - v;
  return <div style={s("display:flex;flex-direction:column;gap:13px")}>
    <button className="btn" onClick={onBack} style={s("align-self:flex-start;font-size:12px;padding:7px 12px;display:inline-flex;align-items:center;gap:6px")}><Icon d="chevron" size={14} style={{ transform: 'rotate(180deg)' }} />{t('backToList')}</button>
    <div><div className="eyebrow" style={{ color: 'var(--green-deep)' }}>{t('eudrTitle')}</div>
      <div style={s("font-size:14px;color:var(--ink);line-height:1.5;margin-top:6px")}>{t('eudrIntro')}</div></div>
    <div style={s(CARD + ";display:flex;flex-direction:column;gap:8px")}>
      <SegBar segs={[{ v, color: RAMP.green }, { v: p, color: RAMP.amber }]} />
      <div style={s("display:flex;gap:14px")}>
        <span style={s("font-size:11.5px;color:var(--ink-soft)")}><span className="sdot" style={{ width: 8, height: 8, background: RAMP.green, marginRight: 5 }} />{v} {t('verified')}</span>
        <span style={s("font-size:11.5px;color:var(--ink-soft)")}><span className="sdot" style={{ width: 8, height: 8, background: RAMP.amber, marginRight: 5 }} />{p} {t('provisional')}</span>
      </div>
    </div>
    <div style={s(CARD + ";padding:0;overflow:hidden")}>
      {farms.map(f => <div key={f.id} style={s("display:flex;align-items:center;gap:10px;padding:9px 13px;border-top:1px solid var(--line-soft)")}>
        <Avatar name={f.farmer} size={26} />
        <div style={s("flex:1;min-width:0")}><div style={s("font-size:12.5px;font-weight:600;color:var(--ink);line-height:1.1")}>{f.farmer}</div><div style={s("font-size:10.5px;color:var(--ink-mute)")}>{f.name}{!f.live && f.ward ? ' · ' + f.ward : ''}{f.area != null ? ' · ' + f.area + ' ha' : ''}</div></div>
        <span className="schip" style={f.verified ? { background: 'var(--green-soft)', color: 'var(--green-deep)' } : { background: 'var(--amber-soft)', color: 'var(--amber-ink)' }}>{f.verified ? t('verified') : t('provisional')}</span>
      </div>)}
    </div>
    <button className="btn orange" onClick={() => alert(t('prepPack') + ' — ' + farms.length + ' ' + t('plots'))} style={s("font-size:13px;padding:11px;border:none;background:var(--orange);color:#fff;font-weight:700;border-radius:11px;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:7px")}><Icon d="download" size={17} color="#fff" />{t('prepPack')}</button>
    <div style={s("font-size:10.5px;color:var(--ink-mute);font-style:italic;text-align:center")}>{P.live ? t('liveNote') : t('demoNote')} · {active.name}</div>
  </div>;
}

function Mimi({ t, lang, setLang, active, coopId, setCoopId, coopList, live, fbUser, liveErr, isLive }) {
  const [open, setOpen] = useState(false);
  const [cq, setCq] = useState('');
  const [ph, setPh] = useState('+254 ');
  const [pin, setPin] = useState('');
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState('');
  const install = () => { if (deferredPrompt) { deferredPrompt.prompt(); deferredPrompt = null; } else alert(t('install')); };
  const list = cq ? coopList.filter(c => (c.name + ' ' + c.county).toLowerCase().includes(cq.toLowerCase())) : coopList;
  const signIn = () => {
    setErr(''); setBusy(true);
    firebase.auth().signInWithEmailAndPassword(phoneToEmail(ph), pin)
      .then(() => setBusy(false))
      .catch(() => { setBusy(false); setErr(t('liveErr')); });
  };
  const inp = s("width:100%;box-sizing:border-box;padding:10px 12px;margin:5px 0;font-size:14px;font-family:inherit;border:1px solid var(--line);border-radius:9px;background:var(--bg-cream-dim);color:var(--ink);outline:none");
  const repLine = (isLive && fbUser) ? '+' + (fbUser.email || '').split('@')[0] : active.rep;
  return <div style={s("display:flex;flex-direction:column;gap:11px")}>
    <div style={s(CARD + ";display:flex;gap:11px;align-items:center")}><Avatar name={active.rep} size={44} /><div><div style={s("font-size:15px;font-weight:700;color:var(--ink)")}>{repLine}</div><div style={s("font-size:12px;color:var(--ink-mute)")}>{active.name}</div></div></div>

    {/* live-mode sign-in (field agent / coop manager, `coop` claim) */}
    {!fbUser && <div style={s(CARD + ";display:flex;flex-direction:column;gap:4px")}>
      <span className="eyebrow" style={{ color: 'var(--green-deep)' }}>{t('signin')}</span>
      <input value={ph} onChange={e => setPh(e.target.value)} placeholder={t('phoneLabel')} style={inp} />
      <input value={pin} onChange={e => setPin(e.target.value)} type="password" inputMode="numeric" placeholder={t('pinLabel')} style={inp} />
      <button onClick={signIn} disabled={busy} style={s("margin-top:4px;padding:11px;border:none;border-radius:9px;background:var(--forest);color:#fff;font-weight:700;font-size:13px;font-family:inherit;cursor:pointer")}>{busy ? '…' : t('signin')}</button>
      {(err || liveErr) && <div style={s("font-size:11.5px;color:var(--red);margin-top:4px")}>{err || liveErr}</div>}
    </div>}
    {fbUser && <div style={s(CARD + ";display:flex;align-items:center;gap:10px")}>
      <span className="sdot" style={{ width: 9, height: 9, background: isLive ? 'var(--green)' : 'var(--amber)' }} />
      <div style={s("flex:1;min-width:0")}><div style={s("font-size:13px;font-weight:600;color:var(--ink)")}>{(fbUser.email || '').split('@')[0]}</div><div style={s("font-size:10.5px;color:var(--ink-mute)")}>{isLive ? t('liveNote') : (liveErr || '—')}</div></div>
      <button onClick={() => firebase.auth().signOut()} className="tag" style={s("cursor:pointer;border:1px solid var(--line)")}>{t('signout')}</button>
    </div>}
    <div style={s(CARD + ";display:flex;justify-content:space-between;align-items:center")}><span style={s("font-size:13px;color:var(--ink)")}>{t('language')}</span>
      <div style={s("display:flex;gap:6px")}>{['sw', 'en'].map(l => <button key={l} className="tag" onClick={() => setLang(l)} style={merge("cursor:pointer;border:1px solid var(--line)", lang === l ? { background: 'var(--orange)', color: '#fff', borderColor: 'var(--orange)' } : {})}>{l.toUpperCase()}</button>)}</div></div>
    <div style={s(CARD + ";padding:0;overflow:hidden")}>
      <div onClick={() => setOpen(o => !o)} style={s("display:flex;justify-content:space-between;align-items:center;color:var(--ink);cursor:pointer;padding:13px 14px")}>
        <span style={s("display:inline-flex;align-items:center;gap:7px;font-size:13px")}>{t('switchTitle')}
          <span className="tag" style={live ? { background: 'var(--green-soft)', color: 'var(--green-deep)' } : {}}>{live ? (coopList.length + ' · live') : 'demo'}</span></span>
        <Icon d="chevdown" size={16} color="var(--ink-mute)" style={{ transform: open ? 'rotate(180deg)' : 'none' }} /></div>
      {open && <div style={s("padding:0 14px 10px")}>
        <div style={s("display:flex;align-items:center;gap:8px;border:1px solid var(--line);border-radius:9px;padding:7px 10px;background:var(--bg-cream)")}>
          <Icon d="search" size={14} color="var(--ink-mute)" />
          <input value={cq} onChange={e => setCq(e.target.value)} placeholder={t('search')} style={s("border:none;outline:none;background:transparent;font-family:inherit;font-size:12.5px;color:var(--ink);width:100%")} />
        </div>
      </div>}
      {open && list.slice(0, 40).map(c => <div key={c.id} onClick={() => { setCoopId(c.id); setOpen(false); setCq(''); }} style={s("display:flex;justify-content:space-between;align-items:center;padding:10px 14px;border-top:1px solid var(--line-soft);cursor:pointer")}>
        <div><div style={s("font-size:12.5px;font-weight:600;color:var(--ink)")}>{c.short}</div><div style={s("font-size:10.5px;color:var(--ink-mute)")}>{c.county}{c.membersTotal ? ' · ' + c.membersTotal.toLocaleString() + ' ' + t('coopMembersTotal') : ''}</div></div>
        {c.id === coopId && <Icon d="check" size={16} color="var(--orange-deep)" />}
      </div>)}
      {open && list.length > 40 && <div style={s("padding:8px 14px;font-size:10.5px;color:var(--ink-mute);border-top:1px solid var(--line-soft)")}>+{list.length - 40} — {t('search')}</div>}
    </div>
    <div onClick={install} style={s(CARD + ";display:flex;gap:9px;align-items:center;color:var(--ink);cursor:pointer")}><Icon d="download" size={17} color="var(--orange-deep)" /><span style={s("font-size:13px")}>{t('install')}</span></div>
    <div style={s("font-size:11px;color:var(--ink-mute);font-style:italic;text-align:center;padding:6px")}>{isLive ? t('liveNote') : t('demoNote')} · {active.short}{!isLive && active.membersTotal ? ' (' + active.membersTotal.toLocaleString() + ' ' + t('members').toLowerCase() + ')' : ''}</div>
  </div>;
}

/* ---- shell ---- */
/* ── Ofisi ya Ushirika — the desktop dashboard (>=1000px, tab 'connect').
 * Same P data as the phone views; Kenyan voice — English base, Swahili dashes
 * (shamba, mvua, kazi, leo, kanda, mazao). Calm doctrine: mambo ya haraka
 * kwanza, the rest can wait. Approved mock: design/coop-office-dashboard-mock-v1. */
/* ---- display hygiene: the warehouse carries mixed-case crops, technical farm
 * names and ids that must never be shown raw to a chairman. ---- */
const titleCase = w => String(w || '').replace(/[_-]+/g, ' ').trim().toLowerCase().replace(/\b[a-z]/g, c => c.toUpperCase());
/* live rows carry an explicit crop; the demo portfolio has none per farm, so it
 * falls back to the co-op's own crop rather than mis-reading the farm name. */
const cropOf = (f, fallback) => {
  const raw = f.crop ? String(f.crop) : (String(f.name || '').indexOf(' \u00b7 ') > -1 ? String(f.name).split(' \u00b7 ')[0] : (fallback || ''));
  return raw.trim() ? titleCase(raw.trim()) : '\u2014';
};
/* warehouse test values like unknown_test must never face a chairman */
const cropDisp = (f, fb, lang) => { const n = cropOf(f, fb); return /^unknown/i.test(n) ? (lang === 'sw' ? 'Haijulikani' : 'Unknown') : n; };
const farmLabel = f => {                                // hide field_2026... / firestore ids
  const n = String(f.farmer || '').trim();
  return (!n || /^(field_\d+|[A-Za-z0-9_-]{18,})$/.test(n)) ? 'Shamba ' + String(f.id).slice(-4) : n;
};
/* Desk copy - EN is Kenyan English (shamba, mvua, kanda stay), SW is fuller Swahili. */
const DK = {
  sw: {
    briefing: 'Taarifa ya leo',
    cropLab: 'Chuja kwa zao', all: 'Zote', back: '\u2039 Rudi', thNdvi: 'NDVI',
    segNeed: 'Wanaohitaji kutembelewa \u2014 wote', segHealth: 'Alama ya kila shamba',
    segAll: 'Wanachama wote', tapTile: 'Gusa kadi kuona orodha kamili',
    kaziLocal: 'Orodha ya ndani ya ofisi \u2014 bado haijatumwa kwa app ya agronomist.',
 prep: 'Maandalizi', remind: 'Vikumbusho vya wiki hii',
    rem1: 'Safisha mifereji kabla ya mvua ya Alhamisi.', rem2: 'Tembelea shamba zenye upungufu wa maji kwanza.',
    rem3: 'Angalia hali baada ya raundi ijayo ya kumwagilia.', remTag: 'mwongozo \u00b7 si utabiri wa KMD',
    kuzaEyeb: 'Jinsi tunavyosoma shamba', kuzaTitle: 'Kipande, Point, na ekari', openMap: 'Fungua ramani',
    spotT: 'Kipande \u00b7 Spot', spotB: 'Mita 10 kwa 10 \u2014 kipimo kidogo kabisa cha setilaiti.',
    pointT: 'Point', pointB: 'Vipande 2 kwa 2. Hapa ndipo mkulima anaenda.',
    acreT: 'Ekari', acreB: 'Karibu Points 10 \u2014 vipande 40 hivi.',
    need1: 'Shamba moja linahitaji kutembelewa leo.', needN: n => 'Shamba ' + n + ' zinahitaji kutembelewa leo.',
    calm: 'Hakuna cha dharura leo', calmTail: 'shamba zote ziko sawa.', cause: 'Sababu kubwa',
    farmsWord: 'shamba', noUrgent: 'hakuna cha dharura', asOf: 'Data ya asubuhi', sample: 'Portfolio ya mfano \u00b7 demo',
    kMembers: 'Wanachama', kNeed: 'Wanaohitaji kutembelewa', kHealth: 'Afya ya jumla', kVerified: 'Zimehakikiwa',
    kReady: 'Tayari kwa mnunuzi', kTasks: 'Kazi za leo', good: 'mzuri', watch: 'angalia', urgent: 'haraka',
    worstFirst: 'worst-first \u00b7 anza hapa chini', noNew: 'bila data mpya', readySub: 'kuiva au mavuno karibu',
    tasksSet: 'zimepangwa \u00b7 Kazi', tasksNone: 'hakuna bado \u2014 panga hapa chini',
    startHere: 'Anza na hawa', startSub: 'anza hapa', calmLine: 'Mambo ya haraka kwanza \u2014 mengine yanaweza kusubiri.',
    thFarmer: 'Mkulima', thCrop: 'Zao', thCounty: 'Kaunti', thWhy: 'Kwa nini', thState: 'Hali',
    assign: 'Mpe kazi', assigned: 'imepangwa', rain: 'Mvua', thisWeek: 'wiki hii', sampleTag: 'mfano \u00b7 illustrative',
    rainLive: 'Mvua ya kila shamba iko kwenye ukurasa wake. Feed ya ushirika mzima inakuja \u2014 kwa sasa,',
    rainLink: 'bulletin ya wiki \u2192', plant: 'Panda Ijumaa.', spray: 'Nyunyiza kabla ya Alhamisi.',
    tasks: 'Kazi', today: 'leo', tasksEmpty: 'Hakuna kazi bado. Chagua shamba kwenye orodha \u2014 mpe mtu.',
    zones: 'Kanda', glance: 'kwa mtazamo', zHaraka: 'haraka \u2014 anza hapa', zAngalia: 'angalia',
    crops: 'Mazao yetu', cropsSub: 'tunayolima', records: 'Rekodi', recordsSub: 'wazi \u00b7 na tarehe',
    recVerified: 'shamba zimehakikiwa kutoka angani', recNoData: 'bila data mpya ya setilaiti',
    recTail: 'tunaangalia kutoka juu, mara mbili kwa wiki',
    ninoT: 'Tayari kwa El Ni\u00f1o.', ninoB: 'Mvua kubwa zinatarajiwa msimu huu \u2014 safisha mifereji, funika mazao ghalani, angalia mteremko.',
    ninoLink: 'Soma bulletin ya wiki \u2192' },
  en: {
    briefing: 'Today\u2019s briefing',
    cropLab: 'Filter by crop', all: 'All', back: '\u2039 Back', thNdvi: 'NDVI',
    segNeed: 'Everyone who needs a visit', segHealth: 'Every shamba, its own reading',
    segAll: 'All members', tapTile: 'Tap a tile to see the full list',
    kaziLocal: 'Office-local list \u2014 not yet sent to the agronomist\u2019s app.',
 prep: 'Preparedness', remind: 'This week\u2019s reminders',
    rem1: 'Clear the drains before Thursday\u2019s rain.', rem2: 'Visit the shamba with water shortfall first.',
    rem3: 'Check recovery after the next watering round.', remTag: 'guidance \u00b7 not a KMD forecast',
    kuzaEyeb: 'How we read a shamba', kuzaTitle: 'Spot, Point and acre', openMap: 'Open the map',
    spotT: 'Spot \u00b7 kipande', spotB: '10 m by 10 m \u2014 the smallest thing the satellite sees.',
    pointT: 'Point', pointB: '2 by 2 spots. This is where the farmer actually walks.',
    acreT: 'Acre', acreB: 'About 10 Points \u2014 roughly 40 spots.',
    need1: 'One shamba needs a visit today.', needN: n => n + ' shamba need a visit today.',
    calm: 'Nothing urgent today', calmTail: 'every shamba is fine.', cause: 'Biggest cause',
    farmsWord: 'shamba', noUrgent: 'nothing urgent', asOf: 'Morning data', sample: 'Sample portfolio \u00b7 demo',
    kMembers: 'Members', kNeed: 'Need a visit', kHealth: 'Overall health', kVerified: 'Verified',
    kReady: 'Ready for the buyer', kTasks: 'Today\u2019s work', good: 'good', watch: 'watch', urgent: 'urgent',
    worstFirst: 'worst-first \u00b7 start below', noNew: 'with no fresh data', readySub: 'ripening or near harvest',
    tasksSet: 'assigned \u00b7 Kazi', tasksNone: 'none yet \u2014 assign below',
    startHere: 'Start with these', startSub: 'start here', calmLine: 'Urgent things first \u2014 the rest can wait.',
    thFarmer: 'Member', thCrop: 'Crop', thCounty: 'County', thWhy: 'Why', thState: 'State',
    assign: 'Assign', assigned: 'assigned', rain: 'Mvua \u00b7 rain', thisWeek: 'this week', sampleTag: 'sample \u00b7 illustrative',
    rainLive: 'Each shamba carries its own rain on its page. The whole-co-op feed is in build \u2014 for now,',
    rainLink: 'this week\u2019s bulletin \u2192', plant: 'Plant Friday.', spray: 'Spray before Thursday.',
    tasks: 'Kazi \u00b7 work', today: 'today', tasksEmpty: 'No work assigned yet. Pick a shamba from the list and give it to someone.',
    zones: 'Kanda \u00b7 zones', glance: 'at a glance', zHaraka: 'urgent \u2014 start here', zAngalia: 'to watch',
    crops: 'What we grow', cropsSub: 'across the co-op', records: 'The record', recordsSub: 'plain \u00b7 dated',
    recVerified: 'shamba verified from above', recNoData: 'with no fresh satellite pass',
    recTail: 'we check from above, twice a week',
    ninoT: 'El Ni\u00f1o readiness.', ninoB: 'Heavy rains are expected this season \u2014 clear the drains, cover stored produce, watch the slopes.',
    ninoLink: 'Read this week\u2019s bulletin \u2192' },
};
const HALI_W = { sw: { red: 'HARAKA', amber: 'ANGALIA', nodata: 'TEMBELEA', green: 'MZURI' },
                 en: { red: 'URGENT', amber: 'WATCH', nodata: 'VISIT', green: 'GOOD' } };
const HALI = { red: ['HARAKA', 'c-haraka'], amber: ['ANGALIA', 'c-angalia'], nodata: ['TEMBELEA', 'c-tembelea'], green: ['MZURI', 'c-tayari'] };
function Desk({ P, active, isLive, lang, tasks, onOpen, onAssign, onMap }) {
  const d = DK[lang === 'sw' ? 'sw' : 'en'], hw = HALI_W[lang === 'sw' ? 'sw' : 'en'];
  const [cropF, setCropF] = useState('');           // '' = all crops
  const [seg, setSeg] = useState(null);             // null | 'need' | 'health' | 'all'
  const cropName = f => cropDisp(f, lang === 'sw' ? active.cropSw : active.crop, lang);

  // crop chips from the real portfolio (only when there is a real mix)
  const cropCounts = {};
  P.FARMS.forEach(f => { const c = cropName(f); if (c !== '—') cropCounts[c] = (cropCounts[c] || 0) + 1; });
  const cropChips = Object.entries(cropCounts).sort((a, b) => b[1] - a[1]).slice(0, 6);

  // EVERYTHING below derives from the filtered set — coffee-only means
  // coffee-only headline, KPIs, kanda and table. That is the point.
  const FA = cropF ? P.FARMS.filter(f => cropName(f) === cropF) : P.FARMS;
  const need = FA.filter(f => f.status === 'red' && f.hasData !== false);
  const N = need.length;
  const health = { green: 0, amber: 0, red: 0 }; let noData = 0, verified = 0;
  FA.forEach(f => { if (f.status === 'green') health.green++; else if (f.status === 'amber') health.amber++; else if (f.status === 'red') health.red++; else noData++; if (f.verified) verified++; });
  const drvCounts = need.reduce((a, f) => { a[f.driver] = (a[f.driver] || 0) + 1; return a; }, {});
  const topDrv = Object.entries(drvCounts).sort((a, b) => b[1] - a[1])[0];
  const drvWord = k => (C.DRIVERS[k] ? (lang === 'sw' ? C.DRIVERS[k].sw : C.DRIVERS[k].en) : (k || '—'));
  const byWard = {};
  FA.forEach(f => { const w = f.ward; if (!w) return; const o = byWard[w] = byWard[w] || { n: 0, red: 0, amber: 0 }; o.n++; if (f.status === 'red') o.red++; else if (f.status === 'amber') o.amber++; });
  const kanda = Object.entries(byWard).sort((a, b) => (b[1].red - a[1].red) || (b[1].n - a[1].n)).slice(0, 4);
  const calmWard = kanda.filter(kv => !kv[1].red).sort((a, b) => b[1].n - a[1].n)[0];
  const tayari = FA.filter(f => f.stage === 'harvest' || f.stage === 'ripening').length;
  const ha = cropF ? Math.round(FA.reduce((a, f) => a + (f.area || 0), 0) * 10) / 10 : P.hectares;

  // the table is the drill surface: default = worst-first six; a tapped tile
  // expands it to the full segment with each shamba's own NDVI reading
  const W = { red: 0, amber: 1, nodata: 2, no_data: 2, green: 3 };
  let rows, segTitle = null;
  if (seg === 'need') { rows = need.concat(FA.filter(f => f.status === 'amber')); segTitle = d.segNeed; }
  else if (seg === 'health') { rows = FA.slice().sort((a, b) => ((W[a.status] ?? 2) - (W[b.status] ?? 2)) || ((a.ndvi ?? 1) - (b.ndvi ?? 1))); segTitle = d.segHealth; }
  else if (seg === 'all') { rows = FA.slice().sort((a, b) => farmLabel(a).localeCompare(farmLabel(b))); segTitle = d.segAll; }
  else rows = need.concat(FA.filter(f => f.status === 'amber')).slice(0, 6);
  const drill = !!seg;
  const tile = k => () => setSeg(seg === k ? null : k);

  return <div className="dk">

    {/* ── today's briefing ─────────────────────────────────────────── */}
    <div className="sheet">
      <div className="headrow">
        <div>
          <div className="eyeb">{d.briefing}{cropF ? ' · ' + cropF : ''}</div>
          <h2 className="big">{N > 0 ? (N === 1 ? d.need1 : d.needN(N)) : d.calm + ' — ' + d.calmTail}</h2>
          <p className="sub">
            {topDrv && C.DRIVERS[topDrv[0]] ? d.cause + ' — ' + drvWord(topDrv[0]).toLowerCase() + ' (' + topDrv[1] + ' ' + d.farmsWord + '). ' : ''}
            {calmWard ? calmWard[0] + ' — ' + d.noUrgent + '. ' : ''}
            {P.asOf ? d.asOf + ' · ' + P.asOf + '.' : d.sample + '.'}
          </p>
        </div>
        <a className="pillbtn" href="/bulletin">{d.ninoLink}</a>
      </div>

      {cropChips.length > 1 && <div className="fchips">
        <span className="flab">{d.cropLab}</span>
        <button className={'fchip' + (cropF === '' ? ' on' : '')} onClick={() => setCropF('')}>{d.all} · {P.FARMS.length}</button>
        {cropChips.map(c => <button key={c[0]} className={'fchip' + (cropF === c[0] ? ' on' : '')}
          onClick={() => setCropF(cropF === c[0] ? '' : c[0])}>{c[0]} · {c[1]}</button>)}
      </div>}

      <div className="kpis">
        <div className={'kpi2 click' + (seg === 'all' ? ' sel' : '')} onClick={tile('all')} title={d.tapTile}>
          <div><div className="kl">{d.kMembers}</div><div className="kv">{FA.length}</div>
          <div className="ks">{ha != null ? ha + ' ha · ' : ''}{cropF || active.county}</div></div><span className="kic">◍</span></div>
        <div className={'kpi2 focus click' + (seg === 'need' ? ' sel' : '')} onClick={tile('need')} title={d.tapTile}>
          <div><div className="kl">{d.kNeed}</div><div className="kv">{N}</div>
          <div className="ks">{d.worstFirst}</div></div><span className="kic">⚠</span></div>
        <div className={'kpi2 click' + (seg === 'health' ? ' sel' : '')} onClick={tile('health')} title={d.tapTile}>
          <div><div className="kl">{d.kHealth}</div><div className="kv">{health.green}<small> {d.good}</small></div>
          <div className="ks">{health.amber} {d.watch} · {health.red} {d.urgent}{noData ? ' · ' + noData + ' ' + d.noNew : ''}</div></div><span className="kic">✦</span></div>
        {tayari > 0
          ? <div className="kpi2"><div><div className="kl">{d.kReady}</div><div className="kv">{tayari}<small> {d.farmsWord}</small></div>
              <div className="ks">{d.readySub}</div></div><span className="kic">◆</span></div>
          : <div className="kpi2"><div><div className="kl">{d.kTasks}</div><div className="kv">{tasks.length}</div>
              <div className="ks">{tasks.length ? d.tasksSet : d.tasksNone}</div></div><span className="kic">✓</span></div>}
      </div>
    </div>

    {/* ── start here / drill list + right rail ─────────────────────── */}
    <div className="grid">
      <div className="panel">
        <div className="ph2">
          <div><div className="eyeb">{drill ? (cropF || d.all) + ' · ' + rows.length : d.startSub}</div>
            <h3>{segTitle || d.startHere}</h3></div>
          {drill
            ? <button className="pillbtn" onClick={() => setSeg(null)}>{d.back}</button>
            : <span className="calm">{d.calmLine}</span>}
        </div>
        <div className="tbox" style={drill ? { maxHeight: 470, overflowY: 'auto' } : null}>
          <table>
            <thead><tr><th>{d.thFarmer}</th><th>{d.thCrop}</th><th>{d.thCounty}</th>
              {drill ? <th>{d.thNdvi}</th> : <th>{d.thWhy}</th>}<th>{d.thState}</th><th></th></tr></thead>
            <tbody>{rows.map(f => { const done = tasks.some(x => x.id === f.id); const actionable = f.status === 'red' || f.status === 'amber';
              return <tr key={f.id} onClick={() => onOpen(f.id)}>
              <td><div className="fn">{farmLabel(f)}</div>{f.area ? <div className="shid">{f.area} ha</div> : null}</td>
              <td>{cropName(f)}</td>
              <td>{f.ward}</td>
              {drill
                ? <td><span className="fn" style={{ fontSize: 13.5 }}>{f.ndvi != null ? f.ndvi.toFixed(2) : '—'}</span></td>
                : <td>{drvWord(f.driver)}</td>}
              <td><span className={'chip2 ' + (HALI[f.status] || HALI.nodata)[1]}>{hw[f.status] || hw.nodata}</span></td>
              <td style={{ textAlign: 'right' }}>{done
                ? <span className="shid">{d.assigned} ✓</span>
                : actionable ? <button className="assign" onClick={e => { e.stopPropagation(); onAssign(f, true); }}>{d.assign} ›</button> : null}</td>
            </tr>; })}</tbody>
          </table>
        </div>
      </div>

      <div className="col">
        <div className="panel">
          <div className="ph2"><div><div className="eyeb">{d.glance}</div><h3>{d.zones}</h3></div></div>
          {kanda.map(kv => { const w = kv[0], o = kv[1];
            const col = o.red ? 'var(--red)' : o.amber ? 'var(--amber)' : 'var(--green)';
            const word = o.red ? o.red + ' ' + d.zHaraka : o.amber ? o.amber + ' ' + d.zAngalia : d.noUrgent;
            return <div key={w} className="rrow"><div><div className="rn">{w}</div><div className="rs">{word}</div></div>
              <span className="rdot" style={{ background: col }}></span></div>; })}
        </div>

        <div className="panel">
          <div className="ph2"><div><div className="eyeb">{d.recordsSub}</div><h3>{d.records}</h3></div></div>
          <div className="honest"><b>{cropF ? verified : P.verifiedCount}</b> {d.recVerified}<br /><b>{cropF ? noData : P.noData}</b> {d.recNoData}<br />
            {P.asOf ? d.asOf + ' · ' + P.asOf + ' · ' : ''}{d.recTail}</div>
        </div>
      </div>
    </div>

    {/* ── how the co-op reads a shamba (Kuza) + the record ─────────── */}
    <div className="band" style={{ gridTemplateColumns: '1fr' }}>
      <div className="panel">
        <div className="ph2">
          <div><div className="eyeb">{d.kuzaEyeb}</div><h3>{d.kuzaTitle}</h3></div>
          <button className="pillbtn" onClick={onMap}>{d.openMap} ›</button>
        </div>
        <div className="kuza">
          <div className="kz"><div className="kzgrid"><i style={{ background: 'var(--gold)' }}></i></div>
            <b>{d.spotT}</b><span>{d.spotB}</span></div>
          <div className="kz"><div className="kzgrid"><i></i><i></i><i></i><i></i></div>
            <b>{d.pointT}</b><span>{d.pointB}</span></div>
          <div className="kz"><div className="kzgrid" style={{ width: 34, gridTemplateColumns: 'repeat(4,1fr)' }}>
            {[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15].map(i => <i key={i} style={{ background: 'var(--blue)', opacity: .8 }}></i>)}</div>
            <b>{d.acreT}</b><span>{d.acreB}</span></div>
        </div>
        <div className="mix">{P.cropMix.map((m, i) => <i key={i} style={{ width: m.pct + '%', background: m.color || MIX_COLORS[i % MIX_COLORS.length] }}></i>)}</div>
        <div className="mixkey">{P.cropMix.map((m, i) => <span key={i}><i style={{ background: m.color || MIX_COLORS[i % MIX_COLORS.length] }}></i>{titleCase(lang === 'sw' ? m.cropSw : m.crop)} {m.pct}%</span>)}</div>
      </div>
    </div>
  </div>;
}

const NAV = [['connect', 'nav_connect', 'home'], ['members', 'nav_members', 'person'], ['map', 'nav_map', 'grid'], ['tasks', 'nav_tasks', 'task'], ['me', 'nav_me', 'globe']];

function App() {
  const [lang, setLang] = useState('sw');
  const [tab, setTab] = useState('connect');
  const [farmId, setFarmId] = useState(null);
  const [filter, setFilter] = useState('all');
  const [coopId, setCoopId] = useState('othaya');
  const [eudr, setEudr] = useState(false);
  const [directory, setDirectory] = useState(null); // live CRM list (null = not loaded; static COOPS remain the fallback)
  useEffect(() => {
    fetch(DIRECTORY_URL).then(r => r.ok ? r.json() : null)
      .then(j => { if (j && j.coops && j.coops.length) setDirectory(j.coops.map(dirToCoop)); })
      .catch(() => {});
  }, []);

  /* ---- LIVE mode: field-agent/manager sign-in (coop claim) -> get_coop_dashboard ---- */
  const [fbUser, setFbUser] = useState(undefined);
  const [liveData, setLiveData] = useState(null);
  const [liveErr, setLiveErr] = useState('');
  useEffect(() => {
    // survive a partial firebase script load (flaky rural networks): demo path must never white-screen
    if (typeof firebase === 'undefined' || typeof firebase.auth !== 'function') { setFbUser(null); return; }
    try { if (!firebase.apps.length) firebase.initializeApp(FB_CONFIG); } catch (e) { setFbUser(null); return; }
    return firebase.auth().onAuthStateChanged(u => {
      setFbUser(u || null); setLiveErr('');
      if (!u) { setLiveData(null); setCoopId(id => id === 'live_rada' ? 'othaya' : id); return; }
      u.getIdToken()
        .then(tok => fetch(COOP_DASH_URL, { headers: { Authorization: 'Bearer ' + tok } }))
        .then(r => r.json().then(j => ({ ok: r.ok, j })))
        .then(({ ok, j }) => {
          // stale-response guard: if the agent signed out while this was in flight, drop it
          const cur = firebase.auth().currentUser;
          if (!cur || cur.uid !== u.uid) return;
          if (ok && j && j.farms) { setLiveData(j); setCoopId('live_rada'); }
          else setLiveErr((j && j.error) || 'access denied');
        })
        .catch(e => setLiveErr(String(e)));
    });
  }, []);
  const liveP = useMemo(() => liveData ? buildLiveP(liveData) : null, [liveData]);

  const coopList = useMemo(() => {
    const base = directory || C.COOPS || [];
    if (!liveData) return base;
    const lc = liveData.coop || {};
    return [{ id: 'live_rada', short: radaBrand(lc.short_name) || 'RaDa Co-op', name: radaBrand(lc.name) || 'RaDa Co-op', county: lc.county || 'Multi-county', crop: lc.crop_focus || '', cropSw: lc.crop_focus || '', membersTotal: (liveData.summary || {}).farms || 0, live: true }].concat(base);
  }, [directory, liveData]);
  const active = Object.assign({}, C.COOP, coopList.find(c => c.id === coopId) || {});
  const isLive = coopId === 'live_rada' && !!liveP;
  const P = isLive ? liveP : {
    live: false, FARMS: C.FARMS, needAttention: C.needAttention, driverCounts: C.driverCounts,
    health: C.health, noData: 0, verifiedCount: C.verifiedCount,
    members: C.COOP.members, hectares: C.COOP.hectares, cropMix: C.cropMix, asOf: null, method: '',
  };
  const [tasks, setTasks] = useState(() => { try { return JSON.parse(localStorage.getItem('rada_coop_tasks_v1') || '[]'); } catch (e) { return []; } });
  const t = useMemo(() => C.makeT(lang), [lang]);
  useEffect(() => { try { localStorage.setItem('rada_coop_tasks_v1', JSON.stringify(tasks)); } catch (e) {} }, [tasks]);

  const scrollTop = () => { const m = document.querySelector('.coop-main'); if (m) m.scrollTop = 0; };
  const openFarm = id => { setFarmId(id); scrollTop(); };
  /* stay=true (desktop dashboard) keeps you on the page instead of jumping to Kazi */
  const assign = (f, stay) => { setTasks(ts => ts.some(x => x.id === f.id) ? ts : ts.concat([{ id: f.id, farmer: f.farmer, name: f.name, driver: f.driver }])); setFarmId(null); if (!stay) setTab('tasks'); };
  /* >=1000px — the Connect tab becomes the Ofisi ya Ushirika dashboard */
  const [wide, setWide] = useState(() => matchMedia('(min-width:1000px)').matches);
  useEffect(() => {
    const mq = matchMedia('(min-width:1000px)'); const h = () => setWide(mq.matches);
    if (mq.addEventListener) mq.addEventListener('change', h); else mq.addListener(h);
    window.addEventListener('resize', h);
    return () => { if (mq.removeEventListener) mq.removeEventListener('change', h); else mq.removeListener(h); window.removeEventListener('resize', h); };
  }, []);
  /* theme: dark default, 'light' = brand cream (Chemeli). Persisted; set pre-paint in coop.html */
  const [theme, setTheme] = useState(() => document.documentElement.dataset.theme === 'light' ? 'light' : 'dark');
  const flipTheme = () => { const nx = theme === 'dark' ? 'light' : 'dark'; setTheme(nx); if (nx === 'light') document.documentElement.dataset.theme = 'light'; else delete document.documentElement.dataset.theme; try { localStorage.setItem('rada_theme', nx); } catch (e) {} };
  const goRoster = k => { setTab('members'); };
  const farm = farmId && P.FARMS.find(f => f.id === farmId);
  const openEudr = () => { setEudr(true); scrollTop(); };

  let body;
  if (eudr) body = <EudrView active={active} P={P} t={t} lang={lang} onBack={() => setEudr(false)} />;
  else if (farm) body = <FarmView f={farm} P={P} fbUser={fbUser} t={t} lang={lang} onBack={() => setFarmId(null)} onAssign={assign} />;
  else if (tab === 'connect') body = wide
    ? <Desk P={P} active={active} isLive={isLive} lang={lang} tasks={tasks} onOpen={openFarm} onAssign={assign} onMap={() => setTab('map')} />
    : <Connect P={P} t={t} lang={lang} onOpen={openFarm} onAssign={assign} filter={filter} setFilter={setFilter} goRoster={goRoster} onOpenEudr={openEudr} />;
  else if (tab === 'members') body = <Roster P={P} active={active} t={t} lang={lang} onOpen={openFarm} />;
  else if (tab === 'map') body = <Ramani t={t} lang={lang} P={P} isLive={isLive} fbUser={fbUser} />;
  else if (tab === 'tasks') body = <Kazi t={t} lang={lang} tasks={tasks} setTasks={setTasks} />;
  else body = <Mimi t={t} lang={lang} setLang={setLang} active={active} coopId={coopId} setCoopId={setCoopId} coopList={coopList} live={!!directory} fbUser={fbUser} liveErr={liveErr} isLive={isLive} />;

  return <div className="coop-root">
    <div className="coop-phone">
      <header className="coop-head" style={s("flex:0 0 auto;padding:14px 16px 12px;background:var(--forest);color:var(--ink-on-dark);display:flex;justify-content:space-between;align-items:flex-start")}>
        <div><div style={s("font-family:var(--font-mono);font-size:9.5px;letter-spacing:1.2px;text-transform:uppercase;opacity:.7")}>{t('eyebrow')}</div>
          <div style={s("display:flex;align-items:center;gap:8px;margin-top:2px")}><span style={s("font-size:18px;font-weight:700;line-height:1.1")}>{active.short}</span>
            {isLive && <span style={s("font-family:var(--font-mono);font-size:9px;font-weight:700;letter-spacing:.6px;background:var(--green);color:#fff;border-radius:6px;padding:2px 7px")}>LIVE</span>}</div>
          <div style={s("font-size:11.5px;opacity:.8")}>{P.members} {t('members').toLowerCase()} · {P.hectares != null ? P.hectares + ' ha · ' : ''}{active.county}{isLive && P.asOf ? ' · ' + P.asOf : ''}</div></div>
        <div style={s("display:flex;gap:8px;align-items:center")}>
          <button onClick={flipTheme} title={theme === 'dark' ? 'Switch to the cream daylight theme' : 'Switch to the dark theme'} style={s("background:rgba(255,255,255,.14);border:1px solid rgba(255,255,255,.25);color:#fff;border-radius:8px;padding:5px 9px;font-size:11px;font-family:var(--font-mono);cursor:pointer")}>{theme === 'dark' ? '☀ Krem' : '☾ Giza'}</button>
          <button onClick={() => setLang(lang === 'sw' ? 'en' : 'sw')} style={s("background:rgba(255,255,255,.14);border:1px solid rgba(255,255,255,.25);color:#fff;border-radius:8px;padding:5px 9px;font-size:11px;font-family:var(--font-mono);cursor:pointer")}>{lang === 'sw' ? 'EN' : 'SW'}</button>
          <Avatar name={C.COOP.rep} size={30} />
        </div>
      </header>
      <main className="coop-main">{body}</main>
      <nav className="coop-nav" style={s("flex:0 0 auto;display:flex;justify-content:space-around;border-top:1px solid var(--line);background:rgba(10,12,16,.88);padding:8px 6px 10px")}>
        {NAV.map(([id, key, icon]) => { const on = tab === id && !farm && !eudr; return <button key={id} onClick={() => { setFarmId(null); setEudr(false); setTab(id); }} style={s("background:none;border:none;cursor:pointer;display:flex;flex-direction:column;align-items:center;gap:3px;font-family:var(--font-mono);font-size:9px;padding:2px 6px;color:" + (on ? 'var(--orange-deep)' : 'var(--ink-mute)'))}>
          <Icon d={icon} size={20} color={on ? 'var(--orange-deep)' : 'var(--ink-mute)'} />{t(key)}</button>; })}
      </nav>
    </div>
  </div>;
}

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