local ADDON_NAME, RPAL = ...

-- ============================================================
-- ODYN Persona Beta 2.0 — coherent mind / semantic renderer
--
-- The older Persona layers remain responsible for observation, continuity,
-- location, relationships and cadence.  This layer turns those facts into a
-- stable point of view.  A thought is selected for semantic fit first, checked
-- against the character's persistent profile, and only then rendered through
-- the calibrated Voice Register.
-- ============================================================

local VERSION=2
local MAX_RECENT=36

local function trim(s)return tostring(s or""):gsub("^%s+",""):gsub("%s+$","")end
local function clamp(v,a,b)v=tonumber(v)or a;if v<a then return a elseif v>b then return b end;return v end
local function now()return time and time()or 0 end
local function lower(s)return trim(s):lower():gsub("’","'"):gsub("%s+"," ")end
local function shortName(value)
    local s=trim(value):match("^[^-]+")or trim(value)
    s=s:gsub("|c%x%x%x%x%x%x%x%x",""):gsub("|r",""):gsub("%s+"," ")
    return trim(s:match("^([^%s]+)")or s)
end
local function push(t,v,max)t[#t+1]=v;while#t>(max or MAX_RECENT)do table.remove(t,1)end end

local TRAITS={
    "chaoticLawful","forgivingVindictive","altruisticSelfish","truthfulDeceitful","gentleBrutal",
    "superstitiousRational","renegadeParagon","cautiousImpulsive","asceticBonVivant",
    "valorousSpineless","introvertedExtroverted",
}

local function rawTrait(p,key)
    if RPAL.GetEffectivePersonalityTrait then
        local ok,v=pcall(RPAL.GetEffectivePersonalityTrait,key);if ok and tonumber(v)then return clamp(v,0,100)end
    end
    return clamp(p and p.traits and p.traits[key]or 50,0,100)
end
local function rawAlignment(p)
    local a=p and p.alignment or{good=.34,neutral=.46,evil=.20}
    if RPAL.GetEffectivePersonalityAlignment then local ok,v=pcall(RPAL.GetEffectivePersonalityAlignment);if ok and type(v)=="table"then a=v end end
    local g,n,e=clamp(a.good,0,1),clamp(a.neutral,0,1),clamp(a.evil,0,1);local total=g+n+e
    if total<=0 then return{good=.34,neutral=.46,evil=.20}end
    return{good=g/total,neutral=n/total,evil=e/total}
end

local function ensureMind(p)
    if type(p)~="table"then return nil end
    p.mindV2=type(p.mindV2)=="table"and p.mindV2 or{}
    local m=p.mindV2;m.version=VERSION;m.stableTraits=type(m.stableTraits)=="table"and m.stableTraits or{}
    m.stableAlignment=type(m.stableAlignment)=="table"and m.stableAlignment or{}
    m.recent=type(m.recent)=="table"and m.recent or{};while#m.recent>MAX_RECENT do table.remove(m.recent,1)end
    m.stats=type(m.stats)=="table"and m.stats or{generated=0,rejected=0,legacyRewritten=0}
    return m
end

local function updateStableProfile(p,m)
    local t=now();local previous=tonumber(m.profileAt)or 0
    local maxShift=previous<=0 and 100 or clamp(math.max(.35,(t-previous)/3600*.75),.35,3.0)
    for _,key in ipairs(TRAITS)do
        local target=rawTrait(p,key);local old=tonumber(m.stableTraits[key])
        if not old then m.stableTraits[key]=target else m.stableTraits[key]=old+clamp(target-old,-maxShift,maxShift)end
    end
    local a=rawAlignment(p)
    if not tonumber(m.stableAlignment.good)then
        m.stableAlignment={good=a.good,neutral=a.neutral,evil=a.evil}
    else
        local step=maxShift/100
        for _,key in ipairs({"good","neutral","evil"})do local old=tonumber(m.stableAlignment[key])or a[key];m.stableAlignment[key]=old+clamp(a[key]-old,-step,step)end
        local total=(m.stableAlignment.good or 0)+(m.stableAlignment.neutral or 0)+(m.stableAlignment.evil or 0)
        if total>0 then for _,key in ipairs({"good","neutral","evil"})do m.stableAlignment[key]=m.stableAlignment[key]/total end end
    end
    m.profileAt=t
end

local PROFILE_LABELS={
    warmth="warmth",guard="guardedness",restraint="restraint",candor="candor",force="force",
    reason="reason",wonder="instinct",impulse="impulse",indulgence="indulgence",courage="courage",
    social="sociability",forgiveness="forgiveness",duty="duty",autonomy="independence",self="self-interest",
}

local function buildProfile(p)
    local m=ensureMind(p);if not m then return nil end;updateStableProfile(p,m)
    local function axis(key)return clamp(((tonumber(m.stableTraits[key])or 50)-50)/50,-1,1)end
    local order=axis("chaoticLawful");local grudge=axis("forgivingVindictive");local selfInterest=axis("altruisticSelfish")
    local secrecy=axis("truthfulDeceitful");local force=axis("gentleBrutal");local reason=axis("superstitiousRational")
    local dutyAxis=axis("renegadeParagon");local impulse=axis("cautiousImpulsive");local indulgence=axis("asceticBonVivant")
    local fear=axis("valorousSpineless");local social=axis("introvertedExtroverted")
    local a=m.stableAlignment;local moral=clamp((tonumber(a.good)or 0)-(tonumber(a.evil)or 0),-1,1)
    local profile={
        order=order,grudge=grudge,self=selfInterest,secrecy=secrecy,force=force,reason=reason,duty=dutyAxis,
        impulse=impulse,indulgence=indulgence,fear=fear,social=social,moral=moral,
        warmth=clamp((moral-selfInterest-(force*.62))/2.15,-1,1),
        guard=clamp((secrecy-impulse-(social*.22)+(fear*.32)+(grudge*.18))/2.45,-1,1),
        restraint=clamp((order+dutyAxis-impulse)/3,-1,1),candor=-secrecy,wonder=-reason,
        courage=-fear,forgiveness=-grudge,autonomy=clamp((-order-dutyAxis+(impulse*.45))/2.45,-1,1),
        alignment={good=a.good,neutral=a.neutral,evil=a.evil},mind=m,
    }
    local ranked={}
    for key,label in pairs(PROFILE_LABELS)do local value=tonumber(profile[key])or 0;if math.abs(value)>=.22 then ranked[#ranked+1]={key=key,label=label,value=value}end end
    table.sort(ranked,function(x,y)if math.abs(x.value)==math.abs(y.value)then return x.key<y.key end;return math.abs(x.value)>math.abs(y.value)end)
    profile.dominants={};for i=1,math.min(4,#ranked)do profile.dominants[i]=ranked[i]end
    local labels={};for i=1,math.min(3,#profile.dominants)do local d=profile.dominants[i];labels[#labels+1]=(d.value>=0 and""or"low ")..d.label end
    profile.summary=#labels>0 and table.concat(labels," + ")or"balanced profile"
    return profile
end

function RPAL.GetPersonaMindProfile()
    local p=RPAL.GetPersonalityData and RPAL.GetPersonalityData();return buildProfile(p)
end

local function V(text,lean,side)return{text=text,lean=lean or{},side=side}end
local function recentMap(p)
    local seen={};local h=type(p and p.history)=="table"and p.history or{}
    for i=math.max(1,#h-119),#h do local r=h[i];if type(r)=="table"then seen[lower(r.rawText)]=true;seen[lower(r.text)]=true end end
    return seen
end
local function strongestLens(lean,profile)
    local best,bestScore="balanced",0
    for key,value in pairs(lean or{})do local score=math.abs((tonumber(value)or 0)*(tonumber(profile[key])or 0));if score>bestScore then best,bestScore=key,score end end
    return best
end
local function chooseVariant(rows,profile,p,name,side)
    if type(rows)~="table"or#rows==0 or not profile then return nil end
    local seen=recentMap(p);local candidates={};local best=-999
    for _,row in ipairs(rows)do
        if type(row)=="string"then row=V(row)end
        if type(row)=="table"and(not row.side or row.side==side)then
            local text=trim(row.text);local needs=text:find("%%s")~=nil
            if not needs or trim(name)~=""then
                if needs then text=string.format(text,shortName(name))end
                local score=tonumber(row.base)or 1
                for key,w in pairs(row.lean or{})do
                    local fit=(tonumber(profile[key])or 0)*(tonumber(w)or 0);score=score+(fit*1.18)
                    if fit<-.34 then score=score-(math.abs(fit)*1.25)end
                end
                if seen[lower(text)]then score=score-4 end
                local lens=strongestLens(row.lean,profile);local recent=profile.mind.recent
                for i=math.max(1,#recent-4),#recent do local old=recent[i];if old and old.lens==lens and lens~="balanced"then score=score-.18 end end
                candidates[#candidates+1]={text=text,score=score,lens=lens,row=row};if score>best then best=score end
            end
        end
    end
    local top,total={},0
    for _,c in ipairs(candidates)do if c.score>=best-.72 then local w=math.max(.04,c.score-(best-.82));c.weight=w;total=total+w;top[#top+1]=c end end
    if#top==0 then return nil end
    local roll=math.random()*total;local picked=top[#top]
    for _,c in ipairs(top)do roll=roll-c.weight;if roll<=0 then picked=c;break end end
    return picked.text,{version=VERSION,lens=picked.lens,fit=picked.score,profile=profile.summary}
end

local AMBIENT={
    warm={
        V("I keep noticing when somebody goes quiet at the edge of a conversation.",{warmth=1,social=.15}),
        V("Seeing somebody relax after a little kindness sits well with me.",{warmth=1}),
        V("I still remember the people who helped without making a performance of it.",{warmth=.9,restraint=.3}),
        V("Somebody trying not to ask for help is usually more obvious than they think.",{warmth=.8,reason=.2}),
        V("I like the small moment when somebody realizes they are not handling everything alone.",{warmth=1}),
        V("Quiet decency tends to stay with me longer than grand promises do.",{warmth=.8,duty=.35}),
    },
    self={
        V("I have only so much patience to spend, and I feel protective of what is left.",{self=1,guard=.2}),
        V("I keep measuring whether a problem is actually mine before I let it become mine.",{self=.8,reason=.35}),
        V("Being useful does not mean I need to be available to everybody.",{self=.8,candor=.2}),
        V("Part of me wants to keep the easier option for myself this time.",{self=1,indulgence=.15}),
        V("I am less willing than usual to spend energy on somebody else's urgency.",{self=.9,restraint=.2}),
        V("Wanting something for myself does not need to become a whole argument.",{self=.7,candor=.25}),
    },
    candid={
        V("I would rather say the plain part and deal with the reaction.",{candor=1,courage=.25}),
        V("Keeping the truth simple feels easier than carrying a careful version of it.",{candor=1}),
        V("I keep wanting to name exactly what I mean instead of talking around it.",{candor=.9}),
        V("There is relief in not having to remember which version I told somebody.",{candor=1,reason=.2}),
        V("If something matters, I would rather make the important part clear.",{candor=.8,duty=.2}),
        V("I am not in the mood to soften an honest answer until it means something else.",{candor=.85,force=.2}),
    },
    guarded={
        V("I am still deciding how much of myself I want visible.",{guard=1,secrecy=.5}),
        V("There are a few thoughts I would rather keep entirely mine.",{guard=.9,secrecy=.6}),
        V("I keep noticing which questions reveal more than the answers do.",{guard=.8,reason=.3}),
        V("Trust feels easier when I can see what somebody does after the words.",{guard=.9,reason=.45}),
        V("I do not need everybody to understand what I am aiming for yet.",{guard=.8,autonomy=.25}),
        V("Part of me is still listening for the detail that does not fit.",{guard=.85,reason=.25}),
    },
    ordered={
        V("Loose ends keep tugging at my attention.",{order=1,duty=.25}),
        V("I feel better when I know what has been promised and what is actually finished.",{order=.8,duty=.6}),
        V("One clear next step would quiet half the noise in my head.",{order=.9,reason=.2}),
        V("I keep sorting small obligations into an order without meaning to.",{order=.9,duty=.35}),
        V("It bothers me when a simple responsibility is left vague.",{order=.8,duty=.55}),
        V("Knowing where everything stands makes it easier to breathe.",{order=.85,guard=.15}),
    },
    freeform={
        V("A small change of plan sounds more interesting than it probably should.",{autonomy=.8,impulse=.45}),
        V("The less obvious option keeps catching my attention.",{autonomy=.9,impulse=.25}),
        V("I would not mind finding my own way through this instead of the usual one.",{autonomy=1}),
        V("Routine starts to itch when I stay inside it too long.",{autonomy=.9,impulse=.35}),
        V("Part of me wants to take the odd route just to see what it changes.",{autonomy=.8,wonder=.2}),
        V("I keep looking for the part nobody said had to stay the same.",{autonomy=1,reason=.1}),
    },
    gentle={
        V("My first instinct is still to lower the pressure, not add to it.",{force=-1,warmth=.35}),
        V("I would rather leave somebody room to recover than corner them into an answer.",{force=-.9,warmth=.4}),
        V("There is usually a way to be clear without making the moment cruel.",{force=-.8,candor=.35}),
        V("I keep noticing how much easier people breathe when nobody is pushing them.",{force=-.8,warmth=.35}),
        V("A softer hand often gets further than another hard demand.",{force=-.85,reason=.2}),
        V("I do not like using more force than a moment actually needs.",{force=-1,restraint=.35}),
    },
    forceful={
        V("I am getting tired of treating every hard edge like it needs soft handling.",{force=1,candor=.2}),
        V("Some problems become easier the moment I stop being delicate with them.",{force=.9,reason=.2}),
        V("Pressure makes more sense to me when it is direct.",{force=.8,candor=.3}),
        V("Part of me would rather meet resistance head-on than keep circling it.",{force=.9,courage=.4}),
        V("I have little patience for pretending a blunt answer is something else.",{force=.75,candor=.5}),
        V("Once a line is crossed, I do not always feel like answering gently.",{force=.85,grudge=.25}),
    },
    rational={
        V("I keep wanting one solid fact before I settle on an answer.",{reason=1,guard=.2}),
        V("The simplest explanation still seems more useful than the most dramatic one.",{reason=1,restraint=.2}),
        V("I am more interested in what can be checked than what merely feels possible.",{reason=1}),
        V("One detail keeps failing to fit the rest, and that matters more than the mood around it.",{reason=.9,guard=.2}),
        V("I keep separating what I know from what I only suspect.",{reason=1,candor=.15}),
        V("A clean explanation would settle me more than another confident guess.",{reason=.9}),
    },
    instinct={
        V("One small coincidence keeps feeling a little too neat to ignore.",{wonder=1,guard=.15}),
        V("I cannot explain the feeling yet, but it has not left me alone.",{wonder=.9}),
        V("Some patterns feel noticeable before they make any sensible kind of sense.",{wonder=.85,reason=-.2}),
        V("Part of me keeps treating that little sign as if it matters.",{wonder=1}),
        V("The timing of that still feels stranger than the thing itself.",{wonder=.8,guard=.2}),
        V("I have learned not to dismiss every uneasy instinct just because it arrives without proof.",{wonder=.8,guard=.35}),
    },
    cautious={
        V("I keep noticing what could go wrong before I notice what might work.",{impulse=-1,guard=.35}),
        V("Knowing the way out makes it easier to consider going in.",{impulse=-.9,courage=-.15}),
        V("I would rather take one extra look than discover the missing detail too late.",{impulse=-1,reason=.25}),
        V("Part of me is already measuring the cost of being wrong.",{impulse=-.8,self=.15}),
        V("I keep wanting a little more room before I commit to the next move.",{impulse=-.9,guard=.2}),
        V("The risk feels easier to carry once I have named it properly.",{impulse=-.75,candor=.2}),
    },
    impulsive={
        V("Part of me has already moved before the rest of me finishes deciding.",{impulse=1}),
        V("Waiting is making the first available choice look better by the second.",{impulse=.9}),
        V("I keep wanting to start and let the answer catch up with me.",{impulse=1,autonomy=.25}),
        V("The urge to act is louder than the reasons to keep standing still.",{impulse=.9,courage=.2}),
        V("I would rather adjust on the way than think the moment to death.",{impulse=.9,reason=-.2}),
        V("Something in me is getting ready to say yes before the question is finished.",{impulse=.85,social=.15}),
    },
    restrained={
        V("I do not need much around me to feel settled.",{indulgence=-1,restraint=.3}),
        V("A little quiet and fewer things to manage sounds like enough.",{indulgence=-.9,social=-.2}),
        V("The simple option keeps sounding better than the impressive one.",{indulgence=-.9,reason=.15}),
        V("I feel lighter when there is less to carry, choose, and maintain.",{indulgence=-.85}),
        V("Comfort is easier to enjoy when it does not ask much from me afterward.",{indulgence=-.8,reason=.2}),
        V("I could happily leave a few unnecessary wants alone.",{indulgence=-1,restraint=.25}),
    },
    indulgent={
        V("Something rich and warm would improve my mood.",{indulgence=1}),
        V("A proper meal sounds worth taking time over.",{indulgence=.9,restraint=.1}),
        V("I keep thinking about the kind of comfort that makes an hour disappear.",{indulgence=.9}),
        V("There is no virtue in making every pleasure difficult.",{indulgence=.85,autonomy=.2}),
        V("Good food and familiar company would be difficult to refuse.",{indulgence=.8,social=.35}),
        V("Part of me wants something needlessly pleasant for once.",{indulgence=1,self=.15}),
    },
    brave={
        V("Pressure is easier to bear once I decide I am not moving.",{courage=1,force=.2}),
        V("The ugly part feels less frightening when I face it directly.",{courage=.9,candor=.25}),
        V("I can feel fear without letting it choose the direction.",{courage=1,restraint=.25}),
        V("Part of me gets steadier when there is finally something real to stand against.",{courage=.9,force=.15}),
        V("I would rather know the danger in front of me than imagine it from a distance.",{courage=.8,reason=.25}),
        V("Once I commit to holding my ground, the noise in my head gets quieter.",{courage=1,duty=.2}),
    },
    avoidant={
        V("I keep checking how easy it would be to step away if things turn ugly.",{courage=-1,guard=.35}),
        V("Distance is starting to look more useful than proving a point.",{courage=-.85,reason=.25}),
        V("I feel better when I know I have not let the exit disappear behind me.",{courage=-.9,impulse=-.25}),
        V("Part of me wants fewer people between me and open space.",{courage=-.8,social=-.2}),
        V("There are moments when leaving early is the only sensible victory.",{courage=-.8,self=.25}),
        V("I do not like how quickly a crowded situation can stop feeling optional.",{courage=-.8,guard=.3}),
    },
    private={
        V("Quiet company sounds better than a crowded conversation.",{social=-1,warmth=.15}),
        V("I would rather talk properly with one person than be half-present with several.",{social=-.9,candor=.2}),
        V("A little space from everybody would feel good without meaning anything dramatic.",{social=-1}),
        V("I keep saving my better answers for conversations that are not fighting a roomful of noise.",{social=-.8,guard=.2}),
        V("Silence feels comfortable when nobody expects me to fill it.",{social=-.9,restraint=.2}),
        V("I do not mind company; I mind having to perform for it.",{social=-.8,guard=.25}),
    },
    social={
        V("A familiar voice would do me good.",{social=1,warmth=.2}),
        V("I feel more awake when a conversation has somewhere to go.",{social=.9}),
        V("Part of me wants to find somebody easy to talk to.",{social=1}),
        V("A little shared noise sounds better than being alone with every thought.",{social=.85,warmth=.15}),
        V("I keep wondering who might be in the mood for an unplanned conversation.",{social=.9,impulse=.15}),
        V("Good company could make an ordinary hour feel useful.",{social=.85,warmth=.2}),
    },
    forgiving={
        V("The irritation is already losing some of its hold.",{forgiveness=1,warmth=.15}),
        V("I can remember the mistake without wanting to live inside it.",{forgiveness=.9,restraint=.2}),
        V("Part of me is tired of paying attention to an anger that has stopped being useful.",{forgiveness=.8,reason=.2}),
        V("The person is starting to come back into focus around the thing they did.",{forgiveness=.9,warmth=.25}),
        V("I do not need to pretend it was fine to let it take up less room.",{forgiveness=1,candor=.2}),
        V("The sharpest part of that memory is finally wearing down.",{forgiveness=.9}),
    },
    grudge={
        V("I still remember the exact part that crossed the line.",{forgiveness=-1,guard=.2}),
        V("The anger has gone quiet; it has not gone away.",{forgiveness=-1,restraint=.25}),
        V("Part of me keeps the old detail ready in case I need it.",{forgiveness=-.9,guard=.35}),
        V("I am not interested in acting as though time alone repaired anything.",{forgiveness=-1,candor=.2}),
        V("The memory still changes how much trust I am willing to offer.",{forgiveness=-.85,guard=.45}),
        V("I have not forgotten what they were willing to do when it suited them.",{forgiveness=-.9,reason=.2}),
    },
    dutiful={
        V("An unfinished promise has a way of following me.",{duty=1,candor=.2}),
        V("I keep measuring the next step against what I said I would do.",{duty=1,order=.25}),
        V("Knowing somebody is counting on me makes the loose end harder to ignore.",{duty=.9,warmth=.2}),
        V("Responsibility feels clearer once I stop pretending it belongs to nobody.",{duty=.9,candor=.2}),
        V("I would rather carry the difficult part than become the reason it was left undone.",{duty=1,courage=.25}),
        V("My word feels heavier after somebody decides to rely on it.",{duty=1,warmth=.15}),
    },
    independent={
        V("Being told there is only one acceptable way makes me want another.",{autonomy=1}),
        V("I keep separating what I actually want from what was expected of me.",{autonomy=.9,candor=.2}),
        V("Part of me would rather own a difficult choice than borrow an easy one.",{autonomy=1,courage=.2}),
        V("I feel boxed in fastest when somebody decides for me before asking.",{autonomy=.9,force=.15}),
        V("The freedom to choose matters even when I choose the sensible thing.",{autonomy=.85,reason=.2}),
        V("I do not like obligations that arrive disguised as assumptions.",{autonomy=.9,guard=.2}),
    },
    pragmatic={
        V("I keep looking for the part that actually changes what happens next.",{reason=.35,restraint=.2}),
        V("The outcome matters more to me than making the explanation sound clean.",{reason=.3,candor=.2}),
        V("There are details worth caring about and details that only make more noise.",{reason=.35}),
        V("I am still measuring how much effort this really deserves.",{reason=.25,self=.1}),
        V("One useful answer would be better than another perfect theory.",{reason=.45}),
        V("I keep coming back to what can actually be done with what I know.",{reason=.4,duty=.1}),
    },
}

local LENS_STRENGTH={
    warm=function(p)return p.warmth end,self=function(p)return p.self end,candid=function(p)return p.candor end,
    guarded=function(p)return p.guard end,ordered=function(p)return p.order end,freeform=function(p)return p.autonomy end,
    gentle=function(p)return-p.force end,forceful=function(p)return p.force end,rational=function(p)return p.reason end,
    instinct=function(p)return p.wonder end,cautious=function(p)return-p.impulse end,impulsive=function(p)return p.impulse end,
    restrained=function(p)return-p.indulgence end,indulgent=function(p)return p.indulgence end,brave=function(p)return p.courage end,
    avoidant=function(p)return-p.courage end,private=function(p)return-p.social end,social=function(p)return p.social end,
    forgiving=function(p)return p.forgiveness end,grudge=function(p)return-p.forgiveness end,dutiful=function(p)return p.duty end,
    independent=function(p)return p.autonomy end,pragmatic=function()return.32 end,
}

local function chooseAmbient(p,profile)
    local state=p.state or{};local weighted={};local recent=profile.mind.recent
    for lens,fn in pairs(LENS_STRENGTH)do
        local strength=clamp(fn(profile),-1,1);local w=strength>.18 and(.35+strength*1.75)or 0
        if lens=="indulgent"then w=w+(tonumber(state.appetite)or 0)*.55
        elseif lens=="freeform"or lens=="impulsive"then w=w+(tonumber(state.restlessness)or 0)*.36
        elseif lens=="guarded"or lens=="cautious"then w=w+(tonumber(state.vigilance)or 0)*.42
        elseif lens=="social"or lens=="warm"then w=w+(tonumber(state.connection)or 0)*.28
        elseif lens=="rational"or lens=="instinct"or lens=="private"then w=w+(tonumber(state.reflection)or 0)*.25 end
        for i=math.max(1,#recent-4),#recent do if recent[i]and recent[i].lens==lens then w=w*.64 end end
        if w>.02 then weighted[#weighted+1]={lens=lens,weight=w}end
    end
    local total=0;for _,r in ipairs(weighted)do total=total+r.weight end;if total<=0 then weighted={{lens="pragmatic",weight=1}};total=1 end
    local roll=math.random()*total;local lens=weighted[#weighted].lens
    for _,r in ipairs(weighted)do roll=roll-r.weight;if roll<=0 then lens=r.lens;break end end
    local line,meta=chooseVariant(AMBIENT[lens]or AMBIENT.pragmatic,profile,p,nil,nil)
    return line,meta,lens
end

local ASSOCIATION_POS={
    V("There is something about %s that still draws me back.",{warmth=.2}),
    V("I have not lost my interest in %s.",{candor=.2}),
    V("More time around %s would be easy to welcome.",{indulgence=.15}),
    V("I keep finding small reasons to enjoy %s.",{warmth=.15}),
    V("The thought of %s sits better with me than most things do.",{guard=.1}),
    V("I would not mind making a little room for %s soon.",{impulse=.1}),
}
local ASSOCIATION_NEG={
    V("My patience for %s is still thin.",{forgiveness=-.2}),
    V("The thought of dealing with %s already wears on me.",{guard=.2}),
    V("I keep finding fresh reasons to avoid %s.",{courage=-.1}),
    V("I would be content with less %s in my life for a while.",{restraint=.15}),
    V("Whatever appeal %s has for other people, I am not feeling it.",{candor=.25}),
    V("I have had enough of %s to know it rarely improves my mood.",{reason=.2}),
}
local function associationThought(p,profile)
    if not RPAL.GetPersonalityTopAssociations then return nil end
    local rows=RPAL.GetPersonalityTopAssociations(24,false)or{};local eligible={};local t=now()
    for _,r in ipairs(rows)do
        local age=r.lastAt and math.max(0,t-(tonumber(r.lastAt)or 0))or nil
        local recent=age and age<=30*86400
        local manual=math.abs(tonumber(r.manual)or 0)>=.2
        if math.abs(tonumber(r.score)or 0)>=.28 and((tonumber(r.confidence)or 0)>=.26)and(recent or manual)then
            local w=.5+math.abs(r.score)*(recent and 2.2 or .8)+(recent and math.max(0,1-age/(30*86400))*1.4 or 0)
            eligible[#eligible+1]={row=r,weight=w}
        end
    end
    if#eligible==0 then return nil end
    local total=0;for _,r in ipairs(eligible)do total=total+r.weight end;local roll=math.random()*total;local pick=eligible[#eligible].row
    for _,r in ipairs(eligible)do roll=roll-r.weight;if roll<=0 then pick=r.row;break end end
    local label=lower(pick.display or pick.key);if label==""then return nil end
    local line,meta=chooseVariant((pick.score or 0)>=0 and ASSOCIATION_POS or ASSOCIATION_NEG,profile,p,label,nil)
    if meta then meta.association=pick.key;meta.associationScore=pick.score;meta.associationRecent=pick.lastAt end
    return line,meta
end

function RPAL.GeneratePersonaMindThought(context,p)
    p=p or(RPAL.GetPersonalityData and RPAL.GetPersonalityData());local profile=buildProfile(p);if not profile then return nil end
    local line,meta
    if math.random()<.16 then line,meta=associationThought(p,profile)end
    local lens
    if not line then line,meta,lens=chooseAmbient(p,profile)end
    if not line then return nil end
    meta=meta or{version=VERSION,profile=profile.summary,lens=lens or"balanced"};meta.intent=meta.association and"preference"or"ambient"
    return line,meta.association and"preference"or"reflective","persona-mind-v2",meta
end

local STATE_BANK={
    CALM={V("Nothing in me feels in a hurry right now.",{restraint=.4}),V("My thoughts are moving without pulling against each other.",{reason=.2}),V("I feel settled enough to notice small things without chasing them.",{guard=-.2}),V("The quiet inside me feels earned rather than empty.",{courage=.15})},
    RESTLESS={V("Stillness is starting to feel like something I have to work at.",{impulse=.5}),V("My hands want a task before my mind has chosen one.",{impulse=.35}),V("I keep looking for a reason to move.",{autonomy=.35}),V("The unused energy in me is getting difficult to ignore.",{force=.15})},
    GUARDED={V("I have not relaxed enough to stop reading the small details.",{guard=.6}),V("Part of me is keeping more distance than the moment seems to require.",{guard=.55}),V("I keep listening for what is not being said.",{guard=.55,reason=.2}),V("Trust is not arriving as quickly as the words are.",{guard=.6})},
    WARM={V("I feel more open to company than I did a little while ago.",{warmth=.5,social=.2}),V("There is an easy warmth sitting under everything else.",{warmth=.55}),V("I keep feeling a little glad about the people close to me.",{warmth=.5}),V("The softer part of my mood is winning for once.",{force=-.25,warmth=.4})},
    IRRITATED={V("My patience has gone thin around the edges.",{force=.25}),V("Small things are landing harder than they deserve.",{guard=.2}),V("I can feel myself answering problems more sharply in my head.",{candor=.2,force=.2}),V("The irritation is quiet, but it is not subtle.",{forgiveness=-.2})},
    REFLECTIVE={V("I keep finding another angle on the same memory.",{reason=.3}),V("My mind is in no hurry to leave the question alone.",{restraint=.25}),V("One small detail keeps changing the shape of what I remember.",{reason=.25}),V("I am listening to my own reaction more closely than usual.",{candor=.25})},
    FOCUSED={V("My attention is holding one clean line without much effort.",{reason=.45}),V("Everything unrelated feels a little farther away.",{restraint=.3}),V("I can stay with the detail long enough to understand it properly.",{reason=.5}),V("My thoughts feel precise instead of hurried.",{reason=.4,impulse=-.25})},
    WEARY={V("The weight behind my eyes is getting difficult to ignore.",{}),V("My body feels a step behind my thoughts.",{}),V("Even small effort is starting to feel expensive.",{restraint=.15}),V("I keep imagining how good it would feel to stop moving for a while.",{indulgence=.2})},
    UNCOMFORTABLE={V("The ache keeps stealing pieces of my attention.",{}),V("My body is arranging every movement around the sore place.",{impulse=-.15}),V("I keep finding the edge of the pain and backing away from it.",{guard=.15}),V("The discomfort is making ordinary posture feel deliberate.",{})},
    HURT={V("The hurt is quieter now, but it is still close enough to touch.",{warmth=.15}),V("Ordinary thoughts keep brushing against the sore part of this.",{}),V("I am carrying the feeling forward even though the moment is over.",{forgiveness=-.1}),V("Part of me keeps returning to where the pain changed shape.",{reason=.15})},
    DISTRAUGHT={V("I cannot keep one clean line of thought going.",{}),V("Everything smaller keeps disappearing under the same heavy feeling.",{}),V("My attention keeps folding back toward the same center.",{}),V("I can tell I am not fully present even when I try to be.",{candor=.15})},
    SELF_CONSCIOUS={V("I keep hearing my own words back with too much attention.",{social=.2}),V("I am suddenly aware of everything I do with my hands and face.",{guard=.2}),V("That awkward moment is taking longer to fade than it deserves.",{}),V("Part of me keeps wondering how I looked from the other side.",{reason=.15})},
    WITHDRAWN={V("Keeping to myself feels easier than risking another raw edge.",{social=-.4,guard=.3}),V("I do not have much of myself available for company right now.",{social=-.45}),V("The quiet around me feels safer than another conversation.",{social=-.4,guard=.35}),V("I can feel myself pulling inward before anybody asks anything.",{guard=.3})},
    RELIEVED={V("I keep noticing the pressure that is no longer there.",{restraint=.2}),V("Breathing feels easier now that I am not bracing for the same thing.",{}),V("The relief is small, but my whole body seems to know about it.",{indulgence=.1}),V("I can finally let my attention move somewhere else.",{reason=.15})},
    TENSE={V("My body is still treating every quick movement as useful information.",{guard=.4}),V("I have not stopped measuring the space around me.",{guard=.4,courage=-.1}),V("Part of me is ready before I have decided what for.",{impulse=.2,guard=.3}),V("The tension is keeping my attention too close to the surface.",{})},
    CONCERNED={V("Part of my attention keeps returning to whether they are alright.",{warmth=.4}),V("I keep thinking about the strain I noticed in them.",{warmth=.35}),V("Something in me is still waiting to know they are safe.",{warmth=.45,guard=.1}),V("Their distress has not quite stopped feeling like unfinished business.",{warmth=.3,duty=.25})},
}

function RPAL.GeneratePersonaStateThought(state,p,source)
    p=p or(RPAL.GetPersonalityData and RPAL.GetPersonalityData());local profile=buildProfile(p);local label=type(state)=="table"and state.label or state
    local line,meta=chooseVariant(STATE_BANK[tostring(label or""):upper()],profile,p,nil,nil)
    if meta then meta.intent="state";meta.state=tostring(label or""):upper();meta.source=source end
    return line,meta
end
RPAL.GeneratePersonaCognitiveStateThought=RPAL.GeneratePersonaStateThought

local WORLD_BANK={
    house={
        V("The familiar quiet in here is letting my shoulders drop.",{guard=.3,social=-.2}),V("Everything is where I expect it to be, and that settles me.",{order=.5}),
        V("This place feels easier to breathe in than anywhere crowded.",{social=-.4}),V("The room is comfortable enough that leaving it can wait.",{indulgence=.4}),
        V("I know the sounds of this place well enough to notice when one changes.",{guard=.5}),V("There is not much here I do not need, and I like it that way.",{indulgence=-.45}),
        V("The stillness in here makes company sound unexpectedly welcome.",{social=.35,warmth=.2}),V("Staying inside too long is starting to wake the restless part of me.",{impulse=.35,autonomy=.3}),
    },
    stormwind={
        V("Stormwind is busy enough that a quiet person can disappear into it.",{social=-.4,guard=.2}),V("There is always another voice nearby in Stormwind if I want one.",{social=.45}),
        V("The city falls into familiar routes when I pay attention.",{order=.4}),V("Stormwind rewards keeping track of who is watching whom.",{guard=.45}),
        V("Somewhere in this city is food worth stopping for.",{indulgence=.4}),V("Stormwind offers more than I actually need.",{indulgence=-.4}),
        V("Another side street keeps looking more interesting than the usual route.",{autonomy=.4,impulse=.2}),V("The crowd makes it easier to notice who still makes room for somebody else.",{warmth=.35}),
    },
    city={
        V("The crowd is loud enough to give my thoughts some cover.",{social=-.3,guard=.2}),V("A city always has another conversation waiting somewhere nearby.",{social=.4}),
        V("I keep noticing the paths people repeat without thinking.",{order=.35,reason=.2}),V("Busy streets make me more aware of what I am carrying.",{guard=.35}),
        V("There is probably something good to eat within a few streets of here.",{indulgence=.4}),V("Most of what the city offers is easier to admire than to need.",{indulgence=-.35}),
        V("The less obvious street is tempting me again.",{autonomy=.4}),V("It is easier to judge a place by how its hurried people treat somebody in the way.",{warmth=.3,reason=.2}),
    },
    outdoors={
        V("Open space makes it easier to notice where the tension was sitting.",{guard=.2}),V("The air out here is doing more for me than I expected.",{indulgence=-.15}),
        V("I like being able to see farther than the next wall.",{courage=-.1,guard=.25}),V("There are more directions available out here than I need, which is pleasant.",{autonomy=.35}),
        V("The quiet outside has a different weight from the quiet indoors.",{social=-.25}),V("A long walk would give the restless part of me something honest to do.",{impulse=.3}),
    },
}
function RPAL.GeneratePersonaWorldThought(world,p,context)
    p=p or(RPAL.GetPersonalityData and RPAL.GetPersonalityData());local profile=buildProfile(p);if not profile then return nil end
    local key=world and world.insideHouse and"house"or(world and world.isStormwind and"stormwind"or(world and world.isCity and"city"or(world and world.outdoors and"outdoors"or nil)))
    local line,meta=chooseVariant(WORLD_BANK[key],profile,p,nil,nil);if meta then meta.intent="observation";meta.world=key end
    return line,key and("world-"..key)or nil,meta
end

local RELATION_POS={
    V("Having %s nearby feels easier than it used to.",{warmth=.5}),V("I keep noticing the little ways %s has become familiar.",{reason=.15,warmth=.3}),
    V("%s is becoming somebody I can be quiet around without needing to fix the silence.",{social=-.25,warmth=.45}),V("A conversation with %s sounds better than being left with my own noise.",{social=.4}),
    V("I trust the steadiness of %s more than I trust grand assurances.",{guard=.3,duty=.2}),V("Part of me already counts on %s being there, and that is worth noticing.",{duty=.25,warmth=.35}),
}
local RELATION_CLOSE={
    V("The closeness with %s no longer feels like something casual.",{warmth=.55,candor=.2}),V("My body relaxes around %s before I think to tell it to.",{warmth=.5,guard=-.25}),
    V("What happened with %s has changed the distance between us.",{candor=.3,warmth=.35}),V("I keep remembering how natural it felt to have %s that close.",{warmth=.45,indulgence=.2}),
    V("Trusting %s with that much closeness still leaves part of me exposed.",{guard=.45,warmth=.25}),V("The tenderness after everything with %s is staying with me more than the heat of it.",{warmth=.55,restraint=.2}),
}
local RELATION_NEG={
    V("I still keep a little extra distance around %s.",{guard=.45}),V("The details around %s make more sense when I remember not to relax too quickly.",{guard=.4,reason=.25}),
    V("My irritation with %s has gone quiet, not gone away.",{forgiveness=-.45}),V("I would rather know where %s is than be surprised by them.",{guard=.5,courage=-.1}),
    V("Part of me wants to confront %s directly instead of carrying the tension.",{force=.4,candor=.25}),V("I can feel some of the anger around %s losing its grip, even if trust is not back.",{forgiveness=.4,guard=.25}),
}
local RELATION_UNCERTAIN={
    V("I have not settled on what %s means to me yet.",{guard=.2}),V("Another honest conversation with %s would probably change the shape of this.",{candor=.3}),
    V("I keep watching what %s does when there is nothing to gain from it.",{guard=.35,reason=.3}),V("Something about %s still feels unfinished in my head.",{}),
    V("Part of me wants to know %s better before deciding anything.",{social=.25,impulse=-.15}),V("I am curious what happens if I stop holding %s at arm's length.",{warmth=.2,guard=-.15}),
}
function RPAL.GeneratePersonaRelationshipThought(rel,p,mode)
    if type(rel)~="table"then return nil end;p=p or(RPAL.GetPersonalityData and RPAL.GetPersonalityData());local profile=buildProfile(p);local name=shortName(rel.name or rel.display)
    if name==""then return nil end
    local attraction=tonumber(rel.attraction)or 0;local comfort=tonumber(rel.comfort)or 0;local warmth=tonumber(rel.warmth)or 0;local tension=tonumber(rel.tension)or 0;local score=tonumber(rel.score)or 0
    local pool,kind
    if attraction>=.42 and comfort>=.48 then pool,kind=RELATION_CLOSE,"close"
    elseif tension>=.40 or score<=-.28 then pool,kind=RELATION_NEG,"tense"
    elseif warmth>=.34 or score>=.28 or rel.kind=="love"or rel.kind=="friend"or rel.kind=="family"then pool,kind=RELATION_POS,"warm"
    else pool,kind=RELATION_UNCERTAIN,"uncertain"end
    local line,meta=chooseVariant(pool,profile,p,name,nil);if meta then meta.intent="relationship";meta.person=name;meta.relationship=kind;meta.mode=mode end
    return line,meta
end

function RPAL.GeneratePersonaMoralThought(kind,p)
    p=p or(RPAL.GetPersonalityData and RPAL.GetPersonalityData());local profile=buildProfile(p);if not profile then return nil end
    local lens=kind=="good"and"warm"or(kind=="dark"and(profile.self>.15 and"self"or"grudge")or"pragmatic")
    local line,meta=chooseVariant(AMBIENT[lens],profile,p,nil,nil);if meta then meta.intent="moral";meta.moralKind=kind end
    return line,meta
end

function RPAL.GeneratePersonaInjuryThought(item,p)
    if type(item)~="table"then return nil end;p=p or(RPAL.GetPersonalityData and RPAL.GetPersonalityData());local profile=buildProfile(p);if not profile then return nil end
    local label=lower(item.customName or item.type or"injury");local sev=lower(item.severity or"moderate")
    local rows={
        V("The "..label.." keeps changing how I move, whether I admit it or not.",{candor=.3}),
        V("I hate how the "..label.." makes weakness easy to notice.",{guard=.3,force=.25}),
        V("The "..label.." hurts, but pain does not get to choose my direction.",{courage=.5}),
        V("I need to know what the "..label.." can bear before I make it worse.",{reason=.35,impulse=-.35}),
        V("The "..label.." is costing more attention than I want to spend on it.",{self=.25}),
        V("Pushing through the "..label.." only matters if I am still useful afterward.",{duty=.35,reason=.2}),
    }
    local line,meta=chooseVariant(rows,profile,p,nil,nil);if meta then meta.intent="injury";meta.severity=sev end
    return line,meta
end

-- Episodic thoughts keep the observed event intact. Interpretation confidence
-- controls how boldly the private meaning may be stated: observed behaviour is
-- safe to name; inferred motives remain tentative until repeated evidence or a
-- player clarification confirms them.
local EPISODE_REACTION={
    warm={
        V("When @EVENT@, I answered with warmth. @AFTER@",{warmth=.70,candor=.20}),
        V("@EVENT@. I let myself move closer instead of making distance. @AFTER@",{warmth=.55,guard=-.20}),
        V("I remember when @EVENT@. My response was warmer than guarded. @AFTER@",{warmth=.62,restraint=.15}),
        V("When @EVENT@, I did not keep the feeling entirely at arm's length. @AFTER@",{warmth=.50,guard=-.18}),
    },
    guarded={
        V("When @EVENT@, I kept my reaction guarded. @AFTER@",{guard=.70,reason=.25}),
        V("@EVENT@. I remember holding more back than I said. @AFTER@",{guard=.66,candor=-.20}),
        V("I remember when @EVENT@, and how carefully I kept myself unread. @AFTER@",{guard=.62,restraint=.32}),
        V("When @EVENT@, caution showed before I had a clean answer. @AFTER@",{guard=.58,reason=.32}),
    },
    avoidant={
        V("When @EVENT@, I created distance instead of moving closer. @AFTER@",{guard=.58,social=-.42}),
        V("@EVENT@. I remember wanting room before I wanted an answer. @AFTER@",{autonomy=.52,guard=.42}),
        V("I keep remembering when @EVENT@, and how quickly I moved away from the pressure of it. @AFTER@",{courage=-.28,guard=.45}),
        V("When @EVENT@, I pulled inward before I had sorted out the reason. @AFTER@",{social=-.45,guard=.38}),
    },
    uncertain={
        V("When @EVENT@, I did not give a clean answer. @AFTER@",{reason=.30,guard=.22}),
        V("@EVENT@. I remember the hesitation more clearly than the explanation. @AFTER@",{guard=.35,restraint=.25}),
        V("I keep returning to when @EVENT@. My response was uncertain, even if the event itself was not. @AFTER@",{reason=.35,candor=.18}),
        V("When @EVENT@, I needed longer than the moment gave me to understand my response. @AFTER@",{restraint=.35,reason=.30}),
    },
    hurt={
        V("@EVENT@. I let some of the hurt show. @AFTER@",{warmth=.18,forgiveness=-.22}),
        V("When @EVENT@, hurt reached my response before anything calmer did. @AFTER@",{candor=.28,warmth=.12}),
        V("I keep remembering when @EVENT@, and how exposed the hurt made me. @AFTER@",{guard=.42,forgiveness=-.20}),
        V("@EVENT@. I can remember where my response started to hurt. @AFTER@",{reason=.25,candor=.18}),
    },
    angry={
        V("@EVENT@. I answered with anger. @AFTER@",{force=.34,forgiveness=-.48}),
        V("When @EVENT@, anger arrived faster than a careful answer. @AFTER@",{impulse=.35,force=.35}),
        V("I keep remembering when @EVENT@, and how sharply I reacted. @AFTER@",{force=.30,forgiveness=-.40}),
        V("@EVENT@. I have calmed down since; that does not make my reaction imaginary. @AFTER@",{restraint=.38,candor=.22}),
    },
    vigilant={
        V("When @EVENT@, I stayed on guard. @AFTER@",{guard=.62,reason=.30}),
        V("@EVENT@. My response became watchful before anything else. @AFTER@",{guard=.52,courage=.18}),
        V("I keep remembering when @EVENT@, and how quickly my attention went to danger. @AFTER@",{reason=.42,guard=.40}),
        V("When @EVENT@, I treated the moment like something I might have to protect myself from. @AFTER@",{restraint=.42,guard=.45}),
    },
    neutral={
        V("@EVENT@. I know what happened; I am less certain what it meant to me. @AFTER@",{reason=.42,candor=.18}),
        V("When @EVENT@, the fact registered more clearly than the feeling. @AFTER@",{reason=.35,restraint=.18}),
        V("I keep remembering when @EVENT@. The event is clear even if my reading of myself is not. @AFTER@",{reason=.30,guard=.18}),
        V("@EVENT@. I noticed my response, but I am not going to pretend I understand all of it yet. @AFTER@",{reason=.38,restraint=.20}),
    },
}

local function episodeReactionKind(episode,intent,profile)
    local observed=episode and episode.reaction and tostring(episode.reaction.kind or"")or""
    if observed~=""then return EPISODE_REACTION[observed]and observed or"uncertain"end
    -- No observed response means no emotional canon. The event itself can be
    -- concrete while Persona remains undecided about what the character felt.
    return "neutral"
end

local function episodeAfterClause(episode)
    local interpretation=type(episode and episode.interpretation)=="table"and episode.interpretation or{}
    local status=tostring(interpretation.status or"unknown")
    local label=trim(interpretation.label)
    local confidence=tonumber(interpretation.confidence)or 0
    if episode and episode.status=="resolved"then
        local reaction=episode.reaction and tostring(episode.reaction.kind or"")or""
        if reaction=="avoidant"or reaction=="guarded"or reaction=="uncertain"then return "It does not carry the same tension now." end
        if reaction=="hurt"or reaction=="angry"then return "It is settled now, even if I remember how it landed." end
        return "Whatever it changed then feels more settled now."
    end
    if status=="confirmed"and label~=""and confidence>=.72 then
        local confirmed={
            ["fear"]="I know now that fear was underneath that response.",["overwhelmed"]="I know now that I was overwhelmed by it.",
            ["discomfort"]="I know now that discomfort was part of why I reacted that way.",["hiding warmth"]="I know now that I was hiding more warmth than I showed.",
            ["reciprocal warmth"]="I was not imagining the warmth in my own response.",["warmth"]="The warmth in my response was real.",
            ["uncertainty"]="The uncertainty was real; I did not have a clean answer yet.",["anger"]="The anger in my response was real.",["hurt"]="The hurt in my response was real.",
        }
        return confirmed[label]or("I understand that response more clearly now: "..label..".")
    end
    if status=="inferred"and label~=""then
        if confidence>=.66 then return "It may have been "..label..", but I am not treating that guess as certainty." end
        return "I still cannot tell whether that was "..label.." or something simpler."
    end
    if episode and episode.reaction then return "I still do not want to invent a motive for that response." end
    return "I did not leave enough of a response to know what it meant."
end

local function generateEpisodeThought(episode,p,intent,memory)
    if type(episode)~="table"or trim(episode.anchor)==""then return nil end
    if RPAL.PersonaEpisodePassesThoughtAudit then local ok=RPAL.PersonaEpisodePassesThoughtAudit(episode);if not ok then return nil end end
    p=p or(RPAL.GetPersonalityData and RPAL.GetPersonalityData());local profile=buildProfile(p);if not profile then return nil end
    local reaction=episodeReactionKind(episode,intent,profile);local rows=EPISODE_REACTION[reaction]or EPISODE_REACTION.neutral;local formatted={}
    local anchor=trim(episode.anchor);local after=episodeAfterClause(episode)
    for _,row in ipairs(rows)do
        local text=tostring(row.text);text=text:gsub("@EVENT@",function()return anchor end);text=text:gsub("@AFTER@",function()return after end)
        formatted[#formatted+1]=V(text,row.lean,row.side)
    end
    local line,mind=chooseVariant(formatted,profile,p,nil,nil)
    if mind then
        local interpretation=type(episode.interpretation)=="table"and episode.interpretation or{}
        mind.intent="episodic-memory";mind.memoryKind="episode";mind.eventType=episode.eventType;mind.person=episode.person;mind.reaction=reaction
        mind.episode={eventID=episode.eventID,eventType=episode.eventType,at=episode.at,person=episode.person,anchor=episode.anchor,factKind=episode.factKind,reaction=reaction,behavior=episode.reaction and episode.reaction.behavior,status=episode.status,confidence=episode.confidence,intensity=episode.intensity,weight=episode.weight,interpretationStatus=interpretation.status,interpretation=interpretation.label,interpretationConfidence=interpretation.confidence,cues=episode.cues}
    end
    return line,mind,reaction
end
RPAL.GeneratePersonaEpisodeThought=generateEpisodeThought

function RPAL.GeneratePersonaMemoryThought(memory,p)
    if type(memory)~="table"then return nil end;p=p or(RPAL.GetPersonalityData and RPAL.GetPersonalityData());local meta=type(memory.meta)=="table"and memory.meta or{}
    local episode=RPAL.GetPersonaEpisodeFromMemory and RPAL.GetPersonaEpisodeFromMemory(memory)
    if episode then return generateEpisodeThought(episode,p,"memory",memory)end
    if memory.kind=="relationship"and trim(meta.name)~=""then
        return RPAL.GeneratePersonaRelationshipThought({name=meta.name,score=meta.score,warmth=meta.score and math.max(0,meta.score)or 0,tension=meta.score and math.max(0,-meta.score)or 0,attraction=meta.attraction,comfort=meta.comfort},p,"memory")
    end
    -- Trait, moral and association memories already carry a concrete label in
    -- PersonaContinuity. Let that layer render the label instead of replacing
    -- it with a context-free line about "that memory" or "the moment".
    return nil
end

-- Temporal lanes keep "just happened" separate from actual recollection.
-- LIVE is a reaction, FRESH is the first bit of processing, RECENT uses the
-- reflective event bank below, and only genuinely older/cued material uses the
-- episodic-memory renderer.
local LIVE_EVENT={
    GRATITUDE={V("Huh. Nice hearing that from %s.",{warmth=.55},"incoming"),V("...That actually means something coming from %s.",{warmth=.48,candor=.18},"incoming"),V("Didn't expect %s's thanks to land like that.",{guard=.18,warmth=.42},"incoming")},
    COMPLIMENT={V("Oh. I actually liked hearing that from %s.",{warmth=.42},"incoming"),V("...What am I supposed to do with praise from %s?",{social=-.32,warmth=.25},"incoming"),V("Right. That gave my confidence a little kick.",{self=.26,social=.18},"incoming")},
    APOLOGY={V("Okay. I heard %s.",{forgiveness=.30,candor=.20},"incoming"),V("That takes some of the edge off. Some.",{forgiveness=.24,guard=.28},"incoming"),V("Part of me wants to soften toward %s already.",{forgiveness=.52,warmth=.18},"incoming")},
    AFFECTION={V("...Oh.",{warmth=.62,guard=.12},"incoming"),V("That felt good. More than I expected.",{warmth=.58},"incoming"),V("Careful. %s is getting through the guard.",{guard=.48,warmth=.28},"incoming")},
    SUPPORT={V("Good. %s is actually here.",{warmth=.45,duty=.15},"incoming"),V("I can work with that. I'm not carrying this alone.",{warmth=.38,courage=.18},"incoming"),V("Didn't expect %s being on my side to steady me that much.",{warmth=.42,guard=.12},"incoming")},
    REASSURANCE={V("...I want to believe %s.",{guard=.34,warmth=.18},"incoming"),V("That helped. A little.",{warmth=.28},"incoming"),V("Okay. Breathe. %s might be right.",{reason=.20,warmth=.24},"incoming")},
    VULNERABILITY={V("%s is really telling me this.",{warmth=.34,duty=.20},"incoming"),V("Don't make this heavier for %s than it already is.",{warmth=.38,force=-.18},"incoming"),V("That's... more trust than I expected from %s.",{warmth=.42,guard=.18},"incoming")},
    TRUST_GIVEN={V("%s trusts me. Right.",{duty=.38,warmth=.22},"incoming"),V("That's not nothing. Not from %s.",{warmth=.34},"incoming"),V("Well. Now I actually have something to live up to.",{duty=.48},"incoming")},
    FORGIVENESS={V("...Okay. %s really is letting this go.",{forgiveness=.42},"incoming"),V("I can breathe a little easier now.",{forgiveness=.38},"incoming"),V("That doesn't erase it, but it matters.",{reason=.18,forgiveness=.30},"incoming")},
    PROMISE={V("%s gave me their word.",{duty=.35,guard=.18},"incoming"),V("Right. I'll remember that.",{guard=.28},"incoming"),V("That just made this a lot more serious.",{duty=.34},"incoming")},
    INVITATION={V("Yeah... I like the sound of that.",{social=.22,warmth=.22},"incoming"),V("Part of me already wants to say yes to %s.",{impulse=.42},"incoming"),V("Tempting. Very tempting.",{impulse=.30,warmth=.16},"incoming")},
    BOUNDARY={V("Okay. That's clear.",{candor=.36,order=.18},"incoming"),V("Right. I know where %s's line is now.",{reason=.24,guard=.18},"incoming"),V("I don't have to like the limit to understand it.",{autonomy=.28,reason=.18},"incoming")},
    AGREEMENT={V("Good. %s sees it too.",{social=.18,warmth=.18},"incoming"),V("Nice. No fight over that one.",{warmth=.20},"incoming")},
    DISAGREEMENT={V("Hm. %s has a point to challenge.",{reason=.32},"incoming"),V("No, wait. I want to answer that properly.",{reason=.28,impulse=-.18},"incoming"),V("Part of me wants to push back immediately.",{impulse=.34,force=.20},"incoming")},
    HUMOR_LANDED={V("Heh. Good, %s got it.",{social=.28,warmth=.20},"incoming"),V("Okay, that landed.",{self=.16,social=.24},"incoming"),V("Nice. Read the room right for once.",{reason=.18,social=.20},"incoming")},
    SOCIAL_WARMTH={V("That was... nice.",{warmth=.42},"incoming"),V("I caught that, %s.",{warmth=.32},"incoming"),V("Easy. I could get used to that kind of warmth.",{warmth=.38,guard=-.10},"incoming")},
    REJECTION={V("...Right. Message received.",{guard=.32,warmth=.08},"incoming"),V("Fuck. That stings.",{force=.12,warmth=.08},"incoming"),V("Okay. Don't chase a closed door.",{duty=.18,courage=.16},"incoming")},
    INSULT={V("Oh, fuck off, %s.",{force=.36,forgiveness=-.34},"incoming"),V("...That hit harder than I wanted it to.",{guard=.30,candor=.14},"incoming"),V("Is there actually anything true in that?",{reason=.34},"incoming")},
    MOCKERY={V("Ah. So I'm the joke now.",{social=.22,force=.18},"incoming"),V("Keep the face straight. Don't give %s the satisfaction.",{guard=.36,courage=.18},"incoming"),V("That wasn't joking with me. That was at me.",{reason=.26,forgiveness=-.22},"incoming")},
    HOSTILITY={V("Right. %s wants this ugly.",{guard=.36,force=.24},"incoming"),V("Fine. I can feel the room change around %s.",{guard=.34},"incoming"),V("Part of me wants to meet that edge head-on.",{courage=.36,force=.28},"incoming")},
    THREAT={V("That's a threat. Good to know.",{guard=.42,reason=.24},"incoming"),V("Don't flinch. Watch %s.",{courage=.24,guard=.40},"incoming"),V("How much of that was performance?",{reason=.38,guard=.28},"incoming")},
    AGGRESSION={V("Move. Now.",{guard=.46,impulse=.34},"incoming"),V("Okay. %s just made this physical.",{guard=.42,force=.24},"incoming"),V("Body's already bracing for the next move.",{guard=.44},"incoming")},
    SOCIAL_DISAPPROVAL={V("...Well, %s didn't like that.",{social=.22},"incoming"),V("Right. That look said enough.",{reason=.18,social=.20},"incoming"),V("Do I care what %s thinks? ...A little, apparently.",{self=.18,social=.24},"incoming")},
    HUMOR_REJECTED={V("Oof. That joke did not land.",{social=.26},"incoming"),V("Right. Misread that one.",{reason=.28,social=.18},"incoming"),V("...Yeah, that was worse out loud.",{candor=.26,guard=.16},"incoming")},
    AMBIGUOUS_TONE={V("...What exactly did %s mean by that?",{reason=.34,guard=.20},"incoming"),V("Can't read that cleanly.",{guard=.28,reason=.26},"incoming")},
    RECONCILIATION={V("Okay. %s is actually trying.",{forgiveness=.38,warmth=.20},"incoming"),V("Don't rush it. But... good.",{guard=.24,forgiveness=.30},"incoming")},
    RELATIONSHIP_BREAK={V("...So that's it.",{warmth=.18,guard=.34},"incoming"),V("Fuck. That's actually over.",{force=.12,warmth=.14},"incoming"),V("Don't react before the words finish landing.",{restraint=.34,guard=.26},"incoming")},
}
local FRESH_EVENT={
    GRATITUDE={V("That thank-you from %s is still sitting with me.",{warmth=.52},"incoming"),V("Didn't expect %s's thanks to mean that much.",{warmth=.44,guard=.12},"incoming")},
    COMPLIMENT={V("What %s just said is still giving me a stupid little lift.",{self=.22,warmth=.26},"incoming"),V("I'm still not sure where to put that compliment from %s.",{social=-.28,warmth=.24},"incoming")},
    APOLOGY={V("%s's apology is taking some of the heat out of this.",{forgiveness=.34},"incoming"),V("I heard the apology. I'm still deciding what it changes.",{guard=.34,reason=.24},"incoming")},
    AFFECTION={V("That closeness with %s is still warm in me.",{warmth=.54},"incoming"),V("I'm still feeling how easily %s got past my guard.",{guard=.34,warmth=.30},"incoming")},
    SUPPORT={V("%s being there is still making this feel lighter.",{warmth=.42},"incoming"),V("I'm noticing how much steadier I feel with %s on my side.",{warmth=.38,courage=.18},"incoming")},
    REASSURANCE={V("%s's words are still taking some pressure out of me.",{warmth=.32},"incoming"),V("I'm still checking whether I believe %s. I want to.",{guard=.38,reason=.22},"incoming")},
    VULNERABILITY={V("What %s just trusted me with feels too exposed to brush aside.",{warmth=.42,duty=.22},"incoming"),V("I'm still thinking about how much %s chose to show me.",{warmth=.36,reason=.18},"incoming")},
    TRUST_GIVEN={V("%s trusting me is still carrying weight.",{duty=.42,warmth=.20},"incoming"),V("I'm still wondering what made %s decide I was safe.",{reason=.26,guard=.18},"incoming")},
    FORGIVENESS={V("%s forgiving me is still loosening something in my chest.",{forgiveness=.44},"incoming"),V("The forgiveness matters. The consequence hasn't vanished.",{reason=.20,forgiveness=.28},"incoming")},
    PROMISE={V("%s's promise is still sitting beside whatever they do next.",{guard=.34,reason=.24},"incoming"),V("That word from %s already feels like something I'll measure later.",{duty=.32},"incoming")},
    INVITATION={V("I'm still leaning toward %s's invitation.",{impulse=.34,warmth=.16},"incoming"),V("That invitation sounds better the longer it sits.",{warmth=.24,social=.18},"incoming")},
    BOUNDARY={V("%s's boundary is clear. I'm still feeling out what it changes.",{reason=.28,guard=.18},"incoming"),V("I'm still measuring that line against what I actually want.",{candor=.24,reason=.24},"incoming")},
    HUMOR_LANDED={V("I'm still privately pleased that %s got the joke.",{self=.18,social=.24},"incoming"),V("That laugh from %s took some tension out of the moment.",{warmth=.30,social=.20},"incoming")},
    SOCIAL_WARMTH={V("That little bit of warmth from %s is still hanging around.",{warmth=.38},"incoming"),V("I'm noticing I relaxed around %s without deciding to.",{guard=.28,warmth=.24},"incoming")},
    REJECTION={V("What %s just made clear still stings.",{warmth=.08,guard=.30},"incoming"),V("I'm still trying not to turn %s's rejection into a bigger wound than it is.",{reason=.22,courage=.18},"incoming")},
    INSULT={V("%s's words are still scraping at me.",{force=.24,forgiveness=-.28},"incoming"),V("I'm still checking whether that insult from %s had any truth in it.",{reason=.34,candor=.16},"incoming")},
    MOCKERY={V("%s making me the joke is still sitting badly.",{social=.20,forgiveness=-.24},"incoming"),V("I'm still annoyed at how deliberately %s made that land.",{force=.28,forgiveness=-.30},"incoming")},
    HOSTILITY={V("%s's hostility is still keeping me on edge.",{guard=.38},"incoming"),V("I'm still deciding whether distance from %s is smarter than answering the edge.",{reason=.26,guard=.26},"incoming")},
    THREAT={V("%s's threat is still keeping part of me watchful.",{guard=.40},"incoming"),V("I'm still weighing how seriously to take what %s just said.",{reason=.36,guard=.28},"incoming")},
    AGGRESSION={V("My body still hasn't stopped expecting another move from %s.",{guard=.42},"incoming"),V("I'm still reconstructing how quickly %s turned physical.",{reason=.30,force=.16},"incoming")},
    SOCIAL_DISAPPROVAL={V("%s's reaction is still making me a little too aware of myself.",{social=.22},"incoming"),V("I'm still wondering what %s thought they saw.",{reason=.26},"incoming")},
    HUMOR_REJECTED={V("That failed joke is still making me wince.",{social=.24,candor=.14},"incoming"),V("I'm still checking what I misread before I said it.",{reason=.34},"incoming")},
    AMBIGUOUS_TONE={V("I'm still not sure what %s meant by that.",{guard=.30,reason=.28},"incoming"),V("That tone from %s is still refusing to resolve cleanly.",{reason=.30},"incoming")},
    RECONCILIATION={V("%s trying to repair this still feels fragile, but real.",{forgiveness=.36,warmth=.20},"incoming")},
    RELATIONSHIP_BREAK={V("The ending with %s is still too fresh to feel like memory.",{warmth=.14,guard=.30},"incoming"),V("I'm still waiting for the fact that this is over to feel real.",{guard=.28,candor=.20},"incoming")},
}
local function temporalPhase(event)
    local age=math.max(0,now()-(tonumber(event and event.at)or now()))
    if age<=15 then return"reaction",age end
    if age<=120 then return"fresh-reflection",age end
    if age<=1200 then return"recent-reflection",age end
    return"memory-ready",age
end
local function generateTemporalEvent(event,profile,p,phase)
    local bank=(phase=="reaction"and LIVE_EVENT)or(phase=="fresh-reflection"and FRESH_EVENT)or nil
    if not bank then return nil end
    local rows=bank[event.type];if not rows then return nil end
    local side=event.mine and"mine"or"incoming";local name=event.person and shortName(event.person.name)or""
    local line,meta=chooseVariant(rows,profile,p,name,side)
    if meta then meta.intent=phase;meta.temporalPhase=phase;meta.eventType=event.type;meta.person=name~=""and name or nil;meta.side=side end
    return line,meta
end

-- Event variants are intentionally event-specific.  Their lean vectors let the
-- same observed fact become a different private reaction without changing the
-- fact itself or inventing a reply for the player.
local EVENT={
    GRATITUDE={
        V("There was something in the way %s thanked me that stayed with me.",{warmth=.7},"incoming"),V("%s's thanks made the effort feel more worthwhile than I expected.",{duty=.45,warmth=.35},"incoming"),
        V("I liked hearing that from %s, even if I did not show much of it.",{guard=.35,warmth=.45},"incoming"),V("The thanks from %s landed cleanly; no guessing what they meant.",{candor=.45,reason=.2},"incoming"),
        V("Thanking %s aloud made me realize how much the moment mattered.",{candor=.4,warmth=.4},"mine"),V("I meant the thanks I gave %s. Saying it made the debt feel real.",{duty=.4,candor=.35},"mine"),
    },
    COMPLIMENT={
        V("What %s noticed about me is still sitting pleasantly in my head.",{warmth=.5},"incoming"),V("I never quite know where to put praise from %s, but I liked it.",{social=-.4,warmth=.35},"incoming"),
        V("Part of me is still checking whether %s meant that as plainly as it sounded.",{guard=.45},"incoming"),V("Hearing that from %s gave my confidence a small, shameless lift.",{self=.3,social=.25},"incoming"),
        V("I meant the compliment I gave %s; their reaction made it worth saying.",{candor=.45,warmth=.35},"mine"),
    },
    APOLOGY={
        V("%s's apology took some of the edge off without erasing what happened.",{forgiveness=.45,candor=.2},"incoming"),V("I heard %s apologize. I still want to see what follows the words.",{guard=.55,reason=.3},"incoming"),
        V("Part of me was more ready to soften toward %s than I expected.",{forgiveness=.65,warmth=.2},"incoming"),V("An apology does not settle the account with %s, but it changes the balance.",{forgiveness=-.5,reason=.25},"incoming"),
        V("Apologizing to %s made my own part harder to look away from.",{candor=.5,duty=.25},"mine"),
    },
    AFFECTION={
        V("The way %s showed affection made the distance between us feel smaller.",{warmth=.6},"incoming"),V("I keep remembering how natural that closeness with %s felt.",{warmth=.55,indulgence=.2},"incoming"),
        V("Letting that warmth from %s in still leaves part of me exposed.",{guard=.5,warmth=.25},"incoming"),V("Part of me had already leaned toward %s before I finished thinking.",{impulse=.5,warmth=.35},"incoming"),
        V("Showing %s that much affection made the feeling harder to dismiss as casual.",{candor=.45,warmth=.4},"mine"),
    },
    INTIMACY={
        V("What happened with %s changed how close they feel to me.",{warmth=.55,candor=.25}),V("My body is still remembering the ease of having %s that close.",{indulgence=.35,warmth=.35}),
        V("The intimacy with %s felt less casual than I was prepared to admit.",{guard=.35,candor=.35}),V("Part of me wants that closeness with %s again before the rest of me has sorted it out.",{impulse=.55,indulgence=.25}),
        V("Trusting %s with that much of me carries more weight than the heat of the moment did.",{duty=.35,guard=.35}),V("I can separate desire from trust, but what happened with %s touched both.",{reason=.4,candor=.25}),
    },
    AFTERCARE={
        V("The gentleness from %s afterward is staying with me.",{warmth=.65}),V("Being held by %s after everything made the closeness feel real in a different way.",{warmth=.55,candor=.2}),
        V("I did not expect the quiet care from %s to lower my guard that much.",{guard=.45,warmth=.35}),V("The tenderness after the heat with %s mattered more than I thought it would.",{restraint=.3,warmth=.55}),
        V("Part of me trusts %s more because they stayed attentive when the urgency was gone.",{guard=.3,duty=.3,warmth=.35}),
    },
    SUPPORT={
        V("%s staying with me made the problem feel lighter without making me feel small.",{warmth=.55},"incoming"),V("I noticed that %s helped instead of merely saying they would.",{duty=.45,reason=.25},"incoming"),
        V("Relying on %s still feels unfamiliar, but less dangerous than it did.",{guard=.5,warmth=.25},"incoming"),V("Knowing %s was on my side steadied me more than I showed.",{courage=.25,warmth=.4},"incoming"),
        V("Staying beside %s felt more important than finding the perfect thing to say.",{warmth=.55,duty=.25},"mine"),
    },
    REASSURANCE={
        V("There was something steady in the way %s reassured me.",{warmth=.5},"incoming"),V("%s made the safer possibility sound believable for a moment.",{guard=.3,warmth=.35},"incoming"),
        V("I want to believe %s; part of me is still checking the ground under the promise.",{guard=.55,reason=.25},"incoming"),V("Hearing that from %s took some of the pressure out of my chest.",{warmth=.4},"incoming"),
        V("Reassuring %s made me feel responsible for the certainty I offered.",{duty=.45,candor=.25},"mine"),
    },
    VULNERABILITY={
        V("%s trusted me with something too exposed to treat lightly.",{warmth=.55,duty=.25},"incoming"),V("I keep thinking about the point where %s stopped pretending to be alright.",{warmth=.45,reason=.2},"incoming"),
        V("Part of me wants to help %s; another part is careful not to promise what I cannot carry.",{warmth=.35,guard=.35},"incoming"),V("What %s admitted gave me information they could have kept hidden.",{self=.3,guard=.25},"incoming"),
        V("Letting %s see that fear left me feeling more exposed than the words themselves.",{guard=.45,candor=.3},"mine"),
    },
    TRUST_GIVEN={
        V("Being trusted by %s feels more personal than ordinary praise.",{warmth=.45,duty=.25},"incoming"),V("%s's trust feels like something I need to earn again through what I do next.",{duty=.6},"incoming"),
        V("Part of me likes that %s placed something valuable in my hands.",{self=.25,warmth=.25},"incoming"),V("I keep wondering exactly what made %s decide I was safe to trust.",{guard=.35,reason=.25},"incoming"),
        V("Telling %s I trust them made the risk feel much more concrete.",{guard=.4,candor=.35},"mine"),
    },
    FORGIVENESS={
        V("%s's forgiveness made it easier to breathe without making the mistake disappear.",{forgiveness=.55},"incoming"),V("I am relieved %s forgave me; I still owe the change that makes it matter.",{duty=.55,candor=.2},"incoming"),
        V("Being forgiven by %s does not mean I am ready to forgive myself.",{guard=.25,candor=.3},"incoming"),V("The forgiveness from %s settles the words, not every consequence.",{forgiveness=-.25,reason=.3},"incoming"),
        V("Forgiving %s loosened the anger without asking me to forget.",{forgiveness=.65,candor=.2},"mine"),V("I told %s I forgive them. Trust will still have to return by another road.",{forgiveness=-.35,guard=.45},"mine"),
    },
    PROMISE={
        V("What %s promised is sitting in my mind beside what they do next.",{guard=.45,reason=.3},"incoming"),V("%s giving their word made the moment feel more serious.",{duty=.45,warmth=.2},"incoming"),
        V("Part of me wants to lean on the promise from %s before caution agrees.",{impulse=.35,guard=.25},"incoming"),V("I heard the exact words %s gave me. I will remember them.",{forgiveness=-.2,guard=.35},"incoming"),
        V("The promise I made %s is already shaping the next thing I choose.",{duty=.65},"mine"),V("Giving %s my word felt good in the moment; keeping it is the part that counts.",{reason=.3,duty=.55},"mine"),
    },
    INVITATION={
        V("I liked how naturally %s made room for me in that plan.",{warmth=.45,social=.3},"incoming"),V("Part of me had already accepted %s's invitation before I finished weighing it.",{impulse=.5},"incoming"),
        V("The invitation from %s sounds good; I still want to know what I am walking into.",{guard=.4,impulse=-.3},"incoming"),V("Being included by %s pleased me more than I expected.",{social=.25,warmth=.35},"incoming"),
        V("Inviting %s felt easier than wondering whether they wanted to be included.",{candor=.35,social=.25},"mine"),
    },
    BOUNDARY={
        V("I am glad %s made the boundary plain enough that neither of us has to guess.",{candor=.55,order=.25},"incoming"),V("The line %s drew makes sense; part of me still dislikes feeling contained by it.",{autonomy=.45,reason=.2},"incoming"),
        V("I keep measuring what %s asked against what I actually want from this.",{candor=.3,reason=.25},"incoming"),V("Knowing %s's limit makes the closeness feel safer, not smaller.",{guard=.35,warmth=.3},"incoming"),
        V("Telling %s where my boundary sits made the relationship feel more real.",{candor=.5,guard=.25},"mine"),
    },
    AGREEMENT={
        V("It felt good having %s meet me on that point without a fight.",{social=.25,warmth=.25},"incoming"),V("%s agreeing makes me more confident in the conclusion, not less interested in checking it.",{reason=.4},"incoming"),
        V("I liked the ease of agreeing with %s more than I cared about being right.",{warmth=.35},"incoming"),V("Having %s agree is useful; the result still matters more than the approval.",{self=.2,reason=.3},"incoming"),
    },
    DISAGREEMENT={
        V("I keep turning over the exact point %s challenged.",{reason=.45},"incoming"),V("%s disagreeing makes me want to state my case more plainly, not louder.",{candor=.45,force=-.2},"incoming"),
        V("Part of me wants to push back at %s before I have checked whether they are right.",{impulse=.4,force=.25},"incoming"),V("The disagreement with %s is useful if it exposes the weak part of my thinking.",{reason=.5},"incoming"),
    },
    HUMOR_LANDED={
        V("Hearing %s laugh made the whole moment feel easier.",{warmth=.4,social=.3},"incoming"),V("I am still privately pleased that %s caught the joke exactly as I meant it.",{self=.2,social=.25},"incoming"),
        V("Shared humor with %s took more distance out of the room than I expected.",{warmth=.4},"incoming"),V("%s's laugh was a useful sign that I had read the mood correctly.",{reason=.3},"incoming"),
    },
    SOCIAL_WARMTH={
        V("That small warmth from %s felt easy to accept.",{warmth=.5},"incoming"),V("The way %s moved closer made the familiarity between us feel real.",{warmth=.45},"incoming"),
        V("I noticed myself lowering my guard around %s before I chose to.",{guard=.4,warmth=.25},"incoming"),V("Part of me wanted to return the gesture from %s immediately.",{impulse=.35,warmth=.3},"incoming"),
    },
    OTHER_DISTRESS={
        V("%s did not look alright, and part of my attention is still with them.",{warmth=.55}),V("I keep thinking about the strain %s tried to hide.",{warmth=.45,reason=.2}),
        V("I want to help %s without taking away their choice in how.",{warmth=.45,force=-.25}),V("Something is wrong with %s; I need more than a look before I decide what.",{reason=.4,guard=.25}),
        V("%s's distress matters, but I am measuring how much of it I can actually carry.",{self=.3,warmth=.2}),
    },
    RELATIONSHIP_BREAK={
        V("The absence of %s is already showing up in ordinary parts of my thoughts.",{warmth=.45}),V("I am still angry with %s, and the ending has not made that simpler.",{forgiveness=-.55,force=.2}),
        V("Part of me feels relief where %s used to create pressure, and I do not know what to do with that yet.",{self=.2,candor=.3}),V("I keep replaying where the structure between %s and me finally failed.",{reason=.35,order=.25}),
        V("What ended with %s hurts, but I will not let the hurt choose every next step.",{courage=.45,duty=.2}),V("I miss %s. I also remember exactly why the ending became necessary.",{candor=.45,guard=.25}),
    },
    RECONCILIATION={
        V("The repair with %s feels fragile, but it feels real enough to notice.",{warmth=.4,guard=.25}),V("Part of me was ready to meet %s halfway before pride admitted it.",{forgiveness=.5,candor=.2}),
        V("I am glad %s came back to the conversation; trust will still move at the speed of evidence.",{guard=.45,reason=.3}),V("Fixing things with %s matters more to me than winning the old argument.",{warmth=.45,force=-.15}),
    },
    REJECTION={
        V("Being pushed away by %s still stings more than I want it to.",{warmth=.2}),V("The rejection from %s is turning into anger the longer I sit with it.",{forgiveness=-.4,force=.25}),
        V("Part of me wants distance from everybody after hearing that from %s.",{social=-.45,guard=.25}),V("%s made the answer clear. I would rather respect the line than beg around it.",{duty=.25,candor=.3,courage=.2}),
        V("The way %s rejected me gave me useful information, even if I dislike how it felt.",{reason=.4,self=.15}),
    },
    INSULT={
        V("What %s said hit a softer place than I wanted them to see.",{guard=.35},"incoming"),V("The insult from %s is still feeding my temper.",{force=.35,forgiveness=-.45},"incoming"),
        V("I keep checking whether there was any truth inside what %s said.",{reason=.45,candor=.2},"incoming"),V("Part of me is already tired of giving %s's words this much space.",{forgiveness=.4,self=.2},"incoming"),
        V("I want to answer %s directly, not spend the rest of the day arguing with them in my head.",{candor=.45,courage=.25},"incoming"),
    },
    SOCIAL_DISAPPROVAL={
        V("The look from %s made me suddenly too aware of myself.",{social=.3},"incoming"),V("I keep wondering what %s thought they saw in that moment.",{reason=.3},"incoming"),
        V("%s's reaction annoyed me more than it embarrassed me.",{force=.25,forgiveness=-.25},"incoming"),V("Part of me wants to let %s keep their opinion and stop paying rent for it in my head.",{forgiveness=.35,self=.2},"incoming"),
    },
    HUMOR_REJECTED={
        V("That joke sounded better before I saw %s's reaction.",{social=.25},"incoming"),V("I keep checking what I misread before I made the joke to %s.",{reason=.4},"incoming"),
        V("%s's cold reaction felt harsher than the joke deserved.",{forgiveness=-.25,force=.2},"incoming"),V("Part of me wants to shrug off %s's reaction; another part is still embarrassed.",{forgiveness=.2,guard=.2},"incoming"),
    },
    MOCKERY={
        V("Being made into %s's joke left me too aware of myself.",{social=.25},"incoming"),V("The mockery from %s is still tightening my temper.",{force=.3,forgiveness=-.4},"incoming"),
        V("I keep trying to work out when %s stopped joking with me and started at me.",{reason=.4},"incoming"),V("Part of me refuses to give %s the satisfaction of seeing how it landed.",{guard=.4,courage=.2},"incoming"),
    },
    HOSTILITY={
        V("The open hostility from %s changed how safe the space around them feels.",{guard=.5},"incoming"),V("I am still angry at how easily %s made their contempt obvious.",{force=.3,forgiveness=-.45},"incoming"),
        V("Part of me wants to meet %s's hostility directly instead of shrinking from it.",{courage=.5,force=.3},"incoming"),V("Distance from %s sounds more useful than another ugly exchange.",{courage=-.35,reason=.25},"incoming"),
        V("I let %s hear exactly how hostile I felt, and the anger is still there.",{candor=.35,force=.35},"mine"),
    },
    THREAT={
        V("I keep measuring how much of %s's threat was performance and how much was intent.",{reason=.45,guard=.35},"incoming"),V("The threat from %s makes me want to plant my feet harder.",{courage=.55,force=.25},"incoming"),
        V("Part of me is already mapping the safest distance from %s.",{courage=-.45,guard=.45},"incoming"),V("I have not forgotten the exact words %s chose when they threatened me.",{forgiveness=-.4,guard=.25},"incoming"),
    },
    AGGRESSION={
        V("My body is still braced for another move from %s.",{guard=.45},"incoming"),V("Being struck by %s has left more anger than shock.",{courage=.3,force=.35},"incoming"),
        V("Part of me wants enough distance from %s that surprise stops being possible.",{courage=-.4,guard=.5},"incoming"),V("I keep reconstructing the moment %s decided to get physical.",{reason=.35},"incoming"),
    },
    TRUST_BROKEN={
        V("Trusting %s used to feel ordinary. That is the part I keep missing.",{warmth=.3},"incoming"),V("I am still angry that %s made my trust feel careless.",{forgiveness=-.55,force=.2},"incoming"),
        V("Everything %s said before this is being checked again in my head.",{guard=.55,reason=.35},"incoming"),V("Part of me wants to forgive %s; none of me wants to be fooled twice.",{forgiveness=.35,guard=.45},"incoming"),
        V("I know exactly what advantage %s gained from being trusted now.",{self=.35,guard=.35},"incoming"),
    },
    TRUST_ACCUSATION={
        V("%s saying I broke their trust has me looking harder at my own choices.",{candor=.45,duty=.25},"incoming"),V("Part of me is still arguing with the intention %s assigned to me.",{force=.2,candor=.25},"incoming"),
        V("I keep comparing what I did with what %s believes I meant.",{reason=.45},"incoming"),V("If %s feels betrayed, the consequence matters even if my intention was different.",{duty=.45,warmth=.2},"incoming"),
    },
    AMBIGUOUS_TONE={
        V("I still cannot tell whether %s meant that kindly or wanted it to sting.",{guard=.45,reason=.25},"incoming"),V("The words from %s were simple; the tone was not.",{reason=.35},"incoming"),
        V("Part of me wants to ask %s plainly instead of inventing a second conversation in my head.",{candor=.45},"incoming"),V("I am keeping a little distance from %s until the intention becomes clearer.",{guard=.5},"incoming"),
    },
    SELF_PAIN={V("The pain is changing how carefully I move.",{impulse=-.25}),V("I hate how much attention the ache can take by force.",{force=.2}),V("Pain is information; I still need to decide what it means for the next move.",{reason=.4}),V("The hurt is real, but it does not get to make every choice for me.",{courage=.45})},
    SELF_SADNESS={V("Admitting the sadness did not make it smaller, only clearer.",{candor=.4}),V("Part of me wants company; another part cannot bear explaining the feeling.",{warmth=.2,social=-.25}),V("The sadness keeps returning to the same quiet place in me.",{}),V("I can carry the hurt without pretending it is the only thing I am.",{courage=.35})},
    SELF_ANGER={V("Saying I was angry did not drain it away.",{candor=.25,force=.3}),V("My temper is still close enough that every answer wants an edge.",{force=.35}),V("I need one clear reason before I decide what to do with this anger.",{reason=.35,impulse=-.3}),V("Part of me wants to act before the anger has to explain itself.",{impulse=.45,force=.25})},
    SELF_FEAR={V("The fear is quieter now, but my body is still listening to it.",{guard=.35}),V("I can feel fear without giving it the final word.",{courage=.55}),V("Part of me is already looking for the safest way out.",{courage=-.45,guard=.35}),V("Naming the fear made it easier to separate the danger from the feeling.",{reason=.4,candor=.2})},
}

function RPAL.GeneratePersonaCognitiveThought(event,concern,defaultIntent,q,p)
    if type(event)~="table"then return nil end;p=p or(RPAL.GetPersonalityData and RPAL.GetPersonalityData());local profile=buildProfile(p);if not profile then return nil end
    local phase,age=temporalPhase(event)
    if phase=="reaction"or phase=="fresh-reflection"then
        local temporal,meta=generateTemporalEvent(event,profile,p,phase)
        if temporal then
            if meta then meta.eventAge=age end
            return temporal,phase,meta
        end
    end
    -- During the same scene, an event is still being processed, not remembered
    -- as an old episode. The ordinary event bank is reflective without forcing
    -- "when X happened" recollection language.
    if phase=="recent-reflection"then
        local side=event.mine and"mine"or"incoming";local name=event.person and shortName(event.person.name)or""
        local line,meta=chooseVariant(EVENT[event.type],profile,p,name,side)
        if meta then meta.intent=phase;meta.temporalPhase=phase;meta.eventAge=age;meta.eventType=event.type;meta.person=name~=""and name or nil;meta.side=side end
        return line,phase,meta
    end
    local episode=event.episode or(RPAL.AttachPersonaEpisode and RPAL.AttachPersonaEpisode(event,q,p))
    if episode then
        local episodic,mind=generateEpisodeThought(episode,p,defaultIntent)
        if episodic then if mind then mind.temporalPhase="memory";mind.eventAge=age end;return episodic,"memory",mind end
    end
    local side=event.mine and"mine"or"incoming";local name=event.person and shortName(event.person.name)or""
    local line,meta=chooseVariant(EVENT[event.type],profile,p,name,side)
    if meta then meta.intent=defaultIntent or"reflection";meta.temporalPhase=phase;meta.eventAge=age;meta.eventType=event.type;meta.person=name~=""and name or nil;meta.side=side end
    return line,defaultIntent,meta
end

local function backgroundContradiction(line,row,profile)
    local src=lower(row and row.source);if src:find("cognitive",1,true)or src:find("world",1,true)or src:find("persona%-mind")or src:find("relationship",1,true)then return nil end
    local s=" "..lower(line):gsub("[^%w']"," "):gsub("%s+"," ").." "
    if profile.reason>.62 and(s:find(" omen ",1,true)or s:find(" fate ",1,true)or s:find(" sign ",1,true))then return"rational-profile-clash"end
    if profile.wonder>.62 and(s:find(" only facts ",1,true)or s:find(" nothing but evidence ",1,true))then return"instinct-profile-clash"end
    if profile.social<-.66 and(s:find(" crowded room ",1,true)or s:find(" find somebody to talk ",1,true))then return"introversion-clash"end
    if profile.social>.70 and s:find(" nobody around me ",1,true)then return"sociability-clash"end
    if profile.order>.68 and profile.impulse<-.30 and(s:find(" improvise ",1,true)or s:find(" figure it out as i go ",1,true))then return"restraint-clash"end
    if profile.forgiveness<-.68 and(s:find(" let it go ",1,true)or s:find(" forgive them ",1,true))then return"grudge-clash"end
    if profile.forgiveness>.72 and(s:find(" pay them back ",1,true)or s:find(" settle the score ",1,true))then return"forgiveness-clash"end
    if profile.warmth>.70 and(s:find(" their discomfort does not bother me ",1,true)or s:find(" leave them to their mess ",1,true))then return"warmth-clash"end
    return nil
end

local function removeLastRow(p,row)
    local h=type(p and p.history)=="table"and p.history or{};for i=#h,math.max(1,#h-5),-1 do if h[i]==row then table.remove(h,i);return end end
end
local baseGenerate=RPAL.GeneratePersonalityThought
if baseGenerate then
    RPAL.GeneratePersonalityThought=function(context)
        local line,row=baseGenerate(context);if not line then return line,row end
        local p=RPAL.GetPersonalityData and RPAL.GetPersonalityData();local profile=buildProfile(p);if not profile then return line,row end
        row=type(row)=="table"and row or{text=line,rawText=line,source="persona",at=now(),context=context}
        local reason=backgroundContradiction(row.rawText or line,row,profile)
        if reason then
            profile.mind.stats.rejected=(tonumber(profile.mind.stats.rejected)or 0)+1;removeLastRow(p,row)
            local raw,category,source,mind=RPAL.GeneratePersonaMindThought(context,p)
            if raw then
                local styled=RPAL.StylePersonaThought and RPAL.StylePersonaThought(raw)or raw
                row={text=styled,rawText=raw,category=category,source=source,at=now(),context=tostring(context or"ambient"),mind=mind,suppressedLegacyReason=reason}
                if p then p.history[#p.history+1]=row;while#p.history>120 do table.remove(p.history,1)end end;line=styled
                profile.mind.stats.legacyRewritten=(tonumber(profile.mind.stats.legacyRewritten)or 0)+1
            else return nil end
        end
        row.mind=row.mind or{version=VERSION,lens="context",profile=profile.summary,intent=row.intent or row.category or"thought"}
        row.why=type(row.why)=="table"and row.why or{}
        row.why.persona=row.mind.profile or profile.summary;row.why.personaLens=row.mind.lens;row.why.personaVersion=VERSION
        push(profile.mind.recent,{at=row.at or now(),lens=row.mind.lens,intent=row.mind.intent,source=row.source,text=row.rawText or line},MAX_RECENT)
        profile.mind.stats.generated=(tonumber(profile.mind.stats.generated)or 0)+1;profile.mind.lastThoughtAt=row.at or now();profile.mind.lastLens=row.mind.lens
        return line,row
    end
end
