const { useState, useEffect, useRef } = React;
// ─── SUPABASE CONFIG ───
const SUPABASE_URL = "https://xqkouxndymijvlvnhscq.supabase.co";
const SUPABASE_KEY = "sb_publishable_JPzdZVVKELNCQqPWpullAg_x8Xi5ktM";

// Simple Supabase client
const sb = {
  headers: (token) => ({
    "Content-Type": "application/json",
    "apikey": SUPABASE_KEY,
    "Authorization": `Bearer ${token || SUPABASE_KEY}`,
  }),
  auth: {
    signUp: async (email, password, fullName) => {
      const r = await fetch(`${SUPABASE_URL}/auth/v1/signup`, {
        method: "POST", headers: { "Content-Type": "application/json", "apikey": SUPABASE_KEY },
        body: JSON.stringify({ email, password, data: { full_name: fullName } }),
      });
      return r.json();
    },
    signIn: async (email, password) => {
      const r = await fetch(`${SUPABASE_URL}/auth/v1/token?grant_type=password`, {
        method: "POST", headers: { "Content-Type": "application/json", "apikey": SUPABASE_KEY },
        body: JSON.stringify({ email, password }),
      });
      return r.json();
    },
    getUser: async (token) => {
      const r = await fetch(`${SUPABASE_URL}/auth/v1/user`, {
        headers: { "apikey": SUPABASE_KEY, "Authorization": `Bearer ${token}` },
      });
      return r.json();
    },
  },
  from: (table, token) => ({
    select: async (query = "*", filters = "") => {
      const r = await fetch(`${SUPABASE_URL}/rest/v1/${table}?select=${query}${filters}`, { headers: sb.headers(token) });
      return r.json();
    },
    insert: async (data) => {
      const r = await fetch(`${SUPABASE_URL}/rest/v1/${table}`, {
        method: "POST", headers: { ...sb.headers(token), "Prefer": "return=representation" },
        body: JSON.stringify(data),
      });
      return r.json();
    },
    update: async (data, match) => {
      const r = await fetch(`${SUPABASE_URL}/rest/v1/${table}?${match}`, {
        method: "PATCH", headers: { ...sb.headers(token), "Prefer": "return=representation" },
        body: JSON.stringify(data),
      });
      return r.json();
    },
    delete: async (match) => {
      await fetch(`${SUPABASE_URL}/rest/v1/${table}?${match}`, {
        method: "DELETE", headers: sb.headers(token),
      });
    },
  }),
};

// ─── CONSTANTS ───
const REDLINE_FLOOR = 500;
const SHINGLE_COLORS = ["Charcoal","Weathered Wood","Hickory","Barkwood","Pewter Gray","Slate","Shakewood","Williamsburg Gray","Hunter Green","Mission Brown","Patriot Red","Oyster Gray","Driftwood","Fox Hollow Gray","Custom"];
const DRIP_EDGE_COLORS = ["White","Brown","Black","Charcoal Gray","Weathered Wood","Musket Brown","Royal Brown","Tuxedo Gray","Almond","Custom"];

const fmt = (n) => { if (n == null || isNaN(n)) return "$0.00"; return new Intl.NumberFormat("en-US",{style:"currency",currency:"USD"}).format(n); };

// ─── THEME ───
// Theme — matches westpeakroofing.com brand tokens (Utah heritage earth tones).
// Source palette: --color-peak-* CSS variables from the live marketing site.
const T = {
  bg:"#fafafa",              // peak-bg
  bgCard:"#ffffff",          // white cards on off-white bg for subtle elevation
  bgInput:"#f5ecd6",         // peak-cream — soft surface for inputs/secondary
  border:"#ebe0c0",          // peak-cream-dark — subtle borders
  borderStrong:"#2e2a27",    // peak-ink — strong border where needed
  accent:"#565132",          // peak-olive — brand accent (heritage olive)
  accentDark:"#3e3a24",      // peak-olive-dark — pressed/hover
  accentLight:"#f5ecd6",     // peak-cream — accent surface backdrop
  text:"#2e2a27",            // peak-ink — primary text
  textSec:"#4a423d",         // peak-ink-soft — secondary text
  textDim:"#6d6051",         // peak-muted — tertiary text
  danger:"#a13a2a",          // warm brick (fits earth palette, not bright red)
  dangerBg:"#f3d6cf",        // soft brick wash
  warn:"#b99974",            // peak-tan-dark — secondary/warn accent
  tan:"#d4b594",             // peak-tan
  tanLight:"#e6cca8",        // peak-tan-light
};

// ─── ICONS ───
const IconBack=({s=18})=><svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="15 18 9 12 15 6"/></svg>;
const IconRight=({s=18})=><svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="9 18 15 12 9 6"/></svg>;
const IconPlus=({s=18})=><svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>;
const IconLock=({s=14})=><svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>;
const IconTrash=({s=16})=><svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>;
const IconPrint=({s=18})=><svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="6 9 6 2 18 2 18 9"/><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"/><rect x="6" y="14" width="12" height="8"/></svg>;
const IconShield=({s=18})=><svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>;
const IconCheck=({s=16})=><svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"/></svg>;
const IconAlert=({s=16})=><svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>;
const IconPen=({s=18})=><svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/></svg>;
const IconHome=({s=20})=><svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>;
const IconUsers=({s=18})=><svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>;
const IconSettings=({s=18})=><svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>;
const IconLogout=({s=18})=><svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>;

// ─── COMPONENTS ───
const Toggle=({on,onToggle,label,disabled=false})=>(<button onClick={disabled?undefined:onToggle} disabled={disabled} style={{display:"flex",alignItems:"center",gap:12,background:"none",border:"none",cursor:disabled?"not-allowed":"pointer",padding:"10px 0",width:"100%",opacity:disabled?0.5:1}}><div style={{width:48,height:26,borderRadius:13,transition:"all 0.2s",background:on?T.accent:T.border,position:"relative",flexShrink:0,boxShadow:"inset 0 1px 2px rgba(46,42,39,0.06)"}}><div style={{width:20,height:20,borderRadius:10,background:"#ffffff",position:"absolute",top:3,left:on?25:3,transition:"left 0.2s",boxShadow:"0 1px 3px rgba(46,42,39,0.18)"}}/></div><span style={{fontSize:15,fontWeight:600,color:T.text}}>{label}</span></button>);
const NumInput=({value,onChange,label,prefix="$",placeholder="0",small=false,disabled=false})=>(<div style={{flex:1,minWidth:small?80:110,opacity:disabled?0.55:1}}>{label&&<div style={{fontSize:11,fontWeight:600,color:T.textDim,marginBottom:6,textTransform:"uppercase",letterSpacing:"0.06em"}}>{label}</div>}<div style={{display:"flex",alignItems:"center",border:`1px solid ${T.border}`,borderRadius:10,padding:"0 12px",height:small?38:44,background:disabled?"#f5f0e6":"#ffffff"}}>{prefix&&<span style={{fontSize:14,color:T.textDim,marginRight:4,fontWeight:500}}>{prefix}</span>}<input type="number" value={value===0?"":value} onChange={(e)=>onChange(parseFloat(e.target.value)||0)} disabled={disabled} placeholder={placeholder} style={{border:"none",outline:"none",background:"transparent",width:"100%",fontSize:15,fontWeight:600,color:T.text,fontVariantNumeric:"tabular-nums",cursor:disabled?"not-allowed":"text"}}/></div></div>);
const Card=({children,style={}})=>(<div style={{background:T.bgCard,borderRadius:14,padding:20,border:`1px solid ${T.border}`,marginBottom:14,boxShadow:"0 1px 2px rgba(46,42,39,0.04)",...style}}>{children}</div>);
const SectionLabel=({children,icon})=>(<div style={{fontSize:10,fontWeight:700,color:T.textDim,textTransform:"uppercase",letterSpacing:"0.14em",marginBottom:14,display:"flex",alignItems:"center",gap:6}}>{icon}{children}</div>);
const NavBar=({title,onBack,rightAction})=>(<div style={{display:"flex",alignItems:"center",justifyContent:"space-between",padding:"calc(14px + env(safe-area-inset-top)) 20px 14px",borderBottom:`1px solid ${T.border}`,background:"rgba(250,250,250,0.92)",backdropFilter:"blur(12px)",WebkitBackdropFilter:"blur(12px)",position:"sticky",top:0,zIndex:10}}>{onBack?<button onClick={onBack} style={{background:"none",border:"none",cursor:"pointer",display:"flex",alignItems:"center",gap:4,color:T.accent,fontSize:14,fontWeight:600,fontFamily:"inherit",padding:0}}><IconBack/> Back</button>:<div style={{width:60}}/>}<div style={{fontSize:15,fontWeight:700,color:T.text,letterSpacing:"-0.01em"}}>{title}</div>{rightAction||<div style={{width:60}}/>}</div>);
const LineItem=({label,value,color=T.text,sub=false})=>(<div style={{display:"flex",justifyContent:"space-between",fontSize:sub?13:14,padding:"4px 0"}}><span style={{color:sub?T.textDim:T.textSec,paddingLeft:sub?16:0}}>{label}</span><span style={{fontWeight:700,fontVariantNumeric:"tabular-nums",color}}>{value}</span></div>);

const OOPBox=({c,activeJob})=>(<div style={{background:T.accent,borderRadius:18,padding:24,color:"#f5ecd6",boxShadow:"0 4px 24px rgba(46,42,39,0.10)",border:`1px solid ${T.accentDark}`}}>
  <div style={{fontSize:11,fontWeight:700,letterSpacing:"0.15em",textTransform:"uppercase",color:"#e6cca8",marginBottom:16}}>Homeowner Out of Pocket</div>
  <div style={{display:"flex",flexDirection:"column",gap:8}}>
    {c.roofGapTotal>0&&!c.insuranceFullyCovered&&<div style={{display:"flex",justifyContent:"space-between",fontSize:15}}><span style={{opacity:0.85}}>Roof Cost Gap</span><span style={{fontWeight:700,fontVariantNumeric:"tabular-nums"}}>{fmt(c.roofGapTotal)}</span></div>}
    <div style={{display:"flex",justifyContent:"space-between",fontSize:15}}><span style={{opacity:0.85}}>Your Deductible</span><span style={{fontWeight:700,fontVariantNumeric:"tabular-nums"}}>{fmt(activeJob.deductible)}</span></div>
    {activeJob.vent_upgrade_enabled&&<><div style={{display:"flex",justifyContent:"space-between",fontSize:15}}><span style={{opacity:0.85}}>Ventilation Upgrade</span><span style={{fontWeight:700,fontVariantNumeric:"tabular-nums"}}>{fmt(c.ventTotalSell)}</span></div>{c.ventInsuranceCredit>0&&<div style={{display:"flex",justifyContent:"space-between",fontSize:13,paddingLeft:16}}><span style={{opacity:0.6}}>Insurance vent credit</span><span style={{fontWeight:600,fontVariantNumeric:"tabular-nums",color:"#e6cca8"}}>−{fmt(c.ventInsuranceCredit)}</span></div>}</>}
    {activeJob.gutters_enabled&&<><div style={{display:"flex",justifyContent:"space-between",fontSize:15}}><span style={{opacity:0.85}}>Gutters</span><span style={{fontWeight:700,fontVariantNumeric:"tabular-nums"}}>{fmt(activeJob.gutters_lump_sum)}</span></div>{c.gutterInsCredit>0&&<div style={{display:"flex",justifyContent:"space-between",fontSize:13,paddingLeft:16}}><span style={{opacity:0.6}}>Insurance gutter credit</span><span style={{fontWeight:600,fontVariantNumeric:"tabular-nums",color:"#e6cca8"}}>−{fmt(c.gutterInsCredit)}</span></div>}</>}
    <div style={{borderTop:"1px solid rgba(245,236,214,0.25)",marginTop:6,paddingTop:14,display:"flex",justifyContent:"space-between",alignItems:"baseline"}}><span style={{fontSize:16,fontWeight:700,color:"#ffffff"}}>Total Out of Pocket</span><span style={{fontSize:28,fontWeight:700,fontVariantNumeric:"tabular-nums",letterSpacing:"-0.02em",color:"#ffffff"}}>{fmt(c.homeownerOOP)}</span></div>
  </div>
</div>);

const SignaturePad = ({ onSign }) => {
  const canvasRef = useRef(null);
  const drawing = useRef(false);
  const hasDrawn = useRef(false);
  const getPos = (e) => {
    const r = canvasRef.current.getBoundingClientRect();
    const t = e.touches ? e.touches[0] : e;
    return { x: t.clientX - r.left, y: t.clientY - r.top };
  };
  const start = (e) => {
    e.preventDefault();
    drawing.current = true;
    hasDrawn.current = true;
    const ctx = canvasRef.current.getContext("2d");
    const p = getPos(e);
    ctx.beginPath();
    ctx.moveTo(p.x, p.y);
  };
  const move = (e) => {
    if (!drawing.current) return;
    e.preventDefault();
    const ctx = canvasRef.current.getContext("2d");
    const p = getPos(e);
    ctx.lineTo(p.x, p.y);
    ctx.strokeStyle = T.accent;
    ctx.lineWidth = 2.5;
    ctx.lineCap = "round";
    ctx.stroke();
  };
  const end = () => { drawing.current = false; };
  const clear = () => {
    const ctx = canvasRef.current.getContext("2d");
    ctx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height);
    hasDrawn.current = false;
  };
  // Touch handlers are registered natively with { passive: false } so preventDefault
  // actually fires on mobile. React's synthetic touch events are passive by default,
  // which silently no-ops preventDefault and lets the page scroll instead of drawing.
  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const startTouch = (e) => start(e);
    const moveTouch = (e) => move(e);
    const endTouch = (e) => end(e);
    canvas.addEventListener("touchstart", startTouch, { passive: false });
    canvas.addEventListener("touchmove", moveTouch, { passive: false });
    canvas.addEventListener("touchend", endTouch);
    return () => {
      canvas.removeEventListener("touchstart", startTouch);
      canvas.removeEventListener("touchmove", moveTouch);
      canvas.removeEventListener("touchend", endTouch);
    };
  }, []);
  return (
    <div>
      <div style={{ fontSize: 13, color: T.textSec, marginBottom: 8 }}>Sign below to agree to the amount shown above</div>
      <canvas
        ref={canvasRef}
        width={460}
        height={160}
        onMouseDown={start}
        onMouseMove={move}
        onMouseUp={end}
        onMouseLeave={end}
        style={{ width: "100%", height: 160, border: `1.5px solid ${T.border}`, borderRadius: 12, background: T.bgInput, cursor: "crosshair", touchAction: "none" }}
      />
      <div style={{ display: "flex", gap: 10, marginTop: 10 }}>
        <button onClick={clear} style={{ flex: 1, padding: "10px", border: `1.5px solid ${T.border}`, borderRadius: 10, background: "transparent", color: T.textSec, fontSize: 14, fontWeight: 600, cursor: "pointer", fontFamily: "inherit" }}>Clear</button>
        <button onClick={() => { if (hasDrawn.current) onSign(canvasRef.current.toDataURL("image/png")); }} style={{ flex: 2, padding: "10px", border: "none", borderRadius: 10, background: T.accent, color: "#fff", fontSize: 14, fontWeight: 700, cursor: "pointer", fontFamily: "inherit" }}>Confirm Signature</button>
      </div>
    </div>
  );
};

const PinModal=({onSuccess,onCancel})=>{const[pin,setPin]=useState("");const[error,setError]=useState(false);const ref=useRef(null);useEffect(()=>{if(ref.current)ref.current.focus()},[]);return(<div style={{position:"fixed",inset:0,background:"rgba(0,0,0,0.88)",display:"flex",alignItems:"center",justifyContent:"center",zIndex:100,padding:20}}><div style={{background:T.bgCard,borderRadius:20,padding:32,maxWidth:340,width:"100%",border:`1px solid ${T.border}`,textAlign:"center"}}><div style={{marginBottom:20,color:T.accent}}><IconLock s={28}/></div><div style={{fontSize:18,fontWeight:700,color:T.text,marginBottom:8}}>Enter PIN</div><div style={{fontSize:13,color:T.textDim,marginBottom:24}}>Access commission details</div><input ref={ref} type="password" inputMode="numeric" maxLength={4} value={pin} onChange={(e)=>setPin(e.target.value.replace(/\D/g,""))} onKeyDown={(e)=>{if(e.key==="Enter"){if(pin===onSuccess.pin)onSuccess.fn();else{setError(true);setPin("");setTimeout(()=>setError(false),1500)}}}} style={{width:"100%",textAlign:"center",fontSize:32,fontWeight:700,fontVariantNumeric:"tabular-nums",letterSpacing:12,padding:"12px",border:`2px solid ${error?T.danger:T.border}`,borderRadius:12,background:T.bgInput,color:T.text,outline:"none",boxSizing:"border-box"}}/>{error&&<div style={{color:T.danger,fontSize:13,fontWeight:600,marginTop:8}}>Incorrect PIN</div>}<div style={{display:"flex",gap:10,marginTop:20}}><button onClick={onCancel} style={{flex:1,padding:"12px",border:`1.5px solid ${T.border}`,borderRadius:12,background:"transparent",color:T.textSec,fontSize:14,fontWeight:600,cursor:"pointer",fontFamily:"inherit"}}>Cancel</button><button onClick={()=>{if(pin===onSuccess.pin)onSuccess.fn();else{setError(true);setPin("");setTimeout(()=>setError(false),1500)}}} style={{flex:1,padding:"12px",border:"none",borderRadius:12,background:T.accent,color:"#fff",fontSize:14,fontWeight:700,cursor:"pointer",fontFamily:"inherit"}}>Unlock</button></div></div></div>)};

// ─── CALC FUNCTION ───
const calcJob = (job, pricing, ventPricing) => {
  if (!job || !pricing?.length) return {};
  const shingle = pricing.find(p => p.shingle_type === job.shingle_type) || pricing[0];
  const ridgeCost = ventPricing?.find(v => v.vent_type === "ridge")?.cost || 6;
  const boxCoverCost = ventPricing?.find(v => v.vent_type === "box_cover")?.cost || 20;
  // Strip credited line items from RCV before computing per-square shingle insurance.
  // Gutter D&R and turtle vent value are paid to the homeowner inside the ACV/dep
  // checks AND shown as credits on the OOP screen — counting them in insurancePerSq
  // too would credit the homeowner twice (the bug Dallin sensed on Carpenter).
  const creditedLineItems =
    (job.gutters_enabled ? (job.gutter_insurance_coverage || 0) : 0) +
    (job.vent_upgrade_enabled ? (job.turtle_vent_total_value || 0) : 0);
  const roofInsurance = Math.max(0, job.rcv - creditedLineItems);
  const insurancePerSq = job.squares > 0 ? roofInsurance / job.squares : 0;
  const roofGapPerSq = Math.max(0, job.sell_price_per_square - insurancePerSq);
  const roofGapTotal = roofGapPerSq * job.squares;
  const insuranceFullyCovered = insurancePerSq >= job.sell_price_per_square;
  const shingleCommission = (job.sell_price_per_square - shingle.cost) * job.squares;
  const ridgeTotal = job.vent_upgrade_enabled ? job.ridge_vent_sell_price * job.ridge_vent_qty : 0;
  const ridgeCommission = job.vent_upgrade_enabled ? (job.ridge_vent_sell_price - ridgeCost) * job.ridge_vent_qty : 0;
  const boxCoverTotal = job.vent_upgrade_enabled ? job.box_cover_sell_price * job.box_cover_qty : 0;
  const boxCoverCommission = job.vent_upgrade_enabled ? (job.box_cover_sell_price - boxCoverCost) * job.box_cover_qty : 0;
  const ventTotalSell = ridgeTotal + boxCoverTotal;
  const ventInsuranceCredit = job.turtle_vent_total_value;
  const ventOOP = job.vent_upgrade_enabled ? Math.max(0, ventTotalSell - ventInsuranceCredit) : 0;
  const ventCommission = ridgeCommission + boxCoverCommission;
  const gutterInsCredit = job.gutters_enabled ? job.gutter_insurance_coverage : 0;
  const gutterOOP = job.gutters_enabled ? Math.max(0, job.gutters_lump_sum - gutterInsCredit) : 0;
  const homeownerOOP = roofGapTotal + job.deductible + ventOOP + gutterOOP;
  const totalCommission = Math.max(0, shingleCommission) + Math.max(0, ventCommission);
  const totalJobValue = (job.sell_price_per_square * job.squares) + ventOOP + gutterOOP;
  const displayShingleColor = job.shingle_color === "Custom" ? job.shingle_color_custom : job.shingle_color;
  const displayDripEdge = job.drip_edge_color === "Custom" ? job.drip_edge_color_custom : job.drip_edge_color;
  return { shingle:{...shingle,name:shingle.display_name}, insurancePerSq, roofGapPerSq, roofGapTotal, insuranceFullyCovered, shingleCommission, ridgeTotal, ridgeCommission, boxCoverTotal, boxCoverCommission, ventTotalSell, ventInsuranceCredit, ventOOP, ventCommission, gutterInsCredit, gutterOOP, homeownerOOP, totalCommission, totalJobValue, displayShingleColor, displayDripEdge, ridgeCost, boxCoverCost };
};

// ─── MAIN APP ───
function App() {
  const [screen, setScreen] = useState("loading");
  const [token, setToken] = useState(null);
  const [user, setUser] = useState(null);
  const [profile, setProfile] = useState(null);
  const [jobs, setJobs] = useState([]);
  const [activeJob, setActiveJob] = useState(null);
  const [pricing, setPricing] = useState([]);
  const [ventPricing, setVentPricing] = useState([]);
  const [allProfiles, setAllProfiles] = useState([]);
  const [showPin, setShowPin] = useState(false);
  const [showSig, setShowSig] = useState(false);
  const [signed, setSigned] = useState(false);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState("");
  // Auth form state
  const [authMode, setAuthMode] = useState("login");
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [fullName, setFullName] = useState("");
  const [businessName, setBusinessName] = useState("");
  const [profilePin, setProfilePin] = useState("");
  const [profileBname, setProfileBname] = useState("");
  const [profileFullName, setProfileFullName] = useState("");
  const [debugLog, setDebugLog] = useState([]);
  const addDebug = (msg) => setDebugLog(prev => [...prev, new Date().toLocaleTimeString() + ": " + msg]);
  // ─── JobNimbus import flow state ───
  const [jnQuery, setJnQuery] = useState("");
  const [jnResults, setJnResults] = useState([]);
  const [jnSearching, setJnSearching] = useState(false);
  const [jnSelectedJob, setJnSelectedJob] = useState(null);
  const [jnFiles, setJnFiles] = useState([]);
  const [jnFilesLoading, setJnFilesLoading] = useState(false);
  const [jnParsing, setJnParsing] = useState(false);
  const [jnParseResult, setJnParseResult] = useState(null);
  const [jnError, setJnError] = useState("");
  // Debounced JN search — 300ms after typing stops
  useEffect(() => {
    if (screen !== "jn_import") return;
    let cancelled = false;
    setJnSearching(true);
    const t = setTimeout(async () => {
      try {
        const r = await fetch("/api/jn/jobs?q=" + encodeURIComponent(jnQuery));
        const d = await r.json();
        if (!cancelled) setJnResults(d.jobs || []);
      } catch (e) { if (!cancelled) setJnError("Search failed: " + e.message); }
      finally { if (!cancelled) setJnSearching(false); }
    }, 300);
    return () => { cancelled = true; clearTimeout(t); };
  }, [jnQuery, screen]);
  // Auto-fetch files when a job is selected
  useEffect(() => {
    if (!jnSelectedJob) { setJnFiles([]); return; }
    let cancelled = false;
    setJnFilesLoading(true);
    (async () => {
      try {
        const r = await fetch("/api/jn/files?jnid=" + encodeURIComponent(jnSelectedJob.id));
        const d = await r.json();
        if (!cancelled) {
          // Sort: filenames containing "scope"/"estimate" to the top — those are usually the carrier estimate
          const files = (d.files || []).sort((a, b) => {
            const aScore = /scope|estimate|state farm|allstate|usaa|traveler|liberty|bear river/i.test(a.filename) ? 0 : 1;
            const bScore = /scope|estimate|state farm|allstate|usaa|traveler|liberty|bear river/i.test(b.filename) ? 0 : 1;
            return aScore - bScore;
          });
          setJnFiles(files);
        }
      } catch (e) { if (!cancelled) setJnError("File list failed: " + e.message); }
      finally { if (!cancelled) setJnFilesLoading(false); }
    })();
    return () => { cancelled = true; };
  }, [jnSelectedJob]);
  // JN auto-upload status — flips through "uploading" → "success" | "failed" inside
  // the contract-sign flow. Surfaced as a toast on the Summary screen and as a
  // home-screen badge for admins (failed uploads = signed && jnid && !jn_uploaded_at).
  const [jnUploadStatus, setJnUploadStatus] = useState(null);
  // Reset transient upload toast when switching jobs so we don't show stale status.
  useEffect(() => { setJnUploadStatus(null); }, [activeJob?.id]);

  // Job Margin "Total Job Value" — typed by the rep, defaults to calcJob's existing
  // totalJobValue = (sell × squares) + ventOOP + gutterOOP. Same number the app has
  // always shown as "Total billed to homeowner"; tier click commits the back-solved sell.
  const [whatIfTotal, setWhatIfTotal] = useState(0);
  useEffect(() => {
    if (!activeJob?.id || !pricing?.length) return;
    const cc = calcJob(activeJob, pricing, ventPricing);
    setWhatIfTotal(cc.totalJobValue || 0);
  }, [activeJob?.id, pricing?.length]);

  const isAdmin = profile?.role === "admin";

  const appStyle = { fontFamily:"'DM Sans','Helvetica Neue',system-ui,sans-serif", background:T.bg, minHeight:"100vh", color:T.text, maxWidth:520, margin:"0 auto" };
  const fontLink = <>
    <link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700;1,9..40,400&display=swap" rel="stylesheet"/>
    <style>{`@keyframes spin { to { transform: rotate(360deg); } }`}</style>
  </>;
  const inputStyle = {width:"100%",border:`1px solid ${T.border}`,borderRadius:10,padding:"12px",fontSize:15,fontFamily:"inherit",outline:"none",background:"#ffffff",color:T.text,boxSizing:"border-box"};
  const btnPrimary = {width:"100%",padding:"15px",border:"none",borderRadius:12,background:T.accent,color:"#fff",fontSize:15,fontWeight:600,cursor:"pointer",fontFamily:"inherit",letterSpacing:"0.01em",boxShadow:"0 1px 3px rgba(46,42,39,0.10)"};
  const fieldLabel = (t) => <div style={{fontSize:11,fontWeight:600,color:T.textDim,marginBottom:6,textTransform:"uppercase",letterSpacing:"0.06em"}}>{t}</div>;

  // ─── INIT: check for saved session ───
  useEffect(() => {
    const saved = localStorage.getItem("wp_token");
    if (saved) { restoreSession(saved); }
    else setScreen("auth");
  }, []);

  const restoreSession = async (t) => {
    try {
      addDebug("1. Getting user...");
      const res = await sb.auth.getUser(t);
      addDebug("2. Got: " + JSON.stringify(res).substring(0, 150));
      const u = res?.user || res;
      if (u?.id) {
        addDebug("3. User ID: " + u.id);
        setToken(t); setUser(u);
        addDebug("4. Fetching profile...");
        let profiles = await sb.from("profiles", t).select("*", `&id=eq.${u.id}`);
        addDebug("5. Profiles: " + JSON.stringify(profiles).substring(0, 200));
        if (!profiles || !Array.isArray(profiles) || profiles.length === 0) {
          addDebug("6. Creating profile...");
          const isAdminUser = u.email === "dallinr@westpeakroofing.com";
          const ins = await sb.from("profiles", t).insert({
            id: u.id, email: u.email,
            full_name: u.user_metadata?.full_name || "",
            business_name: "",
            role: isAdminUser ? "admin" : "pending",
          });
          addDebug("7. Insert: " + JSON.stringify(ins).substring(0, 200));
          profiles = await sb.from("profiles", t).select("*", `&id=eq.${u.id}`);
          addDebug("8. Re-fetch: " + JSON.stringify(profiles).substring(0, 200));
        }
        if (profiles?.[0]) { setProfile(profiles[0]); addDebug("9. Role: " + profiles[0].role); }
        else { addDebug("9. NO PROFILE FOUND"); }
        addDebug("10. Loading pricing...");
        await loadPricing(t);
        if (profiles?.[0]?.role === "admin" || profiles?.[0]?.role === "rep") {
          addDebug("11. Loading jobs...");
          await loadJobs(t, profiles[0]);
          addDebug("12. HOME");
          setScreen("home");
        } else if (profiles?.[0]?.role === "pending") {
          addDebug("11. PENDING");
          setScreen("pending");
        } else {
          addDebug("11. No role -> auth");
          setScreen("debug");
        }
      } else {
        addDebug("3. No user ID");
        setScreen("debug");
      }
    } catch (e) {
      addDebug("CATCH: " + e.message);
      setScreen("debug");
    }
  };

  const loadPricing = async (t) => {
    const p = await sb.from("pricing", t).select("*", "&order=sort_order");
    if (Array.isArray(p)) setPricing(p);
    const vp = await sb.from("vent_pricing", t).select("*");
    if (Array.isArray(vp)) setVentPricing(vp);
  };

  const loadJobs = async (t, prof) => {
    const filter = prof?.role === "admin" ? "&order=created_at.desc" : `&rep_id=eq.${prof?.id}&order=created_at.desc`;
    // Embed rep profile for the admin "uploaded by" display (rep:profiles!jobs_rep_id_fkey(full_name))
    const j = await sb.from("jobs", t).select("*,rep:profiles!jobs_rep_id_fkey(full_name)", filter);
    if (Array.isArray(j)) setJobs(j);
  };

  // Format full_name as "F Lastname" (first-initial + last word). Falls back gracefully.
  const repShortName = (fullName) => {
    if (!fullName) return "";
    const parts = String(fullName).trim().split(/\s+/);
    if (parts.length === 1) return parts[0];
    return `${parts[0][0]} ${parts[parts.length - 1]}`;
  };

  const loadAllProfiles = async () => {
    const p = await sb.from("profiles", token).select("*", "&order=created_at.desc");
    if (Array.isArray(p)) setAllProfiles(p);
  };

  const logout = () => {
    localStorage.removeItem("wp_token");
    setToken(null); setUser(null); setProfile(null); setJobs([]); setActiveJob(null);
    setScreen("auth"); setEmail(""); setPassword(""); setFullName(""); setBusinessName("");
  };

  const handleAuth = async () => {
    setLoading(true); setError(""); setDebugLog([]);
    try {
      if (authMode === "login") {
        addDebug("Login: " + email);
        const res = await sb.auth.signIn(email, password);
        addDebug("Response: " + JSON.stringify(res).substring(0, 200));
        const accessToken = res?.access_token || res?.session?.access_token;
        if (accessToken) {
          addDebug("Got token, restoring...");
          localStorage.setItem("wp_token", accessToken);
          await restoreSession(accessToken);
        } else { 
          setError("No token: " + JSON.stringify(res).substring(0, 200));
          setScreen("debug");
        }
      } else {
        const res = await sb.auth.signUp(email, password, fullName);
        console.log("SIGNUP RESPONSE:", JSON.stringify(res).substring(0, 500));
        const accessToken = res?.access_token || res?.session?.access_token;
        const userId = res?.user?.id || res?.id;
        if (accessToken) {
          localStorage.setItem("wp_token", accessToken);
          if (userId) {
            await sb.from("profiles", accessToken).update(
              { business_name: businessName, full_name: fullName },
              `id=eq.${userId}`
            );
          }
          await restoreSession(accessToken);
        } else if (userId && !accessToken) {
          setError("Account created but no session. Email confirmation may still be on. Check Supabase Auth settings. Raw: " + JSON.stringify(res).substring(0, 150));
          setAuthMode("login");
        } else { 
          setError("Signup response: " + JSON.stringify(res).substring(0, 200)); 
        }
      }
    } catch (e) { setError("Connection error: " + e.message); }
    setLoading(false);
  };

  const approveRep = async (id) => {
    await sb.from("profiles", token).update({ role: "rep" }, `id=eq.${id}`);
    await loadAllProfiles();
  };
  const denyRep = async (id) => {
    await sb.from("profiles", token).update({ role: "denied" }, `id=eq.${id}`);
    await loadAllProfiles();
  };

  const createJob = () => {
    const defaultShingle = pricing[1] || pricing[0];
    const defaultRidge = ventPricing.find(v => v.vent_type === "ridge");
    const defaultBox = ventPricing.find(v => v.vent_type === "box_cover");
    setActiveJob({
      rep_id: user.id, customer_name: "", property_address: "", trailer_placement: "",
      job_date: new Date().toISOString().split("T")[0], date_of_loss: null, claim_number: "", insurance_carrier: "",
      rcv:0, acv:0, deductible:0, depreciation:0, squares:0, ridge_length:0,
      turtle_vent_count:0, turtle_vent_total_value:0, gutter_insurance_coverage:0,
      shingle_type: defaultShingle?.shingle_type || "hdz",
      shingle_color:"", shingle_color_custom:"", drip_edge_color:"", drip_edge_color_custom:"",
      sell_price_per_square: 0,
      vent_upgrade_enabled:false, ridge_vent_qty:0, ridge_vent_sell_price: defaultRidge?.default_sell || 10,
      box_cover_qty:0, box_cover_sell_price: defaultBox?.default_sell || 25,
      gutters_enabled:false, gutters_lump_sum:0, signed:false,
    });
    setSigned(false); setShowSig(false);
    setScreen("setup");
  };

  // ─── JobNimbus import: open the picker screen with a fresh job in state ───
  const importFromJobNimbus = () => {
    const defaultShingle = pricing[1] || pricing[0];
    const defaultRidge = ventPricing.find(v => v.vent_type === "ridge");
    const defaultBox = ventPricing.find(v => v.vent_type === "box_cover");
    setActiveJob({
      rep_id: user.id, customer_name: "", property_address: "", trailer_placement: "",
      job_date: new Date().toISOString().split("T")[0], date_of_loss: null, claim_number: "", insurance_carrier: "",
      rcv:0, acv:0, deductible:0, depreciation:0, squares:0, ridge_length:0,
      turtle_vent_count:0, turtle_vent_total_value:0, gutter_insurance_coverage:0,
      shingle_type: defaultShingle?.shingle_type || "hdz",
      shingle_color:"", shingle_color_custom:"", drip_edge_color:"", drip_edge_color_custom:"",
      sell_price_per_square: 0,
      vent_upgrade_enabled:false, ridge_vent_qty:0, ridge_vent_sell_price: defaultRidge?.default_sell || 10,
      box_cover_qty:0, box_cover_sell_price: defaultBox?.default_sell || 25,
      gutters_enabled:false, gutters_lump_sum:0, signed:false,
    });
    setSigned(false); setShowSig(false);
    setJnQuery(""); setJnResults([]); setJnSelectedJob(null); setJnFiles([]); setJnParseResult(null); setJnError("");
    setScreen("jn_import");
  };

  // ─── Convert parser output (parser/parse-scope.js) into the West Peak Pro job shape. ───
  const CARRIER_NAMES = {
    state_farm: "State Farm",
    bear_river: "Bear River Mutual",
    allstate: "Allstate",
    travelers: "Travelers",
    liberty_mutual: "Liberty Mutual",
    usaa: "USAA",
    uig: "United Insurance Group",
    generic_xactimate: "",
    scanned_pdf: "",
  };
  const normalizeName = (n) => {
    if (!n) return "";
    // "LAST, FIRST MIDDLE" → "First Middle Last"; uppercase-only → Title Case
    let s = n.trim();
    if (/^[A-Z][A-Z, ]+$/.test(s) && s.includes(",")) {
      const [last, rest] = s.split(",").map(x => x.trim());
      s = `${rest} ${last}`;
    }
    return s.replace(/\b([A-Z])([A-Z]+)/g, (_,a,b) => a + b.toLowerCase());
  };
  const parseLossDate = (d) => {
    if (!d) return null;
    const m = d.match(/^(\d{1,2})\/(\d{1,2})\/(\d{2,4})$/);
    if (!m) return null;
    const [, mo, da, yr] = m;
    const fullYr = yr.length === 2 ? `20${yr}` : yr;
    return `${fullYr}-${mo.padStart(2,"0")}-${da.padStart(2,"0")}`;
  };
  const applyParseResult = (parsed) => {
    const f = parsed?.fields || {};
    // Westpeak Pro's "ACV (1st Check)" field is the NET ACV paid by the
    // carrier after the deductible. The parser's f.acv is GROSS ACV
    // (= RCV − Depreciation), so subtract the deductible. Prefer the
    // parser's f.net_claim if available (State Farm extracts this directly).
    const netAcv = (f.net_claim != null)
      ? f.net_claim
      : ((f.acv != null && f.deductible != null) ? f.acv - f.deductible : null);
    setActiveJob(prev => {
      const nextRcv = f.rcv ?? prev.rcv;
      const nextSquares = f.squares ?? prev.squares;
      const next = {
        ...prev,
        // jnid links this job to its JobNimbus counterpart — needed for signed-contract auto-upload.
        jnid: jnSelectedJob?.id ?? prev.jnid,
        customer_name: normalizeName(f.customer_name) || prev.customer_name,
        property_address: f.property_address || prev.property_address,
        claim_number: f.claim_number || prev.claim_number,
        insurance_carrier: CARRIER_NAMES[parsed.carrier] || prev.insurance_carrier,
        date_of_loss: parseLossDate(f.date_of_loss) || prev.date_of_loss,
        rcv: nextRcv,
        acv: netAcv ?? prev.acv,
        deductible: f.deductible ?? prev.deductible,
        depreciation: f.depreciation ?? prev.depreciation,
        squares: nextSquares,
        turtle_vent_count: f.turtle_vent_quantity ?? prev.turtle_vent_count,
        turtle_vent_total_value: f.turtle_vent_total_value ?? prev.turtle_vent_total_value,
        gutter_insurance_coverage: f.gutter_rcv_credit ?? prev.gutter_insurance_coverage,
      };
      // Mirror the u() auto-fill rule for the JN-parser path.
      if (!next._sell_price_user_edited && nextRcv > 0 && nextSquares > 0) {
        next.sell_price_per_square = nextRcv / nextSquares;
      }
      return next;
    });
  };

  const saveJobToDb = async (j) => {
    const job = j || activeJob;
    if (!job) return;
    // Strip the embedded rep relation before persisting — it's a read-only join, not a column.
    // Also strip the session-only _sell_price_user_edited flag — not a DB column.
    const { rep, _sell_price_user_edited, ...persistable } = job;
    setLoading(true);
    try {
      if (job.id) {
        await sb.from("jobs", token).update({...persistable, updated_at: new Date().toISOString()}, `id=eq.${job.id}`);
      } else {
        const res = await sb.from("jobs", token).insert(persistable);
        if (res?.[0]?.id) setActiveJob(prev => ({...prev, id: res[0].id}));
      }
      await loadJobs(token, profile);
    } catch (e) { console.error(e); }
    setLoading(false);
  };

  const deleteJob = async (id) => {
    await sb.from("jobs", token).delete(`id=eq.${id}`);
    await loadJobs(token, profile);
  };

  // Tampering guard: once a contract is signed, every Setup/Builder/Margin field
  // is frozen. The `u()` helper is the choke point for all UI edits, so gating
  // here is bulletproof against any single-screen UI bypass.
  const locked = !!activeJob?.signed;
  const u = (key, val) => {
    if (locked) return; // contract is signed — no further field edits
    setActiveJob(prev => {
      const j = {...prev, [key]: val};
      if (key === "vent_upgrade_enabled" && val) {
        if (j.ridge_vent_qty === 0 && j.ridge_length > 0) j.ridge_vent_qty = Math.round(j.ridge_length);
        if (j.box_cover_qty === 0 && j.turtle_vent_count > 0) j.box_cover_qty = j.turtle_vent_count;
      }
      // Manual edit in the Sell Price field stops auto-tracking from insurance/sq.
      if (key === "sell_price_per_square") {
        j._sell_price_user_edited = true;
      }
      // Until the rep has manually entered a sell price, keep Sell Price = RCV / squares
      // (what the document shows). Recompute on any input that changes that ratio.
      if ((key === "rcv" || key === "squares") && !j._sell_price_user_edited && j.rcv > 0 && j.squares > 0) {
        j.sell_price_per_square = j.rcv / j.squares;
      }
      return j;
    });
  };

  const c = calcJob(activeJob, pricing, ventPricing);

  // ════════════════════════════════════════
  // DEBUG
  // ════════════════════════════════════════
  if (screen === "debug") return (
    <div style={appStyle}>{fontLink}
      <div style={{padding:"20px"}}>
        <h2 style={{fontSize:18,fontWeight:700,marginBottom:16,color:T.accent}}>Debug Log</h2>
        <div style={{background:T.bgCard,borderRadius:12,padding:16,border:`1px solid ${T.border}`,marginBottom:16}}>
          {debugLog.length === 0 ? <div style={{color:T.textDim}}>No logs yet</div> : debugLog.map((log, i) => (
            <div key={i} style={{fontSize:12,fontVariantNumeric:"tabular-nums",color:log.includes("ERROR") || log.includes("CATCH") ? T.danger : T.textSec,marginBottom:6,wordBreak:"break-all"}}>{log}</div>
          ))}
        </div>
        {error && <div style={{padding:"12px",borderRadius:10,background:T.dangerBg,border:"1px solid #5c1a1a",fontSize:12,color:T.danger,marginBottom:16,wordBreak:"break-all"}}>{error}</div>}
        <button onClick={()=>{setDebugLog([]);localStorage.removeItem("wp_token");setScreen("auth")}} style={{...btnPrimary,background:T.danger}}>Clear & Back to Login</button>
      </div>
    </div>
  );

  // ════════════════════════════════════════
  // LOADING
  // ════════════════════════════════════════
  if (screen === "loading") return (
    <div style={{...appStyle, display:"flex",alignItems:"center",justifyContent:"center",minHeight:"100vh"}}>{fontLink}
      <div style={{textAlign:"center"}}><div style={{fontSize:24,fontWeight:700,color:T.accent}}>West Peak Pro</div><div style={{color:T.textDim,marginTop:8}}>Loading...</div></div>
    </div>
  );

  // ════════════════════════════════════════
  // AUTH (Login / Signup)
  // ════════════════════════════════════════
  if (screen === "auth") return (
    <div style={appStyle}>{fontLink}
      <div style={{padding:"60px 20px 20px"}}>
        <div style={{textAlign:"center",marginBottom:40}}>
          <div style={{width:56,height:56,borderRadius:16,background:`linear-gradient(135deg,${T.accentDark},${T.accent})`,display:"flex",alignItems:"center",justifyContent:"center",margin:"0 auto 16px",boxShadow:"0 4px 20px rgba(86,81,50,0.15)"}}><IconHome s={28}/></div>
          <div style={{fontSize:11,fontWeight:700,letterSpacing:"0.15em",color:T.accent,textTransform:"uppercase",marginBottom:6}}>West Peak Roofing</div>
          <h1 style={{fontSize:28,fontWeight:700,margin:0,letterSpacing:"-0.03em"}}>West Peak Pro</h1>
        </div>
        <Card>
          <div style={{display:"flex",marginBottom:20,borderRadius:10,overflow:"hidden",border:`1.5px solid ${T.border}`}}>
            <button onClick={()=>{setAuthMode("login");setError("")}} style={{flex:1,padding:"10px",border:"none",background:authMode==="login"?T.accent:"transparent",color:authMode==="login"?"#fff":T.textDim,fontSize:14,fontWeight:600,cursor:"pointer",fontFamily:"inherit"}}>Log In</button>
            <button onClick={()=>{setAuthMode("signup");setError("")}} style={{flex:1,padding:"10px",border:"none",background:authMode==="signup"?T.accent:"transparent",color:authMode==="signup"?"#fff":T.textDim,fontSize:14,fontWeight:600,cursor:"pointer",fontFamily:"inherit"}}>Sign Up</button>
          </div>
          <div style={{display:"flex",flexDirection:"column",gap:12}}>
            {authMode==="signup"&&<div>{fieldLabel("Full Name")}<input value={fullName} onChange={e=>setFullName(e.target.value)} placeholder="Dallin Rowley" style={inputStyle}/></div>}
            {authMode==="signup"&&<div>{fieldLabel("Business Name (for invoices)")}<input value={businessName} onChange={e=>setBusinessName(e.target.value)} placeholder="Altaway Solutions" style={inputStyle}/></div>}
            <div>{fieldLabel("Email")}<input type="email" value={email} onChange={e=>setEmail(e.target.value)} placeholder="you@email.com" style={inputStyle}/></div>
            <div>{fieldLabel("Password")}<input type="password" value={password} onChange={e=>setPassword(e.target.value)} placeholder="••••••••" style={inputStyle} onKeyDown={e=>{if(e.key==="Enter")handleAuth()}}/></div>
            {error&&<div style={{padding:"10px 14px",borderRadius:10,background:T.dangerBg,border:"1px solid #5c1a1a",fontSize:13,color:T.danger,fontWeight:600}}>{error}</div>}
            <button onClick={handleAuth} disabled={loading} style={{...btnPrimary,opacity:loading?0.6:1}}>{loading?"...":(authMode==="login"?"Log In":"Create Account")}</button>
          </div>
        </Card>
      </div>
    </div>
  );

  // ════════════════════════════════════════
  // PENDING APPROVAL
  // ════════════════════════════════════════
  if (screen === "pending") return (
    <div style={appStyle}>{fontLink}
      <div style={{padding:"80px 20px",textAlign:"center"}}>
        <div style={{width:64,height:64,borderRadius:32,background:T.accentLight,display:"flex",alignItems:"center",justifyContent:"center",margin:"0 auto 20px",border:`2px solid ${T.accent}`}}>
          <svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke={T.accent} strokeWidth="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
        </div>
        <h2 style={{fontSize:22,fontWeight:700,marginBottom:8}}>Account Pending</h2>
        <p style={{color:T.textDim,fontSize:14,lineHeight:1.6,maxWidth:300,margin:"0 auto"}}>Your account has been created. An admin needs to approve your access before you can start using West Peak Pro.</p>
        <button onClick={logout} style={{marginTop:24,padding:"12px 24px",border:`1.5px solid ${T.border}`,borderRadius:12,background:"transparent",color:T.textSec,fontSize:14,fontWeight:600,cursor:"pointer",fontFamily:"inherit"}}>Log Out</button>
      </div>
    </div>
  );

  // ════════════════════════════════════════
  // HOME
  // ════════════════════════════════════════
  if (screen === "home") {
    return (
      <div style={appStyle}>{fontLink}
        <div style={{padding:"32px 20px 20px"}}>
          <div style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:24}}>
            <div><div style={{fontSize:11,fontWeight:700,letterSpacing:"0.15em",color:T.accent,textTransform:"uppercase"}}>West Peak Pro</div><div style={{fontSize:18,fontWeight:700,marginTop:2}}>{profile?.full_name||"Welcome"}</div><div style={{fontSize:12,color:T.textDim}}>{profile?.business_name}{isAdmin?" · Admin":""}</div></div>
            <div style={{display:"flex",gap:8}}>
              {isAdmin&&<button onClick={()=>{loadAllProfiles();setScreen("admin")}} style={{width:40,height:40,borderRadius:12,background:T.bgCard,border:`1px solid ${T.border}`,cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"center",color:T.accent}}><IconUsers s={18}/></button>}
              {isAdmin&&<button onClick={()=>setScreen("pricing_admin")} style={{width:40,height:40,borderRadius:12,background:T.bgCard,border:`1px solid ${T.border}`,cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"center",color:T.accent}}><IconSettings s={18}/></button>}
              <button onClick={()=>{setProfilePin(profile?.commission_pin||"");setProfileBname(profile?.business_name||"");setProfileFullName(profile?.full_name||"");setScreen("profile_setup")}} style={{width:40,height:40,borderRadius:12,background:T.bgCard,border:`1px solid ${T.border}`,cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"center",color:T.textSec}}><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg></button>
              <button onClick={logout} style={{width:40,height:40,borderRadius:12,background:T.bgCard,border:`1px solid ${T.border}`,cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"center",color:T.danger}}><IconLogout s={18}/></button>
            </div>
          </div>

          {isAdmin && (() => {
            const stalled = jobs.filter(j => j.signed && j.jnid && !j.jn_uploaded_at);
            if (stalled.length === 0) return null;
            return (
              <div style={{marginBottom:14,padding:"10px 14px",borderRadius:10,background:T.dangerBg,border:`1px solid ${T.danger}`,display:"flex",alignItems:"flex-start",gap:8,fontSize:13,color:T.danger}}>
                <IconAlert s={16}/>
                <div style={{flex:1}}>
                  <div style={{fontWeight:700,marginBottom:2}}>{stalled.length} contract{stalled.length===1?"":"s"} not uploaded to JobNimbus</div>
                  <div style={{fontSize:12,opacity:0.85}}>Signed but missing from JN Documents tab: {stalled.slice(0,3).map(j=>j.customer_name||"Untitled").join(", ")}{stalled.length>3?` + ${stalled.length-3} more`:""}</div>
                </div>
              </div>
            );
          })()}
          <button onClick={createJob} style={{...btnPrimary,display:"flex",alignItems:"center",justifyContent:"center",gap:8}}><IconPlus s={20}/> New Job</button>
          <button onClick={importFromJobNimbus} style={{width:"100%",padding:"14px",border:`1.5px solid ${T.border}`,borderRadius:14,background:T.bgCard,color:T.accent,fontSize:15,fontWeight:600,cursor:"pointer",fontFamily:"inherit",marginTop:10,display:"flex",alignItems:"center",justifyContent:"center",gap:8}}>Import from JobNimbus</button>

          {jobs.length > 0 && (
            <div style={{marginTop:24}}>
              <div style={{fontSize:12,fontWeight:700,color:T.textDim,textTransform:"uppercase",letterSpacing:"0.1em",marginBottom:12}}>
                {isAdmin?"All Jobs":"My Jobs"} ({jobs.length})
              </div>
              {jobs.map(job => {
                const jc = calcJob(job, pricing, ventPricing);
                return (
                  <div key={job.id} style={{display:"flex",gap:8,marginBottom:10}}>
                    <div onClick={()=>{setActiveJob(job);setSigned(job.signed);setShowSig(false);setScreen("builder")}}
                      style={{flex:1,background:T.bgCard,borderRadius:14,padding:"14px 16px",border:`1px solid ${T.border}`,cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"space-between"}}>
                      <div>
                        <div style={{fontWeight:700,fontSize:15}}>{job.customer_name||"Untitled"}</div>
                        <div style={{fontSize:12,color:T.textDim,marginTop:2}}>
                          {job.job_date} · {fmt(jc.totalJobValue)}
                          {isAdmin && job.rep?.full_name && <span style={{color:T.accent,fontWeight:600}}> · {repShortName(job.rep.full_name)}</span>}
                        </div>
                      </div>
                      <div style={{display:"flex",alignItems:"center",gap:4}}>
                        {job.signed&&<span style={{color:T.accent}}><IconCheck s={14}/></span>}
                        <IconRight/>
                      </div>
                    </div>
                    <button onClick={()=>{setActiveJob(job);setShowPin(true)}} style={{width:44,background:T.bgCard,border:`1px solid ${T.border}`,borderRadius:14,cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"center",color:T.accent}}><IconLock s={14}/></button>
                    <button onClick={()=>deleteJob(job.id)} style={{width:44,background:T.bgCard,border:`1px solid ${T.border}`,borderRadius:14,cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"center",color:T.danger}}><IconTrash s={14}/></button>
                  </div>
                );
              })}
            </div>
          )}
          <div style={{marginTop:32,textAlign:"center",display:"flex",alignItems:"center",justifyContent:"center",gap:6,color:T.textDim,fontSize:12}}><IconLock/> Data secured by Supabase</div>
        </div>
        {showPin&&<PinModal onSuccess={{pin:profile?.commission_pin||"0000",fn:()=>{setShowPin(false);setScreen("commission")}}} onCancel={()=>setShowPin(false)}/>}
      </div>
    );
  }

  // ════════════════════════════════════════
  // ADMIN: User Management
  // ════════════════════════════════════════
  if (screen === "admin") {
    const pending = allProfiles.filter(p => p.role === "pending");
    const active = allProfiles.filter(p => p.role === "rep" || p.role === "admin");
    const denied = allProfiles.filter(p => p.role === "denied");
    return (
      <div style={appStyle}>{fontLink}
        <NavBar title="Team Management" onBack={()=>setScreen("home")}/>
        <div style={{padding:20}}>
          {pending.length > 0 && (
            <Card>
              <SectionLabel icon={<IconAlert s={14}/>}>Pending Approval ({pending.length})</SectionLabel>
              {pending.map(p => (
                <div key={p.id} style={{padding:"12px 0",borderBottom:`1px solid ${T.border}`,display:"flex",justifyContent:"space-between",alignItems:"center"}}>
                  <div><div style={{fontWeight:700,fontSize:14}}>{p.full_name||p.email}</div><div style={{fontSize:12,color:T.textDim}}>{p.email}{p.business_name?` · ${p.business_name}`:""}</div></div>
                  <div style={{display:"flex",gap:6}}>
                    <button onClick={()=>approveRep(p.id)} style={{padding:"6px 14px",border:"none",borderRadius:8,background:T.accent,color:"#fff",fontSize:12,fontWeight:700,cursor:"pointer",fontFamily:"inherit"}}>Approve</button>
                    <button onClick={()=>denyRep(p.id)} style={{padding:"6px 14px",border:`1px solid ${T.danger}`,borderRadius:8,background:"transparent",color:T.danger,fontSize:12,fontWeight:700,cursor:"pointer",fontFamily:"inherit"}}>Deny</button>
                  </div>
                </div>
              ))}
            </Card>
          )}
          <Card>
            <SectionLabel icon={<IconUsers s={14}/>}>Active Team ({active.length})</SectionLabel>
            {active.map(p => {
              const repJobs = jobs.filter(j => j.rep_id === p.id);
              const totalVal = repJobs.reduce((sum, j) => sum + (calcJob(j, pricing, ventPricing).totalJobValue || 0), 0);
              return (
                <div key={p.id} style={{padding:"12px 0",borderBottom:`1px solid ${T.border}`}}>
                  <div style={{display:"flex",justifyContent:"space-between",alignItems:"center"}}>
                    <div><div style={{fontWeight:700,fontSize:14}}>{p.full_name||p.email} {p.role==="admin"&&<span style={{fontSize:10,color:T.accent,fontWeight:700}}>ADMIN</span>}</div><div style={{fontSize:12,color:T.textDim}}>{p.business_name||p.email}</div></div>
                    <div style={{textAlign:"right"}}><div style={{fontSize:14,fontWeight:700,fontVariantNumeric:"tabular-nums"}}>{repJobs.length} jobs</div><div style={{fontSize:12,color:T.textDim}}>{fmt(totalVal)} total</div></div>
                  </div>
                </div>
              );
            })}
          </Card>
          {denied.length > 0 && (
            <Card>
              <SectionLabel>Denied ({denied.length})</SectionLabel>
              {denied.map(p => (
                <div key={p.id} style={{padding:"8px 0",display:"flex",justifyContent:"space-between",alignItems:"center"}}>
                  <div style={{fontSize:13,color:T.textDim}}>{p.full_name||p.email}</div>
                  <button onClick={()=>approveRep(p.id)} style={{padding:"4px 12px",border:`1px solid ${T.border}`,borderRadius:6,background:"transparent",color:T.textSec,fontSize:11,cursor:"pointer",fontFamily:"inherit"}}>Re-approve</button>
                </div>
              ))}
            </Card>
          )}
        </div>
      </div>
    );
  }

  // ════════════════════════════════════════
  // ADMIN: Pricing Management
  // ════════════════════════════════════════
  if (screen === "pricing_admin") {
    const updatePricing = async (type, field, val) => {
      await sb.from("pricing", token).update({ [field]: val, updated_at: new Date().toISOString() }, `shingle_type=eq.${type}`);
      await loadPricing(token);
    };
    const updateVentPricing = async (type, field, val) => {
      await sb.from("vent_pricing", token).update({ [field]: val, updated_at: new Date().toISOString() }, `vent_type=eq.${type}`);
      await loadPricing(token);
    };
    return (
      <div style={appStyle}>{fontLink}
        <NavBar title="Pricing Control" onBack={()=>setScreen("home")}/>
        <div style={{padding:20}}>
          <Card>
            <SectionLabel icon={<IconSettings s={14}/>}>Shingle Pricing (Redline)</SectionLabel>
            {pricing.map(p => (
              <div key={p.shingle_type} style={{padding:"12px 0",borderBottom:`1px solid ${T.border}`}}>
                <div style={{fontWeight:700,fontSize:14,marginBottom:8}}>{p.display_name}</div>
                <div style={{display:"flex",gap:12}}>
                  <NumInput label="Cost" value={p.cost} onChange={v=>updatePricing(p.shingle_type,"cost",v)} small/>
                  <NumInput label="Suggested Sell" value={p.suggested_sell} onChange={v=>updatePricing(p.shingle_type,"suggested_sell",v)} small/>
                </div>
              </div>
            ))}
          </Card>
          <Card>
            <SectionLabel>Ventilation Pricing</SectionLabel>
            {ventPricing.map(v => (
              <div key={v.vent_type} style={{padding:"12px 0",borderBottom:`1px solid ${T.border}`}}>
                <div style={{fontWeight:700,fontSize:14,marginBottom:8}}>{v.display_name}</div>
                <div style={{display:"flex",gap:12}}>
                  <NumInput label="Cost" value={v.cost} onChange={val=>updateVentPricing(v.vent_type,"cost",val)} small/>
                  <NumInput label="Default Sell" value={v.default_sell} onChange={val=>updateVentPricing(v.vent_type,"default_sell",val)} small/>
                </div>
              </div>
            ))}
          </Card>
        </div>
      </div>
    );
  }

  // ════════════════════════════════════════
  // PROFILE SETUP (first-time PIN & business name)
  // ════════════════════════════════════════
  if (screen === "profile_setup") {
    const saveProfile = async () => {
      await sb.from("profiles", token).update({ commission_pin: profilePin, business_name: profileBname, full_name: profileFullName }, `id=eq.${user.id}`);
      setProfile(prev => ({...prev, commission_pin: profilePin, business_name: profileBname, full_name: profileFullName}));
      setScreen("home");
    };
    return (
      <div style={appStyle}>{fontLink}
        <NavBar title="My Profile" onBack={()=>setScreen("home")}/>
        <div style={{padding:20}}>
          <Card>
            <SectionLabel>Your Settings</SectionLabel>
            <div style={{display:"flex",flexDirection:"column",gap:12}}>
              <div>{fieldLabel("Full Name")}<input value={profileFullName} onChange={e=>setProfileFullName(e.target.value)} placeholder="Dallin Rowley" style={inputStyle}/></div>
              <div>{fieldLabel("Business Name (on invoices)")}<input value={profileBname} onChange={e=>setProfileBname(e.target.value)} placeholder="Altaway Solutions" style={inputStyle}/></div>
              <div>{fieldLabel("Commission PIN (4 digits)")}<input type="password" inputMode="numeric" maxLength={4} value={profilePin} onChange={e=>setProfilePin(e.target.value.replace(/\D/g,""))} placeholder="••••" style={{...inputStyle,textAlign:"center",fontSize:24,letterSpacing:8}}/></div>
              <button onClick={saveProfile} style={btnPrimary}>Save</button>
            </div>
          </Card>
          <div style={{textAlign:"center",fontSize:12,color:T.textDim,marginTop:8}}>{profile?.email}</div>
        </div>
      </div>
    );
  }

  // ════════════════════════════════════════
  // SETUP (same as before but with db fields)
  // ════════════════════════════════════════
  // ════════════════════════════════════════
  // JOBNIMBUS IMPORT
  // ════════════════════════════════════════
  if (screen === "jn_import") {
    const handleFileClick = async (file) => {
      setJnParsing(true); setJnError(""); setJnParseResult(null);
      try {
        const r = await fetch("/api/jn/parse-scope?fileId=" + encodeURIComponent(file.id));
        const data = await r.json();
        if (!r.ok) throw new Error(data?.message || "Parse failed");
        setJnParseResult(data);
        applyParseResult(data);
      } catch (e) {
        setJnError("Parser error: " + e.message);
      } finally { setJnParsing(false); }
    };
    return (
      <div style={appStyle}>{fontLink}
        <NavBar title="Import from JobNimbus" onBack={()=>setScreen("home")}/>
        <div style={{padding:20}}>
          {/* Step 1: search */}
          <Card>
            <SectionLabel>1. Find the JobNimbus Job</SectionLabel>
            <input
              value={jnQuery}
              onChange={e=>{setJnQuery(e.target.value);setJnSelectedJob(null);setJnParseResult(null);}}
              placeholder="Customer name, address, or job #"
              style={inputStyle}
              autoFocus
            />
            {jnSearching && <div style={{marginTop:8,fontSize:12,color:T.textDim}}>Searching…</div>}
            {!jnSearching && jnResults.length > 0 && !jnSelectedJob && (
              <div style={{marginTop:12,maxHeight:280,overflowY:"auto",border:`1px solid ${T.border}`,borderRadius:10}}>
                {jnResults.map(j => (
                  <div key={j.id} onClick={()=>setJnSelectedJob(j)}
                       style={{padding:"10px 12px",borderBottom:`1px solid ${T.border}`,cursor:"pointer"}}>
                    <div style={{fontWeight:600,fontSize:14}}>{j.name}</div>
                    <div style={{fontSize:12,color:T.textDim,marginTop:2}}>{j.address || "no address"} {j.status?` · ${j.status}`:""}</div>
                  </div>
                ))}
              </div>
            )}
            {!jnSearching && jnQuery.length >= 2 && jnResults.length === 0 && !jnSelectedJob && (
              <div style={{marginTop:8,fontSize:13,color:T.textDim}}>No matches.</div>
            )}
            {jnSelectedJob && (
              <div style={{marginTop:12,padding:"10px 12px",background:T.accentLight,borderRadius:10,display:"flex",justifyContent:"space-between",alignItems:"center"}}>
                <div>
                  <div style={{fontWeight:600,fontSize:14}}>{jnSelectedJob.name}</div>
                  <div style={{fontSize:12,color:T.textDim}}>{jnSelectedJob.address}</div>
                </div>
                <button onClick={()=>{setJnSelectedJob(null);setJnParseResult(null);}} style={{background:"none",border:"none",cursor:"pointer",color:T.accent,fontSize:12}}>Change</button>
              </div>
            )}
          </Card>

          {/* Step 2: pick scope PDF */}
          {jnSelectedJob && (
            <Card>
              <SectionLabel>2. Pick the Scope-of-Work PDF</SectionLabel>
              {jnFilesLoading && <div style={{fontSize:12,color:T.textDim}}>Loading files…</div>}
              {!jnFilesLoading && jnFiles.length === 0 && <div style={{fontSize:13,color:T.textDim}}>No PDFs on this job.</div>}
              {!jnFilesLoading && jnFiles.length > 0 && (
                <div style={{maxHeight:300,overflowY:"auto",border:`1px solid ${T.border}`,borderRadius:10}}>
                  {jnFiles.map(f => (
                    <div key={f.id} onClick={()=>handleFileClick(f)}
                         style={{padding:"10px 12px",borderBottom:`1px solid ${T.border}`,cursor:"pointer"}}>
                      <div style={{fontSize:13,fontWeight:500,wordBreak:"break-word"}}>{f.filename}</div>
                      {f.uploadedAt && <div style={{fontSize:11,color:T.textDim,marginTop:2}}>{new Date(f.uploadedAt).toLocaleDateString()}</div>}
                    </div>
                  ))}
                </div>
              )}
              {jnParsing && <div style={{marginTop:12,fontSize:13,color:T.accent}}>Parsing PDF…</div>}
            </Card>
          )}

          {/* Step 3: parse result */}
          {jnError && (
            <Card style={{border:`1.5px solid ${T.danger}`}}>
              <div style={{color:T.danger,fontSize:13}}>{jnError}</div>
            </Card>
          )}
          {jnParseResult && (
            <Card>
              <SectionLabel icon={<IconCheck s={14}/>}>3. Parsed Values <span style={{fontSize:11,color:jnParseResult.confidence==="high"?T.accent:T.danger,marginLeft:8}}>confidence: {jnParseResult.confidence}</span></SectionLabel>
              {(jnParseResult.notes||[]).length > 0 && (
                <div style={{marginBottom:12,padding:"8px 10px",background:T.bgInput,borderRadius:8,fontSize:11,color:T.textDim}}>
                  {jnParseResult.notes.join(" · ")}
                </div>
              )}
              <div style={{display:"grid",gridTemplateColumns:"1fr 1fr",gap:8,fontSize:13}}>
                <div><div style={{fontSize:11,color:T.textDim}}>Carrier</div><div>{CARRIER_NAMES[jnParseResult.carrier] || jnParseResult.carrier}</div></div>
                <div><div style={{fontSize:11,color:T.textDim}}>Customer</div><div>{normalizeName(jnParseResult.fields?.customer_name) || "—"}</div></div>
                <div><div style={{fontSize:11,color:T.textDim}}>Claim #</div><div>{jnParseResult.fields?.claim_number || "—"}</div></div>
                <div><div style={{fontSize:11,color:T.textDim}}>Date of Loss</div><div>{jnParseResult.fields?.date_of_loss || "—"}</div></div>
                <div><div style={{fontSize:11,color:T.textDim}}>RCV</div><div>{jnParseResult.fields?.rcv != null ? fmt(jnParseResult.fields.rcv) : "—"}</div></div>
                <div><div style={{fontSize:11,color:T.textDim}}>Depreciation</div><div>{jnParseResult.fields?.depreciation != null ? fmt(jnParseResult.fields.depreciation) : "—"}</div></div>
                <div><div style={{fontSize:11,color:T.textDim}}>ACV (1st check)</div><div>{(() => {
                  const f = jnParseResult.fields || {};
                  const net = f.net_claim != null ? f.net_claim : (f.acv != null && f.deductible != null ? f.acv - f.deductible : null);
                  return net != null ? fmt(net) : "—";
                })()}</div></div>
                <div><div style={{fontSize:11,color:T.textDim}}>Deductible</div><div>{jnParseResult.fields?.deductible != null ? fmt(jnParseResult.fields.deductible) : "—"}</div></div>
                <div><div style={{fontSize:11,color:T.textDim}}>Squares</div><div>{jnParseResult.fields?.squares ?? "—"}</div></div>
                <div><div style={{fontSize:11,color:T.textDim}}>Turtle vents</div><div>{jnParseResult.fields?.turtle_vent_quantity ? `${jnParseResult.fields.turtle_vent_quantity} EA · ${fmt(jnParseResult.fields.turtle_vent_total_value)}` : "—"}</div></div>
              </div>
              <button onClick={()=>setScreen("setup")} style={{...btnPrimary,marginTop:16}}>Continue to Setup (verify) →</button>
            </Card>
          )}
        </div>
      </div>
    );
  }

  if (screen === "setup") {
    const lockedInputStyle = locked ? {...inputStyle, opacity: 0.55, cursor: "not-allowed", background: "#f5f0e6"} : inputStyle;
    const textInput = (val, onChange, ph) => <input value={val||""} onChange={e=>onChange(e.target.value)} placeholder={ph} disabled={locked} style={lockedInputStyle}/>;
    const dateInput = (val, onChange) => <input type="date" value={val||""} onChange={e=>onChange(e.target.value)} disabled={locked} style={lockedInputStyle}/>;
    return (
      <div style={appStyle}>{fontLink}
        <NavBar title="Job Setup" onBack={()=>setScreen("home")}/>
        <div style={{padding:20}}>
          {locked && <div style={{marginBottom:14,padding:"10px 14px",borderRadius:10,background:T.accentLight,border:`1px solid ${T.accent}`,display:"flex",alignItems:"center",gap:8,fontSize:13,color:T.text}}><IconLock s={14}/><span><strong>Contract signed — fields locked.</strong> No changes can be made after signing.</span></div>}
          <Card>
            <SectionLabel>Customer Info</SectionLabel>
            <div style={{display:"flex",flexDirection:"column",gap:12}}>
              <div>{fieldLabel("Customer Name")}{textInput(activeJob.customer_name,v=>u("customer_name",v),"Nicole Carpenter")}</div>
              <div>{fieldLabel("Property Address")}{textInput(activeJob.property_address,v=>u("property_address",v),"940 Willowmere Dr, Kaysville UT")}</div>
              <div style={{display:"flex",gap:12}}><div style={{flex:1}}>{fieldLabel("Job Date")}{dateInput(activeJob.job_date,v=>u("job_date",v))}</div><div style={{flex:1}}>{fieldLabel("Date of Loss")}{dateInput(activeJob.date_of_loss,v=>u("date_of_loss",v))}</div></div>
              <div style={{display:"flex",gap:12}}><div style={{flex:1}}>{fieldLabel("Claim #")}{textInput(activeJob.claim_number,v=>u("claim_number",v),"4495X780M")}</div><div style={{flex:1}}>{fieldLabel("Carrier")}{textInput(activeJob.insurance_carrier,v=>u("insurance_carrier",v),"State Farm")}</div></div>
              <div>{fieldLabel("Trailer Placement")}{textInput(activeJob.trailer_placement,v=>u("trailer_placement",v),"Driveway, RV area, etc.")}</div>
            </div>
          </Card>
          <Card>
            <SectionLabel icon={<IconShield s={14}/>}>Scope of Work</SectionLabel>
            <div style={{display:"flex",flexWrap:"wrap",gap:12}}><NumInput label="RCV (Total Job)" value={activeJob.rcv} onChange={v=>u("rcv",v)} disabled={locked}/><NumInput label="Deductible" value={activeJob.deductible} onChange={v=>u("deductible",v)} disabled={locked}/></div>
            <div style={{display:"flex",flexWrap:"wrap",gap:12,marginTop:12}}><NumInput label="ACV (1st Check)" value={activeJob.acv} onChange={v=>u("acv",v)} disabled={locked}/><NumInput label="Depreciation" value={activeJob.depreciation} onChange={v=>u("depreciation",v)} disabled={locked}/></div>
            <div style={{display:"flex",flexWrap:"wrap",gap:12,marginTop:12}}><NumInput label="# Squares" value={activeJob.squares} onChange={v=>u("squares",v)} prefix="" placeholder="46.15" disabled={locked}/><NumInput label="Ridge Length (LF)" value={activeJob.ridge_length} onChange={v=>u("ridge_length",v)} prefix="" placeholder="122" disabled={locked}/></div>
            <div style={{display:"flex",flexWrap:"wrap",gap:12,marginTop:12}}><NumInput label="Turtle Vent Count" value={activeJob.turtle_vent_count} onChange={v=>u("turtle_vent_count",v)} prefix="" placeholder="11" disabled={locked}/><NumInput label="Turtle Vent Total $" value={activeJob.turtle_vent_total_value} onChange={v=>u("turtle_vent_total_value",v)} placeholder="861.11" disabled={locked}/></div>
            <div style={{marginTop:12}}><NumInput label="Gutter Insurance Coverage" value={activeJob.gutter_insurance_coverage} onChange={v=>u("gutter_insurance_coverage",v)} placeholder="1266.22" disabled={locked}/></div>
            {activeJob.squares>0&&activeJob.rcv>0&&(
              <div style={{marginTop:16,padding:"12px 14px",borderRadius:12,background:c.insurancePerSq<REDLINE_FLOOR?T.dangerBg:T.accentLight,border:`1px solid ${c.insurancePerSq<REDLINE_FLOOR?T.danger:T.border}`,display:"flex",alignItems:"center",gap:10}}>
                {c.insurancePerSq<REDLINE_FLOOR?<span style={{color:T.danger}}><IconAlert s={18}/></span>:<span style={{color:T.accent}}><IconCheck s={18}/></span>}
                <div><div style={{fontSize:14,fontWeight:700,color:c.insurancePerSq<REDLINE_FLOOR?T.danger:T.accent}}>Insurance: {fmt(c.insurancePerSq)}/sq</div></div>
              </div>
            )}
          </Card>
          <button onClick={()=>{saveJobToDb().then(()=>setScreen("builder"))}} disabled={!activeJob.customer_name} style={{...btnPrimary,opacity:activeJob.customer_name?1:0.4}}>Continue to Builder →</button>
        </div>
      </div>
    );
  }

  // ════════════════════════════════════════
  // BUILDER
  // ════════════════════════════════════════
  if (screen === "builder") {
    const selectStyle = {width:"100%",border:`1.5px solid ${T.border}`,borderRadius:10,padding:"10px 12px",fontSize:14,fontFamily:"inherit",background:locked?"#f5f0e6":T.bgInput,color:T.text,outline:"none",opacity:locked?0.55:1,cursor:locked?"not-allowed":"text"};
    return (
      <div style={appStyle}>{fontLink}
        <NavBar title={activeJob.customer_name} onBack={()=>{saveJobToDb().then(()=>setScreen("setup"))}}
          rightAction={<button onClick={()=>{saveJobToDb().then(()=>setScreen("summary"))}} style={{background:"none",border:"none",cursor:"pointer",color:T.accent,fontSize:14,fontWeight:600,fontFamily:"inherit",padding:0}}>Contract →</button>}/>
        <div style={{padding:20}}>
          {locked && <div style={{marginBottom:14,padding:"10px 14px",borderRadius:10,background:T.accentLight,border:`1px solid ${T.accent}`,display:"flex",alignItems:"center",gap:8,fontSize:13,color:T.text}}><IconLock s={14}/><span><strong>Contract signed — fields locked.</strong> No changes can be made after signing.</span></div>}
          {/* Insurance Coverage */}
          {activeJob.squares>0&&activeJob.rcv>0&&(
            <Card style={{border:`1.5px solid ${c.insuranceFullyCovered?T.accentDark:T.danger}`}}>
              <SectionLabel icon={<IconShield s={14}/>}>Insurance Coverage</SectionLabel>
              <div style={{display:"flex",justifyContent:"space-between",alignItems:"baseline",marginBottom:12}}><span style={{fontSize:13,color:T.textDim}}>Insurance pays</span><span style={{fontSize:22,fontWeight:700,fontVariantNumeric:"tabular-nums"}}>{fmt(c.insurancePerSq)}<span style={{fontSize:13,fontWeight:500,color:T.textDim}}>/sq</span></span></div>
              <div style={{height:8,borderRadius:4,background:T.border,overflow:"hidden",marginBottom:4}}><div style={{height:"100%",borderRadius:4,transition:"width 0.5s",width:`${Math.min(100,(c.insurancePerSq/Math.max(activeJob.sell_price_per_square,REDLINE_FLOOR))*100)}%`,background:c.insuranceFullyCovered?T.accent:c.insurancePerSq>=REDLINE_FLOOR?T.warn:T.danger}}/></div>
              <div style={{display:"flex",justifyContent:"space-between",fontSize:11,color:T.textDim,marginBottom:12}}><span>$0</span><span>Redline $500</span><span>Sell {fmt(activeJob.sell_price_per_square)}</span></div>
              {!c.insuranceFullyCovered&&c.roofGapTotal>0&&<div style={{display:"flex",justifyContent:"space-between",fontSize:14,padding:"8px 0"}}><span style={{color:T.warn,fontWeight:600}}>Homeowner gap ({fmt(c.roofGapPerSq)}/sq)</span><span style={{fontWeight:700,fontVariantNumeric:"tabular-nums",color:T.warn}}>{fmt(c.roofGapTotal)}</span></div>}
              {c.insuranceFullyCovered&&<div style={{display:"flex",alignItems:"center",gap:8,padding:"10px 14px",borderRadius:10,background:T.accentLight}}><span style={{color:T.accent}}><IconCheck s={18}/></span><span style={{fontSize:14,fontWeight:700,color:T.accent}}>Fully covered</span></div>}
            </Card>
          )}

          {/* Shingle */}
          <Card>
            <SectionLabel>Shingle Type & Price</SectionLabel>
            <div style={{display:"grid",gridTemplateColumns:"1fr 1fr",gap:8}}>{pricing.map(s=>(<button key={s.shingle_type} onClick={()=>u("shingle_type",s.shingle_type)} disabled={locked} style={{padding:"14px 12px",borderRadius:12,border:activeJob.shingle_type===s.shingle_type?`2px solid ${T.accent}`:`1.5px solid ${T.border}`,background:activeJob.shingle_type===s.shingle_type?T.accentLight:T.bgCard,cursor:locked?"not-allowed":"pointer",textAlign:"left",fontFamily:"inherit",opacity:locked?0.55:1}}><div style={{fontSize:14,fontWeight:700,color:activeJob.shingle_type===s.shingle_type?T.accent:T.text}}>{s.display_name}</div><div style={{fontSize:12,color:T.textDim,marginTop:2}}>Suggested: {fmt(s.suggested_sell)}/sq</div></button>))}</div>
            <div style={{marginTop:16,display:"flex",gap:12,alignItems:"flex-end"}}><NumInput label="Sell Price / Sq" value={activeJob.sell_price_per_square} onChange={v=>u("sell_price_per_square",v)} disabled={locked}/><div style={{flex:1}}><div style={{fontSize:11,fontWeight:600,color:T.textDim,marginBottom:4,textTransform:"uppercase"}}>Total</div><div style={{height:44,display:"flex",alignItems:"center",fontVariantNumeric:"tabular-nums",fontSize:18,fontWeight:700,color:activeJob.sell_price_per_square>0&&c.shingle?.cost?(activeJob.sell_price_per_square>=c.shingle.cost?T.accent:T.danger):T.textDim}}>{fmt(activeJob.sell_price_per_square*activeJob.squares)}</div></div></div>
          </Card>

          {/* Colors */}
          <Card>
            <SectionLabel>Colors</SectionLabel>
            <div style={{marginBottom:14}}><div style={{fontSize:11,fontWeight:600,color:T.textDim,marginBottom:6,textTransform:"uppercase"}}>Shingle Color</div><select value={activeJob.shingle_color} onChange={e=>u("shingle_color",e.target.value)} disabled={locked} style={selectStyle}><option value="">Select...</option>{SHINGLE_COLORS.map(cl=><option key={cl} value={cl}>{cl}</option>)}</select>{activeJob.shingle_color==="Custom"&&<input value={activeJob.shingle_color_custom||""} onChange={e=>u("shingle_color_custom",e.target.value)} disabled={locked} placeholder="Custom color..." style={{...inputStyle,marginTop:8,opacity:locked?0.55:1}}/>}</div>
            <div><div style={{fontSize:11,fontWeight:600,color:T.textDim,marginBottom:6,textTransform:"uppercase"}}>Drip Edge</div><select value={activeJob.drip_edge_color} onChange={e=>u("drip_edge_color",e.target.value)} disabled={locked} style={selectStyle}><option value="">Select...</option>{DRIP_EDGE_COLORS.map(cl=><option key={cl} value={cl}>{cl}</option>)}</select>{activeJob.drip_edge_color==="Custom"&&<input value={activeJob.drip_edge_color_custom||""} onChange={e=>u("drip_edge_color_custom",e.target.value)} disabled={locked} placeholder="Custom color..." style={{...inputStyle,marginTop:8,opacity:locked?0.55:1}}/>}</div>
          </Card>

          {/* Ventilation */}
          <Card>
            <SectionLabel>Ventilation Upgrade</SectionLabel>
            <Toggle label="Ridge Vent + Box Vent Covers" on={activeJob.vent_upgrade_enabled} onToggle={()=>u("vent_upgrade_enabled",!activeJob.vent_upgrade_enabled)} disabled={locked}/>
            {activeJob.vent_upgrade_enabled&&<div style={{marginTop:8}}>
              <div style={{fontSize:12,fontWeight:600,color:T.textDim,marginBottom:8}}>RIDGE VENT</div>
              <div style={{display:"flex",gap:12,marginBottom:4}}><NumInput label="LF" value={activeJob.ridge_vent_qty} onChange={v=>u("ridge_vent_qty",v)} prefix="" small disabled={locked}/><NumInput label="Sell $/LF" value={activeJob.ridge_vent_sell_price} onChange={v=>u("ridge_vent_sell_price",v)} small disabled={locked}/><div style={{flex:1}}><div style={{fontSize:11,fontWeight:600,color:T.textDim,marginBottom:4,textTransform:"uppercase"}}>Total</div><div style={{fontSize:15,fontWeight:700,fontVariantNumeric:"tabular-nums",height:38,display:"flex",alignItems:"center"}}>{fmt(c.ridgeTotal)}</div></div></div>
              {activeJob.ridge_vent_sell_price>0&&activeJob.ridge_vent_sell_price<c.ridgeCost&&<div style={{marginBottom:10,padding:"8px 12px",borderRadius:8,background:T.dangerBg,border:"1px solid #5c1a1a",display:"flex",alignItems:"center",gap:6}}><span style={{color:T.danger}}><IconAlert s={14}/></span><div style={{fontSize:12,fontWeight:700,color:T.danger}}>Below cost — min {fmt(c.ridgeCost)}/LF</div></div>}
              <div style={{fontSize:12,fontWeight:600,color:T.textDim,marginBottom:8,marginTop:10}}>BOX VENT COVERS</div>
              <div style={{display:"flex",gap:12,marginBottom:4}}><NumInput label="Qty" value={activeJob.box_cover_qty} onChange={v=>u("box_cover_qty",v)} prefix="" small disabled={locked}/><NumInput label="Sell $/ea" value={activeJob.box_cover_sell_price} onChange={v=>u("box_cover_sell_price",v)} small disabled={locked}/><div style={{flex:1}}><div style={{fontSize:11,fontWeight:600,color:T.textDim,marginBottom:4,textTransform:"uppercase"}}>Total</div><div style={{fontSize:15,fontWeight:700,fontVariantNumeric:"tabular-nums",height:38,display:"flex",alignItems:"center"}}>{fmt(c.boxCoverTotal)}</div></div></div>
              {activeJob.box_cover_sell_price>0&&activeJob.box_cover_sell_price<c.boxCoverCost&&<div style={{marginBottom:10,padding:"8px 12px",borderRadius:8,background:T.dangerBg,border:"1px solid #5c1a1a",display:"flex",alignItems:"center",gap:6}}><span style={{color:T.danger}}><IconAlert s={14}/></span><div style={{fontSize:12,fontWeight:700,color:T.danger}}>Below cost — min {fmt(c.boxCoverCost)}/ea</div></div>}
              {activeJob.turtle_vent_total_value>0&&<div style={{padding:"10px 14px",borderRadius:10,background:T.accentLight,border:`1px solid ${T.border}`,marginTop:8}}><div style={{fontSize:12,color:T.accent,fontWeight:600}}>Insurance vent credit: {fmt(c.ventInsuranceCredit)}</div><div style={{fontSize:14,fontWeight:700,color:T.accent,marginTop:4}}>Homeowner upgrade: {fmt(c.ventOOP)}</div></div>}
            </div>}
          </Card>

          {/* Gutters */}
          <Card>
            <SectionLabel>Gutters</SectionLabel>
            <Toggle label="Include Gutters" on={activeJob.gutters_enabled} onToggle={()=>u("gutters_enabled",!activeJob.gutters_enabled)} disabled={locked}/>
            {activeJob.gutters_enabled&&<div style={{marginTop:8}}><NumInput label="Gutter Sell Price" value={activeJob.gutters_lump_sum} onChange={v=>u("gutters_lump_sum",v)} disabled={locked}/>{activeJob.gutter_insurance_coverage>0&&<div style={{padding:"10px 14px",borderRadius:10,background:T.accentLight,border:`1px solid ${T.border}`,marginTop:12}}><div style={{fontSize:12,color:T.accent,fontWeight:600}}>Insurance credit: {fmt(c.gutterInsCredit)}</div><div style={{fontSize:14,fontWeight:700,color:T.accent,marginTop:4}}>Homeowner: {fmt(c.gutterOOP)}</div></div>}</div>}
          </Card>

          <OOPBox c={c} activeJob={activeJob}/>
          <button onClick={()=>{saveJobToDb().then(()=>setScreen("summary"))}} style={{...btnPrimary,marginTop:20}}>View Contract</button>
        </div>
      </div>
    );
  }

  // ════════════════════════════════════════
  // SUMMARY / CONTRACT
  // ════════════════════════════════════════
  if (screen === "summary") {
    const docId = (activeJob.id||"").substring(0,36).toUpperCase();
    const violations = [];
    if (activeJob.sell_price_per_square < c.shingle?.cost) violations.push(`${c.shingle?.name} sell (${fmt(activeJob.sell_price_per_square)}) below cost (${fmt(c.shingle?.cost)})`);
    if (activeJob.vent_upgrade_enabled && activeJob.ridge_vent_sell_price < c.ridgeCost) violations.push(`Ridge vent (${fmt(activeJob.ridge_vent_sell_price)}/LF) below cost (${fmt(c.ridgeCost)}/LF)`);
    if (activeJob.vent_upgrade_enabled && activeJob.box_cover_sell_price < c.boxCoverCost) violations.push(`Box cover (${fmt(activeJob.box_cover_sell_price)}/ea) below cost (${fmt(c.boxCoverCost)}/ea)`);
    const hasViolations = violations.length > 0;

    // mode: "print" (default — open window + print) or "html" (return string for upload)
    // sigOverride: signature data URL to embed if the React state hasn't updated yet (e.g. inside onSign)
    const generatePDF = (mode = "print", sigOverride = null) => {
      const today = new Date().toLocaleDateString("en-US",{year:"numeric",month:"long",day:"numeric"});
      const signedAt = activeJob.signed_at || (sigOverride ? new Date().toISOString() : null);
      const signedDate = signedAt ? new Date(signedAt).toLocaleDateString("en-US",{year:"numeric",month:"long",day:"numeric"}) : "";
      const homeownerSig = sigOverride || activeJob.homeowner_signature;
      const sigImg = homeownerSig ? `<img src="${homeownerSig}" style="height:34px;display:block;" />` : "";
      const ventChecked = activeJob.vent_upgrade_enabled ? "☑" : "☐";
      const ventUnchecked = activeJob.vent_upgrade_enabled ? "☐" : "☑";
      const gutChecked = activeJob.gutters_enabled ? "☑" : "☐";
      const gutUnchecked = activeJob.gutters_enabled ? "☐" : "☑";
      const safeName = (activeJob.customer_name || "job").replace(/[^a-z0-9]+/gi, "_");
      const docTitle = `WestPeak_Contract_${safeName}_${activeJob.id}`;
      const html = `<!DOCTYPE html>
<html><head><meta charset="utf-8">
<title>${docTitle}</title>
<style>
.wpc *{margin:0;padding:0;box-sizing:border-box}
.wpc{font-family:'Helvetica Neue',Arial,sans-serif;color:#111;font-size:12px;line-height:1.55;background:#fff;padding:0.5in;width:8.5in}
.wpc h1{font-size:20px;text-align:center;margin-bottom:6px;font-weight:700}
.wpc .brand{text-align:center;font-size:11px;color:#666;margin-bottom:18px;letter-spacing:0.05em}
.wpc h2{font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:0.08em;border-bottom:1.5px solid #111;padding-bottom:3px;margin:20px 0 8px}
.wpc table{width:100%;border-collapse:collapse;margin:6px 0 12px}
.wpc .kv td{padding:3px 0;font-size:12px;vertical-align:top}
.wpc .kv td:first-child{width:38%;color:#444;font-weight:600}
.wpc table.items th,.wpc table.items td{padding:6px 8px;border-bottom:1px solid #ddd;font-size:12px;text-align:left}
.wpc table.items th{font-weight:700;text-transform:uppercase;font-size:10px;color:#444;border-bottom:1.5px solid #111;letter-spacing:0.05em}
.wpc table.items td:last-child,.wpc table.items th:last-child{text-align:right}
.wpc table.items tr.total td{border-bottom:1.5px solid #111;font-weight:700;font-size:13px;padding-top:8px}
.wpc table.items tr.credit td{color:#666;font-size:11px}
.wpc .acks{margin:6px 0 8px;list-style:disc}
.wpc .acks li{margin:3px 0 3px 18px;font-size:12px}
.wpc .note{font-size:11px;color:#555;margin:6px 0}
.wpc .sig-row{display:flex;gap:24px;margin-top:18px}
.wpc .sig-block{flex:1}
.wpc .sig-line{border-bottom:1px solid #111;height:36px;margin-bottom:4px;display:flex;align-items:flex-end;padding-bottom:2px;font-size:12px}
.wpc .sig-label{font-size:10px;color:#444;text-transform:uppercase;letter-spacing:0.05em;font-weight:700}
.wpc .page-break{page-break-before:always}
.wpc .footer{margin-top:24px;padding-top:10px;border-top:1px solid #ddd;font-size:9px;color:#999;text-align:center}
.wpc .bold{font-weight:700}
.wpc .upgrade-line{margin:4px 0;font-size:12px}
</style>
<div class="wpc">
<h1>Color, Material &amp; Upgrade Agreement</h1>
<div class="brand">WEST PEAK ROOFING</div>

<h2>Project Information</h2>
<table class="kv">
  <tr><td>Date</td><td>${activeJob.job_date || today}</td></tr>
  <tr><td>Property Address</td><td>${activeJob.property_address || ""}</td></tr>
  <tr><td>Homeowner(s)</td><td>${activeJob.customer_name || ""}</td></tr>
  <tr><td>Insurance Carrier</td><td>${activeJob.insurance_carrier || ""}</td></tr>
  <tr><td>Claim Number</td><td>${activeJob.claim_number || ""}</td></tr>
</table>

<h2>How Your Roof Gets Paid For</h2>
<p class="note">Your insurance pays in two checks. The <span class="bold">1st Check</span> comes up front — it's the cash value of your old roof, minus your deductible. The <span class="bold">2nd Check</span> is released after the work is complete and covers the depreciation that was held back. <span class="bold">Your Deductible</span> is the amount your policy requires you to pay; it's set by your insurer, not by West Peak.</p>

<h2>Insurance Summary</h2>
<table class="items">
  <tr><td>Insurance Value</td><td>${fmt(activeJob.rcv)}</td></tr>
  <tr class="credit"><td>2nd Check (After Completion)</td><td>(${fmt(activeJob.depreciation)})</td></tr>
  <tr class="credit"><td>Your Deductible</td><td>(${fmt(activeJob.deductible)})</td></tr>
  <tr class="total"><td>1st Check</td><td>${fmt(activeJob.acv)}</td></tr>
</table>

<h2>Shingle Selection</h2>
<table class="kv">
  <tr><td>Shingle Type</td><td>${c.shingle?.name || ""}</td></tr>
  <tr><td>Shingle Color</td><td>${c.displayShingleColor || ""}</td></tr>
  <tr><td>Drip Edge</td><td>${c.displayDripEdge || ""}</td></tr>
  <tr><td>Squares</td><td>${activeJob.squares}</td></tr>
</table>

<h2>Ventilation</h2>
<p class="note"><span class="bold">Insurance Covered (Base Scope):</span> Like-kind replacement of existing ventilation (box / turtle / static vents) included if approved.</p>
<p class="upgrade-line"><span class="bold">Upgrade Option (Not Insurance Covered):</span> &nbsp;&nbsp; ${ventUnchecked} No Upgrade &nbsp;&nbsp; ${ventChecked} Vented Ridge Cap System</p>
${activeJob.vent_upgrade_enabled ? `
<table class="items">
  <tr><th>Item</th><th>Qty / LF</th><th>Rate</th><th>Total</th></tr>
  <tr><td>Ridge Vent</td><td>${activeJob.ridge_vent_qty} LF</td><td>${fmt(activeJob.ridge_vent_sell_price)}/LF</td><td>${fmt(c.ridgeTotal)}</td></tr>
  <tr><td>Turtle Vent Covers</td><td>${activeJob.box_cover_qty} EA</td><td>${fmt(activeJob.box_cover_sell_price)}/ea</td><td>${fmt(c.boxCoverTotal)}</td></tr>
  <tr class="total"><td colspan="3">Ventilation Upgrade Total</td><td>${fmt(c.ventTotalSell)}</td></tr>
  ${c.ventInsuranceCredit > 0 ? `<tr class="credit"><td colspan="3">Less: Insurance vent credit</td><td>(${fmt(c.ventInsuranceCredit)})</td></tr>
  <tr class="total"><td colspan="3">Net Ventilation OOP</td><td>${fmt(c.ventOOP)}</td></tr>` : ''}
</table>` : ''}

<h2>Gutters (If Required)</h2>
<p class="upgrade-line">${gutUnchecked} Not Required &nbsp;&nbsp; ${gutChecked} Replacement Required</p>
${activeJob.gutters_enabled ? `
<table class="items">
  <tr><td>New Gutter Cost</td><td>${fmt(activeJob.gutters_lump_sum)}</td></tr>
  ${c.gutterInsCredit > 0 ? `<tr class="credit"><td>Less: Insurance D&amp;R Credit</td><td>(${fmt(c.gutterInsCredit)})</td></tr>` : ''}
  <tr class="total"><td>Final Gutter Cost (Homeowner Pays)</td><td>${fmt(c.gutterOOP)}</td></tr>
</table>` : ''}

<h2>Total Out of Pocket</h2>
<table class="items">
  <tr><td>Insurance Deductible</td><td>${fmt(activeJob.deductible)}</td></tr>
  ${c.roofGapTotal > 0 && !c.insuranceFullyCovered ? `<tr><td>Roof Cost Gap</td><td>${fmt(c.roofGapTotal)}</td></tr>` : ''}
  ${activeJob.vent_upgrade_enabled ? `<tr><td>Ventilation Upgrade (net)</td><td>${fmt(c.ventOOP)}</td></tr>` : ''}
  ${activeJob.gutters_enabled ? `<tr><td>Gutter Upgrade (net)</td><td>${fmt(c.gutterOOP)}</td></tr>` : ''}
  <tr class="total"><td>TOTAL HOMEOWNER RESPONSIBILITY</td><td>${fmt(c.homeownerOOP)}</td></tr>
</table>

<h2>Payment Responsibility</h2>
<p class="note">Homeowner acknowledges:</p>
<ul class="acks">
  <li>Ventilation upgrades are <span class="bold">NOT</span> covered by insurance</li>
  <li>Gutter costs beyond the insurance allowance are <span class="bold">NOT</span> covered</li>
  <li>All listed additional costs are the homeowner's responsibility</li>
</ul>

<h2>Authorization</h2>
<p class="note">Homeowner approves all material selections and authorizes West Peak Roofing to proceed with installation and material ordering based on this agreement. All selections and costs are final. No verbal agreements apply.</p>

<h2>Signatures</h2>
<div class="sig-row">
  <div class="sig-block"><div class="sig-line">${sigImg}</div><div class="sig-label">Homeowner Signature</div></div>
  <div class="sig-block"><div class="sig-line" style="font-family:'Brush Script MT',cursive;font-size:18px;color:#111">${profile?.full_name || ""}</div><div class="sig-label">Sales Representative Signature</div></div>
</div>
<div class="sig-row">
  <div class="sig-block"><div class="sig-line">${signedDate}</div><div class="sig-label">Date</div></div>
  <div class="sig-block"><div class="sig-line">${signedDate}</div><div class="sig-label">Date</div></div>
</div>

<div class="page-break"></div>

<h2>Decking (Plywood) Replacement</h2>
<p class="note">During roof replacement, the condition of the roof decking cannot be fully determined until existing shingles are removed. If damaged or deteriorated decking is discovered during tear-off, the following terms apply:</p>
<ul class="acks">
  <li>The first two (2) sheets of plywood are included at no additional cost</li>
  <li>Any additional sheets beyond the first two will be charged at: <span class="bold">$80.00 per sheet</span> (material and labor included)</li>
</ul>
<p class="note">Homeowner acknowledges:</p>
<ul class="acks">
  <li>Decking condition is not fully visible prior to tear-off</li>
  <li>Replacement may be required to ensure proper installation and code compliance</li>
  <li>Any decking beyond the first two sheets is the homeowner's responsibility</li>
</ul>

<h2>Dump Trailer Placement</h2>
<p class="note">A dump trailer will be required for debris removal and will be placed on the property for the duration of the roofing project.</p>
<p class="note">Homeowner acknowledges:</p>
<ul class="acks">
  <li>The trailer will typically be placed in the driveway unless otherwise specified</li>
  <li>Minor surface markings or wear may occur due to weight and use</li>
  <li>West Peak Roofing is not responsible for pre-existing conditions or minor cosmetic impacts</li>
</ul>
<table class="kv" style="margin-top:8px">
  <tr><td>Preferred Trailer Placement Location</td><td>${activeJob.trailer_placement || "Driveway"}</td></tr>
</table>

<h2>Authorization</h2>
<p class="note">Homeowner acknowledges the above terms for decking replacement and trailer placement and authorizes West Peak Roofing to proceed accordingly. All terms are final. No verbal agreements apply.</p>

<h2>Signatures</h2>
<div class="sig-row">
  <div class="sig-block"><div class="sig-line">${sigImg}</div><div class="sig-label">Homeowner Signature</div></div>
  <div class="sig-block"><div class="sig-line" style="font-family:'Brush Script MT',cursive;font-size:18px;color:#111">${profile?.full_name || ""}</div><div class="sig-label">Sales Representative Signature</div></div>
</div>
<div class="sig-row">
  <div class="sig-block"><div class="sig-line">${signedDate}</div><div class="sig-label">Date</div></div>
  <div class="sig-block"><div class="sig-line">${signedDate}</div><div class="sig-label">Date</div></div>
</div>

<div class="footer">Document ID: ${docId} · ${today} · West Peak Roofing</div>
</div>
</body></html>`;
      if (mode === "html") return html;
      const w = window.open("", "_blank");
      if (w) {
        w.document.write(html);
        w.document.close();
        setTimeout(() => w.print(), 300);
      }
    };

    // Auto-upload the signed contract PDF back to JobNimbus Documents.
    // Called from the homeowner-sign callback. Silent failure — never blocks the sign flow.
    const uploadContractToJn = async (jnid, sigDataUrl) => {
      if (!jnid) return { skipped: "no_jnid" };
      try {
        // Lazy-load html2pdf from CDN on first use (kept out of the eager bundle).
        if (!window.html2pdf) {
          await new Promise((resolve, reject) => {
            const s = document.createElement("script");
            s.src = "https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js";
            s.onload = resolve;
            s.onerror = reject;
            document.head.appendChild(s);
          });
        }
        const html = generatePDF("html", sigDataUrl);
        const container = document.createElement("div");
        container.innerHTML = html;
        container.style.position = "absolute";
        container.style.left = "-9999px";
        container.style.top = "0";
        document.body.appendChild(container);
        try {
          const target = container.querySelector(".wpc") || container;
          const blob = await window.html2pdf().from(target).set({
            margin: 0,
            filename: "contract.pdf",
            image: { type: "jpeg", quality: 0.92 },
            html2canvas: { scale: 2, useCORS: true },
            jsPDF: { unit: "in", format: "letter", orientation: "portrait" },
          }).outputPdf("blob");
          const base64 = await new Promise(resolve => {
            const reader = new FileReader();
            reader.onloadend = () => resolve(String(reader.result).split(",")[1] || "");
            reader.readAsDataURL(blob);
          });
          const safeName = (activeJob.customer_name || "job").replace(/[^a-z0-9]+/gi, "_");
          const stamp = new Date().toISOString().slice(0, 10);
          const filename = `WestPeak_Contract_${safeName}_${stamp}.pdf`;
          const res = await fetch("/api/jn/upload-contract", {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ jnid, filename, base64 }),
          });
          const data = await res.json();
          return data;
        } finally {
          document.body.removeChild(container);
        }
      } catch (e) {
        console.error("JN auto-upload failed:", e);
        return { error: e.message };
      }
    };

    return (
      <div style={appStyle}>{fontLink}
        <NavBar title="Contract" onBack={()=>{saveJobToDb();setScreen("builder")}}/>
        <div style={{padding:20}}>
          <Card style={{background:T.accent,border:`1px solid ${T.accentDark}`}}><div style={{textAlign:"center"}}><div style={{fontSize:11,fontWeight:700,letterSpacing:"0.15em",color:"#e6cca8",textTransform:"uppercase"}}>West Peak Roofing</div><h2 style={{fontSize:20,fontWeight:700,margin:"8px 0 4px",color:"#ffffff"}}>Color, Material & Upgrade Agreement</h2><div style={{fontSize:13,color:"#f5ecd6"}}>{activeJob.customer_name}</div>{activeJob.property_address&&<div style={{fontSize:12,color:"#f5ecd6",marginTop:2,opacity:0.85}}>{activeJob.property_address}</div>}</div></Card>

          <Card>
            <SectionLabel>Project Information</SectionLabel>
            <div style={{display:"flex",flexDirection:"column",gap:8}}>
              <LineItem label="Date" value={activeJob.job_date || new Date().toLocaleDateString()}/>
              <LineItem label="Property Address" value={activeJob.property_address || "—"}/>
              <LineItem label="Homeowner(s)" value={activeJob.customer_name || "—"}/>
              <LineItem label="Insurance Carrier" value={activeJob.insurance_carrier || "—"}/>
              <LineItem label="Claim Number" value={activeJob.claim_number || "—"}/>
            </div>
          </Card>

          <Card>
            <SectionLabel>Shingle Selection</SectionLabel>
            <div style={{display:"grid",gridTemplateColumns:"1fr 1fr",gap:14}}>
              <div><div style={{fontSize:11,color:T.textDim,fontWeight:600,textTransform:"uppercase"}}>Shingle Type</div><div style={{fontSize:15,fontWeight:700,marginTop:2}}>{c.shingle?.name}</div></div>
              <div><div style={{fontSize:11,color:T.textDim,fontWeight:600,textTransform:"uppercase"}}>Squares</div><div style={{fontSize:15,fontWeight:700,marginTop:2}}>{activeJob.squares}</div></div>
              <div><div style={{fontSize:11,color:T.textDim,fontWeight:600,textTransform:"uppercase"}}>Shingle Color</div><div style={{fontSize:15,fontWeight:700,marginTop:2}}>{c.displayShingleColor || "—"}</div></div>
              <div><div style={{fontSize:11,color:T.textDim,fontWeight:600,textTransform:"uppercase"}}>Drip Edge</div><div style={{fontSize:15,fontWeight:700,marginTop:2}}>{c.displayDripEdge || "—"}</div></div>
            </div>
          </Card>

          <Card>
            <SectionLabel>Ventilation</SectionLabel>
            <div style={{fontSize:12,color:T.textSec,lineHeight:1.6,marginBottom:10}}>
              <span style={{color:T.text,fontWeight:700}}>Insurance Covered (Base Scope):</span> Like-kind replacement of existing ventilation (box / turtle / static vents) included if approved.
            </div>
            <div style={{fontSize:13,marginBottom:10}}>
              <span style={{color:T.textDim,fontWeight:600,textTransform:"uppercase",fontSize:11,letterSpacing:"0.05em"}}>Upgrade Option (Not Insurance Covered)</span>
              <div style={{marginTop:6,display:"flex",gap:18,flexWrap:"wrap"}}>
                <span style={{color:!activeJob.vent_upgrade_enabled?T.accent:T.textDim,fontWeight:!activeJob.vent_upgrade_enabled?700:500}}>{!activeJob.vent_upgrade_enabled ? "☑" : "☐"} No Upgrade</span>
                <span style={{color:activeJob.vent_upgrade_enabled?T.accent:T.textDim,fontWeight:activeJob.vent_upgrade_enabled?700:500}}>{activeJob.vent_upgrade_enabled ? "☑" : "☐"} Vented Ridge Cap System</span>
              </div>
            </div>
            {activeJob.vent_upgrade_enabled && (
              <div style={{marginTop:8,padding:"10px 14px",borderRadius:10,background:T.bgInput,border:`1px solid ${T.border}`}}>
                <LineItem label={`Ridge Vent · ${activeJob.ridge_vent_qty} LF × ${fmt(activeJob.ridge_vent_sell_price)}/LF`} value={fmt(c.ridgeTotal)}/>
                <LineItem label={`Turtle Vent Covers · ${activeJob.box_cover_qty} EA × ${fmt(activeJob.box_cover_sell_price)}/ea`} value={fmt(c.boxCoverTotal)}/>
                <div style={{borderTop:`1px solid ${T.border}`,marginTop:6,paddingTop:6}}>
                  <LineItem label="Ventilation Upgrade Total" value={fmt(c.ventTotalSell)} color={T.text}/>
                  {c.ventInsuranceCredit > 0 && <LineItem label="Less: Insurance vent credit" value={`(${fmt(c.ventInsuranceCredit)})`} color={T.accent} sub/>}
                  <LineItem label="Net Ventilation OOP" value={fmt(c.ventOOP)} color={T.accent}/>
                </div>
              </div>
            )}
          </Card>

          <Card>
            <SectionLabel>Gutters</SectionLabel>
            <div style={{display:"flex",gap:18,flexWrap:"wrap",marginBottom:10}}>
              <span style={{color:!activeJob.gutters_enabled?T.accent:T.textDim,fontWeight:!activeJob.gutters_enabled?700:500}}>{!activeJob.gutters_enabled ? "☑" : "☐"} Not Required</span>
              <span style={{color:activeJob.gutters_enabled?T.accent:T.textDim,fontWeight:activeJob.gutters_enabled?700:500}}>{activeJob.gutters_enabled ? "☑" : "☐"} Replacement Required</span>
            </div>
            {activeJob.gutters_enabled && (
              <div style={{padding:"10px 14px",borderRadius:10,background:T.bgInput,border:`1px solid ${T.border}`}}>
                <LineItem label="New Gutter Cost" value={fmt(activeJob.gutters_lump_sum)}/>
                {c.gutterInsCredit > 0 && <LineItem label="Less: Insurance D&R Credit" value={`(${fmt(c.gutterInsCredit)})`} color={T.accent} sub/>}
                <div style={{borderTop:`1px solid ${T.border}`,marginTop:6,paddingTop:6}}>
                  <LineItem label="Final Gutter Cost (Homeowner Pays)" value={fmt(c.gutterOOP)} color={T.accent}/>
                </div>
              </div>
            )}
          </Card>

          <Card>
            <SectionLabel>How your roof gets paid for</SectionLabel>
            <div style={{fontSize:13,color:T.textSec,lineHeight:1.7}}>
              Your insurance pays in two checks. The <strong style={{color:T.text}}>1st Check</strong> comes up front — it's the cash value of your old roof, minus your deductible. The <strong style={{color:T.text}}>2nd Check</strong> is released after the work is complete and covers the depreciation that was held back. <strong style={{color:T.text}}>Your Deductible</strong> is the amount your policy requires you to pay; it's set by your insurer, not by West Peak.
            </div>
          </Card>

          <Card>
            <SectionLabel>Insurance Summary</SectionLabel>
            <LineItem label="Insurance Value" value={fmt(activeJob.rcv)}/>
            <LineItem label="2nd Check (After Completion)" value={`(${fmt(activeJob.depreciation)})`} color={T.danger}/>
            <LineItem label="Your Deductible" value={`(${fmt(activeJob.deductible)})`} color={T.danger}/>
            <div style={{borderTop:`1px solid ${T.border}`,marginTop:8,paddingTop:8}}>
              <LineItem label="1st Check" value={fmt(activeJob.acv)} color={T.accent}/>
            </div>
          </Card>

          <OOPBox c={c} activeJob={activeJob}/>

          <Card style={{marginTop:16}}>
            <SectionLabel>Payment Responsibility</SectionLabel>
            <div style={{fontSize:13,color:T.textSec,lineHeight:1.7}}>Homeowner acknowledges:</div>
            <ul style={{margin:"8px 0 0 18px",padding:0,fontSize:13,color:T.textSec,lineHeight:1.7}}>
              <li>Ventilation upgrades are <strong style={{color:T.text}}>NOT</strong> covered by insurance</li>
              <li>Gutter costs beyond the insurance allowance are <strong style={{color:T.text}}>NOT</strong> covered</li>
              <li>All listed additional costs are the homeowner's responsibility</li>
            </ul>
          </Card>

          <Card>
            <SectionLabel>Decking (Plywood) Replacement</SectionLabel>
            <div style={{fontSize:13,color:T.textSec,lineHeight:1.7}}>During roof replacement, decking condition cannot be fully determined until existing shingles are removed.</div>
            <ul style={{margin:"8px 0 8px 18px",padding:0,fontSize:13,color:T.textSec,lineHeight:1.7}}>
              <li>First two (2) sheets of plywood included at no additional cost</li>
              <li>Additional sheets charged at <strong style={{color:T.text}}>$80.00 per sheet</strong> (material and labor included)</li>
            </ul>
            <div style={{fontSize:13,color:T.textSec,lineHeight:1.7}}>Homeowner acknowledges decking condition is not fully visible prior to tear-off; replacement may be required for code compliance; any decking beyond the first two sheets is the homeowner's responsibility.</div>
          </Card>

          <Card>
            <SectionLabel>Dump Trailer Placement</SectionLabel>
            <div style={{fontSize:13,color:T.textSec,lineHeight:1.7}}>A dump trailer will be required for debris removal and placed on the property for the duration of the project. Minor surface markings or wear may occur; West Peak Roofing is not responsible for pre-existing conditions or minor cosmetic impacts.</div>
            <div style={{marginTop:10,padding:"10px 14px",borderRadius:10,background:T.bgInput,border:`1px solid ${T.border}`}}>
              <div style={{fontSize:11,color:T.textDim,fontWeight:600,textTransform:"uppercase",letterSpacing:"0.05em"}}>Preferred Trailer Placement</div>
              <div style={{fontSize:15,fontWeight:700,marginTop:4,color:T.text}}>{activeJob.trailer_placement || "Driveway"}</div>
            </div>
          </Card>

          <Card style={{marginTop:16}}>
            <SectionLabel icon={<IconPen s={14}/>}>Authorization & Signature</SectionLabel>
            <div style={{fontSize:12,color:T.textDim,lineHeight:1.7,marginBottom:16}}>Homeowner approves all material selections and authorizes West Peak Roofing to proceed with installation and material ordering based on this agreement. All selections and costs are final. No verbal agreements apply.</div>
            {hasViolations ? (
              <div style={{padding:16,borderRadius:12,background:T.dangerBg,border:`1.5px solid ${T.danger}`}}><div style={{display:"flex",alignItems:"center",gap:8,marginBottom:10}}><span style={{color:T.danger}}><IconAlert s={18}/></span><div style={{fontSize:14,fontWeight:700,color:T.danger}}>Cannot Sign — Below Redline</div></div>{violations.map((v,i)=><div key={i} style={{fontSize:12,color:T.danger,marginBottom:4,paddingLeft:26}}>• {v}</div>)}</div>
            ) : signed ? (
              <div>
                <div style={{textAlign:"center",padding:"20px 0"}}><div style={{width:48,height:48,borderRadius:24,background:T.accentLight,display:"flex",alignItems:"center",justifyContent:"center",margin:"0 auto 12px",border:`2px solid ${T.accent}`}}><span style={{color:T.accent}}><IconCheck s={24}/></span></div><div style={{fontSize:16,fontWeight:700,color:T.accent}}>Contract Signed</div><div style={{fontSize:13,color:T.textDim,marginTop:4}}>{activeJob.customer_name} · {new Date().toLocaleDateString()}</div></div>
                {activeJob.jnid && jnUploadStatus === "uploading" && (
                  <div style={{marginTop:8,padding:"10px 14px",borderRadius:10,background:T.bgInput,border:`1px solid ${T.border}`,fontSize:13,color:T.textSec,display:"flex",alignItems:"center",gap:8}}>
                    <div style={{width:14,height:14,borderRadius:7,border:`2px solid ${T.accent}`,borderTopColor:"transparent",animation:"spin 0.8s linear infinite"}}/>
                    Uploading to JobNimbus…
                  </div>
                )}
                {activeJob.jnid && jnUploadStatus === "success" && (
                  <div style={{marginTop:8,padding:"10px 14px",borderRadius:10,background:T.accentLight,border:`1px solid ${T.accent}`,fontSize:13,color:T.accent,display:"flex",alignItems:"center",gap:8,fontWeight:600}}>
                    <IconCheck s={16}/> Uploaded to JobNimbus Documents
                  </div>
                )}
                {activeJob.jnid && jnUploadStatus === "failed" && (
                  <div style={{marginTop:8,padding:"10px 14px",borderRadius:10,background:T.dangerBg,border:`1px solid ${T.danger}`,fontSize:13,color:T.danger,display:"flex",alignItems:"flex-start",gap:8}}>
                    <IconAlert s={16}/>
                    <div style={{flex:1}}>
                      <div style={{fontWeight:700,marginBottom:2}}>JobNimbus upload failed</div>
                      <div style={{fontSize:12,color:T.danger,opacity:0.85}}>Contract is still signed and saved. An admin will retry the upload — let the office know.</div>
                    </div>
                  </div>
                )}
                {activeJob.jnid && activeJob.jn_uploaded_at && !jnUploadStatus && (
                  <div style={{marginTop:8,padding:"8px 12px",borderRadius:8,background:T.accentLight,fontSize:12,color:T.accent,display:"flex",alignItems:"center",gap:6,fontWeight:600}}>
                    <IconCheck s={14}/> Already in JobNimbus Documents
                  </div>
                )}
              </div>
            ) : (
              <div>
                {!showSig ? (
                  <button onClick={()=>setShowSig(true)} style={{width:"100%",padding:"14px",border:`1.5px dashed ${T.border}`,borderRadius:12,background:"transparent",color:T.accent,fontSize:15,fontWeight:600,cursor:"pointer",fontFamily:"inherit",display:"flex",alignItems:"center",justifyContent:"center",gap:8}}>
                    <IconPen s={18}/> Sign Contract
                  </button>
                ) : (
                  <SignaturePad onSign={(signatureDataUrl)=>{
                    setSigned(true);
                    setShowSig(false);
                    setActiveJob(prev => {
                      const upd = {...prev, signed: true, signed_at: new Date().toISOString(), homeowner_signature: signatureDataUrl};
                      saveJobToDb(upd);
                      // Fire-and-forget: auto-upload signed contract PDF to JN Documents tab.
                      // Status surfaced as a toast on this screen; admin-only badge on home tracks failures.
                      if (upd.jnid) {
                        setJnUploadStatus("uploading");
                        uploadContractToJn(upd.jnid, signatureDataUrl).then(result => {
                          if (result?.ok) {
                            setJnUploadStatus("success");
                            setActiveJob(p => {
                              const u = {...p, jn_uploaded_at: new Date().toISOString()};
                              saveJobToDb(u);
                              return u;
                            });
                          } else {
                            setJnUploadStatus("failed");
                          }
                        }).catch(() => setJnUploadStatus("failed"));
                      }
                      return upd;
                    });
                  }}/>
                )}
              </div>
            )}
          </Card>

          <button onClick={generatePDF} style={{...btnPrimary,marginTop:8,display:"flex",alignItems:"center",justifyContent:"center",gap:8}}><IconPrint/> Generate Contract PDF</button>
          <button onClick={()=>setScreen("builder")} style={{width:"100%",padding:"14px",border:`1.5px solid ${T.border}`,borderRadius:12,background:"transparent",color:T.textSec,fontSize:15,fontWeight:600,cursor:"pointer",fontFamily:"inherit",marginTop:10}}>← Back to Builder</button>
        </div>
      </div>
    );
  }

  // ════════════════════════════════════════
  // COMMISSION (PIN protected)
  // ════════════════════════════════════════
  if (screen === "commission") {
    const biz = profile?.business_name || "";
    // `locked` is defined at App scope (line ~601) and inherited here

    // Phase B vent fix (lines 182-189 of calcJob) is preserved end-to-end:
    // turtle vent value is stripped from RCV inside calcJob, and we never add it back here.
    const D = activeJob.deductible || 0;
    const V = c.ventOOP || 0;
    const G = c.gutterOOP || 0;
    const S = activeJob.squares || 0;

    // Total Job Value matches calcJob.totalJobValue exactly: T = sell·S + V + G.
    // Back-solving: sell = (T − V − G) / S.
    const impliedSell = S > 0 ? (whatIfTotal - V - G) / S : 0;

    // Tiers ascending by cost (NS < HDZ < UHDZ < Grand Sequoia < PermaLock).
    const tiers = [...(pricing || [])].sort((a, b) => (a.cost || 0) - (b.cost || 0));

    // Per-tier commission via calcJob — never re-derive math, always defer to the function.
    const tierResults = tiers.map(tier => {
      const synth = { ...activeJob, shingle_type: tier.shingle_type, sell_price_per_square: impliedSell };
      const r = calcJob(synth, pricing, ventPricing);
      return { tier, result: r };
    });

    const defaultTotal = c.totalJobValue || 0;
    const totalDirty = Math.abs((whatIfTotal || 0) - defaultTotal) > 0.005;

    const printInvoice = () => {
      const today = new Date().toLocaleDateString("en-US",{year:"numeric",month:"long",day:"numeric"});
      const s = c.shingle;
      const safeName = (activeJob.customer_name || "job").replace(/[^a-z0-9]+/gi, "_");
      const docTitle = `WestPeak_Invoice_${safeName}_${activeJob.id}`;
      const html = `<!DOCTYPE html>
<html><head><meta charset="utf-8">
<title>${docTitle}</title>
<style>
.wpi *{margin:0;padding:0;box-sizing:border-box}
.wpi{font-family:'Helvetica Neue',Arial,sans-serif;color:#111;background:#fff;padding:0.5in;width:8.5in;font-size:13px;line-height:1.5}
.wpi .hdr{text-align:center;margin-bottom:32px;padding-bottom:20px;border-bottom:2px solid #111}
.wpi .hdr h1{font-size:22px;margin-bottom:4px;font-weight:700}
.wpi .hdr p{font-size:13px;color:#666}
.wpi .meta{display:flex;justify-content:space-between;margin-bottom:28px;font-size:13px;line-height:1.7}
.wpi .meta strong{font-weight:700}
.wpi table{width:100%;border-collapse:collapse;margin-bottom:28px}
.wpi th{text-align:left;font-size:11px;text-transform:uppercase;letter-spacing:0.08em;color:#666;padding:8px 0;border-bottom:2px solid #111}
.wpi td{padding:10px 0;border-bottom:1px solid #e5e7eb;font-size:14px}
.wpi td:last-child,.wpi th:last-child{text-align:right}
.wpi tr.total td{border-bottom:2px solid #111;font-weight:700;font-size:16px;padding-top:14px}
.wpi .ft{margin-top:36px;padding-top:18px;border-top:1px solid #e5e7eb;font-size:12px;color:#999;text-align:center}
</style>
<div class="wpi">
  <div class="hdr"><h1>COMMISSION INVOICE</h1><p>${profile?.full_name||"Sales Representative"}</p></div>
  <div class="meta">
    <div><strong>From:</strong> ${biz||"Independent Contractor"}<br><strong>To:</strong> West Peak Roofing<br><strong>Date:</strong> ${today}</div>
    <div style="text-align:right"><strong>Customer:</strong> ${activeJob.customer_name}<br><strong>Job Date:</strong> ${activeJob.job_date}<br><strong>Claim:</strong> ${activeJob.claim_number||"N/A"}</div>
  </div>
  <table>
    <tr><th>Item</th><th>Details</th><th>Commission</th></tr>
    <tr><td>${s?.name}</td><td>${activeJob.squares} sq × ${fmt(activeJob.sell_price_per_square-s?.cost)}/sq</td><td>${fmt(c.shingleCommission)}</td></tr>
    ${activeJob.vent_upgrade_enabled?`<tr><td>Ridge Vent</td><td>${activeJob.ridge_vent_qty} LF × ${fmt(activeJob.ridge_vent_sell_price-c.ridgeCost)}/LF</td><td>${fmt(c.ridgeCommission)}</td></tr><tr><td>Box Covers</td><td>${activeJob.box_cover_qty} ea × ${fmt(activeJob.box_cover_sell_price-c.boxCoverCost)}/ea</td><td>${fmt(c.boxCoverCommission)}</td></tr>`:""}
    <tr class="total"><td colspan="2">TOTAL DUE</td><td>${fmt(c.totalCommission)}</td></tr>
  </table>
  <div class="ft">${today} · ${biz} · Confidential</div>
</div>
</body></html>`;
      const w = window.open("", "_blank");
      if (w) {
        w.document.write(html);
        w.document.close();
        setTimeout(() => w.print(), 300);
      }
    };

    const onPickTier = (tier) => {
      if (locked) return;
      if (!(impliedSell > 0) || S <= 0) return;
      setActiveJob(prev => {
        const upd = {...prev, shingle_type: tier.shingle_type, sell_price_per_square: impliedSell};
        saveJobToDb(upd);
        return upd;
      });
    };

    const onTotalInput = (raw) => {
      if (locked) return;
      const cleaned = String(raw).replace(/[^0-9.]/g, "");
      if (cleaned === "" || cleaned === ".") { setWhatIfTotal(0); return; }
      const n = Number(cleaned);
      if (!isNaN(n)) setWhatIfTotal(n);
    };

    const activeTierCost = c.shingle?.cost || 0;
    const marginPerSq = Math.max(0, impliedSell - activeTierCost);

    // Best/worst tier commissions to give reps a sense of the spread
    const affordableTiers = tierResults.filter(t => impliedSell >= (t.tier.cost || 0));
    const bestTierResult = affordableTiers.length > 0
      ? affordableTiers.reduce((a, b) => (a.result.totalCommission >= b.result.totalCommission ? a : b))
      : null;
    const activeResult = tierResults.find(t => t.tier.shingle_type === activeJob.shingle_type);

    const bumpTotal = (delta) => {
      if (locked) return;
      setWhatIfTotal(prev => Math.max(0, (prev || 0) + delta));
    };

    const QuickBump = ({label, delta}) => (
      <button
        onClick={() => bumpTotal(delta)}
        disabled={locked}
        style={{flex:1,padding:"9px 4px",border:`1px solid ${T.border}`,borderRadius:8,background:"#ffffff",color:T.textSec,fontSize:12.5,fontWeight:600,cursor:locked?"default":"pointer",fontVariantNumeric:"tabular-nums",opacity:locked?0.4:1,fontFamily:"inherit"}}
      >{label}</button>
    );

    return (
      <div style={appStyle}>{fontLink}
        <NavBar title="Job Margin" onBack={()=>setScreen("home")}/>
        <div style={{padding:20}}>

          {/* HERO — solid olive, cream typography. Quiet, premium. */}
          <div style={{background:T.accent,borderRadius:16,padding:"28px 24px",color:"#f5ecd6",textAlign:"center",marginBottom:14,border:`1px solid ${T.accentDark}`,boxShadow:"0 2px 8px rgba(46,42,39,0.08)"}}>
            <div style={{fontSize:10,fontWeight:600,letterSpacing:"0.18em",textTransform:"uppercase",color:"#e6cca8"}}>Your Commission</div>
            <div style={{fontSize:60,fontWeight:600,marginTop:6,marginBottom:0,letterSpacing:"-0.03em",color:"#ffffff",lineHeight:1.05,fontVariantNumeric:"tabular-nums"}}>{fmt(c.totalCommission)}</div>
            <div style={{fontSize:13,opacity:0.78,marginTop:8,fontWeight:500,color:"#f5ecd6"}}>{c.shingle?.name || "—"} · {fmt(activeJob.sell_price_per_square)}/sq · {S} sq</div>
            {(marginPerSq > 0 || (bestTierResult && bestTierResult.tier.shingle_type !== activeJob.shingle_type && !locked)) && (
              <div style={{display:"flex",gap:8,justifyContent:"center",marginTop:14,flexWrap:"wrap"}}>
                {marginPerSq > 0 && (
                  <div style={{padding:"4px 11px",borderRadius:6,background:"rgba(245,236,214,0.12)",border:"1px solid rgba(245,236,214,0.22)",fontSize:11.5,fontWeight:600,color:"#f5ecd6",fontVariantNumeric:"tabular-nums"}}>
                    {fmt(marginPerSq)}/sq margin
                  </div>
                )}
                {bestTierResult && bestTierResult.tier.shingle_type !== activeJob.shingle_type && !locked && (
                  <div style={{padding:"4px 11px",borderRadius:6,background:"rgba(0,0,0,0.18)",fontSize:11.5,fontWeight:600,color:"#f5ecd6"}}>
                    {bestTierResult.tier.display_name} +{fmt(bestTierResult.result.totalCommission - c.totalCommission)}
                  </div>
                )}
              </div>
            )}
          </div>

          {/* TOTAL JOB INPUT — the lever; line-item math reacts below */}
          <Card>
            <SectionLabel>Total job value</SectionLabel>
            <div style={{display:"flex",alignItems:"center",gap:8,padding:"12px 14px",borderRadius:10,background:"#ffffff",border:`1px solid ${locked?T.border:T.accent}`}}>
              <span style={{fontSize:22,fontWeight:500,color:T.textDim,fontVariantNumeric:"tabular-nums"}}>$</span>
              <input
                type="text"
                inputMode="decimal"
                value={whatIfTotal ? whatIfTotal.toFixed(2) : ""}
                onChange={(e) => onTotalInput(e.target.value)}
                disabled={locked}
                style={{flex:1,minWidth:0,background:"transparent",border:"none",outline:"none",fontSize:24,fontWeight:600,color:T.text,fontVariantNumeric:"tabular-nums",letterSpacing:"-0.02em",WebkitAppearance:"none",fontFamily:"inherit"}}
              />
            </div>
            <div style={{display:"flex",gap:6,marginTop:10}}>
              <QuickBump label={`−${fmt(50*S)}`} delta={-50*S}/>
              <QuickBump label={`−${fmt(10*S)}`} delta={-10*S}/>
              <QuickBump label={`+${fmt(10*S)}`} delta={10*S}/>
              <QuickBump label={`+${fmt(50*S)}`} delta={50*S}/>
            </div>
            <div style={{fontSize:11.5,color:T.textDim,marginTop:12,display:"flex",justifyContent:"space-between",alignItems:"center"}}>
              <span>{locked ? "Locked" : (totalDirty ? "Preview — tap a tier below to commit" : "Live from current job state")}</span>
              <span style={{fontVariantNumeric:"tabular-nums",color:T.textSec,fontWeight:600}}>{fmt(impliedSell)}/sq</span>
            </div>
          </Card>

          {/* YOUR COMMISSION — work-backward line-item breakdown */}
          <Card>
            <SectionLabel>Your commission</SectionLabel>
            <div style={{display:"flex",flexDirection:"column",gap:18,fontSize:13,fontVariantNumeric:"tabular-nums"}}>

              {/* Shingles */}
              <div>
                <div style={{fontSize:10,fontWeight:700,color:T.textDim,textTransform:"uppercase",letterSpacing:"0.1em",marginBottom:8}}>Shingles · {c.shingle?.name || ""}</div>
                <div style={{display:"flex",justifyContent:"space-between",lineHeight:1.7}}>
                  <span style={{color:T.textSec}}>Sold {S} sq × {fmt(impliedSell)}/sq</span>
                  <span style={{color:T.text,fontWeight:600}}>{fmt(impliedSell * S)}</span>
                </div>
                <div style={{display:"flex",justifyContent:"space-between",lineHeight:1.7}}>
                  <span style={{color:T.textSec}}>Redline cost {S} sq × {fmt(activeTierCost)}/sq</span>
                  <span style={{color:T.danger,fontWeight:600}}>−{fmt(activeTierCost * S)}</span>
                </div>
                <div style={{borderTop:`1px solid ${T.border}`,marginTop:6,paddingTop:8,display:"flex",justifyContent:"space-between",alignItems:"baseline"}}>
                  <span style={{fontWeight:700,color:T.text}}>Shingle commission</span>
                  <span style={{fontWeight:700,color:T.accent,fontSize:15}}>{fmt(c.shingleCommission)}</span>
                </div>
              </div>

              {/* Ridge Vent */}
              {activeJob.vent_upgrade_enabled && (
                <div>
                  <div style={{fontSize:10,fontWeight:700,color:T.textDim,textTransform:"uppercase",letterSpacing:"0.1em",marginBottom:8}}>Ridge Vent</div>
                  <div style={{display:"flex",justifyContent:"space-between",lineHeight:1.7}}>
                    <span style={{color:T.textSec}}>Sold {activeJob.ridge_vent_qty} LF × {fmt(activeJob.ridge_vent_sell_price)}/LF</span>
                    <span style={{color:T.text,fontWeight:600}}>{fmt(c.ridgeTotal)}</span>
                  </div>
                  <div style={{display:"flex",justifyContent:"space-between",lineHeight:1.7}}>
                    <span style={{color:T.textSec}}>Redline cost {activeJob.ridge_vent_qty} LF × {fmt(c.ridgeCost)}/LF</span>
                    <span style={{color:T.danger,fontWeight:600}}>−{fmt(c.ridgeCost * (activeJob.ridge_vent_qty || 0))}</span>
                  </div>
                  <div style={{borderTop:`1px solid ${T.border}`,marginTop:6,paddingTop:8,display:"flex",justifyContent:"space-between",alignItems:"baseline"}}>
                    <span style={{fontWeight:700,color:T.text}}>Ridge commission</span>
                    <span style={{fontWeight:700,color:T.accent,fontSize:15}}>{fmt(c.ridgeCommission)}</span>
                  </div>
                </div>
              )}

              {/* Box Covers */}
              {activeJob.vent_upgrade_enabled && (
                <div>
                  <div style={{fontSize:10,fontWeight:700,color:T.textDim,textTransform:"uppercase",letterSpacing:"0.1em",marginBottom:8}}>Box Covers</div>
                  <div style={{display:"flex",justifyContent:"space-between",lineHeight:1.7}}>
                    <span style={{color:T.textSec}}>Sold {activeJob.box_cover_qty} ea × {fmt(activeJob.box_cover_sell_price)}/ea</span>
                    <span style={{color:T.text,fontWeight:600}}>{fmt(c.boxCoverTotal)}</span>
                  </div>
                  <div style={{display:"flex",justifyContent:"space-between",lineHeight:1.7}}>
                    <span style={{color:T.textSec}}>Redline cost {activeJob.box_cover_qty} ea × {fmt(c.boxCoverCost)}/ea</span>
                    <span style={{color:T.danger,fontWeight:600}}>−{fmt(c.boxCoverCost * (activeJob.box_cover_qty || 0))}</span>
                  </div>
                  <div style={{borderTop:`1px solid ${T.border}`,marginTop:6,paddingTop:8,display:"flex",justifyContent:"space-between",alignItems:"baseline"}}>
                    <span style={{fontWeight:700,color:T.text}}>Box cover commission</span>
                    <span style={{fontWeight:700,color:T.accent,fontSize:15}}>{fmt(c.boxCoverCommission)}</span>
                  </div>
                </div>
              )}

              {/* Gutters — passthrough, no commission */}
              {activeJob.gutters_enabled && (
                <div>
                  <div style={{fontSize:10,fontWeight:700,color:T.textDim,textTransform:"uppercase",letterSpacing:"0.1em",marginBottom:8}}>Gutters</div>
                  <div style={{display:"flex",justifyContent:"space-between",lineHeight:1.7}}>
                    <span style={{color:T.textSec}}>Sold to homeowner</span>
                    <span style={{color:T.text,fontWeight:600}}>{fmt(activeJob.gutters_lump_sum || 0)}</span>
                  </div>
                  <div style={{padding:"7px 11px",marginTop:6,borderRadius:6,background:T.bgInput,fontSize:11.5,color:T.textDim,fontStyle:"italic"}}>
                    No commission on gutters — passthrough
                  </div>
                </div>
              )}

              {/* Total commission */}
              <div style={{borderTop:`2px solid ${T.text}`,paddingTop:12,marginTop:2,display:"flex",justifyContent:"space-between",alignItems:"baseline"}}>
                <span style={{fontWeight:700,color:T.text,fontSize:13,textTransform:"uppercase",letterSpacing:"0.08em"}}>Your total commission</span>
                <span style={{fontWeight:700,color:T.text,fontSize:20,letterSpacing:"-0.01em"}}>{fmt(c.totalCommission)}</span>
              </div>
            </div>
          </Card>

          {/* HOMEOWNER PAYS — billing breakdown with credits explicit */}
          <Card>
            <SectionLabel>Homeowner pays</SectionLabel>
            <div style={{fontSize:13,fontVariantNumeric:"tabular-nums",display:"flex",flexDirection:"column",gap:18}}>

              {/* Roof */}
              <div>
                <div style={{fontSize:10,fontWeight:700,color:T.textDim,textTransform:"uppercase",letterSpacing:"0.1em",marginBottom:8}}>Roof</div>
                <div style={{display:"flex",justifyContent:"space-between",lineHeight:1.7}}>
                  <span style={{color:T.textSec}}>Insurance pays</span>
                  <span style={{color:T.text,fontWeight:600}}>{fmt(c.insurancePerSq * S)}</span>
                </div>
                <div style={{display:"flex",justifyContent:"space-between",lineHeight:1.7}}>
                  <span style={{color:T.textSec}}>Deductible</span>
                  <span style={{color:T.text,fontWeight:600}}>{fmt(activeJob.deductible || 0)}</span>
                </div>
                {c.roofGapTotal > 0 && !c.insuranceFullyCovered && (
                  <div style={{display:"flex",justifyContent:"space-between",lineHeight:1.7}}>
                    <span style={{color:T.textSec}}>Roof gap (sell above insurance)</span>
                    <span style={{color:T.warn,fontWeight:600}}>{fmt(c.roofGapTotal)}</span>
                  </div>
                )}
              </div>

              {/* Ventilation upgrade */}
              {activeJob.vent_upgrade_enabled && (
                <div>
                  <div style={{fontSize:10,fontWeight:700,color:T.textDim,textTransform:"uppercase",letterSpacing:"0.1em",marginBottom:8}}>Ventilation upgrade</div>
                  <div style={{display:"flex",justifyContent:"space-between",lineHeight:1.7}}>
                    <span style={{color:T.textSec}}>Vent upgrade billed</span>
                    <span style={{color:T.text,fontWeight:600}}>{fmt(c.ventTotalSell)}</span>
                  </div>
                  {c.ventInsuranceCredit > 0 && (
                    <div style={{display:"flex",justifyContent:"space-between",lineHeight:1.7}}>
                      <span style={{color:T.accent}}>Less: turtle-vent insurance credit</span>
                      <span style={{color:T.accent,fontWeight:600}}>−{fmt(c.ventInsuranceCredit)}</span>
                    </div>
                  )}
                  <div style={{borderTop:`1px solid ${T.border}`,marginTop:6,paddingTop:8,display:"flex",justifyContent:"space-between",alignItems:"baseline"}}>
                    <span style={{fontWeight:700,color:T.text}}>Net vent OOP</span>
                    <span style={{fontWeight:700,color:T.accent,fontSize:14}}>{fmt(c.ventOOP)}</span>
                  </div>
                </div>
              )}

              {/* Gutters */}
              {activeJob.gutters_enabled && (
                <div>
                  <div style={{fontSize:10,fontWeight:700,color:T.textDim,textTransform:"uppercase",letterSpacing:"0.1em",marginBottom:8}}>Gutters</div>
                  <div style={{display:"flex",justifyContent:"space-between",lineHeight:1.7}}>
                    <span style={{color:T.textSec}}>Gutters billed</span>
                    <span style={{color:T.text,fontWeight:600}}>{fmt(activeJob.gutters_lump_sum || 0)}</span>
                  </div>
                  {c.gutterInsCredit > 0 && (
                    <div style={{display:"flex",justifyContent:"space-between",lineHeight:1.7}}>
                      <span style={{color:T.accent}}>Less: insurance D&amp;R credit</span>
                      <span style={{color:T.accent,fontWeight:600}}>−{fmt(c.gutterInsCredit)}</span>
                    </div>
                  )}
                  <div style={{borderTop:`1px solid ${T.border}`,marginTop:6,paddingTop:8,display:"flex",justifyContent:"space-between",alignItems:"baseline"}}>
                    <span style={{fontWeight:700,color:T.text}}>Net gutter OOP</span>
                    <span style={{fontWeight:700,color:T.accent,fontSize:14}}>{fmt(c.gutterOOP)}</span>
                  </div>
                </div>
              )}

              {/* Total */}
              <div style={{borderTop:`2px solid ${T.text}`,paddingTop:12,marginTop:2,display:"flex",justifyContent:"space-between",alignItems:"baseline"}}>
                <span style={{fontWeight:700,color:T.text,fontSize:13,textTransform:"uppercase",letterSpacing:"0.08em"}}>Total homeowner OOP</span>
                <span style={{fontWeight:700,color:T.text,fontSize:20,letterSpacing:"-0.01em"}}>{fmt(c.homeownerOOP)}</span>
              </div>
            </div>
          </Card>

          {/* COMPACT TIER LIST — pre-appointment planning */}
          <Card>
            <SectionLabel>Other shingle tiers</SectionLabel>
            {tiers.length === 0 ? (
              <div style={{fontSize:13,color:T.textDim,padding:"12px 0"}}>No shingle pricing rows loaded yet.</div>
            ) : (
              <div style={{display:"flex",flexDirection:"column",gap:6}}>
                {tierResults.map(({tier, result}) => {
                  const isActive = tier.shingle_type === activeJob.shingle_type;
                  const canAfford = impliedSell >= (tier.cost || 0);
                  return (
                    <button
                      key={tier.shingle_type}
                      onClick={() => onPickTier(tier)}
                      disabled={locked || !canAfford}
                      style={{textAlign:"left",padding:"11px 14px",borderRadius:8,background:isActive?T.accentLight:"#ffffff",border:`1px solid ${isActive?T.accent:T.border}`,cursor:(locked||!canAfford)?"default":"pointer",opacity:canAfford?1:0.42,fontFamily:"inherit",color:T.text,display:"flex",justifyContent:"space-between",alignItems:"center",gap:10}}
                    >
                      <div style={{display:"flex",alignItems:"center",gap:8}}>
                        {isActive && <span style={{width:6,height:6,borderRadius:"50%",background:T.accent,display:"inline-block"}}/>}
                        <span style={{fontWeight:600,fontSize:14,letterSpacing:"-0.01em"}}>{tier.display_name}</span>
                      </div>
                      <span style={{fontWeight:700,fontVariantNumeric:"tabular-nums",color:canAfford?T.text:T.textDim,fontSize:15,letterSpacing:"-0.01em"}}>
                        {canAfford ? fmt(result.totalCommission) : "—"}
                      </span>
                    </button>
                  );
                })}
              </div>
            )}
            {locked && (
              <div style={{marginTop:12,padding:"10px 14px",borderRadius:8,background:T.accentLight,border:`1px solid ${T.border}`,fontSize:12,color:T.textSec,display:"flex",alignItems:"center",gap:8}}>
                <IconLock/> Locked — contract signed.
              </div>
            )}
          </Card>

          <button
            onClick={printInvoice}
            disabled={!activeJob.signed}
            style={{...btnPrimary,display:"flex",alignItems:"center",justifyContent:"center",gap:8,opacity:activeJob.signed?1:0.4,cursor:activeJob.signed?"pointer":"default"}}
          >
            <IconPrint/> {activeJob.signed ? "Generate Invoice PDF" : "Sign contract first"}
          </button>

          <div style={{marginTop:12,textAlign:"center",color:T.textDim,fontSize:12,display:"flex",alignItems:"center",justifyContent:"center",gap:6}}>
            <IconLock/> PIN Protected · Rep view only
          </div>
        </div>
      </div>
    );
  }

  return null;
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
