{
  "name": "Price Reduction Negotiator",
  "category": "Real Estate",
  "description": "Helps you guide sellers to a realistic price with evidence instead of pressure. It analyzes days on market, showings, feedback and the competition, builds a side-by-side net sheet showing what waiting really costs, and scripts the conversation with empathy. You get a bold one-page Pricing Review you can share or print.",
  "instructions": "FIRST CONVERSATION: Before anything else, send exactly this opening message and wait for the answer: \"Hi! I'll help you have the price conversation with your seller, backed by data. Which listing is it, and how many days has it been on the market?\" Then follow WORKFLOW one step at a time.\n\nNO ROUTINE OFFER: This is a one-off bot per listing conversation. Don't offer a recurring routine. When done, say they can come back anytime a listing needs a pricing review.\n\nROLE: A pricing-conversation coach. Your job is to help the seller make an informed decision, not to pressure them. The decision is always the seller's. You prepare the evidence, the math and a respectful script.\n\nDATA: The agent's listing activity (list date, price history, showings, online views if the agent has them, feedback, offers), an MLS CSV of active, pending, sold and expired competition, county records, and the seller's payoff and monthly holding costs (mortgage, taxes, insurance, utilities, HOA). Never scrape, crawl or automate Zillow, Redfin, Realtor.com or the MLS website. Use only the user's own MLS exports, county public records, data the user pastes, and permitted APIs. Never bypass logins, paywalls or CAPTCHAs.\nOPTIONAL RENTCAST UPGRADE: The bot works fully without it, using county assessor/recorder records, the user's own MLS export CSV, or listing text the user pastes. If the user wants automatic property records, value estimates and comps, they can add their own RentCast API key (free Developer plan: 50 requests a month; paid plans from $74/month for 1,000) as a bot secret named RENTCAST_API_KEY (bot settings > Secrets). Never ask them to paste the key in chat, never echo it, and never write it to files or the tracker. Endpoints (header X-Api-Key): GET https://api.rentcast.io/v1/properties?address=... (records), GET /v1/avm/value?address=...&compCount=10 (value estimate with comps), GET /v1/listings/sale?... (active sale listings gathered from public sources). Count calls in the tracker and warn before a batch would exceed 50 a month on the free plan. On a 401 error, ask them to re-enter the key in Secrets; on a 429 error or quota limit, fall back to county records or CSV. Label RentCast values as automated estimates, not appraisals.\n\nEVIDENCE TO ASSEMBLE: DOM vs area median; showings per week; feedback themes (price, condition, location); the subject's $/sq ft vs sold and pending comps; active competitors priced below with better features; months of supply; sale-to-list ratio; and the price bands buyers search ($25k steps). Note published research the agent can cite, such as Realtor.com's 2026 finding that homes selling after 18 weeks get worse sale-to-list results than those selling in about 4 weeks. Always cite the source and date.\n\nCOMPLIANCE AND ETHICS: Fiduciary duty: put the seller's interests first; disclose that the agent's own incentive also favors a sale; never threaten, guilt or misrepresent buyer feedback or offers. Pricing is a broker opinion, not an appraisal. Fees are negotiable. The net sheet is an estimate; the title or escrow company provides official figures. Fair-housing-safe language only. Any price change needs the seller's signed authorization (a listing amendment).\n\nWORKFLOW:\n1) Intake: address, list date, current and original price, showings, feedback, offers, the seller's goal and motivation, payoff and holding costs.\n2) Diagnose: price, condition or marketing? If showings are low, it's likely price or exposure; if showings are high but there are no offers, it's price vs condition. Be honest when the issue is marketing or photos rather than price.\n3) Options: build three scenarios (stay; adjust to market; price to spark offers). Each gets a list price, a likely sale price (from sale-to-list, competition and how long it sits), expected months to sell, net at closing, holding costs and net after holding costs. Offer non-price alternatives: a buyer-incentive credit, a rate buydown, staging, pre-inspection, better photos.\n4) Script: an empathetic conversation guide (set the agenda; share what the market is saying with facts; ask \"What would you like to happen by <date>?\"; show the options; let the seller choose; confirm next steps in writing). Add objection handlers for \"we're not giving it away\", \"we'll wait for the right buyer\", \"Zillow says it's worth more\", \"the neighbor got more\", \"you just want a quick sale\" and \"we'll pull it off the market\".\n5) Build the one-pager: load the skill \"price-review-net-sheet-template\", copy its HTML exactly, and replace ONLY the JSON inside <script id=\"report-data\" type=\"application/json\"> (meta, property, seller, listing, market, feedback[], comps[], costs, scenarios[{name,price,sale,months}], recommended). Save to /workspace/pricing/<address-slug>-<date>.html and give the user the file.\n6) Draft a recap email for after the conversation, plus a price-change authorization checklist (the brokerage's amendment form is signed by the seller).\n\nRULES: Never invent feedback, showings or offers. Never push a specific number as the 'right' one; give a range with reasons. Nothing is sent without approval. Report only real actions.\n\nACTIVITY TRACKER: keep a log at /workspace/trackers/price-reduction-negotiator.csv (create it with this header row if it doesn't exist: date,address,seller,list_price,dom,showings,recommended_price,file,conversation_status,seller_decision,next_action,notes). Append a row for every analysis, one-pager build, script, conversation outcome and seller decision. 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- \"88 Ridge Dr, 63 days, 6 showings, $449k\" -> a diagnosis plus what's missing.\n- \"Here's the competition\" (CSV) -> evidence cards and $/sf comparison.\n- \"Build the pricing review\" -> a one-page HTML with 3 scenarios and live net math.\n- \"Script the call; they think it's worth more because of the remodel\" -> an empathetic script plus objection handling.",
  "whatItDoes": [
    "Diagnoses whether the problem is price, condition or marketing, and says so honestly",
    "Builds evidence: DOM vs area, showings per week, $/sq ft vs comps, feedback themes, competition",
    "Calculates 3 scenarios: likely sale price, months to sell, net at closing and net after holding costs",
    "Suggests non-price options too (incentives, rate buydown, staging, photos)",
    "Scripts an empathetic seller conversation with objection handlers",
    "Produces a bold one-page Pricing Review (editable, live math, CSV export, print-to-PDF)",
    "Optional RentCast key for records and value estimates; works with your MLS CSV",
    "Keeps it ethical: the seller decides, with signed authorization for any change"
  ],
  "gettingStarted": [
    "Click 'Add to Grok Bot' on this page (or import the template JSON) and open the bot.",
    "Export active, pending, sold and expired competition for the listing from your MLS as a CSV.",
    "Optional: get a free RentCast API key at rentcast.io/api (50 requests a month) and add it in the bot's Secrets as RENTCAST_API_KEY. Without it, the bot uses county records, your MLS CSV or pasted listings.",
    "Gather showings, feedback, offers, and the seller's payoff and monthly holding costs.",
    "Answer the first question with the listing and its days on market, then the follow-ups.",
    "Open the Pricing Review it sends. Tap any number to edit and tap a scenario to recommend it. Export CSV or Save PDF.",
    "It logs each review to /workspace/trackers/price-reduction-negotiator.csv."
  ],
  "howToUse": [
    "First message: when you open the bot it greets you with: \"Hi! I'll help you have the price conversation with your seller, backed by data. Which listing is it, and how many days has it been on the market?\"",
    "\"88 Ridge Dr, 63 days, 6 showings, $449k\". You get: a diagnosis plus what's missing.",
    "\"Here's the competition\" (CSV). You get: evidence cards and $/sf comparison.",
    "\"Build the pricing review\". You get: a one-page HTML with 3 scenarios and live net math.",
    "\"Script the call; they think it's worth more because of the remodel\". You get: an empathetic script plus objection handling."
  ],
  "activityTracker": {
    "path": "/workspace/trackers/price-reduction-negotiator.csv",
    "columns": [
      "date",
      "address",
      "seller",
      "list_price",
      "dom",
      "showings",
      "recommended_price",
      "file",
      "conversation_status",
      "seller_decision",
      "next_action",
      "notes"
    ],
    "starterCsv": "date,address,seller,list_price,dom,showings,recommended_price,file,conversation_status,seller_decision,next_action,notes\n",
    "updateRule": "Append a row after every action; review open rows at session start."
  },
  "faq": [
    {
      "question": "Do I need RentCast or any paid data?",
      "answer": "No. It works with county records, your own MLS export CSV or listing text you paste. Optionally add your own RentCast key (free plan: 50 requests a month) as the secret RENTCAST_API_KEY for automatic property records, value estimates and comps."
    },
    {
      "question": "Does it scrape Zillow, Redfin, Realtor.com or my MLS?",
      "answer": "No. Those sites' terms forbid automated scraping. It uses your own MLS exports, county public records, data you paste and permitted APIs."
    },
    {
      "question": "Will it pressure my seller?",
      "answer": "No. It lays out evidence and options, includes non-price alternatives, and leaves the decision to the seller."
    },
    {
      "question": "Is the net sheet official?",
      "answer": "No. It's an estimate for discussion. Your title or escrow company provides the official figures."
    },
    {
      "question": "How is this different from the CMA Writer?",
      "answer": "The CMA Writer sets the initial price. This bot handles listings already on the market that aren't selling: diagnosis, holding-cost math and the conversation."
    }
  ],
  "tags": [
    "real estate",
    "price reduction",
    "seller conversation",
    "net sheet",
    "pricing",
    "realtor"
  ],
  "openingMessage": "Hi! I'll help you have the price conversation with your seller, backed by data. Which listing is it, and how many days has it been on the market?",
  "skills": [
    {
      "name": "price-review-net-sheet-template",
      "description": "Editable HTML pricing review and net sheet one-pager. Copy it exactly and replace only the JSON inside <script id=\"report-data\">.",
      "fileName": "price-reduction-net-sheet-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>Pricing Review &amp; Net Sheet</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.hero [contenteditable]{background:transparent;color:#fff;padding:2px 6px;margin-left:-6px;min-height:0;font-size:inherit;font-weight:inherit;border:1px dashed transparent}.hero [contenteditable]:focus{background:#ffffff14;border-color:#a5b4fc;box-shadow:none}.hero h1 [contenteditable]{display:inline-block;color:#e0e7ff}\n.ev{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}\n.evc{border:1px solid var(--line);border-radius:16px;padding:16px;background:#fff}\n.evc .k{font-size:12px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em}\n.evc .v{font-size:26px;font-weight:850;letter-spacing:-.02em;margin-top:2px;font-variant-numeric:tabular-nums}\n.evc .s{font-size:13px;color:var(--muted)}\n.meter{height:10px;border-radius:99px;background:var(--line2);margin-top:10px;position:relative;overflow:hidden}.meter i{position:absolute;left:0;top:0;bottom:0;border-radius:99px;background:var(--accent)}.meter i.warn{background:#f59e0b}.meter i.bad{background:#ef4444}\n.scen{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}\n.sc{border:1px solid var(--line);border-radius:18px;padding:18px;background:#fff;cursor:pointer;position:relative}\n.sc.rec{border:2px solid var(--accent);box-shadow:0 12px 28px -16px #4f46e5}\n.sc .badge{position:absolute;top:-11px;left:16px;background:var(--accent);color:#fff;font-size:11px;font-weight:800;padding:3px 10px;border-radius:99px;display:none}.sc.rec .badge{display:block}\n.sc [contenteditable].nm{font-weight:800;font-size:14px;background:transparent;padding:2px 0;min-height:28px;color:var(--ink2)}\n.sc [contenteditable].p{font-size:30px;font-weight:850;letter-spacing:-.02em;background:transparent;padding:2px 0}\n.mini{display:flex;justify-content:space-between;font-size:14px;padding:8px 0;border-bottom:1px solid var(--line2)}.mini b{font-variant-numeric:tabular-nums}\n.mini [contenteditable]{min-height:32px;padding:4px 8px;width:auto;min-width:72px;white-space:nowrap;text-align:right;font-size:14px}\n.eff{margin-top:10px;font-size:13px;color:var(--muted);font-weight:650}.eff b{display:block;font-size:24px;color:var(--ink);font-weight:850;font-variant-numeric:tabular-nums}\n.bars{margin-top:12px}.barrow{display:grid;grid-template-columns:120px 1fr 110px;gap:10px;align-items:center;font-size:13px;padding:6px 0}.barrow .tr{height:14px;background:var(--line2);border-radius:99px;overflow:hidden}.barrow .tr i{display:block;height:100%;background:var(--accent);border-radius:99px}.barrow .tr i.dim{background:#c7d2fe}\n.fb{list-style:none;padding:0;margin:0;display:grid;gap:8px}.fb li{display:flex;gap:10px;align-items:flex-start}.fb li::before{content:\"“\";font-size:28px;line-height:1;color:var(--accent);font-weight:900}.fb [contenteditable]{flex:1}\n.cmp{width:100%;border-collapse:collapse;font-size:14px}.cmp th,.cmp td{padding:8px;border-bottom:1px solid var(--line2);text-align:left}.cmp th{font-size:12px;color:var(--muted);text-transform:uppercase;letter-spacing:.05em}.cmp [contenteditable]{min-height:36px;padding:6px 8px;font-size:14px}\n@media (max-width:899px){.ev,.scen{grid-template-columns:1fr}.barrow{grid-template-columns:90px 1fr 92px}\n .cmp thead{display:none}.cmp tr{display:grid;grid-template-columns:1fr 1fr;gap:4px 10px;padding:10px 0;border-bottom:1px solid var(--line2)}.cmp td{border:0;padding:0}.cmp td:first-child{grid-column:1/-1}.cmp td[data-l]::before{content:attr(data-l);display:block;font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase}}\n@media print{.sc.rec{border:2px solid #000}.sc .badge{display:block;border:1px solid #000}.evc,.sc{border:1px solid #000;break-inside:avoid}.meter,.barrow .tr{border:1px solid #000}.meter i,.barrow .tr i{background:#000!important;-webkit-print-color-adjust:exact;print-color-adjust:exact}}\n</style></head><body>\n<div class=\"wrap\">\n <div class=\"top\"><div class=\"brand\"><div class=\"logo\">P</div><span>Pricing Review &amp; Net Sheet</span></div>\n  <div class=\"actions\"><button class=\"btn\" data-act=\"reset\">Reset</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>Estimate for discussion.</b> Figures are approximate and not an appraisal, legal or tax advice. Your title or escrow company provides the official net sheet. The decision to change the price is always yours.</div></div>\n <main id=\"main\"></main><footer id=\"foot\"></footer>\n</div>\n<nav class=\"dock\"><button class=\"btn\" data-act=\"reset\">Reset</button><button class=\"btn\" data-act=\"csv\">Export CSV</button><button class=\"btn primary\" data-act=\"pdf\">Save PDF</button></nav>\n<script id=\"report-data\" type=\"application/json\">\n{\"meta\":{\"reportId\":\"netsheet\",\"example\":false,\"generated\":\"\"},\"property\":{},\"seller\":{},\"listing\":{},\"market\":{},\"feedback\":[\"\",\"\",\"\"],\"comps\":[{},{},{}],\"costs\":{\"closingPct\":\"1.5\"},\"scenarios\":[{\"name\":\"Stay at current price\"},{\"name\":\"Adjust to\"},{\"name\":\"Adjust to\"}],\"recommended\":1}\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('ns:'+(BASE.meta.reportId||'n')+':'+location.pathname);\nlet S=ST.load()||JSON.parse(JSON.stringify(BASE));\nconst ce=(p,cls)=>`<div contenteditable=\"true\" role=\"textbox\" data-path=\"${p}\" class=\"${cls||''}\">${esc(getP(S,p))}</div>`;\nfunction render(){\n const L=S.listing||{},M=S.market||{};\n document.getElementById('hero').innerHTML=`<div><span class=\"pill\">Pricing review</span>${S.meta.example?' <span class=\"pill ex\">EXAMPLE DATA · fictional</span>':''}\n <h1>Prepared for ${ce('seller.names')}</h1><div class=\"addr\">${ce('property.address')}</div>\n <div class=\"val\" id=\"hVal\">—</div><div class=\"vallab\" id=\"hLab\">Suggested price</div><div class=\"range\" id=\"hDelta\"></div></div>\n <div class=\"stats\"><div class=\"stat\"><div class=\"k\">Current list price</div><div class=\"v\" id=\"hList\">—</div></div>\n <div class=\"stat\"><div class=\"k\">Your days on market</div><div class=\"v\" id=\"hDom\">—</div></div>\n <div class=\"stat\"><div class=\"k\">Area median DOM</div><div class=\"v\" id=\"hArea\">—</div></div>\n <div class=\"stat\"><div class=\"k\">Showings</div><div class=\"v\" id=\"hShow\">—</div></div></div>`;\n let h=`<section class=\"card\" style=\"padding:20px\"><h2>What the market is telling us</h2><p class=\"hint\">Data source: ${ce('market.source')}</p><div class=\"ev\">\n  <div class=\"evc\"><div class=\"k\">Days on market vs area</div><div class=\"v\" id=\"eDom\">—</div><div class=\"meter\"><i id=\"mDom\"></i></div><div class=\"s\" id=\"sDom\"></div></div>\n  <div class=\"evc\"><div class=\"k\">Showings per week</div><div class=\"v\" id=\"eShow\">—</div><div class=\"meter\"><i id=\"mShow\"></i></div><div class=\"s\">Healthy listings usually see several a week in a balanced market.</div></div>\n  <div class=\"evc\"><div class=\"k\">Your $/sq ft vs sold comps</div><div class=\"v\" id=\"ePsf\">—</div><div class=\"meter\"><i id=\"mPsf\"></i></div><div class=\"s\" id=\"sPsf\"></div></div></div>\n  <div class=\"rows\" style=\"margin-top:8px\">${[['listing.listPrice','Current list price $'],['listing.daysOnMarket','Days on market'],['listing.showings','Showings so far'],['listing.offers','Offers received'],['property.sqft','Home sq ft'],['market.areaDom','Area median days on market'],['market.monthsSupply','Months of supply'],['market.saleToList','Area sale-to-list %']].map(([p,l])=>`<div class=\"row\"><div class=\"lab\">${l}</div>${ce(p)}</div>`).join('')}</div></section>`;\n h+=`<details class=\"card\" open><summary><span class=\"ttl\">Buyer feedback</span>${chev}</summary><div class=\"body\"><ul class=\"fb\" style=\"margin-top:12px\">${(S.feedback||[]).map((f,i)=>`<li>${ce('feedback.'+i)}</li>`).join('')}</ul></div></details>`;\n h+=`<details class=\"card\" open><summary><span class=\"ttl\">Competition &amp; recent sales</span>${chev}</summary><div class=\"body\"><table class=\"cmp\"><thead><tr><th>Address</th><th>Status</th><th>Price</th><th>Sq ft</th><th>DOM</th></tr></thead><tbody>${(S.comps||[]).map((c,i)=>`<tr><td>${ce('comps.'+i+'.address')}</td><td data-l=\"Status\">${ce('comps.'+i+'.status')}</td><td data-l=\"Price\">${ce('comps.'+i+'.price')}</td><td data-l=\"Sq ft\">${ce('comps.'+i+'.sqft')}</td><td data-l=\"DOM\">${ce('comps.'+i+'.dom')}</td></tr>`).join('')}</tbody></table></div></details>`;\n h+=`<section class=\"card\" style=\"padding:20px\"><h2>Your options, side by side</h2><p class=\"hint\">Tap a card to mark the one we're discussing. \"Likely sale price\" is what we expect it to actually sell for at that list price (based on sale-to-list and how long it sits). \"Months\" is how long we expect it to take. Holding costs add up every month.</p><div class=\"scen\">\n  ${S.scenarios.map((s,i)=>`<div class=\"sc ${S.recommended==i?'rec':''}\" data-sc=\"${i}\"><span class=\"badge\">Recommended</span>${ce('scenarios.'+i+'.name','nm')}${ce('scenarios.'+i+'.price','p')}\n  <div class=\"mini\"><span>Likely sale price</span>${ce('scenarios.'+i+'.sale')}</div><div class=\"mini\"><span>Expected months to sell</span>${ce('scenarios.'+i+'.months')}</div>\n  <div class=\"mini\"><span>Net at closing</span><b data-c=\"g${i}\">—</b></div><div class=\"mini\"><span>Holding costs</span><b data-c=\"h${i}\">—</b></div>\n  <div class=\"eff\">Net after holding costs<b data-c=\"e${i}\">—</b></div></div>`).join('')}</div>\n  <div class=\"bars\" id=\"bars\"></div></section>`;\n h+=`<details class=\"card\"><summary><span class=\"ttl\">Costs used in the math</span>${chev}</summary><div class=\"body\"><div class=\"rows\">${[['costs.payoff','Mortgage payoff $'],['costs.listingFeePct','Listing brokerage fee %'],['costs.buyerSidePct','Buyer-agent comp / concessions %'],['costs.closingPct','Other closing costs %'],['costs.monthlyCarry','Monthly holding cost $ (mortgage, taxes, insurance, utilities, HOA)']].map(([p,l])=>`<div class=\"row\"><div class=\"lab\">${l}</div>${ce(p)}</div>`).join('')}</div><p class=\"hint\">Fees and commissions are negotiable and not set by law.</p></div></details>`;\n document.getElementById('main').innerHTML=h;\n document.getElementById('foot').innerHTML=`Market data: ${esc(M.source||'agent-provided MLS export')}; deemed reliable but not guaranteed. Estimates only, not an appraisal. Equal Housing Opportunity.${S.meta.generated?' Prepared '+esc(S.meta.generated)+'.':''} <span id=\"saved\"></span>`;\n calc();\n}\nfunction calc(){\n const L=S.listing||{},M=S.market||{},C=S.costs||{},$=i=>document.getElementById(i),q=s=>document.querySelector(s);\n const fee=((num(C.listingFeePct)||0)+(num(C.buyerSidePct)||0)+(num(C.closingPct)||0))/100,pay=num(C.payoff)||0,carry=num(C.monthlyCarry)||0;\n const R=S.scenarios.map(s=>{const lp=num(s.price),sp=num(s.sale),p=isFinite(sp)?sp:lp,m=num(s.months)||0,g=p-pay-p*fee,hc=carry*m;return{p:lp,g,hc,e:g-hc}});\n R.forEach((r,i)=>{q(`[data-c=\"g${i}\"]`).textContent=money(r.g);q(`[data-c=\"h${i}\"]`).textContent=money(-r.hc);q(`[data-c=\"e${i}\"]`).textContent=money(r.e)});\n const valid=R.filter(r=>isFinite(r.e)),mx=Math.max(...valid.map(r=>r.e),1);\n $('bars').innerHTML=R.map((r,i)=>`<div class=\"barrow\"><span>${esc(S.scenarios[i].name||'Option '+(i+1))}</span><div class=\"tr\"><i class=\"${S.recommended==i?'':'dim'}\" style=\"width:${isFinite(r.e)&&r.e>0?r.e/mx*100:0}%\"></i></div><b class=\"num\">${money(r.e)}</b></div>`).join('');\n const rec=R[S.recommended]||{},base=R[0]||{};\n $('hVal').textContent=money(rec.p);$('hLab').textContent=S.recommended==0?'Current price (stay)':'Suggested adjusted price';\n const d=rec.e-base.e;$('hDelta').textContent=S.recommended!=0&&isFinite(d)?(d>=0?'About '+money(d)+' more in your pocket after holding costs than staying put':'About '+money(-d)+' less after holding costs than staying put, but likely to sell faster'):'';\n const lp=num(L.listPrice),dom=num(L.daysOnMarket),ad=num(M.areaDom),sh=num(L.showings);\n $('hList').textContent=money(lp);$('hDom').textContent=isFinite(dom)?dom+' days':'—';$('hArea').textContent=isFinite(ad)?ad+' days':'—';$('hShow').textContent=isFinite(sh)?sh:'—';\n const ratio=dom/ad;$('eDom').textContent=isFinite(ratio)?ratio.toFixed(1)+'×':'—';$('mDom').style.width=Math.min(100,(ratio||0)/3*100)+'%';$('mDom').className=ratio>2?'bad':ratio>1.2?'warn':'';\n $('sDom').textContent=isFinite(ratio)?(ratio>1.2?'Longer than typical. Buyers notice stale listings.':'In line with the market.'):'';\n const wk=dom/7,spw=sh/(wk||1);$('eShow').textContent=isFinite(spw)?spw.toFixed(1):'—';$('mShow').style.width=Math.min(100,(spw||0)/5*100)+'%';$('mShow').className=spw<1?'bad':spw<2.5?'warn':'';\n const sold=(S.comps||[]).filter(c=>/sold|closed/i.test(c.status||'')).map(c=>num(c.price)/num(c.sqft)).filter(isFinite);const sqft=num((S.property||{}).sqft);\n if(sold.length&&isFinite(sqft)&&isFinite(lp)){const med=sold.sort((a,b)=>a-b)[Math.floor(sold.length/2)],mine=lp/sqft,diff=(mine/med-1)*100;$('ePsf').textContent=(diff>=0?'+':'')+diff.toFixed(0)+'%';$('mPsf').style.width=Math.min(100,Math.max(0,50+diff*2.5))+'%';$('mPsf').className=diff>8?'bad':diff>3?'warn':'';$('sPsf').textContent='You: $'+Math.round(mine)+'/sf · sold median: $'+Math.round(med)+'/sf'}\n else{$('ePsf').textContent='—';$('sPsf').textContent='Add sold comps with price and sq ft.'}\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)}\ndocument.addEventListener('input',e=>{const el=e.target.closest('[data-path]');if(!el)return;setP(S,el.dataset.path,el.innerText.trim());calc();save()});\ndocument.addEventListener('click',e=>{const sc=e.target.closest('[data-sc]');if(sc&&!e.target.closest('[contenteditable]')){S.recommended=+sc.dataset.sc;document.querySelectorAll('.sc').forEach(x=>x.classList.toggle('rec',x===sc));calc();save();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==='reset'&&confirm('Discard edits?')){ST.clear();S=JSON.parse(JSON.stringify(BASE));render()}\n if(a==='csv'){const rows=[['option','list_price','likely_sale_price','expected_months','net_at_closing','holding_costs','net_after_holding','recommended']];S.scenarios.forEach((s,i)=>rows.push([s.name,s.price,s.sale,s.months,document.querySelector(`[data-c=\"g${i}\"]`).textContent,document.querySelector(`[data-c=\"h${i}\"]`).textContent,document.querySelector(`[data-c=\"e${i}\"]`).textContent,S.recommended==i?'yes':'']));download('pricing-review.csv',rows.map(r=>r.map(csvq).join(',')).join('\\n'),'text/csv')}});\nwindow.addEventListener('beforeprint',()=>document.querySelectorAll('details').forEach(d=>d.open=true));\nwindow.__doc={get state(){return S},calc};render();\n})();\n</script></body></html>\n"
    }
  ]
}