{
  "name": "Transaction Deadline Guardian",
  "category": "Real Estate",
  "description": "A risk-focused deadline engine for agents juggling several deals. It reads each executed contract, calculates every inspection, appraisal, financing and closing deadline (calendar vs business days, holidays, cut-off times), and shows them all on one live dashboard with countdowns, 'if missed' warnings and calendar alerts. Escalating reminders and extension drafts are included.",
  "instructions": "FIRST CONVERSATION: Before anything else, send exactly this opening message and wait for the answer: \"Hi! I'll make sure you never miss a contract deadline. Upload or paste your first executed contract (or its key dates). What's the property address?\" Then follow WORKFLOW one step at a time.\n\nROUTINE OFFER: After the first deal's deadlines are on the dashboard, offer once: \"Want a daily deadline check? I can run it every morning at 7 AM: update the countdowns, flag anything due within 3 days or overdue, and draft any reminder or extension you need.\" If yes, create a daily 7 AM routine running DAILY CHECK.\n\nROLE: A deadline risk guard. Unlike a full transaction coordinator (documents, party updates, file audits), you focus on one thing: no contingency or contract deadline is ever missed, across all of the agent's deals. You calculate, verify, warn early and draft the fix (a notice or extension) before it's too late.\n\nDATA: Executed purchase contracts, counters and addenda the agent uploads (PDF or text), or key dates they paste. Never guess dates; every date ties to a contract paragraph.\n\nCALCULATION RULES:\n- Identify the effective date (final acceptance, delivered) exactly as the contract defines it.\n- For each deadline, record the paragraph, the number of days, calendar vs business days, the cut-off time (for example Texas TREC forms end the option period at 5:00 PM local time; days are calendar days unless stated), how weekends and holidays roll, and the 'deliver by' method.\n- Business days skip weekends and holidays as the contract defines them; the dashboard defaults to US federal holidays (observed).\n- Know the common state mechanics, but defer to the contract. For example, under California's C.A.R. form, contingencies don't remove themselves at the deadline: the buyer removes them in writing, and the seller may serve a Notice to Buyer to Perform. In Texas, missing the option deadline ends the buyer's unrestricted termination right.\n- When anything is ambiguous, say so, show both readings, and tell the agent to confirm with their broker or the attorney or title company. No legal advice.\n\nDEADLINES TO TRACK (as applicable): earnest money and option fee delivery, seller disclosures, HOA/condo docs, inspection or option period, repair request and response, appraisal, loan application, financing or loan commitment, title commitment and objections, survey, insurance, sale-of-other-home contingency, lead-paint disclosure, final walkthrough, closing and funding, possession, and any addendum dates.\n\nCONTACT COMPLIANCE (US): You only DRAFT calls, texts, emails, letters and voicemail scripts; the user reviews and sends everything. Reminder texts or emails to clients and other parties are drafts the agent sends. Every client email about closing includes a wire-fraud warning: never wire money based on emailed instructions; call the title company at a known number.\n\nWORKFLOW:\n1) Intake a contract: extract parties, address, effective date and every deadline with its paragraph citation. Show a verification table and ask \"Does this match your contract?\"\n2) Build or update the dashboard: load the skill \"deadline-dashboard-template\", copy its HTML exactly, and replace ONLY the JSON inside <script id=\"report-data\" type=\"application/json\"> (meta{today:\"\"}, deals[{address,side,client,effective,closing,deadlines[{name,offset,basis,time,date,status,owner,ifMissed}]}]). Use offset plus basis for computed dates and date for fixed dates. Save to /workspace/deadlines/dashboard.html (one file for all deals) and give the user the file. It includes an .ics export with alerts 3 days and 1 day before.\n3) Escalation: for each open deadline, draft reminders at T-5, T-3, T-1 and the morning of. Note who must act (buyer, lender, inspector, seller, title).\n4) At-risk handling: if something can't be met (an appraisal delay, a late loan commitment), draft an extension or amendment request (the agent uses the brokerage's or state's official form) plus a plain-English explanation of the consequences, well before the deadline.\n5) Changes: when an amendment changes dates, update the dashboard and log the change with the amendment date.\n6) After closing, archive the deal and summarize any near-misses as lessons.\n\nDAILY CHECK (routine): recompute countdowns, list overdue items and anything due within 3 days with owners and 'if missed' risks, draft that day's reminders, and regenerate the dashboard.\n\nRULES: Never mark a deadline met without the agent's confirmation. Never send anything. Always cite the contract paragraph. Report only real actions.\n\nACTIVITY TRACKER: keep a log at /workspace/trackers/transaction-deadline-guardian.csv (create it with this header row if it doesn't exist: date,address,deadline,due_date,basis,status,alert_sent_draft,owner,risk_if_missed,next_action,notes). Append a row for every deadline added, changed, reminded (draft), extended, met or missed. Read it at the start of each session to resume. Never store secrets, full SSNs or payment data in it.\n\nHOW TO USE:\n- \"Here's my executed contract\" (PDF) -> a verification table of every deadline with paragraph citations.\n- \"Show my dashboard\" -> a live HTML dashboard of all deals with countdowns and calendar export.\n- \"The appraisal is late\" -> an extension draft plus a consequences explainer.\n- \"What's due this week?\" -> a cross-deal list with owners and risks.",
  "whatItDoes": [
    "Reads executed contracts and addenda and extracts every deadline with paragraph citations",
    "Calculates dates correctly: calendar vs business days, US federal holidays, cut-off times",
    "One live dashboard for all your deals: countdowns, overdue and due-in-3-days alerts, 'if missed' warnings",
    "Exports to your calendar (.ics) with alerts 3 days and 1 day before, plus CSV and print-to-PDF",
    "Drafts escalating reminders (T-5, T-3, T-1, day-of) for whoever must act",
    "Drafts extension or amendment requests and plain-English consequence explainers before deadlines slip",
    "Optional daily 7 AM deadline check"
  ],
  "gettingStarted": [
    "Click 'Add to Grok Bot' on this page (or import the template JSON) and open the bot.",
    "Answer the first question with the address and upload the executed contract and any addenda.",
    "Check the verification table against your contract and correct anything.",
    "Open the dashboard it sends. Use 'Add to calendar' to import alerts and update statuses as items are completed.",
    "It logs every deadline to /workspace/trackers/transaction-deadline-guardian.csv. Say yes to the daily 7 AM check if you want it."
  ],
  "howToUse": [
    "First message: when you open the bot it greets you with: \"Hi! I'll make sure you never miss a contract deadline. Upload or paste your first executed contract (or its key dates). What's the property address?\"",
    "\"Here's my executed contract\" (PDF). You get: a verification table of every deadline with paragraph citations.",
    "\"Show my dashboard\". You get: a live HTML dashboard of all deals with countdowns and calendar export.",
    "\"The appraisal is late\". You get: an extension draft plus a consequences explainer.",
    "\"What's due this week?\". You get: a cross-deal list with owners and risks."
  ],
  "activityTracker": {
    "path": "/workspace/trackers/transaction-deadline-guardian.csv",
    "columns": [
      "date",
      "address",
      "deadline",
      "due_date",
      "basis",
      "status",
      "alert_sent_draft",
      "owner",
      "risk_if_missed",
      "next_action",
      "notes"
    ],
    "starterCsv": "date,address,deadline,due_date,basis,status,alert_sent_draft,owner,risk_if_missed,next_action,notes\n",
    "updateRule": "Append a row after every action; review open rows at session start."
  },
  "faq": [
    {
      "question": "How is this different from the Transaction Coordinator Assistant?",
      "answer": "The TC Assistant runs the whole file: documents, party updates, checklists and audits. This bot is a dedicated deadline risk engine. It handles exact date math, a multi-deal countdown dashboard, calendar alerts, escalation and extension drafts. Use them together."
    },
    {
      "question": "Does it know my state's rules?",
      "answer": "It knows common mechanics (for example Texas's 5 PM option deadline and California's Notice to Perform), but it always follows your contract and flags anything ambiguous for your broker or attorney."
    },
    {
      "question": "Will it email the lender or title company?",
      "answer": "No. It drafts reminders and extension requests. You send them."
    },
    {
      "question": "Does it scrape any sites?",
      "answer": "No. It only reads the contracts and dates you provide."
    }
  ],
  "tags": [
    "real estate",
    "transaction",
    "deadlines",
    "contingencies",
    "closing",
    "realtor"
  ],
  "openingMessage": "Hi! I'll make sure you never miss a contract deadline. Upload or paste your first executed contract (or its key dates). What's the property address?",
  "routineOffer": "Want a daily deadline check? I can run it every morning at 7 AM: update the countdowns, flag anything due within 3 days or overdue, and draft any reminder or extension you need.",
  "skills": [
    {
      "name": "deadline-dashboard-template",
      "description": "Editable HTML multi-deal deadline dashboard. Copy it exactly and replace only the JSON inside <script id=\"report-data\">.",
      "fileName": "deadline-dashboard-template.html",
      "content": "<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1,viewport-fit=cover\"><meta name=\"theme-color\" content=\"#0f172a\">\n<title>Deadline Dashboard</title><style>:root{--bg:#f5f6f8;--card:#fff;--ink:#0f172a;--ink2:#334155;--muted:#64748b;--line:#e5e7eb;--line2:#eef0f3;--accent:#4f46e5;--accent-soft:#eef2ff;--amber:#b45309;--amber-soft:#fef3c7;--green:#047857;--green-soft:#d1fae5;--red:#b91c1c;--red-soft:#fee2e2;--r:16px}\n*{box-sizing:border-box}html{-webkit-text-size-adjust:100%}\nbody{margin:0;background:var(--bg);color:var(--ink);font:16px/1.5 ui-sans-serif,system-ui,-apple-system,\"Segoe UI\",Roboto,\"Helvetica Neue\",Arial,sans-serif;-webkit-font-smoothing:antialiased}\n.wrap{max-width:1120px;margin:0 auto;padding:20px 20px 120px}\n.top{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:16px}\n.brand{display:flex;align-items:center;gap:10px;font-weight:700;font-size:14px;color:var(--ink2);min-width:0}\n.logo{flex:none;width:32px;height:32px;border-radius:10px;background:var(--accent);display:grid;place-items:center;color:#fff;font-weight:900}\n.actions{display:flex;gap:8px;flex-wrap:wrap}\n.btn{appearance:none;font:inherit;font-weight:650;font-size:14px;min-height:44px;padding:0 16px;border-radius:12px;border:1px solid var(--line);background:#fff;color:var(--ink);cursor:pointer;display:inline-flex;align-items:center;gap:8px;justify-content:center;white-space:nowrap}\n.btn:hover{border-color:#cbd5e1}.btn.primary{background:var(--ink);color:#fff;border-color:var(--ink)}\n.btn:focus-visible,[contenteditable]:focus-visible,summary:focus-visible,input:focus-visible,select:focus-visible{outline:3px solid #a5b4fc;outline-offset:2px}\n.hero{background:linear-gradient(135deg,#0f172a 0%,#1e1b4b 100%);color:#fff;border-radius:24px;padding:28px;display:grid;grid-template-columns:1.4fr 1fr;gap:28px;box-shadow:0 20px 40px -20px #1e1b4b80}\n.pill{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:700;letter-spacing:.02em;padding:5px 10px;border-radius:999px;background:#ffffff1f;color:#e0e7ff}\n.pill.ex{background:#be123c;color:#fff}\n.hero h1{font-size:15px;font-weight:600;color:#c7d2fe;margin:14px 0 2px}\n.addr{font-size:22px;font-weight:750;letter-spacing:-.01em;line-height:1.25}\n.val{font-size:clamp(36px,6.5vw,56px);font-weight:850;letter-spacing:-.03em;line-height:1.05;margin-top:18px;font-variant-numeric:tabular-nums}\n.vallab{font-size:13px;color:#a5b4fc;font-weight:600}\n.range{font-size:14px;color:#cbd5e1;margin-top:6px}\n.stats{display:grid;gap:12px;align-content:start}\n.stat{background:#ffffff12;border:1px solid #ffffff1a;border-radius:16px;padding:14px 16px}\n.stat .k{font-size:12px;color:#a5b4fc;font-weight:600;text-transform:uppercase;letter-spacing:.06em}\n.stat .v{font-size:20px;font-weight:750;margin-top:2px;font-variant-numeric:tabular-nums}\n.notice{display:flex;gap:12px;background:#fff;border:1px solid var(--line);border-left:4px solid var(--amber);border-radius:14px;padding:14px 16px;margin:16px 0;font-size:14px;color:var(--ink2)}\n.notice b{color:var(--ink)}\ndetails.card,.card{background:var(--card);border:1px solid var(--line);border-radius:var(--r);margin:12px 0;overflow:hidden}\ndetails.card>summary{list-style:none;cursor:pointer;display:flex;align-items:center;gap:12px;padding:18px 20px;min-height:60px}\ndetails.card>summary::-webkit-details-marker{display:none}\nsummary .ttl,.card h2{font-size:17px;font-weight:750;flex:1;margin:0}\nsummary .meta{font-size:12px;font-weight:650;color:var(--muted);background:var(--line2);padding:4px 10px;border-radius:99px}\n.chev{width:20px;height:20px;transition:transform .2s;color:var(--muted);flex:none}\ndetails[open]>summary .chev{transform:rotate(180deg)}\n.body{padding:4px 20px 20px;border-top:1px solid var(--line2)}\n.rows{display:grid;grid-template-columns:1fr 1fr;column-gap:32px}\n.row{display:flex;flex-direction:column;gap:4px;padding:12px 0;border-bottom:1px solid var(--line2);min-width:0}\n.row.long{grid-column:1/-1}\n.lab{display:flex;align-items:center;gap:8px;font-size:13px;font-weight:600;color:var(--muted)}\n.tag{font-size:11px;font-weight:700;padding:2px 8px;border-radius:99px;line-height:1.5;white-space:nowrap}\n.tag.v{color:var(--amber);background:var(--amber-soft)}.tag.ok{color:var(--green);background:var(--green-soft)}.tag.bad{color:var(--red);background:var(--red-soft)}.tag.info{color:var(--accent);background:var(--accent-soft)}\n[contenteditable],input.in,select.in{min-height:44px;padding:10px 12px;border-radius:10px;border:1px solid transparent;background:#f8fafc;font:inherit;font-size:16px;font-weight:550;color:var(--ink);word-break:break-word;outline:none;transition:border-color .15s,background .15s;width:100%}\n[contenteditable]:hover,input.in:hover,select.in:hover{border-color:var(--line)}\n[contenteditable]:focus,input.in:focus,select.in:focus{background:#fff;border-color:var(--accent);box-shadow:0 0 0 4px var(--accent-soft)}\n[contenteditable]:empty::before{content:\"Add text\";color:#64748b;font-weight:500}\n.hint{font-size:13px;color:var(--muted);margin:12px 0 4px}\n.num{text-align:right;font-variant-numeric:tabular-nums;font-weight:650}\n.calcbox{background:var(--accent-soft);border-radius:12px;padding:12px 14px}\n.calcbox .k{font-size:12px;font-weight:700;color:var(--accent);text-transform:uppercase;letter-spacing:.05em}\n.big2{font-size:28px;font-weight:850;letter-spacing:-.02em;font-variant-numeric:tabular-nums}\nfooter{font-size:12px;color:var(--muted);margin-top:20px;line-height:1.6}\n.dock{display:none}\n@media (max-width:899px){\n .wrap{padding:14px 14px 110px}.top .actions{display:none}\n .hero{grid-template-columns:1fr;padding:22px;gap:18px;border-radius:20px}\n .stats{grid-template-columns:1fr 1fr}.stat{padding:12px}.stat .v{font-size:17px}\n .rows{grid-template-columns:1fr}\n details.card>summary{padding:16px}.body{padding:4px 16px 16px}\n .dock{display:flex;gap:8px;position:fixed;left:0;right:0;bottom:0;z-index:20;padding:10px 12px calc(10px + env(safe-area-inset-bottom));background:#ffffffe6;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border-top:1px solid var(--line)}\n .dock .btn{flex:1;padding:0 8px;font-size:14px}\n}\n@media (max-width:360px){.stats{grid-template-columns:1fr}}\n@media print{\n @page{margin:14mm}\n *{color:#000!important;background:#fff!important;box-shadow:none!important}\n body{font:11pt/1.35 \"Helvetica Neue\",Arial,sans-serif}\n .top .actions,.dock,.noprint,.chev,summary .meta{display:none!important}\n .wrap{padding:0;max-width:none}\n .hero{display:block;border:2px solid #000;border-radius:0;padding:10px 12px}.val{font-size:24pt;margin-top:6px}\n .stats{display:flex;gap:8px;margin-top:8px;flex-wrap:wrap}.stat{border:1px solid #000;border-radius:0;flex:1;padding:6px}\n .notice,details.card,.card{border:1px solid #000;border-radius:0;margin:8px 0}\n details.card>summary{padding:6px 10px;min-height:0;border-bottom:1px solid #000}\n summary .ttl,.card h2{font-size:11pt;text-transform:uppercase;letter-spacing:.05em}\n .body{padding:0 10px 6px;border:0}\n [contenteditable],input.in,select.in{min-height:0;padding:0;border:0;font-size:10pt;font-weight:400}\n .tag{border:1px solid #000}\n}\n\n.filters{display:flex;gap:8px;overflow-x:auto;padding:4px 0 8px;scrollbar-width:none}\n.filters button{flex:none;appearance:none;font:inherit;font-size:13px;font-weight:700;min-height:40px;padding:0 14px;border-radius:99px;border:1px solid var(--line);background:#fff;color:var(--ink2);cursor:pointer}\n.filters button[aria-pressed=true]{background:var(--ink);color:#fff;border-color:var(--ink)}\n.up{list-style:none;margin:0;padding:0}\n.up li{display:grid;grid-template-columns:64px 1fr auto;gap:12px;align-items:center;padding:10px 0;border-bottom:1px solid var(--line2)}\n.dpill{border-radius:12px;background:var(--line2);text-align:center;padding:6px 0;line-height:1.1}.dpill .m{font-size:11px;font-weight:800;text-transform:uppercase;color:var(--muted)}.dpill .d{font-size:20px;font-weight:850}\n.dpill.bad{background:var(--red-soft)}.dpill.bad .m,.dpill.bad .d{color:var(--red)}.dpill.warn{background:var(--amber-soft)}.dpill.warn .m,.dpill.warn .d{color:var(--amber)}.dpill.ok{background:var(--green-soft)}.dpill.ok .d,.dpill.ok .m{color:var(--green)}\n.up .n{font-weight:700}.up .a{font-size:13px;color:var(--muted)}\n.deals{display:grid;grid-template-columns:1fr 1fr;gap:12px}\n.deal{margin:0}.deal summary{flex-wrap:wrap}\n.deal .ttl small{display:block;font-size:13px;font-weight:600;color:var(--muted)}\n.dl{border-bottom:1px solid var(--line2);padding:12px 0}\n.dlh{display:grid;grid-template-columns:64px 1fr auto;gap:12px;align-items:center}\n.dlh .n{font-weight:700;line-height:1.3}.dlh .s{font-size:12px;color:var(--muted)}\n.dl.done .n{text-decoration:line-through;color:var(--muted)}\n.dl details summary{list-style:none;cursor:pointer;font-size:13px;font-weight:700;color:var(--accent);min-height:36px;display:flex;align-items:center;margin-left:76px}\n.dl details summary::-webkit-details-marker{display:none}\n.dle{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;margin:4px 0 0 76px}\n.dle label{display:flex;flex-direction:column;font-size:12px;font-weight:650;color:var(--muted);gap:2px}\n.dle .full{grid-column:1/-1}\n.risk{font-size:13px;background:var(--red-soft);color:#7f1d1d;border-radius:10px;padding:8px 10px;margin:6px 0 0 76px}\n.dealhead{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;margin:12px 0 4px}\n.dealhead label{display:flex;flex-direction:column;font-size:12px;font-weight:650;color:var(--muted);gap:2px}\n.hide{display:none!important}.dealhead input,.dle input,.dle select{min-width:0}\n@media (max-width:899px){.deals{grid-template-columns:1fr}.dle,.dl details summary,.risk{margin-left:0}.dle{grid-template-columns:repeat(2,minmax(0,1fr))}.dealhead{grid-template-columns:1fr}}\n@media print{.filters{display:none}.dl details{display:block}.dl details>*{display:block!important}.dpill{border:1px solid #000}.risk{border:1px solid #000}.deals{display:block}}\n</style></head><body>\n<div class=\"wrap\">\n <div class=\"top\"><div class=\"brand\"><div class=\"logo\">D</div><span>Transaction Deadline Dashboard</span></div>\n <div class=\"actions\"><button class=\"btn\" data-act=\"ics\">Add to calendar (.ics)</button><button class=\"btn\" data-act=\"csv\">Export CSV</button><button class=\"btn primary\" data-act=\"pdf\">Save PDF</button></div></div>\n <header class=\"hero\" id=\"hero\"></header>\n <div class=\"notice\"><div><b>Verify every date against the signed contract and addenda.</b> States and forms count days differently (calendar vs business days, cut-off times, holidays). This dashboard is a reminder tool, not legal advice. Ask your broker or attorney when in doubt.</div></div>\n <div class=\"filters noprint\" id=\"filters\"></div>\n <section class=\"card\" style=\"padding:20px\" id=\"upcoming\"></section>\n <div class=\"deals\" id=\"deals\"></div>\n <footer id=\"foot\"></footer>\n</div>\n<nav class=\"dock\"><button class=\"btn\" data-act=\"ics\">Calendar</button><button class=\"btn\" data-act=\"csv\">CSV</button><button class=\"btn primary\" data-act=\"pdf\">Save PDF</button></nav>\n<script id=\"report-data\" type=\"application/json\">\n{\"meta\":{\"reportId\":\"deadlines\",\"example\":false,\"generated\":\"\",\"today\":\"\"},\"deals\":[{\"address\":\"\",\"side\":\"Buyer\",\"client\":\"\",\"effective\":\"\",\"closing\":\"\",\"deadlines\":[{\"name\":\"Earnest money due\",\"offset\":3,\"basis\":\"business\",\"status\":\"open\"},{\"name\":\"Inspection / option period ends\",\"offset\":10,\"basis\":\"calendar\",\"status\":\"open\"},{\"name\":\"Appraisal contingency\",\"offset\":17,\"basis\":\"calendar\",\"status\":\"open\"},{\"name\":\"Financing / loan approval\",\"offset\":21,\"basis\":\"calendar\",\"status\":\"open\"},{\"name\":\"Closing\",\"offset\":30,\"basis\":\"calendar\",\"status\":\"open\"}]}]}\n</script>\n<script>const esc=s=>String(s==null?'':s).replace(/[&<>\"]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;'}[c]));\nconst num=v=>{if(v==null)return NaN;const m=String(v).replace(/[$,\\s%]/g,'').match(/-?\\d*\\.?\\d+/);return m?parseFloat(m[0]):NaN};\nconst money=n=>isFinite(n)?(n<0?'−$':'$')+Math.round(Math.abs(n)).toLocaleString('en-US'):'—';\nconst getP=(o,p)=>p.split('.').reduce((a,k)=>a==null?a:a[k],o);\nfunction setP(o,path,val){const p=path.split('.');for(let i=0;i<p.length-1;i++)o=o[p[i]]=o[p[i]]||(isFinite(+p[i+1])?[]:{});o[p[p.length-1]]=val}\nconst chev='<svg class=\"chev\" viewBox=\"0 0 20 20\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><path d=\"M5 8l5 5 5-5\"/></svg>';\nfunction store(key){return{load(){try{return JSON.parse(localStorage.getItem(key))}catch(e){return null}},save(s){try{localStorage.setItem(key,JSON.stringify(s));return true}catch(e){return false}},clear(){try{localStorage.removeItem(key)}catch(e){}}}}\nfunction download(name,text,type){const a=document.createElement('a');a.href=URL.createObjectURL(new Blob([text],{type}));a.download=name;document.body.appendChild(a);a.click();a.remove()}\nconst csvq=v=>'\"'+String(v==null?'':v).replace(/\"/g,'\"\"')+'\"';\n\n(function(){\nconst BASE=JSON.parse(document.getElementById('report-data').textContent);\nconst ST=store('dd:'+(BASE.meta.reportId||'d')+':'+location.pathname);\nlet S=ST.load()||JSON.parse(JSON.stringify(BASE));let F='all';\nconst iso=d=>d.getFullYear()+'-'+String(d.getMonth()+1).padStart(2,'0')+'-'+String(d.getDate()).padStart(2,'0');\nconst parse=s=>{const m=/^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(s||'');return m?new Date(+m[1],m[2]-1,+m[3]):null};\nconst TODAY=parse(S.meta.today)||new Date(new Date().toDateString());\nconst nth=(y,m,wd,n)=>{const d=new Date(y,m,1);const off=(wd-d.getDay()+7)%7;return new Date(y,m,1+off+7*(n-1))};\nconst last=(y,m,wd)=>{const d=new Date(y,m+1,0);return new Date(y,m,d.getDate()-((d.getDay()-wd+7)%7))};\nconst obs=d=>{const w=d.getDay();return w===6?new Date(d.getFullYear(),d.getMonth(),d.getDate()-1):w===0?new Date(d.getFullYear(),d.getMonth(),d.getDate()+1):d};\nconst HC={};function hol(y){if(HC[y])return HC[y];const L=[new Date(y,0,1),nth(y,0,1,3),nth(y,1,1,3),last(y,4,1),new Date(y,5,19),new Date(y,6,4),nth(y,8,1,1),nth(y,9,1,2),new Date(y,10,11),nth(y,10,4,4),new Date(y,11,25)].map(obs).map(iso);return HC[y]=new Set(L)}\nconst isBiz=d=>d.getDay()!==0&&d.getDay()!==6&&!hol(d.getFullYear()).has(iso(d));\nfunction addDays(start,n,basis){const d=new Date(start);if(basis==='business'){let k=0;while(k<n){d.setDate(d.getDate()+1);if(isBiz(d))k++}}else{d.setDate(d.getDate()+n)}return d}\nfunction resolve(deal,x){const e=parse(deal.effective);if(x.offset!==''&&x.offset!=null&&e)return addDays(e,+x.offset,x.basis);return parse(x.date)}\nconst daysTo=d=>Math.round((d-TODAY)/864e5);\nconst MON=['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];\nfunction cls(dl,st){if(st!=='open')return'ok';if(dl<0)return'bad';if(dl<=3)return'warn';return''}\nfunction lbl(dl,st){if(st==='done')return['ok','Done'];if(st==='waived')return['ok','Waived'];if(st==='extended')return['info','Extended'];if(dl<0)return['bad',(-dl)+'d overdue'];if(dl===0)return['bad','Due today'];if(dl<=3)return['v','In '+dl+'d'];return['info','In '+dl+'d']}\nconst pill=d=>d?`<div class=\"dpill CLS\"><div class=\"m\">${MON[d.getMonth()]}</div><div class=\"d\">${d.getDate()}</div></div>`:'<div class=\"dpill\"><div class=\"m\">Set</div><div class=\"d\">?</div></div>';\nfunction all(){const out=[];S.deals.forEach((deal,di)=>(deal.deadlines||[]).forEach((x,xi)=>{const d=resolve(deal,x);out.push({deal,di,x,xi,d,dl:d?daysTo(d):NaN})}));return out}\nfunction render(){\n const A=all(),open=A.filter(a=>a.x.status==='open'&&a.d).sort((a,b)=>a.d-b.d);\n const over=open.filter(a=>a.dl<0).length,soon=open.filter(a=>a.dl>=0&&a.dl<=3).length,nx=open.find(a=>a.dl>=0)||open[0];\n document.getElementById('hero').innerHTML=`<div><span class=\"pill\">Deadline guardian</span>${S.meta.example?' <span class=\"pill ex\">EXAMPLE DATA · fictional</span>':''}\n <h1>Next deadline</h1><div class=\"addr\">${nx?esc(nx.x.name):'No open deadlines'}</div><div class=\"range\">${nx?esc(nx.deal.address||'')+' · '+nx.d.toDateString()+(nx.x.time?' · '+esc(nx.x.time):''):''}</div>\n <div class=\"val\">${nx?(nx.dl<0?(-nx.dl)+'d overdue':nx.dl===0?'Today':nx.dl+(nx.dl===1?' day':' days')):'All clear'}</div><div class=\"vallab\">As of ${TODAY.toDateString()}</div></div>\n <div class=\"stats\"><div class=\"stat\"><div class=\"k\">Overdue</div><div class=\"v\" style=\"color:${over?'#fca5a5':'#fff'}\">${over}</div></div><div class=\"stat\"><div class=\"k\">Due in 3 days</div><div class=\"v\" style=\"color:${soon?'#fcd34d':'#fff'}\">${soon}</div></div>\n <div class=\"stat\"><div class=\"k\">Open deadlines</div><div class=\"v\">${open.length}</div></div><div class=\"stat\"><div class=\"k\">Active deals</div><div class=\"v\">${S.deals.length}</div></div></div>`;\n const fl=[['all','All'],['week','Next 7 days'],['over','Overdue'],['done','Done / waived']];\n document.getElementById('filters').innerHTML=fl.map(([k,l])=>`<button data-f=\"${k}\" aria-pressed=\"${F===k}\">${l}</button>`).join('');\n const show=a=>F==='all'||(F==='week'&&a.x.status==='open'&&a.dl>=0&&a.dl<=7)||(F==='over'&&a.x.status==='open'&&a.dl<0)||(F==='done'&&a.x.status!=='open');\n const up=A.filter(a=>a.d&&show(a)&&(F!=='all'||a.x.status==='open')).sort((a,b)=>a.d-b.d).slice(0,8);\n document.getElementById('upcoming').innerHTML=`<h2>${F==='all'?'Coming up across all deals':fl.find(f=>f[0]===F)[1]}</h2><ul class=\"up\" style=\"margin-top:8px\">${up.length?up.map(a=>{const [tc,tl]=lbl(a.dl,a.x.status);return`<li>${pill(a.d).replace('CLS',cls(a.dl,a.x.status))}<div><div class=\"n\">${esc(a.x.name)}</div><div class=\"a\">${esc(a.deal.address||'Deal '+(a.di+1))}${a.x.owner?' · '+esc(a.x.owner):''}</div></div><span class=\"tag ${tc}\">${tl}</span></li>`}).join(''):'<li style=\"display:block;color:var(--muted)\">Nothing here.</li>'}</ul>`;\n document.getElementById('deals').innerHTML=S.deals.map((deal,di)=>{const cd=parse(deal.closing);const c=cd?daysTo(cd):NaN;\n  return `<details class=\"card deal\" open><summary><span class=\"ttl\">${esc(deal.address||'New deal')}<small>${esc(deal.side||'')} side${deal.client?' · '+esc(deal.client):''}${isFinite(c)?' · closing in '+c+'d':''}</small></span>${chev}</summary><div class=\"body\">\n  <div class=\"dealhead noprint\"><label>Address<input class=\"in\" data-d=\"${di}.address\" value=\"${esc(deal.address)}\"></label><label>Client<input class=\"in\" data-d=\"${di}.client\" value=\"${esc(deal.client)}\"></label>\n  <label>Effective (acceptance) date<input class=\"in\" type=\"date\" data-d=\"${di}.effective\" value=\"${esc(deal.effective)}\"></label><label>Scheduled closing<input class=\"in\" type=\"date\" data-d=\"${di}.closing\" value=\"${esc(deal.closing)}\"></label></div>\n  ${(deal.deadlines||[]).map((x,xi)=>{const a=A.find(z=>z.di===di&&z.xi===xi);if(!show(a))return'';const [tc,tl]=lbl(a.dl,x.status);const auto=x.offset!==''&&x.offset!=null;\n   return `<div class=\"dl ${x.status!=='open'?'done':''}\"><div class=\"dlh\">${pill(a.d).replace('CLS',cls(a.dl,x.status))}<div><div class=\"n\">${esc(x.name)}</div><div class=\"s\">${auto?x.offset+' '+x.basis+' days from effective':'Fixed date'}${x.time?' · by '+esc(x.time):''}${x.owner?' · '+esc(x.owner):''}</div></div><span class=\"tag ${tc}\">${tl}</span></div>\n   ${x.status==='open'&&a.dl<=3&&x.ifMissed?`<div class=\"risk\"><b>If missed:</b> ${esc(x.ifMissed)}</div>`:''}\n   <details class=\"noprint\"><summary>Edit</summary><div class=\"dle\">\n    <label>Status<select class=\"in\" data-x=\"${di}.${xi}.status\">${['open','done','waived','extended'].map(s=>`<option ${x.status===s?'selected':''}>${s}</option>`).join('')}</select></label>\n    <label>Date${auto?' (auto)':''}<input class=\"in\" type=\"date\" data-x=\"${di}.${xi}.date\" value=\"${a.d?iso(a.d):''}\"></label>\n    <label>Days from effective<input class=\"in\" inputmode=\"numeric\" data-x=\"${di}.${xi}.offset\" value=\"${auto?x.offset:''}\"></label>\n    <label>Count<select class=\"in\" data-x=\"${di}.${xi}.basis\"><option ${x.basis!=='business'?'selected':''}>calendar</option><option ${x.basis==='business'?'selected':''}>business</option></select></label>\n    <label class=\"full\">If missed<input class=\"in\" data-x=\"${di}.${xi}.ifMissed\" value=\"${esc(x.ifMissed)}\"></label></div></details></div>`}).join('')}\n  </div></details>`}).join('');\n document.getElementById('foot').innerHTML=`Business days skip weekends and US federal holidays (observed). Your contract may define days, holidays and cut-off times differently.${S.meta.generated?' Generated '+esc(S.meta.generated)+'.':''} <span id=\"saved\"></span>`;\n}\nlet t;function save(){clearTimeout(t);t=setTimeout(()=>{const ok=ST.save(S);const el=document.getElementById('saved');if(el)el.textContent=ok?'Autosaved '+new Date().toLocaleTimeString():'Autosave unavailable'},300)}\nfunction keepOpen(fn){const op=[...document.querySelectorAll('details')].map(d=>d.open);const ae=document.activeElement&&document.activeElement.dataset;const key=ae&&(ae.x||ae.d);fn();const ds=document.querySelectorAll('details');op.forEach((o,i)=>{if(ds[i])ds[i].open=o});if(key){const el=document.querySelector(`[data-x=\"${key}\"],[data-d=\"${key}\"]`);if(el){el.focus();}}}\ndocument.addEventListener('change',e=>{const el=e.target;if(el.dataset.d){const [di,k]=el.dataset.d.split('.');S.deals[di][k]=el.value}\n else if(el.dataset.x){const [di,xi,k]=el.dataset.x.split('.');const x=S.deals[di].deadlines[xi];if(k==='date'){x.date=el.value;x.offset=''}else if(k==='offset'){x.offset=el.value===''?'':+el.value}else x[k]=el.value}else return;\n keepOpen(render);save()});\ndocument.addEventListener('click',e=>{const f=e.target.closest('[data-f]');if(f){F=f.dataset.f;keepOpen(render);return}\n const b=e.target.closest('[data-act]');if(!b)return;const a=b.dataset.act;if(a==='pdf')window.print();\n if(a==='csv'){const rows=[['address','side','deadline','date','basis','offset','status','days_left','owner','if_missed']];all().forEach(z=>rows.push([z.deal.address,z.deal.side,z.x.name,z.d?iso(z.d):'',z.x.basis,z.x.offset,z.x.status,isFinite(z.dl)?z.dl:'',z.x.owner,z.x.ifMissed]));download('deadlines.csv',rows.map(r=>r.map(csvq).join(',')).join('\\n'),'text/csv')}\n if(a==='ics'){const st=new Date().toISOString().replace(/[-:]/g,'').split('.')[0]+'Z';const icx=s=>String(s||'').replace(/[\\\\,;]/g,m=>'\\\\'+m).replace(/\\n/g,'\\\\n');let L=['BEGIN:VCALENDAR','VERSION:2.0','PRODID:-//Grok Bot//Deadline Guardian//EN','CALSCALE:GREGORIAN'];\n  all().filter(z=>z.d&&z.x.status==='open').forEach((z,i)=>{const d=iso(z.d).replace(/-/g,''),n=new Date(z.d);n.setDate(n.getDate()+1);L.push('BEGIN:VEVENT','UID:dg-'+i+'-'+d+'@grokbot','DTSTAMP:'+st,'DTSTART;VALUE=DATE:'+d,'DTEND;VALUE=DATE:'+iso(n).replace(/-/g,''),'SUMMARY:'+icx('⚠ '+z.x.name+' – '+(z.deal.address||'')),'DESCRIPTION:'+icx((z.x.time?'Due by '+z.x.time+'. ':'')+(z.x.ifMissed?'If missed: '+z.x.ifMissed:'')+' Verify against the contract.'),'BEGIN:VALARM','ACTION:DISPLAY','DESCRIPTION:Deadline tomorrow','TRIGGER:-P1D','END:VALARM','BEGIN:VALARM','ACTION:DISPLAY','DESCRIPTION:Deadline in 3 days','TRIGGER:-P3D','END:VALARM','END:VEVENT')});\n  L.push('END:VCALENDAR');download('deadlines.ics',L.join('\\r\\n'),'text/calendar')}});\nwindow.addEventListener('beforeprint',()=>document.querySelectorAll('details').forEach(d=>d.open=true));\nwindow.__doc={get state(){return S},render,all,addDays,iso};render();\n})();\n</script></body></html>\n"
    }
  ]
}