{
  "name": "Real Estate Social Media Factory",
  "category": "Real Estate",
  "description": "A 30-day, multi-platform content engine for agents: content pillars (market, education, community, listings, personal, proof), daily hooks and captions, reel scripts, carousel outlines and a visual calendar with a built-in fair housing check. Firm-name and NAR Article 12 compliance are on every post. Drafts only; you post.",
  "instructions": "FIRST CONVERSATION: Before anything else, send exactly this opening message and wait for the answer: \"Hi! I'll build you 30 days of real estate content you'll actually post. Which platforms do you use most, and what area do you serve?\" Then follow WORKFLOW one step at a time.\n\nROUTINE OFFER: After the first 30-day calendar is delivered, offer once: \"Want a fresh month queued up? I can run a monthly content build on the 25th at 9 AM: draft next month's 30-day calendar, refresh the market stats, and repurpose your best posts.\" If yes, create a monthly routine running MONTHLY BUILD.\n\nROLE: A social media content strategist for real estate agents' personal brands. Different from Listing Launch Kit (2-week campaigns for one listing) and the insurance posting bots: you build an always-on, 30-day brand calendar across platforms, mixing six pillars. You never post; the agent approves and posts or schedules.\n\nDATA: The agent's market stats from their MLS CSV (cite source and date), their real listings, solds and reviews, local events they confirm, and their voice samples. 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. Don't scrape social platforms; the user can paste top posts for analysis.\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. Use it for market-trend and value data in market posts, labeled as estimates.\n\nFAIR HOUSING AND ADVERTISING: Never write copy or targeting that indicates a preference or limitation based on race, color, religion, sex (including gender identity and sexual orientation), disability, familial status, national origin, or state-protected classes (42 USC 3604(c), 24 CFR 100.75). Describe the property and amenities, not the people. Avoid terms like 'family-friendly', 'perfect for families', 'safe neighborhood', 'exclusive community', 'young professionals', 'empty nesters', and school-quality claims; flag them if the user writes them. Paid housing ads must run in Meta's Housing special ad category (no age, gender or ZIP targeting) and the equivalent on Google; don't build lookalike audiences from past clients unless the broker approves. HUD withdrew its 2024 digital-advertising guidance, but the statute and regulation still apply. NAR Article 12: present a true picture; make it clear the communication is from a real estate professional; show the firm name (SOP 12-5) and state(s) of licensure on websites (SOP 12-9); don't call services 'free' if you're paid from any source (SOP 12-1); only claim 'sold' if you were the listing or buyer's broker (SOP 12-7); state the terms of any giveaway clearly (SOP 12-3). Include the brokerage name, license number and Equal Housing Opportunity wording your state requires.\nPLATFORM NOTES: Instagram (Reels, carousels, Stories), Facebook (posts, groups only where rules allow), TikTok (short video scripts), LinkedIn (professional insights), YouTube Shorts and X. Respect each platform's rules. Use music only from licensed libraries, use photos only with permission (MLS photo licenses may restrict social use, so check), and get written consent before featuring clients or their homes.\n\nWORKFLOW:\n1) Intake: platforms, area, niche (first-time buyers, luxury, relocation, investors), voice (3 adjectives plus sample posts), posting capacity (posts per week), brokerage, license and any team branding rules.\n2) Pillar mix: Market 20%, Education 25%, Community 15%, Listings and Solds 15%, Personal 15%, Proof 10%. Adjust to the agent's goals.\n3) Build 30 days: for each post give platform(s), format, pillar, hook, caption (with brokerage name), CTA (a soft ask: DM, comment a keyword, link in bio), hashtags (3–8 local and niche), a visual idea, and for reels a 15–30 second script with on-screen text. Include 4 'batch filming' days.\n4) Fair housing and Article 12 scan on every post; fix flagged words.\n5) Deliver the calendar: load the skill \"content-calendar-template\", copy its HTML exactly, and replace ONLY the JSON inside <script id=\"report-data\" type=\"application/json\"> (meta{start,example:false}, brand{agent,brokerage,license,market}, pillars, posts[{platforms[],format,pillar,hook,caption,status:\"draft\"}]). Save to /workspace/content/calendar-<YYYY-MM>.html, plus a CSV for schedulers (Meta Business Suite, Later, Buffer).\n6) Repurposing: turn each reel into a carousel, a LinkedIn post and an email tip.\n7) Engagement: draft reply templates for common comments and DMs. Replies are drafts the agent sends; never auto-reply.\n\nMONTHLY BUILD (routine): next month's calendar, refreshed stats, and 5 repurposed top posts (from performance the user pastes).\n\nRULES: Never invent stats, solds, testimonials or awards. Only claim 'sold' if the agent was the listing or buyer's broker. Nothing is posted. Report only real actions.\n\nACTIVITY TRACKER: keep a log at /workspace/trackers/real-estate-social-media-factory.csv (create it with this header row if it doesn't exist: date,day,platform,pillar,hook,status,fh_check,file,next_action,notes). Append a row for every post drafted, revised, approved, scheduled or posted (as reported by the user). 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- \"Instagram and Facebook, Maple Grove, first-time buyers\" -> a pillar plan plus a 30-day calendar.\n- \"Write the reel scripts for week 1\" -> hooks, scripts and on-screen text.\n- \"Check this caption for fair housing\" -> flags with a compliant rewrite.\n- \"Export for Later\" -> a scheduler-ready CSV.",
  "whatItDoes": [
    "Builds a 30-day, multi-platform content calendar across six pillars (market, education, community, listings, personal, proof)",
    "Writes hooks, captions, CTAs, hashtags, reel scripts and carousel outlines in your voice",
    "Visual HTML calendar with status tracking, an automatic fair housing word check and scheduler CSV export",
    "Puts the brokerage name and NAR Article 12 rules on every post (true picture, 'sold' claims, firm name)",
    "Repurposes reels into carousels, LinkedIn posts and email tips",
    "Drafts comment and DM reply templates (never auto-replies)",
    "Optional monthly build on the 25th; optional RentCast key for market data"
  ],
  "gettingStarted": [
    "Click 'Add to Grok Bot' on this page (or import the template JSON) and open the bot.",
    "Optional: export area market stats 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.",
    "Answer the first question with your platforms and area, then share your niche, voice and brokerage details.",
    "Open the calendar it sends. Edit any hook or caption, fix anything flagged 'Review', and mark posts approved.",
    "Post or schedule approved posts yourself (export CSV for Later, Buffer or Meta Business Suite).",
    "It logs every post to /workspace/trackers/real-estate-social-media-factory.csv."
  ],
  "howToUse": [
    "First message: when you open the bot it greets you with: \"Hi! I'll build you 30 days of real estate content you'll actually post. Which platforms do you use most, and what area do you serve?\"",
    "\"Instagram and Facebook, Maple Grove, first-time buyers\". You get: a pillar plan plus a 30-day calendar.",
    "\"Write the reel scripts for week 1\". You get: hooks, scripts and on-screen text.",
    "\"Check this caption for fair housing\". You get: flags with a compliant rewrite.",
    "\"Export for Later\". You get: a scheduler-ready CSV."
  ],
  "activityTracker": {
    "path": "/workspace/trackers/real-estate-social-media-factory.csv",
    "columns": [
      "date",
      "day",
      "platform",
      "pillar",
      "hook",
      "status",
      "fh_check",
      "file",
      "next_action",
      "notes"
    ],
    "starterCsv": "date,day,platform,pillar,hook,status,fh_check,file,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": "How is this different from Listing Launch Kit?",
      "answer": "Listing Launch Kit builds a 2-week campaign for one listing. This bot builds your ongoing 30-day personal-brand calendar across six content pillars."
    },
    {
      "question": "Will it post for me?",
      "answer": "No. It drafts everything. You approve and post or schedule."
    },
    {
      "question": "Can I use MLS photos on social?",
      "answer": "Check your MLS and photographer license. Many restrict use. The bot reminds you and suggests your own photos or video."
    }
  ],
  "tags": [
    "real estate",
    "social media",
    "content calendar",
    "instagram",
    "reels",
    "realtor"
  ],
  "openingMessage": "Hi! I'll build you 30 days of real estate content you'll actually post. Which platforms do you use most, and what area do you serve?",
  "routineOffer": "Want a fresh month queued up? I can run a monthly content build on the 25th at 9 AM: draft next month's 30-day calendar, refresh the market stats, and repurpose your best posts.",
  "skills": [
    {
      "name": "content-calendar-template",
      "description": "Editable HTML 30-day content calendar with fair housing check. Copy it exactly and replace only the JSON inside <script id=\"report-data\">.",
      "fileName": "content-calendar-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>30-Day Content Calendar</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.wk h3{font-size:14px;text-transform:uppercase;letter-spacing:.08em;color:var(--muted);margin:22px 0 8px}\n.days{display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:12px}\n.day{background:#fff;border:1px solid var(--line);border-radius:16px;padding:14px;display:flex;flex-direction:column;gap:8px;border-top:5px solid var(--pc)}\n.dh{display:flex;justify-content:space-between;align-items:center;gap:8px}.dd{font-weight:850;font-size:15px}.dd small{display:block;font-size:12px;color:var(--muted);font-weight:650}\n.chips{display:flex;flex-wrap:wrap;gap:4px}.chip{font-size:11px;font-weight:800;padding:3px 8px;border-radius:99px;background:var(--line2);color:var(--ink2)}\n.pl{font-size:11px;font-weight:800;padding:3px 8px;border-radius:99px;color:#fff;background:var(--pc)}\n.day [contenteditable]{font-size:14px;padding:8px 10px;min-height:40px}.day .hook{font-weight:750;font-size:15px}\n.day select.in{min-height:40px;padding:6px 10px;font-size:14px}\n.row2{display:flex;gap:8px;align-items:center;justify-content:space-between}\n.legend{display:flex;flex-wrap:wrap;gap:8px;margin:8px 0}\n@media (max-width:899px){.days{grid-template-columns:1fr}}\n@media print{.day{border:1px solid #000;border-top-width:4px;break-inside:avoid}.days{grid-template-columns:1fr 1fr}.pl{border:1px solid #000}}\n</style></head><body>\n<div class=\"wrap\">\n <div class=\"top\"><div class=\"brand\"><div class=\"logo\">S</div><span>30-Day Content Calendar</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>Every post shows your brokerage name and is fair-housing safe.</b> Posts flagged \"Review\" contain words that often cause fair housing issues; edit them before posting. Paid housing ads must use the platform's housing ad category. Nothing posts automatically; you approve and post each one.</div></div>\n <div class=\"legend\" id=\"legend\"></div>\n <main id=\"main\"></main>\n <footer id=\"foot\"></footer>\n</div>\n<nav class=\"dock\"><button class=\"btn\" data-act=\"reset\">Reset</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\":\"social\",\"example\":false,\"generated\":\"\",\"start\":\"\"},\"brand\":{\"agent\":\"\",\"brokerage\":\"\",\"license\":\"\",\"market\":\"\"},\"pillars\":{\"Market\":\"#1d4ed8\",\"Listings\":\"#b45309\",\"Education\":\"#047857\",\"Community\":\"#7c3aed\",\"Personal\":\"#be185d\",\"Proof\":\"#0e7490\"},\"posts\":[]}\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('sc:'+(BASE.meta.reportId||'s')+':'+location.pathname);\nlet S=ST.load()||JSON.parse(JSON.stringify(BASE));\nconst FH=[/family[- ]friendly/i,/perfect for (a )?famil/i,/safe (neighborhood|area|community)/i,/exclusive (community|neighborhood)/i,/\\bno (kids|children)\\b/i,/adults? only/i,/bachelor pad/i,/young professionals/i,/empty[- ]nesters?/i,/christian|church[- ]going/i,/\\bhandicap/i,/able[- ]bodied/i,/integrated|ethnic|minorit/i,/great schools/i];\nconst flag=t=>FH.filter(r=>r.test(t||'')).map(r=>(t.match(r)||[''])[0]);\nconst ce=(p,cls)=>`<div contenteditable=\"true\" role=\"textbox\" data-path=\"${p}\" class=\"${cls||''}\">${esc(getP(S,p))}</div>`;\nfunction render(){const P=S.posts||[],start=S.meta.start?new Date(S.meta.start+'T12:00'):null;\n document.getElementById('legend').innerHTML=Object.entries(S.pillars).map(([k,c])=>`<span class=\"pl\" style=\"--pc:${c}\">${k}</span>`).join('');\n let h='';for(let w=0;w*7<P.length;w++){h+=`<section class=\"wk\"><h3>Week ${w+1}</h3><div class=\"days\">`;\n  P.slice(w*7,w*7+7).forEach((p,j)=>{const i=w*7+j,d=start?new Date(start.getTime()+i*864e5):null,c=S.pillars[p.pillar]||'#475569';\n   h+=`<article class=\"day\" style=\"--pc:${c}\"><div class=\"dh\"><div class=\"dd\">Day ${i+1}<small>${d?d.toLocaleDateString('en-US',{weekday:'short',month:'short',day:'numeric'}):''}</small></div><span class=\"pl\">${esc(p.pillar||'')}</span></div>\n   <div class=\"chips\">${(p.platforms||[]).map(x=>`<span class=\"chip\">${esc(x)}</span>`).join('')}${p.format?`<span class=\"chip\">${esc(p.format)}</span>`:''}</div>\n   ${ce('posts.'+i+'.hook','hook')}${ce('posts.'+i+'.caption')}\n   <div class=\"row2\"><select class=\"in\" data-s=\"posts.${i}.status\" aria-label=\"Status\">${['draft','approved','scheduled','posted'].map(s=>`<option ${p.status===s?'selected':''}>${s}</option>`).join('')}</select><span class=\"tag\" data-fh=\"${i}\"></span></div></article>`});h+='</div></section>'}\n document.getElementById('main').innerHTML=h;calc()}\nfunction calc(){const P=S.posts||[],B=S.brand||{};let fl=0;\n P.forEach((p,i)=>{const f=flag((p.hook||'')+' '+(p.caption||''));const el=document.querySelector(`[data-fh=\"${i}\"]`);if(el){el.className='tag '+(f.length?'bad':'ok');el.textContent=f.length?'Review: \"'+f[0]+'\"':'✓ FH check'}if(f.length)fl++});\n const ap=P.filter(p=>p.status!=='draft').length,cnt={};P.forEach(p=>cnt[p.pillar]=(cnt[p.pillar]||0)+1);\n document.getElementById('hero').innerHTML=`<div><span class=\"pill\">Content calendar</span>${S.meta.example?' <span class=\"pill ex\">EXAMPLE DATA · fictional</span>':''}<h1>${esc(B.agent||'Your')} · ${esc(B.market||'your market')}</h1><div class=\"val\">${P.length} posts</div><div class=\"vallab\">30 days · ${[...new Set(P.flatMap(p=>p.platforms||[]))].join(', ')}</div><div class=\"range\">${Object.entries(cnt).map(([k,v])=>k+' '+v).join(' · ')}</div></div>\n <div class=\"stats\"><div class=\"stat\"><div class=\"k\">Approved+</div><div class=\"v\">${ap}/${P.length}</div></div><div class=\"stat\"><div class=\"k\">Fair housing flags</div><div class=\"v\" style=\"color:${fl?'#fca5a5':'#fff'}\">${fl}</div></div><div class=\"stat\"><div class=\"k\">Brokerage shown</div><div class=\"v\">${B.brokerage?'Yes':'Add it'}</div></div><div class=\"stat\"><div class=\"k\">Start date</div><div class=\"v\">${esc(S.meta.start||'—')}</div></div></div>`;\n document.getElementById('foot').innerHTML=`${esc(B.agent||'')}${B.brokerage?' · '+esc(B.brokerage):''}${B.license?' · '+esc(B.license):''} · Equal Housing Opportunity. Market stats must be accurate, sourced and dated (NAR Article 12). Only claim \"sold\" if you were the listing or buyer's broker.${S.meta.generated?' Generated '+esc(S.meta.generated)+'.':''} <span id=\"saved\"></span>`}\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('change',e=>{const p=e.target.dataset.s;if(!p)return;setP(S,p,e.target.value);calc();save()});\ndocument.addEventListener('click',e=>{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 st=S.meta.start?new Date(S.meta.start+'T12:00'):null;const rows=[['day','date','platforms','format','pillar','hook','caption','status','fair_housing']];(S.posts||[]).forEach((p,i)=>{const d=st?new Date(st.getTime()+i*864e5).toISOString().slice(0,10):'';rows.push([i+1,d,(p.platforms||[]).join('|'),p.format,p.pillar,p.hook,p.caption,p.status,flag((p.hook||'')+' '+(p.caption||'')).join('|')||'ok'])});download('content-calendar.csv',rows.map(r=>r.map(csvq).join(',')).join('\\n'),'text/csv')}});\nwindow.__doc={get state(){return S},calc};render();\n})();\n</script></body></html>\n"
    }
  ]
}