1'use strict'; 2 3// ââ CONTENT PILLARS v2 ââââââââââââââââââââââââââââââââââââ 4// âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 5// STRATEGY SNACK MACHINE Ã POSTIQ INTEGRATION 6// Drop this entire block into app.js, replacing the existing 7// ContentPillars IIFE. Then update the cpGateNew handler below. 8// 9// CHANGES TO EXISTING HTML (app.html): 10// 1. Replace cpGateNew card inner HTML (see GATE CARD HTML section) 11// 2. Add SSM form HTML (see SSM FORM HTML section) 12// Both are drop-in replacements â no structural changes needed. 13// âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 14 15// ââ SSM GATE CARD HTML (replace cpGateNew button contents) ââââââ 16// Paste this as the innerHTML of the existing #cpGateNew button: 17/* 18<span class="cp-gate-icon">â¡</span> 19<span style="display:inline-flex;align-items:center;gap:4px;font-size:10px;font-family:'DM Mono',monospace;font-weight:600;letter-spacing:.06em;text-transform:uppercase;background:var(--brand-dim);border:1px solid var(--brand-glow);color:var(--brand);padding:2px 8px;border-radius:4px;margin-bottom:8px;">Pillar Plan Builder</span> 20<span class="cp-gate-title">Generate my content strategy</span> 21<span class="cp-gate-desc">Answer 6 quick questions. Get 5 content pillars, 25 post ideas, hooks, CTAs, recurring series ideas, and one post you can publish today.</span> 22<span class="cp-gate-cta">Build my plan â</span> 23*/ 24 25// ââ SSM FORM HTML (insert inside #cpStageJourney, replace all inner content) ââ 26/* 27Replace everything inside <div class="cp-stage" id="cpStageJourney"> with: 28 29<div style="max-width:660px;"> 30 <!-- Loading screen (hidden until generate()) --> 31 <div id="ssmLoading" style="display:none;text-align:center;padding:40px 20px;"> 32 <div style="font-family:'DM Mono',monospace;font-size:11px;text-transform:uppercase;letter-spacing:.1em;color:var(--accent);margin-bottom:12px;" id="ssmLoadStep">Reading your inputs...</div> 33 <div style="border:1px solid var(--border2);height:14px;max-width:280px;margin:0 auto 16px;overflow:hidden;border-radius:2px;"><div style="height:100%;background:var(--brand);width:0%;transition:width .1s linear;" id="ssmLoadBar"></div></div> 34 <div style="font-family:'Bricolage Grotesque',sans-serif;font-weight:800;font-size:20px;color:var(--ink);letter-spacing:-.02em;">Building your pillar plan<span id="ssmDots">...</span></div> 35 </div> 36 37 <!-- Form (shown by default) --> 38 <div id="ssmForm"> 39 <div style="margin-bottom:20px;"> 40 <div style="font-family:'DM Mono',monospace;font-size:11px;text-transform:uppercase;letter-spacing:.1em;color:var(--brand);margin-bottom:8px;">Pillar Plan Builder</div> 41 <div style="font-family:'Bricolage Grotesque',sans-serif;font-weight:700;font-size:18px;color:var(--ink);margin-bottom:4px;">Answer a few questions. Get a pillar plan.</div> 42 <div style="font-size:13px;color:var(--muted);line-height:1.6;">6 quick fields. Deterministic output â built from your inputs.</div> 43 </div> 44 45 <div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;"> 46 <div class="field"> 47 <label class="label">Brand / Creator Name</label> 48 <input type="text" class="input" id="ssm-brand" placeholder="e.g. Maria Chen, TechWave" /> 49 </div> 50 <div class="field"> 51 <label class="label">Industry Category</label> 52 <select class="input" id="ssm-industry"> 53 <option value="">Select industry...</option> 54 <option value="fitness">Fitness & Wellness</option> 55 <option value="food">Food & Beverage</option> 56 <option value="saas">SaaS / Tech</option> 57 <option value="realestate">Real Estate</option> 58 <option value="creative">Creative Services</option> 59 <option value="ecommerce">E-Commerce / Product Brand</option> 60 <option value="coaching">Coaching & Consulting</option> 61 <option value="finance">Finance & Money</option> 62 <option value="education">Education & Courses</option> 63 <option value="nonprofit">Nonprofit / Mission-Driven</option> 64 <option value="entertainment">Entertainment / Media</option> 65 <option value="localbiz">Local Business</option> 66 </select> 67 </div> 68 <div class="field"> 69 <label class="label">Target Audience</label> 70 <input type="text" class="input" id="ssm-audience" placeholder="e.g. early-stage founders, busy moms" /> 71 </div> 72 <div class="field"> 73 <label class="label">Product / Service / Offer</label> 74 <input type="text" class="input" id="ssm-offer" placeholder="e.g. 1:1 coaching, SaaS tool, meal kits" /> 75 </div> 76 <div class="field"> 77 <label class="label">Brand Tone</label> 78 <select class="input" id="ssm-tone"> 79 <option value="">Select tone...</option> 80 <option value="bold">Bold & Direct</option> 81 <option value="playful">Playful & Fun</option> 82 <option value="expert">Expert & Authoritative</option> 83 <option value="warm">Warm & Relatable</option> 84 <option value="edgy">Edgy & Provocative</option> 85 <option value="professional">Professional & Polished</option> 86 </select> 87 </div> 88 <div class="field"> 89 <label class="label">Main Goal</label> 90 <select class="input" id="ssm-goal"> 91 <option value="">Select goal...</option> 92 <option value="grow">Grow Audience</option> 93 <option value="sales">Drive Sales</option> 94 <option value="trust">Build Trust</option> 95 <option value="educate">Educate</option> 96 <option value="hired">Get Hired</option>
97 <option value="launch">Launch Product</option> 98 <option value="event">Promote Event</option> 99 </select> 100 </div> 101 </div> 102 103 <div class="field" style="margin-top:4px;grid-column:1/-1;"> 104 <label class="label">Anything else? (optional)</label> 105 <textarea class="input" id="ssm-notes" placeholder="Upcoming launches, topics to avoid, specific angles..." style="min-height:60px;"></textarea> 106 </div> 107 108 <div style="display:flex;gap:8px;margin-top:16px;align-items:center;"> 109 <button class="btn ghost" id="ssmBackBtn" type="button">â Back</button> 110 <button class="btn primary" id="ssmGenerateBtn" type="button">â¡ Generate pillar plan â</button> 111 </div> 112 </div> 113</div> 114*/ 115 116// âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 117// JAVASCRIPT â replaces window.ContentPillars IIFE in app.js 118// âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 119 120window.ContentPillars = (() => { 121 const CP_KEY = 'postiq_pillars_v3'; 122 const USAGE_KEY = 'postiq_pillars_usage_v1'; 123 const CP_TEMPLATE_TAG = 'postiq-content-pillars'; 124 const TONES = ['Practical', 'Story', 'Contrarian', 'Question']; 125 const COLORS = ['#3a3fff','#0fa672','#f59e0b','#ff4f6a','#7c3aed','#9298b0']; 126 127 const cpState = { 128 identity: '', 129 pillars: [], 130 _ssmInputs: null, 131 }; 132 133 const cpQs = id => document.getElementById(id); 134 const cpUid = () => Date.now().toString(36) + '-' + Math.random().toString(36).slice(2, 7); 135 const cpEsc = s => String(s || '').replace(/[&<>"']/g, m => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[m])); 136 const pillarColor = i => COLORS[i % COLORS.length]; 137 138 function cpGetUsage() { try { return JSON.parse(localStorage.getItem(USAGE_KEY) || '{}'); } catch { return {}; } } 139 function cpBumpUsage(pid) { const u = cpGetUsage(); u[pid] = (u[pid] || 0) + 1; try { localStorage.setItem(USAGE_KEY, JSON.stringify(u)); } catch {} } 140 function cpTotalUsage() { return Object.values(cpGetUsage()).reduce((a, b) => a + Number(b || 0), 0); } 141 function cpUsageFor(pid) { return cpGetUsage()[pid] || 0; } 142 143 function cpGeneratedSeriesNames() { 144 try { return new Set(Object.values(SERIES || {}).flat().map(s => String(s?.name || '').trim()).filter(Boolean)); } 145 catch { return new Set(); } 146 } 147 148 function cpIsGeneratedTemplate(tpl) { 149 const tags = Array.isArray(tpl?.tags) ? tpl.tags.map(x => String(x || '').trim().toLowerCase()) : []; 150 if (tags.includes(CP_TEMPLATE_TAG) || tags.includes('pillar-plan')) return true; 151 const title = String(tpl?.title || '').trim(); 152 return tags.includes('series') && cpGeneratedSeriesNames().has(title); 153 } 154 155 function cpClearGeneratedTemplates() { 156 if (typeof state === 'undefined' || !Array.isArray(state.templates)) return 0; 157 const before = state.templates.length; 158 state.templates = state.templates.filter(t => !cpIsGeneratedTemplate(t)); 159 const removed = before - state.templates.length; 160 if (removed > 0) { 161 if (typeof persistTemplates === 'function') persistTemplates(); 162 if (typeof renderTemplates === 'function') renderTemplates(); 163 } 164 return removed; 165 } 166 167 function cpNormalizePillar(p, i = 0) { 168 return { 169 id: String(p?.id || cpUid()), 170 name: String(p?.name || `Pillar ${i + 1}`), 171 promise: String(p?.promise || p?.desc || ''), 172 layer: ['awareness','credibility','action'].includes(p?.layer) ? p.layer : '', 173 seeds: Array.isArray(p?.seeds) && p.seeds.length ? p.seeds.map(s => String(s || '')) : (Array.isArray(p?.ideas) ? p.ideas.map(s => String(s || '')) : ['']), 174 tones: p?.tones && typeof p.tones === 'object' ? p.tones : {}, 175 hook: String(p?.hook || ''), 176 cta: String(p?.cta || ''), 177 }; 178 } 179 180 function cpPersist() { 181 try { localStorage.setItem(CP_KEY, JSON.stringify({ identity: cpState.identity, pillars: cpState.pillars })); } catch {} 182 cpRenderCompact(); 183 } 184 185 function cpLoad() { 186 try { 187 const d = JSON.parse(localStorage.getItem(CP_KEY) || 'null'); 188 if (d?.pillars?.length) { 189 cpState.identity = d.identity || ''; 190 cpState.pillars = d.pillars.map(cpNormalizePillar); 191 return true; 192 } 193 } catch {} 194 return false;
195 } 196 197 function cpShowStage(id) { 198 document.querySelectorAll('.cp-stage').forEach(el => el.classList.remove('active')); 199 const el = cpQs(id); 200 if (el) el.classList.add('active'); 201 } 202 203 // ââ SSM ENGINE âââââââââââââââââââââââââââââââââââââââââââââââââ 204 205 // Pillar library (subset â grow/sales for most verticals) 206 // Full library inlined from SSM index.html 207 const LIBRARY = { 208 fitness_grow: [ 209 { name:'Real Progress, No Filter', desc:'Show the actual journey â sweat, setbacks, and small wins.', ideas:['Week 3 and I almost quit â here\'s what kept me going','My workout looked nothing like the plan today. Here\'s why that\'s fine','The fitness advice that actually worked for me (vs what I tried first)','What nobody shows you about getting in shape after 30','Progress pic I almost didn\'t post â but here we are'], hook:'The fitness account you actually need isn\'t showing you the full picture.', cta:'Follow if you\'re tired of the highlight reel. Real progress lives here.' }, 210 { name:'Workout How-Tos', desc:'Teach your audience a specific exercise, modification, or technique.', ideas:['How to do a proper Romanian deadlift if you\'ve never tried one','3 beginner-friendly swaps for exercises that hurt your knees','The form mistake everyone makes on bench press (and how to fix it)','5-minute morning mobility routine you can do in bed','How to build a full-body workout with just a resistance band'], hook:'You\'re working out consistently and still not seeing results. Here\'s why.', cta:'Save this one. Come back to it when you need a form check.' }, 211 { name:'Myth Busting', desc:'Fitness is full of bad advice that won\'t die. Be the person who calls it out.', ideas:['No, you don\'t need to do cardio every day to lose fat','The \'no pain no gain\' mindset is actually keeping you injured','Spot reduction isn\'t real â here\'s what actually works','You don\'t need a gym membership to get strong','Why eating less is sometimes making your weight loss harder'], hook:'I\'ve been in fitness for years and the amount of bad advice out there is actually alarming.', cta:'Drop your biggest fitness myth in the comments. Let\'s debunk it.' }, 212 { name:'Community & Motivation', desc:'Build belonging. People stay loyal to communities, not just content creators.', ideas:['If you worked out at all this week, this post is for you','Who else started their fitness journey after a doctor\'s visit?','The type of gym-goer I love to see (and it\'s not who you think)','Tag someone who needs to hear this today','What getting fit actually gave me that had nothing to do with my body'], hook:'Fitness changed my life. But not the way I expected.', cta:'Tell me where you\'re at in your journey. No judgment here.' }, 213 { name:'Lifestyle & Nutrition', desc:'Show that fitness is a full-system game â sleep, food, stress, recovery all matter.', ideas:['What I eat in a day as someone who actually lifts','The sleep habit that improved my training more than any supplement','3 high-protein meals I make when I have no time','Why I stopped counting calories and what I do instead','Recovery isn\'t lazy. Here\'s how I structure my rest days'], hook:'Your workouts aren\'t the problem. Your recovery is.', cta:'Save this. Your future self will thank you.' }, 214 ], 215 fitness_sales: [ 216 { name:'Client Transformations', desc:'Document real outcomes from real clients â not just bodies, but confidence and consistency.', ideas:['Client went from skipping workouts to showing up 5x/week â here\'s what changed','Before & after: 90 days with a plan that actually fit her schedule','He told me he\'d "tried everything." 8 weeks later, this happened.','What my client learned in month one that she wishes she knew sooner','Real talk from a client: what coaching actually feels like from the inside'], hook:'I don\'t sell shortcuts. I sell systems that stick â and here\'s proof.', cta:'DM me READY to see if my coaching is the right fit for you.' }, 217 { name:'What You Get', desc:'Break down your offer in concrete, specific terms.', ideas:['Here\'s exactly what happens in week 1 of working with me','What\'s included in my program (and why I built it that way)','The difference between my coaching and a generic app','How I build your program around your actual schedule, not an ideal one','FAQ: Is your program right for me? Honest answer.'], hook:'Most fitness programs fail because they weren\'t built for your life. Mine is.', cta:'See the full breakdown at the link in bio. Spots are limited.' }, 218 { name:'Common Objections Answered', desc:'Meet your audience where their doubts live.', ideas:['"I don\'t have time to work out." Let me reframe that.','"I\'ve tried programs before and quit." Here\'s why this is different.','"I can just use YouTube." You can. Here\'s what you\'ll miss.','"I can\'t afford a coach right now." Let\'s talk about what\'s actually expensive.','"I\'m not in good enough shape to start." That\'s exactly why you should.'], hook:'You\'ve been telling yourself the same story for months. Let\'s challenge it.', cta:'Your questions are valid. Let\'s talk. DM me anytime.' }, 219 { name:'Social Proof', desc:'Let your clients do the selling.', ideas:['What she told me at the end of our 12 weeks together','Client DM that made my morning â sharing with permission','From "I hate working out" to 4x a week. Her words, not mine.','Results that surprised even me (and I\'ve been coaching for years)','The review that reminded me why I do this'], hook:'I could tell you my program works. Or I could let my clients.', cta:'Ready to write your own story? DM me to get started.' }, 220 { name:'Your Story & Method', desc:'Share why you do this and how your method is different.', ideas:['Why I became a fitness coach (it wasn\'t for the obvious reason)','The method I use that most trainers skip entirely','What I got wrong about fitness before I figured this out','My philosophy: I don\'t chase aesthetics, I chase ability','The moment I knew I had to start coaching other people'], hook:'There are thousands of fitness coaches. Here\'s why my clients choose me.', cta:'Follow for a different kind of fitness perspective. No fluff.' }, 221 ], 222 saas_grow: [ 223 { name:'Build In Public', desc:'Document what you\'re building, deciding, and learning.', ideas:['We shipped a feature we almost cut. Here\'s what convinced us.','The metric we watch instead of MRR (and why)','3 things our users told us that changed our roadmap','Why we chose to build in [tech stack] and what we\'d do differently','What our churn data taught us that our NPS score never could'], hook:'Most SaaS companies share the wins. We\'re going to share everything.', cta:'Follow if you want to see what building a software company actually looks like.' }, 224 { name:'Hot Takes & Industry POV', desc:'Have real opinions about the tools, trends, and practices in your space.', ideas:['Unpopular opinion: your tech stack matters less than your distribution strategy','The SaaS metric everyone obsesses over that actually means nothing','Why most B2B software has a UX problem and nobody\'s talking about it','The "growth hack" that\'s actually just good product thinking','AI isn\'t replacing SaaS â but it\'s changing what SaaS has to be'], hook:'The SaaS advice that keeps getting recycled is keeping founders stuck.', cta:'Follow for takes on software, growth, and building products that last.' }, 225 { name:'Founder & Team Stories', desc:'Put humans behind the logo.', ideas:['Why we turned down a partnership that looked perfect on paper','The hire that changed everything about how we build','A decision I made in year one that still haunts me (and what I fixed)','Our remote team has never met in person. Here\'s how we make it work.','The week we thought about shutting down â and what we learned'], hook:'Behind every SaaS is a team making hard calls with incomplete information.', cta:'Follow along as we build this thing in public â wins, mistakes, and all.' }, 226 { name:'Use Case Education', desc:'Show your product solving real problems in specific industries.', ideas:['How a solo foun
226der uses [product] to replace 3 different tools','[Industry] team saved 6 hours/week with this workflow in [product]','The exact setup a [role] uses in [product] to manage their pipeline','3 ways [product] handles [problem] that [competitor] can\'t','A day in the life of a power user â every tool, every trigger'], hook:'You\'ve heard about [product]. Here\'s what it actually looks like in the wild.', cta:'Try it free. Link in bio. No credit card required.' }, 227 { name:'Market Commentary', desc:'React to industry news with genuine analysis.', ideas:['What [major company] shutting down [feature] means for everyone in [space]','This funding round tells us a lot about where the market is going','The trend I\'m watching in [category] that nobody\'s written about yet','Why the consolidation happening in [space] is actually good for smaller players','My take on the "AI-first" narrative and what it gets wrong'], hook:'The [industry] news cycle moves fast. Here\'s the take you actually need.', cta:'Follow for weekly signal vs noise analysis on [space].' }, 228 ], 229 saas_sales: [ 230 { name:'Problem-First Content', desc:'Name the exact friction your product removes.', ideas:['If your team is still doing [task] manually, here\'s what that\'s costing you','The reason [workflow] breaks down at scale (and what companies do about it)','Signs y
230our current [tool category] is holding your team back','How much time does [problem] actually cost per week? (We did the math)','What happens when [process] doesn\'t have a system behind it'], hook:'The [problem] you\'ve been working around has a fix. You\'re just not using it.', cta:'See how [offer] handles this in 2 minutes â demo link in bio.' }, 231 { name:'Feature Storytelling', desc:'Tell the story of the problem features solve.', ideas:['We built [feature] because a customer showed us something that broke our heart','How [feature] works and why we built it this way (not the obvious way)','The edge case that led to [feature] â and why it matters for your team','[Feature] in 60 seconds: here\'s what changes for your workflow','Before [feature] vs after [feature] â a real user workflow comparison'], hook:'Software features don\'t matter. The problems they solve do. Here\'s ours.', cta:'Activate [feature] in your account today.' }, 232 { name:'ROI & Business Case', desc:'Help your buyers justify the purchase internally.', ideas:['How to calculate what [problem] is actually costing your company','The ROI calculation our customers use to justify [product] internally','What "saving time" actually means when 4 people use [product] every day','Our customers report [outcome] in the first 30 days â here\'s how','The business case for [product]: a template you can steal'], hook:'Your boss needs a number, not a feature list. Here\'s the number.', cta:'Need a custom ROI analysis? DM us your team size.' }, 233 { name:'Customer Stories', desc:'Your customers are your best salespeople.', ideas:['[Customer type] reduced [metric] by [X]% using this workflow in [product]','From spreadsheet chaos to [outcome]: a [company type] story','How a [team size] team uses [product] to do what used to take twice as many people','The customer who said they\'d "never switch tools again" â and what changed their mind','Interview: how [customer type] went from [before] to [after] in 60 days'], hook:'Don\'t take our word for it. Here\'s what [industry] teams say after 90 days.', cta:'Book a 15-min call. We\'ll show you how this works for your team.' }, 234 { name:'Comparison & Positioning', desc:'Help buyers understand why you\'re the right choice.', ideas:['[Product] vs [competitor]: what\'s actually different (honest breakdown)','Why [product] isn\'t for everyone â and who it\'s built for','The workflow [competitor] can\'t do that [product] was designed for','What to look for when evaluating [tool category] (regardless of who you choose)','The questions to ask any [tool category] vendor before you sign'], hook:'Every [tool category] looks the same on a comparison page. Here\'s what to actually look for.', cta:'See a side-by-side comparison. Link in bio.' }, 235 ], 236 coaching_grow: [ 237 { name:'Thought Leadership', desc:'Share your genuine perspective on the problems your clients face.', ideas:['The advice every [client type] keeps getting that\'s actually holding them back','What [goal] actually requires â and why nobody wants to hear it','The coaching industry problem I\'m not willing to ignore anymore','Why [popular approach] works for some people and fails for most','My most controversial belief about [your niche] â and why I stand by it'], hook:'Most coaches in [space] are teaching the same thing. Here\'s what they\'re missing.', cta:'Follow if you\'re tired of advice that sounds good but doesn\'t land.' }, 238 { name:'Client Story Spotlights', desc:'Tell transformation stories with context, emotion, and specifics.', ideas:['She came to me believing [limiting belief]. Here\'s what shifted.','The client who had tried everything before working with me â and what finally worked','What 6 months of coaching looks like from the inside','A client win that had nothing to do with [obvious metric]','The moment a client said something that completely changed how I coach'], hook:'Transformation doesn\'t look the way you think it does. Let me show you.', cta:'Follow for real stories about what growth actually looks like.' }, 239 { name:'Your Framework & Method', desc:'Give your audience a taste of how you think and how you work.', ideas:['The 3-part framework I use with every new client','How I diagnose what\'s really blocking [audience type] in the first session','The question I ask that unlocks everything else','My signature process, explained â step by step','Why most [audience type] are solving the wrong problem'], hook:'Most people in [niche] focus on the symptom. I work on the root.', cta:'Follow to learn the framework behind real results.' }, 240 { name:'Behind The Coaching Business', desc:'Show what it\'s like to run a coaching practice.', ideas:['What a full coaching week actually looks like for me','How I structure my offers (and why I changed them twice)','The boundary I set that changed my business','What I charge and why (transparency post)','The tools I use to run my entire coaching practice'], hook:'Running a coaching business is nothing like what the gurus show you.', cta:'Follow for an honest look at what building a coaching practice actually takes.' }, 241 { name:'Mindset & Growth Content', desc:'Speak to the internal game â beliefs, fears, and patterns.', ideas:['The belief that\'s costing [audience type] the most right now','Why [audience] keep starting over (and how to stop)','The fear I see in almost every new client â and how we move through it','What "not ready" really means (and what to do about it)','The mindset shift that changes everything for [audience type]'], hook:'Your [goal]
241isn\'t a strategy problem. It\'s a belief problem.', cta:'Follow if you\'re ready to work on the thing that\'s actually in the way.' }, 242 ], 243 creative_grow: [ 244 { name:'Process & Craft', desc:'Show your work in progress â not just the polished final product.', ideas:['Before and after: how this project evolved from brief to final delivery','The sketch that became the brand identity for [client type]','How I approach color when I have complete creative freedom','Why this logo went through 6 rounds â and what each version taught me','The creative decision I almost didn\'t make (and why I\'m glad I did)'], hook:'The final version never looks like the first one. Here\'s the full story.', cta:'Follow for a behind-the-scenes look at how creative work actually gets made.' }, 245 { name:'Portfolio Storytelling', desc:'Tell the story behind the work â the brief, the challenge, the decision-making.', ideas:['The brief: [client] needed [goal]. Here\'s what we built and why.','This project had one constraint that made it better','The client who trusted us completely â and what that unlocked','A project I\'m proud of that almost nobody saw','What I would do differently on this project if I had another shot'], hook:'Great creative work doesn\'t happen in a vacuum. Here\'s the context behind this one.', cta:'See the full project breakdown at [link]. Follow for more.' }, 246 { name:'Industry Takes', desc:'Have opinions about design, photography, video, or your craft.', ideas:['The design trend I\'m already tired of seeing everywhere','What makes [craft] actually hard â not just technically, but creatively','The client feedback that secretly made the project better','Why [popular style] is overused and what I\'d recommend instead','What AI is doing to [creative field] â my honest take'], hook:'Most designers/photographers/editors won\'t say this publicly. I will.', cta:'Follow for takes on [craft] that go beyond tutorials and aesthetics.' }, 247 { name:'Creative Tips & Education', desc:'Teach your audience something real about your craft.', ideas:['The typography rule I apply to every project without fail','3 things I check before I call a design/photo/video done','How I approach creative briefs when clients don\'t know what they want','The free tool that changed how I present work to clients','What I wish I knew in year one of being a [role]'], hook:'I\'ve been doing [craft] for [X] years. Here\'s the shortcut I wish I had.', cta:'Save this. The tutorial version is coming next week.' }, 248 { name:'Client Collaboration', desc:'Show what it\'s like to work with you.', ideas:['What my client onboarding actually looks like â step by step','The questions I ask before I touch any creative work','How I handle client feedback that misses the mark â honestly','What "unlimited revisions" really means and why I don\'t offer it','Why I show 1-2 concepts instead of 5 â and how clients feel about it afterwards'], hook:'Working with a creative shouldn\'t feel like a mystery. Here\'s how I make it easy.', cta:'Curious about working together? DM me. Let\'s see if it\'s a fit.' }, 249 ], 250 ecommerce_grow: [ 251 { name:'Product in the Wild', desc:'Show your product living in the world â styled, used, loved.', ideas:['How our customers are styling [product] this season','UGC of the week: [product] in a home we never expected','The way [product] actually gets used vs how we originally imagined','Our team\'s favorite way to use [product] (3 very different answers)','[Product] in a day: morning, afternoon, and night'], hook:'The best thing about [product] is how many different ways people use it.', cta:'Tag us in your [product] photos. We share every one.' }, 252 { name:'Behind The Brand', desc:'Show your sourcing, your values, your process.', ideas:['Where [product] is made â and why we chose that factory/supplier/maker','The material we spent 6 months finding before we felt good about launching','Meet the person who [role] at [brand]','Why we made [brand decision] that other brands don\'t','The product we almost launched â and why we pulled it'], hook:'You can buy [product category] anywhere. Here\'s why people buy ours.', cta:'Follow [brand] to go behind the curtain on what we build and why.' }, 253 { name:'Education & Use Cases', desc:'Teach your audience how to get more value out of what you sell.', ideas:['The [product] mistake most people make in the first week','How to get the most out of [product] â tips from our most loyal customers','[Product] 101: everything to know before your first order','The use case for [product] you probably haven\'t tried yet','How to pair [product] with [complementary thing] for [better outcome]'], hook:'Most people only use [product] one way. Here are four more.', cta:'Save this for when your order arrives.' },
254 { name:'Trend & Culture', desc:'Connect your product to what\'s culturally relevant right now.', ideas:['[Product] and the [trend] moment: why this is the year to care about [value]','How [cultural shift] is changing how people think about [product category]','The aesthetic moment [product] was made for â and why it\'s having a moment','What [pop culture thing] gets right about [product category]','Why [season/moment/year] is the perfect time for [product]'], hook:'[Product category] is having a moment. Here\'s where [brand] fits in.', cta:'Follow [brand] â we stay ahead of what\'s coming next.' }, 255 { name:'Community & UGC', desc:'Turn your customers into content.', ideas:['Customer photo of the month â congrats to [handle]','The review that stopped us in our tracks â sharing it here','How our community is using [product] in ways we didn\'t expect','The [product] photo that outperformed every piece of branded content we\'ve made','Real customers, real homes, real [product]: this week\'s roundup'], hook:'Our best marketing doesn\'t come from us. It comes from people like you.', cta:'Order [product], take a photo, tag us. We\'ll feature you here.' }, 256 ], 257 finance_grow: [ 258 { name:'Personal Finance Education', desc:'Break down money concepts that feel intimidating into content that actually helps.', ideas:['What a Roth IRA actually is â explained like you\'re 25 and finally ready to care','The difference between good debt and bad debt (with real examples)','Why your budget keeps failing â and the simpler version that works','What [financial concept] means and why it matters for [audience]','The money move I wish I\'d made in my 20s (not clickbait)'], hook:'The financial system wasn\'t built to be understood. Let me fix that.', cta:'Follow for money content that makes you feel smarter, not worse.' }, 259 { name:'Market & News Commentary', desc:'React to economic news and market events with genuine analysis.', ideas:['What today\'s [economic news] actually means for your accounts/savings/portfolio','The Fed rate decision explained in 3 sentences that actually make sense','Why I\'m not panicking about [market event] â here\'s how I\'m thinking about it','The economic news nobody\'s talking about â and why it matters','What this recession signal does and doesn\'t mean for your money'], hook:'Financial news is designed to be alarming. Let\'s look at it clearly instead.', cta:'Follow for weekly market context â no fear, no hype.' }, 260 { name:'Budgeting & Spending Systems', desc:'Give your audience practical tools for managing money day-to-day.', ideas:['The budgeting method I\'ve seen work for people who hate budgets','How I allocate my income â the percentages and why','The subscription audit that saves people $100+ a month (do this today)','Why I stopped using a spending category called "miscellaneous"','How to pay yourself first when money is already tight'], hook:'Budgeting advice is everywhere. Here\'s what actually works in the real world.', cta:'Save this and actually do it this weekend. Your future self will be glad.' }, 261 { name:'Building Wealth Basics', desc:'Demystify investing, saving, and building long-term wealth.', ideas:['What to do with $1,000 if you have no idea where to start','Index funds explained without the jargon â why I recommend them first','The wealth gap between people who invest and people who don\'t (the math is wild)','How compound interest works â visualized simply','3 investing mistakes I see over and over with first-timers'], hook:'Wealth building isn\'t complicated. It\'s just not taught anywhere.', cta:'Follow for the financial education system that failed you.' }, 262 { name:'Financial Mindset', desc:'Address the emotional and psychological side of money.', ideas:['The money story from childhood that\'s affecting your finances right now','Why high earners still feel broke â the spending psychology behind it','The guilt I see around money that\'s keeping people from building wealth','What "good with money" actually means â it\'s not what you think','Financial anxiety is real. Here\'s how I talk to clients about it.'], hook:'Most money problems aren\'t math problems. They\'re psychology problems.', cta:'Follow if you want to fix your relationship with money â not just your budget.' }, 263 ], 264 localbiz_grow: [ 265 { name:'Local Expertise & Context', desc:'Be the most useful local expert in your space.', ideas:['The [neighborhood] spots we recommend to our own customers','What [local event/season] means for [your business category]','Why [city] customers are different from national trends â and what we do about it','The [local thing] that inspired something we do at [business]','Our take on what\'s happening in [local area] right now'], hook:'Nobody knows [city] like we do. Here\'s what we\'re seeing.', cta:'Follow [business] â we\'re as local as it gets.' }, 266 { name:'Behind The Business', desc:'Show your team, your space, your process.', ideas:['Meet [team member] â what they do here and why they love it','A day in [business] before we open the doors','How we prep for [busy season] â the real version','What it actually took to open [business] in [city]','The decision we made about [business practice] that surprised people'], hook:'You\'ve probably walked past [business]. Here\'s who we actually are.', cta:'Come visit us at [address]. We\'re here [hours].' },
267 { name:'Customer Love', desc:'Celebrate your regulars, your reviews, and the community.', ideas:['Customer of the month: [name] has been coming since [year]','The review that made our whole team stop what they were doing','A thank-you post to our neighborhood for [milestone]','What a local regular means to a small business â honestly','The community event we hosted and what it meant to us'], hook:'Small businesses run on regulars. Here\'s a thank-you to ours.', cta:'Leave us a review if we\'ve earned it. It matters more than you know.' }, 268 { name:'Educational & Service Content', desc:'Teach your audience something relevant to your category.', ideas:['What to look for when choosing a [business category] in [city]','The question you should always ask before hiring a [service type]','How to tell quality [product/service] from the generic version','Why [common misconception about your category] isn\'t quite right','What we\'ve learned after [X] years serving [city]'], hook:'We\'ve been in [business category] long enough to know what actually matters.', cta:'Questions about [service/product]? DM us or stop in anytime.' }, 269 { name:'Events & Promotions', desc:'Drive foot traffic and community engagement.', ideas:['[Event] at [business] on [date] â here\'s what to expect','We\'re doing [special offer] this week only â here\'s the deal','Celebrating [local event] with [something special] at [business]','Why we [host/participate in] [local thing] every year','[Limited time thing] is back â and it won\'t last long'], hook:'Something\'s happening at [business] and you don\'t want to miss it.', cta:'See you at [location] on [date]. Bring a friend.' }, 270 ], 271 }; 272 273 // Inline the full fallback + series + hooks from SSM 274 const SERIES = { 275 grow: [ { name:'Weekly Insight Drop', desc:'Every week, one specific insight from your world. Keep it concrete, keep it useful.' }, { name:'Myth vs. Reality', desc:'Monthly myth-busting post targeted at your audience\'s most common misconceptions.' }, { name:'Community Spotlight', desc:'Regular feature on a real person from your community â customer, follower, or collaborator.' } ], 276 sales: [ { name:'Client Story of the Month', desc:'Feature one real client outcome every month. The before, the work, the result.' }, { name:'Objection of the Week', desc:'Pick one real objection you hear and address it with care and specificity.' }, { name:'Value Stack Breakdown', desc:'Monthly breakdown of everything inside your offer. Reinforce perceived value without discounting.' } ], 277 trust: [ { name:'Learning in Public', desc:'Weekly update on something you\'re actively figuring out. Show the messy middle.' }, { name:'Process Deep Dive', desc:'Every two weeks, document the exact process behind something you do.' }, { name:'Transparency Report', desc:'Monthly real numbers post â what happened, what worked, what didn\'t.' } ], 278 educate: [ { name:'101 â 301 Series', desc:'Build a curriculum from beginner to advanced on your core topic.' }, { name:'Myth of the Month', desc:'Monthly explainer that kills one widely-held misconception in your space.' }, { name:'Tool Teardown', desc:'Biweekly breakdown of a relevant tool â what it does, who it\'s for, honest verdict.' } ], 279 hired: [ { name:'Work in Progress', desc:'Document a real project across multiple posts. Show your thinking, not just the output.' }, { name:'Skills Unlocked', desc:'Weekly post on one specific skill â what it is, how you apply it, proof it works.' }, { name:'Industry Read of the Week', desc:'Share and react to one article or trend each week.' } ], 280 launch: [ { name:'Build Log', desc:'Weekly documentation of building your product.' }, { name:'Founding Story Arc', desc:'Multi-part narrative from idea to launch.' }, { name:'Beta Diaries', desc:'Share early user stories as they come in.' } ], 281 event: [ { name:'Road to the Event', desc:'A short countdown series that reveals why the event matters.' }, { name:'Speaker / Feature Spotlight', desc:'Highlight one person, product, or reason to attend at a time.' }, { name:'Last Call Logistics', desc:'A practical series answering time, place, cost, schedule, and what-to-bring questions.' } ], 282 }; 283 284 const HOOKS = { 285 bold: ['Stop doing this in {industry}. It\'s costing you.','The {industry} advice everyone repeats that is actually wrong.','Nobody in {industry} wants to say this out loud.','Here is the take that will get me unfollowed:','If you are doing {offer}, you need to hear this first.'], 286 playful: ['Okay but why does nobody talk about this in {industry}??','This is unhinged but hear me out...','POV: you finally figured out {industry}','Friendly reminder that this exists and it is completely free.','Hot take incoming and I am not sorry at all.'], 287 expert: ['After years working in {industry}, here is what I know with confidence:','The research on this is clearer than most people realize.','A framework I have used across many {audience}: it works.','Here is what actually happens in {industry} â not what the courses say.','If you only take one thing from this:'], 288 warm: ['I have been wanting to share this for a while now.','Can we be honest for a second about {industry}?','Something I wish someone had told me earlier:','This community keeps teaching me things. Here is what I mean.','You are not behind. You are just human. Let me explain.'], 289 edgy: ['Everyone is wrong about this in {industry}. Here is proof.','I am about to upset some people and that is fine.','This is what {industry} does not want {audience} to know.','Hard truth: the popular {industry} advice is keeping {audience} stuck.','Controversial? Maybe. But someone had to say it.'], 290 professional: ['Sharing a framework that consistently drives results for {audience}:','Based on our experience across {industry}, here is what we have found:','A strategic insight for {audience} worth bookmarking:','The data does not lie: here is what matters in {industry}.','For {audience} making decisions in {industry}: this is worth your time.'], 291 }; 292 293 const QUICK_WINS = { 294 fitness_grow:'Post a real photo from your last workout â no filter, no setup. Caption it with one honest sentence about where you\'re at in your fitness journey. Authenticity outperforms aesthetic every time.', 295 fitness_sales:'Share a 3-sentence client story: the problem they came to you with, what changed, and what they s
295aid to you after. End with "DM me READY if this sounds familiar."', 296 saas_grow:'Share one thing you learned from your last user interview or support ticket that changed how you think about your product. Be specific. Founders and operators love content that shows real product thinking.', 297 saas_sales:'Write a 3-line business case for your product: the problem, what it costs companies to ignore it, and what your product does about it. No features â just outcome language.', 298 coaching_grow:'Give away one piece of genuinely useful content from inside your coaching â a framework, a question you ask clients, a mindset shift. If it\'s useful enough to put in a program, it\'s useful enough to post.', 299 coaching_sales:'Describe your ideal client in 4 sentences: where they are, what they\'ve already tried, what\'s actually blocking them, and where they want to be. The right person will read it and DM you.', 300 creative_grow:'Show a before/after of a project you finished recently â the rough first version and the final delivery. Add 2 sentences about the key creative decision that changed everything.', 301 creative_sales:'Post a project spotlight: the brief you received, the challenge you faced, and the outcome you delivered. Use the client\'s industry so the right buyer sees themselves in the story.', 302 ecommerce_grow:'Post a real UGC photo from a customer â with permission â and in the caption, share what they said about the product. No polish. Real people, real products, real words.', 303 finance_grow:'Break down one financial concept that most of your audience is confused about â in plain language, no jargon. The simpler and more useful, the better the save rate.', 304 localbiz_grow:'Post a "day before we open" photo or video â your team getting ready, the space being prepped. Add one sentence about what you love about your neighborhood. Local and human always wins.', 305 }; 306 307 // Trust layer mapping â maps SSM pillar order to PostIQ trust layers 308 const LAYER_MAP = ['awareness', 'credibility', 'awareness', 'action', 'credibility']; 309 310 function getFallback(industry, goal, inp) { 311 const byGoal = { 312 grow: [ 313 { name:'Behind The Scenes', desc:`Pull the curtain back on how ${inp.brand} works.`, ideas:[`A day running ${inp.brand}`,`The decision behind ${inp.offer} that surprised us`,`What ${inp.brand} looks like before it's polished`,`The team/person behind ${inp.brand}`,`Why we do things differently in ${inp.industry}`], hook:`Most ${inp.industry} brands only show you the highlight reel.`, cta:`Follow ${inp.brand} for the real version.` }, 314 { name:'Education & Value', desc:`Teach ${inp.audience} something genuinely useful every week.`, ideas:[`The ${inp.industry} mistake ${inp.audience} keep making`,`How to get more out of ${inp.offer}`,`What most people misunderstand about ${inp.industry}`,`3 things ${inp.audience} should know before ${inp.offer}`,`The question I get asked most â answered properly`], hook:`${inp.audience} deserve better information about ${inp.industry}.`, cta:`Follow for real, useful content â no fluff.` }, 315 { name:'Hot Takes & POV', desc:`Say something real. Generic accounts are invisible.`, ideas:[`The ${inp.industry} advice I'm tired of seeing`,`Unpopular opinion about ${inp.offer}`,`What everyone gets wrong about ${inp.industry}`,`The trend in ${inp.industry} I think is overhyped`,`My honest take on where ${inp.industry} is headed`], hook:`Most ${inp.industry} content sounds the same. Here's a different take.`, cta:`Follow if you want ${inp.industry} content with an actual perspective.` }, 316 { name:'Community & Stories', desc:`Build belonging. Feature your audience, celebrate wins.`, ideas:[`A win from someone in our community worth celebrating`,`The DM that made our week`,`Who our ${inp.audience} actually are â real stories`,`What ${inp.audience} have in common (it's not what you think)`,`Shoutout to every ${inp.audience} doing the work right now`], hook:`${inp.brand} isn't just content. It's a community.`, cta:`Tell us your story in the comments. We want to hear it.` }, 317 { name:'Progress & Milestones', desc:`Document your journey â the wins, the setbacks, the lessons.`, ideas:[`What ${inp.brand} looked like 1 year ago vs today`,`A mistake we made and what it taught us`,`The milestone we just hit â and what it took`,`What we're working on right now (and why it's hard)`,`The decision that changed everything for ${inp.brand}`], hook:`The best brands show you where they started and how far they've come.`, cta:`Follow ${inp.brand} to see where we go next.` }, 318 ], 319 sales: [ 320 { name:'Problem Clarity', desc:`Name the exact pain ${inp.offer} solves.`, ideas:[`The real reason ${inp.audience} struggle with ${inp.industry}`,`What happens when ${inp.audience} keep doing ${inp.industry} without a system`,`Signs ${inp.audience} are ready for ${inp.offer}`,`The cost of not solving this problem`,`What ${inp.audience} tell us they tried before ${inp.offer}`], hook:`If you're ${inp.audience} dealing with [problem], this is for you.`, cta:`DM us or visit the link in bio to learn more.` }, 321 { name:'Offer Breakdown', desc:`Explain ${inp.offer} with specificity.`, ideas:[`What's inside ${inp.offer} â the full breakdown`,`Who ${inp.offer}
321 is built for (and who it's not)`,`How ${inp.offer} works from start to finish`,`The difference between ${inp.offer} and everything else in ${inp.industry}`,`FAQ about ${inp.offer} â the real questions answered`], hook:`Here's exactly what you get with ${inp.offer} â no surprises.`, cta:`Full details at the link in bio. Questions? DM us.` }, 322 { name:'Social Proof', desc:`Let results, reviews, and real customers do the selling.`, ideas:[`A customer result we're proud of â with the full story`,`What ${inp.audience} say after 30 days with ${inp.offer}`,`The review that surprised even us`,`Before & after: how ${inp.offer} changed things for a real customer`,`X customers in â here's what we're hearing`], hook:`Don't take our word for it. Here's what ${inp.audience} say.`, cta:`Ready to add your name to this list? Link in bio.` }, 323 { name:'Objection Handling', desc:`Meet your audience where their doubts live.`, ideas:[`"Is ${inp.offer} right for me?" â honest answer`,`The hesitation we hear most often (and why it makes sense)`,`What makes ${inp.offer} worth it, even if you've tried other things`,`For ${inp.audience} who aren't sure they're ready`,`Why now is actually a better time than waiting`], hook:`The reason you haven't bought yet is probably one of these things.`, cta:`Still have questions? DM us. We'll be straight with you.` }, 324 { name:'Your Story & Credibility', desc:`Show who's behind ${inp.brand} and why they're qualified.`, ideas:[`Why we built ${inp.offer}`,`What qualifies us to help ${inp.audience} with ${inp.industry}`,`The mistake that led to building ${inp.brand}`,`What we've learned after serving ${inp.audience}`,`Our values â and how they show up in ${inp.offer}`], hook:`There are options in ${inp.industry}. Here's why ${inp.audience} choose us.`, cta:`Follow ${inp.brand} â and reach out when you're ready.` }, 325 ], 326 }; 327 return byGoal[goal] || byGoal.grow; 328 } 329 330 function fillHook(hook, inp) { 331 return hook.replace(/{industry}/g, inp.industry).replace(/{audience}/g, inp.audience).replace(/{offer}/g, inp.offer).replace(/{brand}/g, inp.brand); 332 } 333 334 function getQuickWin(industry, goal, inp) { 335 const key = `${industry}_${goal}`; 336 if (QUICK_WINS[key]) return QUICK_WINS[key]; 337 const fallbacks = { 338 grow: `Post one honest, specific insight from your work in ${inp.industry} this week. Not a tip â a real observation. Something ${inp.audience} rarely hear from anyone in your space.`, 339 sales: `Write three sentences about ${inp.offer}: the problem it solves, who it's for, and one specific outcome a real customer got. Then add your contact or link.`, 340 trust: `Share something you got wrong recently in ${inp.industry} and specifically what you changed because of it. Honest, specific, and humble content is the fastest trust-builder there is.`, 341 educate: `Teach one small, specific, genuinely useful thing to ${inp.audience} about ${inp.industry}. Make it complete â not a teaser.`, 342 hired: `Post a breakdown of a recent project: the brief, your process, and the outcome. Show your actual thinking, not just the final product.`, 343 launch: `Share the "why" behind ${inp.offer} in 3-5 sentences. No features, no specs. Just the problem it solves, who it's for, and why you cared enough to build it.`, 344 event: `Post the clearest possible invite for ${inp.offer}: who should come, what they will get, when it happens, and why it is worth showing up.`, 345 }; 346 return fallbacks[goal] || fallbacks.grow; 347 } 348 349 function ssmGetPillars(industry, goal) { 350 return LIBRARY[`${industry}_${goal}`] || null; 351 } 352 353 function ssmBuildStrategy(inp) { 354 let rawPillars = ssmGetPillars(inp.industry, inp.goal); 355 if (!rawPillars) rawPillars = getFallback(inp.industry, inp.goal, inp); 356 357 const tonePool = HOOKS[inp.tone] || HOOKS.bold; 358 const series = SERIES[inp.goal] || SERIES.grow; 359 const quickWin = getQuickWin(inp.industry, inp.goal, inp); 360 361 // Convert SSM pillars â PostIQ pillar format with trust layer tags 362 const pillars = rawPillars.map((p, i) => ({ 363 id: cpUid(), 364 name: p.name, 365 promise: p.desc, 366 layer: LAYER_MAP[i] || 'awareness', 367 seeds: (p.ideas || []).slice(0, 5).map(s => String(s || '')), 368 tones: {}, 369 hook: fillHook(tonePool[i] || tonePool[0], inp), 370 cta: p.cta || '', 371 })); 372 373 if (inp.notes && pillars.length) { 374 pillars[0].seeds = [ 375 `Context to work in: ${inp.notes}`, 376 ...pillars[0].seeds 377 ].slice(0, 6); 378 } 379 380 return { pillars, series, quickWin, inp }; 381 } 382 383 // ââ SSM FORM LOGIC âââââââââââââââââââââââââââââââââââââââââââââ 384 385 function ssmInit() { 386 const backBtn = cpQs('ssmBackBtn'); 387 const generateBtn = cpQs('ssmGenerateBtn'); 388 if (!backBtn || !generateBtn) return; 389 390 backBtn.onclick = () => cpShowStage('cpStageGate'); 391 generateBtn.onclick = ssmRunGenerate; 392 } 393 394 function ssmRunGenerate() { 395 const generationStartTime = Date.now(); 396 window.__postiqSsmGenerationStart = generationStartTime; 397 const brand = (cpQs('ssm-brand')?.value || '').trim() || 'Your Brand'; 398 const industry = cpQs('ssm-industry')?.value || ''; 399 const audience = (cpQs('ssm-audience')?.value || '').trim() || 'your audience'; 400 const offer = (cpQs('ssm-offer')?.value || '').trim() || 'your offer'; 401 const tone = cpQs('ssm-tone')?.value || 'bold'; 402 const goal = cpQs('ssm-goal')?.value || 'grow'; 403 const notes = (cpQs('ssm-notes')?.value || '').trim(); 404 405 if (!industry) { if (typeof showToast === 'function') showToast('Select an industry first', 'error'); return; } 406 if (!goal) { if (typeof showToast === 'function') showToast('Select a goal first', 'error'); return; } 407 408 const inp = { brand, industry, audience, offer, tone, goal, notes } 409 cpState._ssmInputs = inp; 410 safeTrack(() =>
410 GA4_Pillars.ssmQuestionnaireComplete({ industry, tone, goal })); 411 safeTrack(() => GA4_Pillars.ssmGenerationStarted()); 412 413 // Show loading screen 414 const form = cpQs('ssmForm'); 415 const loading = cpQs('ssmLoading'); 416 if (form) form.style.display = 'none'; 417 if (loading) loading.style.display = 'block'; 418 419 const steps = ['Detecting your industry...','Matching goal + industry combo...','Pulling pillar templates...','Generating hooks for your tone...','Finalizing your pillar plan...']; 420 const bar = cpQs('ssmLoadBar'); 421 const label = cpQs('ssmLoadStep'); 422 let i = 0; 423 const iv = setInterval(() => { 424 if (bar) bar.style.width = ((i + 1) * 20) + '%'; 425 if (label) label.textContent = steps[i] || 'Almost ready...'; 426 i++; 427 if (i >= 5) { 428 clearInterval(iv); 429 setTimeout(() => ssmFinish(inp), 200); 430 } 431 }, 320); 432 } 433 434 function ssmFinish(inp) { 435 const result = ssmBuildStrategy(inp); 436 437 // Set identity from inputs 438 cpState.identity = `${inp.brand} â ${inp.audience} â ${inp.offer}${inp.notes ? ` â Context: ${inp.notes}` : ''}`; 439 cpState.pillars = result.pillars.map(cpNormalizePillar); 440 safeTrack(() => GA4_Pillars.ssmGenerationComplete(cpState.pillars.length)); 441 safeTrack(() => GA4_System.performanceMetric('ssm_generation', Date.now() - (window.__postiqSsmGenerationStart || Date.now()))); 442 cpState._ssmInputs = inp; 443 444 cpPersist(); 445 446 // Move to builder 447 cpShowStage('cpStageBuilder'); 448 449 // Populate identity field 450 const bi = cpQs('cpBuilderIdentity'); 451 if (bi) bi.value = cpState.identity; 452 453 // Render pillars in builder 454 cpRenderPillars(); 455 456 // Keep the quick-win starter inside the pillar plan. Do not auto-insert into Compose. 457 cpState._latestGeneratedStarter = result.quickWin || ''; 458 459 // Replace any old pillar-generated series templates so reset/new generation stays clean. 460 cpClearGeneratedTemplates(); 461 462 // Save recurring series as PostIQ templates 463 ssmSaveSeriesAsTemplates(result.series, inp); 464 465 if (typeof showToast === 'function') { 466 showToast('Pillar plan generated â review your starters below', 'success'); 467 } 468 469 // Restore form for next time 470 const form = cpQs('ssmForm'); 471 const loading = cpQs('ssmLoading'); 472 const bar = cpQs('ssmLoadBar'); 473 if (form) form.style.display = 'block'; 474 if (loading) loading.style.display = 'none'; 475 if (bar) bar.style.width = '0%'; 476 } 477 478 function ssmSendQuickWin(text) { 479 const editor = document.getElementById('composerEditor'); 480 if (!editor) return; 481 const existing = editor.innerText.trim(); 482 editor.innerText = existing ? `${existing}\n\n${text}` : text; 483 editor.dispatchEvent(new InputEvent('input', { bubbles: true, cancelable: true })); 484 } 485 486 function ssmSaveSeriesAsTemplates(series, inp) { 487 if (!Array.isArray(series) || !series.length) return; 488 const now = new Date().toISOString(); 489 const newTpls = series.map(s => ({ 490 id: cpUid(), 491 title: s.name, 492 type: 'Hooks', 493 platform: 'Universal', 494 tags: [CP_TEMPLATE_TAG, 'pillar-plan', 'series', inp.industry, inp.goal, inp.notes ? 'custom-context' : ''].filter(Boolean), 495 body: inp.notes ? `${s.desc}\n\nContext to consider: ${inp.notes}` : s.desc, 496 createdAt: now, 497 updatedAt: now, 498 })); 499 if (typeof state !== 'undefined' && Array.isArray(state.templates)) { 500 state.templates = [...newTpls, ...state.templates]; 501 if (typeof persistTemplates === 'function') persistTemplates(); 502 if (typeof renderTemplates === 'function') renderTemplates(); 503 } 504 } 505 506 // ââ BUILDER RENDER âââââââââââââââââââââââââââââââââââââââââââââ 507 508 function cpRenderPillars() { 509 const list = cpQs('cpPillarsList'); 510 if (!list) return; 511 cpState.pillars = cpState.pillars.map(cpNormalizePillar); 512 list.innerHTML = ''; 513 cpState.pillars.forEach((pillar, pi) => { 514 const card = document.createElement('div'); 515 card.className = 'cp-pillar-card'; 516 card.dataset.pid = pillar.id; 517 const usage = cpUsageFor(pillar.id); 518 card.innerHTML = `<div class="cp-pillar-head"><div class="cp-pillar-tab" style="background:${pillarColor(pi)};"></div><div class="cp-pillar-head-inner"><div class="cp-pillar-inputs"><input class="cp-pillar-name" data-field="name" value="${cpEsc(pillar.name)}" placeholder="Pillar name" /><input class="cp-pillar-promise" data-field="promise" value="${cpEsc(pillar.promise)}" placeholder="The recurring promise this pillar makesâ¦" /></div></div><div class="cp-pillar-head-right"><select class="cp-layer-select" data-field="layer"><option value="" ${pillar.layer ? '' : 'selected'}>
518Tag layerâ¦</option><option value="awareness" ${pillar.layer==='awareness'?'selected':''}>ð Awareness</option><option value="credibility" ${pillar.layer==='credibility'?'selected':''}>ð Credibility</option><option value="action" ${pillar.layer==='action'?'selected':''}>ð Action</option></select><span class="cp-usage-badge ${usage > 0 ? 'used' : ''}">${usage > 0 ? `${usage} starter${usage > 1 ? 's' : ''}` : 'unused'}</span></div></div>` + 519 (pillar.hook ? `<div style="font-size:11px;font-family:'DM Mono',monospace;color:var(--brand);background:var(--brand-dim);border-top:1px solid var(--brand-glow);padding:8px 13px;line-height:1.45;">Hook: "${cpEsc(pillar.hook)}"</div>` : '') + 520 `<div class="cp-seeds" data-seeds-for="${pillar.id}">${pillar.seeds.map((seed, si) => cpSeedRowHtml(pillar, si, seed)).join('')}</div><div class="cp-pillar-footer"><button class="btn sm ghost" data-action="add-seed" type="button" style="font-size:11px;height:26px;padding:0 10px;">+ Add seed idea</button><button class="btn sm ghost" data-action="remove-pillar" type="button" style="font-size:11px;height:26px;padding:0 8px;color:var(--subtle);">Remove pillar</button></div>`; 521 522 card.querySelectorAll('[data-field="name"],[data-field="promise"]').forEach(inp => { 523 inp.addEventListener('input', () =>
523 { pillar[inp.dataset.field] = inp.value; cpPersist(); cpUpdateHealth(); }); 524 }); 525 card.querySelector('[data-field="layer"]').addEventListener('change', e => { pillar.layer = e.target.value; cpPersist(); cpUpdateLayerCheck(); }); 526 card.querySelector('[data-action="add-seed"]').addEventListener('click', () => { pillar.seeds.push(''); cpRenderPillars(); cpPersist(); }); 527 card.querySelector('[data-action="remove-pillar"]').addEventListener('click', () => { 528 if (!confirm('Remove this pillar?')) return; 529 cpState.pillars = cpState.pillars.filter(p => p.id !== pillar.id); 530 cpRenderPillars(); cpPersist(); cpUpdateHealth(); 531 if (typeof showToast === 'function') showToast('Pillar removed'); 532 }); 533 cpBindSeeds(card, pillar); 534 list.appendChild(card); 535 }); 536 cpUpdateHealth(); 537 cpUpdateLayerCheck(); 538 } 539 540 function cpSeedRowHtml(pillar, si, seed) { 541 const tone = (pillar.tones && pillar.tones[si]) || 'Practical'; 542 return `<div class="cp-seed-row" data-si="${si}"><div class="cp-seed-idx">${si + 1}</div><div class="cp-seed-body"><input class="cp-seed-input" value="${cpEsc(seed)}" placeholder="A specific, real idea you could write aboutâ¦" /><select class="cp-tone-select" data-tone="${si}">${TONES.map(t => `<option value="${t}" ${t === tone ? 'selected' : ''}>${t}</option>`).join('')}</select></div><div class="cp-seed-actions"><button class="cp-seed-btn go" data-action="start" type="button">Start</button><button class="cp-seed-btn del" data-action="del" type="button" title="Remove">Ã</button></div></div>`; 543 } 544 545 function cpBindSeeds(card, pillar) { 546 const wrap = card.querySelector('[data-seeds-for]'); 547 wrap.querySelectorAll('.cp-seed-input').forEach((inp, si) => { 548 inp.addEventListener('input', () => { pillar.seeds[si] = inp.value; cpPersist(); cpUpdateHealth(); }); 549 }); 550 wrap.querySelectorAll('[data-tone]').forEach(sel => { 551 sel.addEventListener('change', e => { if (!pillar.tones) pillar.tones = {}; pillar.tones[+e.target.dataset.tone] = e.target.value; cpPersist(); }); 552 }); 553 wrap.querySelectorAll('[data-action="start"]').forEach((btn, si) => { 554 btn.addEventListener('click', () => { 555 const seed = (pillar.seeds[si] || '').trim(); 556 if (!seed) { if (typeof showToast === 'function') showToast('Add a seed idea first', 'error'); return; } 557 const tone = (pillar.tones && pillar.tones[si]) || 'Practical'; 558 safeTrack(() => GA4_Pillars.seedIdeaStarted()); 559 safeTrack(() => GA4_Composer.composerOpened('pillar_seed')); 560 cpSendToComposer(cpBuildStarter(pillar, seed, tone)); 561 cpBumpUsage(pillar.id); 562 cpRenderPillars(); 563 if (typeof showToast === 'function') showToast('Starter added to Compose', 'success'); 564 }); 565 }); 566 wrap.querySelectorAll('[data-action="del"]').forEach((btn, si) => { 567 btn.addEventListener('click', () => { 568 if (pillar.seeds.length <= 1) pillar.seeds = ['']; 569 else pillar.seeds.splice(si, 1); 570 cpRenderPillars(); cpPersist(); 571 }); 572 }); 573 } 574 575 function cpBuildStarter(pillar, seed, tone) { 576 const identity = (cpState.identity || '').trim(); 577 const voiceLine = identity ? `Voice: ${identity}\n\n` : ''; 578 const openers = { Practical:'Here\'s the clearest way I can explain this:', Story:'Here\'s a moment that changed how I think about this:', Contrarian:'Unpopular take:', Question:'Quick question for you:' }; 579 const hookLine = pillar.hook ? `\nHook: "${pillar.hook}"\n` : ''; 580 const ctaLine = pillar.cta ? `\nCTA: ${pillar.cta}\n` : ''; 581 return `${voiceLine}Pillar: ${pillar.name} â ${pillar.promise}\nTopic: ${seed}${hookLine}${ctaLine}\nPost starter: ${openers[tone] || openers.Practical}`; 582 } 583 584 function cpSendToComposer(text) { 585 const editor = document.getElementById('composerEditor'); 586 if (editor) { 587 const existing = editor.innerText.trim(); 588 editor.innerText = existing ? `${existing}\n\n${text}` : text; 589 editor.dispatchEvent(new InputEvent('input', { bubbles: true, cancelable: true })); 590 updateComposerClearButtonVisibility(); 591 if (typeof window.activateView === 'function') window.activateView('composerView'); 592 editor.focus(); 593 return true; 594 } 595 return false;
596 } 597 598 function cpUpdateHealth() { 599 const total = cpState.pillars.length; 600 const seeds = cpState.pillars.reduce((a, p) => a + p.seeds.filter(s => String(s || '').trim()).length, 0); 601 const used = cpTotalUsage(); 602 const hP = cpQs('cpHealthPillars'), hS = cpQs('cpHealthSeeds'), hU = cpQs('cpHealthUsage'); 603 if (hP) hP.textContent = total; 604 if (hS) hS.textContent = seeds; 605 if (hU) hU.textContent = used; 606 const score = Math.min(100, (Math.min(total, 5) / 5) * 40 + (Math.min(seeds, 15) / 15) * 40 + (Math.min(used, 5) / 5) * 20); 607 const bar = cpQs('cpHealthBar'); 608 if (bar) { bar.style.width = `${score}%`; bar.className = `cp-health-fill${score >= 70 ? ' good' : score >= 40 ? ' warn' : ''}`; } 609 const msgs = [[90,'Pillar system firing on all cylinders.'],[70,'Strong foundation. Keep creating starters.'],[50,'Looking solid. Start from a few seeds.'],[20,'Good start â add more seed ideas.'],[0,'Add your pillars to start.']]; 610 const msg = msgs.find(m => score >= m[0]); 611 const el = cpQs('cpHealthMsg'); if (el) el.textContent = (msg || msgs[msgs.length - 1])[1]; 612 } 613 614 function cpUpdateLayerCheck() { 615 const has = l => cpState.pillars.some(p => p.layer === l); 616 const fmt = l => has(l) ? '<span style="color:var(--green);font-weight:700;">â</span>' : '<span style="color:var(--border2);">â</span>'; 617 const a = cpQs('cpLayerA'), c = cpQs('cpLayerC'), x = cpQs('cpLayerX'); 618 if (a) a.innerHTML = fmt('awareness'); 619 if (c) c.innerHTML = fmt('credibility'); 620 if (x) x.innerHTML = fmt('action'); 621 } 622 623 function cpRenderCompact() { 624 const wrap = document.getElementById('composerPillarsCompact'); 625 if (!wrap) return; 626 const pillars = cpState.pillars.slice(0, 4); 627 if (!pillars.length) { 628 wrap.innerHTML = "<div style=\"font-size:12px;color:var(--subtle);padding:8px 0;font-family:'DM Mono',monospace;\">Build your pillars in the Ideas tab to see quick starters here.</div>"; 629 return; 630 } 631 wrap.innerHTML = ''; 632 const usage = cpGetUsage(); 633 pillars.forEach(pillar => { 634 const card = document.createElement('div'); 635 card.className = 'cp-compact-card'; 636 const seed = pillar.seeds.find(s => String(s || '').trim()) || ''; 637 const count = usage[pillar.id] || 0; 638 const unusedBadge = count === 0 ? `<span style="font-size:10px;font-family:'DM Mono',monospace;color:var(--subtle);border:1px solid var(--border);padding:1px 6px;border-radius:999px;margin-left:6px;">unused</span>` : ''; 639 const draftedLabel = count > 0 ? `${count} starter${count > 1 ? 's' : ''} created` : 'Not used yet'; 640 const seedHtml = seed ? `<div class="cp-compact-seed">${cpEsc(seed)}</div><div class="cp-compact-row"><span style="font-size:10px;font-family:'DM Mono',monospace;color:var(--subtle);">${draftedLabel}</span><button class="btn sm primary" type="button" style="height:24px;font-size:11px;padding:0 8px;" data-start="${cpEsc(pillar.id)}">Start</button></div>` : ''; 641 card.innerHTML = `<div class="cp-compact-name">${cpEsc(pillar.name)}${unusedBadge}</div>${seedHtml}`; 642 const btn = card.querySelector('[data-start]'); 643 if (btn) btn.addEventListener('click', e => { 644 e.stopPropagation(); 645 const p = cpState.pillars.find(item => item.id === pillar.id); 646 if (p) { cpSendToComposer(cpBuildStarter(p, seed, 'Practical')); cpBumpUsage(p.id); cpRenderCompact(); if (typeof showToast === 'function') showToast('Starter added to Compose', 'success'); } 647 }); 648 wrap.appendChild(card); 649 }); 650 } 651 652 function init() { 653 const hasData = cpLoad(); 654 655 // Gate: SSM path 656 const gN = cpQs('cpGateNew'); 657 if (gN) gN.addEventListener('click', () => { safeTrack(() => GA4_Pillars.ssmQuestionnaireStarted()); cpShowStage('cpStageJourney'); ssmInit(); }); 658 659 // Gate: manual path 660 const gE = cpQs('cpGateExperienced'); 661 if (gE) gE.addEventListener('click', () => { 662 if (!cpState.pillars.length) { 663 // Seed default pillars 664 cpState.pillars = ['Behind The Scenes','Education & Value','Hot Takes & POV','Community & Stories','Your Offer & Story'].map((name, i) => ({ 665 id: cpUid(), name, promise: 'Define the recurring promise this pillar makes to your audience.', layer: LAYER_MAP[i] || 'awareness', seeds: [''], tones: {}, hook: '', cta: '', 666 })); 667 } 668 cpShowStage('cpStageBuilder'); 669 const bi = cpQs('cpBuilderIdentity'); if (bi) bi.value = cpState.identity || ''; 670 cpRenderPillars(); cpPersist(); 671 }); 672 673 // Builder UI 674 const bi = cpQs('cpBuilderIdentity'); 675 if (bi) bi.addEventListener('input', e => { cpState.identity = e.target.value; cpPersist(); }); 676 677 const ap = cpQs('cpAddPillarBtn'); 678 if (ap) ap.addEventListener('click', () => { 679 const newPillar = { id: cpUid(), name: 'New Pillar', promise: 'The recurring promise this pillar makesâ¦', layer: '', seeds: [''], tones: {}, hook: '', cta: '' }; 680 cpState.pillars.push(newPillar); 681 safeTrack(() => GA4_Pillars.pillarCreated({ icon: 'none', seeds: newPillar.seeds.length, trustLayer: false })); 682 cpRenderPillars(); cpPersist(); if (typeof showToast === 'function') showToast('Pillar added'); 683 }); 684 685 const rb = cpQs('cpRestartBtn'); 686 if (rb) rb.addEventListener('click', () => { 687 if (!confirm('Start over? This clears your pillars, starter counts, and pillar-generated series templates.')) return; 688 const removedTemplates = cpClearGeneratedTemplates(); 689 cpState.pillars = []; 690 cpState.identity = ''; 691 cpState._ssmInputs = null; 692 cpState._latestGeneratedStarter = '';
693 try { localStorage.removeItem(CP_KEY); localStorage.removeItem(USAGE_KEY); } catch {} 694 const bi = cpQs('cpBuilderIdentity'); if (bi) bi.value = ''; 695 ['ssm-brand','ssm-industry','ssm-audience','ssm-offer','ssm-tone','ssm-goal','ssm-notes'].forEach(id => { const el = cpQs(id); if (el) el.value = ''; }); 696 cpRenderPillars(); 697 cpShowStage('cpStageGate'); 698 cpRenderCompact(); 699 if (typeof showToast === 'function') showToast(`Content pillars reset${removedTemplates ? ` â removed ${removedTemplates} generated template${removedTemplates > 1 ? 's' : ''}` : ''}.`, 'success'); 700 }); 701 702 const eb = cpQs('cpExportBtn'); 703 if (eb) eb.addEventListener('click', () => { 704 const lines = ['# My Content Pillars\n', `Voice: ${cpState.identity || '(not set)'}\n`]; 705 cpState.pillars.forEach(p => { 706 lines.push(`\n## ${p.name}`, `Promise: ${p.promise}`, `Layer: ${p.layer || 'untagged'}`, p.hook ? `Hook: "${p.hook}"` : '', p.cta ? `CTA: ${p.cta}` : ''); 707 p.seeds.filter(s => String(s || '').trim()).forEach((s, i) => lines.push(`${i + 1}. ${s}`)); 708 }); 709 const a = document.createElement('a'); 710 a.href = URL.createObjectURL(new Blob([lines.filter(Boolean).join('\n')], { type: 'text/plain' })); 711 a.download = 'content-pillars.txt'; 712 a.click(); 713 URL.revokeObjectURL(a.href); 714 if (typeof showToast === 'function') showToast('Exported', 'success'); 715 }); 716 717 if (hasData) { 718 cpShowStage('cpStageBuilder'); 719 const bi2 = cpQs('cpBuilderIdentity'); if (bi2) bi2.value = cpState.identity || ''; 720 cpRenderPillars(); 721 } 722 cpRenderCompact(); 723 } 724 725 return { 726 init, 727 renderCompact: cpRenderCompact, 728 renderDraftCompact: cpRenderCompact, 729 getData: () => ({ identity: cpState.identity, pillars: cpState.pillars }), 730 insertStarter: (pillar, seed, dateLabel) => { 731 const normalized = cpNormalizePillar(pillar || { name: 'Pillar', promise: 'Post starter' }); 732 const topic = String(seed || '').trim(); 733 if (!topic) return false; 734 const starter = `${dateLabel ? `Date: ${dateLabel}\n` : ''}${cpBuildStarter(normalized, topic, 'Practical')}`; 735 const wrote = cpSendToComposer(starter);
736 if (wrote && normalized.id) cpBumpUsage(normalized.id); 737 cpRenderCompact(); 738 return wrote; 739 }, 740 }; 741})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.