/* ============================================================
   PRODUCTION STATUS — owner-only financial control room
   · Gated behind a simple "boss login" (the creator/owner)
   · NEVER visible to invited crew — entry only from the
     owner's top-right menu; numbers live outside the project
   · Portfolio P&L across all of the owner's productions +
     four health gauges: budget · days · work intensity · crew
   ============================================================ */

/* ---------- global open/close store (called from the avatar menu) ---------- */
const psSubs = new Set();
let psOpen = false;
window.openProductionStatus = () => { psOpen = true; psSubs.forEach(f => f()); };
window.closeProductionStatus = () => { psOpen = false; psSubs.forEach(f => f()); };
function usePSOpen() {
  const [, force] = useState(0);
  useEffect(() => { const fn = () => force(n => n + 1); psSubs.add(fn); return () => psSubs.delete(fn); }, []);
  return psOpen;
}

/* ---------- helpers ---------- */
const ps$ = n => '$' + Math.round(Number(n) || 0).toLocaleString('en-US');
const psK = n => {                                   /* compact for tickers */
  const v = Math.round(Number(n) || 0);
  if (Math.abs(v) >= 1000000) return '$' + (v / 1000000).toFixed(2) + 'M';
  if (Math.abs(v) >= 1000) return '$' + (v / 1000).toFixed(0) + 'K';
  return '$' + v;
};
const pctOf = (a, b) => b ? (a / b) * 100 : 0;
const clamp = (v, a, b) => Math.max(a, Math.min(b, v));
/* health → tone: 'good' | 'watch' | 'risk' (lower ratio is better for spend) */
const tone3 = (ratio, warn = 0.85, bad = 1) => ratio >= bad ? 'risk' : ratio >= warn ? 'watch' : 'good';
const TONE = {
  good:  { t: 'text-mint',     b: 'bg-mint',           soft: 'bg-mint/12 text-mint',         ring: 'border-mint/40',   hex: '#22D48A', label: 'Healthy' },
  watch: { t: 'text-amber',    b: 'bg-amber',          soft: 'bg-amber/12 text-amber',       ring: 'border-amber/40',  hex: '#FFB020', label: 'Watch' },
  risk:  { t: 'text-[#FF6B8A]',b: 'bg-[#FF4D6D]',      soft: 'bg-[#FF4D6D]/12 text-[#FF6B8A]',ring: 'border-[#FF4D6D]/40',hex: '#FF4D6D', label: 'At risk' },
};

/* ============================================================
   THE OWNER'S SLATE — confidential financials (not in project)
   dep rows: [budget, spent, committed]
   ============================================================ */
const PS_DEPTS = ['Camera', 'Lighting & Grip', 'Art Dept', 'Wardrobe & HMU', 'Cast', 'Production & Crew', 'Locations', 'Post', 'Travel & Catering', 'Insurance & Contingency'];

const OWNER_SLATE = [
  {
    code: 'CRSK', name: 'Crimson Skies', client: 'Aether Aviation Co.', type: 'Commercial', status: 'In Production',
    color: '#5B0952', fg: '#F7A8CF', fee: 420000, daysUsed: 1, daysPlanned: 4, intensity: 72, crewLoad: 64,
    dep: [[38000, 22000, 9000], [30000, 18000, 6000], [42000, 25000, 12000], [18000, 11000, 3000], [35000, 20000, 8000], [46000, 28000, 7000], [22000, 14000, 4000], [25000, 4000, 6000], [12000, 7000, 2000], [15000, 9000, 0]],
  },
  {
    code: 'NTDE', name: 'Northern Tide', client: 'Meridian Public Media', type: 'Documentary Series', status: 'In Post',
    color: '#16323A', fg: '#7FD7C4', fee: 680000, daysUsed: 18, daysPlanned: 18, intensity: 41, crewLoad: 38,
    dep: [[52000, 50000, 1000], [40000, 39000, 0], [30000, 28000, 1000], [16000, 15000, 0], [44000, 44000, 0], [88000, 82000, 4000], [34000, 33000, 0], [120000, 61000, 38000], [26000, 24000, 1000], [22000, 18000, 0]],
  },
  {
    code: 'VLVT', name: 'Velvet Hours', client: 'Maison Lëa', type: 'Brand Film', status: 'Delivered',
    color: '#200248', fg: '#C9A9FF', fee: 295000, daysUsed: 3, daysPlanned: 3, intensity: 30, crewLoad: 22,
    dep: [[30000, 31500, 0], [24000, 24800, 0], [38000, 41000, 0], [14000, 14200, 0], [22000, 22000, 0], [34000, 35500, 0], [18000, 18600, 0], [22000, 21000, 0], [10000, 11200, 0], [12000, 12000, 0]],
  },
  {
    code: 'HRBR', name: 'Harbor & Co.', client: 'Still Water Spirits', type: 'Campaign', status: 'In Prep',
    color: '#1F2A3D', fg: '#9FC1E8', fee: 240000, daysUsed: 0, daysPlanned: 5, intensity: 12, crewLoad: 18,
    dep: [[34000, 0, 14000], [26000, 0, 8000], [40000, 2000, 16000], [16000, 0, 4000], [30000, 5000, 12000], [42000, 3000, 9000], [20000, 0, 6000], [24000, 0, 0], [12000, 1000, 3000], [14000, 0, 0]],
  },
];

/* derive everything for one production at a given VAT + protected margin */
function derive(p, vatRate, protectPct) {
  const budget = p.dep.reduce((s, d) => s + d[0], 0);
  const spent = p.dep.reduce((s, d) => s + d[1], 0);
  const committed = p.dep.reduce((s, d) => s + d[2], 0);
  const projFinal = p.dep.reduce((s, d) => s + Math.max(d[0], d[1] + d[2]), 0); /* lands at budget, or higher if over-ordered */
  const vat = p.fee - p.fee / (1 + vatRate);          /* fee is gross, incl. VAT */
  const net = p.fee - vat;                            /* net revenue available to make the film + profit */
  const profit = net - projFinal;                     /* top budget − VAT − all production */
  const margin = pctOf(profit, net);
  const protectReserve = net * protectPct;            /* profit the owner refuses to spend into */
  const safeToSpend = net - protectReserve - (spent + committed);
  const budgetTone = tone3((spent + committed) / Math.max(1, net - protectReserve));
  const daysTone = tone3(p.daysPlanned ? p.daysUsed / p.daysPlanned : 0, 0.8, 1.001);
  const intensityTone = tone3(p.intensity / 100, 0.7, 0.9);
  const crewTone = tone3(p.crewLoad / 100, 0.7, 0.9);
  return { budget, spent, committed, projFinal, vat, net, profit, margin, protectReserve, safeToSpend, budgetTone, daysTone, intensityTone, crewTone };
}

/* ============================================================
   LIVE SLATE — derive each owned production's financials from its
   real per-project stores (budget, purchasing, brief, DPR, board).
   Reads across productions with the raw (un-proxied) localStorage.
   ============================================================ */
function psReadRaw(pid, key) {
  try {
    const k = (pid === 'crimson') ? key : '@' + pid + '.' + key;
    const raw = window.__lsRaw ? window.__lsRaw.get(k) : localStorage.getItem(k);
    return raw == null ? null : JSON.parse(raw);
  } catch (e) { return null; }
}
const PS_DEPT_RX = [
  [/camera|cinematograph|\bdop\b|\bdp\b/i, 'Camera'],
  [/light|grip|gaffer|electric/i, 'Lighting & Grip'],
  [/art|set dress|dressing|prop|design|construction/i, 'Art Dept'],
  [/wardrobe|costume|make ?up|hmu|hair|styling/i, 'Wardrobe & HMU'],
  [/cast|talent|actor|extra|background/i, 'Cast'],
  [/location|permit|scout/i, 'Locations'],
  [/post|edit|colou?r|grade|vfx|sound|music|\bmix\b|online|offline/i, 'Post'],
  [/travel|transport|cater|craft|logist|hotel|flight|meal/i, 'Travel & Catering'],
  [/insurance|contingency|legal|misc|bond/i, 'Insurance & Contingency'],
];
function deptIndexFor(name) {
  for (let i = 0; i < PS_DEPT_RX.length; i++) if (PS_DEPT_RX[i][0].test(name || '')) return PS_DEPTS.indexOf(PS_DEPT_RX[i][1]);
  return PS_DEPTS.indexOf('Production & Crew'); // above-the-line, producer, crew & anything unmatched
}
function deriveSlateEntry(prod) {
  const pid = prod.pid;
  const brief = psReadRaw(pid, 'silverlines.brief.v1') || {};
  const cats = psReadRaw(pid, 'silverlines.budget.' + pid + '.v1');
  const purchase = psReadRaw(pid, 'silverlines.purchase.' + pid + '.v1') || {};
  const dpr = psReadRaw(pid, 'silverlines.dpr.' + pid + '.v1');
  const sb = psReadRaw(pid, 'silverlines.storyboard.v1');
  const crewlog = psReadRaw(pid, 'silverlines.crewlog.' + pid + '.v1');
  const dep = PS_DEPTS.map(() => [0, 0, 0]);
  (Array.isArray(cats) ? cats : []).forEach(c => {
    const est = (c.lines || []).reduce((a, l) => a + (+l.qty || 0) * (+l.rate || 0), 0);
    const act = (c.lines || []).reduce((a, l) => a + (+l.actual || 0), 0);
    const idx = deptIndexFor(c.name); dep[idx][0] += est; dep[idx][1] += act;
  });
  const pAll = (purchase.sets || []).flatMap(s => s.items || []);
  const pSum = st => pAll.filter(i => i.status === st).reduce((a, i) => a + (+i.qty || 0) * (+i.price || 0), 0);
  const ai = PS_DEPTS.indexOf('Art Dept'); dep[ai][1] += pSum('purchased'); dep[ai][2] += pSum('approved');
  const budget = dep.reduce((s, d) => s + d[0], 0);
  const spent = dep.reduce((s, d) => s + d[1], 0);
  const daysUsed = (Array.isArray(dpr) ? dpr : []).filter(d => d.wrap).length;
  const daysPlanned = +brief.days || (Array.isArray(sb) ? sb.reduce((m, s) => Math.max(m, (s.sched && +s.sched.day) || 0), 0) : 0) || daysUsed || 0;
  return {
    code: prod.code || (prod.name || 'Untitled').slice(0, 4).toUpperCase(),
    name: prod.name || 'Untitled', client: brief.client || prod.client || '—', type: brief.type || prod.type || 'Production',
    status: prod.status || brief.status || 'In Prep', color: prod.color || prod.bg || '#1F2A3D', fg: prod.fg || '#9FC1E8',
    fee: +brief.budget || budget || 0, daysUsed, daysPlanned,
    intensity: clamp(Math.round(pctOf(spent, budget || 1)), 0, 100),
    crewLoad: clamp((Array.isArray(crewlog) ? crewlog.length : 0) * 6, 0, 100),
    dep, _real: true,
  };
}
/* the real productions (with data) + the Crimson demo; falls back to the fictional
   slate as clearly-badged demo garnish when the owner has no real productions yet. */
function ownerSlate() {
  const prods = (window.slProductions ? window.slProductions() : []) || [];
  const real = prods.filter(p => !p.demo && p.pid && p.pid !== 'crimson');
  const realEntries = real.map(deriveSlateEntry);
  const crsk = OWNER_SLATE.find(p => p.code === 'CRSK');
  if (realEntries.length) return [...realEntries, ...(crsk ? [{ ...crsk, _demo: true }] : [])];
  return OWNER_SLATE.map(p => ({ ...p, _demo: true }));
}

/* ============================================================
   SMALL UI PARTS
   ============================================================ */
function StatBlock({ label, value, sub, tone, big }) {
  const tn = tone && TONE[tone];
  return (
    <div className="min-w-0">
      <div className="font-mono text-[9.5px] uppercase tracking-[0.16em] text-zinc-500">{label}</div>
      <div className={cx('mt-1 font-mono font-700 leading-none tabular-nums', big ? 'text-[26px]' : 'text-[18px]', tn ? tn.t : 'text-head')}>{value}</div>
      {sub != null && <div className="mt-1 font-mono text-[10px] text-zinc-500">{sub}</div>}
    </div>
  );
}

/* ---------- department icons + per-production teams (people, not just money) ---------- */
const DEPT_META = {
  'Camera': 'Video', 'Lighting & Grip': 'Lightbulb', 'Art Dept': 'Palette', 'Wardrobe & HMU': 'Shirt',
  'Cast': 'Drama', 'Production & Crew': 'ClipboardList', 'Locations': 'MapPin', 'Post': 'Scissors',
  'Travel & Catering': 'UtensilsCrossed', 'Insurance & Contingency': 'ShieldCheck',
};
const CAST_STATUS = {
  confirmed: { t: 'text-mint', d: 'bg-mint', label: 'Confirmed' },
  hold: { t: 'text-amber', d: 'bg-amber', label: 'On hold' },
  optioned: { t: 'text-electric-soft', d: 'bg-electric', label: 'Optioned' },
  wrapped: { t: 'text-zinc-400', d: 'bg-zinc-500', label: 'Wrapped' },
};
/* crew: [name, role, dayRate, days] · cast: [name, character, fee, status] */
const PS_TEAMS = {
  CRSK: {
    cast: [
      ['Lena Frost', '“The Aviator” · Lead', 14000, 'confirmed'],
      ['Marcus Vane', '“Ground Control”', 6500, 'confirmed'],
      ['Iris Cole', '“The Engineer”', 4200, 'confirmed'],
      ['Theo Mraz', '“Tower Operator”', 2800, 'hold'],
      ['Background · 6 pilots', 'Featured extras', 1600, 'confirmed'],
    ],
    crew: {
      'Camera': [['Idris Vance', 'Director of Photography', 1800, 4], ['Mia Sorenson', '1st AC', 700, 4], ['Owen Pike', '2nd AC', 520, 4], ['Dana Reuben', 'DIT', 640, 4]],
      'Lighting & Grip': [['Theo Nkemelu', 'Gaffer', 850, 4], ['Russ Calderon', 'Key Grip', 800, 4], ['Sam Iversen', 'Best Boy', 520, 4]],
      'Art Dept': [['Ravi Anand', 'Production Designer', 1100, 6], ['Cleo Banks', 'Set Decorator', 680, 6], ['Hugo Pratt', 'Props Master', 620, 5], ['Nina Sol', 'Set Dresser', 420, 6]],
      'Wardrobe & HMU': [['Juno Park', 'Costume Designer', 700, 4], ['Coraline Bisset', 'Key MUA', 650, 4], ['Remy Dax', 'Hair', 480, 4]],
      'Production & Crew': [['Suki Tanaka', '1st AD', 950, 4], ['Priya Raman', '2nd AD', 620, 4], ['Noah Vega', 'Coordinator', 560, 5], ['Jess Okoro', 'Runner', 240, 4], ['Diego Salas', 'Runner', 240, 4], ['Amy Chen', 'Set Runner', 240, 4]],
      'Travel & Catering': [['Goldspoon Catering', 'Craft & hot meals', 2400, 1], ['Bea Lumen', 'Craft Service', 360, 4], ['Carl Otis', 'Driver Captain', 420, 4]],
      'Locations': [['Marble & Co.', 'Locations + permits', 1800, 1], ['Wes Hark', 'Location Manager', 700, 3]],
      'Post': [['Edit Bay 4', 'Offline suite', 1500, 1]],
    },
  },
  NTDE: {
    cast: [['Aria Voss', 'Narrator', 9000, 'confirmed'], ['Documentary subjects', '7 interviewees', 3500, 'confirmed']],
    crew: {
      'Camera': [['Sana Mehre', 'DP / Operator', 1400, 18], ['Lou Brandt', 'AC', 600, 18]],
      'Production & Crew': [['Noor Haddad', 'Producer', 1500, 18], ['Kit Yarrow', 'Field Producer', 800, 18], ['Tess Vu', 'Runner', 240, 12]],
      'Post': [['Mara Silva', 'Lead Editor', 1100, 40], ['Owen Hale', 'Assistant Editor', 620, 30], ['Sound House', 'Mix & master', 6000, 1]],
      'Travel & Catering': [['Roadhouse Catering', 'Location meals', 1800, 6]],
    },
  },
  VLVT: {
    cast: [['Noé Saint', '“The Muse” · Lead', 12000, 'wrapped'], ['Elise Mar', '“The Watcher”', 5000, 'wrapped']],
    crew: {
      'Camera': [['Idris Vance', 'DP', 1900, 3], ['Mia Sorenson', '1st AC', 720, 3]],
      'Art Dept': [['Ravi Anand', 'Production Designer', 1200, 3], ['Nina Sol', 'Set Dresser', 440, 3]],
      'Production & Crew': [['Suki Tanaka', '1st AD', 1000, 3], ['Amy Chen', 'Runner', 260, 3]],
    },
  },
  HRBR: {
    cast: [['Casting in progress', '2 leads optioned', 0, 'optioned']],
    crew: {
      'Camera': [['TBD', 'DP — quoting', 0, 0]],
      'Production & Crew': [['Noor Haddad', 'Producer', 1500, 5], ['Priya Raman', '1st AD — held', 950, 5]],
      'Art Dept': [['Cleo Banks', 'Designer — prepping', 720, 4]],
    },
  },
};
const teamCost = m => (m.fee != null ? m.fee : (m.rate || 0) * (m.days || 1));

/* ---------- one department row, expandable to its people ---------- */
function DeptRow({ name, fin, members, open, onToggle }) {
  const [budget, spent, committed] = fin;
  const used = spent + committed, rem = budget - used;
  const t = tone3(used / Math.max(1, budget));
  const isCast = name === 'Cast';
  const head = members && members[0];
  return (
    <div className={cx('border-t border-line/60 first:border-t-0', open && 'bg-ink-900/50')}>
      <button onClick={onToggle} className="flex w-full items-center gap-3 px-4 py-2.5 text-left transition-colors hover:bg-ink-900/60">
        <span className="flex h-7 w-7 shrink-0 items-center justify-center rounded-md border border-line bg-ink-850 text-zinc-400"><Icon name={DEPT_META[name] || 'Folder'} size={14} /></span>
        <span className="w-[150px] shrink-0 truncate text-[12.5px] font-600 text-zinc-200">{name}</span>
        <span className="flex shrink-0 items-center gap-1 rounded-full border border-line px-1.5 py-0.5 font-mono text-[9.5px] text-zinc-400"><Icon name={isCast ? 'Drama' : 'User'} size={9} />{members ? members.length : 0}</span>
        <div className="relative mx-1 h-1.5 min-w-0 flex-1 overflow-hidden rounded-full bg-ink-700">
          <div className="absolute inset-y-0 left-0 rounded-full bg-zinc-500" style={{ width: clamp(pctOf(spent, budget), 0, 100) + '%' }}></div>
          <div className="absolute inset-y-0 rounded-r-full bg-zinc-500/40" style={{ left: clamp(pctOf(spent, budget), 0, 100) + '%', width: clamp(pctOf(committed, budget), 0, 100 - pctOf(spent, budget)) + '%' }}></div>
        </div>
        <span className="w-[70px] shrink-0 text-right font-mono text-[11.5px] text-zinc-300 tabular-nums">{psK(used)}</span>
        <span className="w-[58px] shrink-0 text-right font-mono text-[11px] tabular-nums" style={{ color: TONE[t].hex }}>{rem < 0 ? '−' : ''}{psK(Math.abs(rem))}</span>
        <Icon name={open ? 'ChevronDown' : 'ChevronRight'} size={14} className="shrink-0 text-zinc-500" />
      </button>
      {open && (
        <div className="px-4 pb-3 pt-0.5">
          {members && members.length ? (
            <div className="overflow-hidden rounded-lg border border-line/70">
              {members.map((m, i) => {
                const cost = teamCost(m);
                const st = isCast && CAST_STATUS[m.status];
                return (
                  <div key={i} className="flex items-center gap-2.5 border-t border-line/50 bg-ink-900/60 px-3 py-2 first:border-t-0">
                    <Avatar name={m.name} color={isCast ? '#5B0952' : '#2B303D'} size={24} />
                    <span className="min-w-0 flex-1">
                      <span className="block truncate text-[12px] font-600 text-zinc-100">{m.name}</span>
                      <span className="block truncate text-[10px] text-zinc-500">{m.sub}</span>
                    </span>
                    {st && <span className={cx('flex shrink-0 items-center gap-1 font-mono text-[9.5px]', st.t)}><span className={cx('h-1.5 w-1.5 rounded-full', st.d)}></span>{st.label}</span>}
                    {!isCast && (m.rate ? <span className="shrink-0 font-mono text-[10px] text-zinc-500 tabular-nums">{ps$(m.rate)}/d · {m.days}d</span> : null)}
                    <span className="w-[64px] shrink-0 text-right font-mono text-[11.5px] font-600 text-zinc-200 tabular-nums">{cost ? ps$(cost) : '—'}</span>
                  </div>
                );
              })}
            </div>
          ) : (
            <div className="rounded-lg border border-dashed border-line/70 px-3 py-2.5 text-center font-mono text-[10.5px] text-zinc-600">Roster not itemised · {psK(used)} committed</div>
          )}
        </div>
      )}
    </div>
  );
}

function DeptOverview({ p, depFin }) {
  const team = PS_TEAMS[p.code] || { cast: [], crew: {} };
  const cast = (team.cast || []).map(c => ({ name: c[0], sub: c[1], fee: c[2], status: c[3] }));
  const crewOf = name => (team.crew[name] || []).map(c => ({ name: c[0], sub: c[1], rate: c[2], days: c[3] }));
  const [open, setOpen] = useState(() => new Set(['Cast']));
  const toggle = name => setOpen(s => { const n = new Set(s); n.has(name) ? n.delete(name) : n.add(name); return n; });
  const heads = PS_DEPTS.reduce((a, name) => a + (name === 'Cast' ? cast.length : crewOf(name).length), 0);
  return (
    <div className="overflow-hidden rounded-xl border border-line bg-ink-900/40 lg:col-span-2">
      <div className="flex items-center justify-between border-b border-line px-4 py-2.5">
        <span className="font-mono text-[10px] uppercase tracking-[0.16em] text-zinc-500">Departments &amp; people · {p.name}</span>
        <span className="flex items-center gap-2 font-mono text-[10px] text-zinc-600">
          <span className="flex items-center gap-1"><Icon name="Users" size={11} />{heads} people</span>
          <span className="hidden sm:inline">click a dept to expand</span>
        </span>
      </div>
      <div>
        {PS_DEPTS.map((name, i) => (
          <DeptRow key={name} name={name} fin={depFin[i]} members={name === 'Cast' ? cast : crewOf(name)}
            open={open.has(name)} onToggle={() => toggle(name)} />
        ))}
      </div>
      <div className="flex items-center justify-between gap-2 border-t border-line px-4 py-2 font-mono text-[10px] text-zinc-500">
        <span className="flex items-center gap-3">
          <span className="flex items-center gap-1"><span className="h-2 w-2 rounded-full bg-zinc-500"></span>spent</span>
          <span className="flex items-center gap-1"><span className="h-2 w-2 rounded-full bg-zinc-500/40"></span>committed</span>
        </span>
        <span>right column = remaining vs budget</span>
      </div>
    </div>
  );
}

function Gauge({ icon, label, value, caption, tone, pct }) {
  const tn = TONE[tone];
  return (
    <div className={cx('rounded-xl border bg-ink-900 p-3.5', tn.ring)}>
      <div className="flex items-center justify-between">
        <span className="flex items-center gap-1.5 font-mono text-[9.5px] uppercase tracking-[0.14em] text-zinc-400"><Icon name={icon} size={12} className={tn.t} />{label}</span>
        <span className={cx('flex items-center gap-1 rounded-full px-1.5 py-0.5 text-[9px] font-700', tn.soft)}><span className={cx('h-1.5 w-1.5 rounded-full', tn.b)}></span>{tn.label}</span>
      </div>
      <div className={cx('mt-2 font-mono text-[24px] font-700 leading-none tabular-nums', tn.t)}>{value}</div>
      <div className="mt-2 h-1.5 overflow-hidden rounded-full bg-ink-700">
        <div className={cx('h-full rounded-full', tn.b)} style={{ width: clamp(pct, 3, 100) + '%' }}></div>
      </div>
      <div className="mt-1.5 font-mono text-[10px] text-zinc-500">{caption}</div>
    </div>
  );
}

/* ============================================================
   BOSS LOGIN — the gate
   ============================================================ */
function BossLogin({ onUnlock }) {
  const acct = window.Auth ? window.Auth.current() : null;
  const [email] = useState((acct && acct.email) || 'owner@silverlines.app');
  const [pw, setPw] = useState('');
  const [show, setShow] = useState(false);
  const [shake, setShake] = useState(false);
  const [err, setErr] = useState('');
  const submit = () => {
    /* verify the owner's real password (salted SHA-256) — not a length check */
    const res = window.Auth ? window.Auth.signIn(email, pw) : { ok: false, reason: 'no-auth' };
    if (!res.ok) {
      setErr(res.reason === 'password' ? 'That password does not match the owner account.'
        : res.reason === 'no-account' ? 'No owner account is set up on this device.'
        : 'Could not verify — please sign in again.');
      setShake(true); setTimeout(() => setShake(false), 420);
      return;
    }
    setErr('');
    try { sessionStorage.setItem('silverlines.boss.session', '1'); } catch (e) {}
    onUnlock();
  };
  return (
    <div className="flex min-h-full items-center justify-center p-6">
      <div className={cx('w-full max-w-[400px]', shake && 'ps-shake')}>
        <div className="mb-6 flex flex-col items-center text-center">
          <span className="relative flex h-16 w-16 items-center justify-center rounded-2xl border border-proflow/30 bg-proflow/10 text-proflow-soft">
            <Icon name="Vault" size={30} />
            <span className="absolute -bottom-1 -right-1 flex h-6 w-6 items-center justify-center rounded-full border-2 border-ink-950 bg-ink-850 text-amber"><Icon name="Lock" size={11} /></span>
          </span>
          <div className="mt-4 font-mono text-[10px] uppercase tracking-[0.22em] text-proflow-soft">Owner access only</div>
          <h2 className="mt-1.5 font-display text-[26px] leading-tight text-head">Production Status</h2>
          <p className="mt-2 text-[12.5px] leading-relaxed text-zinc-500">The real books — fees, costs and profit across your slate. Invited crew never see this. Sign in as the production owner to continue.</p>
        </div>
        <div className="rounded-2xl border border-line bg-ink-850 p-5">
          <label className="mb-1.5 block font-mono text-[10px] uppercase tracking-[0.14em] text-zinc-500">Owner</label>
          <div className="flex items-center gap-2.5 rounded-xl border border-line bg-ink-900 px-3 py-2.5">
            <Avatar name={(acct && acct.name) || 'Producer'} color={acct && window.Auth ? window.Auth.color(acct.id) : '#22D48A'} size={26} />
            <span className="min-w-0 flex-1 truncate text-[13px] text-zinc-200">{email}</span>
            <Chip tone="mint">Owner</Chip>
          </div>
          <label className="mb-1.5 mt-4 block font-mono text-[10px] uppercase tracking-[0.14em] text-zinc-500">Password</label>
          <div className="flex items-center gap-2 rounded-xl border border-line bg-ink-900 px-3 py-2.5 focus-within:border-electric/60">
            <Icon name="KeyRound" size={15} className="shrink-0 text-zinc-500" />
            <input autoFocus type={show ? 'text' : 'password'} value={pw} onChange={e => setPw(e.target.value)}
              onKeyDown={e => e.key === 'Enter' && submit()} placeholder="Enter owner password"
              className="min-w-0 flex-1 bg-transparent text-[13px] tracking-wide text-zinc-100 outline-none placeholder:text-zinc-600" />
            <button onClick={() => setShow(s => !s)} className="shrink-0 text-zinc-500 hover:text-zinc-300"><Icon name={show ? 'EyeOff' : 'Eye'} size={15} /></button>
          </div>
          <button onClick={submit} className="mt-4 flex w-full items-center justify-center gap-2 rounded-xl bg-cta py-3 font-display text-[14px] text-cta-ink transition-colors hover:bg-cta-hover">
            <Icon name="Unlock" size={15} /> Unlock Production Status
          </button>
          {err && <div className="mt-2.5 flex items-center justify-center gap-1.5 text-[11.5px] font-600" style={{ color: '#FF4D6D' }}><Icon name="AlertTriangle" size={12} /> {err}</div>}
          <div className="mt-3 flex items-center justify-center gap-1.5 font-mono text-[10px] text-zinc-600">
            <Icon name="ShieldCheck" size={11} className="text-mint" /> Owner-verified · session locks on exit
          </div>
        </div>
        <button onClick={() => window.closeProductionStatus()} className="mt-4 flex w-full items-center justify-center gap-1.5 text-[12px] font-600 text-zinc-500 transition-colors hover:text-zinc-300">
          <Icon name="ArrowLeft" size={13} /> Back to the production
        </button>
      </div>
    </div>
  );
}

/* ============================================================
   THE TERMINAL
   ============================================================ */
function StatusTerminal() {
  const { theme } = useContext(AccessContext) || {};
  const [vatRate, setVatRate] = useState(0.20);
  const [protectPct, setProtectPct] = useState(0.15);
  const SLATE = useMemo(() => ownerSlate(), []);
  const [sel, setSel] = useState(SLATE[0].code);
  const [clock, setClock] = useState(() => new Date());
  useEffect(() => { const t = setInterval(() => setClock(new Date()), 1000); return () => clearInterval(t); }, []);

  const rows = SLATE.map(p => ({ p, d: derive(p, vatRate, protectPct) }));
  /* portfolio active = everything not delivered counts toward live exposure; totals span the whole slate */
  const T = rows.reduce((a, { p, d }) => ({
    fee: a.fee + p.fee, vat: a.vat + d.vat, net: a.net + d.net, spent: a.spent + d.spent,
    committed: a.committed + d.committed, projFinal: a.projFinal + d.projFinal, profit: a.profit + d.profit,
    safe: a.safe + d.safeToSpend,
  }), { fee: 0, vat: 0, net: 0, spent: 0, committed: 0, projFinal: 0, profit: 0, safe: 0 });
  const portMargin = pctOf(T.profit, T.net);
  const portTone = tone3((T.spent + T.committed) / Math.max(1, T.net - T.net * protectPct));

  const current = rows.find(r => r.p.code === sel) || rows[0];
  const cp = current.p, cd = current.d;
  const burn = cp.daysUsed ? cd.spent / cp.daysUsed : 0;

  const fmtTime = clock.toLocaleTimeString('en-US', { hour12: false });
  const fmtDate = clock.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' });

  return (
    <div className="flex h-full flex-col bg-ink-950">
      {/* ===== terminal header ===== */}
      <header className="flex items-center gap-4 border-b border-line bg-ink-900/70 px-5 py-3 backdrop-blur">
        <span className="flex h-9 w-9 items-center justify-center rounded-lg border border-proflow/30 bg-proflow/10 text-proflow-soft"><Icon name="Vault" size={18} /></span>
        <div className="min-w-0">
          <div className="flex items-center gap-2">
            <h1 className="font-display text-[17px] leading-none text-head">Production Status</h1>
            <span className="flex items-center gap-1 rounded-full border border-[#FF4D6D]/30 bg-[#FF4D6D]/10 px-2 py-0.5 font-mono text-[9px] font-700 uppercase tracking-wider text-[#FF6B8A]"><Icon name="EyeOff" size={9} />Confidential</span>
          </div>
          <div className="mt-0.5 font-mono text-[10px] text-zinc-500">Owner &amp; finance only · {SLATE.length} production{SLATE.length === 1 ? '' : 's'} on slate{SLATE.every(p => p._demo) ? ' · demo data' : ''}</div>
        </div>
        <div className="ml-auto flex items-center gap-3">
          <div className="hidden items-center gap-1.5 font-mono text-[11px] text-zinc-400 sm:flex">
            <span className="h-1.5 w-1.5 rounded-full bg-mint proflow-pulse"></span>LIVE
            <span className="text-zinc-600">·</span><span className="tabular-nums text-zinc-300">{fmtTime}</span>
            <span className="text-zinc-600">{fmtDate}</span>
          </div>
          <div className="flex items-center gap-1.5">
            <Avatar name={(window.Auth && window.Auth.current() && window.Auth.current().name) || 'Producer'} color={window.Auth && window.Auth.current() ? window.Auth.color(window.Auth.current().id) : '#22D48A'} size={26} />
            <button onClick={() => { try { sessionStorage.removeItem('silverlines.boss.session'); } catch (e) {} window.closeProductionStatus(); }}
              title="Lock & exit" className="flex h-8 items-center gap-1.5 rounded-lg border border-line bg-ink-900 px-2.5 font-mono text-[11px] font-700 text-zinc-400 transition-colors hover:border-[#FF4D6D]/40 hover:text-[#FF6B8A]">
              <Icon name="Lock" size={13} /> Lock
            </button>
          </div>
        </div>
      </header>

      {/* ===== KPI ticker strip ===== */}
      <div className="grid grid-cols-2 gap-x-5 gap-y-4 border-b border-line bg-ink-900/40 px-5 py-4 sm:grid-cols-4 lg:grid-cols-7">
        <StatBlock big label="Client fees (gross)" value={psK(T.fee)} sub={ps$(T.fee)} />
        <StatBlock label={`VAT ${(vatRate * 100).toFixed(0)}%`} value={psK(T.vat)} sub={`net ${psK(T.net)}`} />
        <StatBlock label="Spent to date" value={psK(T.spent)} sub={`${pctOf(T.spent, T.net).toFixed(0)}% of net`} />
        <StatBlock label="Committed" value={psK(T.committed)} sub="POs / bookings" />
        <StatBlock label="Proj. final cost" value={psK(T.projFinal)} sub={`${pctOf(T.projFinal, T.net).toFixed(0)}% of net`} />
        <StatBlock big label="Proj. profit" value={psK(T.profit)} tone={portTone} sub={`${portMargin.toFixed(1)}% margin`} />
        <StatBlock big label="Safe to spend" value={psK(T.safe)} tone={tone3(1 - clamp(T.safe, 0, T.net) / Math.max(1, T.net), 0.85, 0.97)} sub={`protect ${(protectPct * 100).toFixed(0)}%`} />
      </div>

      {/* ===== body ===== */}
      <div className="grain relative min-h-0 flex-1 overflow-y-auto">
        <div className="relative z-10 mx-auto max-w-[1240px] space-y-5 px-5 py-5">

          {/* controls */}
          <div className="flex flex-wrap items-center gap-3 rounded-xl border border-line bg-ink-900/60 px-4 py-2.5">
            <span className="font-mono text-[10px] uppercase tracking-[0.14em] text-zinc-500">Owner controls</span>
            <div className="flex items-center gap-2">
              <span className="font-mono text-[11px] text-zinc-400">VAT</span>
              <div className="flex items-center rounded-lg border border-line bg-ink-900 p-0.5">
                {[0, 0.1, 0.2].map(r => (
                  <button key={r} onClick={() => setVatRate(r)} className={cx('rounded px-2 py-1 font-mono text-[11px] font-700 transition-colors', vatRate === r ? 'bg-ink-700 text-head' : 'text-zinc-500 hover:text-zinc-300')}>{(r * 100).toFixed(0)}%</button>
                ))}
              </div>
            </div>
            <div className="flex min-w-[210px] flex-1 items-center gap-2.5">
              <span className="whitespace-nowrap font-mono text-[11px] text-zinc-400">Protected profit</span>
              <input type="range" min="0" max="40" step="1" value={protectPct * 100} onChange={e => setProtectPct(+e.target.value / 100)} className="min-w-0 flex-1 accent-proflow" />
              <span className="w-10 shrink-0 text-right font-mono text-[12px] font-700 text-proflow-soft tabular-nums">{(protectPct * 100).toFixed(0)}%</span>
            </div>
            <span className="ml-auto font-mono text-[10px] text-zinc-600">Headroom = fee − VAT − all production</span>
          </div>

          {/* health gauges for the selected production */}
          <div>
            <div className="mb-2.5 flex items-center gap-2">
              <span className="font-mono text-[10px] uppercase tracking-[0.16em] text-zinc-500">Health status</span>
              <span className="font-display text-[13px] text-zinc-300">{cp.name}</span>
              <span className="font-mono text-[10px] text-zinc-600">{cp.code} · {cp.status}</span>
            </div>
            <div className="grid grid-cols-2 gap-3 lg:grid-cols-4">
              <Gauge icon="Wallet" label="Budget" tone={cd.budgetTone}
                value={`${pctOf(cd.spent + cd.committed, cd.net - cd.protectReserve).toFixed(0)}%`}
                pct={pctOf(cd.spent + cd.committed, cd.net - cd.protectReserve)}
                caption={`${ps$(cd.safeToSpend)} still safe to spend`} />
              <Gauge icon="CalendarClock" label="Days" tone={cd.daysTone}
                value={`${cp.daysUsed}/${cp.daysPlanned}`} pct={pctOf(cp.daysUsed, cp.daysPlanned)}
                caption={cp.daysUsed >= cp.daysPlanned ? 'Shoot complete' : `${cp.daysPlanned - cp.daysUsed} day(s) to go`} />
              <Gauge icon="Activity" label="Work intensity" tone={cd.intensityTone}
                value={`${cp.intensity}`} pct={cp.intensity}
                caption={cp.intensity >= 90 ? 'Overloaded schedule' : cp.intensity >= 70 ? 'Heavy but holding' : 'Comfortable pace'} />
              <Gauge icon="Users" label="Crew workload" tone={cd.crewTone}
                value={`${cp.crewLoad}%`} pct={cp.crewLoad}
                caption={cp.crewLoad >= 90 ? 'Burnout risk' : cp.crewLoad >= 70 ? 'Near capacity' : 'Within limits'} />
            </div>
          </div>

          {/* portfolio P&L table */}
          <div className="overflow-hidden rounded-xl border border-line bg-ink-900/40">
            <div className="flex items-center justify-between border-b border-line px-4 py-2.5">
              <span className="font-mono text-[10px] uppercase tracking-[0.16em] text-zinc-500">Portfolio P&amp;L · all productions</span>
              <span className="font-mono text-[10px] text-zinc-600">click a row to drill in</span>
            </div>
            <div className="overflow-x-auto">
              <table className="w-full min-w-[860px] border-collapse">
                <thead>
                  <tr className="font-mono text-[9.5px] uppercase tracking-[0.1em] text-zinc-500">
                    <th className="px-4 py-2 text-left font-600">Production</th>
                    <th className="px-3 py-2 text-right font-600">Fee</th>
                    <th className="px-3 py-2 text-right font-600">Net</th>
                    <th className="px-3 py-2 text-right font-600">Spent</th>
                    <th className="px-3 py-2 text-right font-600">Committed</th>
                    <th className="px-3 py-2 text-right font-600">Proj. cost</th>
                    <th className="px-3 py-2 text-right font-600">Profit</th>
                    <th className="px-3 py-2 text-right font-600">Margin</th>
                    <th className="px-4 py-2 text-center font-600">Health</th>
                  </tr>
                </thead>
                <tbody>
                  {rows.map(({ p, d }) => {
                    const on = p.code === sel;
                    return (
                      <tr key={p.code} onClick={() => setSel(p.code)}
                        className={cx('cursor-pointer border-t border-line/60 font-mono text-[12px] tabular-nums transition-colors', on ? 'bg-ink-800' : 'hover:bg-ink-900')}>
                        <td className="px-4 py-2.5">
                          <div className="flex items-center gap-2.5">
                            <span className="flex h-7 w-7 shrink-0 items-center justify-center rounded-md font-display text-[10px]" style={{ background: p.color, color: p.fg }}>{p.code.slice(0, 2)}</span>
                            <span className="min-w-0">
                              <span className="block truncate font-sans text-[12.5px] font-600 text-zinc-100">{p.name}</span>
                              <span className="block truncate text-[9.5px] text-zinc-500">{p.status}</span>
                            </span>
                          </div>
                        </td>
                        <td className="px-3 py-2.5 text-right text-zinc-300">{psK(p.fee)}</td>
                        <td className="px-3 py-2.5 text-right text-zinc-400">{psK(d.net)}</td>
                        <td className="px-3 py-2.5 text-right text-zinc-300">{psK(d.spent)}</td>
                        <td className="px-3 py-2.5 text-right text-zinc-400">{psK(d.committed)}</td>
                        <td className="px-3 py-2.5 text-right text-zinc-300">{psK(d.projFinal)}</td>
                        <td className={cx('px-3 py-2.5 text-right font-700', d.profit >= 0 ? 'text-mint' : 'text-[#FF6B8A]')}>{psK(d.profit)}</td>
                        <td className={cx('px-3 py-2.5 text-right', d.margin >= 15 ? 'text-mint' : d.margin >= 0 ? 'text-amber' : 'text-[#FF6B8A]')}>{d.margin.toFixed(1)}%</td>
                        <td className="px-4 py-2.5">
                          <span className="flex items-center justify-center"><span className={cx('h-2.5 w-2.5 rounded-full', TONE[d.budgetTone].b)} title={TONE[d.budgetTone].label}></span></span>
                        </td>
                      </tr>
                    );
                  })}
                </tbody>
                <tfoot>
                  <tr className="border-t border-line bg-ink-900/70 font-mono text-[12px] font-700 tabular-nums text-zinc-200">
                    <td className="px-4 py-2.5 font-sans text-[11px] uppercase tracking-wider text-zinc-500">Portfolio</td>
                    <td className="px-3 py-2.5 text-right">{psK(T.fee)}</td>
                    <td className="px-3 py-2.5 text-right">{psK(T.net)}</td>
                    <td className="px-3 py-2.5 text-right">{psK(T.spent)}</td>
                    <td className="px-3 py-2.5 text-right">{psK(T.committed)}</td>
                    <td className="px-3 py-2.5 text-right">{psK(T.projFinal)}</td>
                    <td className={cx('px-3 py-2.5 text-right', T.profit >= 0 ? 'text-mint' : 'text-[#FF6B8A]')}>{psK(T.profit)}</td>
                    <td className={cx('px-3 py-2.5 text-right', portMargin >= 15 ? 'text-mint' : 'text-amber')}>{portMargin.toFixed(1)}%</td>
                    <td className="px-4 py-2.5"><span className="flex items-center justify-center"><span className={cx('h-2.5 w-2.5 rounded-full', TONE[portTone].b)}></span></span></td>
                  </tr>
                </tfoot>
              </table>
            </div>
          </div>

          {/* drill-in: departments & people + headroom math */}
          <div className="grid grid-cols-1 gap-5 lg:grid-cols-3">
            <DeptOverview p={cp} depFin={cp.dep} />

            {/* headroom / income waterfall */}
            <div className="rounded-xl border border-line bg-ink-900/40">
              <div className="border-b border-line px-4 py-2.5 font-mono text-[10px] uppercase tracking-[0.16em] text-zinc-500">Income → profit · {cp.code}</div>
              <div className="space-y-1 p-4 font-mono text-[12px]">
                {[
                  ['Client fee (gross)', cp.fee, 'text-zinc-200', false],
                  [`VAT ${(vatRate * 100).toFixed(0)}%`, -cd.vat, 'text-zinc-400', false],
                  ['Net revenue', cd.net, 'text-zinc-100 font-700', true],
                  ['Spent to date', -cd.spent, 'text-zinc-400', false],
                  ['Committed', -cd.committed, 'text-zinc-400', false],
                  ['Est. to complete', -(cd.projFinal - cd.spent - cd.committed), 'text-zinc-400', false],
                ].map(([l, v, c, rule], i) => (
                  <div key={i} className={cx('flex items-center justify-between py-1', rule && 'border-t border-line/60')}>
                    <span className={cx('text-[11px]', c)}>{l}</span>
                    <span className={cx('tabular-nums', c)}>{v < 0 ? '−' : ''}{psK(Math.abs(v))}</span>
                  </div>
                ))}
                <div className="mt-1 flex items-center justify-between border-t border-line pt-2.5">
                  <span className="text-[11px] font-700 text-head">Projected profit</span>
                  <span className={cx('text-[16px] font-700 tabular-nums', cd.profit >= 0 ? 'text-mint' : 'text-[#FF6B8A]')}>{psK(cd.profit)}</span>
                </div>
                <div className="flex items-center justify-between">
                  <span className="text-[10px] text-zinc-500">Margin on net</span>
                  <span className={cx('text-[11px] tabular-nums', cd.margin >= 15 ? 'text-mint' : 'text-amber')}>{cd.margin.toFixed(1)}%</span>
                </div>
              </div>
              <div className="mx-4 mb-4 rounded-xl border border-proflow/25 bg-proflow/8 p-3">
                <div className="flex items-center gap-1.5 font-mono text-[9.5px] uppercase tracking-[0.12em] text-proflow-soft"><Icon name="Gauge" size={12} />Safe to spend today</div>
                <div className={cx('mt-1 font-mono text-[22px] font-700 tabular-nums', cd.safeToSpend >= 0 ? 'text-head' : 'text-[#FF6B8A]')}>{ps$(cd.safeToSpend)}</div>
                <div className="mt-1 font-mono text-[10px] text-zinc-500">After protecting {(protectPct * 100).toFixed(0)}% margin · burn {ps$(burn)}/day</div>
              </div>
            </div>
          </div>

          <p className="pb-2 text-center font-mono text-[10px] text-zinc-600">Confidential — owner &amp; designated finance only. These figures are not shared with crew, clients or invited collaborators.</p>
        </div>
      </div>
    </div>
  );
}

/* ============================================================
   ROOT — overlay + gate, mounted once in the App
   ============================================================ */
function ProductionStatusRoot() {
  const open = usePSOpen();
  const [unlocked, setUnlocked] = useState(() => { try { return sessionStorage.getItem('silverlines.boss.session') === '1'; } catch (e) { return false; } });
  useEffect(() => {
    const onKey = e => { if (e.key === 'Escape' && psOpen) window.closeProductionStatus(); };
    window.addEventListener('keydown', onKey); return () => window.removeEventListener('keydown', onKey);
  }, []);
  if (!open) return null;
  return (
    <div className="fixed inset-0 z-[80] bg-ink-950">
      <div className="ps-fade h-full w-full overflow-hidden">
        {unlocked
          ? <StatusTerminal />
          : <div className="h-full overflow-y-auto"><BossLogin onUnlock={() => setUnlocked(true)} /></div>}
      </div>
    </div>
  );
}
window.ProductionStatusRoot = ProductionStatusRoot;

/* tiny styles (shake + fade) */
(function () {
  if (document.getElementById('ps-styles')) return;
  const s = document.createElement('style'); s.id = 'ps-styles';
  s.textContent = '@keyframes psShake{10%,90%{transform:translateX(-2px)}20%,80%{transform:translateX(4px)}30%,50%,70%{transform:translateX(-7px)}40%,60%{transform:translateX(7px)}}.ps-shake{animation:psShake .42s cubic-bezier(.36,.07,.19,.97) both}@keyframes psFade{from{opacity:0;transform:scale(.99)}to{opacity:1;transform:none}}.ps-fade{animation:psFade .22s ease both}';
  document.head.appendChild(s);
})();
