/* ELEV8 ATS - "Today": a role-aware morning command center. The landing surface.
   One persona switcher flips the whole brief between cohorts (Recruiter / Sales / Ops lead):
   an AI daily brief, a prioritized one-click "get on with it" task queue, and intel cards.
   Designs were generated per-cohort from the real E8DATA; rendered here as a curated daily brief. */
const DStd = window.Stand8DesignSystem_b5c975;

const TD_URGENCY = { now: { label: 'Now', tone: 'danger' }, today: { label: 'Today', tone: 'warning' }, week: { label: 'This week', tone: 'neutral' } };
const TD_STAT_ICON = { danger: 'priority_high', warning: 'schedule', success: 'trending_up', accent: 'bolt', neutral: 'insights' };

const TODAY_COHORTS = [
  {
    cohort: 'recruiter',
    persona: { label: 'Recruiter', role: 'Senior recruiter · Memphis pod', name: 'Sarah Kim', initials: 'SK', icon: 'person_search' },
    brief: 'Morning, Sarah - the FedEx Java search (JO-44219) is hot: Janet Moss asked 2h ago to send your second submittal "as soon as it clears," and Daniel Okafor passed AI screening at $92/hr - his packet is drafted and waiting in Approvals. He also just texted (11 min ago) asking video-or-onsite for Tuesday’s interview. Two fires need you first: JO-44087 (ALSAC Scrum Master) is 4 days from SLA with the sponsor quiet 8 days, and Maya Singh at International Paper is 3 days overdue on Victor Osei + Dana Marquez - a nudge is drafted.',
    briefStats: [
      { label: 'Matches to review', value: '7', tone: 'accent', route: 'matching' },
      { label: 'Approvals to clear', value: '5', tone: 'warning', route: 'approvals' },
      { label: 'SLA at risk', value: 'JO-44087 · 4d', tone: 'danger', route: 'job/JO-44087/overview' },
      { label: 'New applicants', value: '7 · 2 Strong', tone: 'neutral', route: 'applicants' },
    ],
    // R98: this cohort's tasks live in data.js `tasks` (owner: 'Sarah Kim', tk-001..tk-008) - Today filters the live collection.
    cards: [
      { title: 'Hot reqs at risk', icon: 'local_fire_department', route: 'jobs', kind: 'risk', items: [
        { label: 'JO-44087 · Scrum Master · ALSAC', sub: 'Day 41/45 · 4 days to SLA, sponsor quiet 8d', tone: 'danger', route: 'job/JO-44087/overview' },
        { label: 'JO-44150 · ServiceNow Dev · Sedgwick', sub: 'Day 33/45 · client quiet 6 days, 1 interview', tone: 'warning', route: 'jobs' },
        { label: 'JO-44288 · Cloud Security · FedEx', sub: 'Low coverage - only 2 strong matches', tone: 'warning', route: 'job/JO-44288/matching' },
        { label: 'JO-44219 · Sr Java Dev · FedEx', sub: 'On track - 7 matches, 1 submitted, screening 70%', tone: 'success', route: 'job/JO-44219/matching' },
      ] },
      { title: 'Submittals awaiting client', icon: 'rate_review', route: 'submissions', kind: 'list', items: [
        { label: 'Victor Osei → International Paper', sub: 'Feedback overdue 3d · Maya Singh', tone: 'warning', route: 'submissions' },
        { label: 'Dana Marquez → International Paper', sub: 'Feedback overdue 3d · Maya Singh', tone: 'warning', route: 'submissions' },
        { label: 'Aisha Patel → FedEx (JO-44219)', sub: 'Awaiting feedback 1d · Janet holding slots', tone: 'neutral', route: 'candidate/c-patel' },
        { label: 'Nadia Brooks → Elevance (offer out)', sub: 'Decision due Friday · $86/hr remote', tone: 'success', route: 'submissions' },
      ] },
      { title: 'Inbound to triage', icon: 'filter_alt', route: 'applicants', kind: 'list', items: [
        { label: 'Kevin Tran · JO-44219', sub: 'Strong · 91 · Java 17 + Spring, Memphis local', tone: 'success', route: 'job/JO-44219/inbound' },
        { label: 'Grace Liu · JO-44219', sub: 'Strong · 88 · referred by Aisha Patel', tone: 'success', route: 'job/JO-44219/inbound' },
        { label: 'Priya Nair · JO-44310', sub: 'Strong · 87 · SQL + Power BI + Python', tone: 'success', route: 'applicants' },
        { label: '2 knockouts flagged · JO-44219', sub: 'Robert Nash, Tomás Herrera - 1-click reject', tone: 'neutral', route: 'job/JO-44219/inbound' },
      ] },
      { title: 'Voice screening - batch 2', icon: 'support_agent', route: 'voice', kind: 'metric', metric: { value: '14/20', label: 'JO-44219 screening', delta: 'Daniel Okafor qualified · $92/hr', dir: 'up' } },
    ],
  },
  {
    cohort: 'sales',
    persona: { label: 'Sales', role: 'Senior CSM · Mid-South book', name: 'Dana Whitfield', initials: 'DW', icon: 'handshake' },
    brief: 'Morning, Dana. Two fires on your two biggest accounts: FedEx health dropped to 72 (-8) - Tariq Hassan is billing on an expired PO ($291K renewal now Escalated, 21 days no touch) and CSAT slipped to 4.2 after Janet Moss flagged the Q1 cutover slip. You have $1.43M of renewals in flight across 6 consultants and three accounts cooling - ALSAC is At risk (JO-44087 4 days from SLA). On offense: the FedEx Freight spin-off and Elevance’s Carelon team scaling 30% open ~$560K of net-new whitespace your team is already positioned for.',
    briefStats: [
      { label: 'Renewals in flight', value: '$1.43M', tone: 'warning', route: 'renewals' },
      { label: 'GM owned', value: '$3.4M', tone: 'accent', route: 'csms' },
      { label: 'Accounts at risk', value: '3', tone: 'danger', route: 'clients' },
      { label: 'Renewal rate', value: '92%', tone: 'success', route: 'renewals' },
    ],
    // R98: this cohort's tasks live in data.js `tasks` (owner: 'Dana Whitfield', tk-009..tk-016) - Today filters the live collection.
    cards: [
      { title: 'Your book at a glance', icon: 'account_balance', route: 'csms', kind: 'metric', metric: { value: '$3.4M', label: 'GM owned · 92% renewal rate', delta: '+$0.1M QoQ · capacity 82%', dir: 'up' } },
      { title: 'Renewals in flight', icon: 'autorenew', route: 'renewals', kind: 'list', items: [
        { label: 'Tariq Hassan · FedEx - Escalated', sub: '$291K · PO expired · At risk · 21d', tone: 'danger', route: 'engagement/eng-fedex-cloud' },
        { label: 'Devon Carter · First Horizon - Sent', sub: '$250K · PO expiring 91% · awaiting signature', tone: 'warning', route: 'consultant/cons-carter' },
        { label: 'Hannah Cole · ALSAC - Drafting', sub: '$239K · 70% extension intent', tone: 'warning', route: 'consultant/cons-cole' },
        { label: 'Tasha Green · Elevance - Drafting', sub: '$214K · care overdue 17d', tone: 'warning', route: 'consultant/cons-green' },
      ] },
      { title: 'Accounts to protect', icon: 'health_and_safety', route: 'clients', kind: 'risk', items: [
        { label: 'ALSAC - At risk', sub: 'JO-44087 4d to SLA · sponsor quiet 8d', tone: 'danger', route: 'client/cl-alsac' },
        { label: 'AutoZone - Cooling', sub: 'Acceptance down 18% · 9d no touch', tone: 'warning', route: 'client/cl-autozone' },
        { label: 'International Paper - Feedback overdue', sub: 'Maya Singh 3d overdue on 2 submittals', tone: 'warning', route: 'clients' },
        { label: 'FedEx - Healthy but slipping', sub: 'Engagement health 72 (-8) · CSAT 4.2', tone: 'warning', route: 'engagement/eng-fedex-cloud' },
      ] },
      { title: 'New-business signals', icon: 'radar', route: 'client/cl-fedex', kind: 'list', items: [
        { label: 'FedEx Freight spin-off', sub: 'Net-new IT & security hiring, 18 mo · $420K', tone: 'success', route: 'client/cl-fedex' },
        { label: "Int’l Paper · DS Smith SAP", sub: 'Omar Haddad opened 4 SAP reqs · intent 88', tone: 'success', route: 'clients' },
        { label: 'Elevance · Carelon', sub: 'Priscilla Vaughn scaling 30% · ML req approved', tone: 'success', route: 'client/cl-elevance' },
        { label: 'Buckman - land & expand', sub: 'First MSA Mar 2026 · first tech req', tone: 'accent', route: 'clients' },
      ] },
    ],
  },
  {
    cohort: 'ops',
    persona: { label: 'Ops lead', role: 'Delivery & back-office lead', name: 'Renee Castellanos', initials: 'RC', icon: 'account_balance' },
    brief: "Money’s on the table in two places this morning, Renee. Tariq Hassan’s FedEx PO is expired and escalated but he’s still billing at $140/hr with zero coverage, and Devon Carter’s First Horizon PO is 91% burned with a renewal sitting at the client (and a 12-day-late invoice to clear first). 10 timesheets are awaiting approval - two with exceptions: Marco Flores hit 13h Wednesday in California (OT/DT) and Tariq Hassan logged 45h (weekly OT). One QuickBooks sync is hard-errored (Omar Haddad PO number mismatch) and needs a human fix before billing reconciles.",
    briefStats: [
      { label: 'Timesheets to approve', value: '10', tone: 'warning', route: 'time' },
      { label: 'POs at/over 90%', value: '2', tone: 'danger', route: 'engagements' },
      { label: 'Sync errors', value: '1', tone: 'danger', route: 'syncqueue' },
      { label: 'PO revenue at risk', value: '$541K', tone: 'accent', route: 'engagements' },
    ],
    // R98: this cohort's tasks live in data.js `tasks` (owner: 'Renee Castellanos', tk-017..tk-024) - Today filters the live collection.
    cards: [
      { title: 'Timesheet approval queue', icon: 'fact_check', route: 'time', kind: 'list', items: [
        { label: 'Marco Flores - 13h Wed (CA OT/DT)', sub: 'Exception · San Jose remote, cutover', tone: 'warning', route: 'time' },
        { label: 'Tariq Hassan - 45h, 9h Wed/Thu', sub: 'Exception · weekly OT, release cutover', tone: 'warning', route: 'time' },
        { label: 'Angela Park - not submitted', sub: 'Blocking billing', tone: 'danger', route: 'time' },
        { label: '7 more submitted · clean to book', sub: 'Renee is approver', tone: 'success', route: 'time' },
      ] },
      { title: 'PO burn-down watch', icon: 'battery_alert', route: 'engagements', kind: 'risk', items: [
        { label: 'Tariq Hassan · FedEx - 100% EXPIRED', sub: 'Escalated · still billing $140/hr · $291K', tone: 'danger', route: 'consultant/cons-hassan' },
        { label: 'Devon Carter · First Horizon - 91%', sub: 'Renewal sent · $250K · invoice 12d late', tone: 'warning', route: 'consultant/cons-carter' },
        { label: "Angela Park · Int’l Paper - 78%", sub: 'Depleting · renewal drafting · $198K', tone: 'warning', route: 'consultant/cons-park' },
        { label: 'FedEx Cloud Modernization - 71%', sub: 'Exhausts Oct 18 · ~6-week gap', tone: 'warning', route: 'engagement/eng-fedex-cloud' },
      ] },
      { title: 'Integration sync health', icon: 'sync', route: 'syncqueue', kind: 'list', items: [
        { label: 'Omar Haddad PO → QuickBooks', sub: 'ERROR · PO number mismatch · 1h ago', tone: 'danger', route: 'syncqueue' },
        { label: 'Daniel Okafor screening → Bullhorn', sub: 'Pending · queued', tone: 'neutral', route: 'syncqueue' },
        { label: 'REQ-1096 → Bullhorn', sub: 'Pending · queued', tone: 'neutral', route: 'syncqueue' },
        { label: '4 records synced', sub: 'Submittals, job order, candidate', tone: 'success', route: 'syncqueue' },
      ] },
      { title: 'This cycle - billings booked', icon: 'paid', route: 'commissions', kind: 'metric', metric: { value: '10 sheets', label: 'Awaiting approval', delta: 'pending margin unlocks rep commission', dir: 'up' } },
    ],
  },
];

/* ============ R81: the brief derives from live data ============
   Curated seed copy stays as the rich sample; these helpers compute the same stats and a
   deterministic brief paragraph from the LIVE stores (queue, applicants, jobs, timesheets,
   Watchtower flags) - honestly labeled when shown. */
function tdEffCur(cid) {
  try { const s = localStorage.getItem('e8-ts-' + cid); if (s) return JSON.parse(s); } catch (e) {}
  const sh = (window.E8DATA.timesheets || {})[cid];
  return sh ? sh.current : null;
}
function tdLiveFacts() {
  const D = window.E8DATA;
  const approvals = window.E8Queue ? window.E8Queue.counts().review : 0;
  let apOverlay = {};
  try { apOverlay = JSON.parse(localStorage.getItem('e8-ap-screen-v1')) || {}; } catch (e) {}
  const newApps = (D.applications || []).filter((a) => a.stage === 'New');
  const strongNew = newApps.filter((a) => (apOverlay[a.id] ? apOverlay[a.id].aiScreen : a.aiScreen) === 'Strong').length;
  const slaJob = (D.jobs || []).find((j) => j.health && j.health.tone === 'danger');
  const matchesToReview = window.E8HomeData
    ? window.E8HomeData.countMatchesToReview(D, (jobId) => window.E8Match ? window.E8Match.decisions(jobId) : {})
    : (D.matches || []).filter((m) => {
      if (m.jobId && !(D.jobs || []).some((job) => job.id === m.jobId)) return false;
      const dec = m.jobId && window.E8Match ? window.E8Match.decisions(m.jobId)[m.id] : null;
      return (dec ? dec.state : m.state) === 'review';
    }).length;
  const tsPending = (D.engagements || []).filter((e) => (D.timesheets || {})[e.cid]).map((e) => tdEffCur(e.cid)).filter((c) => c && c.status === 'submitted').length;
  const poHot = (D.engagements || []).filter((e) => e.po === 'expired' || e.po === 'expiring' || (e.poBurn || 0) >= 90).length;
  const flags = window.E8Watch ? window.E8Watch.findings() : [];
  return { approvals, newApps: newApps.length, strongNew, slaJob, matchesToReview, tsPending, poHot, flags };
}
function tdLiveStats(cohort, f) {
  if (cohort === 'recruiter') return [
    { label: 'Matches to review', value: String(f.matchesToReview), tone: 'accent', route: 'matching' },
    { label: 'Approvals to clear', value: String(f.approvals), tone: f.approvals ? 'warning' : 'success', route: 'approvals' },
    { label: 'SLA at risk', value: f.slaJob ? f.slaJob.id + ' · ' + Math.max(0, f.slaJob.days - f.slaJob.day) + 'd' : 'None', tone: f.slaJob ? 'danger' : 'success', route: f.slaJob ? 'job/' + f.slaJob.id + '/overview' : 'jobs' },
    { label: 'New applicants', value: f.newApps + (f.strongNew ? ' · ' + f.strongNew + ' Strong' : ''), tone: 'neutral', route: 'applicants' },
  ];
  if (cohort === 'ops') return [
    { label: 'Timesheets to approve', value: String(f.tsPending), tone: f.tsPending ? 'warning' : 'success', route: 'time' },
    { label: 'POs at/over 90%', value: String(f.poHot), tone: f.poHot ? 'danger' : 'success', route: 'engagements' },
    { label: 'Approvals to clear', value: String(f.approvals), tone: f.approvals ? 'warning' : 'success', route: 'approvals' },
    { label: 'Watchtower flags', value: String(f.flags.length), tone: f.flags.length ? 'accent' : 'success', route: 'approvals' },
  ];
  return null; // sales cohort keeps its curated stats (renewal economics have no live source yet)
}
function tdLiveBrief(name, cohort, f) {
  const bits = [];
  if (f.approvals) bits.push(f.approvals + ' approval' + (f.approvals > 1 ? 's are' : ' is') + ' waiting in your queue');
  if (cohort !== 'ops' && f.slaJob) bits.push(f.slaJob.id + ' (' + f.slaJob.title + ') is ' + Math.max(0, f.slaJob.days - f.slaJob.day) + ' days from SLA');
  if (cohort === 'ops' && f.tsPending) bits.push(f.tsPending + ' timesheet' + (f.tsPending > 1 ? 's await' : ' awaits') + ' approval - booking ' + (f.tsPending > 1 ? 'them' : 'it') + ' moves billings and commissions');
  if (cohort === 'ops' && f.poHot) bits.push(f.poHot + ' PO' + (f.poHot > 1 ? 's are' : ' is') + ' at or past 90% burn');
  if (cohort !== 'ops' && f.strongNew) bits.push(f.strongNew + ' Strong applicant' + (f.strongNew > 1 ? 's' : '') + ' landed in triage');
  if (f.flags.length) bits.push('Watchtower flagged ' + f.flags.length + ' risk' + (f.flags.length > 1 ? 's' : '') + ' on its last sweep' + (f.flags[0] ? ' - top: ' + f.flags[0].title : ''));
  if (!bits.length) return 'Morning, ' + name + ' - all clear right now. Nothing is waiting on you: no approvals, no SLA risks, no flags from the last Watchtower sweep.';
  return 'Morning, ' + name + ' - here is where things genuinely stand. ' + bits.map(function (b, i) { return i === 0 ? b.charAt(0).toUpperCase() + b.slice(1) : b; }).join('. ') + '.';
}

function tdResolvedPinTargets(personaName) {
  if (!window.E8Workspace) return [];
  const pref = window.E8Workspace.get(personaName);
  const set = pref.pinSets.find((pinSet) => pinSet.id === pref.activePinSetId) || pref.pinSets[0];
  return set ? set.targets.filter((target) => window.E8Workspace.resolve(target)) : [];
}
function tdPrioritizeTodayTasks(tasks, targets) {
  if (window.E8HomeData) return window.E8HomeData.prioritizeTasks(tasks, targets);
  const related = (task) => targets.some((target) => (
    (target.type === 'task' && target.id === task.id)
    || (task.ref && target.type === task.ref.type && target.id === task.ref.id)
  ));
  return tasks.filter(related).concat(tasks.filter((task) => !related(task)));
}
const TD_WATCH_KINDS_BY_COHORT = {
  recruiter: ['Overdue feedback', 'Stalled req'],
  sales: ['Overdue feedback', 'PO burn', 'Stalled renewal'],
  ops: ['Missing timesheet', 'PO burn', 'Stalled renewal'],
};
function tdWaitingSignals(cohort, f) {
  const signals = [];
  const add = (signal) => {
    if (signals.length < 4 && !signals.some((item) => item.route === signal.route || item.title === signal.title)) signals.push(signal);
  };
  if (cohort === 'ops') {
    if (f.tsPending) add({ title: f.tsPending + ' timesheet' + (f.tsPending === 1 ? '' : 's') + ' awaiting approval', sub: 'Submitted sheets are waiting in the live approval queue', tone: 'warning', route: 'time' });
    if (f.poHot) add({ title: f.poHot + ' PO' + (f.poHot === 1 ? '' : 's') + ' at or past 90% burn', sub: 'Derived from current engagement PO status and burn', tone: 'danger', route: 'engagements' });
  } else if (cohort === 'recruiter' && f.slaJob) {
    add({
      title: f.slaJob.id + ' is ' + Math.max(0, f.slaJob.days - f.slaJob.day) + ' days from SLA',
      sub: f.slaJob.title + ' · ' + f.slaJob.client,
      tone: 'danger',
      route: 'job/' + f.slaJob.id + '/overview',
    });
  }
  if (f.approvals) add({ title: f.approvals + ' approval' + (f.approvals === 1 ? '' : 's') + ' waiting', sub: 'Existing review queue requires a decision', tone: 'warning', route: 'approvals' });
  const kinds = TD_WATCH_KINDS_BY_COHORT[cohort] || [];
  kinds.forEach((kind) => {
    f.flags.filter((flag) => flag.kind === kind).forEach((flag) => add({
      title: flag.title,
      sub: flag.detail || 'Flagged by the latest Watchtower sweep',
      tone: flag.severity === 'danger' ? 'danger' : 'warning',
      route: flag.route || 'approvals',
    }));
  });
  return signals;
}

/* R98: rows come from the shared `tasks` collection (data.js). Deep links resolve via
   tkTaskPath (route wins, then the typed ref) and the why-line now lives in `sub`. */
function TodayTask({ t, done, onToggle, primary }) {
  const path = window.tkTaskPath ? window.tkTaskPath(t) : null;
  const go = () => { if (path) navigate(path); };
  return (
    <div className={'e8-td-task' + (done ? ' is-done' : '')}>
      <button type="button" className="e8-td-check" onClick={onToggle} aria-label={done ? 'Mark not done' : 'Mark done'} title={done ? 'Done' : 'Mark done'}>
        <span className="material-symbols-outlined">{done ? 'check_circle' : 'radio_button_unchecked'}</span>
      </button>
      <span className={'e8-td-task-ic ' + TD_URGENCY[t.urgency].tone}><span className="material-symbols-outlined" aria-hidden="true">{t.icon || (window.TK_KIND_ICON || {})[t.kind] || 'task_alt'}</span></span>
      <div className="e8-td-task-main" onClick={go} role={path ? 'button' : undefined} tabIndex={path ? 0 : undefined} onKeyDown={(e) => { if (path && (e.key === 'Enter' || e.key === ' ')) { e.preventDefault(); go(); } }}>
        <div className="e8-td-task-title">{t.title}{t.aiAssisted ? <span className="e8-td-ai" title="AI teed this up for you"><span className="material-symbols-outlined" aria-hidden="true">auto_awesome</span></span> : null}</div>
        {t.sub ? <div className="e8-td-task-why">{t.sub}</div> : null}
      </div>
      {path ? <DStd.Button variant={primary && !done ? 'primary' : 'secondary'} size="sm" onClick={go}>{t.actionLabel || 'Open'}</DStd.Button> : null}
    </div>
  );
}

/* ---- Redeployment radar - shared by Today (recruiter persona) and the Dashboard widget.
   Consultants rolling off ELEV8 engagements soonest, each with verified history signals
   (roll-off chip, client pulse, skills) and a deep link into the best-fit job's matching tab. */
const RDR_MONTHS = { Jan: 0, Feb: 1, Mar: 2, Apr: 3, May: 4, Jun: 5, Jul: 6, Aug: 7, Sep: 8, Oct: 9, Nov: 10, Dec: 11 };
/* The app's fiction date - the Today/Dashboard headers say "Friday, June 12" (2026). */
const RDR_TODAY = new Date(2026, 5, 12);
function rdrEndsDate(ends) {
  const m = /^([A-Z][a-z]{2})\s+(\d{4})$/.exec(String(ends || '').trim());
  return m && RDR_MONTHS[m[1]] != null ? new Date(+m[2], RDR_MONTHS[m[1]], 1) : null;
}
/* Top consultants by soonest engagement end, where redeploy risk is High or the end is near.
   Shared with the job matching tab's redeployment strip (window export below). */
function rdrRollOffs(limit) {
  const cutoff = new Date(RDR_TODAY.getTime() + 45 * 86400000);
  return (window.E8DATA.engagements || [])
    .map((e) => ({ e, when: rdrEndsDate(e.ends) }))
    .filter((x) => x.when && ((x.e.redeploy && x.e.redeploy.label === 'High') || x.when <= cutoff))
    .sort((a, b) => a.when - b.when)
    .slice(0, limit || 4)
    .map((x) => x.e);
}
/* Tense-aware roll-off chip label: month-granularity ends dates before the fiction date read as past. */
function rdrRolloffLabel(ends) {
  const when = rdrEndsDate(ends);
  return (when && when < RDR_TODAY ? 'Rolled off ' : 'Rolls off ') + ends;
}
/* Client pulse: the managed program's health score when the engagement belongs to one,
   else a band derived from the engagement's care sentiment. */
function rdrPulse(e) {
  const prog = e.projectId ? (window.E8DATA.managedEngagements || []).find((m) => m.id === e.projectId) : null;
  if (prog && prog.health) return prog.health.score;
  return e.sentiment.tone === 'success' ? 91 : e.sentiment.tone === 'warning' ? 72 : 46;
}

function RedeployRadarItem({ e }) {
  const D = window.E8DATA;
  const matches = window.E8Match ? window.E8Match.redeployJobs(e.cid, e.skills) : [];
  const top = matches[0];
  const pulse = rdrPulse(e);
  const prog = e.projectId ? (D.managedEngagements || []).find((m) => m.id === e.projectId) : null;
  const team = prog ? (prog.name.split(' - ')[1] || prog.name) : e.role;
  const tier = top ? ((DStd.MATCH_TIERS || []).find((t) => t.id === top.tier) || {}) : {};
  const ctaLabel = !top
    ? 'Find roles'
    : top.tier === 1
      ? (tier.label || 'Very strong match') + ' · ' + top.job.title
      : matches.length + ' matched opening' + (matches.length > 1 ? 's' : '');
  const open = () => navigate('consultant/' + e.cid);
  return (
    <div className="e8-rdr-item" role="button" tabIndex={0} onClick={open}
      onKeyDown={(ev) => {
        if (ev.target !== ev.currentTarget) return; /* keep Enter/Space on the nested CTA button */
        if (ev.key === 'Enter' || ev.key === ' ') { ev.preventDefault(); open(); }
      }}>
      <div className="e8-rdr-row">
        <DStd.Avatar name={e.consultant} size="lg" />
        <div className="e8-rdr-id">
          <div className="e8-rdr-name">{e.consultant} <DStd.E8Mark title="ELEV8-verified consultant" /></div>
          <div className="e8-rdr-role">{e.role}</div>
        </div>
        <span className="e8-rdr-rolloff" title={'Engagement end date ' + e.ends}>{rdrRolloffLabel(e.ends)}</span>
      </div>
      <div className="e8-rdr-meta">
        <span className="material-symbols-outlined" aria-hidden="true">work_history</span>
        <span className="e8-rdr-meta-t">{e.client} · {team} · {e.tenure}</span>
      </div>
      <div className="e8-rdr-pulserow">
        <span className={'e8-rdr-pulse' + (pulse < 60 ? ' risk' : pulse < 80 ? ' warn' : '')}>{pulse}</span>
        <span className="e8-rdr-pulse-l">client pulse</span>
        <span style={{ flex: 1 }}></span>
        <DStd.SkillChips skills={e.skills} max={3} />
      </div>
      <button type="button" className="e8-rdr-cta"
        onClick={(ev) => { ev.stopPropagation(); top ? navigate('job/' + top.jobId + '/matching') : navigate('jobs'); }}>
        <span className="material-symbols-outlined" aria-hidden="true">auto_awesome</span>
        <span className="e8-rdr-cta-t">{ctaLabel}</span>
      </button>
    </div>
  );
}

function RedeployRadarCard() {
  const [, setTick] = React.useState(0);
  React.useEffect(() => (window.E8Events ? window.E8Events.subscribe(['match:ranked', 'match:decided'], () => setTick((t) => t + 1)) : undefined), []);
  const rows = rdrRollOffs(4);
  if (!rows.length) return null;
  return (
    <div className="e8-card e8-rdr">
      <div className="e8-rdr-head">
        <DStd.E8Mark size="lg" />
        <div className="e8-rdr-head-main">
          <div className="e8-rdr-title">Redeployment radar</div>
          <div className="e8-rdr-sub">Consultants rolling off ELEV8 engagements - verified history, zero sourcing cost</div>
        </div>
        <button type="button" className="e8-rdr-bench" onClick={() => navigate('consultants/bench')}>View bench →</button>
      </div>
      <div className="e8-rdr-grid">
        {rows.map((e) => <RedeployRadarItem key={e.id} e={e} />)}
      </div>
    </div>
  );
}

function TodayCard({ c }) {
  return (
    <div className="e8-card e8-td-card">
      <div className="e8-td-card-head">
        <span className="e8-td-card-ic"><span className="material-symbols-outlined" aria-hidden="true">{c.icon}</span></span>
        <span className="e8-td-card-title">{c.title}</span>
        <button type="button" className="e8-td-card-all" onClick={() => navigate(c.route)}>Open →</button>
      </div>
      {c.kind === 'metric' && c.metric ? (
        <div className="e8-td-metric">
          <div className="e8-td-metric-v">{c.metric.value}</div>
          <div className="e8-td-metric-l">{c.metric.label}</div>
          {c.metric.delta ? <div className={'e8-td-metric-d ' + (c.metric.dir || 'flat')}>{c.metric.delta}</div> : null}
        </div>
      ) : (
        <div className="e8-td-card-list">
          {(c.items || []).map((it, i) => (
            <a key={i} className="e8-td-item" onClick={() => it.route && navigate(it.route)}>
              <span className={'e8-td-dot ' + (it.tone || 'neutral')}></span>
              <span className="e8-td-item-main">
                <span className="e8-td-item-l">{it.label}</span>
                {it.sub ? <span className="e8-td-item-s">{it.sub}</span> : null}
              </span>
            </a>
          ))}
        </div>
      )}
    </div>
  );
}

function TodaySignals({ signals }) {
  return (
    <section className="e8-card e8-td-signals" aria-labelledby="e8-td-waiting-title">
      <div className="e8-td-section-head">
        <div>
          <span className="e8-sectionlabel">Live operations</span>
          <h2 id="e8-td-waiting-title">Waiting and at risk</h2>
        </div>
        <span className="e8-td-section-count">{signals.length || 'All clear'}</span>
      </div>
      {signals.length ? <div className="e8-td-signal-grid">{signals.map((signal, index) => (
        <button key={signal.title + index} type="button" className="e8-td-signal" onClick={() => navigate(signal.route)}>
          <span className={'e8-td-dot ' + signal.tone}></span>
          <span><b>{signal.title}</b><small>{signal.sub}</small></span>
          <span className="material-symbols-outlined" aria-hidden="true">chevron_right</span>
        </button>
      ))}</div> : <p className="e8-td-clear">No waiting or at-risk signals in live facts or the latest Watchtower sweep.</p>}
    </section>
  );
}

function TodayScreen() {
  const D = window.E8DATA;
  const { showToast } = React.useContext(window.E8Ctx);
  window.useWorkspaceVersion();
  // R101: the persona is shared (localStorage['e8-persona']) so Consultants + the dashboard widget
  // read the same active view. Today still owns its switcher; it just seeds from + writes the shared key.
  const [idx, setIdx] = React.useState(() => {
    try {
      const p = window.e8ActivePersona ? window.e8ActivePersona() : null;
      if (p) { const i = TODAY_COHORTS.findIndex((c) => c.persona.name === p.name); if (i > -1) return i; }
    } catch (e) {}
    return 0;
  });
  // R98 Task 6: demo keeps the curated showcase brief; empty/scale derive from live data so
  // the landing page reflects the actual dataset (an empty desk reads "all clear", not demo prose).
  const [live, setLive] = React.useState(!!(window.E8_DATA_MODE && window.E8_DATA_MODE !== 'demo'));
  const [liveAt, setLiveAt] = React.useState(null);
  const [briefOpen, setBriefOpen] = React.useState(false);
  const [, setTick] = React.useState(0);
  // R98: 'store:changed' keeps the queue honest when tasks are completed/snoozed/added on #/tasks.
  React.useEffect(() => (window.E8Events ? window.E8Events.subscribe(['queue:changed', 'watch:swept', 'timesheet:decided', 'store:changed'], () => setTick((t) => t + 1)) : undefined), []);
  const facts = tdLiveFacts();
  const c = TODAY_COHORTS[idx];
  const liveStats = live ? tdLiveStats(c.cohort, facts) : null;
  const briefText = live ? tdLiveBrief(c.persona.name.split(' ')[0], c.cohort, facts) : c.brief;
  const watchCard = {
    title: 'Watchtower flags', icon: 'radar', route: 'approvals', kind: 'list',
    items: facts.flags.length ? facts.flags.slice(0, 4).map((f) => ({ label: f.title, sub: (f.detail || '').slice(0, 80), tone: f.severity === 'danger' ? 'danger' : 'warning', route: f.route })) : [{ label: 'No flags on the last sweep', sub: 'Watchtower sweeps on load and every 15 minutes', tone: 'success', route: 'agents' }],
  };
  const setCohort = (i) => { setIdx(i); if (window.e8SetPersona && TODAY_COHORTS[i]) window.e8SetPersona(TODAY_COHORTS[i].persona.name); };
  /* R98: the queue is an owner-filtered view of the shared `tasks` collection.
     Snoozed tasks drop out of Today; done ones stay visible (struck through) so the
     ring keeps meaning. Completes write through E8Store and survive reload. */
  const myTasks = (D.tasks || []).filter((t) => t.owner === c.persona.name && t.state !== 'snoozed');
  const prioritizedTasks = tdPrioritizeTodayTasks(myTasks, tdResolvedPinTargets(c.persona.name));
  const needsNow = prioritizedTasks.filter((t) => t.state === 'open' && (t.urgency === 'now' || t.urgency === 'today')).slice(0, 3);
  const needsNowIds = new Set(needsNow.map((t) => t.id));
  const remainingTasks = prioritizedTasks.filter((t) => !needsNowIds.has(t.id));
  const waitingSignals = tdWaitingSignals(c.cohort, facts);
  const toggle = (t) => {
    if (!window.tkComplete || !window.tkReopen) { console.warn('ELEV8: task helpers missing - is screens-tasks.jsx loaded?'); return; }
    if (t.state === 'done') { window.tkReopen(t, showToast); return; }
    window.tkComplete(t, showToast);
  };
  const doneCount = myTasks.filter((t) => t.state === 'done').length;
  const greeting = (() => 'Good morning')();
  const groups = [
    { id: 'now', label: 'Right now', tasks: remainingTasks.filter((t) => t.urgency === 'now') },
    { id: 'today', label: 'Today', tasks: remainingTasks.filter((t) => t.urgency === 'today') },
    { id: 'week', label: 'This week', tasks: remainingTasks.filter((t) => t.urgency === 'week') },
  ].filter((g) => g.tasks.length);
  const briefSummary = live
    ? (waitingSignals.length ? waitingSignals[0].title + (waitingSignals.length > 1 ? ' · ' + (waitingSignals.length - 1) + ' more live signal' + (waitingSignals.length > 2 ? 's' : '') : '') : 'No new waiting or at-risk signals.')
    : c.cohort === 'recruiter'
      ? 'A FedEx submittal is ready, with an SLA rescue and overdue client feedback next.'
      : c.cohort === 'sales'
        ? 'FedEx renewal exposure and cooling accounts need attention; whitespace remains open.'
        : 'An expired PO, timesheet exceptions, and a sync error need attention.';

  return (
    <React.Fragment>
      <Topbar crumbs={[{ label: 'Work queue' }]} />
      <div className="e8-content">
        <div className="e8-page">
          <div className="e8-td-head">
            <div>
              <h1 className="e8-h1">Work queue</h1>
              <div className="e8-pagehead-sub">{greeting}, {c.persona.name.split(' ')[0]} · {c.persona.role} · Friday, June 12</div>
            </div>
            <div className="e8-td-switch" role="tablist" aria-label="Viewing as">
              <span className="e8-td-switch-lab">Viewing as</span>
              {TODAY_COHORTS.map((co, i) => (
                <button key={co.cohort} type="button" role="tab" aria-selected={i === idx} className={'e8-td-persona' + (i === idx ? ' on' : '')} onClick={() => setCohort(i)}>
                  <span className="e8-td-persona-av" aria-hidden="true">{co.persona.initials}</span>
                  <span className="material-symbols-outlined" aria-hidden="true">{co.persona.icon}</span>
                  {co.persona.label}
                </button>
              ))}
            </div>
          </div>

          <section className="e8-card e8-td-needs" aria-labelledby="e8-td-needs-title">
            <div className="e8-td-section-head">
              <div>
                <span className="e8-sectionlabel">Action queue</span>
                <h2 id="e8-td-needs-title">Needs you now</h2>
              </div>
              <span className="e8-td-section-count">{needsNow.length} open</span>
            </div>
            {needsNow.length ? <div className="e8-td-needs-list">
              {needsNow.map((t, index) => <TodayTask key={t.id} t={t} done={false} onToggle={() => toggle(t)} primary={index === 0} />)}
            </div> : <p className="e8-td-clear">All clear - no open now or today tasks.</p>}
          </section>

          <TodaySignals signals={waitingSignals} />

          {/* AI daily brief */}
          <section className={'e8-card e8-td-brief' + (briefOpen ? ' is-open' : '')}>
            <div className="e8-td-brief-head">
              <span className="e8-sectionlabel">Changes since yesterday</span>
              <DStd.ProvenanceBadge kind={live ? 'advised' : 'drafted'} />
              <span className="e8-td-brief-when">{live ? 'derived from live data · ' + (liveAt ? new Date(liveAt).toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' }) : 'just now') + (liveStats ? '' : ' · stats: curated sample (no live source for this role yet)') : 'curated sample · briefed at 7:00 am'}</span>
              <span className="e8-td-brief-actions">
                <DStd.Button variant="secondary" size="sm" icon={live ? 'history_edu' : 'bolt'} onClick={() => { if (!live) { setLiveAt(new Date().toISOString()); if (window.E8Watch) window.E8Watch.sweep({ force: true }).then(() => setTick((t) => t + 1)).catch(() => {}); } setLive(!live); }}>
                  {live ? 'Show curated sample' : 'Rebrief from live data'}
                </DStd.Button>
                <DStd.Button variant="ghost" size="sm" icon={briefOpen ? 'expand_less' : 'expand_more'} aria-expanded={briefOpen} onClick={() => setBriefOpen(!briefOpen)}>
                  {briefOpen ? 'Hide AI brief' : 'Read AI brief'}
                </DStd.Button>
              </span>
            </div>
            <p className="e8-td-brief-summary">{briefSummary}</p>
            {briefOpen ? <div className="e8-td-brief-details">
              <p className="e8-td-brief-text">{briefText}</p>
              <div className="e8-td-stats">
                {(liveStats || c.briefStats).map((s, i) => (
                  <window.StatTile key={i} tone={s.tone || 'neutral'} icon={TD_STAT_ICON[s.tone] || 'insights'}
                    value={s.value} label={s.label} route={s.route} />
                ))}
              </div>
            </div> : null}
          </section>

          <div className="e8-td-grid">
            {/* The remaining queue stays complete without repeating the top three actions. */}
            <div className="e8-td-queue">
              <div className="e8-td-queue-head">
                <span className="e8-td-queue-title"><span className="material-symbols-outlined" aria-hidden="true">checklist</span>Full queue</span>
                <span className="e8-td-progwrap" title={doneCount + ' of ' + myTasks.length + ' done'}>
                  <svg className="e8-td-ring" viewBox="0 0 36 36" width="32" height="32" aria-hidden="true">
                    <circle className="e8-td-ring-bg" cx="18" cy="18" r="15.5" />
                    <circle className="e8-td-ring-fg" cx="18" cy="18" r="15.5" style={{ strokeDasharray: (2 * Math.PI * 15.5).toFixed(1), strokeDashoffset: ((1 - doneCount / (myTasks.length || 1)) * 2 * Math.PI * 15.5).toFixed(1) }} />
                  </svg>
                  <span className="e8-td-progress tnum">{doneCount}<span className="e8-td-progress-tot">/{myTasks.length}</span></span>
                </span>
              </div>
              {groups.length ? groups.map((g) => (
                <div key={g.id} className={'e8-td-group e8-td-group-' + g.id}>
                  <div className="e8-td-group-h"><span className={'e8-td-urg ' + TD_URGENCY[g.tasks[0].urgency].tone}>{g.label}</span><span className="e8-td-group-n">{g.tasks.length}</span></div>
                  {g.tasks.map((t) => <TodayTask key={t.id} t={t} done={t.state === 'done'} onToggle={() => toggle(t)} primary={false} />)}
                </div>
              )) : (
                <div className="e8-td-queue-empty">No remaining tasks. Snoozed and completed tasks live in <button type="button" onClick={() => navigate('tasks')}>Tasks</button>.</div>
              )}
            </div>

            {/* Secondary intelligence stays below the action, risk, context, and brief hierarchy. */}
            <div className="e8-td-cards">
              <TodayCard c={watchCard} />
              {c.cohort === 'recruiter' ? <RedeployRadarCard /> : null}
            </div>
          </div>

          {/* Curated cohort cards are a demo narrative device. They flow full-width below the
              queue as a horizontal grid (not a tall stack beside it) so the primary task queue
              keeps the anchor and these list cards get the width they scan best in. */}
          {(window.E8_DATA_MODE === 'demo' || !window.E8_DATA_MODE) && c.cards.length ? (
            <div className="e8-td-intel-cards">
              {c.cards.map((card, i) => <TodayCard key={i} c={card} />)}
            </div>
          ) : null}
        </div>
      </div>
    </React.Fragment>
  );
}

Object.assign(window, { TodayScreen, RedeployRadarCard, rdrRollOffs, rdrRolloffLabel, tdLiveStats, tdPrioritizeTodayTasks, tdWaitingSignals });
