/* =========================================================
   screens-b.jsx — Mailroom, Project Room, Client Room, Staff, Receipts
   ========================================================= */

/* =========================================================
   Backoffice Today pages — functional cockpits
   ========================================================= */
const FINANCE_ACTIONS = [
  { tone:"green", title:"Approve 6 ready bills", body:"Vendor, project, cost code, amount, due date, and source evidence are matched.", primary:"Review packet", desk:"bills" },
  { tone:"green", title:"Approve 12 employee hours", body:"Timekeeper staged hours from site messages, timesheet photos, and replies.", primary:"Review hours", desk:"bills" },
  { tone:"amber", title:"Answer Jay Patel OT exception", body:"4.5 hours of overtime is outside the normal rule and needs your call.", primary:"Answer", desk:"bills" },
  { tone:"amber", title:"Send Carlos missing-hours follow-up", body:"Message is drafted in English and Spanish. No hours will be entered until he replies.", primary:"Send follow-up", desk:"bills" },
];

const FINANCE_ALERTS = [
  { tone:"amber", project:"Oakwood", title:"No new bills in 9 days", body:"Framing work continued but no vendor bills have landed. Bookkeeper can ask vendors for missing invoices.", action:"Ask vendors" },
  { tone:"rust", project:"Glenrose", title:"Duplicate bill risk", body:"Sherwin-Williams invoice matches a bill already in the batch. It is held until you confirm.", action:"Review duplicate" },
  { tone:"amber", project:"St Leonards", title:"Rental bill needs split", body:"One rental invoice appears to cover two houses and needs project allocation.", action:"Split bill" },
];

const FINANCE_COMPLETED = [
  { who:"Bookkeeper", title:"10 bills scanned", detail:"6 ready, 3 mapping questions, 1 duplicate risk." },
  { who:"Timekeeper", title:"13 time entries reviewed", detail:"12 ready and 1 overtime exception held." },
  { who:"Accounting Analyst", title:"Framing labor answer drafted", detail:"Pulled bills, hours, and receipts for Glenrose overage." },
  { who:"Bookkeeper", title:"QuickBooks batch staged", detail:"Nothing posts until you approve from this page or chat." },
];

const SCHEDULING_ACTIONS = [
  { tone:"green", title:"Approve 3 real schedule dates", body:"Demo, underpinning, and plumbing have reliable dates and source receipts.", primary:"Approve dates", desk:"schedule" },
  { tone:"amber", title:"Chase roofing and windows", body:"Lead times are placeholders until those trades answer.", primary:"Send chases", desk:"quotes" },
  { tone:"amber", title:"Ask St Leonards site super for daily logs", body:"No daily log has come in for 2 days. Field Intake wrote the follow-up.", primary:"Send follow-up", desk:"field" },
  { tone:"rust", title:"Review Humber schedule hold", body:"Field photos show site clearing ahead of underpinning sign-off.", primary:"Review hold", desk:"schedule" },
];

const SCHEDULING_ALERTS = [
  { tone:"rust", project:"St Leonards", title:"No daily log in 2 days", body:"Daily log follow-up is ready. Schedule progress is held until the site super replies.", action:"Send follow-up" },
  { tone:"amber", project:"St Leonards", title:"Roofing date not firm", body:"Roofing start depends on quote response and lead time.", action:"Chase roofer" },
  { tone:"navy", project:"Humber", title:"Field progress ahead of sign-off", body:"Scheduler is holding downstream dates until approval is clear.", action:"Review hold" },
];

const SCHEDULING_COMPLETED = [
  { who:"Scheduler", title:"St Leonards draft schedule prepared", detail:"Known dates set; placeholder dates marked." },
  { who:"Field Intake", title:"3 daily logs checked", detail:"Glenrose ready, Oakwood handled, St Leonards missing." },
  { who:"Trade Coordinator", title:"2 trade replies folded into schedule", detail:"Plumbing and demo dates are now real." },
  { who:"Scheduler", title:"Client-safe milestones prepared", detail:"Only approved dates will push to Client Room." },
];

const BackofficeToday = ({ eyebrow, title, intro, actions, alerts, completed, quickAsks, placeholder, onGo }) => (
  <div className="screen backoffice-today simple-office-page">
    <div className="simple-office-hero">
      <div>
        <div className="screen-eyebrow">{eyebrow}</div>
        <h1 className="screen-title">{title}</h1>
        <p>{intro}</p>
      </div>
      <div className="backoffice-hero-note">
        <b>Work here first.</b>
        <span>Open an employee only when you want the full packet or evidence.</span>
      </div>
    </div>

    <div className="simple-office-grid">
      <section className="simple-main-panel">
        <div className="simple-section-head">
          <div>
            <div className="screen-eyebrow">Needs you</div>
            <h2>Approve, answer, or assign</h2>
          </div>
        </div>
        <div className="simple-action-list">
          {actions.map((item) => (
            <SimpleActionRow
              key={item.title}
              tone={item.tone}
              title={item.title}
              body={item.body}
              primary={item.primary}
              onPrimary={() => {}}
              onOpenDesk={() => onGo(item.desk)}
            />
          ))}
        </div>
      </section>

      <aside className="simple-side-stack">
        <OfficeChatPanel
          title="Talk to this office"
          placeholder={placeholder}
          quick={quickAsks}
        />
        <section className="simple-watch-panel">
          <div className="simple-section-head">
            <div>
              <div className="screen-eyebrow">Watching</div>
              <h3>Alerts</h3>
            </div>
          </div>
          <div className="simple-watch-list">
            {alerts.map((alert) => <AlertCard key={alert.title} alert={alert} />)}
          </div>
        </section>
      </aside>
    </div>

    <SimpleDoneStrip items={completed} />
  </div>
);

const ScreenFinanceToday = ({ onGo }) => (
  <BackofficeToday
    eyebrow="Finance Backoffice Today"
    title="Bills, hours, and money questions"
    intro="This is the finance cockpit. You approve what is ready, answer exceptions, and ask the finance office to chase or explain anything."
    stats={[
      { label:"Bills ready", value:"6", note:"$24,318.42 staged", tone:"green" },
      { label:"Hours ready", value:"12", note:"1 OT exception", tone:"amber" },
      { label:"Held items", value:"4", note:"no guessing", tone:"rust" },
      { label:"Receipts saved", value:"18", note:"finance actions today", tone:"ink" },
    ]}
    actions={FINANCE_ACTIONS}
    alerts={FINANCE_ALERTS}
    completed={FINANCE_COMPLETED}
    quickAsks={[
      "Why are Glenrose framing costs up?",
      "Chase missing Oakwood bills",
      "Show this week's cash needs",
      "Prepare vendor payment review",
    ]}
    placeholder='e.g. "Find every St Leonards bill not in QuickBooks yet"'
    onGo={onGo}
  />
);

const ScreenSchedulingToday = ({ onGo }) => (
  <BackofficeToday
    eyebrow="Scheduling Backoffice Today"
    title="Daily logs, trade dates, and schedule risk"
    intro="This is the scheduling cockpit. You see missing logs, placeholder dates, trade follow-ups, and what can safely move into the project schedule."
    stats={[
      { label:"Real dates ready", value:"3", note:"safe to approve", tone:"green" },
      { label:"Placeholder dates", value:"2", note:"waiting on trades", tone:"amber" },
      { label:"Missing daily logs", value:"1", note:"2 days late", tone:"rust" },
      { label:"Schedule receipts", value:"11", note:"saved today", tone:"ink" },
    ]}
    actions={SCHEDULING_ACTIONS}
    alerts={SCHEDULING_ALERTS}
    completed={SCHEDULING_COMPLETED}
    quickAsks={[
      "What projects are missing daily logs?",
      "Chase trades blocking dates",
      "Show schedule risks this week",
      "Draft client-safe milestones",
    ]}
    placeholder='e.g. "What is blocking St Leonards from a real schedule?"'
    onGo={onGo}
  />
);

/* =========================================================
   SCREEN 6 — Mailroom / Sessions
   ========================================================= */
const ScreenMailroom = () => {
  const sessions = [
    { id:"S-2209", channel:"mail",  channelLabel:"Gmail",         project:"St Leonards",
      title:"Architect email · revised plan set",
      summary:"Estimator opened a takeoff packet, filed the PDF to Drive, and created budget workbook draft.",
      agent:"Estimator Agent", status:"new", minsAgo:3, packet:"Estimate packet", next:"Review budget" },

    { id:"S-2208", channel:"mail",  channelLabel:"Gmail",         project:"St Leonards",
      title:"Plumbing quote reply",
      summary:"Trade response matched to budget code 4001 and Scheduler row. Estimator folded the number into the workbook.",
      agent:"Trade Coordinator", status:"reply", minsAgo:8, packet:"Quote queue", next:"Verify exclusions" },

    { id:"S-2207", channel:"wa",    channelLabel:"WhatsApp",     project:"Oakwood",
      title:"Missing-hours reply — Frank Park",
      summary:"Frank wrote back: 8 reg + 2 OT on Oakwood framing. Bookkeeper folded it into today's Hours Packet.",
      agent:"Timekeeper", status:"new", minsAgo:12, packet:"Hours packet", next:"Approve hours" },

    { id:"S-2206", channel:"wa",    channelLabel:"WhatsApp",     project:"Glenrose",
      title:"Photo dump from site",
      summary:"5 photos + 1:24 voice note from Marco. Field Agent will sort and draft the daily log.",
      agent:"Field Agent", status:"new", minsAgo:18, packet:"Daily log", next:"Approve log" },

    { id:"S-2205", channel:"drive", channelLabel:"Drive",          project:"St Leonards",
      title:"Budget workbook export ready",
      summary:"Workbook tabs prepared: Budget, Scheduler, Bills, Reconciliation. Excel export waiting on approval.",
      agent:"Estimator Agent", status:"awaiting", minsAgo:24, packet:"Budget workbook", next:"Export Excel" },

    { id:"S-2204", channel:"mail",  channelLabel:"Gmail",         project:"Glenrose",
      title:"Hartman Roofing · Invoice INV-9921",
      summary:"Vendor + project matched. Sitting in the Bill Approval Packet.",
      agent:"Bookkeeper Agent", status:"new", minsAgo:38, packet:"Bill packet", next:"Approve bill" },

    { id:"S-2203", channel:"client",channelLabel:"Client Room",   project:"Glenrose",
      title:"Margaret Glenrose · tile allowance question",
      summary:"Reply drafted with current allowance + 2 sample tile boards. Waiting on your approval to send.",
      agent:"Client Update Agent", status:"awaiting", minsAgo:54, packet:"Client update", next:"Approve reply" },

    { id:"S-2202", channel:"drive", channelLabel:"Drive",          project:"Glenrose",
      title:"Drawing revision R4 uploaded by BCA Studio",
      summary:"File Clerk filed under /Glenrose/Drawings/R4. Estimator Agent is diffing against R3.",
      agent:"File Clerk", status:"new", minsAgo:82, packet:"Drawing revision", next:"Diff drawings" },

    { id:"S-2201", channel:"qb",    channelLabel:"QuickBooks",    project:"Oakwood",
      title:"New vendor — cost-code mapping",
      summary:"Patel Excavation needs a code. Bookkeeper suggests 02-200. Holding for your call.",
      agent:"Bookkeeper Agent", status:"held", minsAgo:120, packet:"Bill mapping", next:"Choose code" },
  ];

  const statusLabel = {
    new:      { tone:"navy",  label:"New" },
    reply:    { tone:"navy",  label:"Reply" },
    awaiting: { tone:"amber", label:"Awaiting you" },
    held:     { tone:"rust",  label:"Held" },
  };

  const sinceLabel = (m) => m < 60 ? `${m}m ago` : `${Math.floor(m/60)}h ${m%60}m ago`;

  return (
    <div className="screen">
      <DecisionDesk
        eyebrow="Mailroom Clerk · incoming work"
        title="Mailroom"
        intro="Every email, WhatsApp, Drive upload, and bookkeeping notice gets sorted to a house and employee. You only see items that need approval or an answer."
        primary={{ label:"Clear sorted items", icon:"check" }}
        secondary={{ label:"Hold unclear", icon:"clock" }}
        approvals={[
          { title:"Open St Leonards estimate packet", body:"Architect plan was filed and the Estimator packet is ready.", meta:"3m ago", action:"Open packet" },
          { title:"Route plumbing quote reply", body:"Trade response can update the budget and schedule.", meta:"reply", action:"Approve route" },
          { title:"File drawing revision", body:"Glenrose R4 drawing set is filed and ready for Estimator review.", meta:"Drive", action:"File" },
        ]}
        questions={[
          { title:"New vendor mapping", body:"Patel Excavation needs a cost code before Bookkeeper can post.", action:"Choose code" },
          { title:"Client allowance question", body:"Client Liaison drafted a reply but needs your approval.", action:"Review reply" },
        ]}
        handled={[
          "9 sessions sorted",
          "Each has a project",
          "Each has an assigned employee",
          "Source evidence saved",
        ]}
      >
        <Card pad={false}>
          <div>
            {sessions.map((s,i) => (
              <SessionRow key={s.id} session={s} pill={statusLabel[s.status]} sinceLabel={sinceLabel} />
            ))}
          </div>
        </Card>
      </DecisionDesk>
    </div>
  );
};

const SessionRow = ({ session: s, pill, sinceLabel }) => (
  <div style={{
    display:"grid",
    gridTemplateColumns:"180px 1fr 190px 170px 110px",
    gap:18, padding:"18px 22px",
    borderBottom:"1px solid var(--line)",
    alignItems:"flex-start"
  }}>
    <div>
      <div className="row" style={{gap:10}}>
        <span style={{
          width:30, height:30, borderRadius:7,
          background:"var(--paper-2)", border:"1px solid var(--line)",
          display:"grid", placeItems:"center", flex:"0 0 auto"
        }}>
          <Icon name={s.channel === "client" ? "client" : s.channel === "drive" ? "drive" : s.channel === "qb" ? "qb" : s.channel} size={14} color="var(--ink-2)" />
        </span>
        <div>
          <div style={{fontSize:13.5, fontWeight:500}}>{s.channelLabel}</div>
          <div className="muted" style={{fontSize:12, marginTop:1}}>{s.project} · {sinceLabel(s.minsAgo)}</div>
        </div>
      </div>
    </div>
    <div>
      <div style={{fontSize:14, fontWeight:500, color:"var(--ink)", letterSpacing:"-0.005em"}}>{s.title}</div>
      <div className="muted" style={{fontSize:13, marginTop:4, textWrap:"pretty", lineHeight:1.5}}>{s.summary}</div>
    </div>
    <div style={{fontSize:13, color:"var(--ink-2)"}}>
      <div className="screen-eyebrow" style={{marginBottom:2}}>Assigned</div>
      {s.agent}
    </div>
    <div style={{fontSize:13, color:"var(--ink-2)"}}>
      <div className="screen-eyebrow" style={{marginBottom:2}}>Packet + next</div>
      <div>{s.packet}</div>
      <div className="muted" style={{fontSize:12, marginTop:2}}>{s.next}</div>
    </div>
    <div style={{textAlign:"right"}}>
      <Pill tone={pill.tone} dot>{pill.label}</Pill>
    </div>
  </div>
);

/* =========================================================
   Scheduler — employee view
   ========================================================= */
const SCHEDULE_ROWS = [
  { code:"1102", phase:"Demo", owner:"Demo crew", date:"Apr 8", confidence:"real date", status:"ready", source:"Estimator budget + existing Scheduler tab", ask:"Approve", detail:"Already tied to the approved demo line and portal schedule." },
  { code:"1105", phase:"Underpinning", owner:"Foundation trade", date:"May 1", confidence:"real date", status:"ready", source:"Foundation quote + workbook row", ask:"Approve", detail:"Known start date. Can be pushed into the project schedule." },
  { code:"2001 / 2004", phase:"Framing", owner:"Carpentry crew", date:"Aug 1", confidence:"placeholder", status:"question", source:"Budget line + old crew rule", ask:"Confirm crew", detail:"Scheduler needs the crew plan before this becomes a real date." },
  { code:"2401", phase:"Roofing", owner:"Hartman Roofing", date:"Oct 1", confidence:"placeholder", status:"missing", source:"Trade ask still waiting", ask:"Send chase", detail:"Roofing quote and lead time are missing. Trade Coordinator has the follow-up ready." },
  { code:"3002", phase:"Windows", owner:"Pella", date:"Oct 14", confidence:"blocked", status:"missing", source:"Window models missing", ask:"Ask supplier", detail:"Cannot trust window delivery dates until model numbers come back." },
  { code:"4001", phase:"Plumbing rough-in", owner:"JMO Plumbing", date:"Oct 2", confidence:"real date", status:"ready", source:"Trade reply folded into budget", ask:"Approve", detail:"Trade replied with price and start window. Ready for schedule." },
  { code:"4101", phase:"Electrical rough-in", owner:"SAV Electric", date:"Nov 18", confidence:"estimate", status:"soft", source:"Old lead-time rule", ask:"Send reminder", detail:"Usable placeholder, but not reliable until the trade replies." },
];

const SCHEDULE_HANDOFFS = [
  { from:"Estimator", to:"Trade Coordinator", body:"Budget lines that need trade prices", state:"5 quote items" },
  { from:"Trade Coordinator", to:"Scheduler", body:"Trade replies, lead times, missing responses", state:"2 real dates" },
  { from:"Field Intake", to:"Scheduler", body:"Daily log progress and site delays", state:"1 variance flagged" },
];

const projectNameFor = (project) => (
  project === "glenrose" ? "Glenrose" :
  project === "oakwood" ? "Oakwood" :
  project === "humber" ? "Humber" :
  "St Leonards"
);

const ScreenSchedule = ({ project = "all" }) => {
  const projectLabel = projectNameFor(project);
  const [selectedRow, setSelectedRow] = React.useState(SCHEDULE_ROWS.find((row) => row.status === "missing") || SCHEDULE_ROWS[0]);
  const readyCount = SCHEDULE_ROWS.filter((row) => row.status === "ready").length;
  const softCount = SCHEDULE_ROWS.filter((row) => row.status === "soft" || row.status === "question").length;
  const missingCount = SCHEDULE_ROWS.filter((row) => row.status === "missing").length;

  return (
    <div className="screen schedule2">
      <DecisionDesk
        eyebrow={`Scheduler · ${projectLabel}`}
        title="Scheduler Work Packet"
        intro="The Scheduler turns the approved budget and trade replies into a build calendar. Real dates get pushed forward. Missing trade answers stay highlighted."
        primary={{ label:"Approve real dates", icon:"check" }}
        secondary={{ label:"Keep placeholders", icon:"clock" }}
        approvals={[
          { title:"Approve 3 real schedule dates", body:"Demo, underpinning, and plumbing have reliable dates and source receipts.", meta:"3 rows", action:"Approve dates" },
          { title:"Start reminders for upcoming trades", body:"Only trades tied to approved or near-term rows will be reminded.", meta:"after approval", action:"Start reminders" },
          { title:"Push approved milestones to Project Room", body:"The project room gets real dates only. Placeholders stay internal.", meta:"client-safe", action:"Push dates" },
        ]}
        questions={[
          { title:"Roofing and windows are not firm", body:"Both depend on trade replies before the schedule can be trusted.", action:"Chase trades" },
          { title:"Framing crew plan needs a yes/no", body:"Scheduler used the old crew rule because no one confirmed the labour plan.", action:"Confirm crew" },
        ]}
        handled={[
          "Budget connected",
          "Trade replies checked",
          "Dummy dates marked",
          "Field delay linked",
        ]}
      >
        <Card pad={false} className="scheduler-handoff-card">
          <div className="scheduler-card-head">
            <div>
              <div className="screen-eyebrow">How work gets to Scheduler</div>
              <h3>Estimator hands the budget to Trade Coordinator, then Scheduler turns confirmed trade answers into dates.</h3>
            </div>
            <div className="scheduler-counts">
              <span><b>{readyCount}</b> real dates</span>
              <span><b>{softCount}</b> soft dates</span>
              <span><b>{missingCount}</b> blocked</span>
            </div>
          </div>
          <div className="handoff-flow">
            {SCHEDULE_HANDOFFS.map((h, i) => (
              <React.Fragment key={h.from + h.to}>
                <div className="handoff-step">
                  <small>{h.from} to {h.to}</small>
                  <b>{h.body}</b>
                  <span>{h.state}</span>
                </div>
                {i < SCHEDULE_HANDOFFS.length - 1 && <div className="handoff-arrow">-></div>}
              </React.Fragment>
            ))}
          </div>
        </Card>

        <div className="scheduler-grid">
          <Card pad={false} className="scheduler-table-card">
            <div className="scheduler-table-head">
              <span>Code</span><span>Work</span><span>Date</span><span>Status</span>
            </div>
            {SCHEDULE_ROWS.map((row) => (
              <button
                type="button"
                key={row.code + row.phase}
                className={"scheduler-row is-" + row.status + (selectedRow.code === row.code ? " is-selected" : "")}
                onClick={() => setSelectedRow(row)}
              >
                <span className="mono">{row.code}</span>
                <span><b>{row.phase}</b><small>{row.owner} · {row.source}</small></span>
                <span>{row.date}</span>
                <span>{row.confidence}</span>
              </button>
            ))}
          </Card>

          <Card pad={false} className={"scheduler-detail-card is-" + selectedRow.status}>
            <div className="scheduler-detail">
              <div className="screen-eyebrow">Selected schedule line</div>
              <h3>{selectedRow.phase}</h3>
              <div className="scheduler-detail-facts">
                <div><span>Date</span><b>{selectedRow.date}</b></div>
                <div><span>Status</span><b>{selectedRow.confidence}</b></div>
              </div>
              <p>{selectedRow.detail}</p>
              <button type="button">{selectedRow.ask}</button>
            </div>
          </Card>
        </div>
      </DecisionDesk>
    </div>
  );
};

/* =========================================================
   SCREEN 7 — Project Room
   ========================================================= */
const ScreenProject = () => (
  <div className="screen">
    <DecisionDesk
      eyebrow="Scheduler · St Leonards"
      title="Project Room"
      intro="This is the house room. The Scheduler and other employees push approved work here. You approve schedule changes or answer blocked items."
      primary={{ label:"Approve schedule changes", icon:"check" }}
      secondary={{ label:"Preview client view", icon:"eye" }}
      approvals={[
        { title:"Approve draft schedule", body:"Known dates are set and placeholder dates are marked.", meta:"6 rows", action:"Approve schedule" },
        { title:"Push approved milestones to Client Room", body:"Only client-safe dates will show to the owner.", meta:"client-safe", action:"Push milestones" },
        { title:"Start trade reminders", body:"Reminders will go out for trades tied to upcoming schedule rows.", meta:"after approval", action:"Start reminders" },
      ]}
      questions={[
        { title:"Roofing date is not firm", body:"Roofing still depends on trade quote response.", action:"Keep placeholder" },
        { title:"Electrical rough-in date", body:"Scheduler used old lead-time rules until trade confirms.", action:"Use dummy date" },
      ]}
      handled={[
        "Budget lines connected",
        "Trade replies linked",
        "Daily logs and photos attached",
        "Receipts saved",
      ]}
    >
      <div className="project-flow-grid">
      <Card head="Schedule" eyebrow="DRAFTED FROM BUDGET + TRADE DATES"
            right={<Btn size="sm" variant="primary" icon="check">Approve schedule</Btn>}>
        <div className="project-schedule-list">
          {[
            ["1102", "Demolition - Selective/Interior", "known · Apr 8", "from Scheduler tab"],
            ["1105", "Underpinning", "known · May 1", "from Scheduler tab"],
            ["2001/2004", "Lumber + framing", "placeholder · Aug 1", "from budget + crew rules"],
            ["2401", "Shingles", "waiting on roofing quote", "trade ask in queue"],
            ["4001", "Plumbing rough-in", "known · Oct 2", "trade reply folded in"],
            ["4101", "Electrical rough-in", "placeholder · Nov 18", "waiting on electrical"],
          ].map((r) => (
            <div className="project-row" key={r[0] + r[1]}>
              <span className="mono">{r[0]}</span>
              <div><b>{r[1]}</b><small>{r[3]}</small></div>
              <em>{r[2]}</em>
            </div>
          ))}
        </div>
      </Card>

      <Card head="Budget workbook" eyebrow="BUDGET · SCHEDULER · BILLS · RECONCILIATION"
            right={<Btn size="sm" variant="ghost" icon="doc">Open Excel export</Btn>}>
        <div className="project-budget-cards">
          {[
            ["Original budget", "$612,840", "pre-quote total"],
            ["Trade replies folded", "1 of 5", "plumbing updated"],
            ["Lines waiting", "4", "windows, roofing, electrical, labour"],
          ].map((m) => (
            <div key={m[0]}><span>{m[0]}</span><b>{m[1]}</b><small>{m[2]}</small></div>
          ))}
        </div>
      </Card>

      <Card head="Daily logs + photos" eyebrow="FIELD INTAKE WRITES">
        <div className="project-proof-list">
          <ProofItem title="Daily log drafted" detail="Field Agent from WhatsApp voice note. Waiting for Aaron approval." />
          <ProofItem title="Photos filed" detail="5 site photos sorted to Drive and attached to the daily log." />
          <ProofItem title="Schedule variance flagged" detail="Trade Coordinator sees field progress against the approved schedule." />
        </div>
      </Card>

      <Card head="Drawings / RFIs" eyebrow="SOURCE FILES + OPEN QUESTIONS">
        <div className="project-proof-list">
          <ProofItem title="Architect plan uploaded" detail="File Clerk saved the PDF; Estimator created takeoff packet." />
          <ProofItem title="Window schedule uncertainty" detail="Trade ask includes model-number question for supplier." />
          <ProofItem title="RFI trail" detail="Every architect ask links back to the sheet, field note, or quote response." />
        </div>
      </Card>

      <Card head="Bills, invoices, and hours" eyebrow="BOOKKEEPING INPUTS">
        <div className="project-proof-list">
          <ProofItem title="Bills mapped to codes" detail="Vendor, project, cost code, amount, due date, source saved." />
          <ProofItem title="Hours staged" detail="Employee hours from site messages wait for approval before QBO entry." />
          <ProofItem title="Held items visible" detail="Duplicate risks, split bills, and missing employee hours stay out of QBO." />
        </div>
      </Card>

      <Card head="Client-safe update" eyebrow="ONLY APPROVED EXTERNAL VIEW">
        <div className="project-proof-list">
          <ProofItem title="Weekly update drafted" detail="Plain-language progress, selected photos, milestones, and approved budget note." />
          <ProofItem title="Internal notes filtered" detail="No vendor disputes, trade delays, QBO mapping questions, or staff notes." />
          <ProofItem title="Receipt saved" detail="Client update approval and send event is written to the ledger." />
        </div>
      </Card>
      </div>
    </DecisionDesk>
  </div>
);

const ProofItem = ({ title, detail }) => (
  <div className="proof-item">
    <b>{title}</b>
    <span>{detail}</span>
  </div>
);

const RfiRow = ({ tone, title, meta, last }) => (
  <div style={{padding:"8px 0", borderBottom: last ? "none" : "1px dashed var(--line)"}}>
    <div className="row" style={{gap:10, alignItems:"flex-start"}}>
      <span style={{width:8, height:8, borderRadius:"50%", marginTop:7,
                    background: tone === "amber" ? "var(--amber)" : "var(--green)"}}/>
      <div>
        <div style={{fontSize:13, color:"var(--ink)"}}>{title}</div>
        <div className="muted" style={{fontSize:12, marginTop:2}}>{meta}</div>
      </div>
    </div>
  </div>
);

/* =========================================================
   SCREEN 8 — Client Room
   ========================================================= */
const ScreenClient = () => (
  <div className="screen">
    <DecisionDesk
      eyebrow="Client Liaison · Glenrose"
      title="Client Room"
      intro="This is the owner-facing view. Internal trade problems, mapping questions, and staff notes stay out. You approve what the client sees."
      primary={{ label:"Approve client update", icon:"send" }}
      secondary={{ label:"Preview client view", icon:"eye" }}
      approvals={[
        { title:"Send Thursday update", body:"Plain-language progress note with approved photos and milestones.", meta:"client-safe", action:"Send update" },
        { title:"Include 5 selected photos", body:"Only field photos approved for the client are attached.", meta:"5 photos", action:"Approve photos" },
        { title:"Show upcoming milestones", body:"Roof framing, roofing start, windows, and rough-in dates are ready.", meta:"next 60 days", action:"Approve dates" },
      ]}
      questions={[
        { title:"Tile allowance question", body:"Margaret asked if tile selections are still inside allowance.", action:"Approve reply" },
        { title:"Framing walk-through", body:"Tom asked when they can walk the framing.", action:"Confirm date" },
      ]}
      handled={[
        "Vendor issues hidden",
        "Bookkeeping questions hidden",
        "Private staff notes hidden",
        "Receipts saved",
      ]}
    >
      <div className="simple-support-grid">
        <Card head="Draft update" eyebrow="WHAT CLIENT WILL SEE">
          <div className="client-preview-note">
            <b>Framing is well underway.</b>
            <span>The second floor is taking shape, lumber arrived on time, and next milestones are roof framing and windows.</span>
          </div>
        </Card>
        <Card head="Client-safe photos" eyebrow="SELECTED">
          <div style={{display:"grid", gridTemplateColumns:"repeat(5,1fr)", gap:6}}>
            {["ph-wood","ph-wood","ph-dirt","ph-concrete","ph-sky"].map((c,i) => (
              <div key={i} className={"photo "+c}/>
            ))}
          </div>
        </Card>
      </div>
    </DecisionDesk>
  </div>
);

/* =========================================================
   SCREEN 9 — Rules + Staff (Employee Roster)
   ========================================================= */
const ScreenStaff = () => {
  return (
    <div className="screen">
      <div className="row between">
        <div>
          <div className="screen-eyebrow">Employee Roster · 6 employees · last permissions audit Tue 17 May</div>
          <h1 className="screen-title" style={{marginTop:6}}>Your Office Staff</h1>
          <div className="muted" style={{fontSize:13.5, marginTop:6, maxWidth:720}}>
            Each employee has scheduled routines they run on the clock, and you can ask any of them ad-hoc work anytime. Nothing leaves the office without your approval, and every action leaves a receipt.
          </div>
        </div>
        <Btn icon="plus" variant="primary">Hire an agent</Btn>
      </div>

      {/* HERO — the office floor view: all employees on one timeline */}
      <Card head="The office floor today" eyebrow="EVERYONE'S DAY · 6 AM – 8 PM"
            right={<span className="muted" style={{fontSize:12}}>now 7:42 AM · next routine 11:00 AM</span>}>
        <OfficeHoursStrip />
      </Card>

      {/* Employee profiles — one rich row per agent */}
      <div className="col" style={{gap:14}}>
        {ROSTER.map(a => <EmployeeCard key={a.key} a={a} />)}
      </div>

      <Card head="Office-wide rules" eyebrow="APPLY TO ALL EMPLOYEES">
        <div style={{display:"grid", gridTemplateColumns:"1fr 1fr", gap:14, marginTop:4}}>
          <RuleCell icon="lock"    title="Approval required for outbound" body="No email, message, or QuickBooks write leaves the office without your approval."/>
          <RuleCell icon="receipt" title="Receipt for every action" body="Every read, draft, and write is recorded in the Receipt Ledger with source evidence."/>
          <RuleCell icon="alert"   title="Hold on uncertainty" body="If confidence drops below 70%, the employee stops and asks rather than guesses."/>
          <RuleCell icon="eye"     title="Client-safe view" body="Vendor disputes, mapping questions, and internal notes are filtered from the Client Room."/>
        </div>
      </Card>
    </div>
  );
};

const EmployeeCard = ({ a }) => {
  const accent = a.tone === "green" ? "var(--green)" :
                 a.tone === "amber" ? "var(--amber)" :
                 a.tone === "navy"  ? "var(--navy)"  : "var(--ink)";
  return (
    <div className="card" style={{padding:0}}>
      <div style={{
        display:"grid",
        gridTemplateColumns:"260px 1fr 300px",
        gap:0,
        alignItems:"stretch"
      }}>
        {/* Identity */}
        <div style={{padding:"20px 22px", borderRight:"1px solid var(--line)"}}>
          <div className="row" style={{gap:14, alignItems:"center"}}>
            <div style={{
              width:48, height:48, borderRadius:11,
              background: accent, color:"var(--paper)",
              display:"grid", placeItems:"center",
              fontFamily:"var(--f-mono)", fontSize:14, fontWeight:600
            }}>{a.key}</div>
            <div>
              <div style={{fontSize:15.5, fontWeight:500, letterSpacing:"-0.005em"}}>{a.name}</div>
              <div className="muted" style={{fontSize:12, marginTop:1}}>{a.title}</div>
            </div>
          </div>
          <div className="row" style={{gap:6, marginTop:14}}>
            <Pill tone="green" dot>On shift</Pill>
          </div>
          <div className="muted" style={{fontSize:12.5, marginTop:12, textWrap:"pretty", lineHeight:1.55}}>
            {a.blurb}
          </div>
        </div>

        {/* Today + routines */}
        <div style={{padding:"20px 22px", borderRight:"1px solid var(--line)"}}>
          <div className="row between" style={{alignItems:"baseline"}}>
            <div className="screen-eyebrow">Today's day</div>
            <span className="mono" style={{fontSize:10.5, color:"var(--ink-3)", letterSpacing:"0.04em"}}>last · {a.lastDid.split(" · ").slice(-1)[0]}</span>
          </div>
          <div style={{marginTop:6}}>
            <AgentDayMini agent={a} />
          </div>

          <div style={{marginTop:14, display:"grid", gridTemplateColumns:"1fr 1fr", gap:18}}>
            <div>
              <div className="screen-eyebrow">Scheduled routines</div>
              <ul style={{listStyle:"none", padding:0, margin:"6px 0 0", display:"flex", flexDirection:"column", gap:4, fontSize:12.5, color:"var(--ink-2)"}}>
                {a.routines.map((r,i) => (
                  <li key={i} style={{position:"relative", paddingLeft:14}}>
                    <span style={{position:"absolute", left:0, top:8, width:5, height:5, borderRadius:"50%", background:accent}}/>
                    {r}
                  </li>
                ))}
              </ul>
            </div>
            <div>
              <div className="screen-eyebrow">Just did · up next</div>
              <div style={{marginTop:6, fontSize:12.5, color:"var(--ink-2)", lineHeight:1.5}}>
                <div className="row" style={{gap:8, alignItems:"flex-start"}}>
                  <span style={{width:6, height:6, borderRadius:"50%", background:"var(--green-2)", marginTop:7, flex:"0 0 auto"}}/>
                  <span>{a.lastDid}</span>
                </div>
                <div className="row" style={{gap:8, alignItems:"flex-start", marginTop:6}}>
                  <span style={{width:6, height:6, borderRadius:"50%", border:`1.5px dashed ${accent}`, background:"transparent", marginTop:6, flex:"0 0 auto"}}/>
                  <span>{a.nextOn}</span>
                </div>
              </div>
            </div>
          </div>
        </div>

        {/* Ad-hoc Ask */}
        <div style={{padding:"20px 22px", background:"var(--paper-2)", display:"flex", flexDirection:"column", gap:10}}>
          <AskAgent agent={a} />
          <div style={{flex:1}}/>
          <Btn variant="primary" icon="send" size="sm">Send to {a.title}</Btn>
        </div>
      </div>

      {/* Permissions strip */}
      <div style={{
        borderTop:"1px solid var(--line)",
        background:"var(--paper-2)",
        padding:"12px 22px",
        display:"grid",
        gridTemplateColumns:"1fr 1fr 1fr 1.4fr",
        gap:24,
        alignItems:"start"
      }}>
        <PermSection title="Reads"  items={a.reads}  tone="ink"/>
        <PermSection title="Drafts" items={a.drafts} tone="amber"/>
        <PermSection title="Sends"  items={a.sends}  tone="green"/>
        <div>
          <div className="screen-eyebrow" style={{color:"var(--rust)"}}>Never</div>
          <div style={{fontSize:12.5, color:"var(--ink-2)", marginTop:6, lineHeight:1.5}}>{a.never}</div>
        </div>
      </div>
    </div>
  );
};

const PermSection = ({ title, items, tone }) => {
  const colorMap = { green:"var(--green)", amber:"var(--amber)", ink:"var(--ink-3)" };
  return (
    <div>
      <div className="screen-eyebrow" style={{color: colorMap[tone]}}>{title}</div>
      <ul style={{listStyle:"none", padding:0, margin:"6px 0 0", display:"flex", flexDirection:"column", gap:3, fontSize:12.5, color:"var(--ink-2)"}}>
        {items.map((it,i)=>(<li key={i}>{it}</li>))}
      </ul>
    </div>
  );
};

const RuleCell = ({ icon, title, body }) => (
  <div className="row" style={{gap:14, alignItems:"flex-start", padding:"6px 0"}}>
    <div style={{
      width:32, height:32, borderRadius:8,
      background:"var(--paper-2)", border:"1px solid var(--line)",
      display:"grid", placeItems:"center", flex:"0 0 auto"
    }}>
      <Icon name={icon} size={14} color="var(--ink-2)" />
    </div>
    <div>
      <div style={{fontSize:13.5, fontWeight:500}}>{title}</div>
      <div className="muted" style={{fontSize:12.5, marginTop:3, textWrap:"pretty", lineHeight:1.5}}>{body}</div>
    </div>
  </div>
);

/* =========================================================
   SCREEN 10 — Receipts / Ledger
   ========================================================= */
const ScreenReceipts = () => {
  const rows = [
    { t:"7:55 AM",  agent:"BA", agentName:"Bookkeeper", approver:"queued", action:"Missing-hours follow-up drafted",
      detail:"To Carlos Diaz · Oakwood framing · EN + ES",
      source:"Hours Packet · today", system:"Drafts", tone:"amber" },
    { t:"7:42 AM",  agent:"BA", agentName:"Bookkeeper", approver:"auto", action:"Hours reply folded into packet",
      detail:"Frank Park · 8 reg + 2 OT · Oakwood 06-200 framing",
      source:"WhatsApp · Frank Park reply", system:"Hours Packet", tone:"green" },
    { t:"7:42 AM",  agent:"BA", agentName:"Bookkeeper", approver:"Aaron",  action:"Accounting question answered",
      detail:"\u201CWhy is Glenrose framing labor over?\u201D \u00b7 pulled 3 bills + 4 time entries + 2 receipts",
      source:"Aaron WhatsApp", system:"Ask Accounting", tone:"green" },
    { t:"7:38 AM",  agent:"FA", agentName:"Field Intake", approver:"Aaron", action:"Daily log approved & published",
      detail:"Glenrose \u00b7 17 May",  source:"WhatsApp \u00b7 Marco voice 1:24", system:"Project Room", tone:"green" },
    { t:"7:32 AM",  agent:"FA", agentName:"Field Intake", approver:"Aaron", action:"5 photos filed to Drive",
      detail:"Glenrose \u00b7 /Photos/17-May", source:"WhatsApp \u00b7 5 images", system:"Drive", tone:"green" },
    { t:"7:30 AM",  agent:"FA", agentName:"Field Intake", approver:"queued", action:"12 time entries staged for Aaron",
      detail:"Glenrose framing + Oakwood crew + Frank reply \u00b7 1 OT exception flagged",
      source:"WhatsApp \u00b7 Marco voice + 4 timesheet photos + Frank reply", system:"Hours Packet", tone:"amber" },
    { t:"7:28 AM",  agent:"EA", agentName:"Estimator", approver:"\u2014", action:"Takeoff worksheet prepared",
      detail:"Glenrose R4 \u00b7 $207,610 rule-priced + 4 quote-needed",
      source:"Arch R4 + Specs v3 + rule book v3", system:"Estimating Desk", tone:"green" },
    { t:"7:24 AM",  agent:"EA", agentName:"Estimator", approver:"auto", action:"Pella prelim quote folded into worksheet",
      detail:"$86k range pending model #s for W-08\u2013W-12",
      source:"Gmail \u00b7 Drew Markham reply", system:"Estimating Desk", tone:"green" },
    { t:"7:18 AM",  agent:"EA", agentName:"Estimator", approver:"Aaron", action:"Supplier quote request sent",
      detail:"Pella Windows \u00b7 24 units \u00b7 worksheet rows attached",
      source:"Estimate Packet #EST-1148", system:"Gmail", tone:"green" },
    { t:"6:55 AM",  agent:"BA", agentName:"Bookkeeper", approver:"Aaron", action:"Missing-hours follow-up sent",
      detail:"To Frank Park · Oakwood framing · via your WhatsApp",
      source:"Hours Packet · today's chase", system:"WhatsApp", tone:"green" },
    { t:"6:54 AM",  agent:"BA", agentName:"Bookkeeper", approver:"\u2014", action:"2 missing-hours follow-ups drafted",
      detail:"Frank Park + Carlos Diaz \u00b7 EN + ES \u00b7 await your send",
      source:"Hours Packet \u00b7 schedule cross-check", system:"Drafts", tone:"amber" },
    { t:"6:50 AM",  agent:"BA", agentName:"Bookkeeper", approver:"Aaron", action:"6 bills written to QuickBooks",
      detail:"$24,318.42 across 3 projects \u00b7 0 errors",
      source:"Gmail + Drive inbox", system:"QuickBooks", tone:"green" },
    { t:"6:42 AM",  agent:"BA", agentName:"Bookkeeper", approver:"\u2014", action:"4 bills held",
      detail:"3 mapping questions + 1 duplicate suspected",
      source:"Gmail \u00b7 Madsen + Sherwin-Williams + 2 others", system:"Held items", tone:"amber" },
    { t:"6:14 AM",  agent:"CU", agentName:"Client Update", approver:"queued", action:"Weekly client update drafted",
      detail:"Glenrose \u00b7 4 photos \u00b7 sends Thu 8 AM if approved",
      source:"Project Room + photos", system:"Client Room", tone:"ink" },
    { t:"5:48 AM",  agent:"FC", agentName:"File Clerk", approver:"\u2014", action:"38 files reorganised",
      detail:"Oakwood \u00b7 Drawings folder",
      source:"Drive scan \u00b7 auto-rule", system:"Drive", tone:"ink" },
    { t:"Mon 4:32 PM", agent:"BA", agentName:"Bookkeeper", approver:"Aaron", action:"11 time entries posted to QuickBooks Time",
      detail:"Yesterday's batch \u00b7 87.0 reg + 4.5 OT \u00b7 2 projects",
      source:"WhatsApp + email timesheets", system:"QuickBooks Time", tone:"green" },
    { t:"Mon 4:20 PM", agent:"BA", agentName:"Bookkeeper", approver:"Aaron", action:"Missing-hours follow-up sent",
      detail:"To Mike Lee · Glenrose framing · no reply yet · auto-nudge 4 PM today",
      source:"Hours Packet · Mon chase", system:"WhatsApp", tone:"amber" },
    { t:"Mon 2:21 PM", agent:"EA", agentName:"Estimator", approver:"Aaron", action:"Estimate packet prepared",
      detail:"Glenrose \u00b7 #EST-1148",
      source:"Arch set R4 + Specs v3", system:"Estimating Desk", tone:"green" },
    { t:"Mon 12:14 PM", agent:"FC", agentName:"File Clerk", approver:"\u2014", action:"R4 drawing set filed to Drive",
      detail:"Glenrose_Arch_Set_R4.pdf \u00b7 28 sheets",
      source:"Gmail \u00b7 BCA Studio upload", system:"Drive", tone:"green" },
  ];

  return (
    <div className="screen">
      <div className="row between">
        <div>
          <div className="screen-eyebrow">Proof of work · every action your staff takes</div>
          <h1 className="screen-title" style={{marginTop:6}}>Receipts</h1>
          <div className="muted" style={{fontSize:13.5, marginTop:6, maxWidth:680}}>
            One row per action, in the order it happened. Each receipt links back to the source evidence in Gmail, Drive, WhatsApp or QuickBooks and the system it touched. Nothing here was added by you — it's what the office did.
          </div>
        </div>
        <div className="row" style={{gap:8}}>
          <Btn icon="doc" variant="ghost">Export CSV</Btn>
          <Btn icon="lock" variant="primary">Auditor view</Btn>
        </div>
      </div>

      {/* summary band intentionally removed — receipts are plain proof, not analytics */}

      <Card pad={false}>
        <div className="ledger-row head">
          <div>Time</div>
          <div>Action</div>
          <div>Source evidence</div>
          <div>System</div>
          <div style={{textAlign:"right"}}>View</div>
        </div>
        {rows.map((r,i) => (
          <div key={i} className="ledger-row">
            <div className="ts">{r.t}</div>
            <div className="ev">
              <div className="row" style={{gap:10, alignItems:"center"}}>
                <span style={{
                  width:22, height:22, borderRadius:5,
                  background: r.tone === "green" ? "var(--green)" :
                              r.tone === "amber" ? "var(--amber)" :
                              r.tone === "rust"  ? "var(--rust)"  : "var(--ink)",
                  color:"var(--paper)",
                  display:"grid", placeItems:"center",
                  fontFamily:"var(--f-mono)", fontSize:9.5, fontWeight:600, flex:"0 0 auto"
                }}>{r.agent}</span>
                <div>
                  <div style={{fontSize:13.5, color:"var(--ink)", fontWeight:500}}>{r.action}</div>
                  <div className="muted" style={{fontSize:12, marginTop:2}}>
                    {r.detail} · {r.agentName}{r.approver !== "—" && r.approver !== "queued" ? ` · approved by ${r.approver}` : ""}
                  </div>
                </div>
              </div>
            </div>
            <div>
              <div className="row" style={{gap:8, alignItems:"center"}}>
                <Icon name={r.source.startsWith("WhatsApp")?"wa":r.source.startsWith("Gmail")?"mail":r.source.startsWith("Drive")?"drive":r.source.startsWith("Sheet")?"doc":r.source.startsWith("Estimate")?"stack":"paperclip"} size={12} color="var(--ink-3)"/>
                <span style={{fontSize:12.5, color:"var(--ink-2)"}}>{r.source}</span>
              </div>
            </div>
            <div style={{fontSize:12.5, color:"var(--ink-2)"}}>{r.system}</div>
            <div style={{textAlign:"right"}}>
              <Btn size="sm" variant="ghost">Open</Btn>
            </div>
          </div>
        ))}
      </Card>
    </div>
  );
};

const ReceiptStat = ({ label, val, meta, tone }) => {
  const color = tone === "green" ? "var(--green)" : tone === "amber" ? "var(--amber)" : tone === "rust" ? "var(--rust)" : "var(--ink)";
  return (
    <div className="card pad" style={{flex:1}}>
      <div className="screen-eyebrow">{label}</div>
      <div className="serif" style={{fontSize:36, fontStyle:"italic", color:color, lineHeight:1, marginTop:6}}>{val}</div>
      <div className="muted" style={{fontSize:12.5, marginTop:6}}>{meta}</div>
    </div>
  );
};

window.ScreenFinanceToday = ScreenFinanceToday;
window.ScreenSchedulingToday = ScreenSchedulingToday;
window.ScreenMailroom = ScreenMailroom;
window.ScreenSchedule = ScreenSchedule;
window.ScreenProject  = ScreenProject;
window.ScreenClient   = ScreenClient;
window.ScreenStaff    = ScreenStaff;
window.ScreenReceipts = ScreenReceipts;

/* =========================================================
   V2 — Non-technical GC interface
   ========================================================= */

const PRODUCT_PROMISE_V2 = "Your AI staff runs the back office overnight. Each morning, you just approve, reply, or ask.";
const BACKGROUND_PROMISE_V3 = "Your AI office works in the background through email and WhatsApp. We only ping you when we can't move without you.";

const TODAY_ITEMS_V2 = [
  {
    group:"Bills → QuickBooks",
    rowTitle:"Bills → QuickBooks",
    rowMeta:["6 bills", "2 questions", "$24,318.42"],
    inbox:{
      channel:"Email",
      time:"7:14 AM",
      state:"Needs answers",
      preview:"6 bills found. 4 are ready. 2 need answers before anything posts.",
    },
    count:1,
    tone:"green",
    title:"6 bills found. 2 need answers.",
    house:"Glenrose, Oakwood, St Leonards",
    amount:"$24,318.42",
    outcome:"Reply and the Bookkeeper fixes the two bills. Then the office asks for final approval before QuickBooks.",
    summary:"4 bills are ready. 2 need a quick answer.",
    source:"Because the Bookkeeper scanned 10 bills overnight.",
    channelHistory:[
      ["email", "6:12 AM", "Gmail bills label scanned for new vendor bills."],
      ["inapp", "6:24 AM", "Bookkeeper matched vendor, project, cost code, and due date."],
      ["email", "6:41 AM", "Possible Sherwin-Williams duplicate held instead of guessing."],
    ],
    whyYou:"This is over your $5,000 money approval rule, so the office cannot write it without you.",
    actions:["Reply", "Open"],
    details:{
      title:"6 bills found",
      summary:"4 are ready. 2 need your answer before anything posts.",
      facts:["4 ready", "2 questions", "$24,318.42 total"],
      ready:[
        "4 matched bills are ready once these questions are answered.",
      ],
      held:[
        "Patel Excavation: which cost code should I use?",
        "Sherwin-Williams: is this a duplicate of invoice SW-1042?",
      ],
      outside:"Nothing posts to QuickBooks until you answer and approve the final batch.",
    },
  },
  {
    group:"Field issue",
    rowTitle:"Field issue",
    rowMeta:["St Leonards", "2 days quiet", "follow-up drafted"],
    inbox:{
      channel:"WhatsApp",
      time:"7:30 AM",
      state:"Needs nudge",
      preview:"The office reminded Marco twice. St Leonards is still quiet, so the follow-up is waiting for your approval.",
    },
    count:1,
    tone:"amber",
    title:"St Leonards has no daily log in 2 days.",
    house:"St Leonards",
    amount:"2 days quiet",
    outcome:"Approve and the drafted follow-up goes to your site super.",
    summary:"A normal daily log does not come to Today. This one is here because the job went quiet and Schedule is holding dates.",
    source:"Because Field Intake checks every site every morning.",
    channelHistory:[
      ["whatsapp", "Tue 7:30 AM", "Reminder sent to Marco for the St Leonards log."],
      ["whatsapp", "Wed 7:35 AM", "Second reminder sent with photo request."],
      ["inapp", "Thu 6:55 AM", "Scheduler held downstream dates because no log came in."],
    ],
    whyYou:"This is assigned to you because the site has gone quiet for 2 days and Marco usually responds faster when you nudge him.",
    actions:["Approve and send", "Open"],
    details:{
      title:"Follow-up ready for St Leonards",
      summary:"The site has gone quiet, so Schedule is holding downstream dates.",
      facts:["2 days without a log", "1 message drafted", "schedule unchanged"],
      ready:["Message asks for today's work, photos, crew count, and any blockers."],
      held:["Roofing and framing dates stay as-is until a daily log arrives."],
      outside:"This sends outside the app, so you see the message before it leaves.",
    },
  },
  {
    group:"Homeowner portal",
    rowTitle:"Homeowner portal",
    rowMeta:["Thursday update to the Glenroses"],
    inbox:{
      channel:"Email",
      time:"6:48 AM",
      state:"Preview before send",
      preview:"The homeowner update is drafted from approved photos and logs. It will not send until you approve.",
    },
    count:1,
    tone:"navy",
    title:"Send Thursday update to the Glenroses.",
    house:"Glenrose",
    amount:"5 photos · next 60 days",
    outcome:"Approve and the homeowner portal gets the clean weekly update.",
    summary:"Plain-language progress note with 5 approved photos and the next 60 days of milestones. No vendor issues or money notes included.",
    source:"Because the Client Liaison drafted it from this week's logs.",
    channelHistory:[
      ["inapp", "6:18 AM", "Client Liaison drafted the update from approved logs and photos."],
      ["email", "6:26 AM", "Vendor issues and money notes were filtered out."],
      ["inapp", "6:31 AM", "Homeowner-facing draft queued for owner sign-off."],
    ],
    whyYou:"Homeowner messages always need owner approval before anything leaves the office.",
    actions:["Approve and send", "Open"],
    details:{
      title:"What the homeowner sees",
      summary:"The update shows progress, selected photos, and approved milestones only.",
      facts:["5 approved photos", "next 60 days", "vendor issues hidden"],
      ready:[
        "Progress: framing is moving and lumber arrived",
        "Next: roof framing, roofing, windows, rough-ins",
        "Photos: five client-safe field images",
      ],
      held:["Vendor delays, bookkeeping questions, and internal staff notes are filtered out."],
      outside:"This is homeowner-facing. You preview it before anything is sent.",
    },
  },
  {
    group:"Crew hours",
    rowTitle:"Crew hours",
    rowMeta:["Jay Patel", "4.5h OT", "outside your rule"],
    inbox:{
      channel:"WhatsApp",
      time:"7:42 AM",
      state:"Reply needed",
      preview:"Frank answered the missing-hours text. Jay's overtime is outside your rule, so payroll is held.",
    },
    count:1,
    tone:"amber",
    title:"Jay Patel: 4.5 hours of overtime.",
    house:"Oakwood",
    amount:"4.5 OT hours",
    outcome:"Approve and the overtime is included with this week's hours. Reply if you want Jay to explain it first.",
    summary:"Outside your normal rule. Needs your call. Nothing posts to payroll until you decide.",
    source:"Because the Timekeeper folded Frank Park's WhatsApp reply at 7:42 AM.",
    channelHistory:[
      ["whatsapp", "7:08 AM", "Timekeeper asked Frank for missing Oakwood hours."],
      ["whatsapp", "7:42 AM", "Frank replied that Jay worked 12.5 hours, including 4.5 overtime."],
      ["inapp", "7:44 AM", "Timekeeper held the overtime instead of posting it."],
    ],
    whyYou:"Jay's overtime is outside your rule, so the office cannot enter it without your call.",
    actions:["Approve", "Reply", "Open"],
    details:{
      title:"Overtime needs your call",
      summary:"Jay's 4.5 OT hours are outside your normal rule for Oakwood framing.",
      facts:["4.5 OT hours", "Oakwood framing", "payroll not posted"],
      ready:["Frank Park's missing-hours reply is already folded into the week."],
      held:["Jay's overtime stays out until you approve or ask him a question."],
      outside:"Approved hours can be entered into QuickBooks Time after approval.",
    },
  },
  {
    group:"Money questions",
    rowTitle:"Money questions",
    rowMeta:["Why are Glenrose framing costs up?", "answer drafted"],
    inbox:{
      channel:"In-app",
      time:"6:31 AM",
      state:"Answer ready",
      preview:"You asked yesterday. The office pulled bills, hours, and receipts and drafted the answer.",
    },
    count:1,
    tone:"ink",
    title:"Why are Glenrose framing costs up?",
    house:"Glenrose",
    amount:"3 bills · 4 time entries · 2 receipts",
    outcome:"Open and read the answer. Nothing changes unless you ask for a follow-up.",
    summary:"Answer drafted from bills, approved hours, and receipts.",
    source:"Because you asked yesterday.",
    channelHistory:[
      ["inapp", "Yesterday 4:12 PM", "You asked why Glenrose framing costs were up."],
      ["inapp", "6:20 AM", "Accounting Analyst pulled bills, hours, and receipts."],
      ["inapp", "6:31 AM", "Answer drafted with links to source evidence."],
    ],
    whyYou:"This is your question. Nothing is sent or changed until you ask for a follow-up.",
    actions:["Open"],
    details:{
      title:"Glenrose framing cost answer",
      summary:"Framing is $2,140 over. The answer points to lumber price, Saturday overtime, and small crew swaps.",
      facts:["$640 lumber", "$1,500 overtime", "2 receipts attached"],
      ready:[
        "Northern Lumber invoice came in 8% higher than the rule estimate.",
        "Saturday overtime added $1,500 after the rain delay.",
        "Mateo's absences shifted hours but did not add much cost.",
      ],
      held:["Oakwood drywall is not over yet, but the quote is 4% higher than rule-priced."],
      outside:"This is just an answer for you. Nothing is sent.",
    },
  },
];

const TODAY_INLINE_ROWS_V5 = [
  {
    id:"bills",
    tone:"green",
    type:"questions",
    title:"Approve $24,318 in bills for QuickBooks",
    summary:"4 are ready. 2 questions first:",
    meta:"Email · 7:14 AM · Bookkeeper",
    primary:"Approve all 6 now",
    disabledHint:"Answer 2 questions above first.",
    confirmPrimary:"Post $24,318 to QuickBooks — confirm?",
    audit:"Full bill batch",
    questions:[
      {
        id:"patel-code",
        text:"Patel Excavation $4,200 — no cost code yet. Use which one?",
        chips:["Site work 1100", "Excavation 2200", "Pick another…"],
      },
      {
        id:"sherwin-dupe",
        text:"Sherwin-Williams $812 — looks like a duplicate of Tuesday's invoice. Pay both?",
        chips:[
          "It's a duplicate, skip it",
          { label:"Pay both, not a dupe", confirm:"$812 to Sherwin-Williams — confirm?" },
        ],
      },
    ],
  },
  {
    id:"field",
    tone:"amber",
    type:"draft",
    title:"Send Marco a third nudge — St Leonards quiet 2 days.",
    summary:"",
    meta:"WhatsApp · 7:30 AM · Field Intake",
    primary:"Send as is",
    audit:"Open thread",
    draftLabel:"Drafted WhatsApp to Marco:",
    draft:"Hey Marco — haven't had a log from St Leonards since Monday. Quick voice note when you can? Thanks.",
    secondary:["Edit", "Call him instead"],
  },
  {
    id:"homeowner",
    tone:"navy",
    type:"draft",
    title:"Send Thursday update to the Glenroses.",
    summary:"",
    meta:"Email · 6:48 AM · Client Liaison",
    primary:"Send to the Glenroses",
    confirmPrimary:"Send Thursday update to the Glenroses — confirm?",
    audit:"Preview full update",
    draftLabel:"Preview:",
    draft:"Framing is well underway. The second floor is taking shape, lumber arrived on time, and next milestones are roof framing and windows.",
    draftNote:"Plus 5 photos selected and the next 60 days of milestones.",
    secondary:["Edit first", "Hold a day"],
  },
  {
    id:"hours",
    tone:"amber",
    type:"questions",
    title:"Jay Patel logged 4.5h overtime — outside your rule.",
    summary:"",
    meta:"WhatsApp · 7:42 AM · Timekeeper",
    primary:"Confirm answer",
    disabledHint:"Choose one answer above first.",
    confirmPrimary:"Confirm Jay Patel overtime answer?",
    audit:"Open hours",
    questions:[
      {
        id:"jay-ot",
        text:"Approve this once, or change the rule for Jay going forward?",
        chips:["Approve once", "Approve and update Jay's rule", "Reject — ask him to explain"],
      },
    ],
  },
  {
    id:"money",
    tone:"ink",
    type:"answer",
    title:"Answer ready: \"Why are Glenrose framing costs up?\"",
    summary:"Short version: Three lumber bills came in 12% over the budget rule, and the framer billed 14 extra crew-hours on the gable detail.",
    meta:"In-app · 6:31 AM · Accounting Analyst",
    primary:"Read the full answer",
    audit:"Show sources",
    secondary:["Ask a follow-up"],
  },
];

const CHANNEL_ICON_V3 = {
  email:"Email",
  whatsapp:"WhatsApp",
  phone:"Phone",
  inapp:"In-app",
};

const ROLE_TODAY_V3 = {
  owner:{
    label:"Aaron",
    role:"Owner",
    intro:"Money, homeowner messages, scope changes, overtime, and owner-only decisions.",
    items:TODAY_ITEMS_V2,
  },
  pm:{
    label:"Sam",
    role:"Project Manager",
    intro:"Field gaps, schedule risk, trade calls, and escalated site issues on assigned jobs.",
    items:[
      TODAY_ITEMS_V2[1],
      {
        group:"Trade follow-up",
        rowTitle:"Trade follow-up",
        rowMeta:["Pella windows", "St Leonards", "phone call needed"],
        count:1,
        tone:"amber",
        title:"Pella windows still has not sent model numbers.",
        house:"St Leonards",
        amount:"24 openings",
        outcome:"Reply and the office prepares your call note or tries another approved vendor.",
        summary:"The window quote is holding up the St Leonards budget and first schedule dates.",
        source:"Because Pella has not replied to email or WhatsApp follow-up.",
        channelHistory:[
          ["email", "Tue 2:14 PM", "Emailed Sarah at Pella with plan pages and opening counts."],
          ["whatsapp", "Wed 9:02 AM", "WhatsApp follow-up sent to Sarah."],
          ["email", "Thu 8:00 AM", "Second email sent and regional rep copied."],
        ],
        whyYou:"Sarah usually only moves after a direct call. The office cannot escalate in your name without you.",
        actions:["Reply", "Open"],
      },
      {
        group:"Schedule risk",
        rowTitle:"Schedule risk",
        rowMeta:["Glenrose", "roofing lead time", "due by 5 PM"],
        count:1,
        tone:"amber",
        title:"Hartman roofing lead time is due by 5 PM.",
        house:"Glenrose",
        amount:"1 schedule placeholder",
        outcome:"Open and see the quote thread before you call the trade.",
        summary:"The schedule can stay as-is if Hartman confirms the date today.",
        source:"Because Hartman promised the office an answer by 5 PM.",
        channelHistory:[
          ["email", "Wed 4:00 PM", "Roofing quote request sent with roof plan attached."],
          ["phone", "Wed 5:18 PM", "Office logged Hartman's promise for Thursday by 5 PM."],
          ["inapp", "Thu 7:10 AM", "Scheduler kept the date as Waiting on Hartman Roofing."],
        ],
        whyYou:"This belongs to the PM because the job can still stay on track if the trade is chased today.",
        actions:["Open"],
      },
    ],
  },
  bookkeeper:{
    label:"Dana",
    role:"Bookkeeper",
    intro:"Vendor mappings, duplicates, cost codes, and routine hours that do not need the owner.",
    items:[
      {
        group:"Unmapped vendor",
        rowTitle:"Unmapped vendor",
        rowMeta:["Patel Excavation", "5 bills", "choose cost code"],
        count:1,
        tone:"amber",
        title:"Patel Excavation has no cost code yet.",
        house:"Oakwood",
        amount:"5 bills",
        outcome:"Open and choose the cost code once. Future Patel bills will use it.",
        summary:"Five bills are waiting on one vendor mapping.",
        source:"Because the Bookkeeper matched the vendor but not the cost code.",
        channelHistory:[
          ["email", "6:02 AM", "Five Patel Excavation invoices found in Gmail."],
          ["inapp", "6:11 AM", "Vendor matched to Oakwood, cost code missing."],
          ["inapp", "6:15 AM", "Routine bills held for bookkeeper mapping."],
        ],
        whyYou:"This is under the owner approval threshold, but the office needs your bookkeeping judgment once.",
        actions:["Open"],
      },
      {
        group:"Possible duplicate",
        rowTitle:"Possible duplicate",
        rowMeta:["Sherwin-Williams", "$1,284.12", "confirm"],
        count:1,
        tone:"amber",
        title:"Sherwin-Williams may be a duplicate.",
        house:"St Leonards",
        amount:"$1,284.12",
        outcome:"Approve only if this is not the same bill.",
        summary:"Invoice number, vendor, and amount match a bill already in the batch.",
        source:"Because the office will not risk paying the same bill twice.",
        channelHistory:[
          ["email", "6:33 AM", "Invoice copy found in vendor email."],
          ["inapp", "6:35 AM", "Matched against today's batch."],
          ["inapp", "6:36 AM", "Duplicate risk held before write."],
        ],
        whyYou:"This is a bookkeeping call. It stays off the owner's Today unless you escalate it.",
        actions:["Approve", "Open"],
      },
    ],
  },
  super:{
    label:"Marco",
    role:"Site Super",
    intro:"Phone-first prompts for logs, photos, and field questions from the office.",
    items:[
      {
        group:"Daily log",
        rowTitle:"Daily log",
        rowMeta:["Glenrose", "dictate today's note", "1 tap"],
        count:1,
        tone:"green",
        title:"Send today's Glenrose log.",
        house:"Glenrose",
        amount:"voice note · photos",
        outcome:"Reply and the office turns your voice note into the daily log.",
        summary:"Tap Reply, dictate what happened, and attach photos. The office writes the log.",
        source:"Because the Glenrose log is expected between 7:30 and 8:00 AM.",
        channelHistory:[
          ["whatsapp", "7:30 AM", "Office sent your daily log prompt."],
          ["inapp", "7:45 AM", "No log received yet."],
        ],
        whyYou:"You are the person on site. The office can write the log, but only you can say what happened today.",
        actions:["Reply", "Open"],
      },
      {
        group:"Field question",
        rowTitle:"Field question",
        rowMeta:["Stair detail", "5 photos requested"],
        count:1,
        tone:"amber",
        title:"Office needs stair detail photos.",
        house:"Glenrose",
        amount:"5 photos",
        outcome:"Reply with photos and the office files them to the job.",
        summary:"The architect question needs clear photos before it can be answered.",
        source:"Because the Field Intake teammate could not confirm the detail from yesterday's photos.",
        channelHistory:[
          ["whatsapp", "7:50 AM", "Office asked for close photos of the stair opening."],
          ["inapp", "8:05 AM", "RFI draft held until photos arrive."],
        ],
        whyYou:"Only the site super can capture the field condition today.",
        actions:["Reply", "Open"],
      },
    ],
  },
};

const STAFF_PAGE_DATA_V2 = {
  bookkeeper:{
    name:"Bookkeeper",
    type:"Daily staff",
    sentence:"The Bookkeeper reads vendor bills and codes them to the right project and cost code in QuickBooks.",
    note:"You usually handle the Bookkeeper's work from Today. Open this page when you want to see everything they have done, fix a vendor mapping, or ask them a question directly.",
    can:[
      "Read any vendor bill, PDF, email, or photo, and pull out vendor, amount, and due date.",
      "Match each bill to the right house and cost code.",
      "Catch likely duplicates before you pay twice.",
      "Push approved bills to QuickBooks in a batch.",
      "Chase vendors when bills are missing.",
    ],
    strip:{ title:"Patel Excavation has no cost code yet.", body:"Five bills are waiting on this. Choose a cost code once and it will apply to all of them and any future Patel bills.", actions:["Open"] },
    needs:[
      TODAY_ITEMS_V2[0],
      { tone:"amber", title:"Possible duplicate · Sherwin-Williams.", house:"St Leonards", amount:"$1,284.12", outcome:"Approve only if this is not the same bill.", summary:"This invoice matches a bill already in today's batch. Held until you confirm.", source:"Because the Bookkeeper found a matching invoice number.", actions:["Approve", "Reply", "Open"] },
    ],
    done:["Scanned 10 bills from Gmail and Drive.", "Matched 9 to a house and cost code.", "Held 1 possible duplicate for you.", "Pushed 0 of 6 approved bills to QuickBooks, waiting on your approval above."],
    ask:["Any bills missing for Oakwood?", "Show all Sherwin-Williams bills this month.", "What's the biggest bill this week?"],
  },
  field:{
    name:"Field Intake",
    type:"Daily staff",
    sentence:"Field Intake turns texts, voice notes, and photos into daily logs.",
    note:"Open this page when you want to check what came in from each site or ask a site super for missing information.",
    can:["Turn voice notes into daily logs.", "File photos to the right job.", "Spot missing logs before the schedule gets stale.", "Pull receipt images into bill intake.", "Prepare follow-up questions for site supers."],
    siteStatus:["Glenrose: 1 voice note, 5 photos.", "Oakwood: nothing yet.", "St Leonards: 2 days quiet.", "Humber: 3 photos, no issue."],
    needs:[TODAY_ITEMS_V2[1]],
    done:["Filed 22 photos to Drive.", "Drafted 4 daily logs.", "Prepared 1 follow-up to St Leonards.", "Sent 1 receipt image to the Bookkeeper."],
    ask:["Which jobs are quiet today?", "Show Glenrose photos from this morning.", "Ask Marco what is blocking the stair detail."],
  },
  scheduler:{
    name:"Scheduler",
    type:"Daily staff",
    sentence:"The Scheduler keeps dates honest and flags slipping work before it becomes a surprise.",
    note:"Every date is marked Known, Placeholder, or Waiting on a trade. No bare dates.",
    can:["Build schedule drafts from the approved budget.", "Mark which dates are real and which are placeholders.", "Use daily logs to flag slipping work.", "Prepare trade follow-ups for missing dates.", "Update homeowner milestones only after approval."],
    banner:"Schedule built from St Leonards budget v3. When the budget changes, you approve the new schedule.",
    scheduleLines:[
      ["Demo", "Known", "Started Apr 8", "from approved budget"],
      ["Underpinning", "Known", "Started May 1", "from foundation quote"],
      ["Framing", "Placeholder", "Aug 1", "crew plan not confirmed"],
      ["Roofing", "Waiting on Hartman Roofing", "Oct 1", "quote lead time missing"],
      ["Windows", "Waiting on Pella", "Oct 14", "model numbers missing"],
    ],
    needs:[TODAY_ITEMS_V2[1]],
    done:["Checked 4 active job schedules.", "Held St Leonards because daily logs are missing.", "Marked roofing and window dates as waiting on trades.", "Prepared homeowner-safe milestones for Glenrose."],
    ask:["Any project slipping?", "What dates are still placeholders?", "What changes if the roofing quote comes in late?"],
  },
  client:{
    name:"Client Liaison",
    type:"Daily staff",
    sentence:"The Client Liaison turns approved job progress into homeowner updates.",
    note:"Open this page to preview exactly what the homeowner will see.",
    can:["Draft weekly homeowner updates.", "Choose approved photos.", "Hide vendor issues, money notes, and private staff questions.", "Prepare replies to homeowner questions.", "Keep the homeowner portal current."],
    filtered:["Vendor issues hidden.", "Money notes hidden.", "Internal staff questions hidden."],
    needs:[TODAY_ITEMS_V2[2]],
    done:["Drafted Thursday Glenrose update.", "Selected 5 approved photos.", "Removed vendor issues from the homeowner view.", "Prepared next 60 days of milestones."],
    ask:["Preview what the Glenroses will see.", "What did we leave out of the homeowner update?", "Draft a plain-language answer about tile allowance."],
  },
  timekeeper:{
    name:"Timekeeper",
    type:"Daily staff",
    sentence:"The Timekeeper collects crew hours and flags exceptions before they enter QuickBooks Time.",
    note:"Open this page when hours are missing or overtime needs your call.",
    can:["Read hours from texts, photos, and replies.", "Ask employees to enter missing hours.", "Flag overtime outside your rule.", "Stage approved hours for QuickBooks Time.", "Keep job labor tied to the right house."],
    counters:[["Approved this week", "87"], ["Waiting on you", "1"], ["Waiting on the crew", "2"]],
    needs:[TODAY_ITEMS_V2[3]],
    done:["Folded Frank Park's reply into Oakwood hours.", "Prepared a follow-up to Carlos Diaz.", "Staged 12 regular hour entries.", "Held Jay Patel overtime for your call."],
    ask:["Who still owes hours?", "Show Oakwood hours this week.", "Ask Carlos to enter yesterday's time."],
  },
  accounting:{
    name:"Accounting Analyst",
    type:"Daily staff",
    sentence:"The Accounting Analyst answers money questions from bills, budgets, receipts, and approved hours.",
    note:"This page is a question log. Ask money questions here, or from Today.",
    can:["Explain why a job is over budget.", "Compare actual bills to the budget.", "Pull source receipts into an answer.", "Show this week's cash needs.", "Flag a cost line to watch."],
    questions:[
      ["Why are Glenrose framing costs up?", "Draft answer ready from 3 bills, 4 time entries, and 2 receipts."],
      ["Any bills missing for Oakwood?", "Bookkeeper found no framing bills in 9 days."],
      ["Show this week's cash needs.", "$42,610 expected if all approved bills post."],
    ],
    needs:[TODAY_ITEMS_V2[4]],
    done:["Answered Glenrose framing question.", "Pulled Oakwood drywall quote for watch list.", "Compared approved hours against the budget.", "Linked every answer to source evidence."],
    ask:["Show this week's cash needs.", "Why is St Leonards site prep over?", "Which cost code changed most this month?"],
  },
  takeoff:{
    name:"Takeoff Reader",
    type:"Client onboarding",
    sentence:"The Takeoff Reader reads a plan set, builds the rule-priced budget, and hands off quotes and schedule work.",
    note:"Open this page when a new client signs, a plan revision lands, or you want to see how the budget and first schedule were created.",
    can:[
      "Read any new plan set, PDF or Drive folder, and count square footage, openings, linear feet, fixtures, and scope quantities.",
      "Generate the draft budget from your rules and mark what still needs a real quote.",
      "Flag missing dimensions, drawing contradictions, and scope gaps before trades are asked.",
      "Create the communication plan for Trade Coordinator to request quotes.",
      "Hand scope, assumptions, and placeholder dates to Scheduler for the first schedule.",
    ],
    needs:[
      {
        tone:"amber",
        rowTitle:"Takeoff question",
        rowMeta:["St Leonards", "basement ceiling height", "before pricing"],
        title:"Confirm basement ceiling height.",
        house:"St Leonards",
        amount:"28 sheets",
        outcome:"Reply and the Takeoff Reader updates drywall, stairs, framing, budget rules, and quote requests before handoff.",
        summary:"The plan set shows two different basement ceiling heights.",
        source:"Because the Takeoff Reader found a drawing conflict.",
        channelHistory:[
          ["inapp", "6:18 AM", "Takeoff Reader read the R4 plan set."],
          ["inapp", "6:42 AM", "47 lines counted and 3 questions raised."],
          ["email", "6:50 AM", "Architect note checked, but the height conflict stayed unresolved."],
        ],
        whyYou:"The office can read and price the plans, but only you can decide which assumption should drive the starting budget.",
        actions:["Reply", "Open"],
      },
    ],
    done:[
      "Read R4 plan set, 28 sheets.",
      "Counted 47 takeoff lines.",
      "Generated a $634,710 draft budget from your rules.",
      "Built the quote plan for Trade Coordinator.",
      "Handed scope and placeholder dates to Scheduler.",
    ],
    ask:["How many windows on Glenrose?", "Did R4 change the basement footprint?", "Show the budget rules used for roofing."],
  },
  estimator:{
    name:"Estimator",
    type:"Project kickoff",
    sentence:"The Estimator turns a new architect plan into a draft budget.",
    note:"This is rare kickoff work. The Takeoff Reader turns a plan set into a rule-priced budget, a quote plan, and a schedule handoff.",
    can:["Read architect plans and spec sheets.", "Apply your budget rules and generate the draft budget.", "Mark each line Rule-priced, Quote pending, or Real quote.", "Create the communication plan for Trade Coordinator.", "Hand scope and dates to Scheduler for the first schedule."],
    upload:true,
    budgetLines:[
      ["Exterior brick", "Rule-priced", "$84,200"],
      ["Windows", "Quote pending", "Pella waiting on model numbers"],
      ["Roofing", "Quote pending", "email drafted"],
      ["Plumbing", "Real quote from JMO Plumbing", "$51,150"],
      ["Hardwood", "Rule-priced", "3,100 sq ft"],
    ],
    needs:[
      { tone:"green", title:"Upload or email a new St Leonards plan.", house:"St Leonards", amount:"28 sheets", outcome:"Open and the Estimator starts a draft budget using your rules.", summary:"Drop the architect's plan. I will draft a budget and ask trades for real quotes. Results show up in Today tomorrow.", source:"Because project kickoff starts with plans.", actions:["Open"] },
    ],
    done:["Read Glenrose R4 drawing set.", "Prepared draft budget lines.", "Marked windows and roofing as quote pending.", "Exported one Excel budget draft."],
    ask:["What budget lines are still quote pending?", "Export the St Leonards budget to Excel.", "What assumptions did you use for hardwood?"],
  },
  trade:{
    name:"Trade Coordinator",
    type:"Project kickoff",
    sentence:"The Trade Coordinator asks trades for real quotes and folds replies into the budget.",
    note:"Open this page when a budget needs trade numbers to get over the line.",
    can:["Draft quote request emails.", "Attach the right plan pages and schedules.", "Track who replied and who did not.", "Ask follow-up questions when information is missing.", "Fold real trade numbers into the budget after approval."],
    quotes:[
      ["Windows", "Sent", "Pella has the window schedule."],
      ["Roofing", "Sent", "Hartman has the roof plan."],
      ["Drywall", "Replied", "Number ready to fold into budget."],
      ["HVAC", "Missing info", "Needs equipment model confirmation."],
      ["Plumbing", "Folded into budget", "JMO quote approved."],
    ],
    needs:[
      { tone:"green", title:"Send 3 quote requests.", house:"St Leonards", amount:"windows, roofing, drywall", outcome:"Approve and the emails go to trades with the right attachments.", summary:"Each email has the scope summary, plan pages, and reply instructions.", source:"Because the Estimator marked these lines Quote pending.", actions:["Approve", "Open"] },
    ],
    done:["Sent 2 trade requests.", "Folded JMO Plumbing quote into budget.", "Held HVAC because model numbers are missing.", "Prepared one follow-up for Pella."],
    ask:["Who has not replied?", "Show drywall quote details.", "What quote is blocking the budget?"],
  },
  support:{
    name:"File Clerk",
    type:"Behind the scenes",
    sentence:"The File Clerk sorts every email, text, upload, and source file into the right job and teammate.",
    note:"You usually do not need to open this. Open only if something looks miscategorized.",
    can:["Sort email, texts, Drive uploads, and QuickBooks notices.", "Attach each source to the right house.", "Send bills to the Bookkeeper.", "Send field messages to Field Intake.", "Keep source evidence linked."],
    needs:[
      { tone:"amber", title:"QuickBooks mapping question needs a house.", house:"Oakwood or Glenrose", amount:"1 vendor notice", outcome:"Open and choose where this notice belongs.", summary:"The source mentions a vendor but no address.", source:"Because the File Clerk will not guess the house.", actions:["Open"] },
    ],
    done:["Sorted 14 source items.", "Attached 9 items to houses.", "Sent 3 bills to the Bookkeeper.", "Sent 2 field messages to Field Intake."],
    ask:["What came in overnight?", "Show anything not assigned to a house.", "Why did this email go to the Bookkeeper?"],
  },
};

const HISTORY_ROWS_V2 = [
  ["7:42 AM", "Bookkeeper", "Aaron", "Bill batch approved", "Gmail and Drive bills", "QuickBooks", "6 bills written"],
  ["7:36 AM", "Field Intake", "auto", "WhatsApp reminder sent", "St Leonards daily log thread", "WhatsApp", "Marco reminder in flight"],
  ["7:18 AM", "Trade Coordinator", "Aaron", "Pella follow-up email sent", "Window schedule thread", "Gmail", "Regional rep copied"],
  ["7:38 AM", "Field Intake", "Aaron", "Photos filed from WhatsApp", "Marco voice note and photos", "Drive", "5 photos saved"],
  ["7:31 AM", "Field Intake", "Aaron", "Daily log approved", "Glenrose daily log draft", "Job record", "Log published"],
  ["7:18 AM", "Trade Coordinator", "Aaron", "Quote request sent", "Window schedule", "Gmail", "Pella request sent"],
  ["6:48 AM", "Client Liaison", "Aaron", "Homeowner update queued", "Glenrose weekly update", "Gmail", "Waiting for approval"],
  ["6:54 AM", "Estimator", "Aaron", "Budget held", "Plan review", "Budget", "Waiting on window model numbers"],
];

const STAFF_HISTORY_V4 = {
  bookkeeper:[
    ["7:14 AM", "Email to Aaron", "Bills ready after Dana answered 2 questions.", "Glenrose, Oakwood, St Leonards"],
    ["6:41 AM", "Held duplicate", "Sherwin-Williams invoice matched today's batch.", "St Leonards"],
    ["6:24 AM", "Matched bills", "6 bills matched to vendor, job, cost code, and due date.", "3 jobs"],
  ],
  field:[
    ["7:42 AM", "WhatsApp received", "Marco sent Glenrose voice note and 5 photos.", "Glenrose"],
    ["7:30 AM", "WhatsApp sent", "Reminder sent to Marco for St Leonards daily log.", "St Leonards"],
    ["6:55 AM", "Held schedule note", "No St Leonards log in 2 days, Scheduler notified.", "St Leonards"],
  ],
  scheduler:[
    ["7:20 AM", "Schedule hold", "Roofing date marked Waiting on Hartman Roofing.", "Glenrose"],
    ["7:10 AM", "First schedule", "Placeholder dates built from scope and budget assumptions.", "St Leonards"],
    ["6:55 AM", "Field signal", "Daily log gap blocked downstream schedule movement.", "St Leonards"],
  ],
  client:[
    ["6:48 AM", "Email draft", "Thursday homeowner update prepared and held for approval.", "Glenrose"],
    ["6:26 AM", "Filtered notes", "Vendor issues and money notes removed from homeowner view.", "Glenrose"],
    ["Yesterday 4:10 PM", "Homeowner question", "Tile allowance question routed to Client Liaison.", "Glenrose"],
  ],
  timekeeper:[
    ["7:42 AM", "WhatsApp received", "Frank replied with Jay's 12.5 hours and 4.5 overtime.", "Oakwood"],
    ["7:08 AM", "WhatsApp sent", "Asked Frank for missing Oakwood hours.", "Oakwood"],
    ["6:50 AM", "Hours staged", "12 regular time entries staged for QuickBooks Time.", "3 jobs"],
  ],
  accounting:[
    ["6:31 AM", "Answer drafted", "Glenrose framing cost answer prepared from bills, hours, and receipts.", "Glenrose"],
    ["6:20 AM", "Evidence pulled", "3 bills, 4 time entries, and 2 receipts linked.", "Glenrose"],
    ["Yesterday 4:12 PM", "Question received", "Aaron asked why Glenrose framing costs are up.", "Glenrose"],
  ],
  takeoff:[
    ["7:06 AM", "Budget generated", "$634,710 draft budget created from rules and R4 plan counts.", "St Leonards"],
    ["6:58 AM", "Quote plan created", "Windows, roofing, drywall, and HVAC marked for trade quotes.", "St Leonards"],
    ["6:42 AM", "Plan questions", "Basement ceiling height and window models flagged before handoff.", "St Leonards"],
  ],
  trade:[
    ["8:00 AM", "Email sent", "Second Pella follow-up sent, regional rep copied.", "St Leonards"],
    ["7:18 AM", "Quote request sent", "Roofing quote request sent to Hartman with roof plan.", "Glenrose"],
    ["6:14 AM", "Email received", "JMO Plumbing quote received and folded into budget.", "St Leonards"],
  ],
  support:[
    ["6:16 AM", "Sorted source", "Drive plan revision filed to St Leonards plan folder.", "St Leonards"],
    ["6:08 AM", "Sorted source", "Gmail vendor bill routed to Bookkeeper.", "Glenrose"],
    ["6:02 AM", "Sorted message", "WhatsApp photos routed to Field Intake.", "Glenrose"],
  ],
};

const ScreenShellV2 = ({ children, className = "" }) => (
  <div className={"screen v2-screen " + className}>
    {children}
  </div>
);

const AskBarV2 = ({ title = "Ask anything about your jobs.", examples = [] }) => (
  <section className="ask-bar-v2">
    <div className="ask-input-v2">
      <Icon name="ask" size={18} />
      <input aria-label={title} placeholder={title} />
      <button type="button">Ask</button>
    </div>
    <div className="ask-examples-v2">
      {examples.map((example) => <button type="button" key={example}>{example}</button>)}
    </div>
  </section>
);

const ChannelHistoryV3 = ({ item }) => {
  const history = item.channelHistory || [["inapp", "Now", item.source || "The office held this for you."]];
  return (
    <div className="channel-story-v3">
      <h4>What the office tried</h4>
      <div className="channel-history-v3">
        {history.map(([channel, time, note]) => (
          <p key={channel + time + note}>
            <span>{CHANNEL_ICON_V3[channel] || channel}</span>
            <b>{time}</b>
            {note}
          </p>
        ))}
      </div>
      <h4>Why this is on you</h4>
      <p className="why-you-v3">{item.whyYou || item.source || "The office cannot move this without a human decision."}</p>
    </div>
  );
};

const staffForNeedV4 = (item) => {
  const label = item.group || item.rowTitle || item.title || "";
  if (label.includes("Bills")) return "Bookkeeper";
  if (label.includes("Field")) return "Field Intake";
  if (label.includes("Homeowner")) return "Client Liaison";
  if (label.includes("Crew")) return "Timekeeper";
  if (label.includes("Money")) return "Accounting Analyst";
  return "the office";
};

const NeedItemChatV4 = ({ item }) => {
  const staffName = staffForNeedV4(item);
  const title = item.rowTitle || item.group || item.title;
  return (
    <div className="need-chat-v4">
      <div>
        <h4>Chat about this</h4>
        <p>This opens with {staffName}, with {title} attached.</p>
      </div>
      <div className="need-chat-box-v4">
        <textarea
          aria-label={"Chat with " + staffName + " about " + title}
          placeholder={`Ask ${staffName} about this, or reply with what you want done.`}
        />
        <button type="button">Chat</button>
      </div>
    </div>
  );
};

const NeedSimpleDetailsV4 = ({ item }) => {
  const details = item.details || {};
  const ready = details.ready || [];
  const held = details.held || [];
  return (
    <div className="need-simple-v4">
      <div className="need-simple-summary-v4">
        {(details.facts || [item.house, item.amount]).filter(Boolean).map((fact) => <b key={fact}>{fact}</b>)}
      </div>
      {held.length > 0 && (
        <section>
          <h4>Questions</h4>
          {held.slice(0, 3).map((line) => <p key={line}>{line}</p>)}
        </section>
      )}
      {ready.length > 0 && (
        <section>
          <h4>Ready</h4>
          {ready.slice(0, 4).map((line) => <p key={line}>{line}</p>)}
        </section>
      )}
      <section>
        <h4>Next</h4>
        <p>{details.outside || item.outcome}</p>
      </section>
    </div>
  );
};

const NeedCardV2 = ({ item, simple = false }) => {
  const [expanded, setExpanded] = React.useState(false);
  const rowTitle = item.rowTitle || item.group || item.title;
  const rowMeta = item.rowMeta || [item.house, item.amount].filter(Boolean);
  const actions = item.actions || ["Open"];
  const sourceLine = item.inbox
    ? [item.inbox.channel, item.inbox.time, item.inbox.state, ...rowMeta].filter(Boolean).join(" · ")
    : rowMeta.join(" · ");

  return (
    <article
      className={"need-card-v2 t-" + (item.tone || "ink") + (expanded ? " is-expanded" : "")}
      role="button"
      tabIndex="0"
      onClick={() => setExpanded((open) => !open)}
      onKeyDown={(event) => {
        if (event.key === "Enter" || event.key === " ") {
          event.preventDefault();
          setExpanded((open) => !open);
        }
      }}
    >
      <div className="need-line-v2">
        <div className="need-line-copy-v2">
          <h3>
            {rowTitle}
            {item.count > 1 && <span className="need-count-pill-v2">{item.count}</span>}
          </h3>
          {item.inbox
            ? <>
                <p>{item.inbox.preview}</p>
                <small className="need-row-meta-v4">{sourceLine}</small>
              </>
            : <p>{rowMeta.join(" · ")}</p>}
        </div>
        <div className="need-actions-v2">
          {actions.map((action) => (
            <button
              key={action}
              className={action.startsWith("Approve") ? "is-primary" : ""}
              type="button"
              onClick={(event) => {
                event.stopPropagation();
                if (action === "Open") setExpanded((open) => !open);
                if (action === "Reply") setExpanded(true);
              }}
            >
              {action}
            </button>
          ))}
        </div>
      </div>
      {expanded && (
        <div className="need-expanded-v2">
          <p>{item.summary}</p>
          {simple ? (
            <>
              <NeedSimpleDetailsV4 item={item} />
              <NeedItemChatV4 item={item} />
            </>
          ) : (
            <>
              <div className="need-facts-v2">
                <span><b>House</b>{item.house}</span>
                <span><b>Money/time</b>{item.amount}</span>
                <span><b>If approved</b>{item.outcome}</span>
              </div>
              <ChannelHistoryV3 item={item} />
              <NeedItemChatV4 item={item} />
            </>
          )}
        </div>
      )}
    </article>
  );
};

const StaffNeedListV2 = ({ needs = [] }) => (
  <div className="staff-needs-v2">
    {needs.map((item) => <NeedCardV2 key={item.title || item.rowTitle} item={item} simple />)}
  </div>
);

const DetailsSheetV2 = ({ item, action, onClose }) => {
  if (!item) return null;
  const details = item.details || {
    title:item.title,
    summary:item.summary,
    facts:[item.house, item.amount],
    ready:[item.outcome],
    held:[],
    outside:"Nothing changes until you approve.",
  };

  return (
    <div className="details-backdrop-v2" onClick={onClose}>
      <section className="details-sheet-v2" role="dialog" aria-modal="true" aria-label={details.title} onClick={(e) => e.stopPropagation()}>
        <button className="details-close-v2" type="button" aria-label="Close" onClick={onClose}>
          <Icon name="x" size={16} />
        </button>
        <div className="details-head-v2">
          <span>{action || "Open"}</span>
          <h2>{details.title}</h2>
          <p>{details.summary}</p>
        </div>
        <div className="details-facts-v2">
          {(details.facts || []).map((fact) => <b key={fact}>{fact}</b>)}
        </div>
        <div className="details-columns-v2">
          <div>
            <h3>Ready</h3>
            {(details.ready || []).map((line) => <p key={line}>{line}</p>)}
          </div>
          <div>
            <h3>Held or hidden</h3>
            {(details.held && details.held.length ? details.held : ["Nothing else needs you."]).map((line) => <p key={line}>{line}</p>)}
          </div>
        </div>
        <div className="outside-note-v2">
          <Icon name="lock" size={14} />
          <span>{details.outside}</span>
        </div>
        <div className="details-actions-v2">
          <button type="button" onClick={onClose}>Open</button>
          {item.actions && item.actions.includes("Reply") && <button type="button" onClick={onClose}>Reply</button>}
          {item.actions && item.actions.includes("Approve") && <button className="is-primary" type="button" onClick={onClose}>Approve</button>}
        </div>
      </section>
    </div>
  );
};

const InlineTodayRowV5 = ({
  row,
  answers,
  completed,
  editing,
  draftText,
  onChip,
  onPrimary,
  onSecondary,
  onDraftChange,
}) => {
  const allAnswered = !row.questions || row.questions.every((question) => answers?.[question.id]);
  const isDone = !!completed?.[row.id];
  const disabled = row.type === "questions" && !allAnswered;

  return (
    <article className={"inline-row-v5 t-" + row.tone + (isDone ? " is-done" : "")}>
      <div className="inline-row-main-v5">
        <header className="inline-row-head-v5">
          <h3>{row.title}</h3>
          {row.summary && <p>{row.summary}</p>}
          <small>{row.meta}</small>
        </header>

        {row.questions && (
          <div className="inline-question-stack-v5">
            {row.questions.map((question) => (
              <section className="inline-question-v5" key={question.id}>
                <p>{question.text}</p>
                <div className="inline-chip-row-v5">
                  {question.chips.map((chip) => {
                    const option = typeof chip === "string" ? { label:chip } : chip;
                    const selected = answers?.[question.id] === option.label;
                    return (
                      <button
                        key={option.label}
                        type="button"
                        className={selected ? "is-selected" : ""}
                        onClick={() => onChip(row, question, option)}
                      >
                        {option.label}
                      </button>
                    );
                  })}
                </div>
              </section>
            ))}
          </div>
        )}

        {row.type === "draft" && (
          <section className="inline-draft-v5">
            <p>{row.draftLabel}</p>
            {editing?.[row.id] ? (
              <div className="inline-draft-edit-v5">
                <textarea
                  aria-label={"Edit draft for " + row.title}
                  value={draftText[row.id] ?? row.draft}
                  onChange={(event) => onDraftChange(row.id, event.target.value)}
                />
                <div>
                  <button type="button" onClick={() => onSecondary(row, "Save draft")}>Save draft</button>
                  <button type="button" onClick={() => onSecondary(row, "Cancel edit")}>Cancel</button>
                </div>
              </div>
            ) : (
              <blockquote>“{draftText[row.id] ?? row.draft}”</blockquote>
            )}
            {row.draftNote && <small>{row.draftNote}</small>}
          </section>
        )}

        {row.type === "answer" && (
          <section className="inline-answer-v5">
            <p>{row.summary}</p>
          </section>
        )}

        {row.secondary && (
          <div className="inline-secondary-v5">
            {row.secondary.map((action) => (
              <button type="button" key={action} onClick={() => onSecondary(row, action)}>{action}</button>
            ))}
          </div>
        )}
      </div>

      <aside className="inline-row-side-v5">
        <button
          className="inline-primary-v5"
          type="button"
          disabled={disabled}
          onClick={() => onPrimary(row)}
        >
          {isDone ? "Done" : row.primary}
          <span>›</span>
        </button>
        {disabled && <small>{row.disabledHint}</small>}
        <button className="inline-audit-v5" type="button">{row.audit} ›</button>
      </aside>
    </article>
  );
};

const ConfirmOverlayV5 = ({ confirm, onCancel, onConfirm }) => {
  if (!confirm) return null;
  return (
    <div className="confirm-pop-v5" role="dialog" aria-modal="true" aria-label="Confirm action">
      <section>
        <p>{confirm.message}</p>
        <div>
          <button type="button" onClick={onCancel}>Cancel</button>
          <button className="is-primary" type="button" onClick={onConfirm}>Confirm</button>
        </div>
      </section>
    </div>
  );
};

const BackgroundActivityStripV3 = ({ onOpen }) => {
  const [open, setOpen] = React.useState(false);
  const items = ["4 emails out", "2 WhatsApps awaiting reply", "Marco's Glenrose log expected", "Hartman roofing quote due by 5 PM"];
  return (
    <section className={"background-strip-v3" + (open ? " is-open" : "")}>
      <button
        type="button"
        onClick={() => setOpen((value) => !value)}
        aria-expanded={open}
      >
        <span>The office is also working on:</span>
        <b>{items.join(" · ")}.</b>
      </button>
      {open && (
        <div>
          <p>Email follow-ups, WhatsApp reminders, expected voice notes, and promised vendor replies are saved in History.</p>
          <button type="button" onClick={onOpen}>Open</button>
        </div>
      )}
    </section>
  );
};

const ScreenTodayV2 = ({ onGo }) => {
  const [handledOpen, setHandledOpen] = React.useState(false);
  const [answers, setAnswers] = React.useState({});
  const [completed, setCompleted] = React.useState({});
  const [editing, setEditing] = React.useState({});
  const [draftText, setDraftText] = React.useState({});
  const [confirm, setConfirm] = React.useState(null);
  const handled = ["14 incoming items sorted", "22 photos filed", "4 daily logs drafted", "10 bills scanned", "12 crew hours staged"];
  const role = "owner";
  const roleData = ROLE_TODAY_V3[role];
  const items = TODAY_INLINE_ROWS_V5;

  const setAnswer = (rowId, questionId, label) => {
    setAnswers((current) => ({
      ...current,
      [rowId]: {
        ...(current[rowId] || {}),
        [questionId]: label,
      },
    }));
  };

  const handleChip = (row, question, option) => {
    if (option.confirm) {
      setConfirm({ type:"chip", rowId:row.id, questionId:question.id, label:option.label, message:option.confirm });
      return;
    }
    setAnswer(row.id, question.id, option.label);
  };

  const handlePrimary = (row) => {
    if (row.confirmPrimary) {
      setConfirm({ type:"primary", rowId:row.id, message:row.confirmPrimary });
      return;
    }
    setCompleted((current) => ({ ...current, [row.id]: true }));
  };

  const handleSecondary = (row, action) => {
    if (action === "Edit" || action === "Edit first") {
      setEditing((current) => ({ ...current, [row.id]: true }));
      setDraftText((current) => ({ ...current, [row.id]: current[row.id] ?? row.draft }));
      return;
    }
    if (action === "Save draft") {
      setEditing((current) => ({ ...current, [row.id]: false }));
      return;
    }
    if (action === "Cancel edit") {
      setEditing((current) => ({ ...current, [row.id]: false }));
      setDraftText((current) => ({ ...current, [row.id]: row.draft }));
      return;
    }
    setCompleted((current) => ({ ...current, [row.id]: true }));
  };

  const handleConfirm = () => {
    if (!confirm) return;
    if (confirm.type === "chip") setAnswer(confirm.rowId, confirm.questionId, confirm.label);
    if (confirm.type === "primary") setCompleted((current) => ({ ...current, [confirm.rowId]: true }));
    setConfirm(null);
  };

  return (
    <ScreenShellV2 className={"today-v2 today-inbox-page-v4 role-" + role}>
      <header className="today-hero-v2">
        <div>
          <h1>Good morning, {roleData.label}. {items.length} things still need you.</h1>
          <p>The office already tried email and WhatsApp. Anything solved there is gone from this list.</p>
        </div>
      </header>

      <BackgroundActivityStripV3 onOpen={() => onGo("project")} />

      <div className="today-workspace-v4">
        <section className="today-inbox-v4">
          <div className="today-inbox-head-v4">
            <div>
              <span>Today</span>
              <h2>Waiting on you</h2>
            </div>
          </div>
          <main className="today-list-v2">
            {items.map((item) => (
              <InlineTodayRowV5
                key={item.id}
                row={item}
                answers={answers[item.id] || {}}
                completed={completed}
                editing={editing}
                draftText={draftText}
                onChip={handleChip}
                onPrimary={handlePrimary}
                onSecondary={handleSecondary}
                onDraftChange={(rowId, value) => setDraftText((current) => ({ ...current, [rowId]: value }))}
              />
            ))}
          </main>
        </section>
      </div>

      <ConfirmOverlayV5 confirm={confirm} onCancel={() => setConfirm(null)} onConfirm={handleConfirm} />

      <section
        className={"done-v2 done-collapsed-v2" + (handledOpen ? " is-open" : "")}
        role="button"
        tabIndex="0"
        onClick={() => setHandledOpen((open) => !open)}
        onKeyDown={(event) => {
          if (event.key === "Enter" || event.key === " ") {
            event.preventDefault();
            setHandledOpen((open) => !open);
          }
        }}
      >
        <h2>Already handled since yesterday</h2>
        <p>{handled.join(" · ")}.</p>
        {handledOpen && (
          <div>
            {handled.map((line) => <p key={line}>{line}.</p>)}
            <button type="button" onClick={(event) => { event.stopPropagation(); onGo("project"); }}>Open</button>
          </div>
        )}
      </section>
    </ScreenShellV2>
  );
};

const StaffControlsV3 = ({ label = "this teammate" }) => (
  <div className="staff-controls-v3">
    <button type="button"><Icon name="pause" size={14} /> Pause {label}</button>
    <button type="button"><Icon name="clock" size={14} /> Quiet hours</button>
  </div>
);

const MISSING_HOURS_ROW_V5 = {
  id:"time-missing-frank",
  tone:"amber",
  type:"draft",
  title:"Ask Frank for missing Oakwood hours.",
  summary:"",
  meta:"WhatsApp · 7:08 AM · Timekeeper",
  primary:"Send as is",
  audit:"Open thread",
  draftLabel:"Drafted WhatsApp to Frank:",
  draft:"Frank — I am missing Oakwood hours for Carlos and Mateo from yesterday. Can you reply with regular and overtime?",
  secondary:["Edit", "Call him instead"],
};

const ACCOUNTING_FLAG_ROW_V5 = {
  id:"accounting-cost-flag",
  tone:"amber",
  type:"answer",
  title:"Glenrose framing is running 12% over rule.",
  summary:"The office flagged lumber and 14 extra crew-hours on the gable detail. Nothing is sent; this is for your review.",
  meta:"In-app · 6:52 AM · Accounting Analyst",
  primary:"Open analysis",
  audit:"Show source bills",
  secondary:["Ask a follow-up"],
};

const TAKEOFF_QUESTION_ROW_V5 = {
  id:"takeoff-basement-height",
  tone:"amber",
  type:"questions",
  title:"St Leonards plan has two basement ceiling heights.",
  summary:"Pick the assumption before the budget and quote plan finalize.",
  meta:"Architect plan R4 · 28 sheets · Takeoff Reader",
  primary:"Use this assumption",
  disabledHint:"Choose one assumption first.",
  audit:"Open plan notes",
  questions:[
    {
      id:"height",
      text:"Basement ceiling height is shown as 8' on A2.1 and 9' on S1.0. Use which one for budget?",
      chips:["Use 8'", "Use 9'", "Ask architect first"],
    },
  ],
};

const TRADE_REQUEST_ROW_V5 = {
  id:"trade-send-requests",
  tone:"green",
  type:"draft",
  title:"Send 3 quote requests for St Leonards.",
  summary:"",
  meta:"Email · Trade Coordinator · windows, roofing, drywall",
  primary:"Send as is",
  audit:"Open quote plan",
  draftLabel:"Drafted email set:",
  draft:"Hi — pricing St Leonards Custom Home. Attached are the plan pages and scope notes for your trade. Can you send price and lead time by Friday?",
  draftNote:"Pella gets window schedule. Hartman gets roof plan. Boulevard gets wall and ceiling takeoff.",
  secondary:["Edit", "Hold a day"],
};

const SCHEDULE_QUESTION_ROW_V5 = {
  id:"schedule-placeholders",
  tone:"amber",
  type:"questions",
  title:"Build first schedule with placeholders?",
  summary:"Roofing and windows still need trade dates. Scheduler can use placeholders until replies come back.",
  meta:"St Leonards · Scheduler",
  primary:"Confirm schedule plan",
  disabledHint:"Choose one schedule path first.",
  audit:"Open first schedule",
  questions:[
    {
      id:"placeholder-path",
      text:"Use dummy dates for roofing and windows, or wait for trade quotes first?",
      chips:["Use placeholders", "Wait for quotes", "Ask Sam first"],
    },
  ],
};

const EMPLOYEE_WORKLOG_V5 = {
  bookkeeper:{
    name:"Bookkeeper",
    type:"Daily staff",
    sentence:"The Bookkeeper gets vendor bills into QuickBooks with the right job and cost code.",
    note:"Use this page when you want to see bill questions, bill writes, vendor messages, and anything the Bookkeeper touched.",
    needs:[TODAY_INLINE_ROWS_V5[0]],
    history:[
      { day:"Today", items:[
        ["7:14 AM", "Question sent", "Asked about Patel cost code and Sherwin-Williams duplicate.", "Glenrose, Oakwood, St Leonards"],
        ["6:42 AM", "Bills staged", "4 matched bills staged for QuickBooks.", "$20,106 ready"],
        ["6:35 AM", "Duplicate held", "Sherwin-Williams $812 held before posting.", "St Leonards"],
      ]},
      { day:"Yesterday", items:[
        ["4:18 PM", "Bill posted", "JMO Plumbing $51,150 posted after approval.", "St Leonards"],
        ["10:06 AM", "Vendor chased", "Asked Hartman Roofing for missing invoice PDF.", "Glenrose"],
      ]},
    ],
  },
  timekeeper:{
    name:"Timekeeper",
    type:"Daily staff",
    sentence:"The Timekeeper gets crew hours into QuickBooks Time and flags missing hours or overtime.",
    note:"Most questions arrive by WhatsApp or email. This page shows every hours question and the running hours history.",
    needs:[TODAY_INLINE_ROWS_V5[3], MISSING_HOURS_ROW_V5],
    history:[
      { day:"Today", items:[
        ["7:42 AM", "Hours reply received", "Frank replied with Jay Patel's 12.5 hours and 4.5 overtime.", "Oakwood"],
        ["7:08 AM", "Missing-hours text drafted", "Prepared a WhatsApp to Frank for Carlos and Mateo.", "Oakwood"],
        ["6:50 AM", "Hours staged", "12 regular time entries staged for QuickBooks Time.", "3 jobs"],
      ]},
      { day:"Yesterday", items:[
        ["5:20 PM", "Hours posted", "87 regular hours posted after approval.", "All active jobs"],
      ]},
    ],
  },
  client:{
    name:"Client Liaison",
    type:"Daily staff",
    sentence:"The Client Liaison drafts homeowner updates and sends only what you approve.",
    note:"Usually this arrives as an email preview. This page is the running list of homeowner updates sent or waiting.",
    needs:[TODAY_INLINE_ROWS_V5[2]],
    history:[
      { day:"Today", items:[
        ["6:48 AM", "Update drafted", "Thursday Glenrose update drafted with 5 photos and next 60 days.", "Glenrose"],
        ["6:26 AM", "Internal notes removed", "Vendor issues and money notes filtered out.", "Glenrose"],
      ]},
      { day:"Last Thursday", items:[
        ["8:00 AM", "Update sent", "Weekly homeowner update sent and logged.", "Oakwood"],
        ["7:52 AM", "Question received", "Tile allowance question routed into the next update.", "Glenrose"],
      ]},
    ],
  },
  accounting:{
    name:"Accounting Analyst",
    type:"Daily staff",
    sentence:"The Accounting Analyst answers money questions and flags cost movement before it surprises you.",
    note:"Ask by email, WhatsApp, or the Ask button. Spreadsheets, cash-flow views, and source evidence live in the history below.",
    needs:[ACCOUNTING_FLAG_ROW_V5, TODAY_INLINE_ROWS_V5[4]],
    history:[
      { day:"Today", items:[
        ["6:52 AM", "Cost flag", "Glenrose framing running 12% over budget rule.", "3 bills, 14 crew-hours"],
        ["6:31 AM", "Answer drafted", "Answered why Glenrose framing costs are up.", "Glenrose"],
        ["6:12 AM", "Cash view built", "This week's cash needs spreadsheet prepared.", "$42,610 expected"],
      ]},
      { day:"Yesterday", items:[
        ["4:12 PM", "Question received", "Aaron asked for framing cost explanation.", "Glenrose"],
      ]},
    ],
  },
};

const FIELD_SESSIONS_V5 = [
  {
    site:"Glenrose",
    title:"Glenrose site WhatsApp",
    meta:"Marco, Sam, Field Intake · log received today",
    status:"1 voice note · 5 photos · daily log drafted",
    messages:[
      ["7:42 AM", "Marco", "Voice note: framing crew on second floor, lumber delivery received, stair detail still missing.", "Daily log drafted"],
      ["7:45 AM", "Marco", "5 photos uploaded to the group.", "Photos filed to Drive"],
      ["7:51 AM", "Field Intake", "Prepared client-safe photo set and flagged stair detail.", "Waiting on approval"],
    ],
  },
  {
    site:"St Leonards",
    title:"St Leonards site WhatsApp",
    meta:"Marco, Sam, Field Intake · quiet 2 days",
    status:"No log since Monday · third nudge drafted",
    messages:[
      ["Mon 7:36 AM", "Marco", "Short log: site cleanup, framing material check.", "Log published"],
      ["Tue 7:30 AM", "Field Intake", "Asked Marco for daily log and photos.", "No reply"],
      ["Wed 7:35 AM", "Field Intake", "Second reminder sent.", "No reply"],
    ],
  },
  {
    site:"Oakwood",
    title:"Oakwood site WhatsApp",
    meta:"Frank, Dana, Timekeeper · hours thread active",
    status:"Hours reply received · no daily log issue",
    messages:[
      ["7:42 AM", "Frank", "Jay worked 12.5 hours yesterday, 4.5 overtime.", "Sent to Timekeeper"],
      ["8:02 AM", "Field Intake", "No field issue in today's site thread.", "No action"],
    ],
  },
];

const ONBOARDING_HISTORY_V5 = {
  takeoff:[
    { day:"Today", items:[
      ["7:06 AM", "Budget generated", "$634,710 draft budget generated from R4 plan rules.", "St Leonards"],
      ["6:58 AM", "Quote plan created", "Windows, roofing, drywall, and HVAC marked for trade quotes.", "St Leonards"],
      ["6:42 AM", "Plan question", "Basement ceiling height conflict flagged before budget handoff.", "St Leonards"],
    ]},
  ],
  trade:[
    { day:"Today", items:[
      ["8:00 AM", "Email drafted", "Pella, Hartman, and Boulevard quote emails ready.", "St Leonards"],
      ["6:14 AM", "Quote received", "JMO Plumbing quote folded into budget.", "St Leonards"],
    ]},
  ],
  scheduler:[
    { day:"Today", items:[
      ["7:20 AM", "Schedule draft", "First St Leonards schedule built from budget and placeholder dates.", "St Leonards"],
      ["7:10 AM", "Waiting tag", "Roofing marked Waiting on Hartman Roofing.", "Glenrose"],
    ]},
  ],
};

const InlineRowsControllerV5 = ({ rows }) => {
  const [answers, setAnswers] = React.useState({});
  const [completed, setCompleted] = React.useState({});
  const [editing, setEditing] = React.useState({});
  const [draftText, setDraftText] = React.useState({});
  const [confirm, setConfirm] = React.useState(null);

  const setAnswer = (rowId, questionId, label) => {
    setAnswers((current) => ({ ...current, [rowId]: { ...(current[rowId] || {}), [questionId]: label } }));
  };
  const handleChip = (row, question, option) => {
    if (option.confirm) {
      setConfirm({ type:"chip", rowId:row.id, questionId:question.id, label:option.label, message:option.confirm });
      return;
    }
    setAnswer(row.id, question.id, option.label);
  };
  const handlePrimary = (row) => {
    if (row.confirmPrimary) {
      setConfirm({ type:"primary", rowId:row.id, message:row.confirmPrimary });
      return;
    }
    setCompleted((current) => ({ ...current, [row.id]: true }));
  };
  const handleSecondary = (row, action) => {
    if (action === "Edit" || action === "Edit first") {
      setEditing((current) => ({ ...current, [row.id]: true }));
      setDraftText((current) => ({ ...current, [row.id]: current[row.id] ?? row.draft }));
      return;
    }
    if (action === "Save draft") {
      setEditing((current) => ({ ...current, [row.id]: false }));
      return;
    }
    if (action === "Cancel edit") {
      setEditing((current) => ({ ...current, [row.id]: false }));
      setDraftText((current) => ({ ...current, [row.id]: row.draft }));
      return;
    }
    setCompleted((current) => ({ ...current, [row.id]: true }));
  };
  const handleConfirm = () => {
    if (confirm?.type === "chip") setAnswer(confirm.rowId, confirm.questionId, confirm.label);
    if (confirm?.type === "primary") setCompleted((current) => ({ ...current, [confirm.rowId]: true }));
    setConfirm(null);
  };

  return (
    <div className="staff-inline-rows-v5">
      {rows.map((row) => (
        <InlineTodayRowV5
          key={row.id}
          row={row}
          answers={answers[row.id] || {}}
          completed={completed}
          editing={editing}
          draftText={draftText}
          onChip={handleChip}
          onPrimary={handlePrimary}
          onSecondary={handleSecondary}
          onDraftChange={(rowId, value) => setDraftText((current) => ({ ...current, [rowId]: value }))}
        />
      ))}
      <ConfirmOverlayV5 confirm={confirm} onCancel={() => setConfirm(null)} onConfirm={handleConfirm} />
    </div>
  );
};

const DatedWorkLogV5 = ({ groups, name }) => {
  const [openDay, setOpenDay] = React.useState(groups[0]?.day || "");
  return (
    <section className="staff-block-v2 dated-log-v5">
      <h2>Recent work and messages</h2>
      <p>Open a day to see what {name} did. Open any item to chat or ask for an edit on that exact work.</p>
      {groups.map((group) => (
        <article className="dated-day-v5" key={group.day}>
          <button type="button" onClick={() => setOpenDay(openDay === group.day ? "" : group.day)}>
            <b>{group.day}</b>
            <span>{group.items.length} item{group.items.length === 1 ? "" : "s"}</span>
          </button>
          {openDay === group.day && (
            <div>
              {group.items.map(([time, kind, body, job]) => (
                <section key={time + kind + body}>
                  <span>{time}</span>
                  <div>
                    <h3>{kind}</h3>
                    <p>{body}</p>
                    <small>{job}</small>
                  </div>
                  <nav>
                    <button type="button">Open</button>
                    <button type="button">Chat</button>
                  </nav>
                </section>
              ))}
            </div>
          )}
        </article>
      ))}
    </section>
  );
};

const EmployeePageV5 = ({ dataKey }) => {
  const data = EMPLOYEE_WORKLOG_V5[dataKey];
  return (
    <ScreenShellV2 className="staff-page-v2 staff-page-v5">
      <header className="staff-head-v2">
        <div>
          <span>{data.type}</span>
          <h1>{data.name}</h1>
          <p className="staff-sentence-v2">{data.sentence}</p>
          <p>{data.note}</p>
        </div>
        <StaffControlsV3 label={data.name} />
      </header>
      <section className="staff-block-v2">
        <h2>Needs attention</h2>
        <InlineRowsControllerV5 rows={data.needs} />
      </section>
      <DatedWorkLogV5 groups={data.history} name={data.name} />
    </ScreenShellV2>
  );
};

const ScreenFieldIntakeV2 = () => {
  const [openSite, setOpenSite] = React.useState("Glenrose");
  return (
    <ScreenShellV2 className="staff-page-v2 staff-page-v5 field-sessions-v5">
      <header className="staff-head-v2">
        <div>
          <span>Daily staff</span>
          <h1>Field Intake</h1>
          <p className="staff-sentence-v2">Field Intake listens to each job site's WhatsApp group and turns logs, photos, and receipts into records.</p>
          <p>Each site is a session. Open the site to see the running WhatsApp intake history.</p>
        </div>
        <StaffControlsV3 label="Field Intake" />
      </header>
      <section className="staff-block-v2">
        <h2>Needs attention</h2>
        <InlineRowsControllerV5 rows={[TODAY_INLINE_ROWS_V5[1]]} />
      </section>
      <section className="staff-block-v2 site-sessions-v5">
        <h2>Site WhatsApp sessions</h2>
        {FIELD_SESSIONS_V5.map((session) => (
          <article key={session.site} className={openSite === session.site ? "is-open" : ""}>
            <button type="button" onClick={() => setOpenSite(openSite === session.site ? "" : session.site)}>
              <div>
                <b>{session.title}</b>
                <span>{session.meta}</span>
              </div>
              <em>{session.status}</em>
            </button>
            {openSite === session.site && (
              <div className="site-session-thread-v5">
                {session.messages.map(([time, who, body, result]) => (
                  <section key={time + who + body}>
                    <span>{time}</span>
                    <div>
                      <h3>{who}</h3>
                      <p>{body}</p>
                      <small>{result}</small>
                    </div>
                    <button type="button">Chat</button>
                  </section>
                ))}
              </div>
            )}
          </article>
        ))}
      </section>
    </ScreenShellV2>
  );
};

const TakeoffReaderPageV5 = () => (
  <ScreenShellV2 className="staff-page-v2 staff-page-v5 onboarding-page-v5">
    <header className="staff-head-v2">
      <div>
        <span>Client onboarding</span>
        <h1>Takeoff Reader</h1>
        <p className="staff-sentence-v2">Takeoff Reader turns an architect plan into a counted takeoff, rule-priced budget, Excel export, quote plan, and schedule handoff.</p>
        <p>Email a plan to the Takeoff Reader or upload it here. The rest of the onboarding work starts from that plan.</p>
      </div>
      <StaffControlsV3 label="Takeoff Reader" />
    </header>
    <section className="takeoff-import-v5">
      <div>
        <h2>Start a new takeoff</h2>
        <p>Upload the architect PDF or forward the email. The Takeoff Reader reads it, generates the budget Excel, prepares quote requests, and hands schedule assumptions to Scheduler.</p>
      </div>
      <button type="button">Upload plan</button>
      <button type="button">Email plan</button>
    </section>
    <section className="staff-block-v2">
      <h2>Needs attention</h2>
      <InlineRowsControllerV5 rows={[TAKEOFF_QUESTION_ROW_V5]} />
    </section>
    <section className="staff-block-v2 onboarding-active-v5">
      <h2>Active takeoffs</h2>
      <article>
        <h3>St Leonards Custom Home</h3>
        <p>Architect plan R4 · 28 sheets · $634,710 draft budget · Excel ready</p>
        <div>
          {["Plan read", "Takeoff counted", "Budget Excel ready", "Quote plan ready", "Scheduler handoff queued"].map((step) => <span key={step}>{step}</span>)}
        </div>
        <nav>
          <button type="button">Download Excel</button>
          <button type="button">Open budget</button>
          <button type="button">Hand to trades</button>
        </nav>
      </article>
    </section>
    <DatedWorkLogV5 groups={ONBOARDING_HISTORY_V5.takeoff} name="Takeoff Reader" />
  </ScreenShellV2>
);

const TradeCoordinatorPageV5 = () => (
  <ScreenShellV2 className="staff-page-v2 staff-page-v5 onboarding-page-v5">
    <header className="staff-head-v2">
      <div>
        <span>Client onboarding</span>
        <h1>Trade Coordinator</h1>
        <p className="staff-sentence-v2">Trade Coordinator emails trades for real quotes and folds replies back into the budget.</p>
        <p>This is where quote requests are approved, tracked, and followed up until the project budget is real.</p>
      </div>
      <StaffControlsV3 label="Trade Coordinator" />
    </header>
    <section className="staff-block-v2">
      <h2>Needs attention</h2>
      <InlineRowsControllerV5 rows={[TRADE_REQUEST_ROW_V5]} />
    </section>
    <section className="staff-block-v2 quote-status-v5">
      <h2>Quotes in flight</h2>
      {[
        ["Windows", "Pella", "Ready to send", "Feeds window budget and Oct 14 schedule placeholder."],
        ["Roofing", "Hartman Roofing", "Sent", "Waiting on lead time before Scheduler firms up roofing."],
        ["Drywall", "Boulevard Drywall", "Reply received", "Needs approval before budget and schedule update."],
        ["Plumbing", "JMO Plumbing", "Folded into budget", "$51,150 real quote."],
      ].map(([trade, vendor, status, note]) => (
        <article key={trade}>
          <b>{trade}</b>
          <span>{vendor}</span>
          <em>{status}</em>
          <p>{note}</p>
          <button type="button">Open</button>
        </article>
      ))}
    </section>
    <DatedWorkLogV5 groups={ONBOARDING_HISTORY_V5.trade} name="Trade Coordinator" />
  </ScreenShellV2>
);

const SchedulerPageV5 = () => (
  <ScreenShellV2 className="staff-page-v2 staff-page-v5 onboarding-page-v5">
    <header className="staff-head-v2">
      <div>
        <span>Client onboarding</span>
        <h1>Scheduler</h1>
        <p className="staff-sentence-v2">Scheduler builds the first project schedule from the takeoff, budget, trade dates, and placeholder assumptions.</p>
        <p>Known dates, placeholders, and waiting-on-trade dates stay separated so the GC can see what is real.</p>
      </div>
      <StaffControlsV3 label="Scheduler" />
    </header>
    <section className="staff-block-v2">
      <h2>Needs attention</h2>
      <InlineRowsControllerV5 rows={[SCHEDULE_QUESTION_ROW_V5]} />
    </section>
    <section className="staff-block-v2 quote-status-v5 schedule-status-v5">
      <h2>First schedule draft</h2>
      {[
        ["Site prep", "Known", "Jun 3", "from budget scope"],
        ["Foundation", "Known", "Jun 17", "from foundation quote"],
        ["Roofing", "Waiting on Hartman", "Sep 12", "quote lead time missing"],
        ["Windows", "Placeholder", "Oct 14", "model numbers missing"],
      ].map(([phase, state, date, note]) => (
        <article key={phase}>
          <b>{phase}</b>
          <span>{date}</span>
          <em>{state}</em>
          <p>{note}</p>
          <button type="button">Open</button>
        </article>
      ))}
    </section>
    <DatedWorkLogV5 groups={ONBOARDING_HISTORY_V5.scheduler} name="Scheduler" />
  </ScreenShellV2>
);

const ScreenBookkeeperV2 = () => <EmployeePageV5 dataKey="bookkeeper" />;
const ScreenSchedulerV2 = () => <SchedulerPageV5 />;
const ScreenClientLiaisonV2 = () => <EmployeePageV5 dataKey="client" />;
const ScreenTimekeeperV2 = () => <EmployeePageV5 dataKey="timekeeper" />;
const ScreenAccountingAnalystV2 = () => <EmployeePageV5 dataKey="accounting" />;
const ScreenTakeoffReaderV2 = () => <TakeoffReaderPageV5 />;
const ScreenTradeCoordinatorV2 = () => <TradeCoordinatorPageV5 />;
const ScreenSupportStaffV2 = () => <EmployeePageV5 dataKey="bookkeeper" />;

const ESTIMATOR_FLOW_V2 = [
  ["1", "Plan comes in", "Upload a PDF, forward an architect email, or let Drive catch the revision."],
  ["2", "Takeoff is prepared", "Exterior brick, hardwood, doors, trim, and unclear plan notes are pulled out."],
  ["3", "Budget is built", "Your rules price what can be priced. Anything uncertain is clearly marked."],
  ["4", "Trades are asked", "Quote requests go to windows, roofing, drywall, HVAC, and any missing scope."],
  ["5", "Replies fold in", "Real trade numbers replace rule prices after you approve."],
  ["6", "Schedule starts", "Known dates become schedule dates. Unknown dates stay placeholders."],
];

const ESTIMATOR_TAKEOFF_V2 = [
  ["Exterior brick", "8,420 sq ft", "Rule-priced"],
  ["Hardwood", "3,100 sq ft", "Rule-priced"],
  ["Interior doors", "42", "Rule-priced"],
  ["Trim", "1,180 linear ft", "Rule-priced"],
  ["Windows", "24 openings", "Quote pending"],
  ["Roofing", "roof plan", "Quote pending"],
];

const ESTIMATOR_BUDGET_LINES_V2 = [
  ["0102", "Structural engineer", "Rule-priced", "$2,500", "from your budget rule"],
  ["2001", "Lumber", "Quote pending", "$132,560 draft", "supplier quote still needed"],
  ["2004", "Carpenter labor", "Rule-priced", "$183,783", "from crew rule and schedule"],
  ["2401", "Roofing", "Quote pending", "$94,629 draft", "roofing request ready"],
  ["3002", "Windows", "Quote pending", "$190,443 draft", "model numbers missing"],
  ["4001", "Plumbing", "Real quote from JMO Plumbing", "$51,150", "reply folded in"],
  ["4101", "Electrical", "Quote pending", "$40,000 draft", "trade reply missing"],
];

const ESTIMATOR_QUOTES_V2 = [
  ["Windows", "Pella", "Sent", "Waiting on model numbers."],
  ["Roofing", "Hartman Roofing", "Ready", "Roof plan and scope summary attached."],
  ["Drywall", "Boulevard Drywall", "Replied", "Number ready to fold into budget."],
  ["HVAC", "Apex Mechanical", "Missing info", "Equipment model confirmation needed."],
];

const ESTIMATOR_SCHEDULE_V2 = [
  ["Demo", "Known", "Apr 8", "from approved work"],
  ["Underpinning", "Known", "May 1", "from foundation quote"],
  ["Framing", "Placeholder", "Aug 1", "until crew plan is confirmed"],
  ["Roofing", "Waiting on Hartman Roofing", "Oct 1", "until lead time comes back"],
  ["Windows", "Waiting on Pella", "Oct 14", "until model numbers come back"],
  ["Plumbing rough-in", "Known", "Oct 2", "from JMO Plumbing reply"],
];

const ESTIMATOR_WORKFLOW_STEPS_V3 = [
  ["Plan imported", "Takeoff Reader", "takeoff", "Takeoff Reader"],
  ["Takeoff counted", "Takeoff Reader", "takeoff", "Takeoff Reader"],
  ["Budget Excel", "Takeoff Reader", "takeoff", "Takeoff Reader"],
  ["Quotes out", "Trade Coordinator", "quotes", "Trade Coordinator"],
  ["Schedule", "Scheduler", "schedule", "Scheduler"],
];

const EstimatorWorkflowV3 = ({ onGo }) => (
  <section className="estimator-flow-v3" aria-label="Estimator workflow">
    <div>
      <b>How this job moves</b>
      <span>Takeoff Reader handed this over on May 13. Estimator priced this takeoff on May 14.</span>
    </div>
    <div className="workflow-steps-v3 is-five">
      {ESTIMATOR_WORKFLOW_STEPS_V3.map(([label, owner, route]) => (
        <button type="button" key={label} onClick={() => onGo(route)}>
          <span>{label}</span>
          <small>{owner}</small>
        </button>
      ))}
    </div>
  </section>
);

const DEFAULT_ESTIMATOR_PROJECT_V2 = "st-leonards";

const ESTIMATOR_PROJECTS_V2 = [
  {
    id:"st-leonards",
    name:"St Leonards Custom Home",
    short:"St Leonards",
    meta:"Architect plan R4 · 28 sheets · draft budget v1 · $634,710",
    quoteStatus:"4 trade quotes out, 1 back",
    next:"3 quotes are ready to send for St Leonards: windows, roofing, drywall.",
    nextButton:"Approve and send",
    quoteCount:"4 out · 1 back",
    budgetCount:"47",
    needs:[
      {
        tone:"green",
        rowTitle:"Quote requests",
    rowMeta:["St Leonards", "windows, roofing, drywall", "ready to send"],
        inbox:{ channel:"Email", time:"6:58 AM", state:"Ready to send", preview:"Quote requests were drafted from the takeoff and plan pages. They are held until approval." },
        title:"3 quotes are ready to send for St Leonards.",
        house:"St Leonards",
        amount:"windows, roofing, drywall",
        outcome:"Approve and the requests go to trades with the right plan pages and scope notes.",
        summary:"Windows, roofing, and drywall requests are ready.",
        source:"Because the architect plan was read and the takeoff found missing trade prices.",
        actions:["Approve and send", "Open"],
        details:{
          title:"Quote requests ready",
          summary:"Each request includes the scope, plan pages, and what the trade needs to answer.",
          facts:["3 emails", "5 attachments", "nothing leaves until approval"],
          ready:["Windows: schedule attached.", "Roofing: roof plan attached.", "Drywall: wall and ceiling takeoff attached."],
          held:["HVAC waits for equipment model confirmation."],
          outside:"Trade emails leave your office only after you approve.",
        },
      },
      {
        tone:"amber",
        rowTitle:"Missing info",
        rowMeta:["Window model numbers", "24 openings"],
        inbox:{ channel:"Email", time:"6:50 AM", state:"Reply needed", preview:"The office checked the plans and architect thread. Model numbers are still missing." },
        title:"Window model numbers are missing.",
        house:"St Leonards",
        amount:"24 openings",
        outcome:"Reply and the Estimator uses your answer in the supplier request.",
        summary:"The drawings show window openings but not the supplier model numbers.",
        source:"Because the takeoff found gaps in the window schedule.",
        actions:["Reply", "Open"],
      },
    ],
    quoteRows:[
      {
        trade:"Windows",
        supplier:"Pella",
        status:"Sent",
        schedule:"Windows · Oct 14 · Waiting on Pella.",
        note:"When this quote comes back, the Oct 14 placeholder becomes a known date, or the Scheduler will ask you to approve a new one.",
        actions:["Open"],
      },
      {
        trade:"Roofing",
        supplier:"Hartman Roofing",
        status:"Ready to send",
        schedule:"Roofing · Oct 1 · Waiting on Hartman Roofing.",
        note:"When this quote comes back, the Oct 1 placeholder becomes a known date, or the Scheduler will ask you to approve a new one.",
        actions:["Approve and send", "Open"],
      },
      {
        trade:"Drywall",
        supplier:"Boulevard Drywall",
        status:"Ready to send",
        schedule:"Drywall start · Nov 28 · Waiting on Boulevard Drywall.",
        note:"The drywall date stays a placeholder until this reply gives price and lead time.",
        actions:["Approve and send", "Open"],
      },
      {
        trade:"HVAC",
        supplier:"Apex Mechanical",
        status:"Missing info",
        schedule:"HVAC rough-in · Oct 7 · Waiting on equipment model.",
        note:"The request is held until the equipment model is confirmed.",
        actions:["Open"],
      },
    ],
    budgetRows:[
      ["0102", "Structural engineer", "Rule-priced", "$2,500", "your budget rule"],
      ["2001", "Lumber", "Quote pending", "$132,560 draft", "supplier quote still needed"],
      ["2004", "Carpenter labor", "Rule-priced", "$183,783", "crew rule and schedule"],
      ["2401", "Roofing", "Quote pending", "$94,629 draft", "Hartman quote not back"],
      ["3002", "Windows", "Quote pending", "$190,443 draft", "Pella model numbers missing"],
      ["4001", "Plumbing", "Real quote from JMO Plumbing", "$51,150", "reply folded in"],
      ["4101", "Electrical", "Quote pending", "$40,000 draft", "trade reply missing"],
    ],
    questionRows:[
      ["Confirm basement ceiling height.", "This changes drywall, stairs, and framing assumptions."],
      ["Window schedule is missing supplier model numbers.", "Pella cannot firm up price or lead time without them."],
      ["Confirm hardwood split between main floor and second floor.", "The rule price uses one blended assumption until you answer."],
    ],
    scheduleRows:[
      ["Demo", "Known", "Apr 8", "approved work"],
      ["Underpinning", "Known", "May 1", "foundation quote"],
      ["Framing", "Placeholder", "Aug 1", "crew plan is not confirmed"],
      ["Roofing", "Waiting on Hartman Roofing", "Oct 1", "the roofing quote you are approving today"],
      ["Windows", "Waiting on Pella", "Oct 14", "the Pella quote sent May 13"],
      ["Plumbing rough-in", "Known", "Oct 2", "JMO Plumbing reply"],
    ],
    done:[
      "Read 28 architect sheets for St Leonards.",
      "Prepared takeoff quantities and allowance assumptions.",
      "Marked budget lines as Rule-priced, Quote pending, or Real quote.",
      "Prepared the Excel budget export and first schedule draft.",
    ],
    ask:["What lines still need real quotes?", "Export the St Leonards budget to Excel.", "What assumptions did you use for roofing?"],
  },
  {
    id:"glenrose",
    name:"Glenrose Custom Home",
    short:"Glenrose",
    meta:"Architect plan R5 · 31 sheets · draft budget v2 · $712,480",
    quoteStatus:"3 trade quotes out, 2 back",
    next:"Boulevard Drywall replied. Approve the number and lead time before it folds into the Glenrose budget.",
    nextButton:"Approve",
    quoteCount:"3 out · 2 back",
    budgetCount:"52",
    needs:[
      {
        tone:"green",
        rowTitle:"Quote reply",
        rowMeta:["Glenrose", "drywall", "$74,880"],
        title:"Drywall quote is ready to fold into Glenrose.",
        house:"Glenrose",
        amount:"$74,880 · 6 week lead time",
        outcome:"Approve and the budget line changes from Quote pending to Real quote.",
        summary:"Boulevard Drywall replied with price and lead time. Scheduler will use the lead time after you approve.",
        source:"Because Boulevard replied to the quote request this morning.",
        actions:["Approve", "Open"],
      },
    ],
    quoteRows:[
      {
        trade:"Drywall",
        supplier:"Boulevard Drywall",
        status:"Replied",
        schedule:"Drywall start · Nov 18 · Waiting on Boulevard Drywall approval.",
        note:"When you approve this reply, the drywall placeholder uses the 6 week lead time from Boulevard.",
        actions:["Approve", "Open"],
      },
      {
        trade:"Windows",
        supplier:"Pella",
        status:"Folded into budget",
        schedule:"Windows · Sep 30 · Known.",
        note:"This line already feeds the schedule and the budget.",
        actions:["Open"],
      },
      {
        trade:"Roofing",
        supplier:"Hartman Roofing",
        status:"Sent",
        schedule:"Roofing · Sep 12 · Waiting on Hartman Roofing.",
        note:"When Hartman replies, Scheduler confirms or asks you to change the roofing date.",
        actions:["Open"],
      },
    ],
    budgetRows:[
      ["2001", "Framing lumber", "Real quote from Northern Lumber", "$141,240", "reply folded in"],
      ["2401", "Roofing", "Quote pending", "$82,300 draft", "Hartman quote not back"],
      ["3002", "Windows", "Real quote from Pella", "$176,900", "reply folded in"],
      ["3301", "Drywall", "Quote pending", "$74,880", "Boulevard reply needs approval"],
      ["5001", "Hardwood", "Rule-priced", "$48,600", "your allowance rule"],
    ],
    questionRows:[
      ["Confirm basement ceiling height.", "The drywall quote assumes 9 feet throughout the basement."],
      ["Choose allowance split for appliances.", "The homeowner allowance covers appliance supply only."],
    ],
    scheduleRows:[
      ["Framing complete", "Known", "Aug 16", "Marco's approved field logs"],
      ["Roofing", "Waiting on Hartman Roofing", "Sep 12", "the roofing quote sent May 14"],
      ["Windows", "Known", "Sep 30", "Pella quote folded into budget"],
      ["Drywall", "Waiting on Boulevard Drywall approval", "Nov 18", "the drywall quote reply from today"],
      ["Interior trim", "Placeholder", "Jan 6", "until drywall is approved"],
    ],
    done:[
      "Read the R5 drawing changes for Glenrose.",
      "Folded Pella windows into budget v2.",
      "Held drywall until you approve the trade reply.",
      "Kept the homeowner schedule unchanged until dates are approved.",
    ],
    ask:["What changed since Glenrose budget v1?", "Show the drywall quote.", "Export Glenrose budget v2 to Excel."],
  },
  {
    id:"oakwood",
    name:"Oakwood Renovation",
    short:"Oakwood",
    meta:"Architect addendum A2 · 14 sheets · budget changes pending · $184,900",
    quoteStatus:"1 trade quote out, 0 back",
    next:"Answer the hardwood allowance split before Oakwood change pricing goes out.",
    nextButton:"Reply",
    quoteCount:"1 out · 0 back",
    budgetCount:"18",
    needs:[
      {
        tone:"amber",
        rowTitle:"Budget question",
        rowMeta:["Oakwood", "hardwood allowance", "needs your call"],
        title:"Oakwood hardwood allowance needs your call.",
        house:"Oakwood",
        amount:"1 allowance split",
        outcome:"Reply and the Estimator prices the change correctly before quotes go out.",
        summary:"The addendum changes main floor hardwood but does not say if the second floor stays carpet.",
        source:"Because the architect addendum was unclear.",
        actions:["Reply", "Open"],
      },
    ],
    quoteRows:[
      {
        trade:"Flooring",
        supplier:"Select Hardwood",
        status:"Sent",
        schedule:"Flooring · Aug 22 · Waiting on Select Hardwood.",
        note:"When this quote comes back, Scheduler can firm up flooring and trim dates.",
        actions:["Open"],
      },
    ],
    budgetRows:[
      ["1201", "Selective demo", "Rule-priced", "$9,800", "your renovation rule"],
      ["5001", "Hardwood", "Quote pending", "$36,400 draft", "allowance split needs your reply"],
      ["5202", "Stair refinishing", "Rule-priced", "$7,250", "your allowance rule"],
      ["6001", "Paint touchups", "Rule-priced", "$11,300", "room count from addendum"],
    ],
    questionRows:[
      ["Does the second floor stay carpet?", "The drawings only call out hardwood on the main floor."],
      ["Should the stair finish match the new hardwood?", "This affects the flooring quote and schedule."],
    ],
    scheduleRows:[
      ["Demo", "Known", "Jul 8", "approved change work"],
      ["Flooring", "Waiting on Select Hardwood", "Aug 22", "the flooring quote sent May 15"],
      ["Paint touchups", "Placeholder", "Sep 4", "until flooring date is known"],
      ["Final clean", "Placeholder", "Sep 12", "until paint is confirmed"],
    ],
    done:[
      "Read Oakwood addendum A2.",
      "Priced demo, stair, and paint lines from your rules.",
      "Held hardwood until the allowance split is answered.",
    ],
    ask:["What is unclear in Oakwood A2?", "Show Oakwood change budget.", "What quote is blocking the schedule?"],
  },
  {
    id:"humber",
    name:"Humber Station",
    short:"Humber",
    meta:"Site revision · 9 sheets · rules pass started · $96,300",
    quoteStatus:"No trade quotes sent yet",
    next:"Open the basement scope question before quotes go out.",
    nextButton:"Open",
    quoteCount:"0 out · 0 back",
    budgetCount:"12",
    needs:[
      {
        tone:"amber",
        rowTitle:"Scope question",
        rowMeta:["Humber", "basement work", "before quotes"],
        title:"Basement scope is not clear enough to price.",
        house:"Humber",
        amount:"9 revision sheets",
        outcome:"Open and choose the scope before the Estimator asks trades.",
        summary:"The revision mentions basement work but does not say if plumbing rough-in changes.",
        source:"Because the site revision conflicts with the old basement note.",
        actions:["Open"],
      },
    ],
    quoteRows:[
      {
        trade:"Plumbing",
        supplier:"Madsen Bros",
        status:"Not sent",
        schedule:"Basement rough-in · Placeholder · Waiting on scope.",
        note:"This request stays held until you choose whether rough-in changes are included.",
        actions:["Open"],
      },
      {
        trade:"Drywall",
        supplier:"Boulevard Drywall",
        status:"Not sent",
        schedule:"Basement drywall · Placeholder · Waiting on scope.",
        note:"Drywall scope depends on the basement answer.",
        actions:["Open"],
      },
    ],
    budgetRows:[
      ["1501", "Basement plumbing", "Quote pending", "$18,000 draft", "scope needs your answer"],
      ["3301", "Basement drywall", "Quote pending", "$22,600 draft", "scope needs your answer"],
      ["6001", "Paint", "Rule-priced", "$8,900", "area from revision"],
    ],
    questionRows:[
      ["Does the basement plumbing rough-in change?", "The revision note conflicts with the old scope."],
      ["Should basement drywall include the storage room?", "The plan hatch is unclear on sheet A2.1."],
    ],
    scheduleRows:[
      ["Basement rough-in", "Placeholder", "Jun 20", "until scope is answered"],
      ["Basement drywall", "Placeholder", "Jul 8", "until rough-in is known"],
      ["Paint", "Placeholder", "Jul 24", "until drywall is known"],
    ],
    done:[
      "Read 9 Humber revision sheets.",
      "Found two scope conflicts before trade requests went out.",
      "Started a small change budget from your rules.",
    ],
    ask:["What is blocking Humber pricing?", "Show the basement scope conflict.", "Which quotes are not sent yet?"],
  },
];

const ScreenEstimatorV2 = ({ onGo, project, setProject }) => {
  const [activeTab, setActiveTab] = React.useState("Quote requests");
  const incomingProject = project && project !== "all" ? project : DEFAULT_ESTIMATOR_PROJECT_V2;
  const [selectedProject, setSelectedProject] = React.useState(incomingProject);

  React.useEffect(() => {
    if (project && project !== "all" && project !== selectedProject) setSelectedProject(project);
  }, [project, selectedProject]);

  React.useEffect(() => {
    setActiveTab("Quote requests");
  }, [selectedProject]);

  const currentProject = ESTIMATOR_PROJECTS_V2.find((item) => item.id === selectedProject) || ESTIMATOR_PROJECTS_V2[0];
  const handleProjectChange = (event) => {
    const nextProject = event.target.value;
    setSelectedProject(nextProject);
    if (setProject) setProject(nextProject);
  };

  return (
    <ScreenShellV2 className="estimator-v2">
      <header className="estimator-header-v2">
        <div className="estimator-header-top-v2">
          <div>
            <span>Current kickoff job</span>
            <h1>{currentProject.name}</h1>
            <p>{currentProject.meta}</p>
          </div>
          <div className="estimator-header-actions-v2">
            <label className="job-select-v2">
              <span>Change job</span>
              <select aria-label="Change estimator job" value={currentProject.id} onChange={handleProjectChange}>
                {ESTIMATOR_PROJECTS_V2.map((item) => (
                  <option value={item.id} key={item.id}>{item.name}</option>
                ))}
              </select>
            </label>
            <StaffControlsV3 label="Estimator" />
            <button type="button">Start a new job</button>
          </div>
        </div>

        <div className="budget-state-v2">
          <b>Where this budget is right now:</b>
          <div>
            {[
              ["Plan read", "done"],
              ["Takeoff done", "done"],
              ["Rules priced", "done"],
              [currentProject.quoteStatus, "working"],
              ["Final budget", "waiting"],
              ["Schedule", "waiting"],
            ].map(([label, state]) => (
              <span className={"is-" + state} key={label}>{label}</span>
            ))}
          </div>
        </div>

        <EstimatorWorkflowV3 onGo={onGo} />

        <div className="estimator-next-v2">
          <p><b>Next thing for you:</b> {currentProject.next}</p>
          <button type="button">{currentProject.nextButton}</button>
        </div>
      </header>

      <section className="staff-block-v2">
        <h2>Needs you</h2>
        <StaffNeedListV2 needs={currentProject.needs} />
      </section>

      <section className="estimator-tabs-card-v2">
        <nav className="job-tabs-v2" aria-label="Estimator details">
          {[
            ["Quote requests", currentProject.quoteCount],
            ["Budget lines", currentProject.budgetCount],
            ["Questions for you", String(currentProject.questionRows.length)],
            ["First schedule draft", currentProject.scheduleRows.length + " rows"],
          ].map(([tab, count]) => (
            <button key={tab} type="button" className={activeTab === tab ? "is-active" : ""} onClick={() => setActiveTab(tab)}>
              {tab} <span>{count}</span>
            </button>
          ))}
        </nav>

        {activeTab === "Quote requests" && (
          <div className="estimator-tab-panel-v2">
            {currentProject.quoteRows.map((row) => (
              <article className="linked-row-v2" key={row.trade + row.supplier}>
                <div>
                  <h3>{row.trade} · {row.supplier} · {row.status}</h3>
                  <p>Affects schedule row: <b>{row.schedule}</b></p>
                  <small>{row.note}</small>
                </div>
                <div className="need-actions-v2">
                  {row.actions.map((action) => (
                    <button className={action.startsWith("Approve") ? "is-primary" : ""} type="button" key={action} onClick={() => action === "Open" && onGo("quotes")}>{action}</button>
                  ))}
                </div>
              </article>
            ))}
          </div>
        )}

        {activeTab === "Budget lines" && (
          <div className="estimate-budget-v2">
            {currentProject.budgetRows.map(([code, item, state, amount, source]) => (
              <div key={code + item}>
                <span className="mono">{code}</span>
                <b>{item}</b>
                <em>{state}</em>
                <strong>{amount}</strong>
                <small>Because {source}.</small>
              </div>
            ))}
          </div>
        )}

        {activeTab === "Questions for you" && (
          <div className="estimator-tab-panel-v2">
            {currentProject.questionRows.map(([title, body]) => (
              <article className="linked-row-v2" key={title}>
                <div>
                  <h3>{title}</h3>
                  <p>{body}</p>
                  <small>Because the Estimator will not guess on plan gaps.</small>
                </div>
                <div className="need-actions-v2">
                  <button type="button">Reply</button>
                  <button type="button">Open</button>
                </div>
              </article>
            ))}
          </div>
        )}

        {activeTab === "First schedule draft" && (
          <div className="estimator-tab-panel-v2">
            {currentProject.scheduleRows.map(([work, state, date, source]) => (
              <article className="linked-row-v2" key={work}>
                <div>
                  <h3>{work} · {date} · {state}</h3>
                  <p>{state.startsWith("Waiting") ? `Waiting on ${source}.` : `Built from ${source}.`}</p>
                  <small>{state.startsWith("Waiting") ? "When the quote comes back, this placeholder becomes a known date or Scheduler asks you to approve a new one." : "This date can stay in the schedule."}</small>
                </div>
                <div className="need-actions-v2">
                  <button type="button" onClick={() => onGo("schedule")}>Open</button>
                </div>
              </article>
            ))}
          </div>
        )}
      </section>

      <section className="staff-block-v2">
        <h2>Already done today</h2>
        <div className="done-lines-v2">
          {currentProject.done.map((line) => <p key={line}>{line}</p>)}
        </div>
      </section>

      <section className="staff-block-v2">
        <h2>Ask Estimator</h2>
        <AskBarV2 title={`Ask Estimator about ${currentProject.short}.`} examples={currentProject.ask} />
      </section>

    </ScreenShellV2>
  );
};

const ScreenStaffV2 = ({ onGo }) => {
  const groups = [
    ["Daily staff", true, [["Bookkeeper", "bills"], ["Field Intake", "field"], ["Client Liaison", "client"], ["Timekeeper", "timekeeper"], ["Accounting Analyst", "accounting"]]],
    ["Client onboarding", true, [["Takeoff Reader", "takeoff"], ["Trade Coordinator", "quotes"], ["Scheduler", "schedule"]]],
  ];

  return (
    <ScreenShellV2 className="staff-index-v2">
      <header className="plain-head-v2">
        <h1>Your AI staff</h1>
        <p>Daily staff handles the morning work. Client onboarding turns plans into a budget, quote plan, and first schedule.</p>
      </header>
      {groups.map(([group, open, people]) => (
        <section className={"staff-index-group-v2" + (!open ? " is-collapsed" : "")} key={group}>
          <button
            className="staff-group-title-v2"
            type="button"
            onClick={() => {}}
          >
            <h2>{group}</h2>
          </button>
          {open && (
            <div>
              {people.map(([name, route]) => (
                <button type="button" key={name} onClick={() => onGo(route)}>
                  <b>{name}</b>
                  <span>Open</span>
                </button>
              ))}
            </div>
          )}
        </section>
      ))}
      <section className="tour-link-v2">
        <h2>First-run tour</h2>
        <p>You have AI staff. They read your email and texts overnight. Every morning you see a short list of things only you can decide.</p>
        <button type="button" onClick={() => onGo("tour")}>Open</button>
      </section>
    </ScreenShellV2>
  );
};

const JOB_TABS_V2 = ["Bills", "Daily logs", "Schedule", "Homeowner portal", "Hours", "Budget", "Plans"];

const JOB_SECTIONS_V2 = {
  "Bills":[
    ["6 bills ready", "$24,318.42 matched to project and cost code.", "Because the Bookkeeper scanned Gmail and Drive overnight."],
    ["1 duplicate held", "Sherwin-Williams invoice matches today's batch.", "Because the Bookkeeper checks invoice number, vendor, and amount."],
  ],
  "Daily logs":[
    ["Tuesday log drafted", "Marco's voice note and 5 photos are attached.", "Because Marco sent a WhatsApp voice note at 7:38 AM."],
    ["Stair detail flagged", "RFI draft is ready if you want to ask BCA.", "Because Field Intake found the issue in the source note."],
  ],
  "Schedule":[
    ["Framing date variance", "Schedule stays unchanged until the daily log is approved.", "Because the Scheduler compares logs to approved dates."],
    ["Roofing placeholder", "Waiting on Hartman Roofing for lead time.", "Because the Trade Coordinator has not received the quote yet."],
  ],
  "Homeowner portal":[
    ["Thursday update ready", "Progress note, 5 photos, and next 60 days of milestones.", "Because Client Liaison filtered out vendor and money notes."],
  ],
  "Hours":[
    ["Jay Patel overtime", "4.5 hours needs your call.", "Because overtime is outside your normal rule."],
    ["Frank Park reply folded in", "8 regular and 2 overtime hours staged.", "Because Frank replied on WhatsApp at 7:42 AM."],
  ],
  "Budget":[
    ["Draft budget total", "$634,710 with quote-pending lines marked.", "Because the Estimator uses your rules and real trade replies."],
    ["Plumbing real quote", "$51,150 folded into the budget.", "Because JMO Plumbing replied to the quote request."],
  ],
  "Plans":[
    ["Architect plan R4", "28 sheets filed and compared to R3.", "Because BCA Studio emailed the revision."],
  ],
};

const HISTORY_FEED_V4 = [
  {
    id:"hist-1",
    time:"8:00 AM",
    job:"St Leonards",
    staff:"Trade Coordinator",
    kind:"Email sent",
    title:"Pella window model follow-up sent.",
    summary:"Second follow-up sent to Sarah Chen and regional rep. This is still holding up the starting budget.",
    detail:"The office sent plan pages, opening counts, and the missing model-number list. If Sarah replies, the answer folds into the budget and schedule. If she does not, Trade Coordinator will ask whether to call or try another vendor.",
  },
  {
    id:"hist-2",
    time:"7:42 AM",
    job:"Oakwood",
    staff:"Timekeeper",
    kind:"WhatsApp received",
    title:"Frank replied with Jay Patel overtime.",
    summary:"Frank said Jay worked 12.5 hours yesterday, including 4.5 overtime.",
    detail:"The Timekeeper folded Frank's message into the weekly hours. Because Jay's overtime is outside the rule, it stayed on Today for owner approval.",
  },
  {
    id:"hist-3",
    time:"7:30 AM",
    job:"St Leonards",
    staff:"Field Intake",
    kind:"WhatsApp sent",
    title:"Daily log reminder sent to Marco.",
    summary:"St Leonards has no daily log in 2 days. The office sent the reminder and held schedule movement.",
    detail:"Field Intake asked for today's work, photos, crew count, blockers, and materials. Scheduler will not move dates until the log comes in.",
  },
  {
    id:"hist-4",
    time:"7:14 AM",
    job:"Glenrose, Oakwood, St Leonards",
    staff:"Bookkeeper",
    kind:"Email to Aaron",
    title:"6 bills ready for QuickBooks.",
    summary:"Dana answered the two bill questions by email. The final batch is waiting for approval.",
    detail:"The office matched vendor, project, cost code, amount, due date, and source evidence. One possible duplicate remains held outside the approved batch.",
  },
  {
    id:"hist-5",
    time:"7:06 AM",
    job:"St Leonards",
    staff:"Takeoff Reader",
    kind:"Budget generated",
    title:"Draft budget created from R4 plans.",
    summary:"Takeoff Reader counted 47 lines and generated a $634,710 rule-priced budget.",
    detail:"Quote-needed lines were marked for windows, roofing, drywall, and HVAC. Scope and placeholder dates were handed to Trade Coordinator and Scheduler.",
  },
  {
    id:"hist-6",
    time:"6:48 AM",
    job:"Glenrose",
    staff:"Client Liaison",
    kind:"Email draft",
    title:"Thursday homeowner update drafted.",
    summary:"Progress note, selected photos, and next milestones are ready for owner approval.",
    detail:"Vendor issues, money notes, and internal staff questions were removed before the homeowner-facing draft was created.",
  },
  {
    id:"hist-7",
    time:"6:31 AM",
    job:"Glenrose",
    staff:"Accounting Analyst",
    kind:"Answer drafted",
    title:"Framing cost answer prepared.",
    summary:"The office pulled 3 bills, 4 time entries, and 2 receipts to explain the overage.",
    detail:"The answer attributes the overage to lumber price movement and Saturday overtime after the rain delay.",
  },
];

const HistoryFilterV4 = ({ label, value, values, onChange }) => (
  <label className="history-filter-v4">
    <span>{label}</span>
    <select value={value} onChange={(event) => onChange(event.target.value)}>
      {values.map((item) => <option key={item} value={item}>{item}</option>)}
    </select>
  </label>
);

const ScreenJobsV2 = () => {
  const [job, setJob] = React.useState("All jobs");
  const [staff, setStaff] = React.useState("All staff");
  const [kind, setKind] = React.useState("All work");
  const [selectedId, setSelectedId] = React.useState(HISTORY_FEED_V4[0].id);
  const jobs = ["All jobs", ...Array.from(new Set(HISTORY_FEED_V4.flatMap((item) => item.job.split(", ").filter(Boolean))))];
  const staffValues = ["All staff", ...Array.from(new Set(HISTORY_FEED_V4.map((item) => item.staff)))];
  const kindValues = ["All work", ...Array.from(new Set(HISTORY_FEED_V4.map((item) => item.kind)))];
  const filtered = HISTORY_FEED_V4.filter((item) => (
    (job === "All jobs" || item.job.includes(job)) &&
    (staff === "All staff" || item.staff === staff) &&
    (kind === "All work" || item.kind === kind)
  ));
  const selected = filtered.find((item) => item.id === selectedId) || filtered[0] || HISTORY_FEED_V4[0];

  return (
    <ScreenShellV2 className="history-page-v4">
      <header className="plain-head-v2">
        <h1>History</h1>
        <p>A running record of what the office did, what came in, what went out, and which teammate handled it.</p>
      </header>
      <section className="history-filters-v4">
        <HistoryFilterV4 label="Job" value={job} values={jobs} onChange={setJob} />
        <HistoryFilterV4 label="Staff" value={staff} values={staffValues} onChange={setStaff} />
        <HistoryFilterV4 label="Type" value={kind} values={kindValues} onChange={setKind} />
      </section>
      <div className="history-workspace-v4">
        <section className="history-feed-v4">
          {filtered.map((item) => (
            <article
              key={item.id}
              className={selected.id === item.id ? "is-active" : ""}
              onClick={() => setSelectedId(item.id)}
            >
              <span>{item.time}</span>
              <div>
                <h2>{item.title}</h2>
                <p>{item.summary}</p>
                <small>{item.job} · {item.staff} · {item.kind}</small>
              </div>
              <div className="history-row-actions-v4">
                <button type="button" onClick={(event) => { event.stopPropagation(); setSelectedId(item.id); }}>Open</button>
                <button type="button" onClick={(event) => { event.stopPropagation(); setSelectedId(item.id); }}>Chat</button>
              </div>
            </article>
          ))}
          {!filtered.length && <p className="empty-v4">No history matches those filters.</p>}
        </section>
        <aside className="history-detail-v4">
          <span>{selected.kind}</span>
          <h2>{selected.title}</h2>
          <p>{selected.detail}</p>
          <dl>
            <div><dt>Time</dt><dd>{selected.time}</dd></div>
            <div><dt>Job</dt><dd>{selected.job}</dd></div>
            <div><dt>Staff</dt><dd>{selected.staff}</dd></div>
          </dl>
          <div className="history-chat-v4">
            <h3>Chat with {selected.staff}</h3>
            <textarea aria-label={"Chat with " + selected.staff} placeholder={`Ask ${selected.staff} about this item...`} />
            <button type="button">Chat</button>
          </div>
        </aside>
      </div>
    </ScreenShellV2>
  );
};

const CHANNEL_EVENTS_V3 = [
  {
    channel:"Email",
    icon:"Email",
    title:"Pella · Sarah Chen",
    job:"St Leonards",
    staff:"Trade Coordinator",
    vendor:"Pella",
    state:"In flight",
    time:"Thu 8:00 AM",
    summary:"Second window quote email sent, copied regional rep.",
    expectation:"Expecting reply today.",
    thread:"Window models and opening counts",
    approvedBy:"Aaron",
  },
  {
    channel:"WhatsApp",
    icon:"WhatsApp",
    title:"Marco · St Leonards",
    job:"St Leonards",
    staff:"Field Intake",
    vendor:"Marco",
    state:"In flight",
    time:"Thu 7:30 AM",
    summary:"Daily log reminder sent.",
    expectation:"Marco usually replies by 9 AM.",
    thread:"Daily log reminder",
    approvedBy:"auto",
  },
  {
    channel:"Email",
    icon:"Email",
    title:"Hartman Roofing",
    job:"Glenrose",
    staff:"Trade Coordinator",
    vendor:"Hartman Roofing",
    state:"In flight",
    time:"Wed 4:00 PM",
    summary:"Roofing quote request sent with roof plan attached.",
    expectation:"Hartman promised by 5 PM today.",
    thread:"Roofing quote request",
    approvedBy:"Sam",
  },
  {
    channel:"WhatsApp",
    icon:"WhatsApp",
    title:"Frank Park",
    job:"Oakwood",
    staff:"Timekeeper",
    vendor:"Frank Park",
    state:"Just came in",
    time:"7:42 AM",
    summary:"Jay worked 12.5 hours yesterday, 4.5 of that overtime.",
    expectation:"Folded into Crew hours. Staged for owner approval on Today.",
    thread:"Oakwood hours",
    approvedBy:"auto",
  },
  {
    channel:"Email",
    icon:"Email",
    title:"JMO Plumbing",
    job:"St Leonards",
    staff:"Estimator",
    vendor:"JMO Plumbing",
    state:"Just came in",
    time:"6:14 AM",
    summary:"Real quote $51,150 for St Leonards plumbing.",
    expectation:"Folded into the St Leonards budget. Estimator updated line 4001.",
    thread:"Plumbing quote",
    approvedBy:"auto",
  },
  {
    channel:"Phone",
    icon:"Phone",
    title:"Sarah Chen · Pella",
    job:"St Leonards",
    staff:"Trade Coordinator",
    vendor:"Pella",
    state:"Phone log",
    time:"Pending",
    summary:"Office prepared call note because Sarah has not replied.",
    expectation:"Mark called after you speak with her.",
    thread:"Pella call note",
    approvedBy:"Aaron",
  },
];

const EXPECTED_CHANNELS_V3 = [
  ["WhatsApp", "Marco's Glenrose daily log", "typically arrives 7:30-8:00 AM"],
  ["Email", "Hartman roofing quote", "promised by 5 PM"],
  ["Email", "Pella window models", "no commitment, second follow-up sent"],
];

const ChannelFiltersV3 = () => (
  <div className="channel-filters-v3">
    <input aria-label="Search channel activity" placeholder="Search vendor, job, or thread" />
    {["By job", "By staff", "By vendor"].map((label) => <button type="button" key={label}>{label}</button>)}
  </div>
);

const ChannelRowV3 = ({ event }) => (
  <article className="channel-row-v3">
    <div className="channel-row-icon-v3">{event.icon}</div>
    <div>
      <h3>{event.title}</h3>
      <p>{event.summary} <em>{event.time}. {event.expectation}</em></p>
      <small>{event.job} · {event.staff} · drafted by {event.staff} · approved by {event.approvedBy}</small>
    </div>
    <button type="button">Open</button>
  </article>
);

const NudgeTemplatesV3 = () => (
  <section className="nudge-templates-v3">
    <div className="section-title-row-v3">
      <div>
        <h2>Outbound nudges</h2>
        <p>The office pings the GC through WhatsApp or email, then folds replies back into Today.</p>
      </div>
    </div>
    <div className="nudge-grid-v3">
      <article>
        <span>WhatsApp · morning batch</span>
        <p>Aaron — 3 things need you today.</p>
        <p>1. $24,318 of bills ready for QuickBooks.</p>
        <p>2. Jay Patel logged 4.5h overtime.</p>
        <p>3. The Glenroses' Thursday update is drafted.</p>
        <small>Reply: "approve 1", "ok 1 and 3", or "call me about 2".</small>
      </article>
      <article>
        <span>WhatsApp · urgent</span>
        <p>Aaron — quick one. Pella still has not sent window models. We emailed twice and texted Sarah Wednesday.</p>
        <small>Reply 1 to call her, 2 to copy you, 3 to try another vendor.</small>
      </article>
      <article>
        <span>Email · morning batch</span>
        <p><b>Subject:</b> 3 things need you · St Leonards, Glenrose</p>
        <p>Here is what is on you today. Everything else the office handled.</p>
        <small>Each item deep-links to Today. Money and homeowner sends still require Today approval.</small>
      </article>
      <article>
        <span>Rules</span>
        <p>Morning time is chosen by the GC. Urgent items can re-trigger. Quiet hours are honored by channel.</p>
        <small>Replies like "approve 1" and "send it" are parsed and written back to the workflow.</small>
      </article>
    </div>
  </section>
);

const ScreenChannelsV3 = () => {
  const [active, setActive] = React.useState("Live");
  const tabs = ["Live", "Email", "WhatsApp", "Phone log"];
  const visible = CHANNEL_EVENTS_V3.filter((event) => {
    if (active === "Live") return true;
    if (active === "Phone log") return event.channel === "Phone";
    return event.channel === active;
  });
  const inFlight = CHANNEL_EVENTS_V3.filter((event) => event.state === "In flight");
  const cameIn = CHANNEL_EVENTS_V3.filter((event) => event.state === "Just came in");

  return (
    <ScreenShellV2 className="channels-v3">
      <header className="channels-head-v3">
        <div>
          <h1>Channels</h1>
          <p>The office's email and WhatsApp activity.</p>
          <small>Right now: 4 emails out · 2 WhatsApps awaiting reply · 1 voice note expected from Marco.</small>
        </div>
        <button type="button"><Icon name="pause" size={14} /> Pause all outbound</button>
      </header>
      <nav className="job-tabs-v2" aria-label="Channel tabs">
        {tabs.map((tab) => (
          <button key={tab} type="button" className={active === tab ? "is-active" : ""} onClick={() => setActive(tab)}>{tab}</button>
        ))}
      </nav>
      <ChannelFiltersV3 />
      {active === "Live" ? (
        <div className="channels-live-v3">
          <section>
            <h2>In flight</h2>
            {inFlight.map((event) => <ChannelRowV3 event={event} key={event.title + event.time} />)}
          </section>
          <section>
            <h2>Expected today</h2>
            {EXPECTED_CHANNELS_V3.map(([channel, title, detail]) => (
              <article className="expected-row-v3" key={title}>
                <b>{channel}</b>
                <span>{title}</span>
                <small>{detail}</small>
              </article>
            ))}
          </section>
          <section>
            <h2>Just came in</h2>
            {cameIn.map((event) => <ChannelRowV3 event={event} key={event.title + event.time} />)}
          </section>
        </div>
      ) : (
        <section className="channels-live-v3">
          <h2>{active}</h2>
          {visible.map((event) => <ChannelRowV3 event={event} key={event.title + event.time} />)}
        </section>
      )}
      <NudgeTemplatesV3 />
    </ScreenShellV2>
  );
};

const ScreenIncomingV2 = () => (
  <ScreenShellV2 className="incoming-v2">
    <header className="plain-head-v2">
      <h1>Incoming</h1>
      <p>Email, texts, Drive uploads, QuickBooks notices, and homeowner messages become sorted work.</p>
    </header>
    <div className="incoming-list-v2">
      {[
        ["WhatsApp", "Glenrose", "Marco photo dump", "Field Intake", "Daily log draft ready", "5 photos", "Daily logs"],
        ["Gmail", "St Leonards", "Window quote reply", "Trade Coordinator", "Missing model numbers", "1 PDF", "Budget"],
        ["Gmail", "Glenrose", "Roofing vendor bill", "Bookkeeper", "Ready for QuickBooks", "1 invoice", "Bills"],
        ["Homeowner", "Glenrose", "Tile allowance question", "Client Liaison", "Reply drafted", "0 files", "Homeowner portal"],
        ["Drive", "St Leonards", "Uploaded drawing revision", "Estimator", "Budget pass started", "28 sheets", "Plans"],
        ["QuickBooks", "Oakwood", "Mapping question", "Bookkeeper", "Needs cost code", "1 notice", "Bills"],
      ].map(([source, job, title, staff, status, files, linked]) => (
        <article key={title} className="incoming-card-v2">
          <span>{source}</span>
          <h2>{title}</h2>
          <p>{job} · {staff} · {status}</p>
          <small>{files} · linked to {linked}</small>
          <button type="button">Open</button>
        </article>
      ))}
    </div>
  </ScreenShellV2>
);

const ScreenAskV2 = () => (
  <ScreenShellV2 className="ask-page-v2">
    <header className="plain-head-v2">
      <h1>Ask</h1>
      <p>Ask in plain English. CoordOS answers from your jobs, source evidence, and staff work.</p>
    </header>
    <AskBarV2 examples={["What's blocking St Leonards?", "Show this week's cash needs", "Who owes hours?"]} />
    <section className="qa-log-v2">
      <div><b>Why are Glenrose framing costs up?</b><p>Answer drafted from 3 bills, 4 time entries, and 2 receipts.</p></div>
      <div><b>Any project slipping?</b><p>St Leonards is waiting on daily logs and roofing lead time.</p></div>
      <div><b>Who owes hours?</b><p>Carlos Diaz and Jay Patel need follow-up or approval.</p></div>
    </section>
  </ScreenShellV2>
);

const ScreenApprovalsSettingsV3 = () => (
  <ScreenShellV2 className="settings-v3">
    <header className="plain-head-v2">
      <h1>Who approves what?</h1>
      <p>Simple rules for what the office can handle and when it has to ask a person.</p>
    </header>
    <section className="approval-sentences-v3">
      {[
        ["You", "Aaron, Owner", "approve", ["money over $5,000", "homeowner messages", "scope changes", "overtime exceptions"]],
        ["Your Project Manager", "Sam", "approves", ["daily logs", "schedule shifts under 3 days", "trade follow-up emails"]],
        ["Your Bookkeeper", "Dana", "approves", ["vendor cost code mappings", "routine bills under $5,000", "duplicate confirmations"]],
        ["Your Site Super", "Marco", "is asked for", ["daily logs", "photos", "field questions"]],
      ].map(([who, person, verb, rules]) => (
        <article key={who}>
          <h2>{who} <span>({person})</span> {verb}:</h2>
          <p>{rules.map((rule) => <button type="button" key={rule}>{rule}</button>)}</p>
        </article>
      ))}
    </section>
    <section className="quiet-hours-v3">
      <div>
        <h2>Quiet hours</h2>
        <p>No WhatsApp after <button type="button">7 PM</button> · no email after <button type="button">9 PM</button>. Urgent items still show on Today.</p>
      </div>
      <button type="button">Add a teammate</button>
    </section>
  </ScreenShellV2>
);

const ScreenHistoryV2 = ({ onGo }) => (
  <ScreenShellV2 className="history-v2">
    <header className="plain-head-v2">
      <h1>History</h1>
      <p>Proof of what the office did, what was sent, who approved it, and which thread it belongs to.</p>
    </header>
    <div className="history-table-v2">
      {HISTORY_ROWS_V2.map(([time, staff, human, action, source, system, result]) => (
        <article key={time + action}>
          <span>{time}</span>
          <h2>{action}</h2>
          <p>{staff} · approved by {human} · {source}</p>
          <small>{system} · {result}</small>
          <button type="button" onClick={() => onGo("channels")}>Open</button>
        </article>
      ))}
    </div>
  </ScreenShellV2>
);

const ScreenTourV2 = ({ onGo }) => {
  const [step, setStep] = React.useState(0);
  const steps = [
    ["You have an AI office.", BACKGROUND_PROMISE_V3],
    ["It works through email and WhatsApp.", "The office sends follow-ups, watches replies, drafts updates, files photos, and reconciles bills through the channels your crew already uses."],
    ["We only ping you when stuck.", "Most work stays invisible. If the office cannot move without you, it sends a nudge and puts the item on Today."],
    ["Open Today to clear the queue.", "Each item shows what the office already tried, why it needs you, and the source thread behind it."],
  ];
  const [title, body] = steps[step];

  return (
    <ScreenShellV2 className="tour-v2">
      <div className="tour-overlay-v2">
        <section className="tour-card-v2" role="dialog" aria-modal="true" aria-label="First-run tour">
          <span>Step {step + 1} of {steps.length}</span>
          <h1>{title}</h1>
          <p>{body}</p>
          <div className="tour-progress-v2">
            {steps.map((_, i) => <span key={i} className={i <= step ? "is-active" : ""} />)}
          </div>
          <div className="tour-actions-v2">
            <button type="button" onClick={() => onGo("today")}>Open</button>
            {step > 0 && <button type="button" onClick={() => setStep((value) => value - 1)}>Back</button>}
            {step < steps.length - 1
              ? <button className="is-primary" type="button" onClick={() => setStep((value) => value + 1)}>Open</button>
              : <button className="is-primary" type="button" onClick={() => onGo("today")}>Open</button>}
          </div>
        </section>
      </div>
    </ScreenShellV2>
  );
};
