// invodiff-jobs.jsx — analytics dashboard landing page

// Compute per-job summary stats from Firestore job data
function computeJobStats(job) {
  const invoices = job.invoices || [];
  if (!job.quote) {
    return { hasData: false, vendor: '', invoiceCount: invoices.length };
  }
  if (invoices.length === 0) {
    return {
      hasData: true, noBill: true,
      vendor: job.quote.vendor || '',
      invoiceCount: 0,
      quotedTotal: grandTotal(job.quote),
      invoicedTotal: 0,
      remaining: grandTotal(job.quote),
      pctUsed: 0,
      flaggedCount: 0,
      lfQuoted: 0, lfInvoiced: 0,
      status: 'pending',
    };
  }
  try {
    const { summary, rows } = aggregateInvoices(job.quote, invoices, []);
    const lfRows     = rows.filter((r) => r.uomNorm === 'LF' && !r.synthetic && !r.flags?.added);
    const lfQuoted   = lfRows.reduce((s, r) => s + (r.quotedQty || 0), 0);
    const lfInvoiced = lfRows.reduce((s, r) => s + (r.invoicedQty || 0), 0);
    const overBudget = summary.invoicedTotal > summary.quotedTotal;
    return {
      hasData: true,
      vendor: job.quote.vendor || '',
      invoiceCount: invoices.length,
      quotedTotal: summary.quotedTotal,
      invoicedTotal: summary.invoicedTotal,
      remaining: +(summary.quotedTotal - summary.invoicedTotal).toFixed(2),
      pctUsed: summary.quotedTotal > 0 ? summary.invoicedTotal / summary.quotedTotal : 0,
      flaggedCount: summary.flaggedCount,
      lfQuoted, lfInvoiced,
      status: summary.flaggedCount === 0 ? 'ok' : overBudget ? 'bad' : 'warn',
    };
  } catch {
    return { hasData: false, vendor: job.quote?.vendor || '', invoiceCount: invoices.length };
  }
}

// ── Dashboard metric card ─────────────────────────────────────────────────
function DashMetric({ label, value, sub, tone = 'default', icon }) {
  return (
    <div className={`dm dm-${tone}`}>
      <div className="dm-icon"><Icon name={icon} size={17} /></div>
      <div className="dm-body">
        <div className="dm-value num">{value}</div>
        <div className="dm-label">{label}</div>
        {sub && <div className="dm-sub">{sub}</div>}
      </div>
    </div>
  );
}

// ── Job card ──────────────────────────────────────────────────────────────
function JobCard({ job, stats, onClick, onToggleActive, onDelete }) {
  const fmtDate = (ts) => {
    if (!ts?.toDate) return '';
    return ts.toDate().toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' });
  };

  const STATUS_COLOR = { ok: 'var(--ok)', warn: 'var(--warn)', bad: 'var(--bad)', pending: 'var(--fg-dim)' };
  const STATUS_LABEL = { ok: 'On track', warn: 'Review needed', bad: 'Over budget', pending: 'No invoices yet' };

  const s          = stats;
  const isArchived = job.active === false;
  const pctClamped = s ? Math.min(s.pctUsed, 1) : 0;
  const statusColor = s ? STATUS_COLOR[s.status] : 'var(--fg-dim)';

  const stopAndCall = (e, fn) => { e.stopPropagation(); fn(); };

  return (
    <div
      className={`jl-card card${s?.flaggedCount > 0 && !isArchived ? ' jl-card-flagged' : ''}${isArchived ? ' jl-card-archived' : ''}`}
      onClick={onClick}
      role="button"
      tabIndex={0}
      onKeyDown={(e) => e.key === 'Enter' && onClick()}
    >
      <div className="jl-card-top">
        <div className="jl-card-num mono">{job.jobNumber}</div>
        <div className="jl-card-top-right">
          {isArchived ? (
            <span className="jl-archived-tag">Archived</span>
          ) : (
            s && (
              <span className="jl-card-status">
                <span className="jl-cs-dot" style={{ background: statusColor }} />
                <span style={{ color: statusColor }}>{STATUS_LABEL[s.status]}</span>
              </span>
            )
          )}
          <div className="jl-card-acts" onClick={(e) => e.stopPropagation()}>
            <button
              className="jl-act-btn"
              title={isArchived ? 'Restore job' : 'Archive job'}
              onClick={(e) => stopAndCall(e, onToggleActive)}
            >
              <Icon name={isArchived ? 'eye' : 'archive'} size={13} />
            </button>
            <button
              className="jl-act-btn jl-act-del"
              title="Delete job permanently"
              onClick={(e) => stopAndCall(e, onDelete)}
            >
              <Icon name="trash" size={13} />
            </button>
          </div>
        </div>
      </div>

      {s?.vendor && <div className="jl-card-vendor">{s.vendor}</div>}

      {s?.hasData && !s?.noBill && !isArchived && (
        <>
          <div className="jl-budget-bar">
            <div className="jl-bb-track">
              <div className="jl-bb-fill" style={{ width: `${pctClamped * 100}%`, background: statusColor }} />
            </div>
            <span className="jl-bb-pct num" style={{ color: statusColor }}>
              {Math.round(Math.min(s.pctUsed, 9.99) * 100)}%
            </span>
          </div>
          <div className="jl-card-amounts">
            <span className="num jl-ca-inv">{fmtUSD(s.invoicedTotal)}</span>
            <span className="jl-ca-sep">of</span>
            <span className="num jl-ca-qtd">{fmtUSD(s.quotedTotal)}</span>
          </div>
        </>
      )}

      {s?.hasData && s?.noBill && (
        <div className="jl-card-quoted">
          <span className="jl-cq-label">Quoted</span>
          <span className="num">{fmtUSD(s.quotedTotal)}</span>
        </div>
      )}

      {!s?.hasData && (
        <div className="jl-card-setup">Upload a quote to start tracking</div>
      )}

      <div className="jl-card-footer">
        {s?.invoiceCount > 0 && (
          <span className="jl-cf-chip">
            <Icon name="file" size={10} />
            {s.invoiceCount} invoice{s.invoiceCount !== 1 ? 's' : ''}
          </span>
        )}
        {s?.lfInvoiced > 0 && (
          <span className="jl-cf-chip">
            {s.lfInvoiced.toLocaleString()} LF
          </span>
        )}
        {s?.flaggedCount > 0 && !isArchived && (
          <span className="jl-cf-chip jl-cf-flag">
            <Icon name="flag" size={10} />
            {s.flaggedCount} flagged
          </span>
        )}
        {(!s || (!s.invoiceCount && !s.lfInvoiced && !s.flaggedCount)) && (
          <span className="jl-card-date">{fmtDate(job.createdAt)}</span>
        )}
        <span className="jl-cf-arrow"><Icon name="chev" size={12} /></span>
      </div>
    </div>
  );
}

// ── Roadmap scaffold panel ────────────────────────────────────────────────
function ScaffoldPanel({ title, priority, badge, items }) {
  return (
    <div className={`jl-scaffold ${priority ? 'jl-scaffold-priority' : ''}`}>
      <div className="jl-sc-hd">
        <div className="jl-sc-title">
          {title}
          {priority && <span className="jl-sc-star">★ Priority</span>}
        </div>
        <span className="jl-sc-badge">{badge}</span>
      </div>
      <ul className="jl-sc-list">
        {items.map((item, i) => (
          <li key={i} className="jl-sc-item">
            <span className="jl-sc-bullet">•</span>
            {item}
          </li>
        ))}
      </ul>
    </div>
  );
}

// ── JobListView (dashboard root) ──────────────────────────────────────────
function JobListView({ user, onSelectJob }) {
  const [jobs, setJobs]             = React.useState([]);
  const [loading, setLoading]       = React.useState(true);
  const [showInput, setShowInput]   = React.useState(false);
  const [newNumber, setNewNumber]   = React.useState('');
  const [creating, setCreating]     = React.useState(false);
  const [showArchived, setShowArchived] = React.useState(false);
  const inputRef = React.useRef(null);

  React.useEffect(() => {
    const unsub = fbDb
      .collection('users').doc(user.uid)
      .collection('jobs')
      .orderBy('createdAt', 'desc')
      .onSnapshot(
        (snap) => { setJobs(snap.docs.map((d) => ({ id: d.id, ...d.data() }))); setLoading(false); },
        ()     => setLoading(false)
      );
    return unsub;
  }, [user.uid]);

  React.useEffect(() => {
    if (showInput) inputRef.current?.focus();
  }, [showInput]);

  const handleCreate = async () => {
    const num = newNumber.trim();
    if (!num || creating) return;
    setCreating(true);
    try {
      const ref  = await fbDb.collection('users').doc(user.uid).collection('jobs').add({
        jobNumber: num,
        createdAt: firebase.firestore.FieldValue.serverTimestamp(),
        quote:     null,
        invoices:  [],
        active:    true,
      });
      const snap = await ref.get();
      onSelectJob({ id: snap.id, ...snap.data() });
    } catch (e) {
      console.error('Failed to create job:', e);
      setCreating(false);
    }
  };

  const handleToggleActive = async (job) => {
    try {
      await fbDb.collection('users').doc(user.uid).collection('jobs').doc(job.id)
        .update({ active: job.active === false ? true : false });
    } catch (e) {
      console.error('Failed to update job:', e);
    }
  };

  const handleDelete = async (job) => {
    if (!window.confirm(`Delete job "${job.jobNumber}" permanently? This cannot be undone.`)) return;
    try {
      await fbDb.collection('users').doc(user.uid).collection('jobs').doc(job.id).delete();
    } catch (e) {
      console.error('Failed to delete job:', e);
    }
  };

  const cancelInput = () => { setShowInput(false); setNewNumber(''); };

  const jobStats = React.useMemo(
    () => Object.fromEntries(jobs.map((j) => [j.id, computeJobStats(j)])),
    [jobs]
  );

  const activeJobs   = React.useMemo(() => jobs.filter((j) => j.active !== false), [jobs]);
  const archivedJobs = React.useMemo(() => jobs.filter((j) => j.active === false),  [jobs]);

  const metrics = React.useMemo(() => {
    const all = activeJobs.map((j) => jobStats[j.id]).filter(Boolean);
    return {
      totalJobs:    activeJobs.length,
      budgetTotal:  all.reduce((s, st) => s + (st.quotedTotal   || 0), 0),
      invoicedTotal:all.reduce((s, st) => s + (st.invoicedTotal || 0), 0),
      lfTracked:    all.reduce((s, st) => s + (st.lfInvoiced    || 0), 0),
      needsReview:  all.filter((st)    => st.flaggedCount > 0).length,
      invoiceCount: all.reduce((s, st) => s + (st.invoiceCount  || 0), 0),
    };
  }, [jobStats, activeJobs]);

  const priorityJobs = React.useMemo(
    () => activeJobs
      .filter((j) => jobStats[j.id]?.flaggedCount > 0)
      .sort((a, b) => (jobStats[b.id]?.flaggedCount || 0) - (jobStats[a.id]?.flaggedCount || 0))
      .slice(0, 4),
    [activeJobs, jobStats]
  );

  return (
    <div className="jl-view">

      {/* ── Header ──────────────────────────────────────────── */}
      <div className="jl-hd">
        <div>
          <div className="jl-eyebrow mono">DASHBOARD</div>
          <h1 className="jl-title">Job Overview</h1>
        </div>
        {showInput ? (
          <div className="jl-new-row">
            <input
              ref={inputRef}
              className="input jl-new-input"
              placeholder="Job number…"
              value={newNumber}
              disabled={creating}
              onChange={(e) => setNewNumber(e.target.value)}
              onKeyDown={(e) => {
                if (e.key === 'Enter')  handleCreate();
                if (e.key === 'Escape') cancelInput();
              }}
            />
            <button className="btn btn-primary btn-sm" onClick={handleCreate} disabled={creating || !newNumber.trim()}>
              {creating ? 'Creating…' : 'Create'}
            </button>
            <button className="btn btn-ghost btn-sm" onClick={cancelInput} disabled={creating}>Cancel</button>
          </div>
        ) : (
          <button className="btn btn-primary" onClick={() => setShowInput(true)}>
            <Icon name="plus" size={14} /> New Job
          </button>
        )}
      </div>

      {/* ── Summary metrics ─────────────────────────────────── */}
      {!loading && activeJobs.length > 0 && (
        <div className="jl-metrics">
          <DashMetric
            icon="file"   label="Active jobs"
            value={metrics.totalJobs}
            sub={`${metrics.invoiceCount} invoice${metrics.invoiceCount !== 1 ? 's' : ''} processed`}
          />
          <DashMetric
            icon="delta"  label="Budget tracked"
            value={fmtUSD(metrics.budgetTotal)}
            sub={`${fmtUSD(metrics.invoicedTotal)} invoiced so far`}
            tone="info"
          />
          <DashMetric
            icon="arrow"  label="Footage invoiced"
            value={metrics.lfTracked > 0 ? metrics.lfTracked.toLocaleString() + ' LF' : '—'}
            sub={metrics.lfTracked > 0 ? 'Linear feet across all jobs' : 'No LF items yet'}
            tone={metrics.lfTracked > 0 ? 'info' : 'default'}
          />
          <DashMetric
            icon="flag"   label="Need review"
            value={metrics.needsReview}
            sub={metrics.needsReview === 0 ? 'All jobs on track' : `${metrics.needsReview} job${metrics.needsReview !== 1 ? 's' : ''} with issues`}
            tone={metrics.needsReview > 0 ? 'warn' : 'ok'}
          />
        </div>
      )}

      {/* ── Priority alerts ─────────────────────────────────── */}
      {priorityJobs.length > 0 && (
        <div className="jl-alerts">
          <div className="jl-section-hd">
            <Icon name="flag" size={13} style={{ color: 'var(--warn)' }} />
            <span className="jl-section-title">Needs Review</span>
            <span className="jl-section-count num">{priorityJobs.length}</span>
          </div>
          <div className="jl-alert-list">
            {priorityJobs.map((job) => {
              const st = jobStats[job.id];
              return (
                <button key={job.id} className="jl-alert-row" onClick={() => onSelectJob(job)}>
                  <span className="mono jl-alert-num">{job.jobNumber}</span>
                  <span className="jl-alert-vendor">{st?.vendor}</span>
                  <span className="jl-alert-flags">
                    <Icon name="flag" size={11} />
                    {st?.flaggedCount} item{st?.flaggedCount !== 1 ? 's' : ''} flagged
                  </span>
                  {st?.pctUsed > 1 && (
                    <span className="jl-alert-chip jl-alert-bad">Over budget</span>
                  )}
                  <Icon name="chev" size={12} style={{ color: 'var(--fg-dim)', marginLeft: 'auto', flexShrink: 0 }} />
                </button>
              );
            })}
          </div>
        </div>
      )}

      {/* ── Job grid ────────────────────────────────────────── */}
      {loading ? (
        <div className="jl-state"><div className="jl-spin"></div></div>
      ) : jobs.length === 0 ? (
        <div className="jl-state">
          <div className="jl-empty-icon"><Icon name="delta" size={32} /></div>
          <div className="jl-empty-title">No jobs yet</div>
          <div className="jl-empty-sub">Create your first job to start tracking budgets, invoices, and footage.</div>
          <button className="btn btn-primary" onClick={() => setShowInput(true)}>
            <Icon name="plus" size={14} /> New Job
          </button>
        </div>
      ) : (
        <>
          <div className="jl-section-hd">
            <span className="jl-section-title">All Jobs</span>
            <span className="jl-section-count num">{activeJobs.length}</span>
          </div>
          <div className="jl-grid">
            {activeJobs.map((job) => (
              <JobCard
                key={job.id}
                job={job}
                stats={jobStats[job.id]}
                onClick={() => onSelectJob(job)}
                onToggleActive={() => handleToggleActive(job)}
                onDelete={() => handleDelete(job)}
              />
            ))}
          </div>

          {/* ── Archived section ─────────────────────────── */}
          {archivedJobs.length > 0 && (
            <div className="jl-archived-section">
              <button className="jl-archived-toggle" onClick={() => setShowArchived((v) => !v)}>
                <Icon name="archive" size={13} />
                <span>Archived ({archivedJobs.length})</span>
                <Icon name="chev" size={12} style={{ transform: showArchived ? 'rotate(90deg)' : 'none', transition: 'transform .15s' }} />
              </button>
              {showArchived && (
                <div className="jl-grid jl-grid-archived">
                  {archivedJobs.map((job) => (
                    <JobCard
                      key={job.id}
                      job={job}
                      stats={jobStats[job.id]}
                      onClick={() => onSelectJob(job)}
                      onToggleActive={() => handleToggleActive(job)}
                      onDelete={() => handleDelete(job)}
                    />
                  ))}
                </div>
              )}
            </div>
          )}
        </>
      )}

      {/* ── Roadmap: scaffolded upcoming features ────────────── */}
      <div className="jl-roadmap">
        <div className="jl-roadmap-hd">
          <span className="jl-roadmap-title">Coming Soon</span>
          <span className="jl-roadmap-sub">Features in development — built around your workflow</span>
        </div>
        <div className="jl-scaffold-grid">
          <ScaffoldPanel
            title="Invoice Approval"
            priority={true}
            badge="In development"
            items={[
              'Auto-pull invoices from your AP inbox',
              '3-way match: Quote vs. packing slip vs. invoice',
              'Flag price errors, back-orders & duplicates instantly',
            ]}
          />
          <ScaffoldPanel
            title="Pricing Intelligence"
            priority={false}
            badge="Planned"
            items={[
              'Track min / max / recent price per item across all vendors',
              'Alert when invoice price deviates from historical data',
              'Identify cheapest vendor per line item over time',
            ]}
          />
        </div>
      </div>

      <JobListStyles />
    </div>
  );
}

const JobListStyles = () => (
  <style>{`
    /* ── Page layout ─────────────────────────────────────────── */
    .jl-view { max-width: 1200px; margin: 0 auto; padding: 48px 28px 80px; }
    .jl-hd {
      display: flex; align-items: flex-start; justify-content: space-between;
      gap: 16px; margin-bottom: 28px; flex-wrap: wrap;
    }
    .jl-eyebrow { font-size: 10.5px; letter-spacing: 0.14em; color: var(--fg-dim); margin-bottom: 6px; }
    .jl-title   { margin: 0; font-size: 30px; font-weight: 600; letter-spacing: -0.02em; }
    .jl-new-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
    .jl-new-input { width: 180px; }

    /* ── Summary metrics ─────────────────────────────────────── */
    .jl-metrics {
      display: grid; grid-template-columns: repeat(4, 1fr);
      gap: 12px; margin-bottom: 28px;
    }
    .dm {
      display: flex; align-items: flex-start; gap: 12px;
      padding: 16px 18px;
      background: var(--surface); border: 1px solid var(--border);
      border-radius: var(--r-lg); box-shadow: var(--shadow-1);
    }
    .dm-icon {
      width: 38px; height: 38px; border-radius: 9px;
      display: grid; place-items: center;
      background: var(--surface-2); color: var(--fg-dim); flex-shrink: 0;
    }
    .dm-ok   .dm-icon { background: var(--ok-bg);   color: var(--ok);   }
    .dm-warn .dm-icon { background: var(--warn-bg);  color: var(--warn); }
    .dm-info .dm-icon { background: var(--info-bg);  color: var(--info); }
    .dm-body  { flex: 1; min-width: 0; }
    .dm-value { font-size: 22px; font-weight: 600; letter-spacing: -0.02em; line-height: 1.1; }
    .dm-label { font-size: 10.5px; color: var(--fg-dim); text-transform: uppercase; letter-spacing: 0.06em; margin-top: 4px; font-weight: 500; }
    .dm-sub   { font-size: 11px; color: var(--fg-dim); margin-top: 3px; line-height: 1.4; }

    /* ── Section heading ──────────────────────────────────────── */
    .jl-section-hd { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }
    .jl-section-title { font-size: 13px; font-weight: 600; letter-spacing: -0.005em; }
    .jl-section-count {
      font-size: 10.5px; padding: 1px 8px; border-radius: 999px;
      background: var(--surface-2); color: var(--fg-dim); border: 1px solid var(--border);
    }

    /* ── Priority alerts ─────────────────────────────────────── */
    .jl-alerts { margin-bottom: 32px; }
    .jl-alert-list { display: flex; flex-direction: column; gap: 3px; }
    .jl-alert-row {
      appearance: none; border: 1px solid var(--border-strong);
      background: var(--surface); border-radius: var(--r-lg);
      padding: 11px 16px; font: inherit; cursor: pointer;
      display: flex; align-items: center; gap: 14px; text-align: left;
      transition: border-color .12s, background .12s;
    }
    .jl-alert-row:hover { border-color: var(--warn); background: oklch(from var(--warn) l c h / 0.04); }
    .jl-alert-num    { font-size: 13px; font-weight: 600; min-width: 100px; }
    .jl-alert-vendor { font-size: 12.5px; color: var(--fg-muted); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .jl-alert-flags  { display: flex; align-items: center; gap: 5px; font-size: 12px; color: var(--warn); white-space: nowrap; }
    .jl-alert-chip   { font-size: 10.5px; padding: 2px 8px; border-radius: 999px; border: 1px solid; white-space: nowrap; }
    .jl-alert-bad    { color: var(--bad); border-color: oklch(from var(--bad) l calc(c*.5) h / .4); background: var(--bad-bg); }

    /* ── Job cards ───────────────────────────────────────────── */
    .jl-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(270px, 1fr)); gap: 14px; margin-bottom: 40px; }
    .jl-grid-archived { margin-top: 12px; margin-bottom: 0; }

    .jl-card {
      width: 100%; font: inherit;
      background: var(--surface); border: 1px solid var(--border);
      border-radius: var(--r-lg); box-shadow: var(--shadow-1);
      padding: 18px 20px; text-align: left; cursor: pointer;
      display: flex; flex-direction: column; gap: 10px;
      transition: border-color .12s, background .12s, box-shadow .12s;
      outline: none;
      position: relative;
    }
    .jl-card:hover { border-color: var(--accent); background: var(--surface-2); box-shadow: var(--shadow-2); }
    .jl-card:focus-visible { box-shadow: 0 0 0 2px var(--accent); }
    .jl-card-flagged { border-color: oklch(from var(--warn) l calc(c*.5) h / .45); }
    .jl-card-flagged:hover { border-color: var(--warn); }
    .jl-card-archived { opacity: 0.6; }

    .jl-card-top     { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
    .jl-card-top-right { display: flex; align-items: center; gap: 6px; }
    .jl-card-num     { font-size: 14px; font-weight: 600; color: var(--fg); }
    .jl-card-status  { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; font-weight: 500; }
    .jl-cs-dot       { width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; }
    .jl-card-vendor  { font-size: 12px; color: var(--fg-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .jl-archived-tag {
      font-size: 10px; padding: 2px 7px; border-radius: 999px;
      background: var(--surface-3); color: var(--fg-dim);
      border: 1px solid var(--border); font-weight: 500;
      text-transform: uppercase; letter-spacing: 0.05em;
    }

    /* Card action buttons — visible on card hover */
    .jl-card-acts {
      display: flex; gap: 2px;
      opacity: 0; transition: opacity .12s;
    }
    .jl-card:hover .jl-card-acts { opacity: 1; }
    .jl-act-btn {
      appearance: none; border: 1px solid var(--border);
      background: var(--surface-2); border-radius: 5px;
      padding: 4px 5px; cursor: pointer;
      color: var(--fg-dim); display: grid; place-items: center;
      transition: color .1s, background .1s, border-color .1s;
    }
    .jl-act-btn:hover { color: var(--fg); background: var(--surface-3); border-color: var(--border-strong); }
    .jl-act-del:hover { color: var(--bad); background: var(--bad-bg); border-color: oklch(from var(--bad) l calc(c*.5) h / .4); }

    .jl-budget-bar  { display: flex; align-items: center; gap: 8px; }
    .jl-bb-track    { flex: 1; height: 4px; background: var(--surface-3); border-radius: 999px; overflow: hidden; }
    .jl-bb-fill     { height: 100%; border-radius: 999px; transition: width .4s; }
    .jl-bb-pct      { font-size: 11px; font-weight: 600; flex-shrink: 0; }
    .jl-card-amounts{ display: flex; align-items: baseline; gap: 6px; font-size: 12px; }
    .jl-ca-inv      { color: var(--fg); font-size: 14px; font-weight: 600; }
    .jl-ca-sep      { color: var(--fg-dim); }
    .jl-ca-qtd      { color: var(--fg-dim); }

    .jl-card-quoted { display: flex; align-items: center; justify-content: space-between; font-size: 13px; }
    .jl-cq-label    { font-size: 10px; color: var(--fg-dim); text-transform: uppercase; letter-spacing: 0.06em; font-weight: 500; }
    .jl-card-setup  { font-size: 12px; color: var(--fg-dim); font-style: italic; }

    .jl-card-footer {
      display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
      padding-top: 8px; border-top: 1px solid var(--border);
    }
    .jl-cf-arrow  { color: var(--fg-dim); margin-left: auto; flex-shrink: 0; opacity: 0.5; }
    .jl-cf-chip   {
      display: inline-flex; align-items: center; gap: 4px;
      font-size: 10.5px; color: var(--fg-dim);
      background: var(--surface-2); padding: 2px 8px;
      border-radius: 999px; border: 1px solid var(--border);
    }
    .jl-cf-flag   { color: var(--warn); background: var(--warn-bg); border-color: oklch(from var(--warn) l calc(c*.5) h / .35); }
    .jl-card-date { font-size: 11px; color: var(--fg-dim); }

    /* ── Archived section toggle ─────────────────────────────── */
    .jl-archived-section { margin-bottom: 40px; }
    .jl-archived-toggle {
      appearance: none; border: 1px solid var(--border); background: var(--surface-2);
      border-radius: var(--r); padding: 7px 14px; font: inherit; font-size: 12.5px;
      font-weight: 500; color: var(--fg-muted); cursor: pointer;
      display: inline-flex; align-items: center; gap: 7px;
      transition: color .12s, background .12s, border-color .12s;
    }
    .jl-archived-toggle:hover { color: var(--fg); background: var(--surface-3); border-color: var(--border-strong); }

    /* ── Empty / loading states ──────────────────────────────── */
    .jl-state { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px; min-height: 280px; }
    .jl-empty-icon  { color: var(--fg-dim); opacity: 0.3; }
    .jl-empty-title { font-size: 16px; font-weight: 500; color: var(--fg-muted); }
    .jl-empty-sub   { font-size: 13px; color: var(--fg-dim); max-width: 340px; text-align: center; line-height: 1.55; }
    .jl-spin {
      width: 28px; height: 28px; border-radius: 50%;
      border: 2.5px solid var(--border); border-top-color: var(--accent);
      animation: jl-sp .7s linear infinite;
    }
    @keyframes jl-sp { to { transform: rotate(360deg); } }

    /* ── Roadmap scaffold panels ─────────────────────────────── */
    .jl-roadmap    { margin-top: 48px; padding-top: 32px; border-top: 1px solid var(--border); }
    .jl-roadmap-hd { margin-bottom: 18px; }
    .jl-roadmap-title { font-size: 14px; font-weight: 600; letter-spacing: -0.01em; }
    .jl-roadmap-sub   { font-size: 12.5px; color: var(--fg-dim); display: block; margin-top: 4px; }

    .jl-scaffold-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
    .jl-scaffold {
      padding: 22px 24px;
      border: 1px solid var(--border); border-radius: var(--r-lg);
      background: var(--surface); box-shadow: var(--shadow-1);
    }
    .jl-scaffold-priority {
      border-color: oklch(from var(--warn) l calc(c*.5) h / .45);
      background: oklch(from var(--warn) l c h / 0.025);
    }
    .jl-sc-hd    { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin-bottom: 14px; }
    .jl-sc-title { font-size: 14px; font-weight: 600; letter-spacing: -0.01em; display: flex; align-items: center; gap: 8px; }
    .jl-sc-star  { font-size: 11px; color: var(--warn); letter-spacing: 0.02em; font-weight: 500; }
    .jl-sc-badge {
      font-size: 10px; padding: 3px 10px; border-radius: 999px;
      background: var(--surface-3); color: var(--fg-dim);
      border: 1px solid var(--border); white-space: nowrap; flex-shrink: 0;
    }
    .jl-scaffold-priority .jl-sc-badge {
      background: oklch(from var(--warn) l c h / 0.12);
      color: var(--warn);
      border-color: oklch(from var(--warn) l calc(c*.5) h / .4);
    }
    .jl-sc-list   { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 9px; }
    .jl-sc-item   { display: flex; align-items: flex-start; gap: 9px; font-size: 13px; color: var(--fg-muted); line-height: 1.5; }
    .jl-sc-bullet { color: var(--fg-dim); flex-shrink: 0; margin-top: 1px; }

    @media (max-width: 780px) {
      .jl-metrics       { grid-template-columns: repeat(2, 1fr); }
      .jl-scaffold-grid { grid-template-columns: 1fr; }
    }
    @media (max-width: 520px) {
      .jl-metrics { grid-template-columns: 1fr; }
    }
  `}</style>
);

Object.assign(window, { JobListView });
