// MaxAMove — On-Job Mobile View
// Phone-first interface for the lead/owner running a job today.
// Features:
//   • Live clock & elapsed timer (persisted across reloads)
//   • Stops checklist: Depart → Arrive Pickup → Depart Pickup → Arrive Dropoff → Complete
//   • NTE (not-to-exceed) price tracking with hard-stop + override + reason
//   • Expense capture with receipt photo (auto-tagged "receipt")
//   • Damage / condition photos (tagged)
//   • One-tap call/text customer + crew
//   • Complete job → auto-invoice + move lead to "completed"

const { useState, useEffect, useRef } = React;

// ─── PERSISTED ON-JOB STATE ───
// Shape: { [jobId]: {
//   started, arrivedPickup, departedPickup, arrivedDropoff, completed,  // ISO timestamps or null
//   hourlyRate, crewSize,          // editable rate for calc
//   notes,                          // free text
//   expenses: [{id, label, amount, ts, photoId?}],
//   nteOverride: {approved, reason, by, ts} | null,
//   damageReported: boolean,
//   signature: dataUrl | null,
// } }
function getJobStates() {
  try { return JSON.parse(localStorage.getItem('crm_job_states')) || {}; } catch { return {}; }
}
function getJobState(jobId) {
  const all = getJobStates();
  return all[jobId] || null;
}
function saveJobState(jobId, state) {
  const all = getJobStates();
  all[jobId] = state;
  localStorage.setItem('crm_job_states', JSON.stringify(all));
}

// Derive an hourly rate from job value (fallback to $150/hr)
function deriveHourlyRate(job) {
  if (!job) return 150;
  // Assume ~6 hrs quoted for a 2br, scale
  const sizeHours = { studio: 3, '1br': 4, '2br': 6, '3br': 8, '4br': 10, office: 7 };
  const hrs = sizeHours[job.size] || 6;
  return Math.round((job.value || 900) / hrs / 25) * 25; // round to nearest $25
}

// Format elapsed ms as "2h 14m" or "14m"
function fmtElapsed(ms) {
  if (!ms || ms < 0) return '0m';
  const mins = Math.floor(ms / 60000);
  const h = Math.floor(mins / 60);
  const m = mins % 60;
  if (h > 0) return `${h}h ${m}m`;
  return `${m}m`;
}

// Format clock (hh:mm)
function fmtClock(d) {
  const h = d.getHours();
  const m = String(d.getMinutes()).padStart(2, '0');
  const ampm = h >= 12 ? 'PM' : 'AM';
  const h12 = h % 12 || 12;
  return `${h12}:${m} ${ampm}`;
}

// ─── MAIN VIEW ───
function OnJobView({ jobs, onJobsChange, onNavigate }) {
  // Pick the job currently "in progress" — earliest confirmed/pending today,
  // or explicitly selected via localStorage
  const [selectedJobId, setSelectedJobId] = useState(() => localStorage.getItem('crm_onjob_selected'));
  useEffect(() => {
    if (selectedJobId) localStorage.setItem('crm_onjob_selected', selectedJobId);
    else localStorage.removeItem('crm_onjob_selected');
  }, [selectedJobId]);

  const activeJobs = jobs.filter(j => j.status !== 'completed');
  const job = jobs.find(j => j.id === selectedJobId) || null;

  if (!job) {
    return <JobPicker jobs={activeJobs} onPick={setSelectedJobId} />;
  }

  return (
    <JobConsole
      job={job}
      onExit={() => setSelectedJobId(null)}
      onComplete={async (timing) => {
        const actualBillableHours = timing && timing.actualBillableMinutes != null
          ? +(timing.actualBillableMinutes / 60).toFixed(2) : null;
        const actualTotalHours = timing && timing.actualTotalMinutes != null
          ? +(timing.actualTotalMinutes / 60).toFixed(2) : null;
        const tipTotal = timing && timing.tipTotal != null ? Number(timing.tipTotal) : 0;
        const completedAt = new Date().toISOString();
        // Optimistic local update
        onJobsChange(jobs.map(j => j.id === job.id
          ? { ...j, status: 'completed', actualBillableHours, actualTotalHours, completedAt, tipTotal }
          : j));
        // Persist job to Supabase: status + accuracy timing + tip into job.extra
        // The tip flows through to each crew member's worker-portal earnings,
        // split evenly across crew_ids on read.
        if (typeof window.updateJob === 'function') {
          try {
            await window.updateJob(job.id, {
              status: 'completed',
              actualBillableHours,
              actualTotalHours,
              completedAt,
              tipTotal,
            });
          } catch (e) {
            console.warn('[on-job] Failed to persist job completion:', e);
          }
        }
        // Mirror actualBillableHours into the linked estimate's pricing JSON so the
        // accuracy dashboard can compare (system estimate ↔ manual quote ↔ actual) per estimate,
        // AND distribute lbs across assigned crew to update each member's learned rate.
        if (job.estimateUuid && actualBillableHours != null && actualBillableHours > 0
            && typeof window.sb !== 'undefined') {
          try {
            const { data: estRow } = await window.sb.from('estimates')
              .select('pricing, inventory').eq('id', job.estimateUuid).single();
            // Merge actuals into pricing JSON (don't clobber existing fields)
            const mergedPricing = {
              ...(estRow?.pricing || {}),
              actualBillableHours,
              actualTotalHours,
              actualCompletedAt: completedAt,
            };
            await window.sb.from('estimates')
              .update({ pricing: mergedPricing })
              .eq('id', job.estimateUuid);
            // Crew learning: each assigned mover gets credit for an even share of the lbs.
            if (Array.isArray(job.crewIds) && job.crewIds.length > 0
                && typeof window.recordCrewJobPerformance === 'function') {
              const inv = (estRow && estRow.inventory) || [];
              const totalLbs = inv.reduce((s, it) =>
                s + ((window.getItemWeight ? window.getItemWeight(it) : 0) * (it.qty || 1)), 0);
              if (totalLbs > 0) {
                const lbsPerMover = totalLbs / job.crewIds.length;
                await Promise.all(job.crewIds.map(id =>
                  window.recordCrewJobPerformance(id, lbsPerMover, actualBillableHours)
                ));
              }
            }
          } catch (e) {
            console.warn('[on-job] Failed to mirror actuals / update crew learning:', e);
          }
        }
        if (typeof createInvoiceFromJob === 'function') {
          createInvoiceFromJob({ ...job, status: 'completed' });
        }
      }}
      onNavigate={onNavigate}
    />
  );
}

// ─── JOB PICKER (shown when no job is active) ───
function JobPicker({ jobs, onPick }) {
  // Sort by date ascending
  const sorted = [...jobs].sort((a, b) => new Date(a.date) - new Date(b.date));
  const today = new Date().toISOString().split('T')[0];
  const todayJobs = sorted.filter(j => j.date === today);
  const upcoming = sorted.filter(j => j.date !== today);

  return (
    <div style={{ maxWidth: 560, margin: '0 auto' }}>
      <div style={{ marginBottom: 24 }}>
        <h1 style={{ fontSize: 24, fontWeight: 900, fontFamily: 'Poppins', marginBottom: 4 }}>On-Job</h1>
        <p style={{ color: 'var(--muted)', fontSize: 14 }}>Pick a job to open the field console. Clock, stops, expenses, photos — everything you need on-site.</p>
      </div>

      {todayJobs.length > 0 && (
        <div style={{ marginBottom: 24 }}>
          <div style={{ fontSize: 11, fontWeight: 800, color: 'var(--accent)', textTransform: 'uppercase', letterSpacing: '.1em', marginBottom: 10 }}>Today · {todayJobs.length} scheduled</div>
          {todayJobs.map(j => <JobPickCard key={j.id} job={j} onPick={onPick} active />)}
        </div>
      )}

      <div>
        <div style={{ fontSize: 11, fontWeight: 800, color: 'var(--muted)', textTransform: 'uppercase', letterSpacing: '.1em', marginBottom: 10 }}>Upcoming</div>
        {upcoming.length === 0 && (
          <div style={{ padding: 30, textAlign: 'center', background: 'var(--bg)', borderRadius: 12, color: 'var(--muted)', fontSize: 13 }}>No upcoming jobs</div>
        )}
        {upcoming.slice(0, 6).map(j => <JobPickCard key={j.id} job={j} onPick={onPick} />)}
      </div>
    </div>
  );
}

function JobPickCard({ job, onPick, active }) {
  const state = getJobState(job.id);
  const inProgress = state && state.started && !state.completed;
  return (
    <div onClick={() => onPick(job.id)}
      style={{
        background: 'var(--card)', borderRadius: 14, padding: 16, marginBottom: 10,
        border: `1.5px solid ${active ? 'var(--accent)' : 'var(--border)'}`,
        cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 14,
        boxShadow: active ? '0 4px 16px rgba(20,198,191,.12)' : 'none',
      }}>
      <div style={{
        width: 44, height: 44, borderRadius: 10,
        background: inProgress ? 'linear-gradient(135deg,#f97316,#ef4444)' : active ? 'var(--accent-grad, linear-gradient(135deg,#14C6BF,#3DCC7E))' : 'var(--bg)',
        color: inProgress || active ? '#fff' : 'var(--muted)',
        display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 20, flexShrink: 0,
      }}><Icon name={inProgress ? 'clock' : 'truck'} size={22} color={inProgress || active ? '#fff' : 'var(--muted)'}/></div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 8 }}>
          <div style={{ fontWeight: 800, fontSize: 15, fontFamily: 'Poppins' }}>{job.customer}</div>
          <div style={{ fontSize: 12, color: 'var(--muted)', fontWeight: 600, whiteSpace: 'nowrap' }}>{job.time}</div>
        </div>
        <div style={{ fontSize: 12, color: 'var(--muted)', marginTop: 2, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{job.from} → {job.to}</div>
        <div style={{ display: 'flex', gap: 6, marginTop: 6, flexWrap: 'wrap' }}>
          <span style={{ fontSize: 10, background: 'var(--accent-light)', color: 'var(--accent-dark)', padding: '2px 7px', borderRadius: 4, fontWeight: 700 }}>{job.size.toUpperCase()}</span>
          <span style={{ fontSize: 10, background: 'var(--bg)', color: 'var(--muted)', padding: '2px 7px', borderRadius: 4, fontWeight: 700 }}>{job.crew.length} crew</span>
          <span style={{ fontSize: 10, background: '#f0fdfb', color: 'var(--accent-dark)', padding: '2px 7px', borderRadius: 4, fontWeight: 800 }}>${job.value.toLocaleString()}</span>
          {inProgress && <span style={{ fontSize: 10, background: '#fef3c7', color: '#b45309', padding: '2px 7px', borderRadius: 4, fontWeight: 800 }}>IN PROGRESS</span>}
        </div>
      </div>
      <div style={{ color: 'var(--accent)', fontSize: 18, fontWeight: 700 }}>→</div>
    </div>
  );
}

// ─── JOB CONSOLE (the main in-job view) ───
function JobConsole({ job, onExit, onComplete, onNavigate }) {
  const hourlyRate = deriveHourlyRate(job);
  const initialState = {
    started: null, arrivedPickup: null, departedPickup: null,
    arrivedDropoff: null, completed: null,
    hourlyRate, crewSize: (job.crew || []).length,
    notes: '',
    expenses: [],
    nteOverride: null,
    damageReported: false,
    signature: null,
    pauses: [],                    // [{ start: ISO, end: ISO|null, reason: 'lunch'|'break'|'other' }]
    billableMinutesOverride: null, // when set at end-of-job, this is the billed time (minutes)
  };

  const [state, setState] = useState(() => {
    const saved = getJobState(job.id);
    if (saved) return { ...initialState, ...saved, pauses: saved.pauses || [], billableMinutesOverride: saved.billableMinutesOverride ?? null };
    return initialState;
  });
  const [tick, setTick] = useState(0);
  const [showExpense, setShowExpense] = useState(false);
  const [showNote, setShowNote] = useState(false);
  const [showOverride, setShowOverride] = useState(false);
  const [showComplete, setShowComplete] = useState(false);
  const [showPhotos, setShowPhotos] = useState(false);
  const [showPauseReason, setShowPauseReason] = useState(false);

  // Persist
  useEffect(() => { saveJobState(job.id, state); }, [state, job.id]);

  // Re-tick every 5s for the live timer (slightly faster so paused state feels responsive)
  useEffect(() => {
    const iv = setInterval(() => setTick(t => t + 1), 5000);
    return () => clearInterval(iv);
  }, []);

  // ── Push clock state to Supabase so the worker portal's live earnings ticker
  // can sync in real time. Fires on start, pause-begin, pause-end, complete, and
  // billable-time override. The shape below matches what worker.html expects.
  useEffect(() => {
    if (typeof window.updateJob !== 'function') return;
    if (!state.started && !state.completed) return;
    const lastPause = (state.pauses || [])[ (state.pauses || []).length - 1 ];
    const openPause = lastPause && !lastPause.end;
    const pausedTotalMs = (state.pauses || []).reduce((sum, p) => {
      if (!p.end) return sum;
      return sum + Math.max(0, new Date(p.end).getTime() - new Date(p.start).getTime());
    }, 0);
    const clockState = {
      startedAt: state.started || null,
      isPaused: !!openPause,
      pausedAt: openPause ? lastPause.start : null,
      pausedTotalMs,
      completedAt: state.completed || null,
      billableMinutesOverride: state.billableMinutesOverride ?? null,
    };
    window.updateJob(job.id, { clockState })
      .catch(e => console.warn('[on-job] clock state persist failed:', e));
  }, [job.id, state.started, state.completed, state.pauses, state.billableMinutesOverride]);

  // ── Derived values ──
  const now = Date.now();
  const isPaused = (state.pauses || []).some(p => !p.end);

  // Total elapsed = wall-clock time from start → (completed | now), regardless of pauses
  const totalElapsedRaw = state.started
    ? (state.completed
        ? new Date(state.completed).getTime() - new Date(state.started).getTime()
        : now - new Date(state.started).getTime())
    : 0;
  const totalElapsed = Math.max(0, totalElapsedRaw);

  // Sum of pause durations (open pauses count up to now or completed)
  const pausedMs = (state.pauses || []).reduce((sum, p) => {
    const startMs = new Date(p.start).getTime();
    const endMs = p.end
      ? new Date(p.end).getTime()
      : (state.completed ? new Date(state.completed).getTime() : now);
    return sum + Math.max(0, endMs - startMs);
  }, 0);

  // Billable elapsed = total minus paused (unless owner manually overrode at completion)
  const billableElapsedComputed = Math.max(0, totalElapsed - pausedMs);
  const billableElapsed = state.billableMinutesOverride != null
    ? state.billableMinutesOverride * 60000
    : billableElapsedComputed;

  const hours = billableElapsed / 3600000;
  const laborCost = Math.round(hours * state.hourlyRate);
  const expenseTotal = state.expenses.reduce((s, e) => s + e.amount, 0);
  const runningTotal = laborCost + expenseTotal;
  const nte = job.value;
  const nteProgress = Math.min(100, (runningTotal / nte) * 100);
  const nteHit = runningTotal >= nte && !state.nteOverride?.approved;
  const overNte = runningTotal > nte;
  const stepIdx =
    state.completed ? 5 :
    state.arrivedDropoff ? 4 :
    state.departedPickup ? 3 :
    state.arrivedPickup ? 2 :
    state.started ? 1 : 0;

  // ── Actions ──
  const stamp = (key) => setState(s => ({ ...s, [key]: new Date().toISOString() }));

  const startJob = () => {
    if (state.started) return;
    setState(s => ({ ...s, started: new Date().toISOString() }));
    if (typeof showToast === 'function') showToast('⏱️ Clock started — safe travels');
  };

  const pauseClock = (reason = 'Other') => {
    if (!state.started || state.completed || isPaused) return;
    setState(s => ({ ...s, pauses: [...(s.pauses || []), { start: new Date().toISOString(), end: null, reason }] }));
    if (typeof showToast === 'function') showToast(`⏸ Clock paused (${reason}) — total clock keeps running`);
  };

  const resumeClock = () => {
    if (!isPaused) return;
    setState(s => ({
      ...s,
      pauses: (s.pauses || []).map((p, i, arr) => (i === arr.length - 1 && !p.end) ? { ...p, end: new Date().toISOString() } : p),
    }));
    if (typeof showToast === 'function') showToast('▶ Clock resumed');
  };

  const setBillableOverride = (minutesOrNull) => {
    setState(s => ({ ...s, billableMinutesOverride: minutesOrNull }));
  };

  const addExpense = (exp) => {
    setState(s => ({ ...s, expenses: [{ id: 'e' + Date.now(), ts: new Date().toISOString(), ...exp }, ...s.expenses] }));
    if (typeof showToast === 'function') showToast(`Added $${exp.amount} — ${exp.label}`);
  };

  const removeExpense = (id) => {
    setState(s => ({ ...s, expenses: s.expenses.filter(e => e.id !== id) }));
  };

  const approveOverride = (reason) => {
    setState(s => ({ ...s, nteOverride: { approved: true, reason, by: 'Owner', ts: new Date().toISOString() } }));
    if (typeof showToast === 'function') showToast('NTE override logged — customer will see this on invoice');
  };

  const finishJob = (tipTotal = 0) => {
    // Final billable minutes: user override (set in CompleteModal) takes priority,
    // else use the accumulated billable elapsed time from the on-job clock.
    const finalBillableMinutes = state.billableMinutesOverride != null
      ? state.billableMinutesOverride
      : Math.round(billableElapsed / 60000);
    const finalTotalMinutes = Math.round(totalElapsed / 60000);
    setState(s => ({ ...s, completed: new Date().toISOString() }));
    if (typeof showToast === 'function') showToast('Job complete — invoice auto-generated');
    // Pass the final times + tip up so the parent can persist them for accuracy
    // tracking AND so each crew member's worker portal earnings reflect tip share.
    onComplete({
      actualBillableMinutes: finalBillableMinutes,
      actualTotalMinutes: finalTotalMinutes,
      tipTotal: Number(tipTotal) || 0,
    });
    setShowComplete(false);
  };

  const resetJob = () => {
    if (!confirm('Reset all on-job data for this job? This clears the clock, expenses and notes.')) return;
    setState(initialState);
  };

  // ─── RENDER ───
  return (
    <div style={{ maxWidth: 560, margin: '0 auto', paddingBottom: 120 }}>
      {/* Sticky top: job chrome */}
      <div style={{ position: 'sticky', top: 0, zIndex: 10, background: 'linear-gradient(180deg,var(--bg) 85%,transparent)', paddingBottom: 8, marginBottom: 4 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12 }}>
          <button onClick={onExit} style={{ padding: '7px 12px', borderRadius: 9, border: '1.5px solid var(--border)', background: 'var(--card)', cursor: 'pointer', fontFamily: 'inherit', fontWeight: 700, fontSize: 13 }}>← Jobs</button>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontSize: 10, color: 'var(--muted)', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '.08em' }}>{job.id} · {job.size.toUpperCase()}</div>
            <div style={{ fontWeight: 900, fontSize: 18, fontFamily: 'Poppins', lineHeight: 1.1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{job.customer}</div>
          </div>
          {state.completed && (
            <div style={{ padding: '6px 11px', background: '#f0fdf4', color: '#16a34a', borderRadius: 20, fontSize: 11, fontWeight: 800, display: 'inline-flex', alignItems: 'center', gap: 4 }}><Icon name="check" size={12}/> DONE</div>
          )}
        </div>
      </div>

      {/* Timer + NTE band */}
      <TimerBand
        state={state} elapsed={totalElapsed} billableElapsed={billableElapsed}
        pausedMs={pausedMs} isPaused={isPaused}
        hourlyRate={state.hourlyRate}
        laborCost={laborCost} expenseTotal={expenseTotal} runningTotal={runningTotal}
        nte={nte} nteProgress={nteProgress} nteHit={nteHit} overNte={overNte}
        onStart={startJob}
        onPause={() => setShowPauseReason(true)}
        onResume={resumeClock}
        onOverride={() => setShowOverride(true)}
      />

      {/* Quick actions row */}
      <QuickActions job={job} onPhotos={() => setShowPhotos(true)} onNote={() => setShowNote(true)} onExpense={() => setShowExpense(true)} />

      {/* Stops checklist */}
      <StopsCard job={job} state={state} stepIdx={stepIdx} stamp={stamp} />

      {/* Expenses list */}
      <ExpensesCard expenses={state.expenses} total={expenseTotal} onAdd={() => setShowExpense(true)} onRemove={removeExpense} />

      {/* Notes */}
      <NotesCard notes={state.notes} onEdit={() => setShowNote(true)} />

      {/* Verse of the day — quiet motivational anchor at the bottom of every job.
          Same verse as Dashboard + worker portal (shared in components/verse.jsx).
          Bottom margin clears the fixed footer (Start/Complete + ⋯ buttons). */}
      <VerseOfTheDay style={{ marginTop: 12, marginBottom: 110 }} />

      {/* Footer controls */}
      <div style={{ position: 'fixed', bottom: 0, left: 0, right: 0, padding: '12px 16px calc(12px + env(safe-area-inset-bottom,0px))', background: 'linear-gradient(180deg,transparent,var(--card) 20%)', zIndex: 20 }}>
        <div style={{ maxWidth: 528, margin: '0 auto', display: 'flex', gap: 10 }}>
          {!state.completed ? (
            <button
              onClick={() => state.started ? setShowComplete(true) : startJob()}
              disabled={state.started && stepIdx < 4}
              style={{
                flex: 1, padding: '15px', borderRadius: 12, border: 'none',
                background: !state.started ? 'linear-gradient(135deg,#14C6BF,#3DCC7E)' :
                  stepIdx < 4 ? 'var(--bg)' : 'linear-gradient(135deg,#16a34a,#22c55e)',
                color: !state.started || stepIdx >= 4 ? '#fff' : 'var(--muted)',
                fontWeight: 900, fontFamily: 'inherit', fontSize: 15, cursor: state.started && stepIdx < 4 ? 'not-allowed' : 'pointer',
                boxShadow: !state.started || stepIdx >= 4 ? '0 6px 20px rgba(20,198,191,.3)' : 'none',
                letterSpacing: '.02em'
              }}>
              <span style={{display:'inline-flex',alignItems:'center',gap:6,justifyContent:'center'}}>
                {!state.started ? <><Icon name="play" size={14} color="#fff"/> Start Job</> : stepIdx < 4 ? `Next: ${STEP_LABELS[stepIdx]}` : <><Icon name="check" size={14} color="#fff"/> Complete & Invoice</>}
              </span>
            </button>
          ) : (
            <button onClick={resetJob} style={{ flex: 1, padding: '14px', borderRadius: 12, border: '1.5px solid var(--border)', background: 'var(--card)', color: 'var(--muted)', fontWeight: 700, fontFamily: 'inherit', fontSize: 14, cursor: 'pointer' }}>Reset Job Data</button>
          )}
          <button onClick={onExit}
            style={{ padding: '14px 18px', borderRadius: 12, border: '1.5px solid var(--border)', background: 'var(--card)', color: 'var(--text)', fontWeight: 700, fontFamily: 'inherit', fontSize: 14, cursor: 'pointer' }}>
            ⋯
          </button>
        </div>
      </div>

      {/* Modals */}
      {showExpense && <ExpenseModal onClose={() => setShowExpense(false)} onAdd={addExpense} />}
      {showNote && <NoteModal current={state.notes} onClose={() => setShowNote(false)} onSave={(v) => { setState(s => ({ ...s, notes: v })); setShowNote(false); }} />}
      {showOverride && <OverrideModal nte={nte} running={runningTotal} onClose={() => setShowOverride(false)} onApprove={approveOverride} />}
      {showComplete && <CompleteModal
          job={job} state={state}
          hours={hours} laborCost={laborCost} expenseTotal={expenseTotal} runningTotal={runningTotal}
          totalElapsed={totalElapsed} billableElapsed={billableElapsed} pausedMs={pausedMs}
          billableMinutesOverride={state.billableMinutesOverride}
          onSetBillableOverride={setBillableOverride}
          nte={nte} overNte={overNte} hasOverride={!!state.nteOverride?.approved}
          onClose={() => setShowComplete(false)} onConfirm={finishJob} />}
      {showPhotos && <PhotosModal jobId={job.id} onClose={() => setShowPhotos(false)} />}
      {showPauseReason && <PauseReasonModal onClose={() => setShowPauseReason(false)} onPick={(reason) => { pauseClock(reason); setShowPauseReason(false); }} />}
    </div>
  );
}

// Pause-reason picker
function PauseReasonModal({ onClose, onPick }) {
  const reasons = [
    { id: 'Lunch',     label: 'Lunch break',           icon: 'sandwich' },
    { id: 'Break',     label: 'Coffee / short break',  icon: 'coffee' },
    { id: 'Customer',  label: 'Waiting on customer',   icon: 'hourglass' },
    { id: 'Travel',    label: 'Non-billable travel',   icon: 'car' },
    { id: 'Equipment', label: 'Equipment issue',       icon: 'wrench' },
    { id: 'Other',     label: 'Other',                 icon: 'pause' },
  ];
  return (
    <div onClick={onClose} style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,.5)', zIndex: 200, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 20 }}>
      <div onClick={e => e.stopPropagation()} style={{ background: '#fff', borderRadius: 16, padding: 24, maxWidth: 420, width: '100%' }}>
        <div style={{ fontWeight: 800, fontSize: 16, fontFamily: 'Poppins', marginBottom: 14 }}>Pause clock — why?</div>
        <div style={{ fontSize: 12, color: 'var(--muted)', marginBottom: 14, lineHeight: 1.5 }}>
          The <b>billable</b> clock will pause now. The <b>total</b> clock keeps running so you can still see (and pay) full job time.
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
          {reasons.map(r => (
            <button key={r.id} onClick={() => onPick(r.id)}
              style={{ padding: '14px 12px', borderRadius: 10, border: '1.5px solid var(--border)', background: 'var(--card)', cursor: 'pointer', fontFamily: 'inherit', textAlign: 'left' }}>
              <Icon name={r.icon} size={22} color="var(--accent)"/>
              <div style={{ fontWeight: 700, fontSize: 13, marginTop: 4 }}>{r.label}</div>
            </button>
          ))}
        </div>
        <div style={{ marginTop: 14, textAlign: 'right' }}>
          <button onClick={onClose} style={{ padding: '9px 16px', borderRadius: 9, border: '1.5px solid var(--border)', background: 'transparent', cursor: 'pointer', fontFamily: 'inherit', fontWeight: 600, fontSize: 13 }}>Cancel</button>
        </div>
      </div>
    </div>
  );
}

const STEP_LABELS = ['En Route', 'Arrive Pickup', 'Depart Pickup', 'Arrive Dropoff', 'Complete'];

// ─── TIMER / NTE BAND ───
function TimerBand({ state, elapsed, billableElapsed, pausedMs, isPaused, hourlyRate, laborCost, expenseTotal, runningTotal, nte, nteProgress, nteHit, overNte, onStart, onPause, onResume, onOverride }) {
  const running = state.started && !state.completed;
  const hasPauses = pausedMs > 0;
  const billable = billableElapsed != null ? billableElapsed : elapsed;
  return (
    <div style={{
      background: 'var(--card)', borderRadius: 16, padding: 18, marginBottom: 12,
      border: `1.5px solid ${isPaused ? '#f59e0b' : overNte ? '#ef4444' : nteHit ? '#f59e0b' : 'var(--border)'}`,
      boxShadow: '0 2px 12px rgba(0,0,0,0.04)',
    }}>
      {/* Clock */}
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 14, gap: 12 }}>
        <div style={{ flex: 1 }}>
          <div style={{ fontSize: 10, color: 'var(--muted)', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '.08em' }}>
            {isPaused ? 'Billable (paused)' : 'Billable'}
          </div>
          <div style={{ fontSize: 34, fontWeight: 900, fontFamily: 'Poppins', color: isPaused ? '#f59e0b' : (running ? 'var(--accent)' : 'var(--text)'), letterSpacing: '-.02em', lineHeight: 1 }}>
            {fmtElapsed(billable)}
            {running && !isPaused && <span style={{ display: 'inline-block', width: 10, height: 10, background: '#ef4444', borderRadius: '50%', marginLeft: 10, verticalAlign: 'middle', animation: 'pulse 1.5s infinite' }} />}
            {isPaused && <span style={{ marginLeft: 10, fontSize: 14, color: '#f59e0b' }}>⏸</span>}
          </div>
          {hasPauses ? (
            <div style={{ fontSize: 11, color: 'var(--muted)', marginTop: 3 }}>
              Total <b style={{ color: 'var(--text)' }}>{fmtElapsed(elapsed)}</b> · Paused {fmtElapsed(pausedMs)} · ${hourlyRate}/hr
            </div>
          ) : (
            <div style={{ fontSize: 11, color: 'var(--muted)', marginTop: 3 }}>
              {state.started ? <>Started {new Date(state.started).toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })} · ${hourlyRate}/hr</> : 'Not started'}
            </div>
          )}
        </div>
        <div style={{ textAlign: 'right' }}>
          <div style={{ fontSize: 10, color: 'var(--muted)', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '.08em' }}>Running Total</div>
          <div style={{ fontSize: 22, fontWeight: 900, color: overNte ? '#ef4444' : 'var(--accent-dark)', fontFamily: 'Poppins', lineHeight: 1 }}>${runningTotal.toLocaleString()}</div>
          <div style={{ fontSize: 10, color: 'var(--muted)', marginTop: 3 }}>Labor ${laborCost} + Exp ${expenseTotal}</div>
        </div>
      </div>

      {/* Pause / Resume button */}
      {running && (
        <button onClick={isPaused ? onResume : onPause}
          style={{ width: '100%', padding: '11px', borderRadius: 10, border: 'none', marginBottom: 10,
            background: isPaused ? 'linear-gradient(135deg,#14C6BF,#3DCC7E)' : '#fff7ed',
            color: isPaused ? '#fff' : '#9a3412',
            fontWeight: 800, fontFamily: 'inherit', fontSize: 13, cursor: 'pointer',
            border: isPaused ? 'none' : '1.5px solid #fed7aa' }}>
          {isPaused ? '▶ Resume billable clock' : '⏸ Pause billable clock'}
        </button>
      )}

      {/* NTE bar */}
      <div>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 6 }}>
          <div style={{ fontSize: 11, fontWeight: 800, color: overNte ? '#ef4444' : nteHit ? '#b45309' : 'var(--muted)', textTransform: 'uppercase', letterSpacing: '.08em', display: 'inline-flex', alignItems: 'center', gap: 5 }}>
            {(overNte || nteHit) && <Icon name="alert" size={12}/>}
            <span>{overNte ? 'Over Not-to-Exceed' : nteHit ? 'Hit Not-to-Exceed Cap' : 'Not-to-Exceed'}</span>
          </div>
          <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--muted)' }}>${runningTotal.toLocaleString()} / ${nte.toLocaleString()}</div>
        </div>
        <div style={{ height: 8, background: 'var(--bg)', borderRadius: 10, overflow: 'hidden' }}>
          <div style={{
            width: `${nteProgress}%`, height: '100%',
            background: overNte ? 'linear-gradient(90deg,#ef4444,#f97316)' : nteHit ? 'linear-gradient(90deg,#f59e0b,#f97316)' : 'linear-gradient(90deg,#14C6BF,#3DCC7E)',
            transition: 'width .3s',
          }} />
        </div>
        {state.nteOverride?.approved && (
          <div style={{ marginTop: 10, padding: '8px 12px', background: '#fef3c7', borderRadius: 8, fontSize: 11, color: '#b45309', fontWeight: 600, display: 'flex', alignItems: 'center', gap: 8 }}>
            <Icon name="alert" size={13}/>
            <div style={{ flex: 1 }}>
              <b>NTE override approved:</b> {state.nteOverride.reason}
            </div>
          </div>
        )}
        {nteHit && !state.nteOverride?.approved && (
          <button onClick={onOverride} style={{ marginTop: 10, width: '100%', padding: '10px', borderRadius: 9, border: 'none', background: 'linear-gradient(135deg,#f59e0b,#ef4444)', color: '#fff', fontWeight: 800, fontFamily: 'inherit', fontSize: 13, cursor: 'pointer', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 6 }}>
            <Icon name="alert" size={14} color="#fff"/> Request Override (customer sign-off)
          </button>
        )}
      </div>

      {/* Start button inline if not started */}
      {!state.started && (
        <button onClick={onStart} style={{ marginTop: 14, width: '100%', padding: '13px', borderRadius: 10, border: 'none', background: 'linear-gradient(135deg,#14C6BF,#3DCC7E)', color: '#fff', fontWeight: 900, fontFamily: 'inherit', fontSize: 14, cursor: 'pointer' }}>
          ▶ Start Clock
        </button>
      )}

      <style>{`@keyframes pulse{0%,100%{opacity:1}50%{opacity:.3}}`}</style>
    </div>
  );
}

// ─── QUICK ACTIONS (phone/text/photo/note/expense) ───
function QuickActions({ job, onPhotos, onNote, onExpense }) {
  const tel = 'tel:' + job.phone.replace(/\D/g, '');
  const sms = 'sms:' + job.phone.replace(/\D/g, '');
  const nav = (() => {
    const q = encodeURIComponent(`${job.from} to ${job.to}`);
    // iOS-friendly Apple/Google maps
    return `https://www.google.com/maps/dir/?api=1&origin=${encodeURIComponent(job.from)}&destination=${encodeURIComponent(job.to)}`;
  })();
  const items = [
    { icon: 'phone', label: 'Call', onClick: () => window.location.href = tel },
    { icon: 'message', label: 'Text', onClick: () => window.location.href = sms },
    { icon: 'pin', label: 'Route', onClick: () => window.open(nav, '_blank') },
    { icon: 'eye', label: 'Photos', onClick: onPhotos },
    { icon: 'dollar-bill', label: 'Expense', onClick: onExpense },
    { icon: 'pencil', label: 'Note', onClick: onNote },
  ];
  return (
    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(6,1fr)', gap: 6, marginBottom: 12 }}>
      {items.map(it => (
        <button key={it.label} onClick={it.onClick}
          style={{ background: 'var(--card)', border: '1.5px solid var(--border)', borderRadius: 12, padding: '12px 4px', cursor: 'pointer', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4, fontFamily: 'inherit' }}>
          <Icon name={it.icon} size={20} color="var(--accent)"/>
          <span style={{ fontSize: 10, fontWeight: 700, color: 'var(--text)' }}>{it.label}</span>
        </button>
      ))}
    </div>
  );
}

// ─── STOPS CARD ───
function StopsCard({ job, state, stepIdx, stamp }) {
  const steps = [
    { key: 'started', label: 'En Route', sub: `From yard · ${job.time} scheduled`, ts: state.started },
    { key: 'arrivedPickup', label: 'Arrived Pickup', sub: job.from, ts: state.arrivedPickup },
    { key: 'departedPickup', label: 'Departed Pickup', sub: 'Truck loaded · en route to dropoff', ts: state.departedPickup },
    { key: 'arrivedDropoff', label: 'Arrived Dropoff', sub: job.to, ts: state.arrivedDropoff },
    { key: 'completed', label: 'Complete', sub: 'Unloaded · sign & close out', ts: state.completed },
  ];
  return (
    <div style={{ background: 'var(--card)', borderRadius: 14, padding: 18, marginBottom: 12, border: '1px solid var(--border)' }}>
      <div style={{ fontSize: 11, fontWeight: 800, color: 'var(--muted)', textTransform: 'uppercase', letterSpacing: '.1em', marginBottom: 14 }}>Job Stops</div>
      {steps.map((step, i) => {
        const done = !!step.ts;
        const current = i === stepIdx;
        const canTap = i === stepIdx && !state.completed;
        return (
          <div key={step.key} style={{ display: 'flex', gap: 12, paddingBottom: i === steps.length - 1 ? 0 : 14, position: 'relative' }}>
            {/* Connector */}
            {i < steps.length - 1 && (
              <div style={{ position: 'absolute', left: 13, top: 28, bottom: 0, width: 2, background: done ? 'var(--accent)' : 'var(--border)' }} />
            )}
            {/* Dot */}
            <div style={{
              width: 28, height: 28, borderRadius: '50%', flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center',
              background: done ? 'linear-gradient(135deg,#14C6BF,#3DCC7E)' : current ? '#fff' : 'var(--bg)',
              border: current && !done ? '2.5px dashed var(--accent)' : done ? 'none' : '2px solid var(--border)',
              color: done ? '#fff' : 'var(--muted)', fontWeight: 900, fontSize: 11,
              zIndex: 1,
            }}>{done ? <Icon name="check" size={14} color="#fff"/> : i + 1}</div>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontWeight: current || done ? 800 : 600, fontSize: 14, color: done ? 'var(--text)' : current ? 'var(--accent-dark)' : 'var(--muted)' }}>{step.label}</div>
              <div style={{ fontSize: 11, color: 'var(--muted)', marginTop: 1 }}>{step.sub}</div>
              {step.ts && (
                <div style={{ fontSize: 11, color: 'var(--accent-dark)', marginTop: 3, fontWeight: 700, display: 'inline-flex', alignItems: 'center', gap: 4 }}>
                  <Icon name="check" size={11}/> {fmtClock(new Date(step.ts))}
                </div>
              )}
            </div>
            {canTap && step.key !== 'started' && step.key !== 'completed' && (
              <button onClick={() => stamp(step.key)}
                style={{ padding: '7px 12px', borderRadius: 8, border: 'none', background: 'var(--accent)', color: '#fff', fontWeight: 800, fontFamily: 'inherit', fontSize: 12, cursor: 'pointer', alignSelf: 'center', whiteSpace: 'nowrap' }}>
                Stamp →
              </button>
            )}
          </div>
        );
      })}
    </div>
  );
}

// ─── EXPENSES CARD ───
function ExpensesCard({ expenses, total, onAdd, onRemove }) {
  return (
    <div style={{ background: 'var(--card)', borderRadius: 14, padding: 18, marginBottom: 12, border: '1px solid var(--border)' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>
        <div>
          <div style={{ fontSize: 11, fontWeight: 800, color: 'var(--muted)', textTransform: 'uppercase', letterSpacing: '.1em' }}>Expenses</div>
          <div style={{ fontSize: 18, fontWeight: 900, fontFamily: 'Poppins', marginTop: 2 }}>${total.toLocaleString()} <span style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600 }}>· {expenses.length} item{expenses.length !== 1 ? 's' : ''}</span></div>
        </div>
        <button onClick={onAdd} style={{ padding: '8px 14px', borderRadius: 9, border: 'none', background: 'var(--accent-light)', color: 'var(--accent-dark)', fontWeight: 800, fontFamily: 'inherit', fontSize: 12, cursor: 'pointer' }}>+ Add</button>
      </div>
      {expenses.length === 0 ? (
        <div style={{ padding: 14, background: 'var(--bg)', borderRadius: 10, textAlign: 'center', color: 'var(--muted)', fontSize: 12 }}>No expenses — gas, tolls, supplies, bridge fees</div>
      ) : (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
          {expenses.map(e => (
            <div key={e.id} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '8px 10px', background: 'var(--bg)', borderRadius: 8 }}>
              <Icon name={EXPENSE_ICONS[e.category] || 'dollar-bill'} size={16} color="var(--accent)"/>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontWeight: 700, fontSize: 13, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{e.label}</div>
                <div style={{ fontSize: 10, color: 'var(--muted)', display: 'flex', alignItems: 'center', gap: 4 }}>{new Date(e.ts).toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })}{e.receipt && <><span>·</span> <Icon name="paperclip" size={10}/> <span>receipt</span></>}</div>
              </div>
              <div style={{ fontWeight: 800, fontSize: 14 }}>${e.amount}</div>
              <button onClick={() => onRemove(e.id)} style={{ border: 'none', background: 'none', color: 'var(--muted)', cursor: 'pointer', padding: 4, fontSize: 14 }}>×</button>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

const EXPENSE_ICONS = { gas: 'gas', tolls: 'road', supplies: 'box', parking: 'parking', food: 'sandwich', other: 'dollar-bill' };

// ─── NOTES CARD ───
function NotesCard({ notes, onEdit }) {
  return (
    <div onClick={onEdit} style={{ background: 'var(--card)', borderRadius: 14, padding: 18, marginBottom: 12, border: '1px solid var(--border)', cursor: 'pointer' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
        <div style={{ fontSize: 11, fontWeight: 800, color: 'var(--muted)', textTransform: 'uppercase', letterSpacing: '.1em' }}>Notes</div>
        <div style={{ fontSize: 11, color: 'var(--accent)', fontWeight: 700 }}>{notes ? 'Edit →' : 'Add →'}</div>
      </div>
      {notes ? (
        <div style={{ fontSize: 13, lineHeight: 1.5, color: 'var(--text)', whiteSpace: 'pre-wrap' }}>{notes}</div>
      ) : (
        <div style={{ fontSize: 12, color: 'var(--muted)' }}>Stuck elevator? Extra flight? Damaged item? Log it here — it'll appear on the invoice and BOL.</div>
      )}
    </div>
  );
}

// ─── MODAL SHELL ───
function Modal({ title, onClose, children, footer }) {
  return (
    <div onClick={onClose} style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,.5)', zIndex: 200, display: 'flex', alignItems: 'flex-end', justifyContent: 'center' }}>
      <div onClick={e => e.stopPropagation()} style={{ width: '100%', maxWidth: 520, background: 'var(--card)', borderRadius: '20px 20px 0 0', padding: 22, maxHeight: '92vh', overflowY: 'auto', boxShadow: '0 -10px 40px rgba(0,0,0,.15)' }}>
        <div style={{ width: 40, height: 4, background: 'var(--border)', borderRadius: 2, margin: '0 auto 14px' }} />
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
          <h2 style={{ fontSize: 17, fontWeight: 900, fontFamily: 'Poppins' }}>{title}</h2>
          <button onClick={onClose} style={{ border: 'none', background: 'none', fontSize: 22, color: 'var(--muted)', cursor: 'pointer' }}>×</button>
        </div>
        {children}
        {footer && <div style={{ marginTop: 16 }}>{footer}</div>}
      </div>
    </div>
  );
}

// ─── EXPENSE MODAL ───
function ExpenseModal({ onClose, onAdd }) {
  const [amount, setAmount] = useState('');
  const [label, setLabel] = useState('');
  const [category, setCategory] = useState('gas');
  const [receipt, setReceipt] = useState(null);
  const fileRef = useRef(null);

  const cats = [
    { id: 'gas', icon: 'gas', label: 'Gas' },
    { id: 'tolls', icon: 'road', label: 'Tolls' },
    { id: 'supplies', icon: 'box', label: 'Supplies' },
    { id: 'parking', icon: 'parking', label: 'Parking' },
    { id: 'food', icon: 'sandwich', label: 'Crew Lunch' },
    { id: 'other', icon: 'dollar-bill', label: 'Other' },
  ];

  const presetLabels = {
    gas: 'Fuel fill-up', tolls: 'Toll road', supplies: 'Moving supplies',
    parking: 'Parking meter', food: 'Crew lunch', other: 'Misc expense',
  };

  const handleFile = async (files) => {
    if (!files?.[0]) return;
    if (typeof resizeImageFile === 'function') {
      try { setReceipt(await resizeImageFile(files[0])); } catch {}
    }
  };

  const save = () => {
    const amt = parseFloat(amount);
    if (!amt || amt <= 0) return;
    onAdd({ amount: amt, label: label || presetLabels[category], category, receipt });
    onClose();
  };

  return (
    <Modal title="Add Expense" onClose={onClose}>
      {/* Categories */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3,1fr)', gap: 6, marginBottom: 16 }}>
        {cats.map(c => (
          <button key={c.id} onClick={() => { setCategory(c.id); if (!label) setLabel(presetLabels[c.id]); }}
            style={{ padding: '12px 6px', borderRadius: 10, border: `1.5px solid ${category === c.id ? 'var(--accent)' : 'var(--border)'}`, background: category === c.id ? 'var(--accent-light)' : 'var(--card)', cursor: 'pointer', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 3, fontFamily: 'inherit' }}>
            <Icon name={c.icon} size={20} color={category === c.id ? 'var(--accent)' : 'var(--muted)'}/>
            <span style={{ fontSize: 11, fontWeight: 700, color: category === c.id ? 'var(--accent-dark)' : 'var(--text)' }}>{c.label}</span>
          </button>
        ))}
      </div>

      {/* Amount */}
      <div style={{ marginBottom: 14 }}>
        <label style={{ display: 'block', fontSize: 11, fontWeight: 700, color: 'var(--muted)', textTransform: 'uppercase', letterSpacing: '.08em', marginBottom: 6 }}>Amount</label>
        <div style={{ position: 'relative' }}>
          <span style={{ position: 'absolute', left: 14, top: '50%', transform: 'translateY(-50%)', fontSize: 22, fontWeight: 800, color: 'var(--muted)' }}>$</span>
          <input type="number" inputMode="decimal" value={amount} onChange={e => setAmount(e.target.value)}
            autoFocus placeholder="0.00"
            style={{ width: '100%', padding: '14px 14px 14px 34px', borderRadius: 10, border: '1.5px solid var(--border)', fontSize: 22, fontWeight: 800, fontFamily: 'Poppins', outline: 'none', background: 'var(--bg)' }} />
        </div>
      </div>

      {/* Label */}
      <div style={{ marginBottom: 14 }}>
        <label style={{ display: 'block', fontSize: 11, fontWeight: 700, color: 'var(--muted)', textTransform: 'uppercase', letterSpacing: '.08em', marginBottom: 6 }}>Description (optional)</label>
        <input value={label} onChange={e => setLabel(e.target.value)} placeholder={presetLabels[category]}
          style={{ width: '100%', padding: '12px 14px', borderRadius: 10, border: '1.5px solid var(--border)', fontSize: 14, fontFamily: 'inherit', outline: 'none' }} />
      </div>

      {/* Receipt */}
      <div style={{ marginBottom: 8 }}>
        <label style={{ display: 'block', fontSize: 11, fontWeight: 700, color: 'var(--muted)', textTransform: 'uppercase', letterSpacing: '.08em', marginBottom: 6 }}>Receipt Photo</label>
        <input ref={fileRef} type="file" accept="image/*" capture="environment" onChange={e => handleFile(e.target.files)} style={{ display: 'none' }} />
        {receipt ? (
          <div style={{ position: 'relative', borderRadius: 10, overflow: 'hidden' }}>
            <img src={receipt} style={{ width: '100%', maxHeight: 160, objectFit: 'cover', display: 'block' }} />
            <button onClick={() => setReceipt(null)} style={{ position: 'absolute', top: 8, right: 8, padding: '5px 10px', borderRadius: 6, border: 'none', background: 'rgba(0,0,0,.7)', color: '#fff', cursor: 'pointer', fontWeight: 700, fontSize: 11 }}>Remove</button>
          </div>
        ) : (
          <button onClick={() => fileRef.current?.click()}
            style={{ width: '100%', padding: '20px', border: '2px dashed var(--border)', background: 'var(--bg)', borderRadius: 10, cursor: 'pointer', fontFamily: 'inherit', color: 'var(--muted)', fontWeight: 600, fontSize: 13, display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 8 }}>
            <Icon name="eye" size={16}/> Snap a receipt (optional)
          </button>
        )}
      </div>

      <button onClick={save} disabled={!amount || parseFloat(amount) <= 0}
        style={{ marginTop: 16, width: '100%', padding: 14, borderRadius: 11, border: 'none', background: !amount || parseFloat(amount) <= 0 ? 'var(--border)' : 'linear-gradient(135deg,#14C6BF,#3DCC7E)', color: '#fff', fontWeight: 900, fontFamily: 'inherit', fontSize: 15, cursor: !amount || parseFloat(amount) <= 0 ? 'not-allowed' : 'pointer' }}>
        Save Expense {amount && `· $${amount}`}
      </button>
    </Modal>
  );
}

// ─── NOTE MODAL ───
function NoteModal({ current, onClose, onSave }) {
  const [text, setText] = useState(current);
  return (
    <Modal title="Job Notes" onClose={onClose}>
      <textarea value={text} onChange={e => setText(e.target.value)} autoFocus
        placeholder="e.g., Apartment elevator was broken — carried furniture up 3 flights. Customer asked us to move an extra desk from garage."
        style={{ width: '100%', minHeight: 160, padding: 14, borderRadius: 11, border: '1.5px solid var(--border)', fontSize: 14, fontFamily: 'inherit', outline: 'none', resize: 'vertical', lineHeight: 1.5 }} />
      <button onClick={() => onSave(text)}
        style={{ marginTop: 14, width: '100%', padding: 14, borderRadius: 11, border: 'none', background: 'linear-gradient(135deg,#14C6BF,#3DCC7E)', color: '#fff', fontWeight: 900, fontFamily: 'inherit', fontSize: 15, cursor: 'pointer' }}>
        Save Notes
      </button>
    </Modal>
  );
}

// ─── NTE OVERRIDE MODAL ───
function OverrideModal({ nte, running, onClose, onApprove }) {
  const [reason, setReason] = useState('');
  const presets = [
    'Customer added items not in original estimate',
    'Extra flight of stairs / elevator broken',
    'Traffic / longer distance than quoted',
    'Additional packing materials required',
    'Customer requested extra stop',
  ];
  return (
    <Modal title="NTE Override" onClose={onClose}>
      <div style={{ padding: 14, background: '#fef3c7', borderRadius: 11, marginBottom: 16, border: '1.5px solid #f59e0b' }}>
        <div style={{ fontWeight: 800, color: '#b45309', marginBottom: 6, fontSize: 13 }}>You're about to exceed the quoted NTE</div>
        <div style={{ fontSize: 12, color: '#b45309', lineHeight: 1.5 }}>
          Quoted: <b>${nte.toLocaleString()}</b> · Running: <b>${running.toLocaleString()}</b><br/>
          The customer must approve this in writing before you continue. A note will appear on the invoice documenting the override reason.
        </div>
      </div>

      <div style={{ fontSize: 11, fontWeight: 800, color: 'var(--muted)', textTransform: 'uppercase', letterSpacing: '.08em', marginBottom: 8 }}>Reason</div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 6, marginBottom: 14 }}>
        {presets.map(p => (
          <button key={p} onClick={() => setReason(p)}
            style={{ textAlign: 'left', padding: '12px 14px', borderRadius: 9, border: `1.5px solid ${reason === p ? 'var(--accent)' : 'var(--border)'}`, background: reason === p ? 'var(--accent-light)' : 'var(--card)', fontFamily: 'inherit', fontSize: 13, cursor: 'pointer', color: 'var(--text)' }}>
            {p}
          </button>
        ))}
      </div>
      <textarea value={reason} onChange={e => setReason(e.target.value)} placeholder="Or write a custom reason…"
        style={{ width: '100%', minHeight: 70, padding: 12, borderRadius: 10, border: '1.5px solid var(--border)', fontSize: 13, fontFamily: 'inherit', outline: 'none', resize: 'vertical' }} />

      <button onClick={() => { if (reason.trim()) { onApprove(reason); onClose(); } }}
        disabled={!reason.trim()}
        style={{ marginTop: 14, width: '100%', padding: 14, borderRadius: 11, border: 'none', background: !reason.trim() ? 'var(--border)' : 'linear-gradient(135deg,#f59e0b,#ef4444)', color: '#fff', fontWeight: 900, fontFamily: 'inherit', fontSize: 15, cursor: !reason.trim() ? 'not-allowed' : 'pointer' }}>
        Approve Override
      </button>
    </Modal>
  );
}

// ─── COMPLETE MODAL ───
function CompleteModal({ job, state, hours, laborCost, expenseTotal, runningTotal, nte, overNte, hasOverride, totalElapsed, billableElapsed, pausedMs, billableMinutesOverride, onSetBillableOverride, onClose, onConfirm }) {
  const finalAmount = overNte && !hasOverride ? nte : runningTotal;
  const needsReview = overNte && !hasOverride;

  // Allow owner to manually adjust the billable minutes at end-of-job.
  // Default is the auto-computed billable (total minus pauses).
  const computedBillableMins = Math.round((billableElapsed - (billableMinutesOverride != null ? billableMinutesOverride * 60000 : 0)) / 60000);
  const initialOverrideMins = billableMinutesOverride != null ? billableMinutesOverride : Math.round((billableElapsed) / 60000);
  const [overrideMins, setOverrideMins] = React.useState(initialOverrideMins);
  const [showAdjust, setShowAdjust] = React.useState(billableMinutesOverride != null);

  // Tip captured at end-of-job. Splits evenly across the assigned crew (including
  // the owner if on-job) and shows up in each worker's earnings dashboard.
  const crewSize = Math.max(1, (job.crew || []).length);
  const [tipTotal, setTipTotal] = React.useState(0);
  const tipPerPerson = (Number(tipTotal) || 0) / crewSize;

  function applyOverride() {
    onSetBillableOverride(parseInt(overrideMins) || 0);
  }
  function resetOverride() {
    onSetBillableOverride(null);
    setOverrideMins(Math.round(billableElapsed / 60000));
    setShowAdjust(false);
  }

  return (
    <Modal title="Complete Job & Invoice" onClose={onClose}>
      {/* Time review — both clocks shown so owner can compare and adjust */}
      <div style={{ background: 'linear-gradient(135deg,#f0fdfb,#f0fdf4)', border: '1.5px solid var(--accent)', borderRadius: 12, padding: 14, marginBottom: 14 }}>
        <div style={{ fontSize: 11, fontWeight: 800, color: 'var(--accent-dark)', textTransform: 'uppercase', letterSpacing: '.08em', marginBottom: 10 }}>End-of-Job Time Review</div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
          <div style={{ background: '#fff', borderRadius: 9, padding: 10, border: '1px solid var(--border)' }}>
            <div style={{ fontSize: 10, color: 'var(--muted)', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '.07em' }}>Total clock</div>
            <div style={{ fontSize: 20, fontWeight: 900, fontFamily: 'Poppins', color: 'var(--text)', marginTop: 2 }}>{fmtElapsed(totalElapsed)}</div>
            <div style={{ fontSize: 10, color: 'var(--muted)', marginTop: 2 }}>Crew is on-site for this</div>
          </div>
          <div style={{ background: '#fff', borderRadius: 9, padding: 10, border: '1px solid var(--accent)' }}>
            <div style={{ fontSize: 10, color: 'var(--accent-dark)', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '.07em' }}>Billable {billableMinutesOverride != null && '(adjusted)'}</div>
            <div style={{ fontSize: 20, fontWeight: 900, fontFamily: 'Poppins', color: 'var(--accent-dark)', marginTop: 2 }}>{fmtElapsed(billableMinutesOverride != null ? billableMinutesOverride * 60000 : billableElapsed)}</div>
            <div style={{ fontSize: 10, color: 'var(--muted)', marginTop: 2 }}>What the customer pays</div>
          </div>
        </div>
        {pausedMs > 0 && (
          <div style={{ fontSize: 11, color: 'var(--muted)', marginTop: 8 }}>
            Paused {fmtElapsed(pausedMs)} during the job ({(state.pauses || []).filter(p => p.end || !p.end).length} pause{(state.pauses || []).length !== 1 ? 's' : ''}: {(state.pauses || []).map(p => p.reason).join(', ') || 'unspecified'}).
          </div>
        )}

        {!showAdjust ? (
          <button onClick={() => setShowAdjust(true)}
            style={{ marginTop: 10, padding: '8px 14px', borderRadius: 8, background: 'transparent', border: '1.5px solid var(--accent)', color: 'var(--accent-dark)', fontWeight: 700, fontSize: 12, cursor: 'pointer', fontFamily: 'inherit' }}>
            Adjust billable time
          </button>
        ) : (
          <div style={{ marginTop: 10, padding: 12, background: '#fff', borderRadius: 9, border: '1px solid var(--accent)' }}>
            <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--muted)', marginBottom: 6 }}>Billable minutes (override)</div>
            <div style={{ display: 'flex', gap: 6, alignItems: 'center', flexWrap: 'wrap' }}>
              <input type="number" min="0" value={overrideMins}
                onChange={e => setOverrideMins(e.target.value)}
                style={{ width: 100, padding: '8px 10px', borderRadius: 7, border: '1.5px solid var(--border)', fontSize: 14, fontFamily: 'inherit' }}/>
              <span style={{ fontSize: 12, color: 'var(--muted)' }}>= {Math.round((parseInt(overrideMins)||0)/60*100)/100} hrs</span>
              <button onClick={applyOverride} style={{ padding: '8px 14px', borderRadius: 7, background: 'var(--accent)', color: '#fff', border: 'none', fontWeight: 700, fontSize: 12, cursor: 'pointer', fontFamily: 'inherit' }}>Apply</button>
              {billableMinutesOverride != null && (
                <button onClick={resetOverride} style={{ padding: '8px 12px', borderRadius: 7, background: 'transparent', color: 'var(--muted)', border: '1.5px solid var(--border)', fontWeight: 600, fontSize: 12, cursor: 'pointer', fontFamily: 'inherit' }}>Reset to auto</button>
              )}
            </div>
          </div>
        )}
      </div>

      <div style={{ background: 'var(--bg)', borderRadius: 12, padding: 16, marginBottom: 16 }}>
        <div style={{ fontSize: 11, fontWeight: 800, color: 'var(--muted)', textTransform: 'uppercase', letterSpacing: '.08em', marginBottom: 10 }}>Final Totals</div>
        {[
          ['Labor', `${hours.toFixed(2)} hrs × $${state.hourlyRate}`, `$${laborCost.toLocaleString()}`],
          ['Expenses', `${state.expenses.length} item${state.expenses.length !== 1 ? 's' : ''}`, `$${expenseTotal.toLocaleString()}`],
          ['Quoted NTE', 'Per signed estimate', `$${nte.toLocaleString()}`],
        ].map(([k, sub, v]) => (
          <div key={k} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', padding: '7px 0', borderBottom: '1px solid var(--border)' }}>
            <div>
              <div style={{ fontSize: 13, fontWeight: 700 }}>{k}</div>
              <div style={{ fontSize: 10, color: 'var(--muted)' }}>{sub}</div>
            </div>
            <div style={{ fontSize: 14, fontWeight: 800 }}>{v}</div>
          </div>
        ))}
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', paddingTop: 12, marginTop: 4 }}>
          <div style={{ fontSize: 15, fontWeight: 900, fontFamily: 'Poppins' }}>Invoice Total</div>
          <div style={{ fontSize: 22, fontWeight: 900, color: 'var(--accent-dark)', fontFamily: 'Poppins' }}>${finalAmount.toLocaleString()}</div>
        </div>
      </div>

      {needsReview && (
        <div style={{ padding: 13, background: '#fef3c7', borderRadius: 10, marginBottom: 16, fontSize: 12, color: '#b45309', lineHeight: 1.5, border: '1.5px solid #f59e0b' }}>
          <span style={{display:'inline-flex',alignItems:'center',gap:5}}><Icon name="alert" size={13}/> <b>Over NTE, no override on file.</b></span><br/>
          Invoice will be capped at ${nte.toLocaleString()}. You can edit later in Invoices.
        </div>
      )}

      {hasOverride && (
        <div style={{ padding: 13, background: '#fef3c7', borderRadius: 10, marginBottom: 16, fontSize: 12, color: '#b45309', lineHeight: 1.5 }}>
          <span style={{display:'inline-flex',alignItems:'center',gap:5}}><Icon name="check" size={13}/> <b>Override on file:</b> "{state.nteOverride.reason}"</span><br/>
          This will appear as a line item on the invoice.
        </div>
      )}

      {/* Tip entry — splits evenly across assigned crew, shows up in each worker's
          earnings dashboard. Optional. Leave at 0 if no tip received. */}
      <div style={{ background: 'linear-gradient(135deg,#f0fdf4,#dcfce7)', border: '1.5px solid #86efac', borderRadius: 12, padding: 14, marginBottom: 16 }}>
        <div style={{ fontSize: 11, fontWeight: 800, color: '#15803d', textTransform: 'uppercase', letterSpacing: '.08em', marginBottom: 6 }}>Tip received (optional)</div>
        <div style={{ fontSize: 11, color: 'var(--muted)', marginBottom: 10, lineHeight: 1.45 }}>
          Splits evenly across {crewSize} crew{crewSize !== 1 ? ' members' : ''}. Each person will see their share in their worker portal.
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          <div style={{ position: 'relative', flex: 1 }}>
            <span style={{ position: 'absolute', left: 12, top: '50%', transform: 'translateY(-50%)', fontSize: 16, fontWeight: 800, color: 'var(--muted)' }}>$</span>
            <input type="number" min="0" step="1" inputMode="decimal"
              value={tipTotal} onChange={e => setTipTotal(e.target.value)}
              placeholder="0"
              style={{ width: '100%', padding: '10px 12px 10px 26px', borderRadius: 8, border: '1.5px solid var(--border)', fontSize: 16, fontFamily: 'inherit', fontWeight: 700 }}/>
          </div>
          {Number(tipTotal) > 0 && (
            <div style={{ fontSize: 13, fontWeight: 700, color: '#15803d', whiteSpace: 'nowrap' }}>
              = ${tipPerPerson.toFixed(2)} <span style={{ color: 'var(--muted)', fontWeight: 600 }}>each</span>
            </div>
          )}
        </div>
      </div>

      <button onClick={() => onConfirm(Number(tipTotal) || 0)}
        style={{ width: '100%', padding: 15, borderRadius: 11, border: 'none', background: 'linear-gradient(135deg,#16a34a,#22c55e)', color: '#fff', fontWeight: 900, fontFamily: 'inherit', fontSize: 15, cursor: 'pointer', boxShadow: '0 6px 20px rgba(22,163,74,.25)', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 7 }}>
        <Icon name="check" size={16} color="#fff"/> Complete Job · Generate Invoice
      </button>
      <div style={{ fontSize: 11, color: 'var(--muted)', textAlign: 'center', marginTop: 10 }}>Customer will receive a shareable invoice link via the CRM.</div>
    </Modal>
  );
}

// ─── PHOTOS MODAL (reuses JobPhotoGrid) ───
function PhotosModal({ jobId, onClose }) {
  return (
    <Modal title="Photos & Documentation" onClose={onClose}>
      {typeof JobPhotoGrid !== 'undefined' ? (
        <JobPhotoGrid jobId={jobId} label="" compact />
      ) : (
        <div style={{ color: 'var(--muted)', fontSize: 13 }}>Photo grid unavailable.</div>
      )}
    </Modal>
  );
}

Object.assign(window, { OnJobView, getJobState, getJobStates });
