Module:MissionaryDailyOpsBVR: Difference between revisions

From MicrasWiki
Jump to navigationJump to search
NewZimiaGov (talk | contribs)
Undo revision 387695 by NewZimiaGov (talk)
Tag: Undo
NewZimiaGov (talk | contribs)
No edit summary
 
(20 intermediate revisions by the same user not shown)
Line 1: Line 1:
-- Module:MissionaryDailyOpsBVR (REWRITE + FLAG RESOLVER + CULT ICONS)
-- Module:MissionaryDailyOpsBVR
-- Daily (PSSC-calendar keyed) missionary tasking ledger for the Temple Bank of the Reformed Stripping Path.
-- Daily missionary tasking ledger (Temple Bank / Reformed Stripping Path).
-- Deterministic output (no math.random / no os.time seeding).
--
--
-- Features:
-- Embedded requirements:
--  • City profiles (culture/spiritual tags + per-city order bias)
--  1) CITY FLAGS: follow Module:GeneralPortOperations flag-naming pattern, with overrides,
--  • Economic signals parsed from General Port of Lake Morovia "Company Table"
--     BUT any missing flag file falls back to the Imperial Trade Union flag.
--   (counts by sector + sample HQ companies) and used to bias order + task.
--  2) CULT FLAGS (logos): taken directly from Module:GeneralPortOperations addLogo() mappings
--  • Optional Threat Watch integration from Module:ReligiousEvents (opt-in).
--     for ritual cults (and Temple of Aprobelle logo). Missing files fall back to ITU flag.
-- • City flags: existence-checked; fallback to Imperial Trade Union flag when missing.
--  3) CULT LINKS: each cult links to its canonical “Mystery” section target (Divine page section)
--  • Cult/Order icons: from Module:GeneralPortOperations addLogo(...) mapping.
--    per Reformed_Stripping_Path#Planetary_Devotion (including the special “The …” headings).
--     Temple Aprobelle links to [[Temple_Aprobelle]] (not a Mystery section).
--  4) OPS ALERT: integrates Module:BassaridiaForecast getOpsData() (badge + condition + overlays).
--  5) Avoid “too many expensive function calls”:
--    - No candidate-search loops.
--    - Flag existence checks are cached and done at most once per unique file used.
--    - General Port content scan is ONLY executed if showEcon=yes.
--
--
-- Usage:
-- Usage:
Line 17: Line 22:
--  {{#invoke:MissionaryDailyOpsBVR|makeTable|showEcon=yes}}
--  {{#invoke:MissionaryDailyOpsBVR|makeTable|showEcon=yes}}
--  {{#invoke:MissionaryDailyOpsBVR|makeCity|city=Vaeringheim}}
--  {{#invoke:MissionaryDailyOpsBVR|makeCity|city=Vaeringheim}}
--  {{#invoke:MissionaryDailyOpsBVR|makeOrder|order=Order of Aurora Mystica}}
--  {{#invoke:MissionaryDailyOpsBVR|makeOrder|order=Temple Aprobelle}}


local p = {}
local p = {}
Line 25: Line 30:


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- 0) SMALL UTILS
-- 0.1) DAILY CULT RELATIONS BRIDGE
-- This keeps the main relations logic in [[Module:CultRelationsDailyBVR]],
-- but lets this missionary module display it when requested.
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
local function trim(s)
 
if s == nil then return "" end
local cultRelOk, cultRel = pcall(require, "Module:CultRelationsDailyBVR")
s = tostring(s)
if not cultRelOk then
s = s:gsub("^%s+", ""):gsub("%s+$", "")
cultRel = nil
return s
end
 
local function renderCultRelations(args)
args = args or {}
 
if not cultRel or type(cultRel.makeTable) ~= "function" then
return "<div style='border:1px solid #aaa;background:#fff3cd;padding:6px;margin-top:0.5em;'>" ..
"''Daily cult-relations module unavailable. Make sure [[Module:CultRelationsDailyBVR]] exists.''" ..
"</div>"
end
 
local relArgs = {
year = args.year or args.psscyear,
day = args.day or args.dayofyear,
limit = args.relationsLimit or args.rellimit or "20",
events = args.relationEvents or "5",
showEvents = args.showRelationEvents or "yes",
compact = args.relationsCompact or "no"
}
 
local ok, result = pcall(cultRel.makeTable, { args = relArgs })
 
if ok then
return result
end
 
return "<div style='border:1px solid #aaa;background:#f8d7da;padding:6px;margin-top:0.5em;'>" ..
"''Daily cult-relations error: " .. tostring(result) .. "''" ..
"</div>"
end
end


local function safeTitleContent(titleText)
--------------------------------------------------------------------------------
local ok, t = pcall(mw.title.new, titleText)
-- 0) BASIC UTILS
if not ok or not t then return nil end
--------------------------------------------------------------------------------
local ok2, content = pcall(function() return t:getContent() end)
local function trim(s)
if not ok2 then return nil end
s = tostring(s or "")
return content
return (s:gsub("^%s+", ""):gsub("%s+$", ""))
end
end


Line 48: Line 83:
local function stripWiki(s)
local function stripWiki(s)
s = tostring(s or "")
s = tostring(s or "")
-- Remove files/templates quickly
s = s:gsub("%[%[File:[^%]]+%]%]", "")
s = s:gsub("%{%b{}%}", "")
s = s:gsub("%{%b{}%}", "")
-- Links: [[A|B]] => B, [[A]] => A
s = s:gsub("%[%[[^%]|]+|([^%]]+)%]%]", "%1")
s = s:gsub("%[%[[^%]|]+|([^%]]+)%]%]", "%1")
s = s:gsub("%[%[([^%]]+)%]%]", "%1")
s = s:gsub("%[%[([^%]]+)%]%]", "%1")
-- Bold/italics
s = s:gsub("'''''", ""):gsub("'''", ""):gsub("''", "")
s = s:gsub("'''''", ""):gsub("'''", ""):gsub("''", "")
return trim(s)
return trim(s)
end
local function containsAny(s, words)
s = (s or ""):lower()
for _, w in ipairs(words) do
if s:find(w, 1, true) then return true end
end
return false
end
local function joinTags(tags)
if not tags or #tags == 0 then return "" end
local out = {}
for _, t in ipairs(tags) do out[#out+1] = t end
return table.concat(out, ", ")
end
end


Line 102: Line 118:
end
end


local function getDateInfo()
local function getDateInfo(args)
args = args or {}
 
local overrideYear = tonumber(args.year or args.psscyear)
local overrideDay  = tonumber(args.day or args.dayofyear)
 
if overrideYear and overrideDay then
if overrideDay < 1 then overrideDay = 1 end
if overrideDay > DAYS_IN_YEAR then overrideDay = DAYS_IN_YEAR end
 
local m, dom = doyToMonthDom(overrideDay)
return {
year = overrideYear,
dayOfYear = overrideDay,
monthNum = m,
dom = dom,
text = string.format("%d/%d/%d PSSC", dom, m, overrideYear),
raw = string.format("%d/%d/%d PSSC", dom, m, overrideYear),
calendarEvent = "",
}
end
 
local raw = cal.getCurrentDate()
local raw = cal.getCurrentDate()
local y, doy = parseCalendarString(raw)
local y, doy = parseCalendarString(raw)
Line 125: Line 162:
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
local function hash31(str)
local function hash31(str)
str = tostring(str or "")
local h = 0
local h = 0
for i = 1, #str do
for i = 1, #str do
Line 142: Line 180:


local function pickFromList(nextInt, list, avoidValue)
local function pickFromList(nextInt, list, avoidValue)
if #list == 0 then return nil end
if not list or #list == 0 then return nil end
if #list == 1 then return list[1] end
if #list == 1 then return list[1] end
local tries = 0
local tries = 0
Line 155: Line 193:
end
end
return list[1]
return list[1]
end
local function pickWeighted(nextInt, weighted)
local total = 0
for _, it in ipairs(weighted) do total = total + (it.w or 0) end
if total <= 0 then return weighted[1] end
local r = nextInt() % total
local acc = 0
for _, it in ipairs(weighted) do
acc = acc + (it.w or 0)
if r < acc then return it end
end
return weighted[#weighted]
end
--------------------------------------------------------------------------------
-- 3) EXPENSIVE-CALL CONTROL: cached file existence checks (1 per unique file)
--------------------------------------------------------------------------------
local fileExistsCache = {}
local function fileExists(fileName)
fileName = tostring(fileName or "")
if fileName == "" then return false end
if fileExistsCache[fileName] ~= nil then return fileExistsCache[fileName] end
local t = mw.title.new("File:" .. fileName)
local ok = (t and t.exists) or false
fileExistsCache[fileName] = ok
return ok
end
end


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- 3) COLOR CODING (Hatch-style)
-- 4) STYLE / SEVERITY
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
local function sevStyle(sev)
local function sevStyle(sev)
Line 168: Line 233:
return ' style="background-color:#d4edda;"'
return ' style="background-color:#d4edda;"'
end
end
end
local function urgencyScore(u)
u = tostring(u or ""):upper()
if u == "CRITICAL" then return 4 end
if u == "HIGH"    then return 3 end
if u == "MEDIUM"  then return 2 end
if u == "LOW"      then return 1 end
return 0
end
end


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- 4) CITY NETWORK (canonical list)
-- 5) CITY NETWORK
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
local function normalizeForFlag(name)
return (name or "")
:gsub("[%s%-’]", "")
:gsub("[Áá]", "A"):gsub("[Éé]", "E")
:gsub("[Íí]", "I"):gsub("[Óó]", "O")
:gsub("[Úú]", "U"):gsub("[Ýý]", "Y")
end
local function cityLink(name)
local function cityLink(name)
return string.format("[[List of cities in Bassaridia Vaeringheim#%s|%s]]", name, name)
return string.format("[[List_of_cities_in_Bassaridia_Vaeringheim#%s|%s]]", name, name)
end
end


-- 54 canonical cities; tier used for "major city" baseline.
local cityOrder = {
local cityOrder = {
-- Core majors
-- Core majors
Line 198: Line 263:
{name="Lunalis Sancta",  region="Core", tier="major"},
{name="Lunalis Sancta",  region="Core", tier="major"},
{name="Sylvapolis",      region="Core", tier="major"},
{name="Sylvapolis",      region="Core", tier="major"},
-- Core minors
-- Core minors
{name="Saluria",          region="Core", tier="minor"},
{name="Saluria",          region="Core", tier="minor"},
Line 209: Line 273:
{name="Koinonía",        region="Core", tier="minor"},
{name="Koinonía",        region="Core", tier="minor"},
{name="Aureum",          region="Core", tier="minor"},
{name="Aureum",          region="Core", tier="minor"},
-- Alperkin
-- Alperkin
{name="The Alpazkigz",    region="Alperkin", tier="minor"},
{name="The Alpazkigz",    region="Alperkin", tier="minor"},
{name="Odiferia",        region="Alperkin", tier="minor"},
{name="Odiferia",        region="Alperkin", tier="minor"},
-- New South Jangsong
-- New South Jangsong
{name="Skýrophos",        region="New South Jangsong", tier="major"},
{name="Skýrophos",        region="New South Jangsong", tier="major"},
Line 225: Line 287:
{name="Ephyra",          region="New South Jangsong", tier="minor"},
{name="Ephyra",          region="New South Jangsong", tier="minor"},
{name="Halicarn",        region="New South Jangsong", tier="minor"},
{name="Halicarn",        region="New South Jangsong", tier="minor"},
-- Haifan Bassaridia
-- Haifan Bassaridia
{name="Keybir-Aviv",      region="Haifan Bassaridia", tier="major"},
{name="Keybir-Aviv",      region="Haifan Bassaridia", tier="major"},
Line 236: Line 297:
{name="Sufriya",          region="Haifan Bassaridia", tier="minor"},
{name="Sufriya",          region="Haifan Bassaridia", tier="minor"},
{name="Lykopolis",        region="Haifan Bassaridia", tier="minor"},
{name="Lykopolis",        region="Haifan Bassaridia", tier="minor"},
-- Normark
-- Normark
{name="Ardclach",        region="Normark", tier="major"},
{name="Ardclach",        region="Normark", tier="major"},
{name="Riddersborg",      region="Normark", tier="major"},
{name="Riddersborg",      region="Normark", tier="major"},
-- Ouriana
-- Ouriana
{name="Bashkim",          region="Ouriana", tier="major"},
{name="Bashkim",          region="Ouriana", tier="major"},
{name="Ourid",            region="Ouriana", tier="major"},
{name="Ourid",            region="Ouriana", tier="major"},
{name="Tonar",            region="Ouriana", tier="minor"},
{name="Tonar",            region="Ouriana", tier="minor"},
-- Caledonia
{name="Fanghorn",        region="Caledonia", tier="major"},
{name="Eikbu",            region="Caledonia", tier="major"},
{name="Galvø",            region="Caledonia", tier="major"},
{name="Hamarrfell",      region="Caledonia", tier="major"},
{name="Sårensby",        region="Caledonia", tier="minor"},
{name="Slevik",          region="Caledonia", tier="minor"},
{name="Sjøsborg",        region="Caledonia", tier="minor"},
{name="Storesund",        region="Caledonia", tier="minor"},
{name="Notranskja",      region="Caledonia", tier="minor"},
{name="Kaledonija",      region="Caledonia", tier="minor"},
{name="Krlsgorod",        region="Caledonia", tier="minor"},
{name="Skøda",            region="Caledonia", tier="minor"},
}
local citySet = {}
local ctx = { usedTasksByCult = {} }
for _, c in ipairs(cityOrder) do citySet[c.name] = true end


-- Caledonia
--------------------------------------------------------------------------------
{name="Fanghorn",        region="Caledonia", tier="major"},
-- 5.0.1) HAIFAN BASSARIDIA / SOUTHERN STRAIT OPERATING ZONE
{name="Eikbu",            region="Caledonia", tier="major"},
--
{name="Galvø",            region="Caledonia", tier="major"},
-- This does NOT lock missionary cults to these cities.
{name="Sårensby",        region="Caledonia", tier="minor"},
-- It only allows special Southern Strait tasking when the generated station
{name="Slevik",          region="Caledonia", tier="minor"},
-- is one of the Haifan Bassaridian cities.
{name="Sjøsborg",        region="Caledonia", tier="minor"},
--------------------------------------------------------------------------------
{name="Storesund",        region="Caledonia", tier="minor"},
local haifanBassaridiaCities = {
{name="Notranskja",      region="Caledonia", tier="minor"},
["Keybir-Aviv"] = true,
{name="Kaledonija",      region="Caledonia", tier="minor"},
["Tel-Amin"] = true,
["Diamandis"] = true,
["Jogi"] = true,
["Lewisburg"] = true,
["Thermosalem"] = true,
["Akróstadium"] = true,
["Sufriya"] = true,
["Lykopolis"] = true,
}
}
local function isHaifanBassaridiaCity(cityName)
return haifanBassaridiaCities[tostring(cityName or "")] == true
end


local function regionPass(filter, cityRegion)
local function regionPass(filter, cityRegion)
if not filter or filter == "" or filter == "All" then return true end
filter = tostring(filter or "All")
if filter == "" or filter == "All" then return true end
if filter == "Core" then return cityRegion == "Core" end
if filter == "Core" then return cityRegion == "Core" end
if filter == "NSJ" then return cityRegion == "New South Jangsong" end
if filter == "NSJ" then return cityRegion == "New South Jangsong" end
Line 268: Line 357:


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- 4.1) CITY FLAGS (existence-checked + fallback)
-- 5.1) CITY FLAGS (GeneralPortOperations style + fallback-to-ITU if missing)
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
local DEFAULT_CITY_FLAG_FILE = "Imperial Trade Union flag.png"
local ITU_FLAG = "Imperial Trade Union flag.png"


local function asciiNormalizeForFlag(name)
local function normCityForFlag(name)
-- Exactly the normalization used in Module:GeneralPortOperations
return (name or "")
return (name or "")
:gsub("[%s%-’]", "")
:gsub("[%s%-’]", "")
:gsub("[ÁáÀàÂâÄäÃãÅåĀā]", "A")
:gsub("[Áá]", "A"):gsub("[Éé]", "E")
:gsub("[ÉéÈèÊêËëĒē]", "E")
:gsub("[Íí]", "I"):gsub("[Óó]", "O")
:gsub("[ÍíÌìÎîÏïĪī]", "I")
:gsub("[Úú]", "U"):gsub("[Ýý]", "Y")
:gsub("[ÓóÒòÔôÖöÕõØøŌō]", "O")
:gsub("[ÚúÙùÛûÜüŪū]", "U")
:gsub("[ÝýŸÿ]", "Y")
:gsub("[Ææ]", "AE")
end
 
local fileExistsCache = {}
local function fileExists(fileName)
if not fileName or fileName == "" then return false end
if fileExistsCache[fileName] ~= nil then return fileExistsCache[fileName] end
local ok, t = pcall(mw.title.new, "File:" .. fileName)
local exists = (ok and t and t.exists == true) or false
fileExistsCache[fileName] = exists
return exists
end
end


local citySet = {}
for _, c in ipairs(cityOrder) do citySet[c.name] = true end
-- Explicit odd filenames you noted (plus Skyrophos).
local cityFlagOverrides = {
local cityFlagOverrides = {
["Skýrophos"] = "SkyrophosFlag.png",
["Skýrophos"] = "SkyrophosFlag.png", -- override used in GeneralPortOperations
["Sårensby"]  = "SårensbyFlag.png",
["Slevik"]    = "SlevikFlag.png.png", -- known oddity you’ve used elsewhere
["Slevik"]    = "SlevikFlag.png.png",
["Sårensby"] = "SårensbyFlag.png",   -- known to exist in your notes
["Kaledonija"]= "KaledonijaFlag.png",
}
}


Line 308: Line 379:
local function getCityFlagMarkup(cityName)
local function getCityFlagMarkup(cityName)
if cityFlagMarkupCache[cityName] then return cityFlagMarkupCache[cityName] end
if cityFlagMarkupCache[cityName] then return cityFlagMarkupCache[cityName] end
local candidate = cityFlagOverrides[cityName] or (normCityForFlag(cityName) .. "Flag.png")
local file = fileExists(candidate) and candidate or ITU_FLAG
local mk = string.format("[[File:%s|20px]]", file)
cityFlagMarkupCache[cityName] = mk
return mk
end


-- 1) explicit override
--------------------------------------------------------------------------------
local override = cityFlagOverrides[cityName]
-- 6) CULT FLAGS (logos) from Module:GeneralPortOperations addLogo() + fallback
if override and fileExists(override) then
--------------------------------------------------------------------------------
local mk = string.format("[[File:%s|20px]]", override)
local cultLogoFile = {
cityFlagMarkupCache[cityName] = mk
-- Ritual cult logos from Module:GeneralPortOperations
return mk
["Order of Aurora Mystica"]         = "Auroramystica.png",
end
["Harmony Sanctum"]                = "Harmonysanctum.png",
 
["Ignis Aeternum"]                = "Ignis Aeternum.png",
-- 2) try normalized candidates
["Celestial Harmony Sect"]        = "HarmonySect.png",
local norm = normalizeForFlag(cityName)
["Guild of Golden Shadows"]       = "GoldenShadows.png",
local ascii = asciiNormalizeForFlag(cityName)
["Azure Sentinel Sect"]           = "AzureSentinel.png",
local candidates = {
["Reverie Nebulous"]               = "RevNeb.png",
norm .. "Flag.png",
["Eon Fellowship"]                = "EonFel.png",
ascii .. "Flag.png",
["Order of the Umbral Oracle"]    = "Umbral.png",
}
["Mystery of the Verdant Embrace"] = "Verdant.png",
 
["Conclave Illuminara"]            = "Conclave.png",
for _, fn in ipairs(candidates) do
["Sanctum Vitalis"]                = "Vitalis.png",
if fileExists(fn) then
["Temple Alabaster"]              = "Alabaster.png",
local mk = string.format("[[File:%s|20px]]", fn)
["Court of the Ironclad"]          = "Ironclad.png",
cityFlagMarkupCache[cityName] = mk
["Accord Concordia"]              = "Concordia.png",
return mk
["Mystery of the Stygian Veil"]    = "StygianVe.png",
end
["Sylvan Fellowship"]              = "Sylvan.png",
end
["Mystery of Red Mirth"]          = "MirthFlag.png",
["Rex Catonis"]                    = "Catonis.png",
["Sanctum Delphica"]              = "SanctumDel.png",
["Ordo Amicitia"]                 = "OrdoFl.png",
["Temple Illuminata"]             = "AureumFlag.png",
-- Temple Aprobelle logo is mapped in GeneralPortOperations under serviceCompanies name:
["Temple Aprobelle"]              = "TempleAprobelleLogo.png",
["Temple of Aprobelle"]            = "TempleAprobelleLogo.png",
}


-- 3) fallback to ITU
local cultFlagMarkupCache = {}
local mk = string.format("[[File:%s|20px]]", DEFAULT_CITY_FLAG_FILE)
local function getCultFlagMarkup(cultName)
cityFlagMarkupCache[cityName] = mk
if cultFlagMarkupCache[cultName] then return cultFlagMarkupCache[cultName] end
local candidate = cultLogoFile[cultName]
local file = (candidate and fileExists(candidate)) and candidate or ITU_FLAG
local mk = string.format("[[File:%s|18px]] ", file)
cultFlagMarkupCache[cultName] = mk
return mk
return mk
end
end


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- 5) CORRIDORS / ROLE HINTS (hard-coded)
-- 7) CULT LINKS (canonical “Mystery” section targets)
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
local RAIL = {
local cultTargets = {
["Lykopolis"]=true, ["Lewisburg"]=true, ["Bashkim"]=true, ["Somniumpolis"]=true,
["Order of Aurora Mystica"]         = { page="Eos_(Reformed_Stripping_Path)",      anchor="Order_of_Aurora_Mystica" },
["Erythros"]=true, ["Vaeringheim"]=true, ["Symphonara"]=true, ["Skýrophos"]=true,
["Harmony Sanctum"]                = { page="Micras_(Reformed_Stripping_Path)",  anchor="Harmony_Sanctum" },
["Norsolyra"]=true, ["Pelagia"]=true, ["Riddersborg"]=true, ["Sårensby"]=true,
["Ignis Aeternum"]                = { page="Pyros_(Reformed_Stripping_Path)",    anchor="Ignis_Aeternum" },
-- Special headings on those pages include “The …”
["Celestial Harmony Sect"]        = { page="Indigo_(Reformed_Stripping_Path)",  anchor="The_Celestial_Harmony_Sect" },
["Guild of Golden Shadows"]        = { page="Chrysos_(Reformed_Stripping_Path)",  anchor="The_Guild_of_Golden_Shadows" },
["Azure Sentinel Sect"]            = { page="Thalassa_(Reformed_Stripping_Path)", anchor="Azure_Sentinel_Sect" },
["Reverie Nebulous"]              = { page="Nephele_(Reformed_Stripping_Path)",  anchor="Reverie_Nebulous" },
["Eon Fellowship"]                = { page="Glinos_(Reformed_Stripping_Path)",  anchor="Eon_Fellowship" },
["Order of the Umbral Oracle"]    = { page="Noctis_(Reformed_Stripping_Path)",  anchor="Order_of_the_Umbral_Oracle" },
["Mystery of the Verdant Embrace"] = { page="Opsithe_(Reformed_Stripping_Path)",  anchor="Mystery_of_the_Verdant_Embrace" },
["Conclave Illuminara"]           = { page="Tarsica_(Reformed_Stripping_Path)", anchor="Conclave_Illuminara" },
["Sanctum Vitalis"]               = { page="Plateau_(Reformed_Stripping_Path)",  anchor="Sanctum_Vitalis" },
["Temple Alabaster"]               = { page="Ivory_(Reformed_Stripping_Path)",    anchor="Temple_Alabaster" },
["Court of the Ironclad"]          = { page="Fenrir_(Reformed_Stripping_Path)",  anchor="Court_of_the_Ironclad" },
["Accord Concordia"]              = { page="Lukedu_(Reformed_Stripping_Path)",  anchor="Accord_Concordia" },
["Mystery of the Stygian Veil"]   = { page="Styx_(Reformed_Stripping_Path)",    anchor="Mystery_of_the_Stygian_Veil" },
["Sylvan Fellowship"]             = { page="Faun_(Reformed_Stripping_Path)",     anchor="Sylvan_Fellowship" },
["Mystery of Red Mirth"]           = { page="Silenus_(Reformed_Stripping_Path)",  anchor="Mystery_of_Red_Mirth" },
["Rex Catonis"]                   = { page="Cato_(Reformed_Stripping_Path)",     anchor="Rex_Catonis" },
["Sanctum Delphica"]               = { page="Sisera_(Reformed_Stripping_Path)",  anchor="Sanctum_Delphica" },
["Ordo Amicitia"]                 = { page="Erasmus_(Reformed_Stripping_Path)", anchor="Ordo_Amicitia" },
["Temple Illuminata"]             = { page="Nikolaj_(Reformed_Stripping_Path)",  anchor="Temple_Illuminata" },
}
}
local FERRY = {["Jogi"]=true, ["Ardclach"]=true}
 
local function cultLink(cultName)
if cultName == "Temple Aprobelle" or cultName == "Temple of Aprobelle" then
return "[[Temple_Aprobelle|Temple Aprobelle]]"
end
local t = cultTargets[cultName]
if not t then
return string.format("[[%s]]", cultName)
end
return string.format("[[%s#%s|%s]]", t.page, t.anchor, cultName)
end


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- 6) MISSIONARY ORGANIZATION (sizes)
-- 8) ORG UNITS (Missionary echelons)
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
local org = {
local org = {
Lampade    = {size=3,  code="L"},
Lampade    = { code="L", size=8  },
Hetairoi    = {size=6,  code="H"},
Hetairoi    = { code="H", size=12 },
Dodekade    = {size=12,  code="D"},
Dodekade    = { code="D", size=18 },
Kleisthenes = {size=25,  code="K"},
Kleisthenes = { code="K", size=25 },
Stoa        = {size=50,  code="S"},
Stoa        = { code="S", size=40 },
Thiasos    = {size=100, code="T"},
Thiasos    = { code="T", size=60 },
}
}


Line 372: Line 491:


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- 7) ORDERS + TASKS (tagged)
-- 9) OPS ALERT (Module:BassaridiaForecast)
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
local function orderLink(pageTitle, anchor, label)
local wxOk, wx = pcall(require, "Module:BassaridiaForecast")
return string.format("[[%s#%s|%s]]", pageTitle, anchor, label)
if not wxOk then wx = nil end
 
local function wxColor(level)
level = tonumber(level) or 0
if level == 3 then return "#f8d7da" end
if level == 2 then return "#ffe5cc" end
if level == 1 then return "#fff3cd" end
return "#d4edda"
end
end


local function T(text, sev, tags)
local function opsCell(cityName, year, dayOfYear)
return {text=text, sev=sev, tags=tags or {}}
if not wx or type(wx.getOpsData) ~= "function" then return "n/a", nil end
local op = wx.getOpsData(cityName, year, dayOfYear)
if not op or not op.alert then return "n/a", nil end
local lvl  = tonumber(op.alert.level) or 0
local label = tostring(op.alert.label or "Green")
local cond  = tostring(op.condition or "")
local bg    = wxColor(lvl)
 
local cell = string.format(
"<span style='background:%s;padding:2px 6px;border:1px solid #ccc;border-radius:10px;white-space:nowrap;'>%s</span>%s",
bg, label, (cond ~= "" and ("<br/><span style='font-size:90%;'>" .. cond .. "</span>") or "")
)
return cell, op
end
end


local orders = {
local function bumpSevWeather(sev, op)
{key="OSI", name="Ordo Solis Invicti",
if not op or not op.alert then return sev end
link=orderLink("Atos (Reformed Stripping Path)","Ordo_Solis_Invicti","Ordo Solis Invicti"),
local lvl = tonumber(op.alert.level) or 0
tasks={
local imp = op.impacts or {}
T("Dawn rites and public illumination drill to restore civic clarity and discourage rumor cascades in market wards.","green",{"public","education","service"}),
 
T("Temple-school literacy session paired with cosmology instruction and shrine maintenance inspections.","green",{"education","shrine","service"}),
if lvl == 2 and sev == "green" then sev = "yellow" end
T("Solar route inspection at major junctions; traffic etiquette posted for pilgrims and commuters.","green",{"transit","rail","public"}),
if lvl >= 3 then sev = "red" end
T("Sun-pledge renewal for civic clerks; oath sheets sealed and logged with Temple Bank scribes.","green",{"governance","audit","service"}),
 
T("Public lamp and beacon blessing for tunnel nodes; wayfinding clarified for night labor shifts.","green",{"infrastructure","transit","industry"}),
if (imp.ground == "Hazard" or imp.sea == "Closed" or imp.air == "No-Go") and sev == "green" then
T("Radiance audit of public lamps, mirrored courts, and shrine sightlines; repair orders issued under Temple Bank stipend.","yellow",{"infrastructure","audit","service"}),
sev = "yellow"
T("Solar Circle consecration for a new neighborhood shrine, with crowd choreography and compliance wardens.","yellow",{"shrine","crowd","public"}),
end
T("Counter-broadsheet sermon cycle following unsigned pamphlets; witness interviews conducted with local elders.","yellow",{"heresy","investigation","public"}),
return sev
T("Joint patrol-litany along rail platforms to reduce theft and panic during peak transit hours.","yellow",{"rail","security","crowd"}),
end
T("Emergency morale procession after night unrest; hymn leadership and calm restoration at transit choke points.","red",{"unrest","crowd","transit"}),
 
}
local function weatherTaskOverlay(op)
},
if not op or not op.alert then return "" end
local lvl = tonumber(op.alert.level) or 0
if lvl < 2 then return "" end -- Orange/Red only
 
local haz = tostring(op.alert.hazard or "weather disruption")
local imp = op.impacts or {}
local cond = tostring(op.condition or "")
local base = " Weather-driven tasking: " .. haz .. " (" ..
string.format("Gnd %s / Sea %s / Air %s", imp.ground or "OK", imp.sea or "OK", imp.air or "OK") .. ")."
 
if cond == "Storm" or cond == "Rain" then
return base .. " Clinic/relief queues stabilized; rumor bulletins posted; shrine-district lanes marked."
elseif cond == "Blizzard" or cond == "Snow" then
return base .. " Warming-shelter checks; fuel/medicine custody verified; travel guidance posted."
elseif cond == "Fog" then
return base .. " Navigation warnings and crowd guidance in port/canal corridors; panic narratives suppressed."
elseif cond == "Dust" then
return base .. " Mask discipline and vendor hygiene enforcement prioritized; indoor refuge advisories posted."
else
return base .. " Welfare checks and public messaging prioritized."
end
end
 
--------------------------------------------------------------------------------
-- 10) OPTIONAL: THREAT WATCH (Module:ReligiousEvents)
--------------------------------------------------------------------------------
local function parseReligiousEventsTable(wikitext)
local map = {}
wikitext = tostring(wikitext or "")
for line in wikitext:gmatch("[^\n]+") do
if line:match("^|%s") and not line:match("^|%-%s*$") then
local parts = mw.text.split(line:gsub("^|%s*", ""), "%s*||%s*")
if #parts >= 4 then
local city = stripWiki(parts[2])
local ev = stripWiki(parts[3])
local urg = stripWiki(parts[4])
if city ~= "" and ev ~= "" then
map[city] = {event=ev, urg=urg}
end
end
end
end
return map
end
 
--------------------------------------------------------------------------------
-- 11) OPTIONAL: ECON SIGNALS (EXPENSIVE; ONLY if showEcon=yes)
--------------------------------------------------------------------------------
local function getEconSignals()
local title = mw.title.new("General Port of Lake Morovia")
if not title then return {} end
local ok, content = pcall(function() return title:getContent() end)
if not ok or not content or content == "" then return {} end
local signals = {}
local text = normalizeSpaces(content)
 
for _, c in ipairs(cityOrder) do
local n = c.name
local hits = 0
local pattern = n:gsub("([%^%$%(%)%%%.%[%]%*%+%-%?])","%%%1")
for _ in text:gmatch(pattern) do
hits = hits + 1
if hits >= 6 then break end
end
if hits >= 3 then
signals[n] = {summary=string.format("High Port linkage (mentions: %d)", hits)}
end
end
return signals
end
 
--------------------------------------------------------------------------------
-- 12) TASK HELPERS
--------------------------------------------------------------------------------
local function T(text, sev) return {text=text, sev=sev} end
 
-- 10 tasks per cult (kept compact so the module remains maintainable)
local tasksByCult = {
 
  ["Order of Aurora Mystica"] = {
    T("First-light conciliation at a guild hall; signatories recorded with Temple Bank clerks; follow-up meeting scheduled.","green"),
    T("Sunrise clarity bulletin: correct ‘official omen’ rumors before queues form; speakers briefed on approved language.","green"),
    T("Dawn home-visit circuit for anxious households; practical referrals routed through shrine stewards.","green"),
    T("Procession lane rehearsal for a morning rite; crush points marked; stewards drilled on staggered entry.","yellow"),
    T("Targeted pamphlet sweep: remove forged ‘dawn decree’ leaflets; source stalls logged for follow-up.","yellow"),
    T("Joint dawn patrol with civic stewards on canal/rail approaches; lighting faults logged; intimidation reports collected.","yellow"),
    T("Witness-support escort at daybreak: safe path confirmed; hostile crowd avoidance plan posted.","yellow"),
    T("Emergency dawn ceasefire convened after overnight clashes; terms read publicly; retaliation pledges collected.","red"),
    T("Crisis dawn-vigil stabilization after a major scare; grief care + calm stations opened; clinic corridors protected.","red"),
    T("High-risk dawn convoy escort for sacred/custody items; route denial and decoy timing implemented.","red"),
    -- +8 added
    T("Hilltop shrine schedule reset for first-light hymns; latecomer lanes separated; attendance log updated.","green"),
    T("Dawn-worksite blessing for shift crews; safety reminders appended; foreman sign-off obtained.","green"),
    T("Morning-lamp distribution and repair tickets; dark alleys flagged; follow-up patrol time posted.","green"),
    T("Sunrise complaint intake booth at market edge; triage tags assigned; referrals routed to Harmony Sanctum.","yellow"),
    T("Counter-badge check on ‘Aurora stewards’; impostors documented; credentials verified with Temple Bank.","yellow"),
    T("Dawn festival route reroute after hazard report; new signage posted; crowd cadence rehearsed.","yellow"),
    T("Red-posture dawn broadcast: scripted calm loop; speaker rotations scheduled; rumor nodes targeted for removal.","red"),
    T("High-risk witness staging at daybreak: staggered arrivals; decoy meeting point; custody signatures secured.","red"),
  },


{key="OAM", name="Order of Aurora Mystica",
  ["Harmony Sanctum"] = {
link=orderLink("Eos (Reformed Stripping Path)","Order_of_Aurora_Mystica","Order of Aurora Mystica"),
    T("Neighborhood mediation circle: settle a vendor–labor dispute; written concord filed.","green"),
tasks={
    T("Public etiquette coaching at shrine square; queue discipline and nonviolent refusal scripts practiced.","green"),
T("Sunrise vigil and reconciliation rite for competing guilds; signed harmony pledges archived by Temple Bank clerks.","green",{"mediation","guild","service"}),
    T("Household conflict visits after complaint spike; behavior plan issued; check-in date set.","green"),
T("Dawn-watch observatory check and omen bulletin issued to reduce panic and stabilize labor shifts.","green",{"omen","public","service"}),
    T("Festival crowd-lane plan review; bottlenecks marked; stewards assigned to separation corridors.","yellow"),
T("Quiet home-visits to recent migrants; orientation blessings and sanctioned neighborhood guides issued.","green",{"public","service","mediation"}),
    T("Rumor triage: map source nodes feeding panic; corrective bulletin cycle deployed.","yellow"),
T("Rooftop shrine upkeep and dawn-banner replacement; civic lanes kept clear for morning processions.","green",{"shrine","infrastructure","public"}),
    T("Transit choke-point calming patrol; commuter separation corridor opened; escalation triggers documented.","yellow"),
T("Pilgrim flow timing review for a dawn festival; route markers placed to prevent crush points.","green",{"festival","crowd","public"}),
    T("Clerk oversight coaching after audit flags; corrective training delivered; sign-off obtained.","yellow"),
T("Civic blessing for new housing blocks; routing of foot-traffic to prevent crush points and misdirection.","yellow",{"crowd","infrastructure","service"}),
    T("Emergency stabilization during riot flare; dispersal lanes held; care stations deployed at safe corners.","red"),
T("Counter-heresy sermon cycle following illicit broadsides; witness interviews conducted with local elders.","yellow",{"heresy","investigation","public"}),
    T("Night calm-litany enforcement after a panic episode; supervised dispersal and reunification tent opened.","red"),
T("Dawn mediation under escort after a workplace dispute threatens walkouts; settlement notarized.","yellow",{"mediation","industry","governance"}),
    T("Crisis reconciliation hearing after violence; restitution ledger initiated; witness protection arranged.","red"),
T("Omen-risk advisory for coastal weather or fog days; port stewards briefed and signage posted.","yellow",{"port","public","service"}),
    -- +8 added
T("Rapid-response pacification after arson rumor; dawn procession under guard with emergency stipend release.","red",{"unrest","arson","crowd"}),
    T("Balance-rite for a disputed property line; boundary walk performed; neutral witnesses recorded.","green"),
}
    T("Structured apology conference after school conflict; restitution terms set; guardian contacts logged.","green"),
},
    T("Neighborhood rotation plan for shared resources; schedule posted; conflict triggers documented.","green"),
    T("Inter-ward speaking forum moderated; turn order enforced; heckler removal protocol posted.","yellow"),
    T("Counter-rumor ‘myth vs policy’ bulletin; distribution assigned by street; speaker script packet issued.","yellow"),
    T("Market-stall spacing enforcement after crush near fountain; lane widths measured; re-layout posted.","yellow"),
    T("Emergency anti-reprisal pact after stabbing; escort corridors established; no-retaliation oaths collected.","red"),
    T("Mass grievance intake after unrest; testimony queue metered; witness shielding arranged; outcomes logged.","red"),
  },


{key="HSA", name="Harmony Sanctum",
  ["Ignis Aeternum"] = {
link=orderLink("Micras (Reformed Stripping Path)","Harmony_Sanctum","Harmony Sanctum"),
    T("Forge discipline rite: safety roles assigned; firewatch roster posted; tool control verified.","green"),
tasks={
    T("Heat advisory posting for labor crews; hydration discipline enforced; shade points designated.","green"),
T("Neighborhood mediation session for family disputes and trade-feud spillover; binding accords notarized.","green",{"mediation","service"}),
    T("Brazier and lantern inspection at a festival lane; safe distances marked; fuel rules posted.","green"),
T("Harmony clinic for stress and rumor contagion; quiet-room rotations and guided breathing rites.","green",{"health","rumor","service"}),
    T("Brushfire-risk patrol: illegal burns curtailed; emergency water points labeled; reporting chain rehearsed.","yellow"),
T("Queue-discipline coaching at relief points; volunteers trained to prevent confrontations.","green",{"order","queue","relief"}),
    T("Arson-scare containment: inspect fuel stores and service corridors; contractors re-badged; locks audited.","yellow"),
T("Workplace concord circle for service districts; complaint channels clarified and posted.","green",{"service","mediation","governance"}),
    T("Rumor suppression after ‘cursed flame’ claims; sanctioned narrative posted; agitator list started.","yellow"),
T("Inter-shrine courtesy visit to reduce rivalry; shared calendar commitments established.","green",{"balance","mediation","public"}),
    T("Kiln/oven quarter audit after sabotage chatter; witness statements logged; repair tickets issued.","yellow"),
T("Loyalty and morale audit of a civic office after complaint surge; corrective training scheduled.","yellow",{"audit","governance","service"}),
    T("Emergency response to a flare incident: evacuation lanes opened; burn triage support and calm messaging deployed.","red"),
T("Containment of a cult schism meeting; supervised dialogue and sanction warnings delivered.","yellow",{"schism","containment","public"}),
    T("Crisis crowd control at a pyre/procession pinch point; stampede pressure relieved; reunification tent opened.","red"),
T("Rumor forensics on a circulating ‘forbidden rite’ claim; witness tree compiled and forwarded.","yellow",{"rumor","investigation","public"}),
    T("High-risk perimeter posting at depots after credible arson threat; entry controlled; night watch rotated.","red"),
T("Crowd-stabilization drill for festival season; steward assignments and safe exits mapped.","yellow",{"festival","crowd","public"}),
    -- +8 added
T("Major crowd-stabilization operation after riot signals; staged dispersal with peace-liturgy and escorts.","red",{"riot","crowd","public"}),
    T("Workshop ventilation check; soot exposure warnings posted; compliance sign-offs collected.","green"),
}
    T("Approved fuel rationing note posted for braziers; prevent hoarding; vendor guidance issued.","green"),
},
    T("Tool-blessing and inventory day; missing items reconciled; suspect list deferred to Golden Shadows.","green"),
    T("Firebreak drill coordination near warehouses; muster lanes marked; timing rehearsed with stewards.","yellow"),
    T("Controlled relight protocol after blackout; ignition permits checked; unsafe relights stopped.","yellow"),
    T("Inspection sweep for illegal accelerants; confiscations logged; storage seals verified.","yellow"),
    T("Major blaze containment support: perimeter discipline; water point control; after-action safety decree issued.","red"),
    T("Arson-ring disruption support: suspect workshops sealed; witness protection arranged; public guidance broadcast.","red"),
  },


{key="IGA", name="Ignis Aeternum",
  ["Celestial Harmony Sect"] = {
link=orderLink("Pyros (Reformed Stripping Path)","Ignis_Aeternum","Ignis Aeternum"),
    T("Cosmic-order lecture at shrine school; reduce anxiety about sky-signs; practical safety guidance appended.","green"),
tasks={
    T("Guided breathing session for canal workers; stress reduction paired with shift-safety reminder.","green"),
T("Controlled fire blessing for workshops; safety rite doubled as inspection of fuel and storage.","green",{"industry","safety"}),
    T("Calendar correction bulletin: prevent hoarding after false eclipse/omen claims; approved text distributed.","green"),
T("Kiln-and-forge consecration for a new artisan street; insurance seals verified.","green",{"industry","infrastructure","service"}),
    T("Quiet-zone enforcement near hospitals and shelters; regulated chanting and crowd spacing verified.","yellow"),
T("Firebreak marking for dry-season hills; public notices issued to farmers and herders.","green",{"agri","fire","public"}),
    T("Counterfeit star-chart inquiry: vendor interviews; seizure log compiled; correlations forwarded.","yellow"),
T("Hearth-blessing round for crowded tenements; cooking hazards corrected with wardens.","green",{"public","safety","service"}),
    T("Fog-night guidance patrol: speakers briefed; route markers placed; panic triggers documented.","yellow"),
T("Candle and incense quality check at shrine vendors; adulterants rejected and logged.","green",{"shrine","audit","service"}),
    T("Post-event debrief: update approved narratives; distribute scripts to shrine speakers; archive record filed.","yellow"),
T("Purification burn at a compromised shrine-store; ash sealed and catalogued for Temple Bank disposal.","yellow",{"contam","shrine","service"}),
    T("Crisis counseling during mass scare; supervised calm stations opened; clinic access protected.","red"),
T("Investigation of incendiary threats near a gate-temple; firebreak plan issued to local wardens.","yellow",{"arson","investigation","security"}),
    T("Emergency embedded messaging team with civic stewards during unrest; prevent retaliatory panic; crowd lanes held.","red"),
T("Explosive-storage compliance check near port or rail yards; seals renewed and routes revised.","yellow",{"rail","port","security","industry"}),
    T("High-risk omen-driven gathering dispersed under controlled cadence; separation corridors opened; arrests avoided where possible.","red"),
T("Emergency response to active arson or explosive rumor; sanctified suppression teams deployed with escorts.","red",{"arson","crisis","security"}),
    -- +8 added
T("Post-incident purification circuit across multiple oratories; public confidence restored by nightfall.","red",{"incident","public","crowd"}),
    T("Twin-balance mediation between rival guild blocs; alternating speaking order enforced; compromise text drafted.","green"),
}
    T("Opposites-pair workshop for stewards; role swapping practiced; conflict sensitivity notes filed.","green"),
},
    T("Port/market ‘cycle briefing’: scarcity and surplus explained; calm purchasing guidance posted.","green"),
    T("Symbol dispute review (Indigo/Momiji misuse); offending banners removed; replacement signage approved.","yellow"),
    T("Diplomatic liaison support for visiting delegations; translation scripts issued; etiquette rules posted.","yellow"),
    T("Coordinated counter-panic cadence: staggered announcements; prevent feedback loops; rumor nodes mapped.","yellow"),
    T("Emergency unity convocation after sectarian flare; ceasefire terms read; neutral escorts assigned.","red"),
    T("Mass ominal panic response: structured dispersal waves; calm stations staffed; clinic routes protected.","red"),
  },


{key="CHS", name="Celestial Harmony Sect",
  ["Guild of Golden Shadows"] = {
link=orderLink("Indigo (Reformed Stripping Path)","Celestial_Harmony_Sect","Celestial Harmony Sect"),
    T("Quiet intake: log forged vouchers and coercion attempts; route evidence to Temple Bank without public escalation.","green"),
tasks={
    T("Market observation rotation; identify counterfeit seals and pressure networks; ledger packet assembled.","green"),
T("Diplomatic courtesy visit to a city council; alignments review and oath renewal with minimal spectacle.","green",{"diplomacy","governance","service"}),
    T("Discrete tracing of a forged permit chain; map printer stalls and broker intermediaries; file correlation report.","green"),
T("Star-chart instruction for apprentices; quiet observatory checks and record sealing.","green",{"education","astronomy","service"}),
    T("Undercover purchase of fake shrine/custody seals; seize plates and ledgers; custody memo prepared.","yellow"),
T("Inter-city liaison packet prepared for corridor towns; travel permissions confirmed.","green",{"rail","service","governance"}),
    T("Intercept suspicious manifest and cross-check corridor registry for evasions; flag repeats for inspection teams.","yellow"),
T("Boundary-harmony rite at a district line; neighbor shrines exchange tokens under witness.","green",{"balance","public","mediation"}),
    T("Witness-support arrangement for a threatened vendor; relocation and stipend bridge authorized.","yellow"),
T("Calm briefing for merchant captains on festival timing; reduce congestion and rumor spread.","green",{"port","service","public"}),
    T("Rumor suppression operation: remove false broadsheets; replace with official bulletin; source nodes catalogued.","yellow"),
T("Faction-balance mediation between rival shrines; written compromise placed in Temple Bank archive.","yellow",{"balance","mediation","archive"}),
    T("Night extraction of compromised informant; safe route screened; temporary housing arranged under seal.","red"),
T("Rumor forensics on ‘forbidden rite’ claims; witness tree compiled and forwarded to Temple authorities.","yellow",{"rumor","investigation","public"}),
    T("Emergency counter-coercion action during crowd surge; stabilize lanes; evidence chain protected.","red"),
T("Quiet arbitration after contract dispute between a guild and a council office.","yellow",{"service","governance","mediation"}),
    T("High-risk liaison brief to War League contact: attribution summary + prevention plan; sealed addendum filed.","red"),
T("Transit coordination for high-profile delegates; platform escorts and crowd lanes established.","yellow",{"rail","crowd","security"}),
    -- +8 added
T("High-risk stabilization after cult confrontation; negotiators embedded with escorts to prevent escalation.","red",{"confrontation","crowd","security"}),
    T("Silent audit of a clerk’s ledger drawer; discrepancy list sealed; retraining notice drafted.","green"),
}
    T("Courier-route verification for stipend packets; handoff witnesses arranged; route hazards mapped.","green"),
},
    T("Counterfeit stamp reference kit issued to stewards; detection steps taught; escalation chain posted.","green"),
    T("Targeted sweep for extortion collectors; victim intake protected; suspect descriptions logged.","yellow"),
    T("Dockside inventory double-check after ‘lost crate’ claim; reconciliation performed; fraud flags filed.","yellow"),
    T("Controlled public correction of a forged notice; replace postings; photograph originals; printer path traced.","yellow"),
    T("Major embezzlement exposure: emergency ledger seizure; suspect detention support; restitution memo prepared.","red"),
    T("High-risk port gate fraud surge: inspection teams reinforced; entry metering; evidence chain preserved.","red"),
  },


{key="GGS", name="Guild of Golden Shadows",
  ["Azure Sentinel Sect"] = {
link=orderLink("Chrysos (Reformed Stripping Path)","Guild_of_Golden_Shadows","Guild of Golden Shadows"),
    T("Waterfront perimeter walk; safety rope checks along canal districts; hazards logged for civil works.","green"),
tasks={
    T("Storm-drain inspection with engineers; blockages cleared; maintenance tickets filed.","green"),
T("Discrete audit of offertory ledgers and voucher seals; minor variances reconciled quietly.","green",{"finance","audit","service"}),
    T("Port-house briefing: embarkation discipline, theft prevention, and safe-lane etiquette posted.","green"),
T("Merchant vetting interviews for a sensitive consignment; anonymity rites certified.","green",{"merchant","security","service"}),
    T("Medical-supply escort across bridges; accountability signatures collected; chain-of-custody verified.","yellow"),
T("Port-side seal verification for sacred cargo; chain-of-custody stamped and witnessed.","green",{"port","finance","security"}),
    T("Harbor rumor correction: suppress false quarantine/closure claims; prevent stampede behavior at gates.","yellow"),
T("Rail consignment escort paperwork checked; handover points confirmed with station stewards.","green",{"rail","service","security"}),
    T("SAR standby after storm warnings; muster roster checked; equipment count verified.","yellow"),
T("Quiet fraud-prevention briefing for shrine treasurers; common forgery patterns circulated.","green",{"finance","education","service"}),
    T("Debris clearance coordination after gale; hazard markings placed; access lanes reopened in phases.","yellow"),
T("Counter-counterfeit sweep targeting forged stamps and false Temple consignments; evidence boxed and logged.","yellow",{"forgery","investigation","finance"}),
    T("Emergency flood response: evacuation guidance at waterfront shrines; calm stations maintained; shelter intake controlled.","red"),
T("Debt dispute arbitration for a sanctioned charity line; terms rewritten and sealed.","yellow",{"finance","mediation","service"}),
    T("High-risk escort of officials through port-gate crowd; separation corridor enforced; decoy route staged.","red"),
T("Watchlist update for suspected smugglers using festival crowds; surveillance plan filed.","yellow",{"security","crowd","port"}),
    T("Crisis harbor-closure messaging under red posture: notices distributed; prevent vigilante enforcement at ramps.","red"),
T("Recovery detail for stolen reliquary property; surveillance and lawful seizure under civic warrant.","red",{"theft","security","investigation"}),
    -- +8 added
T("Crisis finance stabilization after panic; emergency stipend routing and rumor suppression coordination.","red",{"panic","finance","service"}),
    T("Canal bridge inspection; loose stone and rail faults flagged; repair tickets prioritized.","green"),
}
    T("Dockworker conduct briefing; safe-lift rules posted; harassment complaints intake opened.","green"),
},
    T("Water purity spot-check at fountains; contamination warnings posted; sampling request filed.","green"),
    T("Controlled queue plan at ferry ramps; entry tokens issued; crowd cadence rehearsed.","yellow"),
    T("Suspicious skiff check support; manifests reviewed; contraband seized; custody signatures obtained.","yellow"),
    T("Storm-surge rehearsal: shelter route posted; boat tie-down checks; public update schedule set.","yellow"),
    T("Port lockdown support after violence; civilian zones cleared; rescue lanes protected; communications centralized.","red"),
    T("Mass casualty waterfront incident: triage staging; family intake; perimeter discipline; after-action memo sealed.","red"),
    T("CLASSIFIED tasking: covert counter-assassin sweep along port approaches; decoy route staged; boat manifests cross-checked; suspect surveillance handed off under seal; public posture maintained as routine SAR readiness.","red"),
  },


{key="ASS", name="Azure Sentinel Sect",
  ["Reverie Nebulous"] = {
link=orderLink("Thalassa (Reformed Stripping Path)","Azure_Sentinel_Sect","Azure Sentinel Sect"),
    T("Dream-circle with strict moderation; de-escalation emphasized; referral cards issued for high anxiety.","green"),
tasks={
    T("Quiet art support for children after a scare; parent coaching provided; school liaison note filed.","green"),
T("Canal and fountain sanctification circuit; water-quality blessings paired with public reassurance.","green",{"water","public","service"}),
    T("Sleep-hygiene teaching for shift workers; rest scheduling guidance posted; rumor triggers explained.","green"),
T("Harbor hygiene briefing for fish markets; cold-chain and wash stations checked.","green",{"port","health","service"}),
    T("Rumor mapping: identify dream narratives feeding panic; corrective story deployed with approved phrases.","yellow"),
T("Flood-gate inspection rite; maintenance tickets issued and signed by civic stewards.","green",{"infrastructure","water","service"}),
    T("Witness interviews after trance-gathering complaint; compliance conditions issued; repeat offenders logged.","yellow"),
T("Ferry pier blessing and rope inspection; boarding lines reorganized to reduce accidents.","green",{"ferry","port","public"}),
    T("Night calming near transit nodes; chanting regulated; separation corridors planned to prevent crush behavior.","yellow"),
T("Reservoir vigil for drought days; ration etiquette posted at distribution points.","green",{"water","relief","public"}),
    T("Post-event narrative edit: reduce retaliatory impulses; speaker training conducted; script packet distributed.","yellow"),
T("Harbor-side watch for sabotage indicators; shrine-wardens trained on lock and pier checks.","yellow",{"port","sabotage","security"}),
    T("Containment support for mass panic dream episode; supervised rest zones opened; clinic corridor protected.","red"),
T("Storm or flood readiness rite; evacuation hymn routes verified with civic stewards.","yellow",{"flood","infrastructure","public"}),
    T("Emergency counseling for families after fatal incident; grief rites paired with aid referrals.","red"),
T("Waterborne rumor containment bulletin; discourage panic buying and hoarding.","yellow",{"rumor","service","public"}),
    T("High-risk ‘dream-breach’ report assessed with Aprobelle liaisons; misinformation suppressed; controlled bulletin issued.","red"),
T("Quarantine support for suspected waterborne illness; controlled distribution points blessed and monitored.","red",{"outbreak","water","health"}),
    -- +8 added
T("Major incident response: water infrastructure damage; emergency sanctification and ration-order enforcement.","red",{"infrastructure","crisis","public"}),
    T("Dream journal distribution; privacy rules explained; escalation criteria posted for severe episodes.","green"),
}
    T("Quiet ‘waking boundary’ lesson; discourage prophetic claims; practical coping steps posted.","green"),
},
    T("Artist liaison with civic stewards; performance permits checked; safe audience limits posted.","green"),
    T("Sweep for fraudulent ‘dream sellers’; counterfeit charms seized; victim refunds routed through Amicitia.","yellow"),
    T("Night-market calming protocol: light levels raised; exit lanes marked; chant limits enforced.","yellow"),
    T("Repeat nightmare cluster triage; trigger map compiled; clinic referrals issued; follow-up schedule set.","yellow"),
    T("Mass hysteria response after shared-vision rumor; calm stations expanded; scripts looped; arrests minimized.","red"),
    T("Violent trance incident containment: victim protection; offender transfer; witness shielding; records sealed.","red"),
  },


{key="REN", name="Reverie Nebulous",
  ["Eon Fellowship"] = {
link=orderLink("Nephele (Reformed Stripping Path)","Reverie_Nebulous","Reverie Nebulous"),
    T("Timing audit at market gates: shift rotations clarified; reduce conflict from missed windows.","green"),
tasks={
    T("Public calendar correction bulletin; prevent rumor-driven delays and missed rites; signage posted.","green"),
T("Dream-interpretation clinic to reduce anxiety; rumor narratives redirected into sanctioned symbolism.","green",{"dream","rumor","service"}),
    T("Clinic/shrine entry metering drill: queue cadence tested; stewards trained on timing scripts.","green"),
T("Nocturne counseling for insomnia cluster; soft-lit rites and chant rotation schedules.","green",{"health","sleep","service"}),
    T("Witness-arrival scheduling for a hearing; time windows staged to deter intimidation; escort plan posted.","yellow"),
T("Public guidance sheet on lawful visions vs. sedition pamphlets; distributed at shrines.","green",{"education","public","service"}),
    T("Transit-flow smoothing at rail/canal chokepoint; signage updated; alternate timing lane opened.","yellow"),
T("Quiet household visits after a death or displacement; stabilize grief cycles with sanctioned imagery.","green",{"service","health","public"}),
    T("Record review: repeated stipend-delay disputes analyzed; corrective SOP issued; clerk sign-off obtained.","yellow"),
T("Festival mood stewardship: prevent illicit revel by offering sanctioned evening rites.","green",{"festival","public","morale"}),
    T("Night timing plan during fog disruption; safe intervals posted; navigation warnings coordinated.","yellow"),
T("Investigation of forbidden homily broadcasts; witness interviews and calm messaging drafted.","yellow",{"broadcast","investigation","service"}),
    T("Emergency stabilization after cascading delays: volunteer surge roster synchronized; safe movement hours posted.","red"),
T("Rumor cascade mapping in service districts; identify the first carrier nodes.","yellow",{"rumor","investigation","service"}),
    T("Crisis schedule triage during unrest: curfew windows clarified; dispersal routes timed and enforced.","red"),
T("Containment of mass-vision panic; staged public reassurance with tightly controlled procession.","red",{"panic","crowd","public"}),
    T("High-risk timing escort for custody transfer; decoy timing used; route denial staged.","red"),
T("Stabilization after hallucination-linked violence; liaison with Harmony Sanctum for long-tail care.","red",{"violence","health","service"}),
    -- +8 added
T("Emergency night curfew-support rite; safe corridors marked to reduce shadow-fear incidents.","yellow",{"security","public","service"}),
    T("Endurance briefing for patrol rotations; fatigue flags logged; relief roster posted.","green"),
}
    T("Winterization checklist posted (wind, ice, flood); supply needs tallied; maintenance tickets filed.","green"),
},
    T("Shelter readiness audit; blanket inventory checked; entry rules posted; complaint intake opened.","green"),
    T("Ration-line cadence enforcement; entry stamps issued; fights deterred; incident log filed.","yellow"),
    T("Gate sabotage rumor response; key control audit; access tightened; night watch schedule posted.","yellow"),
    T("Critical-utility staffing pact mediation; interim rota signed; enforcement stewards assigned.","yellow"),
    T("Storm emergency shelter activation: intake discipline; ration control; rumor suppression; clinic route protected.","red"),
    T("Civil disorder deterrence posture: curfew windows broadcast; safe lanes enforced; ringleaders detained.","red"),
  },


{key="EOF", name="Eon Fellowship",
  ["Order of the Umbral Oracle"] = {
link=orderLink("Glinos (Reformed Stripping Path)","Eon_Fellowship","Eon Fellowship"),
    T("Quiet-night rite with strict entry control; visitor conduct posted; disturbance triggers explained.","green"),
tasks={
    T("Counseling for anxious households after nightmare spike; referral paths established; follow-up logged.","green"),
T("Cold-weather relief checks and ration fairness review; storage seals verified.","green",{"relief","logistics","service"}),
    T("Night watch at shrine steps to deter harassment; steward coordination and incident ledger maintained.","green"),
T("Infrastructure endurance audit: roofs, walls, and bridges; damage triage list issued.","green",{"infrastructure","audit","industry"}),
    T("Risk bulletin for shadow-sign nights; discourage panic gatherings at transit nodes; routes marked.","yellow"),
T("Shelter staffing rotation planned for storm season; volunteer rosters sealed.","green",{"relief","service","governance"}),
    T("Investigation of anonymous pamphlets claiming ‘oracle sanction’; counterfeit marks documented; plates traced.","yellow"),
T("Rail convoy support brief for frontier deliveries; pacing and rest points set.","green",{"rail","logistics","service"}),
    T("Audit of unsanctioned night gatherings; compliance restrictions issued; repeat venues logged.","yellow"),
T("Queue discipline at distribution points; conflict triggers reduced through signage.","green",{"order","queue","public"}),
    T("Post-incident narrative discipline: speaker scripts updated; rumor sources mapped; correction bulletins deployed.","yellow"),
T("Conflict de-escalation for resource disputes; binding queue rules posted at distribution points.","yellow",{"order","queue","governance"}),
    T("Containment support for ‘demonic sign’ rumor under red posture: controlled messaging delivered; calm stations opened.","red"),
T("Emergency shelter activation after storm loss; warm-hall rites and controlled entry.","red",{"storm","relief","public"}),
    T("Witness protection for informant threatened by cult rival; safehouse route planned; decoy escort used.","red"),
T("Frontier outpost morale visit; endurance rites and supply accountability review.","yellow",{"frontier","audit","service"}),
    T("Crisis nocturnal dispersal after crowd surge; safe corridors opened; reunification tent activated.","red"),
T("Bridge closure coordination after damage report; detours posted and escorted.","yellow",{"infrastructure","transit","public"}),
    -- +8 added
T("High-risk escort for frontier convoys under threat; endurance cadres embedded with wardens.","red",{"frontier","security","logistics"}),
    T("Nocturnal observation log sweep; key anomalies summarized; sealed note sent to Temple Bank.","green"),
}
    T("Lamp-and-lock check on vulnerable alleys; household guidance posted; repair tickets filed.","green"),
},
    T("Confidential witness intake at a night gate; testimony recorded; protection options listed.","green"),
    T("Blackout exploitation patrol; temporary lighting placed; entry control enforced; suspects logged.","yellow"),
    T("Cultic graffiti catalog; symbols photographed; cleanup ordered; copycat deterrence notice posted.","yellow"),
    T("Stalking complaint response; escort plan staged; restraining paperwork initiated; incident ledger updated.","yellow"),
    T("Assassination rumor triage: safe corridors established; witness relocation; evidence sealed; briefings issued.","red"),
    T("Kidnapping response support: gate checks tightened; informant intake opened; safehouse activation logged.","red"),
  },


{key="UOR", name="Order of the Umbral Oracle",
  ["Mystery of the Verdant Embrace"] = {
link=orderLink("Noctis (Reformed Stripping Path)","Order_of_the_Umbral_Oracle","Order of the Umbral Oracle"),
    T("Canal cleanup rite; silt removed; flow restored; maintenance log filed.","green"),
tasks={
    T("Seed blessing for gardeners; pest-prevention guidance distributed; vendor hygiene note appended.","green"),
T("Night watch and omen-reading in the temple quarter; guidance slips sealed for officials.","green",{"omen","security","service"}),
    T("Wetland path maintenance; travel warnings posted; drowning prevention markers installed.","green"),
T("Shadow-lane safety marking near canals and alleys; lantern placement logged.","green",{"public","security","transit"}),
    T("Aqueduct/sluice audit: leakage mapped; repair crews coordinated; access rules posted at pump houses.","yellow"),
T("Quiet counsel for a magistrate; truth-testing rite witnessed by two scribes.","green",{"governance","inquiry","service"}),
    T("Field mediation over irrigation rights; shared schedule drafted; signatures collected.","yellow"),
T("Missing-persons intake desk hosted at a shrine; reports standardized and forwarded.","green",{"service","investigation","public"}),
    T("Vector mitigation at swamp edges; standing water treated; guidance posted at shelters.","yellow"),
T("Nighttime rumor monitoring in taverns and stations; early interventions logged.","green",{"rumor","rail","service"}),
    T("Post-storm debris clearance with volunteers; hazard markings placed; lanes reopened in phases.","yellow"),
T("Discrete consultation for a magistrate; truth-testing rite witnessed by two scribes.","yellow",{"governance","inquiry","audit"}),
    T("Emergency flood relief: sandbag lines, food staging, shelter support; calm messaging maintained.","red"),
T("Shadow-route mapping after disappearances; safe-path markers installed with civic approval.","yellow",{"security","transit","public"}),
    T("Crisis canal-breach response: closure and reroute; crowd calming in shrine districts; clinic corridor protected.","red"),
T("Evidence sealing after a midnight incident; chain-of-custody verified for Temple authorities.","yellow",{"investigation","security","service"}),
    T("High-risk contamination response at water points; sampling escorted; panic narratives suppressed.","red"),
T("Emergency intervention after ‘demonic sign’ sightings; controlled crowd handling and ward placement.","red",{"demonic","panic","crowd"}),
    -- +8 added
T("High-risk extraction of a threatened cantor; night escort to secure sanctuary.","red",{"abduction","security","service"}),
    T("Apothecary batch check for adulterants; approved sources posted; corrective warnings issued.","green"),
}
    T("Community garden rota mediation; tool access rules posted; theft prevention signage installed.","green"),
},
    T("Midwife support circuit; recovery herbs issued; follow-up visits scheduled; privacy rules posted.","green"),
    T("Illness cluster mapping near wetlands; symptom intake standardized; clinic referrals issued.","yellow"),
    T("Contaminated produce recall support; vendor notices posted; refunds coordinated with Amicitia.","yellow"),
    T("Pest outbreak triage briefing; safe treatment guidance; livestock protection steps posted.","yellow"),
    T("Epidemic response support: quarantine boundaries marked; care kits staged; misinformation suppressed.","red"),
    T("Poisoning event response: antidote routing; source trace; suspect packet assembled; water access controlled.","red"),
  },


{key="MVE", name="Mystery of the Verdant Embrace",
  ["Conclave Illuminara"] = {
link=orderLink("Opsithe (Reformed Stripping Path)","Mystery_of_the_Verdant_Embrace","Mystery of the Verdant Embrace"),
    T("Night-sky lecture: explain calendars; reduce omen anxiety; post practical safety guidance.","green"),
tasks={
    T("Quiet meditation ward at shrine; breathing protocols taught; de-escalation reminders posted.","green"),
T("Harvest blessing and storage rite; spoilage prevention guidance issued to granaries.","green",{"agriculture","harvest","agri"}),
    T("Lantern-route inspection; reflective markers placed; hazard steps flagged.","green"),
T("Garden-shrine restoration day with community labor; devotional planting aligned to calendar.","green",{"garden","shrine","agri"}),
    T("Dream-space custody audit: offerings catalogued; safety signage posted; access rules clarified.","yellow"),
T("Seed and tool stipend audit for smallholders; vouchers revalidated.","green",{"audit","agri","service"}),
    T("Counterfeit moon-token investigation; vendor interviews; seizure log compiled; restitution note drafted.","yellow"),
T("Irrigation channel blessing and silt-clearing volunteer day; water etiquette posted.","green",{"water","agri","public"}),
    T("Controlled night gathering after rumor flare; calm-litany enforced; dispersal cadence rehearsed.","yellow"),
T("Market stall purity checks for produce; vendors instructed on safe storage.","green",{"public","health","agri"}),
    T("Blackout safety drill for storm season: guides staged; warning ropes placed; shelter route posted.","yellow"),
T("Food shortage mediation and ration arbitration; Temple Bank stipend adjustments proposed.","yellow",{"shortage","logistics","agri"}),
    T("Emergency anti-panic messaging after ‘demonic sign’ claim; speaker scripts distributed; rumor sources logged.","red"),
T("Pest or blight containment rites; field inspections with farmers’ associations.","yellow",{"blight","agriculture","agri"}),
    T("Escort for sensitive nocturnal rite during unrest; perimeter observers posted; clinic access protected.","red"),
T("Livestock procession routing for festival markets; prevent crush and theft.","yellow",{"crowd","festival","agri"}),
    T("High-risk crowd-control during an overfull vigil; separation corridors held; reunification tent activated.","red"),
T("Famine-risk surge response; emergency distribution queues sanctified and monitored.","red",{"famine","relief","agri"}),
    -- +8 added
T("Emergency containment of contaminated grain rumor; sample testing coordinated with Sanctum Vitalis.","yellow",{"rumor","health","agri"}),
    T("Lunar-cycle briefing for stewards; ‘dark segment’ messaging standardized; posting schedule set.","green"),
}
    T("Archive copy check: calendars and proclamations reconciled; corrections issued; stamps verified.","green"),
},
    T("Diplomatic reading for visiting officials; neutral phrasing drafted; public reassurance script prepared.","green"),
    T("Forgery sweep for ‘Conclave decree’ sheets; originals photographed; printer path traced.","yellow"),
    T("Vigil capacity enforcement; entry tokens issued; exit lanes marked; medics staged.","yellow"),
    T("Rumor correction loop after eclipse claims; public broadcasts timed; panic buying deterred.","yellow"),
    T("Mass fear response after prolonged blackout; calm stations opened; shelter routes protected; patrol liaison engaged.","red"),
    T("Crisis interpretive bulletin during unrest: ‘cycle framing’ delivered hourly; retaliatory panic prevented.","red"),
  },


{key="CIL", name="Conclave Illuminara",
  ["Sanctum Vitalis"] = {
link=orderLink("Tarsica (Reformed Stripping Path)","Conclave_Illuminara","Conclave Illuminara"),
    T("Clinic hygiene audit: water barrels sealed; entry protocols posted; handwashing station checks logged.","green"),
tasks={
    T("Vaccination schedule coordination with stewards; consent ledger reconciled; follow-up appointments scheduled.","green"),
T("Moonlit instruction circle; eclipse-watch protocol rehearsed with apprentice scribes.","green",{"lunar","education","service"}),
    T("Triage coaching circle: calm scripts practiced; escalation triggers clarified; volunteer assignments posted.","green"),
T("Night-sky bulletin posted to reduce panic during overcast weeks; lawful interpretations provided.","green",{"public","rumor","service"}),
    T("Food-handling inspection at market stalls; spoiled goods removed; corrective warnings issued.","yellow"),
T("Observatory instrument check; calibration seals signed and archived.","green",{"archive","audit","service"}),
    T("Quarantine rumor suppression: correct false ‘plague’ claim; stabilize clinic queues; calm stations opened.","yellow"),
T("Lantern route blessing for tunnels and arcades; wayfinding clarified for late shifts.","green",{"transit","public","service"}),
    T("Vector mitigation near wetlands; standing water treated; guidance posted at dorms and shelters.","yellow"),
T("Quiet archive preservation: scroll humidity checks and secure storage review.","green",{"archive","service","audit"}),
    T("Witness interviews after ‘poisoned well’ claim; evidence packet assembled; sampling escorted.","yellow"),
T("Archive sealing of sensitive liturgies; access logs reviewed for anomalies.","yellow",{"archive","audit","security"}),
    T("Emergency fever center support: intake metering + calm stations established; separation corridor enforced.","red"),
T("Public lantern rite to counter rumor cascades; controlled routes and timing set with wardens.","yellow",{"rumor","crowd","public"}),
    T("Crisis medical escort for officials through crowd; protected clinic route held; decoy timing staged.","red"),
T("Transit hub advisory after night panic; crowd lanes re-drawn and posted.","yellow",{"rail","crowd","transit"}),
    T("High-risk contamination response: water points shut temporarily; alternate distribution controlled; panic narratives suppressed.","red"),
T("Emergency omen bulletin after night panic; official statement drafted and delivered.","red",{"panic","public","service"}),
    -- +8 added
T("High-risk protection of an observatory site under threat; wards reinforced and watchers stationed.","red",{"threat","security","service"}),
    T("Spring/well inspection; contamination risks logged; repair tickets filed; public boil guidance posted if needed.","green"),
}
    T("Clinic staff rota stabilization; break schedule enforced; burnout flags logged; relief volunteers assigned.","green"),
},
    T("Worksite injury-prevention briefing; hazards recorded; employer compliance sign-off obtained.","green"),
    T("Respiratory illness surge triage; mask distribution staged; ventilation rules posted; clinic flow adjusted.","yellow"),
    T("Food-borne illness scare response; vendor recall notices; refund guidance; culprit investigation packet started.","yellow"),
    T("Overcrowded clinic gate plan; entry metering tightened; conflict scripts deployed; escort corridor protected.","yellow"),
    T("Mass casualty support: triage expansion; supply convoy prioritization; family intake; mortality ledger sealed.","red"),
    T("Waterborne outbreak response: quarantine lines; alternate water distribution; daily briefings; misinformation suppressed.","red"),
  },


{key="SCV", name="Sanctum Vitalis",
  ["Temple Alabaster"] = {
link=orderLink("Plateau (Reformed Stripping Path)","Sanctum_Vitalis","Sanctum Vitalis"),
    T("Public conduct rite at shrine; rules reissued; volunteers coached on nonviolent enforcement.","green"),
tasks={
    T("Counseling circle for fearful households; practical aid referrals arranged; follow-up log kept.","green"),
T("Clinic day: basic health screenings and hygiene instruction; water points checked.","green",{"health","public","service"}),
    T("Relief custody check: inventory counted; seals verified; theft-prevention signage posted.","green"),
T("Sanctioned healer coordination meeting; triage roles rehearsed for outbreaks.","green",{"training","health","service"}),
    T("Vendor compliance sweep for counterfeit purity seals; seizures logged; corrective warnings issued.","yellow"),
T("Hygiene signage deployment at markets; handwash stations verified.","green",{"public","health","service"}),
    T("Witness interviews after shrine vandalism; sworn statements taken; intimidation complaints logged.","yellow"),
T("Nutrition counseling for ration lines; supplement vouchers distributed under Temple Bank authorization.","green",{"nutrition","relief","service"}),
    T("Night watch rotation on shrine steps after threat chatter; steward roster posted.","yellow"),
T("Quiet home checks for vulnerable households; prevent panic spirals after illness rumors.","green",{"service","health","rumor"}),
    T("Escort of sacred item to storage; chain-of-custody verified; route screening documented.","yellow"),
T("Supplement and ration fortification distribution under Temple Bank authorization.","yellow",{"nutrition","relief","service"}),
    T("Emergency crowd control during pilgrimage surge; lanes marked; calm stations opened; reunification tent active.","red"),
T("Outbreak protocol rehearsal with civic healers; spacing rules posted at shrines.","yellow",{"outbreak","training","public"}),
    T("Crisis shelter activation in stone precinct during storm; intake discipline enforced; clinic route protected.","red"),
T("Quarantine lane setup drill at a terminal or station; entry control rehearsed.","yellow",{"rail","security","health"}),
    T("High-risk perimeter reinforcement after credible attack rumor; entry controlled; decoy dispersal route staged.","red"),
T("Active outbreak response: triage lanes established; quarantine support and calm messaging.","red",{"outbreak","crisis","public"}),
    -- +8 added
T("Mass-casualty contingency drill after violence reports; intake points and transport routes mapped.","red",{"violence","logistics","service"}),
    T("Purity inspection of public wash points; contamination warnings posted; maintenance tickets filed.","green"),
}
    T("Serenity hour enforcement in market lanes; noise caps posted; disputes mediated; incident log updated.","green"),
},
    T("Sanitation compliance coaching for vendors; corrective steps issued; follow-up date scheduled.","green"),
    T("Crowd cooling protocol outside shrine gates; separation lanes held; harassment complaints intake opened.","yellow"),
    T("Contaminated goods sweep (dye/soap/food); items pulled; source traced; public reassurance posted.","yellow"),
    T("Minor desecration repair coordination; evidence preserved; restoration rite scheduled; offenders logged.","yellow"),
    T("Major desecration fallout: crowd calmed; arrests coordinated; restoration rites performed; retaliation deterred.","red"),
    T("Toxic spill response support: exclusion zone enforced; cleanup supervised; health referrals issued; messaging standardized.","red"),
  },


{key="ALB", name="Temple Alabaster",
  ["Court of the Ironclad"] = {
link=orderLink("Ivory (Reformed Stripping Path)","Temple_Alabaster","Temple Alabaster"),
    T("Volunteer discipline training; nonviolent restraint emphasized; reporting scripts practiced.","green"),
tasks={
    T("Public order presence at shrine steps; etiquette enforced; minor disputes mediated on-site.","green"),
T("Purity inspection of ritual vessels and incense stock; minor replacements approved.","green",{"purity","shrine","service"}),
    T("Oath workshop: pledge cards sealed for civic stewards; compliance expectations posted.","green"),
T("Doctrinal reading circle for novices; sanctioned texts distributed with margin notes.","green",{"education","service","public"}),
    T("Route hardening walk: broken lamps and unsafe crossings flagged; repair tickets filed.","yellow"),
T("Shrine inventory check for relic storage; locks and witness rules enforced.","green",{"security","shrine","audit"}),
    T("Contraband confiscation support; evidence logged for Temple Bank clerks; custody chain verified.","yellow"),
T("Quiet correction of unsanctioned household rites; guidance issued without spectacle.","green",{"service","public","education"}),
    T("Night watch on canal bridges after sabotage chatter; checkpoint discipline enforced; escorts scheduled.","yellow"),
T("Blessing of new white-draped oratory; entry etiquette posted to reduce disorder.","green",{"shrine","public","service"}),
    T("Post-incident intimidation review: complaints investigated; corrective discipline applied; report filed.","yellow"),
T("Doctrinal correction session after unsigned broadsides; sanctioned texts distributed.","yellow",{"heresy","education","public"}),
    T("High-risk escort for witness leaving hearing; safehouse transfer arranged; decoy route used.","red"),
T("Sanctuary security review for valuables; locking discipline and witness rules enforced.","yellow",{"security","shrine","service"}),
    T("Emergency deterrence patrol after riot flare; separation corridors opened; care stations deployed.","red"),
T("Investigation of tainted incense vendors; samples seized and chain-of-custody logged.","yellow",{"poison","investigation","service"}),
    T("Crisis reinforcement at clinic gate during panic surge; queue discipline established; dispersal cadence enforced.","red"),
T("Emergency purification after poisoned incense discovery; wards placed and offenders sought.","red",{"poison","crisis","security"}),
    -- +8 added
T("High-risk protection of a central temple during unrest; entry control and calm liturgy.","red",{"unrest","security","crowd"}),
    T("Endurance drill briefing; heat/fatigue limits posted; rotation enforcement logged.","green"),
}
    T("Equipment discipline check; missing items reconciled; chain-of-command sign-off obtained.","green"),
},
    T("Desert-procession conduct rules posted; hydration points marked; medical standby coordinated.","green"),
    T("Rival faction confrontation risk patrol; meeting sites mapped; entry controls tightened; warnings issued.","yellow"),
    T("Bloodmoon rumor suppression; discourage panic gatherings; authorized statement posted; agitator list started.","yellow"),
    T("Arrest processing support after brawl surge; evidence tagging enforced; witness separation maintained.","yellow"),
    T("Sectarian violence surge: unified deterrence posture; safe corridors held; ringleaders detained; reprisals prevented.","red"),
    T("Critical sabotage threat: depot perimeter locked; entry metering; night watches doubled; public notice issued.","red"),
  },


{key="CIC", name="Court of the Ironclad",
  ["Accord Concordia"] = {
link=orderLink("Fenrir (Reformed Stripping Path)","Court_of_the_Ironclad","Court of the Ironclad"),
    T("Council mediation between neighborhood elders; written concord signed; follow-up date scheduled.","green"),
tasks={
    T("Public forum moderation; speaking order enforced; agitator removal avoided through de-escalation.","green"),
T("Discipline drill for shrine wardens; non-lethal control and restraint practices.","green",{"training","security","service"}),
    T("Vendor-stall settlement: restitution terms logged; compliance expectations posted.","green"),
T("Escort for a threatened courier; route cleared and handover documented.","green",{"escort","security","service"}),
    T("Civic etiquette training for steward teams; de-escalation scripts practiced; lane protocol rehearsed.","yellow"),
T("Patrol coordination with civic guards at terminals; hand signals and cordon rules standardized.","green",{"rail","security","service"}),
    T("Inter-cult boundary meeting; shared space rules posted at shrine gates; signage installed.","yellow"),
T("Gatehouse posture review for festival season; crowd lanes chalked and posted.","green",{"festival","crowd","security"}),
    T("Rumor containment after inflammatory broadsides; corrective bulletin cycle launched; sources logged.","yellow"),
T("Weapons discipline check for attached wardens; compliance logs signed.","green",{"audit","security","service"}),
    T("Audit of district clerk office after complaint surge; corrective plan countersigned; retraining scheduled.","yellow"),
T("Guard coordination in a frontier district; watch rotations synchronized with civic militia.","yellow",{"militia","security","frontier"}),
    T("Emergency truce enforcement during street clash; separation lines held; medical access protected.","red"),
T("Immediate reinforcement during riot signals; cordons established and safe egress opened.","red",{"riot","crowd","security"}),
    T("Crisis reconciliation hearing after riot; pledge collection + witness support; retaliation prevention enforced.","red"),
T("Interdiction rehearsal for barricade incidents; safe corridors mapped.","yellow",{"barricade","security","public"}),
    T("High-risk mediation under red posture: rival leaders separated; agreement read publicly; dispersal routes staged.","red"),
T("High-risk interdiction support for a seized oratory; secure perimeter until authorities arrive.","red",{"siege","security","public"}),
    -- +8 added
T("Protection detail for water or rail infrastructure after sabotage rumor; sentries posted.","yellow",{"sabotage","infrastructure","security"}),
    T("Harmony Gardens work party coordination; mixed crews paired; grievances logged; follow-up circle scheduled.","green"),
}
    T("Shared-meal hosting plan; seating map prevents rival clustering; steward assignments posted.","green"),
},
    T("Welcome rite for new migrants/work crews; support contacts listed; exploitation warnings posted.","green"),
    T("Feast sabotage risk triage; ingredient chain verified; kitchen access controlled; guard liaison engaged.","yellow"),
    T("Inter-guild boycott settlement; compromise pledge signed; enforcement stewards appointed; public notice issued.","yellow"),
    T("Youth rivalry cooling via structured games; chaperones assigned; penalties posted for violence.","yellow"),
    T("Emergency unity convocation after fatal clash; ceasefire sworn; escort corridors established; reprisals deterred.","red"),
    T("Mass panic calming centers opened; volunteer surge; hourly briefings delivered; rumor nodes suppressed.","red"),
  },


{key="ACC", name="Accord Concordia",
  ["Mystery of the Stygian Veil"] = {
link=orderLink("Lukedu (Reformed Stripping Path)","Accord_Concordia","Accord Concordia"),
    T("Funerary custody check: offerings catalogued; tomb precinct rules posted; candle/fire safety verified.","green"),
tasks={
    T("Grief counseling circle; practical aid referrals arranged; follow-up home visits scheduled.","green"),
T("Public harmony dialogue; disputants guided to a signed concord recorded by Temple Bank scribes.","green",{"mediation","public","service"}),
    T("Mortuary hygiene audit; disease-prevention protocols verified; sanitation failures logged.","green"),
T("Garden pavilion mediation for family inheritance disputes; settlement sealed.","green",{"mediation","service"}),
    T("Rumor suppression after ‘restless dead’ claims; evidence bulletin issued; agitation triggers tracked.","yellow"),
T("Civic cooperation workshop for rival guilds; shared calendar commitments set.","green",{"guild","coordination","service"}),
    T("Escort for body transfer; route secured; witnesses logged; custody chain verified.","yellow"),
T("Inter-shrine calendar alignment meeting; procession timings standardized.","green",{"balance","public","service"}),
    T("Night vigil supervision to prevent stampede or retaliatory violence; separation corridor planned.","yellow"),
T("Quiet counseling for newly formed neighborhood councils; rules of debate issued.","green",{"governance","education","service"}),
    T("Witness interviews after desecration; sworn statements sealed; intimidation complaints recorded.","yellow"),
T("Catacomb Harmonic Communion support: logistics, spacing, and participant verification.","yellow",{"rite","logistics","service"}),
    T("Crisis response after mass death incident: grief triage + order restoration; calm stations opened.","red"),
T("Emergency reconciliation after faction clash; mediators embedded with escorts.","red",{"clash","crowd","security"}),
    T("Emergency calm stations at cemetery gate during panic; supervised dispersal coordinated; clinic access protected.","red"),
T("Transit-lane mediation after station dispute; queues reset and monitored.","yellow",{"rail","queue","public"}),
    T("High-risk memorial crowd control under red posture; perimeter held; reunification tent activated.","red"),
T("Harmonization response after rumor spike; joint bulletin with Harmony Sanctum circulated.","yellow",{"rumor","service","public"}),
    -- +8 added
T("High-risk crowd calming after panic; sound-liturgical dispersal and safe routes enforced.","red",{"panic","crowd","public"}),
    T("Rite of transition for a recovering patient; symbolic veil-lifting; community support plan logged.","green"),
}
    T("Inheritance dispute mediation after death; fair division terms drafted; coercion checks recorded.","green"),
},
    T("Cemetery path safety inspection; uneven steps flagged; lantern markers placed; maintenance tickets filed.","green"),
    T("Dusk procession route reset after hazard; entry cadence rehearsed; pinch points staffed.","yellow"),
    T("Grave-robbery scare investigation support; patrol liaison; witness intake; rumor correction posted.","yellow"),
    T("Overcrowded vigil management; capacity limits posted; exit lanes marked; medics staged.","yellow"),
    T("After-violence mourning containment; safe corridors held; retaliation ban sworn; witness shielding arranged.","red"),
    T("Mass panic after ‘demonic figure’ report near tomb precinct; authoritative denial; agitator detention; calm stations expanded.","red"),
  },


{key="MSV", name="Mystery of the Stygian Veil",
  ["Sylvan Fellowship"] = {
link=orderLink("Styx (Reformed Stripping Path)","Mystery_of_the_Stygian_Veil","Mystery of the Stygian Veil"),
    T("Forest-path maintenance; travel warnings posted for pilgrims; hazard markers refreshed.","green"),
tasks={
    T("Wildlife safety briefing; reduce accidents on shrine trails; reporting chain posted.","green"),
T("Funerary liaison and cemetery vigil support; grief rites standardized.","green",{"funeral","public","service"}),
    T("Ecological repair planning: volunteer crews scheduled; debris staging plan filed.","green"),
T("Registry update for burials and memorial offerings; records sealed and witnessed.","green",{"archive","audit","service"}),
    T("Herb/timber vendor conduct audit; sustainable collection rules enforced; repeat violators logged.","yellow"),
T("Transition counseling for displaced families; resettlement blessings recorded.","green",{"displacement","relief","service"}),
    T("Brushfire-watch patrol; illegal burns suppressed; emergency water points marked.","yellow"),
T("Cemetery perimeter inspection; prevent vandalism during festival nights.","green",{"security","public","service"}),
    T("Mediation between trappers and villagers; shared schedule signed; conflict triggers identified.","yellow"),
T("Quiet rites for recovered remains after an accident; soothe panic and rumor.","green",{"public","rumor","service"}),
    T("Rumor containment after ‘forest curse’ claim; corrective bulletin cycle run; agitator list started.","yellow"),
T("Investigation of graveyard desecration rumor; ward placement and witness interviews.","yellow",{"desecration","investigation","security"}),
    T("Emergency shelter staging in forest precinct during storm; intake controlled; calm messaging enforced.","red"),
T("Relic transfer paperwork verified for a shrine move; escorts scheduled.","yellow",{"escort","security","service"}),
    T("Crisis search for missing pilgrim after fog; guides deployed; safe corridors opened.","red"),
T("Emergency containment after death-cult provocation; controlled rites to prevent escalation.","red",{"provocation","crowd","security"}),
    T("High-risk evacuation support under red weather alert; trail closures enforced; reunification tent activated.","red"),
T("High-risk escort of relics through hostile streets; secure route planning with wardens.","red",{"escort","security","public"}),
    -- +8 added
T("Post-violence reconciliation vigil at a canal memorial; prevent retaliation cycles.","yellow",{"violence","public","mediation"}),
    T("Trailhead sign refresh: safe routes marked; dangerous gullies flagged; escort contacts posted.","green"),
}
    T("Livestock safety day; grazing boundaries walked; injury checks; neighbor oaths exchanged.","green"),
},
    T("Waterway cleanup coordination; contamination risks logged; volunteer rota posted.","green"),
    T("Illegal poaching/logging inquiry; evidence gathered; offender descriptions logged; patrol liaison engaged.","yellow"),
    T("Flood/mud hazard reroute postings; bridge warnings issued; rescue staging areas marked.","yellow"),
    T("Farmer–rancher conflict triage; grazing calendar set; penalties defined; enforcement stewards assigned.","yellow"),
    T("Major flood response: shelter intake; animal rescue; public briefings; rumor suppression enforced.","red"),
    T("Violent land access dispute: containment support; arrests coordinated; restitution terms drafted; reprisals deterred.","red"),
  },


{key="SYF", name="Sylvan Fellowship",
  ["Mystery of Red Mirth"] = {
link=orderLink("Faun (Reformed Stripping Path)","Sylvan_Fellowship","Sylvan Fellowship"),
    T("Festival conduct briefing; prevent brawls and dangerous dares; de-escalation scripts posted.","green"),
tasks={
    T("Joy-warden patrol: intervene early in disputes; stabilize lines outside taverns and music halls.","green"),
T("Forest-edge shrine maintenance and trail safety check; minor repairs logged.","green",{"trail","shrine","agri"}),
    T("Volunteer steward training: safe celebration guidance and nonviolent intervention.","green"),
T("Ecology rite with farmers: pest balance and water stewardship guidance.","green",{"ecology","agri","service"}),
    T("Vendor compliance audit for intoxicants; counterfeit batches seized; sanitation notes issued.","yellow"),
T("Wildlife boundary notices posted; discourage poaching near sacred groves.","green",{"public","security","agri"}),
    T("Crowd choreography for parade route; crush-risk minimized; pinch points marked.","yellow"),
T("Community foraging orientation; prevent poisoning and rumor spirals.","green",{"education","health","agri"}),
    T("Rumor suppression after scandal broadsides; approved narrative posted; source stalls logged.","yellow"),
T("Riverbank cleanup day; improve water flow and reduce disease risk.","green",{"water","health","public"}),
    T("Night watch at tavern row after violence chatter; calm messaging distributed; escorts staged.","yellow"),
T("Containment of illegal logging or poaching rumor; community patrol coordination.","yellow",{"poaching","security","investigation"}),
    T("Emergency stabilization after riot flare; separation lanes + care stations deployed; clinic access protected.","red"),
T("Fire season preparedness for woodland towns; firebreak rites and evacuation path marking.","yellow",{"wildfire","public","security"}),
    T("Crisis safe escort for a key witness leaving hearing; safehouse transfer arranged; decoy route used.","red"),
T("Blight liaison with Verdant Embrace; field sampling routes scheduled.","yellow",{"blight","agri","service"}),
    T("High-risk crowd-control during a packed revel; dispersal cadence enforced; reunification tent activated.","red"),
T("Emergency response to wildfire risk; firebreak rites and evacuation path marking.","red",{"wildfire","crisis","public"}),
    -- +8 added
T("High-risk protection for a threatened grove shrine; cordon and ward reinforcement.","red",{"threat","security","public"}),
    T("Mask/prop safety inspection for performers; sharp edges removed; permit list verified.","green"),
}
    T("Controlled wine dosing guidance posted; hydration points marked; refusal rights explained.","green"),
},
    T("Aftercare check for revelers; transport escorts staged; harassment complaints intake opened.","green"),
    T("Stage rigging sabotage check; inspection log filed; access limited; suspects noted.","yellow"),
    T("Brawl-prone venue warning and temporary closure protocol; staff trained; incident ledger updated.","yellow"),
    T("Parade route split after crush-risk alert; alternate route posted; crowd cadence rehearsed.","yellow"),
    T("Stampede incident response: triage lanes; family reunification; safety reforms mandated; public statement issued.","red"),
    T("Coordinated sabotage campaign response: permit freeze; targeted arrests; controlled messaging; patrol liaison engaged.","red"),
  },


{key="MRM", name="Mystery of Red Mirth",
  ["Rex Catonis"] = {
link=orderLink("Silenus (Reformed Stripping Path)","Myster_of_Red_Mirth","Mystery of Red Mirth"),
    T("Civic order proclamation at court square; conduct rules reissued; complaint intake organized.","green"),
tasks={
    T("Docket triage for petty disputes; mediation attempted before formal hearing; outcomes logged.","green"),
T("Morale rite and sanctioned celebration oversight; prevent disorder while restoring joy.","green",{"morale","public","service"}),
    T("Steward training on lawful dispersal and calm messaging; escalation triggers clarified.","green"),
T("Festival logistics support; safe routes and water points verified with civic stewards.","green",{"festival","crowd","public"}),
    T("Municipal fines ledger audit: correct corrupt entries; restitution schedule drafted.","yellow"),
T("Sanctioned tavern-chaplain circuit; reduce brawls and theft through presence.","green",{"public","security","service"}),
    T("Vendor permit compliance sweep; counterfeit permits seized; corrective notices posted.","yellow"),
T("Reconciliation feast for rival crews; agreements sealed before witnesses.","green",{"mediation","festival","service"}),
    T("Rumor suppression after ‘illegal decrees’ claim; clarifying bulletin posted; speaker scripts updated.","yellow"),
T("Public song-rotation at market hours; discourage rumor panic with sanctioned refrains.","green",{"rumor","public","service"}),
    T("Escort for court clerks during sensitive hearing; intimidation deterrence; safe route posted.","yellow"),
T("Containment of illicit revel causing unrest; correction by sanctioned rite and fines.","yellow",{"illicit","order","security"}),
    T("Emergency order restoration after riot; curfew messaging + dispersal corridors; clinic access protected.","red"),
T("Crowd-density enforcement for a stadium day; steward posts assigned.","yellow",{"crowd","festival","security"}),
    T("Crisis protection of court archives after arson threat; perimeter posted; custody chain verified.","red"),
T("Emergency morale stabilization after violence; controlled gatherings and escorts.","red",{"violence","crowd","public"}),
    T("High-risk courthouse crowd control under red posture; separation corridors held; arrests minimized.","red"),
T("High-risk de-escalation during riot-prone festival; staged dispersal and peace hymns.","red",{"riot","crowd","security"}),
    -- +8 added
T("Post-panic comfort rites near transit hubs; prevent stampedes and retaliatory fights.","yellow",{"panic","rail","crowd"}),
    T("Permit renewal clinic; expired permits reissued; lawful signage posted; fine waivers granted for compliance.","green"),
}
    T("Public notice board audit; false postings removed; official stamps verified; clerk sign-off obtained.","green"),
},
    T("Restitution schedule review; payment plan adjusted; guarantors recorded; Temple Bank copy filed.","green"),
    T("Price gouging complaint sweep; violations documented; fines assessed; public deterrence notice posted.","yellow"),
    T("Curfew violation surge response; warning protocol standardized; repeat offenders flagged; incident log filed.","yellow"),
    T("Gang intimidation intake; victim protection options posted; suspect descriptions routed to Ironclad.","yellow"),
    T("Mass corruption scandal response: ledger seizure; official suspensions; prosecution packets assembled; public guidance issued.","red"),
    T("Coordinated violence response: unified command liaison; detention processing; court schedule triage; after-action report filed.","red"),
  },


{key="RCA", name="Rex Catonis",
  ["Sanctum Delphica"] = {
link=orderLink("Cato (Reformed Stripping Path)","Rex_Catonis","Rex Catonis"),
    T("Archive custody inspection: seals verified; storage conditions checked; humidity warnings posted.","green"),
tasks={
    T("Scribe training on custody chain; convoy paperwork rehearsed; handoff scripts practiced.","green"),
T("Civic order inspection: queue rules, market discipline, and shrine traffic flow.","green",{"order","queue","public"}),
    T("Pilgrim etiquette workshop at library shrine; quiet-zone enforced; lane discipline posted.","green"),
T("Courtroom courtesy briefing; witness etiquette and silence rules reinforced.","green",{"governance","public","service"}),
    T("Index confiscated pamphlets for signature motifs; correlation packet forwarded; source stalls logged.","yellow"),
T("Market weights-and-measures rite; deter fraud and stabilize pricing disputes.","green",{"audit","service","public"}),
    T("Witness interviews after shrine vandalism; sworn statements taken; intimidation complaints recorded.","yellow"),
T("Transit node discipline review; platform lines repainted and stewards assigned.","green",{"rail","order","transit"}),
    T("Doctrinal quarantine assistance: entry controls + rumor suppression; approved signage posted.","yellow"),
T("Curfew signage and quiet-hours enforcement brief; reduce night disturbances.","green",{"security","public","service"}),
    T("Court packet assembly for desecration suspects; evidence collated; custody memo sealed.","yellow"),
T("Administrative compliance review after complaints; corrective edicts posted.","yellow",{"compliance","governance","service"}),
    T("Night watch rotation at archive stacks following arson threat; entry controlled; escort-only rule applied.","red"),
T("Guard liaison for a threatened civic hall; protocol rehearsal and watch rotation.","yellow",{"security","governance","service"}),
    T("Escort sensitive artifact transfer; route denial markers deployed; decoy timing staged.","red"),
T("Emergency restoration of order after mob action; escorted dispersal and curfew-litany.","red",{"mob","crowd","security"}),
    T("Emergency custody lockdown after breach attempt; relocation plan activated; public narrative kept neutral.","red"),
T("High-risk support to authorities during barricade incident; perimeter discipline enforced.","red",{"barricade","security","public"}),
    -- +8 added
T("Port and rail dispute arbitration; enforce lawful priority lanes for relief cargo.","yellow",{"port","rail","order","relief"}),
    T("Forecast briefing for stewards: likely choke points listed; preventive signage posted; shift plan updated.","green"),
}
    T("Public ‘clarity notice’ explaining rumor patterns; discourage prophecy claims; practical steps appended.","green"),
},
    T("Queue triage workshop for clerks; intake categories standardized; escalation triggers defined.","green"),
    T("Forgery detection surge response; verification booth staged; suspect stamps cataloged; seizures logged.","yellow"),
    T("High-traffic archive day capacity rules enforced; entry tokens issued; silent zone posted; security liaison set.","yellow"),
    T("False omen bulletin cycle after sky-sign rumors; broadcasts timed; hoarding deterrence included.","yellow"),
    T("Archive fire incident response: document evacuation; custody chain preserved; public messaging controlled; arrests coordinated.","red"),
    T("Mass panic after forged warning decree: authoritative retraction posted; printer network pursued; calm stations opened.","red"),
  },


{key="SDL", name="Sanctum Delphica",
  ["Ordo Amicitia"] = {
link=orderLink("Sisera (Reformed Stripping Path)","Sanctum_Delphica","Sanctum Delphica"),
    T("Kindness distribution at shrine steps; ledger reconciled; needs list updated for tomorrow.","green"),
tasks={
    T("Family mediation after dispute; amity pledges collected; follow-up visits scheduled.","green"),
T("Oracle consultation clinic for officials; questions logged and sealed.","green",{"oracle","governance","service"}),
    T("Volunteer training: calm communication and nonviolent intervention; escalation scripts practiced.","green"),
T("Archive and library integrity check; catalog drift corrected and access verified.","green",{"archive","audit","service"}),
    T("Clinic-queue kindness patrol; conflict prevention and assistance to elderly; complaints logged.","yellow"),
T("Public instruction on lawful prophecy vs. pamphlet fraud; leaflets distributed.","green",{"education","public","service"}),
    T("Rumor containment after defamatory pamphlets; corrective bulletin cycle launched; source stalls mapped.","yellow"),
T("Scribe training session; improve recordkeeping for relief and stipend flows.","green",{"education","audit","service"}),
    T("Audit of relief supplies to prevent theft; custody seals verified; storage rules posted.","yellow"),
T("Observatory-liaison briefing with Conclave Illuminara; shared bulletins standardized.","green",{"service","archive","public"}),
    T("Witness support for targeted volunteer; safe route arranged; intimidation complaints recorded.","yellow"),
T("Investigation of forged prophecy pamphlets; counterfeit sources traced.","yellow",{"forgery","investigation","service"}),
    T("Emergency care stations during unrest; neutral aid posture announced; clinic corridor protected.","red"),
T("Archive sealing of a sensitive docket after unrest; access reduced and witnessed.","yellow",{"security","archive","service"}),
    T("Crisis grief counseling after fatal incident; aid referrals coordinated; retaliation pledges collected.","red"),
T("Emergency counter-homily after loudspeaker seizure; sanctioned address delivered.","red",{"broadcast","crisis","public"}),
    T("High-risk shelter stabilization under red posture; intake controlled; reunification tent activated.","red"),
T("High-risk protection for a key temple scholar; escort and secure lodging arranged.","red",{"threat","security","service"}),
    -- +8 added
T("Transit bulletin for corridor cities after omen panic; prevent stampedes and conspiracies.","yellow",{"rail","panic","public"}),
    T("Elder check-in circuit; medicine delivery; home repair requests logged; follow-up visits scheduled.","green"),
}
    T("School buddy program setup; mentors paired; attendance tracked; guardian contacts logged.","green"),
},
    T("Micro-grant assistance for small vendors; eligibility explained; Temple Bank receipts issued.","green"),
    T("Aid line surge plan; entry metering; high-need triage; conflict scripts deployed.","yellow"),
    T("Fraudulent charity collectors sweep; donations recovered; offenders documented; public warning posted.","yellow"),
    T("Harassment at aid stations response; escorts assigned; offenders barred; reports sealed.","yellow"),
    T("Disaster relief activation: shelter staffing; supply staging; daily reporting; rumor suppression enforced.","red"),
    T("Mass displacement response: registration booth; family reunification; trauma referrals; records sealed.","red"),
  },


{key="OAI", name="Ordo Amicitia",
  ["Temple Illuminata"] = {
link=orderLink("Erasmus (Reformed Stripping Path)","Ordo_Amicitia","Ordo Amicitia"),
    T("Illumination vigil: lantern discipline and public calm guidance posted; safe routes marked.","green"),
tasks={
    T("Counseling for anxious households; referral paths established; follow-up log kept.","green"),
T("Charity distribution and household check-ins; kindness register updated for stipend support.","green",{"charity","relief","service"}),
    T("Archive testimonies and signs; witness statements catalogued; custody rules posted.","green"),
T("Food-line volunteer training; prevent cutting and fights through posted rules.","green",{"relief","queue","public"}),
    T("Night watch of sacred precinct; entry control enforced; conduct rules posted at gates.","yellow"),
T("Shelter visitation for displaced workers; job referrals routed through civic offices.","green",{"service","relief","governance"}),
    T("Omen bulletin: correct false ‘dark star’ claim; discourage panic gatherings; scripts distributed.","yellow"),
T("Agri stipend review for smallholders; vouchers revalidated and extensions logged.","green",{"agri","audit","service"}),
    T("Audit unsanctioned night gatherings; compliance restrictions issued; repeat venues logged.","yellow"),
T("Compassion circuit to hospitals and clinics; morale support and supply notes recorded.","green",{"health","service","relief"}),
    T("Post-incident narrative discipline; rumor sources mapped; correction bulletins deployed.","yellow"),
T("Offering logistics for a major rite; livestock and treasure accounted and sealed.","yellow",{"offering","audit","service"}),
    T("Emergency messaging after ‘demonic symbol’ report; scripts distributed; calm stations opened.","red"),
T("Mediation for neighborhood poverty disputes; aid allocations made transparent.","yellow",{"aid","mediation","service"}),
    T("Crisis crowd dispersal after night surge; guidance routes opened; reunification tent active.","red"),
T("Emergency relief surge after incident; soup lines sanctified and guarded.","red",{"relief","crowd","public"}),
    T("High-risk escort for sensitive nocturnal rite during unrest; perimeter observers posted; decoy route used.","red"),
T("High-risk protection for a vulnerable procession route; escorts and safe shelters staged.","red",{"procession","security","public"}),
    -- +8 added
T("Port-side charity intake after a ferry accident; registries updated and families notified.","yellow",{"port","relief","service"}),
    T("Public symposium moderation; debate rules posted; minutes archived; speaker list verified.","green"),
}
    T("Clarity counseling for civic leaders; action plan sealed; follow-up check scheduled.","green"),
},
    T("Apprentice study circle; reading list issued; mentor pairing logged; conduct rules posted.","green"),
    T("Heckler disruption response at lecture; removals enforced; incident log filed; next session relocated.","yellow"),
    T("Forged ‘Temple order’ correction; official stamps verified; printer path traced; retraction posted.","yellow"),
    T("Threat triage against philosophers/teachers; security escort arranged; venue access restricted.","yellow"),
    T("Attack on lecture venue response: injuries triaged; perpetrators prosecuted; continuity plan issued; public calm statement posted.","red"),
    T("Citywide incitement campaign response: counter-messaging; arrests; curfew guidance; safe corridors protected.","red"),
  },


{key="TIL", name="Temple Illuminata",
  ["Temple Aprobelle"] = {
link=orderLink("Nikolaj (Reformed Stripping Path)","Temple_Illuminata","Temple Illuminata"),
    T("Shrine-leader briefing: weekly rumor-control cycle; clarifications posted; complaint intake organized.","green"),
tasks={
    T("Grief-cadre home visits after accident; de-escalation coaching delivered; referrals filed.","green"),
T("Lamp and beacon maintenance blessing; public wayfinding clarity restored.","green",{"beacon","transit","service"}),
    T("School immunization coordination: schedule posted; consent ledger reconciled; follow-up reminders issued.","green"),
T("Infrastructure illumination audit for tunnels and transit nodes; repair notices issued.","green",{"infrastructure","transit","industry"}),
    T("Clinic-queue stabilization drill: entry metering tested; conflict prevention scripts practiced.","yellow"),
T("Rail signal blessing at key junctions; reduce accidents and rumor panic.","green",{"rail","transit","public"}),
    T("Behavior audit of district office after complaint surge; corrective training issued; sign-off obtained.","yellow"),
T("Workshop visit: lantern production quality review; defective stock quarantined.","green",{"industry","audit","service"}),
    T("Pattern-analysis intake: schism texts logged; signature correlation packet forwarded; printer stalls mapped.","yellow"),
T("Public lantern-route map refreshed for festival season; prevent crush points.","green",{"festival","crowd","public"}),
    T("Anomaly-response dispatch after contamination claims: care paired with narrative discipline; panic triggers logged.","yellow"),
T("Investigation of signal tampering near a transport hub; seals replaced and watchers posted.","yellow",{"tamper","security","transit"}),
    T("Silencing-cadre activated after panic flare; controlled messaging + care stations deployed; retaliation pledges collected.","red"),
T("Emergency lighting deployment after sabotage; temporary beacons installed with escorts.","red",{"sabotage","crisis","security"}),
    T("Emergency stabilization in evacuation/fever center: calming rites + rumor suppression; clinic corridor protected.","red"),
T("High-risk stabilization at a transport hub after panic; controlled flow and sanctuary lanes.","red",{"panic","crowd","transit"}),
    T("High-risk psychological stabilization after mass scare: rumor nodes neutralized; supervised dispersal enforced; evidence chain sealed.","red"),
T("Port navigation-light inspection for fog days; harbor masters briefed.","yellow",{"port","infrastructure","service"}),
    -- +8 added
T("Tunnel crowd-discipline drill; stewards assigned and entry metering practiced.","yellow",{"crowd","infrastructure","transit"}),
    T("Speaker coaching for ward announcements; panic triggers removed; approved phrasing distributed.","green"),
}
    T("Trust survey intake at market edges; key grievances logged; referrals routed to Harmony Sanctum.","green"),
},
    T("School liaison check: bullying/harassment trend review; intervention plan filed; follow-up scheduled.","green"),
    T("Targeted pamphlet purge after printer ID; removal teams assigned; replacement postings issued; suspects logged.","yellow"),
    T("Crowd agitation forecast; patrol liaison; calm broadcast cadence set; escalation thresholds defined.","yellow"),
    T("Workplace intimidation response; protective escort plan; offender sanctions recommended; compliance checks scheduled.","yellow"),
    T("Mass panic containment: calm centers expanded; hourly briefings; agitator roundup coordinated; retaliatory impulses reduced.","red"),
    T("Post-attack trauma response: debrief circles; retaliation bans; witness relocation; compliance sweep scheduled.","red"),
  },
}
}


    --------------------------------------------------------------------------------
    -- 12.1) NOCTIC VAMPIRISM / NOCTIC-RABREV TASK INSERTS
    --
    -- These tasks add Noctic Vampirism and raw Noctic-Rabrev events to the cults
    -- most likely to intervene: dream, health, purification, wetland, funerary,
    -- hidden-knowledge, legal, charity, and counter-fraud orders.
    --------------------------------------------------------------------------------
    local function addCultTasks(cultName, list)
        tasksByCult[cultName] = tasksByCult[cultName] or {}
        for _, t in ipairs(list) do
            table.insert(tasksByCult[cultName], t)
        end
    end
    addCultTasks("Reverie Nebulous", {
        T("[[Noctic Vampirism]] dream-entanglement intake: affected households given sleep-boundary cards; severe nightmare cases referred to [[Temple_Aprobelle|Temple Aprobelle]].","green"),
        T("Noctic dream-circle for registered patients; blood-hunger language redirected into supervised journaling; family witnesses briefed.","green"),
        T("Nightmare cluster triage after suspected raw [[Noctic-Rabrev]] smoke exposure; trigger map compiled; clinic referrals issued.","yellow"),
        T("Shared-vision complaint linked to Noctic leaf use; dream narratives separated from rumor claims; speaker scripts distributed.","yellow"),
        T("Noctic trance-gathering compliance review; unsanctioned sleep rites suspended; repeat venues logged for Temple Bank follow-up.","yellow"),
        T("Crisis rest-ward activation after mass Noctic panic dream; supervised sleep zones opened; clinic corridor protected.","red"),
        T("Violent blood-hunger dream episode contained; families separated safely; witness shielding and trauma referrals arranged.","red"),
        T("High-risk dream-breach report involving suspected nocticine-Δ exposure; Aprobelle liaisons activated; public narrative held neutral.","red"),
    })
    addCultTasks("Sanctum Vitalis", {
        T("[[Noctic Vampirism]] clinic screen: photophobia, anemia, fever, lesions, and blood-craving symptoms logged; dietary guidance issued.","green"),
        T("Registered Noctic patient follow-up: milk, iron food, and blood-substitute stabilization plan checked; family care note filed.","green"),
        T("Raw [[Noctic-Rabrev]] exposure triage at wetland clinic; symptomatic patients separated from ordinary fever queue.","yellow"),
        T("Congenital Noctic case review; guardian consent ledger reconciled; lifelong monitoring schedule updated.","yellow"),
        T("Market illness cluster assessed for raw-leaf adulteration; sampling escorted; vendor recall notice prepared.","yellow"),
        T("Emergency hemorrhagic decline response in serious Noctic case; triage expansion, family shielding, and mortality ledger precautions enacted.","red"),
        T("Necrotic lesion cluster after unauthorized induction rite; quarantine boundary marked; cult-health evaluation ordered.","red"),
        T("High-risk fever center support after Noctic outbreak rumor; panic narratives suppressed while medical intake remains open.","red"),
    })
    addCultTasks("Temple Alabaster", {
        T("Refined nocticine tincture custody check; dosage ledger reconciled; [[Temple Bank of the Reformed Stripping Path|Temple Bank]] seal verified.","green"),
        T("Post-Eidolan recovery clinic review using licensed [[Noctic-Rabrev]] concentrate; patient response notes sealed under medical custody.","green"),
        T("Purity inspection of Noctic balms and tinctures; raw-leaf contamination warnings posted; suspect jars removed.","yellow"),
        T("Therapeutic nocticine distribution audit; storage seals verified; unauthorized substitutions routed to Golden Shadows.","yellow"),
        T("Contaminated Noctic medicine recall; affected households contacted; clinic-stabilization instructions posted.","yellow"),
        T("Emergency support for serious Noctic Vampirism collapse; calm ward opened; hemorrhage supplies and spiritual grounding teams staged.","red"),
        T("Crisis purification rite after counterfeit nocticine batch harms pilgrims; evidence preserved; public reassurance issued.","red"),
        T("High-risk medical custody transfer for unstable Noctic patient; decoy route used; receiving clinic countersigns sealed packet.","red"),
    })
    addCultTasks("Mystery of the Verdant Embrace", {
        T("[[Noctic-Rabrev]] field-blessing with safety lecture; flower and raw-leaf handling rules reissued to cultivators.","green"),
        T("Blood Orchard footpath inspection; leaf-storage huts checked; runoff risks and wetland access hazards logged.","green"),
        T("Noctic crop custody audit after missing leaf bundles; field stewards interviewed; harvest tally reconciled.","yellow"),
        T("Wetland illness cluster mapped near Noctic fields; raw-leaf exposure questions added to intake; clinic referrals issued.","yellow"),
        T("Flood-damaged Noctic storage shed inspected; wet leaves quarantined; safe flower product separated from restricted material.","yellow"),
        T("Emergency Blood Orchard closure after unauthorized raw-leaf distribution; field gates sealed; cultivator list turned over to Temple Bank.","red"),
        T("Poisoning event tied to unprocessed Noctic leaves; antidote routing, source trace, and water access controls initiated.","red"),
        T("High-risk wetland outbreak response: care kits staged, Noctic custody chain rebuilt, misinformation suppressed in shrine districts.","red"),
    })
    addCultTasks("Mystery of the Stygian Veil", {
        T("Funerary guidance for household of controlled Noctic patient; blood-offering limits explained; mourning schedule recorded.","green"),
        T("Noctic transformation counseling at cemetery gate; symbolic death language separated from unsafe raw-leaf use.","green"),
        T("Terminal Noctic vigil supervision; family witnesses logged; raw-leaf access barred during mourning rites.","yellow"),
        T("Blood-rite compliance check after ancestral-contact claims; permitted substitutes verified; coercion complaints recorded.","yellow"),
        T("Rumor suppression after ‘Noctic dead returning’ claim; evidence bulletin posted; graveyard panic triggers tracked.","yellow"),
        T("Crisis response after terminal Noctic transformation death; grief triage and crowd discipline established before nightfall.","red"),
        T("Emergency calm stations at tomb precinct after blood-hunger panic; supervised dispersal and clinic access protected.","red"),
        T("High-risk memorial for Noctic fatalities; retaliation prevention enforced; family shielding and witness support arranged.","red"),
    })
    addCultTasks("Order of the Umbral Oracle", {
        T("Noctic night-watch at shuttered shrine; sleepwalk reports logged; entry rules posted for anxious households.","green"),
        T("Covered-mirror guidance for suspected Noctic cases; families advised on safe vigils, naming restraint, and witness logs.","green"),
        T("Skødan-style mirror scare assessed after Noctic fever report; false-revelation claims documented without public amplification.","yellow"),
        T("Starless vigil compliance review; raw [[Noctic-Rabrev]] use questioned; repeat venues logged for Temple Bank follow-up.","yellow"),
        T("Dream-mapping dispute involving Noctic patient; testimony sealed; dangerous prophecy language corrected.","yellow"),
        T("Crisis nocturnal dispersal after mirror-haunting rumor; safe corridors opened; panic narratives suppressed.","red"),
        T("Serious Noctic fever episode during covered-mirror rite; patient removed safely; household witnesses debriefed.","red"),
        T("High-risk hidden-name panic in [[List of cities in Bassaridia Vaeringheim#Skøda|Skøda]]; Aprobelle and Sanctum Vitalis liaisons activated under seal.","red"),
    })
    addCultTasks("Conclave Illuminara", {
        T("Tarsican phase briefing for Noctic stewards; raw-leaf rites rescheduled away from high-anxiety night windows.","green"),
        T("Lunar calendar note issued for registered Noctic patients; dream disturbances and light-sensitivity triggers explained.","green"),
        T("Nocticine-Δ rumor correction after celestial alignment claims; hoarding and unsafe induction warnings posted.","yellow"),
        T("Moon-token and Noctic permit verification at night vigil; counterfeit ritual permissions seized.","yellow"),
        T("Controlled interpretation session after Noctic dream surge; public cycle-framing delivered to reduce panic.","yellow"),
        T("Emergency anti-panic bulletin after eclipse-linked Noctic outbreak claim; calm stations opened; clinic routes protected.","red"),
        T("High-risk nocturnal rite escort for registered Noctic patients; perimeter observers posted; false prophecy claims suppressed.","red"),
        T("Crisis interpretive response in Skødan mirror-vigil district; Tarsican language used to separate revelation from deception.","red"),
    })
    addCultTasks("Guild of Golden Shadows", {
        T("Quiet audit of [[Noctic-Rabrev]] market stalls; forged raw-leaf permissions logged; suspect brokers mapped.","green"),
        T("Noctic Fleet manifest cross-check; flower extract, tincture, and raw-leaf crates reconciled against Temple Bank seals.","green"),
        T("Undercover purchase of counterfeit Noctic induction permit; printer path traced; custody memo prepared.","yellow"),
        T("Raw-leaf diversion inquiry after Blood Orchard discrepancy; brokers interviewed; sealed attribution packet filed.","yellow"),
        T("Counterfeit nocticine tincture sweep; suspect vials seized; affected vendors routed to Temple Alabaster inspection.","yellow"),
        T("Night extraction of threatened Noctic witness; safe route screened; temporary housing arranged under seal.","red"),
        T("Major Noctic smuggling ring disruption: ledger seizure, broker detention support, and public market guidance coordinated.","red"),
        T("High-risk port-gate fraud surge involving raw Noctic leaf crates; inspection teams reinforced; evidence chain preserved.","red"),
    })
    addCultTasks("Temple Aprobelle", {
        T("[[Noctic Vampirism]] rumor-control briefing: discourage vigilante panic; explain registration, clinic access, and family support routes.","green"),
        T("Household anxiety visits after Noctic diagnosis; de-escalation coaching delivered; ostracism risks logged.","green"),
        T("Pattern-analysis intake for Noctic panic stories; dream claims, blood-hunger rumors, and raw-leaf reports separated.","yellow"),
        T("Noctic outbreak messaging cycle: official symptoms listed; false contagion claims corrected; clinic queues stabilized.","yellow"),
        T("Behavior audit after harassment of registered Noctic patients; corrective training issued; offenders warned.","yellow"),
        T("Silencing-cadre activated after Noctic panic flare; controlled messaging and care stations deployed before night crowds form.","red"),
        T("Emergency psychological stabilization after serious Noctic blood-hunger incident; retaliation bans and witness relocation arranged.","red"),
        T("High-risk mass scare after alleged Noctic attack; rumor nodes neutralized; supervised dispersal enforced; evidence chain sealed.","red"),
    })
    addCultTasks("Harmony Sanctum", {
        T("Family mediation after Noctic diagnosis; care duties, privacy expectations, and visitor rules written into household concord.","green"),
        T("Neighborhood explanation circle for registered Noctic household; stigma reduced; clinic and Temple Bank contacts posted.","green"),
        T("Market-lane calming after Noctic rumor; queue discipline restored; harassment complaints logged.","yellow"),
        T("Inter-family dispute over raw-leaf inheritance; compromise storage terms drafted; Temple Bank audit requested.","yellow"),
        T("Noctic clinic crowd-lane plan reviewed; patient privacy route and public waiting area separated.","yellow"),
        T("Emergency anti-reprisal pact after blood-hunger scare; escort corridors established; no-retaliation oaths collected.","red"),
        T("Mass grievance intake after Noctic outbreak panic; testimony queue metered; witness shielding arranged; outcomes logged.","red"),
        T("Crisis reconciliation hearing after violence against Noctic patient; restitution ledger initiated; family protection arranged.","red"),
    })
    addCultTasks("Azure Sentinel Sect", {
        T("Canal escort for licensed Noctic medical supplies; handoff signatures collected; chain-of-custody verified.","green"),
        T("Waterfront Noctic storage inspection after rain; flood exposure risks logged; damaged crates elevated.","green"),
        T("Storm-drain and wet-market sweep near Noctic vendors; runoff contamination warnings posted.","yellow"),
        T("Ferry-ramp crowd control during Noctic clinic transfer; privacy corridor and rescue lane maintained.","yellow"),
        T("Suspicious skiff check involving raw Noctic leaf bundles; manifests reviewed; contraband seized under seal.","yellow"),
        T("Emergency flood response at Noctic-Rabrev storehouse; restricted leaf material secured before dispersal through canals.","red"),
        T("Port lockdown support after Noctic smuggling violence; civilian zones cleared; clinic route protected.","red"),
        T("Mass casualty waterfront incident involving contaminated Noctic crates; triage staging and perimeter discipline enforced.","red"),
    })
    addCultTasks("Rex Catonis", {
        T("Noctic registration clinic: voluntary induction rules explained; lawful forms checked; appeal route posted.","green"),
        T("Public notice board update on raw [[Noctic-Rabrev]] restrictions; false decrees removed; clerk sign-off obtained.","green"),
        T("Hearing docket triage for unauthorized Noctic induction complaint; mediation attempted where lawful; evidence preserved.","yellow"),
        T("Raw-leaf permit compliance sweep; expired permissions suspended; corrective notices posted to shrine gates.","yellow"),
        T("Coerced induction allegation reviewed; victim protection options posted; suspect descriptions routed to Ironclad.","yellow"),
        T("Mass legal response after Noctic outbreak: unlawful vendors processed, custody ledgers seized, and public guidance issued.","red"),
        T("Coordinated violence response after Noctic panic; detention processing, court schedule triage, and after-action report filed.","red"),
        T("Emergency decree against unsafe raw-leaf distribution read publicly; clinic corridors protected during enforcement.","red"),
    })
    addCultTasks("Sanctum Delphica", {
        T("Noctic case archive update: diagnosis records, raw-leaf permissions, and family witness notes catalogued.","green"),
        T("Scribe training on Noctic custody language; distinguish raw leaf, flower extract, tincture, and nocticine concentrate.","green"),
        T("Doctrinal quarantine assistance after Noctic rumor; entry controls and approved signage posted.","yellow"),
        T("Forgery detection sweep for Noctic induction papers; suspect stamps catalogued; printer path traced.","yellow"),
        T("Witness packet assembled for raw-leaf desecration or coercive induction case; evidence collated and sealed.","yellow"),
        T("Emergency archive lockdown after Noctic registry breach; relocation plan activated; public narrative kept neutral.","red"),
        T("Mass panic after forged Noctic warning decree; authoritative retraction posted; printer network pursued; calm stations opened.","red"),
        T("High-risk transfer of Noctic medical ledgers; route denial markers deployed; decoy timing staged.","red"),
    })
    addCultTasks("Ordo Amicitia", {
        T("Milk and iron-food distribution for Noctic households; needs list updated; stigma-free delivery route assigned.","green"),
        T("Family support visits for congenital Noctic cases; caregiver relief scheduled; privacy rules posted.","green"),
        T("Aid line surge plan after Noctic diagnosis cluster; high-need triage and conflict scripts deployed.","yellow"),
        T("Fraudulent charity collectors using Noctic outbreak claims exposed; donations recovered; public warning posted.","yellow"),
        T("Shelter intake support for families displaced by Noctic panic; family reunification and trauma referrals recorded.","yellow"),
        T("Disaster relief activation after Noctic outbreak in wetland district; supply staging and rumor suppression enforced.","red"),
        T("Mass displacement response after raw-leaf scare; registration booth opened; care packages and witness shielding arranged.","red"),
        T("High-risk relief escort for registered Noctic patients leaving hostile neighborhood; safe housing arranged under seal.","red"),
    })
local function shouldAZSGoCovert(nextInt, sev, threat)
-- Only under red posture and with a real threat record present
if sev ~= "red" or not threat then return false end
local u = urgencyScore(threat.urg)
if u < 3 then return false end -- HIGH/CRITICAL only
-- CRITICAL always qualifies; otherwise look for strong violence/sabotage signals
local ev = string.lower(tostring(threat.event or ""))
local signals =
ev:match("sabot") or ev:match("arson") or ev:match("bomb") or ev:match("terror") or
ev:match("assassin") or ev:match("hit") or ev:match("murder") or
ev:match("insurgent") or ev:match("pirate") or ev:match("rogue")
local qualifies = (u >= 4) or signals
if not qualifies then return false end


local orderByKey, orderByName, orderKeys, orderNames = {}, {}, {}, {}
-- “Sometimes”: deterministic chance, higher for CRITICAL
for _, o in ipairs(orders) do
local chance = (u >= 4) and 25 or 15  -- percent
orderByKey[o.key] = o
return (nextInt() % 100) < chance
orderByName[o.name] = o
orderKeys[#orderKeys+1] = o.key
orderNames[#orderNames+1] = o.name
end
end


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- 7.1) ORDER ICONS (from Module:GeneralPortOperations addLogo mapping)
-- 13) CITY PROFILES (bias order selection)
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
local orderIconFile = {
local cityProfiles = {
["Order of Aurora Mystica"]         = "Auroramystica.png",
["Vaeringheim"]   = {weights={AUM=3,HSA=2,AZS=2,GGS=2,SVI=2,SDE=2,TAP=3}},
["Harmony Sanctum"]               = "Harmonysanctum.png",
["Somniumpolis"]  = {weights={RNB=3,SVI=2,GGS=2,TAP=3,AZS=1}},
["Ignis Aeternum"]                 = "Ignis Aeternum.png",
["Luminaria"]     = {weights={AUM=3,CON=2,HSA=2,TAP=2,SVI=1}},
["Celestial Harmony Sect"]         = "HarmonySect.png",
["Lunalis Sancta"] = {weights={SDE=3,CON=2,TAP=2,HSA=1}},
["Guild of Golden Shadows"]        = "GoldenShadows.png",
["Delphica"]       = {weights={SDE=3,TAP=2,CON=1,SVI=1}},
["Azure Sentinel Sect"]           = "AzureSentinel.png",
["Thermosalem"]   = {weights={SVI=2,TAP=2,AZS=2,HSA=1}},
["Reverie Nebulous"]               = "RevNeb.png",
["Pyralis"]        = {weights={IGA=2,SVI=2,TAP=2,AZS=2,AUM=1}},
["Eon Fellowship"]                 = "EonFel.png",
["Keybir-Aviv"]    = {weights={AZS=3,TAP=2,GGS=2,HSA=1}},
["Order of the Umbral Oracle"]     = "Umbral.png",
["Diamandis"]     = {weights={TAP=2,SVI=2,AUM=1,RNB=1,MIR=1}},
["Mystery of the Verdant Embrace"] = "Verdant.png",
-- Haifan Bassaridia / Southern Strait profiles
["Conclave Illuminara"]           = "Conclave.png",
-- These make Southern Strait-relevant cults more likely when the city row
["Sanctum Vitalis"]               = "Vitalis.png",
-- is one of the Haifan Bassaridian ports or inland-linked market cities.
["Temple Alabaster"]               = "Alabaster.png",
["Keybir-Aviv"] = {weights={AZS=4,GGS=3,TAP=3,HSA=2,SDE=2,SVI=1}},
["Court of the Ironclad"]          = "Ironclad.png",
["Tel-Amin"] = {weights={GGS=4,AZS=3,VDE=3,HSA=2,TAP=2,SVI=1}},
["Accord Concordia"]               = "Concordia.png",
["Diamandis"] = {weights={TAP=3,SVI=3,AUM=2,RNB=2,MIR=2,STY=1}},
["Mystery of the Stygian Veil"]   = "StygianVe.png",
["Jogi"] = {weights={GGS=4,TAP=3,AZS=3,MIR=2,SVI=2,SDE=1}},
["Sylvan Fellowship"]             = "Sylvan.png",
["Lewisburg"] = {weights={HSA=3,GGS=3,TAP=2,SDE=2,AZS=2,ACC=1}},
["Mystery of Red Mirth"]           = "MirthFlag.png",
["Thermosalem"] = {weights={SVI=4,TAP=3,AZS=2,HSA=2,ALB=2,CAT=1}},
["Rex Catonis"]                   = "Catonis.png",
["Akróstadium"] = {weights={HSA=3,TAP=3,MIR=3,GGS=2,AZS=1,CICL=1}},
["Sanctum Delphica"]               = "SanctumDel.png",
["Sufriya"] = {weights={VDE=3,AZS=3,SVI=2,TAP=2,GGS=2,ALB=1}},
["Ordo Amicitia"]                 = "OrdoFl.png",
["Lykopolis"] = {weights={SDE=3,TAP=3,GGS=2,HSA=2,AZS=2,CAT=1}},
["Temple Illuminata"]             = "AureumFlag.png",
["Tonar"]         = {weights={SDE=2,SVI=2,VDE=2,TAP=1,CICL=1}},
["Bashkim"]       = {weights={VDE=3,SVI=2,TAP=1}},
["Ourid"]          = {weights={VDE=3,SVI=2,TAP=1}},
["Ardclach"]       = {weights={AZS=2,SVI=2,TAP=1,CON=1}},
 
-- Alperkin necromancy / ancestor-rite hotspots
-- These weights do not make ordinary ancestor veneration illicit.
-- They only make the relevant investigative, funerary, archival,
-- psychological-stability, purification, and health orders more likely
-- in cities where forbidden necromancy or corrupted ancestor rites may appear.
["The Alpazkigz"] = {weights={STY=5, UMB=5, SDE=3, TAP=3, SVI=2, ALB=2, VDE=2, RNB=1}},
["Odiferia"]    = {weights={UMB=6, STY=5, SDE=4, TAP=4, SVI=3, ALB=3, VDE=3, GGS=2}},
["Erythros"]     = {weights={STY=5, UMB=4, CON=3, SDE=3, TAP=3, RNB=2}},
["Nexa"]         = {weights={SDE=6, UMB=4, STY=3, TAP=3, ILL=2, EOF=2}},
["Pyralis"]     = {weights={STY=5, UMB=4, SDE=3, IGA=3, TAP=3, SVI=2, ALB=2}},
["Acheron"]     = {weights={STY=6, UMB=5, SDE=3, TAP=3, SVI=3, ALB=2, VDE=2}},
["Aureum"]       = {weights={GGS=4, SDE=4, UMB=4, STY=3, TAP=3, ALB=3, SVI=2, ILL=2}},
["Somniumpolis"] = {weights={RNB=5, UMB=4, STY=4, TAP=4, SDE=3, SVI=2, ALB=2}},
}
}


local function getOrderIconMarkup(orderName)
local function getProfile(cityName)
local fn = orderIconFile[orderName]
return cityProfiles[cityName] or {
if fn and fileExists(fn) then
weights={AUM=1,HSA=1,IGA=1,CHS=1,GGS=1,AZS=1,RNB=1,EOF=1,UMB=1,VDE=1,CON=1,SVI=1,ALB=1,CICL=1,ACC=1,STY=1,SYL=1,MIR=1,CAT=1,SDE=1,AMI=1,ILL=1,TAP=1}
return string.format("[[File:%s|20px]] ", fn)
}
end
return ""
end
end


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- 8) CITY PROFILES (complete)
-- 14) ORDERS (missionary-capable cult list)
-- NOTE: These names must match GeneralPortOperations ritualCults list + Temple Aprobelle.
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
local cityProfiles = {
local orders = {
-- =========================
{key="AUM", name="Order of Aurora Mystica"},
-- CORE MAJORS
{key="HSA", name="Harmony Sanctum"},
-- =========================
{key="IGA", name="Ignis Aeternum"},
["Vaeringheim"] = {
{key="CHS", name="Celestial Harmony Sect"},
tags={"capital","admin","port","templebank","canals"},
{key="GGS", name="Guild of Golden Shadows"},
weights={GGS=4,HSA=3,RCA=3,ASS=2,CIL=2,CHS=2,OSI=1},
{key="AZS", name="Azure Sentinel Sect"},
},
{key="RNB", name="Reverie Nebulous"},
["Luminaria"] = {
{key="EOF", name="Eon Fellowship"},
tags={"industry","rollingstock","innovation","dawn","infrastructure"},
{key="UMB", name="Order of the Umbral Oracle"},
weights={TIL=4,OAM=3,EOF=2,IGA=2,OSI=1,GGS=1},
{key="VDE", name="Mystery of the Verdant Embrace"},
},
{key="CON", name="Conclave Illuminara"},
["Serena"] = {
{key="SVI", name="Sanctum Vitalis"},
tags={"civic","lake","hospitality","public"},
{key="ALB", name="Temple Alabaster"},
weights={HSA=3,ACC=2,OAI=2,ASS=1,CHS=1,OSI=1},
{key="CICL", name="Court of the Ironclad"},
},
{key="ACC", name="Accord Concordia"},
["Pyralis"] = {
{key="STY", name="Mystery of the Stygian Veil"},
tags={"volcanic","hazard","fire","reconstruction"},
{key="SYL", name="Sylvan Fellowship"},
weights={IGA=4,ASS=2,SCV=2,EOF=1,CIC=1},
{key="MIR", name="Mystery of Red Mirth"},
baseSev="yellow",
{key="CAT", name="Rex Catonis"},
},
{key="SDE", name="Sanctum Delphica"},
["Symphonara"] = {
{key="AMI", name="Ordo Amicitia"},
tags={"civic","arts","festival","public"},
{key="ILL", name="Temple Illuminata"},
weights={MRM=3,ACC=2,OSI=2,OAM=2,HSA=1,RCA=1},
{key="TAP", name="Temple Aprobelle"},
},
}
["Aurelia"] = {
 
tags={"civic","trade","public"},
local orderByName = {}
weights={HSA=2,CHS=2,GGS=2,ACC=1,OSI=1},
for _, o in ipairs(orders) do orderByName[o.name] = o end
},
 
["Somniumpolis"] = {
--------------------------------------------------------------------------------
tags={"finance","dream","rumorcontrol","noctic"},
-- 15) SEVERITY + ORDER/TASK CHOICE
weights={REN=3,GGS=3,HSA=2,UOR=2,CIL=2,RCA=1},
--------------------------------------------------------------------------------
baseSev="yellow",
    --------------------------------------------------------------------------------
},
    -- 14.1) NOCTIC CITY / CULT WEIGHTING
["Nexa"] = {
    --
tags={"market","trade","logistics"},
    -- Noctic Vampirism is more likely to appear where Noctic-Rabrev, Alperkin
weights={GGS=3,HSA=2,CHS=2,RCA=1,ASS=1,ACC=1},
    -- inheritance, dream rites, raw-leaf custody, or Skødan mirror-vigil customs
},
    -- are already locally important.
["Lunalis Sancta"] = {
    --------------------------------------------------------------------------------
tags={"lunar","archives","pilgrimage","omens"},
 
weights={CIL=4,REN=2,UOR=2,SDL=1,HSA=1},
    local nocticCityRisk = {
},
        ["Somniumpolis"]    = 8,
["Sylvapolis"] = {
        ["Odiferia"]        = 8,
tags={"forest","garden","ecology","shrinework"},
        ["Erythros"]        = 7,
weights={SYF=3,MVE=3,OAI=1,HSA=1,ASS=1},
        ["Acheron"]        = 6,
},
        ["Norsolyra"]      = 6,
        ["Skøda"]          = 6,
        ["The Alpazkigz"]    = 6,
        ["Nexa"]           = 4,
        ["Pyralis"]        = 4,
        ["Vaeringheim"]    = 3,
        ["Jogi"]            = 3,
        ["Aurelia"]        = 3,
    }
 
    local nocticOrderBoost = {
        RNB = 4, -- Reverie Nebulous: dream entanglement, nightmares, trance
        SVI = 4, -- Sanctum Vitalis: clinic response, anemia, lesions, fever
        ALB = 3, -- Temple Alabaster: nocticine medicine and purity controls
        VDE = 4, -- Verdant Embrace: Noctic-Rabrev fields, wetlands, plant custody
        STY = 3, -- Stygian Veil: funerary rites, terminal transformation
        UMB = 3, -- Umbral Oracle: night rites, shadow, hidden revelation
        CON = 3, -- Conclave Illuminara: Tarsica, lunar timing, nocticines
        GGS = 3, -- Golden Shadows: smuggling, forged permits, counter-fraud
        TAP = 4, -- Temple Aprobelle: panic control, trauma, rumor suppression
        HSA = 2, -- Harmony Sanctum: family/community conflict stabilization
        SDE = 2, -- Sanctum Delphica: registries, documents, doctrine quarantine
        AMI = 2, -- Ordo Amicitia: relief for afflicted households
        AZS = 1, -- Azure Sentinel: canal/port custody and wetland movement
        CAT = 1, -- Rex Catonis: legal registration and enforcement
    }
 
    local nocticCitySpecificBoost = {
        ["Somniumpolis"] = {RNB=5, TAP=4, SVI=4, ALB=3, VDE=3},
        ["Odiferia"]    = {VDE=6, SVI=4, TAP=4, ALB=3, GGS=3},
        ["Erythros"]    = {CON=4, RNB=4, STY=3, TAP=3, SDE=2},
        ["Acheron"]    = {STY=6, SVI=3, ALB=3, UMB=3, TAP=3},
        ["Norsolyra"]   = {VDE=5, RNB=4, SVI=3, SYL=2, TAP=3},
        ["Skøda"]      = {UMB=6, CON=4, STY=4, SDE=3, TAP=3, GGS=2},
        ["The Alpazkigz"] = {VDE=4, STY=4, RNB=4, TAP=3, SVI=3},
        ["Jogi"]        = {GGS=3, MIR=2, TAP=3, SVI=2},
        ["Aurelia"]     = {CON=3, SDE=2, TAP=3, RNB=2},
        ["Vaeringheim"] = {GGS=3, TAP=3, SVI=2, ALB=2, CAT=2},
    }
 
    local function addNocticBoostsToWeights(weighted, seen, cityName)
        local risk = nocticCityRisk[cityName] or 0
        if risk <= 0 then return end


-- =========================
        local citySpecific = nocticCitySpecificBoost[cityName] or {}
-- CORE MINORS
        local riskBump = math.floor(risk / 2)
-- =========================
["Saluria"] = {
tags={"civic","artisan","wildlife"},
weights={SYF=2,CHS=2,ACC=1,HSA=1,OAI=1},
},
["Aetherium"] = {
tags={"sky","omens","education"},
weights={CIL=2,OAM=2,CHS=2,OSI=1},
},
["Ferrum Citadel"] = {
tags={"fortress","industry","security","infrastructure"},
weights={CIC=3,RCA=2,TIL=2,EOF=1,IGA=1},
baseSev="yellow",
},
["Acheron"] = {
tags={"frontier","containment","mines","security"},
weights={CIC=3,EOF=2,MSV=1,UOR=1,IGA=1,RCA=1},
baseSev="yellow",
},
["Erythros"] = {
tags={"corridor","trade","security"},
weights={GGS=2,RCA=2,TIL=2,ASS=1,IGA=1},
},
["Catonis Atrium"] = {
tags={"civic","law","order"},
weights={RCA=4,HSA=1,CHS=1,ACC=1},
},
["Delphica"] = {
tags={"oracle","archives","scholarship"},
weights={SDL=4,CIL=2,CHS=1,HSA=1},
},
["Koinonía"] = {
tags={"harmony","mediation","civic"},
weights={ACC=3,HSA=3,CHS=1,OAI=1},
},
["Aureum"] = {
tags={"wealth","craft","trade","finance"},
weights={GGS=4,HSA=1,RCA=1,ALB=1},
},


-- =========================
        for _, it in ipairs(weighted) do
-- ALPERKIN
            local base = nocticOrderBoost[it.key] or 0
-- =========================
            local localBoost = citySpecific[it.key] or 0
["The Alpazkigz"] = {
            if base > 0 or localBoost > 0 then
tags={"tribal","festival","intercession","mediation"},
                it.w = it.w + base + localBoost + riskBump
weights={ACC=3,HSA=2,CHS=2,OAI=1,CIC=1},
            end
baseSev="yellow",
        end
},
["Odiferia"] = {
tags={"noctic","rumorcontrol","dream","folk"},
weights={REN=3,HSA=2,GGS=2,UOR=2,MSV=1},
baseSev="yellow",
},


-- =========================
        for key, base in pairs(nocticOrderBoost) do
-- NEW SOUTH JANGSONG (MAJOR)
            if not seen[key] then
-- =========================
                local localBoost = citySpecific[key] or 0
["Skýrophos"] = {
                if base > 0 or localBoost > 0 then
tags={"integration","civic","corridor","governance"},
                    weighted[#weighted+1] = {key=key, w=base + localBoost + riskBump}
weights={CHS=2,HSA=2,RCA=2,TIL=2,EOF=2,ACC=1},
                    seen[key] = true
},
                end
["Bjornopolis"] = {
            end
tags={"north","fortified","industry","civic"},
        end
weights={EOF=3,CIC=2,TIL=2,ASS=1,RCA=1},
    end
baseSev="yellow",
},
["Aegirheim"] = {
tags={"north","coastal","harbor","watch"},
weights={ASS=3,EOF=2,CIC=1,TIL=1},
baseSev="yellow",
},
["Norsolyra"] = {
tags={"agriculture","wetlands","river","supply"},
weights={MVE=3,ASS=2,SYF=2,OAI=1,SCV=1},
},
["Thorsalon"] = {
tags={"north","vineyards","festival","supply"},
weights={MVE=2,MRM=2,EOF=2,OAI=1,ASS=1},
baseSev="yellow",
},


-- =========================
--------------------------------------------------------------------------------
-- NEW SOUTH JANGSONG (MINOR)
-- 14.2) ALPERKIN NECROMANCY / ANCESTOR-RITE DISTURBANCE WEIGHTING
-- =========================
--
["Pelagia"] = {
-- This system distinguishes sanctioned Alperkin ancestor rites from forbidden
tags={"coastal","distribution","water","relief"},
-- necromancy. It increases the odds that the correct cults are selected in
weights={ASS=3,SCV=2,HSA=1,EOF=1},
-- known hotspots, but the actual necromancy task remains rare and is handled
},
-- by maybeAlperkinNecromancyTask().
["Myrene"] = {
--------------------------------------------------------------------------------
tags={"coastal","civic","trade"},
local necromancyCityRisk = {
weights={ASS=2,HSA=2,CHS=1,ACC=1},
["The Alpazkigz"] = 10, -- Amanitian Lowlands / Gloom Forest ancestor rites
},
["Odiferia"]     = 10, -- Southern Wetlands: forbidden swamp necromancy
["Thyrea"] = {
["Pyralis"]      = 8, -- Volcanic Tooth Caverns / preserved ancestors
tags={"agriculture","market","relief"},
["Erythros"]     = 8,  -- tomb spirits and ancestor voices
weights={MVE=3,OAI=2,SYF=1,HSA=1},
["Acheron"]      = 8, -- revenants, roadside apparitions, canyon tombs
},
["Nexa"]        = 7, -- forbidden scholarly necromancy
["Ephyra"] = {
["Aureum"]       = 7,  -- elite corpse-preservation experiments
tags={"forest","frontier","watch","omens"},
["Somniumpolis"] = 7, -- dream-necromancy and dead-speaking dreams
weights={SYF=3,UOR=2,EOF=1,CIC=1},
}
baseSev="yellow",
},
["Halicarn"] = {
tags={"archives","scholarship","civic"},
weights={SDL=3,CIL=2,CHS=1,HSA=1},
},


-- =========================
local necromancyOrderBoost = {
-- HAIFAN BASSARIDIA (MAJOR)
UMB = 6, -- Order of the Umbral Oracle: occult investigation / spirit-binding
-- =========================
STY = 6, -- Mystery of the Stygian Veil: funerary custody / tombs / the dead
["Keybir-Aviv"] = {
SDE = 4, -- Sanctum Delphica: forbidden rites, manuscripts, quarantine
tags={"port","trade","gateway","security"},
TAP = 4, -- Temple Aprobelle: panic, hysteria, public order
weights={ASS=3,GGS=2,HSA=2,RCA=1,CIC=1},
SVI = 2, -- Sanctum Vitalis: corpse handling / disease concerns
baseSev="yellow",
ALB = 2, -- Temple Alabaster: purification / contamination concerns
},
RNB = 2, -- Reverie Nebulous: dream-necromancy
["Tel-Amin"] = {
VDE = 1, -- Verdant Embrace: wetlands, Gloom Forest, grave-grove settings
tags={"plantation","coffee","trade","agriculture"},
GGS = 1, -- Golden Shadows: elite funding, trafficking, covert evidence
weights={MVE=3,OAI=2,SYF=2,GGS=1,ASS=1},
CON = 1, -- Conclave Illuminara: nocturnal interpretation / lunar timing
},
}
["Diamandis"] = {
tags={"luxury","trade","vaulting","security"},
weights={GGS=4,RCA=2,ALB=1,HSA=1},
baseSev="yellow",
},
["Jogi"] = {
tags={"agroprocessing","market","ferry","trade"},
weights={MVE=3,OAI=2,ASS=2,SYF=1,GGS=1},
},
["Lewisburg"] = {
tags={"rail","hub","logistics","governance"},
weights={TIL=4,RCA=3,GGS=2,HSA=1,EOF=1,ASS=1},
baseSev="yellow",
},


-- =========================
local necromancyCitySpecificBoost = {
-- HAIFAN BASSARIDIA (MINOR)
["The Alpazkigz"] = {STY=5, UMB=5, SDE=3, TAP=3, VDE=2},
-- =========================
["Odiferia"]    = {UMB=6, STY=5, SDE=4, TAP=4, SVI=3, ALB=3, VDE=3},
["Thermosalem"] = {
["Pyralis"]     = {STY=5, UMB=4, SDE=3, IGA=2, ALB=2},
tags={"healing","resort","water","crowd"},
["Erythros"]    = {STY=5, UMB=4, CON=3, SDE=3, TAP=3},
weights={SCV=3,ASS=2,HSA=2,ACC=1,RCA=1},
["Acheron"]     = {STY=6, UMB=5, SVI=3, ALB=2, VDE=2},
baseSev="yellow",
["Nexa"]        = {SDE=6, UMB=4, STY=3, ILL=2, EOF=2},
},
["Aureum"]       = {GGS=4, SDE=4, UMB=4, ALB=3, SVI=2},
["Akróstadium"] = {
["Somniumpolis"] = {RNB=5, UMB=4, STY=4, TAP=4, SDE=3},
tags={"stadium","festival","crowd","public"},
}
weights={MRM=3,OSI=2,ACC=2,HSA=1},
baseSev="yellow",
},
["Sufriya"] = {
tags={"terminal","security","port","logistics"},
weights={ASS=3,GGS=2,CIC=2,RCA=1,TIL=1},
baseSev="yellow",
},
["Lykopolis"] = {
tags={"rail","corridor","security","logistics"},
weights={TIL=4,RCA=2,EOF=2,CIC=1,GGS=1},
baseSev="yellow",
},


-- =========================
local function addNecromancyBoostsToWeights(weighted, seen, cityName)
-- NORMARK
local risk = necromancyCityRisk[cityName] or 0
-- =========================
if risk <= 0 then return end
["Ardclach"] = {
tags={"north","ferry","coastal","watch"},
weights={ASS=3,EOF=2,CIC=1,GGS=1},
baseSev="yellow",
},
["Riddersborg"] = {
tags={"north","rail","corridor","storm"},
weights={EOF=3,TIL=2,CIC=2,ASS=1,RCA=1},
baseSev="yellow",
},


-- =========================
local citySpecific = necromancyCitySpecificBoost[cityName] or {}
-- OURIANA
local riskBump = math.floor(risk / 2)
-- =========================
["Bashkim"] = {
tags={"rail","corridor","agriculture","logistics"},
weights={MVE=2,TIL=3,RCA=2,OAI=1,EOF=1},
baseSev="yellow",
},
["Ourid"] = {
tags={"river","fisheries","supply","trade"},
weights={ASS=3,MVE=2,OAI=2,HSA=1},
},
["Tonar"] = {
tags={"quarantine","doctrine","containment","security"},
weights={ALB=3,CIC=3,RCA=2,HSA=1,SDL=1},
baseSev="red",
},


-- =========================
for _, it in ipairs(weighted) do
-- CALEDONIAN DEPENDENCIES
local base = necromancyOrderBoost[it.key] or 0
-- =========================
local localBoost = citySpecific[it.key] or 0
["Fanghorn"] = {
if base > 0 or localBoost > 0 then
tags={"frontier","forests","security","watch"},
it.w = it.w + base + localBoost + riskBump
weights={EOF=3,CIC=2,SYF=2,UOR=1,RCA=1},
end
baseSev="yellow",
end
},
["Eikbu"] = {
tags={"ranching","agriculture","trail","supply"},
weights={MVE=3,SYF=2,OAI=2,EOF=1},
},
["Galvø"] = {
tags={"island","coastal","port","storm"},
weights={ASS=3,EOF=2,CIC=1},
baseSev="yellow",
},
["Sårensby"] = {
tags={"rail","corridor","industry","civic"},
weights={TIL=3,EOF=2,RCA=2,GGS=1,ASS=1},
baseSev="yellow",
},
["Slevik"] = {
tags={"north","security","civic"},
weights={EOF=3,CIC=2,ASS=1,RCA=1},
baseSev="yellow",
},
["Sjøsborg"] = {
tags={"coastal","port","trade","watch"},
weights={ASS=3,EOF=2,GGS=1},
baseSev="yellow",
},
["Storesund"] = {
tags={"coastal","trade","storm"},
weights={ASS=3,EOF=2,CIC=1},
baseSev="yellow",
},
["Notranskja"] = {
tags={"north","frontier","watch","omens"},
weights={EOF=2,CIC=2,UOR=2,ASS=1},
baseSev="yellow",
},
["Kaledonija"] = {
tags={"regionalhub","governance","trade","civic"},
weights={RCA=3,EOF=2,GGS=2,HSA=1,ASS=1},
baseSev="yellow",
},
}


--------------------------------------------------------------------------------
for key, base in pairs(necromancyOrderBoost) do
-- 9) ECONOMIC SIGNALS: parse "Company Table" on General Port page
if not seen[key] then
--------------------------------------------------------------------------------
local localBoost = citySpecific[key] or 0
local econCache = nil
if base > 0 or localBoost > 0 then
weighted[#weighted+1] = {key=key, w=base + localBoost + riskBump}
seen[key] = true
end
end
end
end


local function sectorOf(industryCell)
local function inferSeverity(cityTier, taskSev, threatUrg)
local s = (industryCell or ""):lower()
local sev = tostring(taskSev or "green")
if s:find("agric", 1, true) then return "Agricultural" end
if cityTier == "major" and sev == "green" then sev = "yellow" end
if s:find("service", 1, true) then return "Service" end
if urgencyScore(threatUrg) >= 3 then
if s:find("manufact", 1, true) or s:find("mining", 1, true) or s:find("energy", 1, true) then
sev = "red"
return "Industry"
elseif urgencyScore(threatUrg) == 2 and sev == "green" then
sev = "yellow"
end
end
return "Other"
return sev
end
end


local function extractCompanyName(cell)
    local function pickOrder(nextInt, cityName, threat)
local n = cell:match("'''(.-)'''")
        local profile = getProfile(cityName)
if n and n ~= "" then return stripWiki(n) end
        local weighted = {}
n = cell:match("%*%*(.-)%*%*")
        local seen = {}
if n and n ~= "" then return stripWiki(n) end
 
n = stripWiki(cell)
        for key, w in pairs(profile.weights or {}) do
if #n > 80 then n = n:sub(1, 80) .. "…" end
            weighted[#weighted+1] = {key=key, w=tonumber(w) or 0}
return n
            seen[key] = true
end
        end


local function extractHQ(cell)
        -- Noctic city bump: prefer the cults most likely to handle raw Noctic-Rabrev,
local s = stripWiki(cell)
        -- Noctic Vampirism, dream disturbance, health evaluation, registration,
if s == "" then return nil end
        -- smuggling, and family/community stabilization.
if s:lower():find("fleet", 1, true) then return nil end
        addNocticBoostsToWeights(weighted, seen, cityName)
if s:lower():find("no fixed", 1, true) then return nil end
s = s:gsub("%s*;%s*.*$", "")
s = s:gsub("%s*%b()%s*", " ")
s = normalizeSpaces(s)
s = trim(s)
if citySet[s] then return s end
return nil
end


local function parseCompanyTable(wikitext)
-- Alperkin necromancy bump: prefer the cults most likely to distinguish
local signals = {}
-- sanctioned ancestor rites from forbidden spirit-binding, corpse animation,
for cname, _ in pairs(citySet) do
-- grave desecration, illicit resurrection, and prohibited funerary texts.
signals[cname] = {Agricultural=0, Service=0, Industry=0, Other=0, samples={}}
addNecromancyBoostsToWeights(weighted, seen, cityName)
end
if type(wikitext) ~= "string" then return signals end


local startPos = wikitext:find("====%s*Company Table%s*====")
        -- Threat bump: prefer Aprobelle + safety/health + shadows.
if not startPos then startPos = wikitext:find("####%s*Company Table") end
        if threat and urgencyScore(threat.urg) >= 2 then
if not startPos then return signals end
            for _, it in ipairs(weighted) do
                if it.key == "TAP" or it.key == "SVI" or it.key == "AZS" or it.key == "GGS" or it.key == "SDE" then
                    it.w = it.w + 2
                end
            end
        end


local tablePos = wikitext:find("{|", startPos)
        local pickedKey = pickWeighted(nextInt, weighted).key
if not tablePos then return signals end
local endPos = wikitext:find("|}", tablePos)
if not endPos then return signals end


local tableText = wikitext:sub(tablePos, endPos + 1)
        for _, o in ipairs(orders) do
            if o.key == pickedKey then return o end
        end


local rows = {}
        return orders[1]
for chunk in tableText:gmatch("\n|%-%s*\n(.-)\n|%-%s*\n") do
    end
rows[#rows+1] = chunk
end
local lastChunk = tableText:match("\n|%-%s*\n(.-)\n|%}")
if lastChunk then rows[#rows+1] = lastChunk end


for _, row in ipairs(rows) do
local function pickTask(nextInt, cultName, sev, usedIdx)
if not row:match("^%s*!") then
  local pool = tasksByCult[cultName] or tasksByCult["Harmony Sanctum"] or {}
local line = normalizeSpaces(row:gsub("\n", " "))
line = line:gsub("^%s*|%s*", "")


local cells = {}
  local weighted = {}
for cell in line:gmatch("([^|]-)%s*||%s*") do
  for idx, t in ipairs(pool) do
cells[#cells+1] = trim(cell)
    if not (usedIdx and usedIdx[idx]) then
end
      local w = 1
local tail = line:match("||%s*([^|].-)%s*$")
      if t.sev == sev then w = 4
if tail and tail ~= "" then
      elseif sev == "red" and t.sev == "yellow" then w = 2
if #cells == 0 or cells[#cells] ~= trim(tail) then
      elseif sev == "yellow" and t.sev == "green" then w = 2 end
cells[#cells+1] = trim(tail)
      table.insert(weighted, {t=t, w=w, idx=idx})
end
    end
end
  end


local industryCell = ""
  if #weighted == 0 then
for _, c in ipairs(cells) do
    for idx, t in ipairs(pool) do
if containsAny(c, {"agric", "service", "manufact", "mining", "energy"}) then
      local w = 1
industryCell = c
      if t.sev == sev then w = 4
break
      elseif sev == "red" and t.sev == "yellow" then w = 2
end
      elseif sev == "yellow" and t.sev == "green" then w = 2 end
end
      table.insert(weighted, {t=t, w=w, idx=idx})
local sector = sectorOf(industryCell)
    end
  end


local hqCell = cells[#cells] or ""
  if #weighted == 0 then
local hq = extractHQ(hqCell)
    return {text="Routine rites and civic support (no task pool configured).", sev="green"}, nil
if hq and signals[hq] then
  end
signals[hq][sector] = (signals[hq][sector] or 0) + 1
local nameCell = cells[3] or cells[2] or cells[1] or ""
local companyName = extractCompanyName(nameCell)
if companyName ~= "" and #signals[hq].samples < 2 then
signals[hq].samples[#signals[hq].samples+1] = companyName
end
end
end
end


return signals
  local picked = pickWeighted(nextInt, weighted)
  return picked.t, picked.idx
end
end


local function getEconSignals()
--------------------------------------------------------------------------------
if econCache then return econCache end
-- 15.0) RARE ALPERKIN NECROMANCY / FORBIDDEN ANCESTOR-RITE OPERATIONS
local content = safeTitleContent("General Port of Lake Morovia")
--
econCache = parseCompanyTable(content)
-- These are rare city-specific overrides. They should not replace ordinary
return econCache
-- Alperkin ancestor traditions. The text deliberately distinguishes sanctioned
end
-- ancestor rites from forbidden necromancy involving spirit-binding, corpse
-- animation, grave desecration, illicit resurrection, or prohibited texts.
--------------------------------------------------------------------------------
local necromancyResponderKeys = {
UMB = true,
STY = true,
SDE = true,
TAP = true,
SVI = true,
ALB = true,
RNB = true,
GGS = true,
}
 
local necromancyResponderLabel = {
UMB = "Umbral Oracle occult-investigation mission",
STY = "Stygian Veil funerary-custody mission",
SDE = "Delphica restricted-rite review",
TAP = "Aprobelle panic-control intervention",
SVI = "Vitalis corpse-handling and disease-screening support",
ALB = "Alabaster purification and contamination-control support",
RNB = "Reverie Nebulous dream-necromancy assessment",
GGS = "Golden Shadows covert evidence inquiry",
}


local function dominantSector(sig)
local necromancyEventsByCity = {
if not sig then return "Other" end
["The Alpazkigz"] = {
local best, bestN = "Other", -1
T("ancestor-rite auditors are dispatched after elders report that preserved ancestral remains have been addressed through coercive rather than sanctioned rites; grave stewards separate accepted ancestor veneration from suspected spirit-binding.", "yellow"),
for _, k in ipairs({"Agricultural","Industry","Service","Other"}) do
T("Gloom Forest burial paths are placed under watch after witnesses hear ancestral voices repeating commands from sealed family tombs; custody seals are checked and unauthorized night offerings are removed.", "yellow"),
local n = sig[k] or 0
T("a red-posture investigation begins after a prohibited rite allegedly binds ancestral spirits to exposed remains near a sacred burial grove; movement is restricted while funerary stewards and occult investigators secure the site.", "red"),
if n > bestN then best, bestN = k, n end
},
end
["Odiferia"] = {
return best
T("wetland grave markers are found overturned beside binding cords, bone charms, and forbidden swamp-script; missionaries seal the path, protect witnesses, and separate local dark Alp customs from illicit necromancy.", "yellow"),
end
T("isolated swamp shrines are inspected after reports of rogue necromancers attempting to bargain with preserved dead; unauthorized offerings are seized and spirit-binding claims are recorded under seal.", "yellow"),
T("a red-level swamp cult response is activated after grave desecration, corpse movement, and forced ancestral invocations are reported in the southern wetlands; quarantine ropes are posted and night travel is suspended.", "red"),
},
["Pyralis"] = {
T("Volcanic Tooth Cavern stewards request assistance after preserved ancestral remains are found repositioned beside ash-script invocations; relic custody is rebuilt and unauthorized tomb access is suspended.", "yellow"),
T("mummified elite remains in a heated cavern tomb show signs of ritual tampering; missionaries verify which ancestor rites were sanctioned and which inscriptions match forbidden resurrection formulae.", "yellow"),
T("a red-posture tomb lockdown is ordered after a volcanic ancestor cult attempts to animate preserved remains using ember-offerings and prohibited names; cavern entrances are sealed pending doctrinal review.", "red"),
},
["Erythros"] = {
T("tomb-spirit reports intensify after families hear ancestral voices from sealed crypts; Stygian custody stewards verify offerings while Umbral witnesses test for coercive spirit-binding.", "yellow"),
T("ancestor-song vigils are paused after a chant leader repeats names not recorded in the family death ledgers; missionaries distinguish accepted memorial practice from necromantic invocation.", "yellow"),
T("a red-level response begins after tomb voices identify the living by hidden names and demand blood-offerings; the precinct is cleared, witnesses are shielded, and ritual transcripts are quarantined.", "red"),
},
["Nexa"] = {
T("a private study circle is investigated after students copy funerary diagrams from restricted manuscripts; pages are seized and participants are redirected into sanctioned theological review.", "yellow"),
T("forbidden scholarly necromancy is suspected after marginal notes describe corpse animation as a calendrical experiment; Delphica scribes quarantine the archive shelf and record all readers.", "yellow"),
T("a red-posture archive lockdown follows evidence of an attempted resurrection rite using prohibited commentaries; manuscripts are relocated under escort and public explanation is kept deliberately narrow.", "red"),
},
["Acheron"] = {
T("roadside apparitions near canyon tombs prompt funerary patrols; missionaries interview travelers, inspect grave ledges, and remove coercive offerings left for wandering dead.", "yellow"),
T("revenant claims spread after miners report figures walking from abandoned cliff burrows; Stygian stewards compare burial records while Umbral observers check for binding marks.", "yellow"),
T("a red-level canyon response is triggered after remains are found arranged beside petrified roots and spoken-name tablets; the road is closed and corpse custody is transferred under seal.", "red"),
},
["Aureum"] = {
T("elite-funded corpse-preservation experiments are reviewed after private patrons request unlawful ancestral consultation; Golden Shadows auditors trace payments while Delphica scribes seize ritual notes.", "yellow"),
T("a hidden preservation chamber beneath a wealthy household is inspected for illicit resurrection instruments; medical, purification, and funerary witnesses are assigned before remains are moved.", "yellow"),
T("a red-posture intervention begins after evidence suggests a patron funded spirit-binding to keep a dead adviser communicative; ledgers are seized and the household is placed under ritual quarantine.", "red"),
},
["Somniumpolis"] = {
T("dream-necromancy reports are triaged after mourners claim the dead are speaking through shared dreams; Reverie guides record narratives while Umbral witnesses screen for coercive invocations.", "yellow"),
T("sleep-circle permits are reviewed after a grieving household attempts to summon an ancestor through dream-binding; counselors separate mourning visions from forbidden manipulation of the dead.", "yellow"),
T("a red-level dream-necromancy response is activated after multiple sleepers repeat the same dead voice demanding physical remains; the household is stabilized and ritual notes are sealed.", "red"),
},
}


local function econTagFromSector(sec)
local necromancyJointCrisis = {
if sec == "Agricultural" then return "agri" end
T("Exceptional Alperkin necromancy crisis: the Order of the Umbral Oracle, Mystery of the Stygian Veil, and Sanctum Delphica respond jointly after evidence emerges that a prohibited rite has successfully bound ancestral spirits to physical remains; ritual sites are quarantined, movement is restricted, corpse custody is rebuilt, and all manuscripts are placed under doctrinal seal.", "red"),
if sec == "Industry" then return "industry" end
T("Exceptional cross-cult necromancy investigation: Umbral Oracle investigators, Stygian Veil funerary stewards, and Delphica archivists assume joint control after corpse animation, grave desecration, and forbidden resurrection formulae are linked to the same Alperkin ritual network; public statements stress that sanctioned ancestor rites remain lawful.", "red"),
if sec == "Service" then return "service" end
}
return "mixed"
end


local function econSummary(sig)
local function maybeAlperkinNecromancyTask(nextInt, cityName, order, desiredSev)
if not sig then return "n/a" end
local risk = necromancyCityRisk[cityName] or 0
local a = sig.Agricultural or 0
if risk <= 0 then return nil end
local i = sig.Industry or 0
if not order or not necromancyResponderKeys[order.key] then return nil end
local s = sig.Service or 0
local o = sig.Other or 0
local dom = dominantSector(sig)
local samp = (sig.samples and #sig.samples > 0) and ("; e.g., " .. table.concat(sig.samples, ", ")) or ""
return string.format("%s (%d agri / %d ind / %d serv / %d other)%s", dom, a, i, s, o, samp)
end


--------------------------------------------------------------------------------
-- Extremely rare joint crisis. Only the three principal responder cults
-- 10) OPTIONAL: Threat Watch integration from Module:ReligiousEvents
-- should carry this as the visible row order.
--------------------------------------------------------------------------------
if order.key == "UMB" or order.key == "STY" or order.key == "SDE" then
local function parseReligiousEventsTable(wikitext)
local crisisChance = 2
local out = {}
if risk >= 10 then
if type(wikitext) ~= "string" then return out end
crisisChance = 7
for line in wikitext:gmatch("[^\n]+") do
elseif risk >= 8 then
if line:match("^|%s*%d+/%d+/%d+%s+PSSC") then
crisisChance = 4
local date, city, event, urg = line:match("^|%s*(.-)%s*||%s*(.-)%s*||%s*(.-)%s*||%s*(.-)%s*$")
end
if city and urg then
if (nextInt() % 1000) < crisisChance then
out[stripWiki(city)] = {urg=trim(urg), event=stripWiki(event or "")}
return pickFromList(nextInt, necromancyJointCrisis, nil)
end
end
end
end
-- Ordinary necromancy incidents remain rare. Higher-risk cities and
-- already-heightened days increase the chance slightly.
local chance = math.floor(risk / 2)
if desiredSev == "yellow" then
chance = chance + 3
elseif desiredSev == "red" then
chance = chance + 6
end
if (nextInt() % 100) >= chance then
return nil
end
local pool = necromancyEventsByCity[cityName]
if not pool or #pool == 0 then return nil end
local chosen = pickFromList(nextInt, pool, nil)
local label = necromancyResponderLabel[order.key] or "Alperkin necromancy-response mission"
return T(
label .. ": " .. chosen.text ..
" Sanctioned ancestor rites remain distinguished from forbidden necromancy involving spirit-binding, corpse animation, grave desecration, illicit resurrection, or prohibited funerary texts.",
chosen.sev or "yellow"
)
end
end
return out
end
local function threatSev(th)
if not th or not th.urg then return nil end
local u = (th.urg or ""):upper()
if u == "N/A" or u == "LOW" then return "green" end
if u == "MEDIUM" then return "yellow" end
if u == "HIGH" or u == "CRITICAL" then return "red" end
return "yellow"
end


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- 11) WEIGHTED ORDER PICK + TASK PICK
-- 15.1) SOUTHERN STRAIT MISSIONARY OPERATIONS
--
-- These special operations may appear only when the generated city row is one
-- of the Haifan Bassaridian cities. The city remains the listed station, but
-- the task text may describe movement across the Southern Strait, former
-- Maritime Markets corridors, pilgrimage routes, inland transfer points,
-- quay markets, ferry lines, canal mouths, or rail-linked cargo routes.
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
local function weightedPickKey(nextInt, weights)
local total = 0
for _, ok in ipairs(orderKeys) do
total = total + (weights[ok] or 0)
end
if total <= 0 then
return pickFromList(nextInt, orderKeys, nil)
end
local r = (nextInt() % total) + 1
local acc = 0
for _, ok in ipairs(orderKeys) do
acc = acc + (weights[ok] or 0)
if r <= acc then return ok end
end
return orderKeys[1]
end


local function buildTagSet(tags)
local southernStraitMissionLabels = {
local set = {}
"Southern Strait missionary circuit",
for _, t in ipairs(tags or {}) do set[t] = true end
"Southern Strait pilgrimage-route mission",
return set
"Former Maritime Markets liaison mission",
end
"Southern Strait dockside intervention",
"Southern Strait inland-route mission",
"Southern Strait shrine-and-quay mission",
"Southern Strait corridor-stabilization mission",
}
 
local southernStraitGenericOps = {
T("missionaries move between shrine piers, ferry queues, inland transfer offices, and market lanes to stabilize lawful religious movement while recording interference by brokers, smugglers, or hostile cult agents.","yellow"),
T("missionaries conduct a multi-stop route audit through quay markets, road-transfer yards, and rail-linked cargo offices; local stewards collect testimony while Temple Bank clerks verify custody records.","yellow"),
T("missionaries establish a temporary calm station near a port-to-inland transfer point, separating pilgrims, cargo handlers, and anxious families while reports are routed back through the local Haifan station.","green"),
}
 
local southernStraitOpsByCultKey = {
AUM = {
T("Order of Aurora Mystica missionaries conduct dawn reassurance rites across multiple harbor shrines, inspect lamp stations for false-signal use, and gather civic complaints from travelers arriving before inspection hours.","green"),
T("Order of Aurora Mystica missionaries run a first-light grievance circuit from the quay to inland registry offices; witnesses reporting intimidation by brokers or smugglers are moved through protected morning routes.","yellow"),
T("Order of Aurora Mystica missionaries bless harbor beacons and shrine lamps while correcting rumors that false lights or unusual dawn colors signal divine approval for illicit passage.","yellow"),
},
 
HSA = {
T("Harmony Sanctum missionaries mediate a dispute between dockworkers, ferry crews, shrine porters, and caravan handlers; neutral lanes are marked so trade and pilgrimage traffic can continue without retaliation.","green"),
T("Harmony Sanctum missionaries establish a reconciliation table for families, vendors, and sailors divided by smuggling accusations; testimony is separated from rumor before being routed to Temple Bank clerks.","yellow"),
T("Harmony Sanctum missionaries conduct a multi-ward crowd discipline mission through quay markets and inland caravan yards, using steward lanes to prevent merchant quarrels from becoming anti-Bassaridian unrest.","yellow"),
},
 
IGA = {
T("Ignis Aeternum missionaries inspect lantern stores, brazier permits, and warehouse fuel rooms along the Southern Strait after rumors of arson cover for smuggling transfers.","yellow"),
T("Ignis Aeternum missionaries conduct firewatch rites at cargo depots and harbor shrines, marking illegal fuel caches and unsafe pyre materials for seizure by local authorities.","yellow"),
T("Ignis Aeternum missionaries support emergency evacuation after a dockside fire scare, keeping torch-bearing celebrants away from bonded warehouses and preserving evidence of possible sabotage.","red"),
},
 
CHS = {
T("Celestial Harmony Sect missionaries correct false omen charts circulating among sailors, pilgrims, and night-ferry crews; approved sky-sign guidance is posted at shrine piers and inland waiting halls.","green"),
T("Celestial Harmony Sect missionaries conduct a night-navigation reassurance circuit, separating legitimate star-lore from coded smuggler signals used near the former Maritime Markets routes.","yellow"),
T("Celestial Harmony Sect missionaries open quiet stations for travelers frightened by sky-sign rumors while clerks record which brokers spread the panic through ferry lines.","yellow"),
},
 
GGS = {
T("Guild of Golden Shadows missionaries trace forged shrine permits, false port vouchers, and suspicious cargo seals through dockside broker houses and inland rail offices.","yellow"),
T("Guild of Golden Shadows missionaries conduct a covert ledger comparison between market scribes, ferry agents, and warehouse clerks to expose cargo-identity laundering in former Maritime Markets networks.","yellow"),
T("Guild of Golden Shadows missionaries identify a counterfeit Temple seal chain moving through pilgrimage baggage; suspect stamps are catalogued and witness statements sealed for review.","red"),
 
T("Guild of Golden Shadows missionaries convene a Southern Strait trade-agreement parley with ferry stewards, orchard carriers, quay merchants, shrine porters, and inland caravan representatives; proposed terms are written into a local compact and forwarded to [[General Port of Lake Morovia]] registry clerks.","green"),
T("Guild of Golden Shadows missionaries establish a provisional trade agreement between a harbor community and nearby inland producers, using voucher reconciliation, witness seals, and Temple Bank notation to prevent old Maritime Markets brokers from distorting the terms.","yellow"),
T("Guild of Golden Shadows missionaries promote [[General Port of Lake Morovia]] membership among local merchants, small-craft owners, shrine suppliers, and caravan brokers by explaining registry protections, lawful access to supervised markets, and the commercial benefits of audited participation.","green"),
T("Guild of Golden Shadows missionaries conduct a General Port investment and membership briefing in a quay market, identifying trustworthy local partners, recording investor interest, and separating legitimate community capital from smuggler-backed broker money.","yellow"),
T("Guild of Golden Shadows missionaries organize a trade-normalization circuit across ferry landings and inland transfer yards, encouraging local communities to enter General Port membership while documenting forged contracts, intimidation, and false debt claims.","yellow"),
T("Guild of Golden Shadows missionaries broker a community-to-port supply arrangement for lawful agricultural, fishery, craft, or shrine goods; General Port membership forms, cargo-origin notes, and Temple Bank witness records are completed on site.","green"),
T("Guild of Golden Shadows missionaries investigate resistance to General Port membership after rumors spread that registry enrollment will erase local trading privileges; the mission corrects false claims while preserving valid community exemptions for review.","yellow"),
T("Guild of Golden Shadows missionaries expose a broker faction attempting to sabotage a new General Port trade agreement by planting false investment notes, counterfeit seals, and misleading debt ledgers among local merchants.","red"),
T("Guild of Golden Shadows missionaries escort local community representatives through the first stage of General Port enrollment, protecting membership papers, investment pledges, and cargo ledgers from theft, intimidation, or alteration by former Maritime Markets intermediaries.","yellow"),
T("Guild of Golden Shadows missionaries create a confidential list of community merchants suitable for future General Port investment partnerships, excluding suspected smugglers, forged-voucher carriers, and broker agents tied to unlawful corridor disruption.","yellow"),
},
 
AZS = {
T("Azure Sentinel Sect missionaries inspect ferry queues, canal approaches, rope lanes, pilgrim piers, and water-safety markers while documenting unsafe crossings used by smugglers.","yellow"),
T("Azure Sentinel Sect missionaries conduct a waterfront safety circuit across the Southern Strait, marking storm lanes, rescue points, and suspicious skiff landings for follow-up by port authorities.","yellow"),
T("Azure Sentinel Sect missionaries respond to a dangerous ferry-ramp surge near a shrine pier, opening rescue lanes and separating pilgrims from unregistered cargo handlers.","red"),
},
 
RNB = {
T("Reverie Nebulous missionaries record nightmare claims among sailors, ferry workers, and dockside families, distinguishing dream contagion rumors from Noctic cargo panic and ordinary smuggling fear.","yellow"),
T("Reverie Nebulous missionaries open supervised rest spaces for distressed travelers moving through the Southern Strait pilgrimage routes; rumor patterns are mapped by quay and ferry station.","yellow"),
T("Reverie Nebulous missionaries intervene after a mass dream-panic episode in a harbor ward, moving witnesses away from crowd pressure and preserving testimony under seal.","red"),
},
 
EOF = {
T("Eon Fellowship missionaries reconcile old Maritime Markets family oaths with current Bassaridian registry expectations, recording continuity pledges from ship families and hereditary port stewards.","green"),
T("Eon Fellowship missionaries conduct an intergenerational testimony circuit along the Southern Strait, preserving family records before broker houses can alter lineage claims or debt histories.","yellow"),
T("Eon Fellowship missionaries stabilize a dispute over ancestral trading privileges by redirecting claimants into formal Temple Bank testimony and registry review.","yellow"),
},
 
UMB = {
T("Order of the Umbral Oracle missionaries conduct a night-route inquiry through alleys, ferry shelters, and shrine back rooms where courier rumors suggest hidden anti-Bassaridian coordination.","yellow"),
T("Order of the Umbral Oracle missionaries interview night watchmen, lamplighters, and shrine attendants to separate legitimate nocturnal rites from covert courier drops.","yellow"),
T("Order of the Umbral Oracle missionaries activate a red-posture shadow-watch after masked agitators attempt to use pilgrimage crowds as cover for illicit ritual movement.","red"),
},
 
VDE = {
T("Mystery of the Verdant Embrace missionaries inspect plant offerings, medicinal herbs, orchard cargo, and market produce moving from the quay into inland shrine routes.","green"),
T("Mystery of the Verdant Embrace missionaries trace suspicious plant bundles through wet markets and ferry landings, checking whether ritual herbs are being used to conceal contraband.","yellow"),
T("Mystery of the Verdant Embrace missionaries secure a contaminated produce consignment after rumors connect it to illicit Noctic-Rabrev handling along the Southern Strait.","red"),
},
 
CON = {
T("Conclave Illuminara missionaries review Tarsica-timed pilgrimage movements, ferry schedules, and night-vigil routes to prevent lunar observances from being exploited by smugglers.","green"),
T("Conclave Illuminara missionaries correct false lunar-tide claims used by brokers to justify unauthorized night crossings; revised timing guidance is posted at harbor shrines.","yellow"),
T("Conclave Illuminara missionaries open a controlled moonlight vigil route after panic spreads through a ferry queue; stewards record names and prevent unsupervised dispersal.","yellow"),
},
 
SVI = {
T("Sanctum Vitalis missionaries escort clinic packets, triage notes, and medical supplies between harbor clinics, ferry stations, and inland care posts along the Southern Strait.","yellow"),
T("Sanctum Vitalis missionaries conduct health screening at a pilgrimage landing, separating fever, exhaustion, Noctic concerns, and ordinary travel sickness before rumors can spread.","yellow"),
T("Sanctum Vitalis missionaries activate a red-posture medical transfer after hostile crowds threaten registered patients moving from a dockside clinic to an inland shelter.","red"),
},
 
ALB = {
T("Temple Alabaster missionaries inspect sealed tinctures, balms, medicines, and purity records moving through Southern Strait warehouses and clinic piers.","green"),
T("Temple Alabaster missionaries identify counterfeit medicine jars in dockside cargo and redirect the consignment through Temple Bank custody for purity review.","yellow"),
T("Temple Alabaster missionaries conduct an emergency medicine-chain audit after a forged clinic seal appears in ferry freight moving inland.","red"),
},
 
CICL = {
T("Court of the Ironclad missionaries establish a protective witness lane for threatened shrine clerks, ferry stewards, and registry witnesses reporting smuggling activity.","yellow"),
T("Court of the Ironclad missionaries support a disciplined perimeter around a seized cargo stack, preventing vigilante interference while legal testimony is gathered.","yellow"),
T("Court of the Ironclad missionaries activate crisis protection for a high-risk detainee transfer connected to pirate recruiting near a pilgrimage route.","red"),
},
 
ACC = {
T("Accord Concordia missionaries negotiate temporary passage terms between ferry operators, caravan leaders, shrine porters, and market stewards to keep lawful movement open.","green"),
T("Accord Concordia missionaries draft a written concord between rival broker families accused of obstructing pilgrims and lawful cargo moving through the Southern Strait.","yellow"),
T("Accord Concordia missionaries convene an emergency truce after dockside violence threatens to spread into inland caravan yards and shrine courtyards.","red"),
},
 
STY = {
T("Mystery of the Stygian Veil missionaries conduct death-at-sea rites for recovered bodies while ensuring mourning families are not exploited by brokers or rumor sellers.","yellow"),
T("Mystery of the Stygian Veil missionaries escort funerary offerings through a contested harbor ward, separating legitimate mourning from illicit ritual theater.","yellow"),
T("Mystery of the Stygian Veil missionaries stabilize a red-posture mortuary transfer after violent rumors spread around unidentified remains from the Southern Strait.","red"),
},
 
SYL = {
T("Sylvan Fellowship missionaries inspect coastal groves, animal lanes, and shrine gardens near ferry roads where displaced wildlife and pilgrim traffic have begun to clash.","green"),
T("Sylvan Fellowship missionaries guide a low-impact pilgrimage route around sensitive shoreline habitat while documenting illegal cutting, trapping, and smuggler paths.","yellow"),
T("Sylvan Fellowship missionaries coordinate emergency animal-control and relief stations after floodwater drives coastal creatures into a crowded shrine district.","red"),
},
 
MIR = {
T("Mystery of Red Mirth missionaries manage reveler lanes during a dockside festival, separating licensed celebration from smuggler cover and unsafe intoxication.","yellow"),
T("Mystery of Red Mirth missionaries conduct a merriment-control circuit from ferry markets to inland tavern lanes, using steward chants to prevent crowd surges.","yellow"),
T("Mystery of Red Mirth missionaries activate a crisis dispersal rite after festival crowds are used to conceal courier drops and provoke anti-registry disorder.","red"),
},
 
CAT = {
T("Rex Catonis missionaries open a legal registration table for ferry workers, cargo handlers, and disputed household claims connected to Southern Strait movement.","green"),
T("Rex Catonis missionaries review petitions over seized cargo, blocked pilgrimage routes, and alleged abuse by brokers, routing valid claims into formal hearing dockets.","yellow"),
T("Rex Catonis missionaries conduct an emergency rights-and-duties reading after a seizure dispute threatens to turn into a dockside riot.","red"),
},
 
SDE = {
T("Sanctum Delphica missionaries audit shrine credentials, restricted rite permits, teaching licenses, and courier papers moving through the Southern Strait corridor.","yellow"),
T("Sanctum Delphica missionaries correct unsafe doctrine claims posted near ferry stations and market shrines, replacing them with approved quarantine and registry language.","yellow"),
T("Sanctum Delphica missionaries lock down a compromised archive packet after forged ritual permissions are discovered in inland-route baggage.","red"),
},
 
AMI = {
T("Ordo Amicitia missionaries distribute food, water, blankets, and family-support vouchers to pilgrims delayed by inspection queues or disrupted ferry schedules.","green"),
T("Ordo Amicitia missionaries escort displaced families between dockside shelters, shrine courtyards, and inland aid offices while shielding them from broker recruitment.","yellow"),
T("Ordo Amicitia missionaries activate a red-posture relief corridor after smuggling violence strands families near a port-to-inland transfer point.","red"),
},
 
ILL = {
T("Temple Illuminata missionaries inspect teaching booths, gold-market charity records, and illumination lamps used for public instruction near Southern Strait trade lanes.","green"),
T("Temple Illuminata missionaries correct false prosperity sermons used by brokers to lure pilgrims into unauthorized cargo schemes and debt traps.","yellow"),
T("Temple Illuminata missionaries secure educational records and donation ledgers after a counterfeit charity ring is uncovered in a quay market.","red"),
},


local function pickTaskForCity(nextInt, taskList, tagSet)
TAP = {
local candidates = {}
T("Temple Aprobelle missionaries conduct a rumor-control circuit across ferry queues, quay markets, shrine lanes, and inland transfer offices after panic spreads about smuggling raids.","yellow"),
for _, task in ipairs(taskList or {}) do
T("Temple Aprobelle missionaries map rumor nodes tied to pirate brokers, false cult warnings, and anti-Bassaridian gossip, then issue corrective language through local stewards.","yellow"),
local ok = false
T("Temple Aprobelle missionaries activate a crisis social-harmony response after crowd panic threatens to block lawful pilgrimage and cargo movement through the Southern Strait.","red"),
for _, tg in ipairs(task.tags or {}) do
},
if tagSet[tg] then ok = true break end
}
end
if ok then candidates[#candidates+1] = task end
end
if #candidates > 0 then
return pickFromList(nextInt, candidates, nil)
end
return pickFromList(nextInt, taskList, nil)
end


local function baseWeights()
local function maybeSouthernStraitCultTask(nextInt, cityName, order, desiredSev)
local w = {}
if not isHaifanBassaridiaCity(cityName) then return nil end
for _, ok in ipairs(orderKeys) do w[ok] = 1 end
if not order or not order.key then return nil end
return w
end


local function applyWeights(w, add)
-- Frequency control:
if not add then return end
-- Green days: occasional; Yellow days: common; Red days: very common.
for k, v in pairs(add) do
local chance = 35
if orderByKey[k] then
if desiredSev == "yellow" then
w[k] = (w[k] or 0) + tonumber(v or 0)
chance = 48
end
elseif desiredSev == "red" then
chance = 65
end
end
end


local function biasFromEcon(w, econTag)
if (nextInt() % 100) >= chance then
if econTag == "agri" then
return nil
applyWeights(w, {MVE=3,SYF=2,OAI=2,ASS=1})
elseif econTag == "industry" then
applyWeights(w, {TIL=3,EOF=2,IGA=2,CIC=1,RCA=1})
elseif econTag == "service" then
applyWeights(w, {GGS=3,HSA=2,RCA=2,CIL=1,CHS=1})
else
applyWeights(w, {CHS=1,HSA=1,ASS=1})
end
end
end


local function biasFromCorridor(w, isRail, isFerry)
local pool = southernStraitOpsByCultKey[order.key] or southernStraitGenericOps
if isRail then applyWeights(w, {TIL=2,RCA=2,GGS=1,EOF=1,ASS=1}) end
local chosen = pickFromList(nextInt, pool, nil) or southernStraitGenericOps[1]
if isFerry then applyWeights(w, {ASS=2,GGS=1,RCA=1}) end
local label = pickFromList(nextInt, southernStraitMissionLabels, nil) or "Southern Strait missionary circuit"
end


local function biasFromTier(w, tier)
return T(
if tier == "major" then
label .. ": " .. chosen.text ..
applyWeights(w, {HSA=1,RCA=1,CIL=1,CHS=1})
" Mission remains staged and logged through " .. cityLink(cityName) ..
end
", but may operate across the wider Southern Strait, former Maritime Markets corridors, pilgrimage routes, ferry lines, and inland transfer points.",
chosen.sev or desiredSev or "yellow"
)
end
end


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- 12) TEMPLE BANK NOTES
-- 16) TEMPLE BANK NOTES
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
local bankNotes = {
local bankNotes = {
"Temple Bank stipend verified; local shrine receipts reconciled; next audit window scheduled.",
"Stipend disbursed after checklist verification; receipt countersigned by local steward.",
"Voucher authorizations issued for ritual materials; chain-of-custody notes logged for any seized items.",
"Voucher reconciliation complete; supplies logged and stored under dual-key protocol.",
"Investor liaison note filed; payroll routing confirmed for the deployed unit; compliance clerk assigned.",
"Observer note: crowd discipline improved after posted guidance and steward coaching.",
"Donation flow stabilized; offertory seals inspected; variance thresholds set for the next three days.",
"Repair authorization issued; labor scheduled at off-peak hours.",
"Emergency stipend line opened for crowd-control and relief logistics; disbursement witnessed by two scribes.",
"Custody ledger updated; medicines/artifacts sealed and moved to secure storage.",
"Training memo filed; corrective instruction issued to volunteers for queue discipline.",
}
}


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- 13) BUILD ONE CITY ROW
-- 17) BUILD ONE CITY ROW
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
local function inferSeverity(cityProfile, econTag, taskSev, threatLevel)
local function buildCityRow(di, c, threatMap, includeThreat, showEcon, econSignals, ctx)
local sev = taskSev or "green"
local cityName = c.name
local seed = hash31(string.format("mops:%d:%03d:%s", di.year, di.dayOfYear, cityName))
local nextInt = lcg(seed)
local th = includeThreat and threatMap and threatMap[cityName] or nil
        local o = pickOrder(nextInt, cityName, th)
        ctx.usedTasksByCult[o.name] = ctx.usedTasksByCult[o.name] or {}
        local used = ctx.usedTasksByCult[o.name]


if cityProfile and cityProfile.baseSev then
        local threatUrg = th and th.urg or ""
sev = cityProfile.baseSev
 
end
        -- Preliminary severity gives pickTask a useful target before the exact
        -- task-driven severity is known.
        local prelimSev = "green"
        if c.tier == "major" then prelimSev = "yellow" end
        if urgencyScore(threatUrg) >= 3 then
            prelimSev = "red"
        elseif urgencyScore(threatUrg) == 2 and prelimSev == "green" then
            prelimSev = "yellow"
        end


if econTag == "industry" or econTag == "service" then
local baseTask, pickedIdx = pickTask(nextInt, o.name, prelimSev, used)
if sev == "green" then sev = "yellow" end
-- Rare Alperkin necromancy / forbidden ancestor-rite override.
-- This happens before the Southern Strait override because it is tied to
-- specific Alperkin and core hotspot cities rather than corridor operations.
local necromancyTask = maybeAlperkinNecromancyTask(nextInt, cityName, o, prelimSev)
if necromancyTask then
baseTask = necromancyTask
pickedIdx = nil
end
end


if threatLevel == "yellow" and sev == "green" then sev = "yellow" end
-- If this missionary assignment is stationed in Haifan Bassaridia, it may
if threatLevel == "red" and sev ~= "red" then sev = "red" end
-- become a Southern Strait-wide cult-specific operation. This does not move
 
-- the city row; it only expands the operating area described in the task.
return sev
local southernStraitTask = maybeSouthernStraitCultTask(nextInt, cityName, o, prelimSev)
if southernStraitTask then
baseTask = southernStraitTask
pickedIdx = nil
end
end


local function buildCityRow(di, c, econSignals, threatMap, includeThreat, showEcon)
if pickedIdx then used[pickedIdx] = true end
local cityName, cityRegion, tier = c.name, c.region, c.tier
local seedStr = string.format("%d:%03d:%s", di.year, di.dayOfYear, cityName)
local nextInt = lcg(hash31(seedStr))


local profile = cityProfiles[cityName] or {tags={}, weights=nil}
local sev = inferSeverity(c.tier, baseTask.sev, threatUrg)
local tags = {}


for _, t in ipairs(profile.tags or {}) do tags[#tags+1] = t end
-- WX overlay
if RAIL[cityName] then tags[#tags+1] = "rail" end
local opsBadge, wxData = opsCell(cityName, di.year, di.dayOfYear)
if FERRY[cityName] then tags[#tags+1] = "ferry" end
sev = bumpSevWeather(sev, wxData)
local taskText = baseTask.text .. weatherTaskOverlay(wxData)


local sig = econSignals and econSignals[cityName] or nil
-- AZS special case: rare clandestine/assassin operations under severe threat
local dom = dominantSector(sig)
if o.key == "AZS" and shouldAZSGoCovert(nextInt, sev, th) then
local econTag = econTagFromSector(dom)
local covert = pickFromList(nextInt, azsCovertOps, nil)
tags[#tags+1] = econTag
if covert and covert.text then
 
taskText = covert.text .. weatherTaskOverlay(wxData)
local tagSet = buildTagSet(tags)
sev = "red"
 
end
local th = includeThreat and threatMap and threatMap[cityName] or nil
end
local tSev = threatSev(th)
 
local w = baseWeights()
biasFromTier(w, tier)
biasFromEcon(w, econTag)
biasFromCorridor(w, RAIL[cityName], FERRY[cityName])
applyWeights(w, profile.weights)
 
local orderKey = weightedPickKey(nextInt, w)
local o = orderByKey[orderKey] or orders[1]
local task = pickTaskForCity(nextInt, o.tasks, tagSet)
 
local sev = inferSeverity(profile, econTag, task.sev, tSev)


-- unit
local unitName = pickOrgUnit(nextInt, sev)
local unitName = pickOrgUnit(nextInt, sev)
local unitMeta = org[unitName]
local unitMeta = org[unitName]
local unitIndex = (nextInt() % 12) + 1
local unitIndex = (nextInt() % 12) + 1
local unitCode = string.format("%s-%s%d", o.key, unitMeta.code, unitIndex)
local unitCode = string.format("%s-%s%d", o.key, unitMeta.code, unitIndex)
local headcount = unitMeta.size


-- bank note
local bank = pickFromList(nextInt, bankNotes, nil)
local bank = pickFromList(nextInt, bankNotes, nil)


local threatText, threatUrg = "", ""
-- econ
if includeThreat and th then
local econ = ""
threatText = th.event or ""
if showEcon and econSignals and econSignals[cityName] then
threatUrg = th.urg or ""
econ = econSignals[cityName].summary or ""
end
end


Line 1,413: Line 2,049:
style = sevStyle(sev),
style = sevStyle(sev),
date = di.text,
date = di.text,
region = cityRegion,
city = string.format("%s %s", getCityFlagMarkup(cityName), cityLink(cityName)),
city = string.format("%s %s", getCityFlagMarkup(cityName), cityLink(cityName)),
order = getOrderIconMarkup(o.name) .. o.link,
region = c.region,
unit = string.format("%s (%d) [%s]", unitName, headcount, unitCode),
ops = opsBadge,
task = task.text,
order = getCultFlagMarkup(o.name) .. cultLink(o.name),
tags = joinTags(tags),
unit = string.format("%s (%d) [%s]", unitName, unitMeta.size, unitCode),
econ = (showEcon and sig) and econSummary(sig) or "",
task = taskText,
bank = bank,
bank = bank,
threat = threatText,
threat = (includeThreat and th and th.event) and th.event or "",
threatUrg = threatUrg,
threatUrg = (includeThreat and th and th.urg) and th.urg or "",
sev = sev,
sev = sev,
econ = econ,
}
}
end
end


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- 14) MAIN TABLE
-- 18) MAIN TABLE
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
function p.makeTable(frame)
function p.makeTable(frame)
local args = (frame and frame.args) or {}
local args = (frame and frame.args) or {}
local regionFilter = tostring(args.region or "All")
local regionFilter = tostring(args.region or "All")
local showLegend = tostring(args.showlegend or "yes"):lower() ~= "no"
local showLegend = tostring(args.showlegend or "yes"):lower() ~= "no"
local includeThreat = tostring(args.includeThreat or "no"):lower() == "yes"
local includeThreat = tostring(args.includeThreat or "no"):lower() == "yes"
local showEcon = tostring(args.showEcon or "no"):lower() == "yes"
local showEcon = tostring(args.showEcon or "no"):lower() == "yes"
local limit = tonumber(args.limit or "")
local showRelations = tostring(args.showRelations or args.cultRelations or "no"):lower() == "yes"
 
local limit = tonumber(args.limit or "")
local di = getDateInfo()
local di = getDateInfo(args)
local econSignals = getEconSignals()


-- Threat map (only if requested)
local threatMap = {}
local threatMap = {}
if includeThreat then
if includeThreat then
Line 1,447: Line 2,083:
threatMap = parseReligiousEventsTable(rel.makeTable())
threatMap = parseReligiousEventsTable(rel.makeTable())
end
end
end
-- Econ signals (expensive; only if requested)
local econSignals = {}
if showEcon then
econSignals = getEconSignals()
end
end


local out = {}
local out = {}
table.insert(out, "{| class='wikitable sortable'")
table.insert(out, "{| class='wikitable sortable'")
local header = "! style='white-space:nowrap;' | Date !! City !! Region !! Cult Order !! Unit !! Tasking"
local header = "! style='white-space:nowrap;' | Date !! City !! Region !! Ops Alert !! Mystery (Cult) !! Unit !! Tasking"
if showEcon then header = header .. " !! Economic signal" end
if showEcon then header = header .. " !! Economic signal" end
header = header .. " !! Temple Bank Notes"
header = header .. " !! Temple Bank Notes"
Line 1,461: Line 2,103:
for _, c in ipairs(cityOrder) do
for _, c in ipairs(cityOrder) do
if regionPass(regionFilter, c.region) then
if regionPass(regionFilter, c.region) then
local row = buildCityRow(di, c, econSignals, threatMap, includeThreat, showEcon)
local row = buildCityRow(di, c, threatMap, includeThreat, showEcon, econSignals, ctx)
table.insert(out, "|-" .. row.style)
table.insert(out, "|-" .. row.style)
 
local line = string.format("| %s || %s || %s || %s || %s || %s || %s",
local line = string.format("| %s || %s || %s || %s || %s || %s",
row.date, row.city, row.region, row.ops, row.order, row.unit, row.task
row.date, row.city, row.region, row.order, row.unit, row.task
)
)
if showEcon then
if showEcon then line = line .. " || " .. (row.econ ~= "" and row.econ or "n/a") end
line = line .. " || " .. (row.econ ~= "" and row.econ or "n/a")
end
line = line .. " || " .. row.bank
line = line .. " || " .. row.bank
if includeThreat then
if includeThreat then
Line 1,476: Line 2,115:
end
end
line = line .. " || " .. row.sev
line = line .. " || " .. row.sev
table.insert(out, line)
table.insert(out, line)


Line 1,487: Line 2,125:


if showLegend then
if showLegend then
table.insert(out, "\n<div style='margin-top:0.5em; font-size:90%%;'>")
table.insert(out, "\n<div style='margin-top:0.5em; font-size:90%;'>")
table.insert(out, "<b>Legend:</b> ")
table.insert(out, "<b>Legend:</b> ")
table.insert(out, "<span style='background:#d4edda;padding:2px 6px;border:1px solid #c3e6cb;'>Green</span> = Routine rites / civic support&nbsp; ")
table.insert(out, "<span style='background:#d4edda;padding:2px 6px;border:1px solid #c3e6cb;'>Green</span> = Routine rites / civic support&nbsp; ")
table.insert(out, "<span style='background:#fff3cd;padding:2px 6px;border:1px solid #ffeeba;'>Yellow</span> = Heightened oversight / rumor control / targeted intervention&nbsp; ")
table.insert(out, "<span style='background:#fff3cd;padding:2px 6px;border:1px solid #ffeeba;'>Yellow</span> = Heightened oversight / rumor control / targeted intervention&nbsp; ")
table.insert(out, "<span style='background:#f8d7da;padding:2px 6px;border:1px solid #f5c6cb;'>Red</span> = Crisis response / unrest / outbreak / high-risk operations")
table.insert(out, "<span style='background:#f8d7da;padding:2px 6px;border:1px solid #f5c6cb;'>Red</span> = Crisis response / unrest / outbreak / high-risk operations")
table.insert(out, "<br/>Ops Alert badge reflects forecast-driven operational risk and aligns with War League / Hatch posture.")
table.insert(out, "<br/>For overseas deployments please see [[Bassaridian_involvement_in_Corum#Corumian_Missionary_Deployment_Reports|Corumian Missionary Deployment Reports]].")
table.insert(out, "</div>")
table.insert(out, "</div>")
end
end


if di.calendarEvent and di.calendarEvent ~= "" then
if di.calendarEvent and di.calendarEvent ~= "" then
table.insert(out, string.format("\n<div style='margin-top:0.5em; font-size:90%%;'><b>Calendar note:</b> %s</div>", di.calendarEvent))
table.insert(out, string.format("\n<div style='margin-top:0.5em; font-size:90%%;'><b>Calendar note:</b> %s</div>", di.calendarEvent))
end
end
 
if showRelations then
table.insert(out, "\n== Daily cult-relations pressure ==")
table.insert(out, renderCultRelations(args))
end


return table.concat(out, "\n")
return table.concat(out, "\n")
end
end


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- 15) SINGLE CITY VIEW
-- 19) SINGLE CITY VIEW
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
function p.makeCity(frame)
function p.makeCity(frame)
Line 1,510: Line 2,155:
if city == "" then return "''Error: city parameter required.''" end
if city == "" then return "''Error: city parameter required.''" end
if not citySet[city] then return "''Error: unknown city (must match canonical name).''" end
if not citySet[city] then return "''Error: unknown city (must match canonical name).''" end
local includeThreat = tostring(args.includeThreat or "no"):lower() == "yes"
local includeThreat = tostring(args.includeThreat or "no"):lower() == "yes"
local showEcon = tostring(args.showEcon or "no"):lower() == "yes"
local showEcon = tostring(args.showEcon or "no"):lower() == "yes"


local di = getDateInfo()
local di = getDateInfo()
local econSignals = getEconSignals()


local threatMap = {}
local threatMap = {}
Line 1,524: Line 2,167:
end
end
end
end
local econSignals = {}
if showEcon then econSignals = getEconSignals() end


local region, tier = "Core", "minor"
local region, tier = "Core", "minor"
Line 1,530: Line 2,175:
end
end


local row = buildCityRow(di, {name=city, region=region, tier=tier}, econSignals, threatMap, includeThreat, showEcon)
local row = buildCityRow(di, {name=city, region=region, tier=tier}, threatMap, includeThreat, showEcon, econSignals, { usedTasksByCult = {} })
 
local out = {}
local out = {}
table.insert(out, "{| class='wikitable'")
table.insert(out, "{| class='wikitable'")
local header = "! Date !! City !! Region !! Cult Order !! Unit !! Tasking"
local header = "! Date !! City !! Region !! Ops Alert !! Mystery (Cult) !! Unit !! Tasking"
if showEcon then header = header .. " !! Economic signal" end
if showEcon then header = header .. " !! Economic signal" end
header = header .. " !! Temple Bank Notes"
header = header .. " !! Temple Bank Notes"
Line 1,542: Line 2,186:
table.insert(out, "|-" .. row.style)
table.insert(out, "|-" .. row.style)


local line = string.format("| %s || %s || %s || %s || %s || %s",
local line = string.format("| %s || %s || %s || %s || %s || %s || %s",
row.date, row.city, row.region, row.order, row.unit, row.task
row.date, row.city, row.region, row.ops, row.order, row.unit, row.task
)
)
if showEcon then line = line .. " || " .. (row.econ ~= "" and row.econ or "n/a") end
if showEcon then line = line .. " || " .. (row.econ ~= "" and row.econ or "n/a") end
Line 1,553: Line 2,197:
line = line .. " || " .. row.sev
line = line .. " || " .. row.sev
table.insert(out, line)
table.insert(out, line)
table.insert(out, "|}")
table.insert(out, "|}")
return table.concat(out, "\n")
return table.concat(out, "\n")
Line 1,559: Line 2,202:


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- 16) ORDER-ONLY VIEW
-- 20) ORDER-ONLY VIEW
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
function p.makeOrder(frame)
function p.makeOrder(frame)
Line 1,571: Line 2,214:
return "''Error: unknown order name (must match module order names).''"
return "''Error: unknown order name (must match module order names).''"
end
end
local includeThreat = tostring(args.includeThreat or "no"):lower() == "yes"
local includeThreat = tostring(args.includeThreat or "no"):lower() == "yes"
local showEcon = tostring(args.showEcon or "no"):lower() == "yes"
local showEcon = tostring(args.showEcon or "no"):lower() == "yes"


local di = getDateInfo()
local di = getDateInfo()
local econSignals = getEconSignals()


local threatMap = {}
local threatMap = {}
Line 1,585: Line 2,226:
end
end
end
end
local econSignals = {}
if showEcon then econSignals = getEconSignals() end


local out = {}
local out = {}
table.insert(out, "{| class='wikitable sortable'")
table.insert(out, "{| class='wikitable sortable'")
local header = "! Date !! City !! Region !! Unit !! Tasking"
local header = "! Date !! City !! Region !! Ops Alert !! Unit !! Tasking"
if showEcon then header = header .. " !! Economic signal" end
if showEcon then header = header .. " !! Economic signal" end
header = header .. " !! Temple Bank Notes"
header = header .. " !! Temple Bank Notes"
Line 1,596: Line 2,239:


for _, c in ipairs(cityOrder) do
for _, c in ipairs(cityOrder) do
local row = buildCityRow(di, c, econSignals, threatMap, includeThreat, showEcon)
local row = buildCityRow(di, c, threatMap, includeThreat, showEcon, econSignals, { usedTasksByCult = {} })
if row.order:find(wantedOrder.link, 1, true) then
-- Compare by cultLink target (stable) rather than raw text
if row.order:find(cultLink(wantedOrder.name), 1, true) or row.order:find(wantedOrder.name, 1, true) then
table.insert(out, "|-" .. row.style)
table.insert(out, "|-" .. row.style)
local line = string.format("| %s || %s || %s || %s || %s",
local line = string.format("| %s || %s || %s || %s || %s || %s",
row.date, row.city, row.region, row.unit, row.task
row.date, row.city, row.region, row.ops, row.unit, row.task
)
)
if showEcon then line = line .. " || " .. (row.econ ~= "" and row.econ or "n/a") end
if showEcon then
line = line .. " || " .. (row.econ ~= "" and row.econ or "n/a")
end
line = line .. " || " .. row.bank
line = line .. " || " .. row.bank
if includeThreat then
if includeThreat then
Line 1,615: Line 2,261:
table.insert(out, "|}")
table.insert(out, "|}")
return table.concat(out, "\n")
return table.concat(out, "\n")
end
--------------------------------------------------------------------------------
-- 21) CULT RELATIONS PROXY FUNCTIONS
-- These keep [[Module:CultRelationsDailyBVR]] separate, but allow pages to call
-- cult relations through this missionary module when desired.
--------------------------------------------------------------------------------
function p.cultRelations(frame)
if not cultRel or type(cultRel.makeTable) ~= "function" then
return "''[[Module:CultRelationsDailyBVR]] is unavailable.''"
end
local ok, result = pcall(cultRel.makeTable, frame)
if ok then
return result
end
return "''Cult-relations display error: " .. tostring(result) .. "''"
end
function p.cultRelationPair(frame)
if not cultRel or type(cultRel.makePair) ~= "function" then
return "''[[Module:CultRelationsDailyBVR]] is unavailable or does not expose makePair.''"
end
local ok, result = pcall(cultRel.makePair, frame)
if ok then
return result
end
return "''Cult-relation pair display error: " .. tostring(result) .. "''"
end
function p.cultRelationEvents(frame)
if not cultRel or type(cultRel.eventLog) ~= "function" then
return "''[[Module:CultRelationsDailyBVR]] is unavailable or does not expose eventLog.''"
end
local ok, result = pcall(cultRel.eventLog, frame)
if ok then
return result
end
return "''Cult-relations event-log error: " .. tostring(result) .. "''"
end
function p.cultRelationMatrix(frame)
if not cultRel or type(cultRel.makeMatrix) ~= "function" then
return "''[[Module:CultRelationsDailyBVR]] is unavailable or does not expose makeMatrix.''"
end
local ok, result = pcall(cultRel.makeMatrix, frame)
if ok then
return result
end
return "''Cult-relations matrix error: " .. tostring(result) .. "''"
end
end


return p
return p

Latest revision as of 17:59, 3 June 2026

Documentation for this module may be created at Module:MissionaryDailyOpsBVR/doc

-- Module:MissionaryDailyOpsBVR
-- Daily missionary tasking ledger (Temple Bank / Reformed Stripping Path).
--
-- Embedded requirements:
--  1) CITY FLAGS: follow Module:GeneralPortOperations flag-naming pattern, with overrides,
--     BUT any missing flag file falls back to the Imperial Trade Union flag.
--  2) CULT FLAGS (logos): taken directly from Module:GeneralPortOperations addLogo() mappings
--     for ritual cults (and Temple of Aprobelle logo). Missing files fall back to ITU flag.
--  3) CULT LINKS: each cult links to its canonical “Mystery” section target (Divine page section)
--     per Reformed_Stripping_Path#Planetary_Devotion (including the special “The …” headings).
--     Temple Aprobelle links to [[Temple_Aprobelle]] (not a Mystery section).
--  4) OPS ALERT: integrates Module:BassaridiaForecast getOpsData() (badge + condition + overlays).
--  5) Avoid “too many expensive function calls”:
--     - No candidate-search loops.
--     - Flag existence checks are cached and done at most once per unique file used.
--     - General Port content scan is ONLY executed if showEcon=yes.
--
-- Usage:
--   {{#invoke:MissionaryDailyOpsBVR|makeTable}}
--   {{#invoke:MissionaryDailyOpsBVR|makeTable|region=Core}}
--   {{#invoke:MissionaryDailyOpsBVR|makeTable|includeThreat=yes}}
--   {{#invoke:MissionaryDailyOpsBVR|makeTable|showEcon=yes}}
--   {{#invoke:MissionaryDailyOpsBVR|makeCity|city=Vaeringheim}}
--   {{#invoke:MissionaryDailyOpsBVR|makeOrder|order=Temple Aprobelle}}

local p = {}

local cal = require('Module:BassaridianCalendar')
local DAYS_IN_YEAR = 183

--------------------------------------------------------------------------------
-- 0.1) DAILY CULT RELATIONS BRIDGE
-- This keeps the main relations logic in [[Module:CultRelationsDailyBVR]],
-- but lets this missionary module display it when requested.
--------------------------------------------------------------------------------

local cultRelOk, cultRel = pcall(require, "Module:CultRelationsDailyBVR")
if not cultRelOk then
	cultRel = nil
end

local function renderCultRelations(args)
	args = args or {}

	if not cultRel or type(cultRel.makeTable) ~= "function" then
		return "<div style='border:1px solid #aaa;background:#fff3cd;padding:6px;margin-top:0.5em;'>" ..
			"''Daily cult-relations module unavailable. Make sure [[Module:CultRelationsDailyBVR]] exists.''" ..
			"</div>"
	end

	local relArgs = {
		year = args.year or args.psscyear,
		day = args.day or args.dayofyear,
		limit = args.relationsLimit or args.rellimit or "20",
		events = args.relationEvents or "5",
		showEvents = args.showRelationEvents or "yes",
		compact = args.relationsCompact or "no"
	}

	local ok, result = pcall(cultRel.makeTable, { args = relArgs })

	if ok then
		return result
	end

	return "<div style='border:1px solid #aaa;background:#f8d7da;padding:6px;margin-top:0.5em;'>" ..
		"''Daily cult-relations error: " .. tostring(result) .. "''" ..
		"</div>"
end

--------------------------------------------------------------------------------
-- 0) BASIC UTILS
--------------------------------------------------------------------------------
local function trim(s)
	s = tostring(s or "")
	return (s:gsub("^%s+", ""):gsub("%s+$", ""))
end

local function normalizeSpaces(s)
	return (s or ""):gsub("[%s\u00A0]+", " ")
end

local function stripWiki(s)
	s = tostring(s or "")
	s = s:gsub("%{%b{}%}", "")
	s = s:gsub("%[%[[^%]|]+|([^%]]+)%]%]", "%1")
	s = s:gsub("%[%[([^%]]+)%]%]", "%1")
	s = s:gsub("'''''", ""):gsub("'''", ""):gsub("''", "")
	return trim(s)
end

--------------------------------------------------------------------------------
-- 1) CALENDAR PARSING
--------------------------------------------------------------------------------
local function parseCalendarString(s)
	if type(s) ~= "string" then return nil end
	local dayOfYear, month, year = s:match("^(%d+),%s+([%a]+)%s+%b()%s*,%s*(%d+)%s+PSSC")
	if dayOfYear and year then
		return tonumber(year), tonumber(dayOfYear), month
	end
	return nil
end

local function extractEventText(s)
	if type(s) ~= "string" then return "" end
	local ev = s:match("PSSC%s+–%s+(.-)%s+–%s+Proverb:")
	return ev or ""
end

local function doyToMonthDom(doy)
	if doy <= 61 then
		return 1, doy
	elseif doy <= 122 then
		return 2, doy - 61
	else
		return 3, doy - 122
	end
end

local function getDateInfo(args)
	args = args or {}

	local overrideYear = tonumber(args.year or args.psscyear)
	local overrideDay  = tonumber(args.day or args.dayofyear)

	if overrideYear and overrideDay then
		if overrideDay < 1 then overrideDay = 1 end
		if overrideDay > DAYS_IN_YEAR then overrideDay = DAYS_IN_YEAR end

		local m, dom = doyToMonthDom(overrideDay)
		return {
			year = overrideYear,
			dayOfYear = overrideDay,
			monthNum = m,
			dom = dom,
			text = string.format("%d/%d/%d PSSC", dom, m, overrideYear),
			raw = string.format("%d/%d/%d PSSC", dom, m, overrideYear),
			calendarEvent = "",
		}
	end

	local raw = cal.getCurrentDate()
	local y, doy = parseCalendarString(raw)
	if not y or not doy then
		y, doy = 0, 1
		raw = tostring(raw or "")
	end
	local m, dom = doyToMonthDom(doy)
	return {
		year = y,
		dayOfYear = doy,
		monthNum = m,
		dom = dom,
		text = string.format("%d/%d/%d PSSC", dom, m, y),
		raw = raw,
		calendarEvent = extractEventText(raw),
	}
end

--------------------------------------------------------------------------------
-- 2) DETERMINISTIC RNG
--------------------------------------------------------------------------------
local function hash31(str)
	str = tostring(str or "")
	local h = 0
	for i = 1, #str do
		h = (h * 131 + str:byte(i)) % 2147483647
	end
	return h
end

local function lcg(seed)
	local s = seed % 2147483647
	if s <= 0 then s = s + 2147483646 end
	return function()
		s = (1103515245 * s + 12345) % 2147483647
		return s
	end
end

local function pickFromList(nextInt, list, avoidValue)
	if not list or #list == 0 then return nil end
	if #list == 1 then return list[1] end
	local tries = 0
	while tries < 12 do
		local idx = (nextInt() % #list) + 1
		local v = list[idx]
		if v ~= avoidValue then return v end
		tries = tries + 1
	end
	for _, v in ipairs(list) do
		if v ~= avoidValue then return v end
	end
	return list[1]
end

local function pickWeighted(nextInt, weighted)
	local total = 0
	for _, it in ipairs(weighted) do total = total + (it.w or 0) end
	if total <= 0 then return weighted[1] end
	local r = nextInt() % total
	local acc = 0
	for _, it in ipairs(weighted) do
		acc = acc + (it.w or 0)
		if r < acc then return it end
	end
	return weighted[#weighted]
end

--------------------------------------------------------------------------------
-- 3) EXPENSIVE-CALL CONTROL: cached file existence checks (1 per unique file)
--------------------------------------------------------------------------------
local fileExistsCache = {}
local function fileExists(fileName)
	fileName = tostring(fileName or "")
	if fileName == "" then return false end
	if fileExistsCache[fileName] ~= nil then return fileExistsCache[fileName] end
	local t = mw.title.new("File:" .. fileName)
	local ok = (t and t.exists) or false
	fileExistsCache[fileName] = ok
	return ok
end

--------------------------------------------------------------------------------
-- 4) STYLE / SEVERITY
--------------------------------------------------------------------------------
local function sevStyle(sev)
	if sev == "red" then
		return ' style="background-color:#f8d7da;"'
	elseif sev == "yellow" then
		return ' style="background-color:#fff3cd;"'
	else
		return ' style="background-color:#d4edda;"'
	end
end

local function urgencyScore(u)
	u = tostring(u or ""):upper()
	if u == "CRITICAL" then return 4 end
	if u == "HIGH"     then return 3 end
	if u == "MEDIUM"   then return 2 end
	if u == "LOW"      then return 1 end
	return 0
end

--------------------------------------------------------------------------------
-- 5) CITY NETWORK
--------------------------------------------------------------------------------
local function cityLink(name)
	return string.format("[[List_of_cities_in_Bassaridia_Vaeringheim#%s|%s]]", name, name)
end

local cityOrder = {
	-- Core majors
	{name="Vaeringheim",      region="Core", tier="major"},
	{name="Luminaria",        region="Core", tier="major"},
	{name="Serena",           region="Core", tier="major"},
	{name="Pyralis",          region="Core", tier="major"},
	{name="Symphonara",       region="Core", tier="major"},
	{name="Aurelia",          region="Core", tier="major"},
	{name="Somniumpolis",     region="Core", tier="major"},
	{name="Nexa",             region="Core", tier="major"},
	{name="Lunalis Sancta",   region="Core", tier="major"},
	{name="Sylvapolis",       region="Core", tier="major"},
	-- Core minors
	{name="Saluria",          region="Core", tier="minor"},
	{name="Aetherium",        region="Core", tier="minor"},
	{name="Ferrum Citadel",   region="Core", tier="minor"},
	{name="Acheron",          region="Core", tier="minor"},
	{name="Erythros",         region="Core", tier="minor"},
	{name="Catonis Atrium",   region="Core", tier="minor"},
	{name="Delphica",         region="Core", tier="minor"},
	{name="Koinonía",         region="Core", tier="minor"},
	{name="Aureum",           region="Core", tier="minor"},
	-- Alperkin
	{name="The Alpazkigz",    region="Alperkin", tier="minor"},
	{name="Odiferia",         region="Alperkin", tier="minor"},
	-- New South Jangsong
	{name="Skýrophos",        region="New South Jangsong", tier="major"},
	{name="Bjornopolis",      region="New South Jangsong", tier="major"},
	{name="Aegirheim",        region="New South Jangsong", tier="major"},
	{name="Norsolyra",        region="New South Jangsong", tier="major"},
	{name="Thorsalon",        region="New South Jangsong", tier="major"},
	{name="Pelagia",          region="New South Jangsong", tier="minor"},
	{name="Myrene",           region="New South Jangsong", tier="minor"},
	{name="Thyrea",           region="New South Jangsong", tier="minor"},
	{name="Ephyra",           region="New South Jangsong", tier="minor"},
	{name="Halicarn",         region="New South Jangsong", tier="minor"},
	-- Haifan Bassaridia
	{name="Keybir-Aviv",      region="Haifan Bassaridia", tier="major"},
	{name="Tel-Amin",         region="Haifan Bassaridia", tier="major"},
	{name="Diamandis",        region="Haifan Bassaridia", tier="major"},
	{name="Jogi",             region="Haifan Bassaridia", tier="major"},
	{name="Lewisburg",        region="Haifan Bassaridia", tier="major"},
	{name="Thermosalem",      region="Haifan Bassaridia", tier="minor"},
	{name="Akróstadium",      region="Haifan Bassaridia", tier="minor"},
	{name="Sufriya",          region="Haifan Bassaridia", tier="minor"},
	{name="Lykopolis",        region="Haifan Bassaridia", tier="minor"},
	-- Normark
	{name="Ardclach",         region="Normark", tier="major"},
	{name="Riddersborg",      region="Normark", tier="major"},
	-- Ouriana
	{name="Bashkim",          region="Ouriana", tier="major"},
	{name="Ourid",            region="Ouriana", tier="major"},
	{name="Tonar",            region="Ouriana", tier="minor"},
-- Caledonia
{name="Fanghorn",         region="Caledonia", tier="major"},
{name="Eikbu",            region="Caledonia", tier="major"},
{name="Galvø",            region="Caledonia", tier="major"},
{name="Hamarrfell",       region="Caledonia", tier="major"},
{name="Sårensby",         region="Caledonia", tier="minor"},
{name="Slevik",           region="Caledonia", tier="minor"},
{name="Sjøsborg",         region="Caledonia", tier="minor"},
{name="Storesund",        region="Caledonia", tier="minor"},
{name="Notranskja",       region="Caledonia", tier="minor"},
{name="Kaledonija",       region="Caledonia", tier="minor"},
{name="Krlsgorod",        region="Caledonia", tier="minor"},
{name="Skøda",            region="Caledonia", tier="minor"},
}

local citySet = {}
local ctx = { usedTasksByCult = {} }
for _, c in ipairs(cityOrder) do citySet[c.name] = true end

--------------------------------------------------------------------------------
-- 5.0.1) HAIFAN BASSARIDIA / SOUTHERN STRAIT OPERATING ZONE
--
-- This does NOT lock missionary cults to these cities.
-- It only allows special Southern Strait tasking when the generated station
-- is one of the Haifan Bassaridian cities.
--------------------------------------------------------------------------------
local haifanBassaridiaCities = {
	["Keybir-Aviv"] = true,
	["Tel-Amin"] = true,
	["Diamandis"] = true,
	["Jogi"] = true,
	["Lewisburg"] = true,
	["Thermosalem"] = true,
	["Akróstadium"] = true,
	["Sufriya"] = true,
	["Lykopolis"] = true,
}

local function isHaifanBassaridiaCity(cityName)
	return haifanBassaridiaCities[tostring(cityName or "")] == true
end

local function regionPass(filter, cityRegion)
	filter = tostring(filter or "All")
	if filter == "" or filter == "All" then return true end
	if filter == "Core" then return cityRegion == "Core" end
	if filter == "NSJ" then return cityRegion == "New South Jangsong" end
	if filter == "Haifan" then return cityRegion == "Haifan Bassaridia" end
	if filter == "Caledonian" then return cityRegion == "Caledonia" end
	return cityRegion == filter
end

--------------------------------------------------------------------------------
-- 5.1) CITY FLAGS (GeneralPortOperations style + fallback-to-ITU if missing)
--------------------------------------------------------------------------------
local ITU_FLAG = "Imperial Trade Union flag.png"

local function normCityForFlag(name)
	-- Exactly the normalization used in Module:GeneralPortOperations
	return (name or "")
		:gsub("[%s%-’]", "")
		:gsub("[Áá]", "A"):gsub("[Éé]", "E")
		:gsub("[Íí]", "I"):gsub("[Óó]", "O")
		:gsub("[Úú]", "U"):gsub("[Ýý]", "Y")
end

local cityFlagOverrides = {
	["Skýrophos"] = "SkyrophosFlag.png", -- override used in GeneralPortOperations
	["Slevik"]    = "SlevikFlag.png.png", -- known oddity you’ve used elsewhere
	["Sårensby"]  = "SårensbyFlag.png",   -- known to exist in your notes
}

local cityFlagMarkupCache = {}
local function getCityFlagMarkup(cityName)
	if cityFlagMarkupCache[cityName] then return cityFlagMarkupCache[cityName] end
	local candidate = cityFlagOverrides[cityName] or (normCityForFlag(cityName) .. "Flag.png")
	local file = fileExists(candidate) and candidate or ITU_FLAG
	local mk = string.format("[[File:%s|20px]]", file)
	cityFlagMarkupCache[cityName] = mk
	return mk
end

--------------------------------------------------------------------------------
-- 6) CULT FLAGS (logos) from Module:GeneralPortOperations addLogo() + fallback
--------------------------------------------------------------------------------
local cultLogoFile = {
	-- Ritual cult logos from Module:GeneralPortOperations
	["Order of Aurora Mystica"]         = "Auroramystica.png",
	["Harmony Sanctum"]                = "Harmonysanctum.png",
	["Ignis Aeternum"]                 = "Ignis Aeternum.png",
	["Celestial Harmony Sect"]         = "HarmonySect.png",
	["Guild of Golden Shadows"]        = "GoldenShadows.png",
	["Azure Sentinel Sect"]            = "AzureSentinel.png",
	["Reverie Nebulous"]               = "RevNeb.png",
	["Eon Fellowship"]                 = "EonFel.png",
	["Order of the Umbral Oracle"]     = "Umbral.png",
	["Mystery of the Verdant Embrace"] = "Verdant.png",
	["Conclave Illuminara"]            = "Conclave.png",
	["Sanctum Vitalis"]                = "Vitalis.png",
	["Temple Alabaster"]               = "Alabaster.png",
	["Court of the Ironclad"]          = "Ironclad.png",
	["Accord Concordia"]               = "Concordia.png",
	["Mystery of the Stygian Veil"]    = "StygianVe.png",
	["Sylvan Fellowship"]              = "Sylvan.png",
	["Mystery of Red Mirth"]           = "MirthFlag.png",
	["Rex Catonis"]                    = "Catonis.png",
	["Sanctum Delphica"]               = "SanctumDel.png",
	["Ordo Amicitia"]                  = "OrdoFl.png",
	["Temple Illuminata"]              = "AureumFlag.png",
	-- Temple Aprobelle logo is mapped in GeneralPortOperations under serviceCompanies name:
	["Temple Aprobelle"]               = "TempleAprobelleLogo.png",
	["Temple of Aprobelle"]            = "TempleAprobelleLogo.png",
}

local cultFlagMarkupCache = {}
local function getCultFlagMarkup(cultName)
	if cultFlagMarkupCache[cultName] then return cultFlagMarkupCache[cultName] end
	local candidate = cultLogoFile[cultName]
	local file = (candidate and fileExists(candidate)) and candidate or ITU_FLAG
	local mk = string.format("[[File:%s|18px]] ", file)
	cultFlagMarkupCache[cultName] = mk
	return mk
end

--------------------------------------------------------------------------------
-- 7) CULT LINKS (canonical “Mystery” section targets)
--------------------------------------------------------------------------------
local cultTargets = {
	["Order of Aurora Mystica"]         = { page="Eos_(Reformed_Stripping_Path)",      anchor="Order_of_Aurora_Mystica" },
	["Harmony Sanctum"]                = { page="Micras_(Reformed_Stripping_Path)",   anchor="Harmony_Sanctum" },
	["Ignis Aeternum"]                 = { page="Pyros_(Reformed_Stripping_Path)",    anchor="Ignis_Aeternum" },
	-- Special headings on those pages include “The …”
	["Celestial Harmony Sect"]         = { page="Indigo_(Reformed_Stripping_Path)",   anchor="The_Celestial_Harmony_Sect" },
	["Guild of Golden Shadows"]        = { page="Chrysos_(Reformed_Stripping_Path)",  anchor="The_Guild_of_Golden_Shadows" },
	["Azure Sentinel Sect"]            = { page="Thalassa_(Reformed_Stripping_Path)", anchor="Azure_Sentinel_Sect" },
	["Reverie Nebulous"]               = { page="Nephele_(Reformed_Stripping_Path)",  anchor="Reverie_Nebulous" },
	["Eon Fellowship"]                 = { page="Glinos_(Reformed_Stripping_Path)",   anchor="Eon_Fellowship" },
	["Order of the Umbral Oracle"]     = { page="Noctis_(Reformed_Stripping_Path)",   anchor="Order_of_the_Umbral_Oracle" },
	["Mystery of the Verdant Embrace"] = { page="Opsithe_(Reformed_Stripping_Path)",  anchor="Mystery_of_the_Verdant_Embrace" },
	["Conclave Illuminara"]            = { page="Tarsica_(Reformed_Stripping_Path)",  anchor="Conclave_Illuminara" },
	["Sanctum Vitalis"]                = { page="Plateau_(Reformed_Stripping_Path)",  anchor="Sanctum_Vitalis" },
	["Temple Alabaster"]               = { page="Ivory_(Reformed_Stripping_Path)",    anchor="Temple_Alabaster" },
	["Court of the Ironclad"]          = { page="Fenrir_(Reformed_Stripping_Path)",   anchor="Court_of_the_Ironclad" },
	["Accord Concordia"]               = { page="Lukedu_(Reformed_Stripping_Path)",   anchor="Accord_Concordia" },
	["Mystery of the Stygian Veil"]    = { page="Styx_(Reformed_Stripping_Path)",     anchor="Mystery_of_the_Stygian_Veil" },
	["Sylvan Fellowship"]              = { page="Faun_(Reformed_Stripping_Path)",     anchor="Sylvan_Fellowship" },
	["Mystery of Red Mirth"]           = { page="Silenus_(Reformed_Stripping_Path)",  anchor="Mystery_of_Red_Mirth" },
	["Rex Catonis"]                    = { page="Cato_(Reformed_Stripping_Path)",     anchor="Rex_Catonis" },
	["Sanctum Delphica"]               = { page="Sisera_(Reformed_Stripping_Path)",   anchor="Sanctum_Delphica" },
	["Ordo Amicitia"]                  = { page="Erasmus_(Reformed_Stripping_Path)",  anchor="Ordo_Amicitia" },
	["Temple Illuminata"]              = { page="Nikolaj_(Reformed_Stripping_Path)",  anchor="Temple_Illuminata" },
}

local function cultLink(cultName)
	if cultName == "Temple Aprobelle" or cultName == "Temple of Aprobelle" then
		return "[[Temple_Aprobelle|Temple Aprobelle]]"
	end
	local t = cultTargets[cultName]
	if not t then
		return string.format("[[%s]]", cultName)
	end
	return string.format("[[%s#%s|%s]]", t.page, t.anchor, cultName)
end

--------------------------------------------------------------------------------
-- 8) ORG UNITS (Missionary echelons)
--------------------------------------------------------------------------------
local org = {
	Lampade     = { code="L", size=8  },
	Hetairoi    = { code="H", size=12 },
	Dodekade    = { code="D", size=18 },
	Kleisthenes = { code="K", size=25 },
	Stoa        = { code="S", size=40 },
	Thiasos     = { code="T", size=60 },
}

local function pickOrgUnit(nextInt, sev)
	if sev == "green" then
		return pickFromList(nextInt, {"Lampade","Hetairoi"}, nil)
	elseif sev == "yellow" then
		return pickFromList(nextInt, {"Hetairoi","Dodekade","Kleisthenes"}, nil)
	else
		return pickFromList(nextInt, {"Kleisthenes","Stoa","Thiasos"}, nil)
	end
end

--------------------------------------------------------------------------------
-- 9) OPS ALERT (Module:BassaridiaForecast)
--------------------------------------------------------------------------------
local wxOk, wx = pcall(require, "Module:BassaridiaForecast")
if not wxOk then wx = nil end

local function wxColor(level)
	level = tonumber(level) or 0
	if level == 3 then return "#f8d7da" end
	if level == 2 then return "#ffe5cc" end
	if level == 1 then return "#fff3cd" end
	return "#d4edda"
end

local function opsCell(cityName, year, dayOfYear)
	if not wx or type(wx.getOpsData) ~= "function" then return "n/a", nil end
	local op = wx.getOpsData(cityName, year, dayOfYear)
	if not op or not op.alert then return "n/a", nil end
	local lvl   = tonumber(op.alert.level) or 0
	local label = tostring(op.alert.label or "Green")
	local cond  = tostring(op.condition or "")
	local bg    = wxColor(lvl)

	local cell = string.format(
		"<span style='background:%s;padding:2px 6px;border:1px solid #ccc;border-radius:10px;white-space:nowrap;'>%s</span>%s",
		bg, label, (cond ~= "" and ("<br/><span style='font-size:90%;'>" .. cond .. "</span>") or "")
	)
	return cell, op
end

local function bumpSevWeather(sev, op)
	if not op or not op.alert then return sev end
	local lvl = tonumber(op.alert.level) or 0
	local imp = op.impacts or {}

	if lvl == 2 and sev == "green" then sev = "yellow" end
	if lvl >= 3 then sev = "red" end

	if (imp.ground == "Hazard" or imp.sea == "Closed" or imp.air == "No-Go") and sev == "green" then
		sev = "yellow"
	end
	return sev
end

local function weatherTaskOverlay(op)
	if not op or not op.alert then return "" end
	local lvl = tonumber(op.alert.level) or 0
	if lvl < 2 then return "" end -- Orange/Red only

	local haz = tostring(op.alert.hazard or "weather disruption")
	local imp = op.impacts or {}
	local cond = tostring(op.condition or "")
	local base = " Weather-driven tasking: " .. haz .. " (" ..
		string.format("Gnd %s / Sea %s / Air %s", imp.ground or "OK", imp.sea or "OK", imp.air or "OK") .. ")."

	if cond == "Storm" or cond == "Rain" then
		return base .. " Clinic/relief queues stabilized; rumor bulletins posted; shrine-district lanes marked."
	elseif cond == "Blizzard" or cond == "Snow" then
		return base .. " Warming-shelter checks; fuel/medicine custody verified; travel guidance posted."
	elseif cond == "Fog" then
		return base .. " Navigation warnings and crowd guidance in port/canal corridors; panic narratives suppressed."
	elseif cond == "Dust" then
		return base .. " Mask discipline and vendor hygiene enforcement prioritized; indoor refuge advisories posted."
	else
		return base .. " Welfare checks and public messaging prioritized."
	end
end

--------------------------------------------------------------------------------
-- 10) OPTIONAL: THREAT WATCH (Module:ReligiousEvents)
--------------------------------------------------------------------------------
local function parseReligiousEventsTable(wikitext)
	local map = {}
	wikitext = tostring(wikitext or "")
	for line in wikitext:gmatch("[^\n]+") do
		if line:match("^|%s") and not line:match("^|%-%s*$") then
			local parts = mw.text.split(line:gsub("^|%s*", ""), "%s*||%s*")
			if #parts >= 4 then
				local city = stripWiki(parts[2])
				local ev = stripWiki(parts[3])
				local urg = stripWiki(parts[4])
				if city ~= "" and ev ~= "" then
					map[city] = {event=ev, urg=urg}
				end
			end
		end
	end
	return map
end

--------------------------------------------------------------------------------
-- 11) OPTIONAL: ECON SIGNALS (EXPENSIVE; ONLY if showEcon=yes)
--------------------------------------------------------------------------------
local function getEconSignals()
	local title = mw.title.new("General Port of Lake Morovia")
	if not title then return {} end
	local ok, content = pcall(function() return title:getContent() end)
	if not ok or not content or content == "" then return {} end
	local signals = {}
	local text = normalizeSpaces(content)

	for _, c in ipairs(cityOrder) do
		local n = c.name
		local hits = 0
		local pattern = n:gsub("([%^%$%(%)%%%.%[%]%*%+%-%?])","%%%1")
		for _ in text:gmatch(pattern) do
			hits = hits + 1
			if hits >= 6 then break end
		end
		if hits >= 3 then
			signals[n] = {summary=string.format("High Port linkage (mentions: %d)", hits)}
		end
	end
	return signals
end

--------------------------------------------------------------------------------
-- 12) TASK HELPERS
--------------------------------------------------------------------------------
local function T(text, sev) return {text=text, sev=sev} end

-- 10 tasks per cult (kept compact so the module remains maintainable)
local tasksByCult = {

  ["Order of Aurora Mystica"] = {
    T("First-light conciliation at a guild hall; signatories recorded with Temple Bank clerks; follow-up meeting scheduled.","green"),
    T("Sunrise clarity bulletin: correct ‘official omen’ rumors before queues form; speakers briefed on approved language.","green"),
    T("Dawn home-visit circuit for anxious households; practical referrals routed through shrine stewards.","green"),
    T("Procession lane rehearsal for a morning rite; crush points marked; stewards drilled on staggered entry.","yellow"),
    T("Targeted pamphlet sweep: remove forged ‘dawn decree’ leaflets; source stalls logged for follow-up.","yellow"),
    T("Joint dawn patrol with civic stewards on canal/rail approaches; lighting faults logged; intimidation reports collected.","yellow"),
    T("Witness-support escort at daybreak: safe path confirmed; hostile crowd avoidance plan posted.","yellow"),
    T("Emergency dawn ceasefire convened after overnight clashes; terms read publicly; retaliation pledges collected.","red"),
    T("Crisis dawn-vigil stabilization after a major scare; grief care + calm stations opened; clinic corridors protected.","red"),
    T("High-risk dawn convoy escort for sacred/custody items; route denial and decoy timing implemented.","red"),
    -- +8 added
    T("Hilltop shrine schedule reset for first-light hymns; latecomer lanes separated; attendance log updated.","green"),
    T("Dawn-worksite blessing for shift crews; safety reminders appended; foreman sign-off obtained.","green"),
    T("Morning-lamp distribution and repair tickets; dark alleys flagged; follow-up patrol time posted.","green"),
    T("Sunrise complaint intake booth at market edge; triage tags assigned; referrals routed to Harmony Sanctum.","yellow"),
    T("Counter-badge check on ‘Aurora stewards’; impostors documented; credentials verified with Temple Bank.","yellow"),
    T("Dawn festival route reroute after hazard report; new signage posted; crowd cadence rehearsed.","yellow"),
    T("Red-posture dawn broadcast: scripted calm loop; speaker rotations scheduled; rumor nodes targeted for removal.","red"),
    T("High-risk witness staging at daybreak: staggered arrivals; decoy meeting point; custody signatures secured.","red"),
  },

  ["Harmony Sanctum"] = {
    T("Neighborhood mediation circle: settle a vendor–labor dispute; written concord filed.","green"),
    T("Public etiquette coaching at shrine square; queue discipline and nonviolent refusal scripts practiced.","green"),
    T("Household conflict visits after complaint spike; behavior plan issued; check-in date set.","green"),
    T("Festival crowd-lane plan review; bottlenecks marked; stewards assigned to separation corridors.","yellow"),
    T("Rumor triage: map source nodes feeding panic; corrective bulletin cycle deployed.","yellow"),
    T("Transit choke-point calming patrol; commuter separation corridor opened; escalation triggers documented.","yellow"),
    T("Clerk oversight coaching after audit flags; corrective training delivered; sign-off obtained.","yellow"),
    T("Emergency stabilization during riot flare; dispersal lanes held; care stations deployed at safe corners.","red"),
    T("Night calm-litany enforcement after a panic episode; supervised dispersal and reunification tent opened.","red"),
    T("Crisis reconciliation hearing after violence; restitution ledger initiated; witness protection arranged.","red"),
    -- +8 added
    T("Balance-rite for a disputed property line; boundary walk performed; neutral witnesses recorded.","green"),
    T("Structured apology conference after school conflict; restitution terms set; guardian contacts logged.","green"),
    T("Neighborhood rotation plan for shared resources; schedule posted; conflict triggers documented.","green"),
    T("Inter-ward speaking forum moderated; turn order enforced; heckler removal protocol posted.","yellow"),
    T("Counter-rumor ‘myth vs policy’ bulletin; distribution assigned by street; speaker script packet issued.","yellow"),
    T("Market-stall spacing enforcement after crush near fountain; lane widths measured; re-layout posted.","yellow"),
    T("Emergency anti-reprisal pact after stabbing; escort corridors established; no-retaliation oaths collected.","red"),
    T("Mass grievance intake after unrest; testimony queue metered; witness shielding arranged; outcomes logged.","red"),
  },

  ["Ignis Aeternum"] = {
    T("Forge discipline rite: safety roles assigned; firewatch roster posted; tool control verified.","green"),
    T("Heat advisory posting for labor crews; hydration discipline enforced; shade points designated.","green"),
    T("Brazier and lantern inspection at a festival lane; safe distances marked; fuel rules posted.","green"),
    T("Brushfire-risk patrol: illegal burns curtailed; emergency water points labeled; reporting chain rehearsed.","yellow"),
    T("Arson-scare containment: inspect fuel stores and service corridors; contractors re-badged; locks audited.","yellow"),
    T("Rumor suppression after ‘cursed flame’ claims; sanctioned narrative posted; agitator list started.","yellow"),
    T("Kiln/oven quarter audit after sabotage chatter; witness statements logged; repair tickets issued.","yellow"),
    T("Emergency response to a flare incident: evacuation lanes opened; burn triage support and calm messaging deployed.","red"),
    T("Crisis crowd control at a pyre/procession pinch point; stampede pressure relieved; reunification tent opened.","red"),
    T("High-risk perimeter posting at depots after credible arson threat; entry controlled; night watch rotated.","red"),
    -- +8 added
    T("Workshop ventilation check; soot exposure warnings posted; compliance sign-offs collected.","green"),
    T("Approved fuel rationing note posted for braziers; prevent hoarding; vendor guidance issued.","green"),
    T("Tool-blessing and inventory day; missing items reconciled; suspect list deferred to Golden Shadows.","green"),
    T("Firebreak drill coordination near warehouses; muster lanes marked; timing rehearsed with stewards.","yellow"),
    T("Controlled relight protocol after blackout; ignition permits checked; unsafe relights stopped.","yellow"),
    T("Inspection sweep for illegal accelerants; confiscations logged; storage seals verified.","yellow"),
    T("Major blaze containment support: perimeter discipline; water point control; after-action safety decree issued.","red"),
    T("Arson-ring disruption support: suspect workshops sealed; witness protection arranged; public guidance broadcast.","red"),
  },

  ["Celestial Harmony Sect"] = {
    T("Cosmic-order lecture at shrine school; reduce anxiety about sky-signs; practical safety guidance appended.","green"),
    T("Guided breathing session for canal workers; stress reduction paired with shift-safety reminder.","green"),
    T("Calendar correction bulletin: prevent hoarding after false eclipse/omen claims; approved text distributed.","green"),
    T("Quiet-zone enforcement near hospitals and shelters; regulated chanting and crowd spacing verified.","yellow"),
    T("Counterfeit star-chart inquiry: vendor interviews; seizure log compiled; correlations forwarded.","yellow"),
    T("Fog-night guidance patrol: speakers briefed; route markers placed; panic triggers documented.","yellow"),
    T("Post-event debrief: update approved narratives; distribute scripts to shrine speakers; archive record filed.","yellow"),
    T("Crisis counseling during mass scare; supervised calm stations opened; clinic access protected.","red"),
    T("Emergency embedded messaging team with civic stewards during unrest; prevent retaliatory panic; crowd lanes held.","red"),
    T("High-risk omen-driven gathering dispersed under controlled cadence; separation corridors opened; arrests avoided where possible.","red"),
    -- +8 added
    T("Twin-balance mediation between rival guild blocs; alternating speaking order enforced; compromise text drafted.","green"),
    T("Opposites-pair workshop for stewards; role swapping practiced; conflict sensitivity notes filed.","green"),
    T("Port/market ‘cycle briefing’: scarcity and surplus explained; calm purchasing guidance posted.","green"),
    T("Symbol dispute review (Indigo/Momiji misuse); offending banners removed; replacement signage approved.","yellow"),
    T("Diplomatic liaison support for visiting delegations; translation scripts issued; etiquette rules posted.","yellow"),
    T("Coordinated counter-panic cadence: staggered announcements; prevent feedback loops; rumor nodes mapped.","yellow"),
    T("Emergency unity convocation after sectarian flare; ceasefire terms read; neutral escorts assigned.","red"),
    T("Mass ominal panic response: structured dispersal waves; calm stations staffed; clinic routes protected.","red"),
  },

  ["Guild of Golden Shadows"] = {
    T("Quiet intake: log forged vouchers and coercion attempts; route evidence to Temple Bank without public escalation.","green"),
    T("Market observation rotation; identify counterfeit seals and pressure networks; ledger packet assembled.","green"),
    T("Discrete tracing of a forged permit chain; map printer stalls and broker intermediaries; file correlation report.","green"),
    T("Undercover purchase of fake shrine/custody seals; seize plates and ledgers; custody memo prepared.","yellow"),
    T("Intercept suspicious manifest and cross-check corridor registry for evasions; flag repeats for inspection teams.","yellow"),
    T("Witness-support arrangement for a threatened vendor; relocation and stipend bridge authorized.","yellow"),
    T("Rumor suppression operation: remove false broadsheets; replace with official bulletin; source nodes catalogued.","yellow"),
    T("Night extraction of compromised informant; safe route screened; temporary housing arranged under seal.","red"),
    T("Emergency counter-coercion action during crowd surge; stabilize lanes; evidence chain protected.","red"),
    T("High-risk liaison brief to War League contact: attribution summary + prevention plan; sealed addendum filed.","red"),
    -- +8 added
    T("Silent audit of a clerk’s ledger drawer; discrepancy list sealed; retraining notice drafted.","green"),
    T("Courier-route verification for stipend packets; handoff witnesses arranged; route hazards mapped.","green"),
    T("Counterfeit stamp reference kit issued to stewards; detection steps taught; escalation chain posted.","green"),
    T("Targeted sweep for extortion collectors; victim intake protected; suspect descriptions logged.","yellow"),
    T("Dockside inventory double-check after ‘lost crate’ claim; reconciliation performed; fraud flags filed.","yellow"),
    T("Controlled public correction of a forged notice; replace postings; photograph originals; printer path traced.","yellow"),
    T("Major embezzlement exposure: emergency ledger seizure; suspect detention support; restitution memo prepared.","red"),
    T("High-risk port gate fraud surge: inspection teams reinforced; entry metering; evidence chain preserved.","red"),
  },

  ["Azure Sentinel Sect"] = {
    T("Waterfront perimeter walk; safety rope checks along canal districts; hazards logged for civil works.","green"),
    T("Storm-drain inspection with engineers; blockages cleared; maintenance tickets filed.","green"),
    T("Port-house briefing: embarkation discipline, theft prevention, and safe-lane etiquette posted.","green"),
    T("Medical-supply escort across bridges; accountability signatures collected; chain-of-custody verified.","yellow"),
    T("Harbor rumor correction: suppress false quarantine/closure claims; prevent stampede behavior at gates.","yellow"),
    T("SAR standby after storm warnings; muster roster checked; equipment count verified.","yellow"),
    T("Debris clearance coordination after gale; hazard markings placed; access lanes reopened in phases.","yellow"),
    T("Emergency flood response: evacuation guidance at waterfront shrines; calm stations maintained; shelter intake controlled.","red"),
    T("High-risk escort of officials through port-gate crowd; separation corridor enforced; decoy route staged.","red"),
    T("Crisis harbor-closure messaging under red posture: notices distributed; prevent vigilante enforcement at ramps.","red"),
    -- +8 added
    T("Canal bridge inspection; loose stone and rail faults flagged; repair tickets prioritized.","green"),
    T("Dockworker conduct briefing; safe-lift rules posted; harassment complaints intake opened.","green"),
    T("Water purity spot-check at fountains; contamination warnings posted; sampling request filed.","green"),
    T("Controlled queue plan at ferry ramps; entry tokens issued; crowd cadence rehearsed.","yellow"),
    T("Suspicious skiff check support; manifests reviewed; contraband seized; custody signatures obtained.","yellow"),
    T("Storm-surge rehearsal: shelter route posted; boat tie-down checks; public update schedule set.","yellow"),
    T("Port lockdown support after violence; civilian zones cleared; rescue lanes protected; communications centralized.","red"),
    T("Mass casualty waterfront incident: triage staging; family intake; perimeter discipline; after-action memo sealed.","red"),
    T("CLASSIFIED tasking: covert counter-assassin sweep along port approaches; decoy route staged; boat manifests cross-checked; suspect surveillance handed off under seal; public posture maintained as routine SAR readiness.","red"),
  },

  ["Reverie Nebulous"] = {
    T("Dream-circle with strict moderation; de-escalation emphasized; referral cards issued for high anxiety.","green"),
    T("Quiet art support for children after a scare; parent coaching provided; school liaison note filed.","green"),
    T("Sleep-hygiene teaching for shift workers; rest scheduling guidance posted; rumor triggers explained.","green"),
    T("Rumor mapping: identify dream narratives feeding panic; corrective story deployed with approved phrases.","yellow"),
    T("Witness interviews after trance-gathering complaint; compliance conditions issued; repeat offenders logged.","yellow"),
    T("Night calming near transit nodes; chanting regulated; separation corridors planned to prevent crush behavior.","yellow"),
    T("Post-event narrative edit: reduce retaliatory impulses; speaker training conducted; script packet distributed.","yellow"),
    T("Containment support for mass panic dream episode; supervised rest zones opened; clinic corridor protected.","red"),
    T("Emergency counseling for families after fatal incident; grief rites paired with aid referrals.","red"),
    T("High-risk ‘dream-breach’ report assessed with Aprobelle liaisons; misinformation suppressed; controlled bulletin issued.","red"),
    -- +8 added
    T("Dream journal distribution; privacy rules explained; escalation criteria posted for severe episodes.","green"),
    T("Quiet ‘waking boundary’ lesson; discourage prophetic claims; practical coping steps posted.","green"),
    T("Artist liaison with civic stewards; performance permits checked; safe audience limits posted.","green"),
    T("Sweep for fraudulent ‘dream sellers’; counterfeit charms seized; victim refunds routed through Amicitia.","yellow"),
    T("Night-market calming protocol: light levels raised; exit lanes marked; chant limits enforced.","yellow"),
    T("Repeat nightmare cluster triage; trigger map compiled; clinic referrals issued; follow-up schedule set.","yellow"),
    T("Mass hysteria response after shared-vision rumor; calm stations expanded; scripts looped; arrests minimized.","red"),
    T("Violent trance incident containment: victim protection; offender transfer; witness shielding; records sealed.","red"),
  },

  ["Eon Fellowship"] = {
    T("Timing audit at market gates: shift rotations clarified; reduce conflict from missed windows.","green"),
    T("Public calendar correction bulletin; prevent rumor-driven delays and missed rites; signage posted.","green"),
    T("Clinic/shrine entry metering drill: queue cadence tested; stewards trained on timing scripts.","green"),
    T("Witness-arrival scheduling for a hearing; time windows staged to deter intimidation; escort plan posted.","yellow"),
    T("Transit-flow smoothing at rail/canal chokepoint; signage updated; alternate timing lane opened.","yellow"),
    T("Record review: repeated stipend-delay disputes analyzed; corrective SOP issued; clerk sign-off obtained.","yellow"),
    T("Night timing plan during fog disruption; safe intervals posted; navigation warnings coordinated.","yellow"),
    T("Emergency stabilization after cascading delays: volunteer surge roster synchronized; safe movement hours posted.","red"),
    T("Crisis schedule triage during unrest: curfew windows clarified; dispersal routes timed and enforced.","red"),
    T("High-risk timing escort for custody transfer; decoy timing used; route denial staged.","red"),
    -- +8 added
    T("Endurance briefing for patrol rotations; fatigue flags logged; relief roster posted.","green"),
    T("Winterization checklist posted (wind, ice, flood); supply needs tallied; maintenance tickets filed.","green"),
    T("Shelter readiness audit; blanket inventory checked; entry rules posted; complaint intake opened.","green"),
    T("Ration-line cadence enforcement; entry stamps issued; fights deterred; incident log filed.","yellow"),
    T("Gate sabotage rumor response; key control audit; access tightened; night watch schedule posted.","yellow"),
    T("Critical-utility staffing pact mediation; interim rota signed; enforcement stewards assigned.","yellow"),
    T("Storm emergency shelter activation: intake discipline; ration control; rumor suppression; clinic route protected.","red"),
    T("Civil disorder deterrence posture: curfew windows broadcast; safe lanes enforced; ringleaders detained.","red"),
  },

  ["Order of the Umbral Oracle"] = {
    T("Quiet-night rite with strict entry control; visitor conduct posted; disturbance triggers explained.","green"),
    T("Counseling for anxious households after nightmare spike; referral paths established; follow-up logged.","green"),
    T("Night watch at shrine steps to deter harassment; steward coordination and incident ledger maintained.","green"),
    T("Risk bulletin for shadow-sign nights; discourage panic gatherings at transit nodes; routes marked.","yellow"),
    T("Investigation of anonymous pamphlets claiming ‘oracle sanction’; counterfeit marks documented; plates traced.","yellow"),
    T("Audit of unsanctioned night gatherings; compliance restrictions issued; repeat venues logged.","yellow"),
    T("Post-incident narrative discipline: speaker scripts updated; rumor sources mapped; correction bulletins deployed.","yellow"),
    T("Containment support for ‘demonic sign’ rumor under red posture: controlled messaging delivered; calm stations opened.","red"),
    T("Witness protection for informant threatened by cult rival; safehouse route planned; decoy escort used.","red"),
    T("Crisis nocturnal dispersal after crowd surge; safe corridors opened; reunification tent activated.","red"),
    -- +8 added
    T("Nocturnal observation log sweep; key anomalies summarized; sealed note sent to Temple Bank.","green"),
    T("Lamp-and-lock check on vulnerable alleys; household guidance posted; repair tickets filed.","green"),
    T("Confidential witness intake at a night gate; testimony recorded; protection options listed.","green"),
    T("Blackout exploitation patrol; temporary lighting placed; entry control enforced; suspects logged.","yellow"),
    T("Cultic graffiti catalog; symbols photographed; cleanup ordered; copycat deterrence notice posted.","yellow"),
    T("Stalking complaint response; escort plan staged; restraining paperwork initiated; incident ledger updated.","yellow"),
    T("Assassination rumor triage: safe corridors established; witness relocation; evidence sealed; briefings issued.","red"),
    T("Kidnapping response support: gate checks tightened; informant intake opened; safehouse activation logged.","red"),
  },

  ["Mystery of the Verdant Embrace"] = {
    T("Canal cleanup rite; silt removed; flow restored; maintenance log filed.","green"),
    T("Seed blessing for gardeners; pest-prevention guidance distributed; vendor hygiene note appended.","green"),
    T("Wetland path maintenance; travel warnings posted; drowning prevention markers installed.","green"),
    T("Aqueduct/sluice audit: leakage mapped; repair crews coordinated; access rules posted at pump houses.","yellow"),
    T("Field mediation over irrigation rights; shared schedule drafted; signatures collected.","yellow"),
    T("Vector mitigation at swamp edges; standing water treated; guidance posted at shelters.","yellow"),
    T("Post-storm debris clearance with volunteers; hazard markings placed; lanes reopened in phases.","yellow"),
    T("Emergency flood relief: sandbag lines, food staging, shelter support; calm messaging maintained.","red"),
    T("Crisis canal-breach response: closure and reroute; crowd calming in shrine districts; clinic corridor protected.","red"),
    T("High-risk contamination response at water points; sampling escorted; panic narratives suppressed.","red"),
    -- +8 added
    T("Apothecary batch check for adulterants; approved sources posted; corrective warnings issued.","green"),
    T("Community garden rota mediation; tool access rules posted; theft prevention signage installed.","green"),
    T("Midwife support circuit; recovery herbs issued; follow-up visits scheduled; privacy rules posted.","green"),
    T("Illness cluster mapping near wetlands; symptom intake standardized; clinic referrals issued.","yellow"),
    T("Contaminated produce recall support; vendor notices posted; refunds coordinated with Amicitia.","yellow"),
    T("Pest outbreak triage briefing; safe treatment guidance; livestock protection steps posted.","yellow"),
    T("Epidemic response support: quarantine boundaries marked; care kits staged; misinformation suppressed.","red"),
    T("Poisoning event response: antidote routing; source trace; suspect packet assembled; water access controlled.","red"),
  },

  ["Conclave Illuminara"] = {
    T("Night-sky lecture: explain calendars; reduce omen anxiety; post practical safety guidance.","green"),
    T("Quiet meditation ward at shrine; breathing protocols taught; de-escalation reminders posted.","green"),
    T("Lantern-route inspection; reflective markers placed; hazard steps flagged.","green"),
    T("Dream-space custody audit: offerings catalogued; safety signage posted; access rules clarified.","yellow"),
    T("Counterfeit moon-token investigation; vendor interviews; seizure log compiled; restitution note drafted.","yellow"),
    T("Controlled night gathering after rumor flare; calm-litany enforced; dispersal cadence rehearsed.","yellow"),
    T("Blackout safety drill for storm season: guides staged; warning ropes placed; shelter route posted.","yellow"),
    T("Emergency anti-panic messaging after ‘demonic sign’ claim; speaker scripts distributed; rumor sources logged.","red"),
    T("Escort for sensitive nocturnal rite during unrest; perimeter observers posted; clinic access protected.","red"),
    T("High-risk crowd-control during an overfull vigil; separation corridors held; reunification tent activated.","red"),
    -- +8 added
    T("Lunar-cycle briefing for stewards; ‘dark segment’ messaging standardized; posting schedule set.","green"),
    T("Archive copy check: calendars and proclamations reconciled; corrections issued; stamps verified.","green"),
    T("Diplomatic reading for visiting officials; neutral phrasing drafted; public reassurance script prepared.","green"),
    T("Forgery sweep for ‘Conclave decree’ sheets; originals photographed; printer path traced.","yellow"),
    T("Vigil capacity enforcement; entry tokens issued; exit lanes marked; medics staged.","yellow"),
    T("Rumor correction loop after eclipse claims; public broadcasts timed; panic buying deterred.","yellow"),
    T("Mass fear response after prolonged blackout; calm stations opened; shelter routes protected; patrol liaison engaged.","red"),
    T("Crisis interpretive bulletin during unrest: ‘cycle framing’ delivered hourly; retaliatory panic prevented.","red"),
  },

  ["Sanctum Vitalis"] = {
    T("Clinic hygiene audit: water barrels sealed; entry protocols posted; handwashing station checks logged.","green"),
    T("Vaccination schedule coordination with stewards; consent ledger reconciled; follow-up appointments scheduled.","green"),
    T("Triage coaching circle: calm scripts practiced; escalation triggers clarified; volunteer assignments posted.","green"),
    T("Food-handling inspection at market stalls; spoiled goods removed; corrective warnings issued.","yellow"),
    T("Quarantine rumor suppression: correct false ‘plague’ claim; stabilize clinic queues; calm stations opened.","yellow"),
    T("Vector mitigation near wetlands; standing water treated; guidance posted at dorms and shelters.","yellow"),
    T("Witness interviews after ‘poisoned well’ claim; evidence packet assembled; sampling escorted.","yellow"),
    T("Emergency fever center support: intake metering + calm stations established; separation corridor enforced.","red"),
    T("Crisis medical escort for officials through crowd; protected clinic route held; decoy timing staged.","red"),
    T("High-risk contamination response: water points shut temporarily; alternate distribution controlled; panic narratives suppressed.","red"),
    -- +8 added
    T("Spring/well inspection; contamination risks logged; repair tickets filed; public boil guidance posted if needed.","green"),
    T("Clinic staff rota stabilization; break schedule enforced; burnout flags logged; relief volunteers assigned.","green"),
    T("Worksite injury-prevention briefing; hazards recorded; employer compliance sign-off obtained.","green"),
    T("Respiratory illness surge triage; mask distribution staged; ventilation rules posted; clinic flow adjusted.","yellow"),
    T("Food-borne illness scare response; vendor recall notices; refund guidance; culprit investigation packet started.","yellow"),
    T("Overcrowded clinic gate plan; entry metering tightened; conflict scripts deployed; escort corridor protected.","yellow"),
    T("Mass casualty support: triage expansion; supply convoy prioritization; family intake; mortality ledger sealed.","red"),
    T("Waterborne outbreak response: quarantine lines; alternate water distribution; daily briefings; misinformation suppressed.","red"),
  },

  ["Temple Alabaster"] = {
    T("Public conduct rite at shrine; rules reissued; volunteers coached on nonviolent enforcement.","green"),
    T("Counseling circle for fearful households; practical aid referrals arranged; follow-up log kept.","green"),
    T("Relief custody check: inventory counted; seals verified; theft-prevention signage posted.","green"),
    T("Vendor compliance sweep for counterfeit purity seals; seizures logged; corrective warnings issued.","yellow"),
    T("Witness interviews after shrine vandalism; sworn statements taken; intimidation complaints logged.","yellow"),
    T("Night watch rotation on shrine steps after threat chatter; steward roster posted.","yellow"),
    T("Escort of sacred item to storage; chain-of-custody verified; route screening documented.","yellow"),
    T("Emergency crowd control during pilgrimage surge; lanes marked; calm stations opened; reunification tent active.","red"),
    T("Crisis shelter activation in stone precinct during storm; intake discipline enforced; clinic route protected.","red"),
    T("High-risk perimeter reinforcement after credible attack rumor; entry controlled; decoy dispersal route staged.","red"),
    -- +8 added
    T("Purity inspection of public wash points; contamination warnings posted; maintenance tickets filed.","green"),
    T("Serenity hour enforcement in market lanes; noise caps posted; disputes mediated; incident log updated.","green"),
    T("Sanitation compliance coaching for vendors; corrective steps issued; follow-up date scheduled.","green"),
    T("Crowd cooling protocol outside shrine gates; separation lanes held; harassment complaints intake opened.","yellow"),
    T("Contaminated goods sweep (dye/soap/food); items pulled; source traced; public reassurance posted.","yellow"),
    T("Minor desecration repair coordination; evidence preserved; restoration rite scheduled; offenders logged.","yellow"),
    T("Major desecration fallout: crowd calmed; arrests coordinated; restoration rites performed; retaliation deterred.","red"),
    T("Toxic spill response support: exclusion zone enforced; cleanup supervised; health referrals issued; messaging standardized.","red"),
  },

  ["Court of the Ironclad"] = {
    T("Volunteer discipline training; nonviolent restraint emphasized; reporting scripts practiced.","green"),
    T("Public order presence at shrine steps; etiquette enforced; minor disputes mediated on-site.","green"),
    T("Oath workshop: pledge cards sealed for civic stewards; compliance expectations posted.","green"),
    T("Route hardening walk: broken lamps and unsafe crossings flagged; repair tickets filed.","yellow"),
    T("Contraband confiscation support; evidence logged for Temple Bank clerks; custody chain verified.","yellow"),
    T("Night watch on canal bridges after sabotage chatter; checkpoint discipline enforced; escorts scheduled.","yellow"),
    T("Post-incident intimidation review: complaints investigated; corrective discipline applied; report filed.","yellow"),
    T("High-risk escort for witness leaving hearing; safehouse transfer arranged; decoy route used.","red"),
    T("Emergency deterrence patrol after riot flare; separation corridors opened; care stations deployed.","red"),
    T("Crisis reinforcement at clinic gate during panic surge; queue discipline established; dispersal cadence enforced.","red"),
    -- +8 added
    T("Endurance drill briefing; heat/fatigue limits posted; rotation enforcement logged.","green"),
    T("Equipment discipline check; missing items reconciled; chain-of-command sign-off obtained.","green"),
    T("Desert-procession conduct rules posted; hydration points marked; medical standby coordinated.","green"),
    T("Rival faction confrontation risk patrol; meeting sites mapped; entry controls tightened; warnings issued.","yellow"),
    T("Bloodmoon rumor suppression; discourage panic gatherings; authorized statement posted; agitator list started.","yellow"),
    T("Arrest processing support after brawl surge; evidence tagging enforced; witness separation maintained.","yellow"),
    T("Sectarian violence surge: unified deterrence posture; safe corridors held; ringleaders detained; reprisals prevented.","red"),
    T("Critical sabotage threat: depot perimeter locked; entry metering; night watches doubled; public notice issued.","red"),
  },

  ["Accord Concordia"] = {
    T("Council mediation between neighborhood elders; written concord signed; follow-up date scheduled.","green"),
    T("Public forum moderation; speaking order enforced; agitator removal avoided through de-escalation.","green"),
    T("Vendor-stall settlement: restitution terms logged; compliance expectations posted.","green"),
    T("Civic etiquette training for steward teams; de-escalation scripts practiced; lane protocol rehearsed.","yellow"),
    T("Inter-cult boundary meeting; shared space rules posted at shrine gates; signage installed.","yellow"),
    T("Rumor containment after inflammatory broadsides; corrective bulletin cycle launched; sources logged.","yellow"),
    T("Audit of district clerk office after complaint surge; corrective plan countersigned; retraining scheduled.","yellow"),
    T("Emergency truce enforcement during street clash; separation lines held; medical access protected.","red"),
    T("Crisis reconciliation hearing after riot; pledge collection + witness support; retaliation prevention enforced.","red"),
    T("High-risk mediation under red posture: rival leaders separated; agreement read publicly; dispersal routes staged.","red"),
    -- +8 added
    T("Harmony Gardens work party coordination; mixed crews paired; grievances logged; follow-up circle scheduled.","green"),
    T("Shared-meal hosting plan; seating map prevents rival clustering; steward assignments posted.","green"),
    T("Welcome rite for new migrants/work crews; support contacts listed; exploitation warnings posted.","green"),
    T("Feast sabotage risk triage; ingredient chain verified; kitchen access controlled; guard liaison engaged.","yellow"),
    T("Inter-guild boycott settlement; compromise pledge signed; enforcement stewards appointed; public notice issued.","yellow"),
    T("Youth rivalry cooling via structured games; chaperones assigned; penalties posted for violence.","yellow"),
    T("Emergency unity convocation after fatal clash; ceasefire sworn; escort corridors established; reprisals deterred.","red"),
    T("Mass panic calming centers opened; volunteer surge; hourly briefings delivered; rumor nodes suppressed.","red"),
  },

  ["Mystery of the Stygian Veil"] = {
    T("Funerary custody check: offerings catalogued; tomb precinct rules posted; candle/fire safety verified.","green"),
    T("Grief counseling circle; practical aid referrals arranged; follow-up home visits scheduled.","green"),
    T("Mortuary hygiene audit; disease-prevention protocols verified; sanitation failures logged.","green"),
    T("Rumor suppression after ‘restless dead’ claims; evidence bulletin issued; agitation triggers tracked.","yellow"),
    T("Escort for body transfer; route secured; witnesses logged; custody chain verified.","yellow"),
    T("Night vigil supervision to prevent stampede or retaliatory violence; separation corridor planned.","yellow"),
    T("Witness interviews after desecration; sworn statements sealed; intimidation complaints recorded.","yellow"),
    T("Crisis response after mass death incident: grief triage + order restoration; calm stations opened.","red"),
    T("Emergency calm stations at cemetery gate during panic; supervised dispersal coordinated; clinic access protected.","red"),
    T("High-risk memorial crowd control under red posture; perimeter held; reunification tent activated.","red"),
    -- +8 added
    T("Rite of transition for a recovering patient; symbolic veil-lifting; community support plan logged.","green"),
    T("Inheritance dispute mediation after death; fair division terms drafted; coercion checks recorded.","green"),
    T("Cemetery path safety inspection; uneven steps flagged; lantern markers placed; maintenance tickets filed.","green"),
    T("Dusk procession route reset after hazard; entry cadence rehearsed; pinch points staffed.","yellow"),
    T("Grave-robbery scare investigation support; patrol liaison; witness intake; rumor correction posted.","yellow"),
    T("Overcrowded vigil management; capacity limits posted; exit lanes marked; medics staged.","yellow"),
    T("After-violence mourning containment; safe corridors held; retaliation ban sworn; witness shielding arranged.","red"),
    T("Mass panic after ‘demonic figure’ report near tomb precinct; authoritative denial; agitator detention; calm stations expanded.","red"),
  },

  ["Sylvan Fellowship"] = {
    T("Forest-path maintenance; travel warnings posted for pilgrims; hazard markers refreshed.","green"),
    T("Wildlife safety briefing; reduce accidents on shrine trails; reporting chain posted.","green"),
    T("Ecological repair planning: volunteer crews scheduled; debris staging plan filed.","green"),
    T("Herb/timber vendor conduct audit; sustainable collection rules enforced; repeat violators logged.","yellow"),
    T("Brushfire-watch patrol; illegal burns suppressed; emergency water points marked.","yellow"),
    T("Mediation between trappers and villagers; shared schedule signed; conflict triggers identified.","yellow"),
    T("Rumor containment after ‘forest curse’ claim; corrective bulletin cycle run; agitator list started.","yellow"),
    T("Emergency shelter staging in forest precinct during storm; intake controlled; calm messaging enforced.","red"),
    T("Crisis search for missing pilgrim after fog; guides deployed; safe corridors opened.","red"),
    T("High-risk evacuation support under red weather alert; trail closures enforced; reunification tent activated.","red"),
    -- +8 added
    T("Trailhead sign refresh: safe routes marked; dangerous gullies flagged; escort contacts posted.","green"),
    T("Livestock safety day; grazing boundaries walked; injury checks; neighbor oaths exchanged.","green"),
    T("Waterway cleanup coordination; contamination risks logged; volunteer rota posted.","green"),
    T("Illegal poaching/logging inquiry; evidence gathered; offender descriptions logged; patrol liaison engaged.","yellow"),
    T("Flood/mud hazard reroute postings; bridge warnings issued; rescue staging areas marked.","yellow"),
    T("Farmer–rancher conflict triage; grazing calendar set; penalties defined; enforcement stewards assigned.","yellow"),
    T("Major flood response: shelter intake; animal rescue; public briefings; rumor suppression enforced.","red"),
    T("Violent land access dispute: containment support; arrests coordinated; restitution terms drafted; reprisals deterred.","red"),
  },

  ["Mystery of Red Mirth"] = {
    T("Festival conduct briefing; prevent brawls and dangerous dares; de-escalation scripts posted.","green"),
    T("Joy-warden patrol: intervene early in disputes; stabilize lines outside taverns and music halls.","green"),
    T("Volunteer steward training: safe celebration guidance and nonviolent intervention.","green"),
    T("Vendor compliance audit for intoxicants; counterfeit batches seized; sanitation notes issued.","yellow"),
    T("Crowd choreography for parade route; crush-risk minimized; pinch points marked.","yellow"),
    T("Rumor suppression after scandal broadsides; approved narrative posted; source stalls logged.","yellow"),
    T("Night watch at tavern row after violence chatter; calm messaging distributed; escorts staged.","yellow"),
    T("Emergency stabilization after riot flare; separation lanes + care stations deployed; clinic access protected.","red"),
    T("Crisis safe escort for a key witness leaving hearing; safehouse transfer arranged; decoy route used.","red"),
    T("High-risk crowd-control during a packed revel; dispersal cadence enforced; reunification tent activated.","red"),
    -- +8 added
    T("Mask/prop safety inspection for performers; sharp edges removed; permit list verified.","green"),
    T("Controlled wine dosing guidance posted; hydration points marked; refusal rights explained.","green"),
    T("Aftercare check for revelers; transport escorts staged; harassment complaints intake opened.","green"),
    T("Stage rigging sabotage check; inspection log filed; access limited; suspects noted.","yellow"),
    T("Brawl-prone venue warning and temporary closure protocol; staff trained; incident ledger updated.","yellow"),
    T("Parade route split after crush-risk alert; alternate route posted; crowd cadence rehearsed.","yellow"),
    T("Stampede incident response: triage lanes; family reunification; safety reforms mandated; public statement issued.","red"),
    T("Coordinated sabotage campaign response: permit freeze; targeted arrests; controlled messaging; patrol liaison engaged.","red"),
  },

  ["Rex Catonis"] = {
    T("Civic order proclamation at court square; conduct rules reissued; complaint intake organized.","green"),
    T("Docket triage for petty disputes; mediation attempted before formal hearing; outcomes logged.","green"),
    T("Steward training on lawful dispersal and calm messaging; escalation triggers clarified.","green"),
    T("Municipal fines ledger audit: correct corrupt entries; restitution schedule drafted.","yellow"),
    T("Vendor permit compliance sweep; counterfeit permits seized; corrective notices posted.","yellow"),
    T("Rumor suppression after ‘illegal decrees’ claim; clarifying bulletin posted; speaker scripts updated.","yellow"),
    T("Escort for court clerks during sensitive hearing; intimidation deterrence; safe route posted.","yellow"),
    T("Emergency order restoration after riot; curfew messaging + dispersal corridors; clinic access protected.","red"),
    T("Crisis protection of court archives after arson threat; perimeter posted; custody chain verified.","red"),
    T("High-risk courthouse crowd control under red posture; separation corridors held; arrests minimized.","red"),
    -- +8 added
    T("Permit renewal clinic; expired permits reissued; lawful signage posted; fine waivers granted for compliance.","green"),
    T("Public notice board audit; false postings removed; official stamps verified; clerk sign-off obtained.","green"),
    T("Restitution schedule review; payment plan adjusted; guarantors recorded; Temple Bank copy filed.","green"),
    T("Price gouging complaint sweep; violations documented; fines assessed; public deterrence notice posted.","yellow"),
    T("Curfew violation surge response; warning protocol standardized; repeat offenders flagged; incident log filed.","yellow"),
    T("Gang intimidation intake; victim protection options posted; suspect descriptions routed to Ironclad.","yellow"),
    T("Mass corruption scandal response: ledger seizure; official suspensions; prosecution packets assembled; public guidance issued.","red"),
    T("Coordinated violence response: unified command liaison; detention processing; court schedule triage; after-action report filed.","red"),
  },

  ["Sanctum Delphica"] = {
    T("Archive custody inspection: seals verified; storage conditions checked; humidity warnings posted.","green"),
    T("Scribe training on custody chain; convoy paperwork rehearsed; handoff scripts practiced.","green"),
    T("Pilgrim etiquette workshop at library shrine; quiet-zone enforced; lane discipline posted.","green"),
    T("Index confiscated pamphlets for signature motifs; correlation packet forwarded; source stalls logged.","yellow"),
    T("Witness interviews after shrine vandalism; sworn statements taken; intimidation complaints recorded.","yellow"),
    T("Doctrinal quarantine assistance: entry controls + rumor suppression; approved signage posted.","yellow"),
    T("Court packet assembly for desecration suspects; evidence collated; custody memo sealed.","yellow"),
    T("Night watch rotation at archive stacks following arson threat; entry controlled; escort-only rule applied.","red"),
    T("Escort sensitive artifact transfer; route denial markers deployed; decoy timing staged.","red"),
    T("Emergency custody lockdown after breach attempt; relocation plan activated; public narrative kept neutral.","red"),
    -- +8 added
    T("Forecast briefing for stewards: likely choke points listed; preventive signage posted; shift plan updated.","green"),
    T("Public ‘clarity notice’ explaining rumor patterns; discourage prophecy claims; practical steps appended.","green"),
    T("Queue triage workshop for clerks; intake categories standardized; escalation triggers defined.","green"),
    T("Forgery detection surge response; verification booth staged; suspect stamps cataloged; seizures logged.","yellow"),
    T("High-traffic archive day capacity rules enforced; entry tokens issued; silent zone posted; security liaison set.","yellow"),
    T("False omen bulletin cycle after sky-sign rumors; broadcasts timed; hoarding deterrence included.","yellow"),
    T("Archive fire incident response: document evacuation; custody chain preserved; public messaging controlled; arrests coordinated.","red"),
    T("Mass panic after forged warning decree: authoritative retraction posted; printer network pursued; calm stations opened.","red"),
  },

  ["Ordo Amicitia"] = {
    T("Kindness distribution at shrine steps; ledger reconciled; needs list updated for tomorrow.","green"),
    T("Family mediation after dispute; amity pledges collected; follow-up visits scheduled.","green"),
    T("Volunteer training: calm communication and nonviolent intervention; escalation scripts practiced.","green"),
    T("Clinic-queue kindness patrol; conflict prevention and assistance to elderly; complaints logged.","yellow"),
    T("Rumor containment after defamatory pamphlets; corrective bulletin cycle launched; source stalls mapped.","yellow"),
    T("Audit of relief supplies to prevent theft; custody seals verified; storage rules posted.","yellow"),
    T("Witness support for targeted volunteer; safe route arranged; intimidation complaints recorded.","yellow"),
    T("Emergency care stations during unrest; neutral aid posture announced; clinic corridor protected.","red"),
    T("Crisis grief counseling after fatal incident; aid referrals coordinated; retaliation pledges collected.","red"),
    T("High-risk shelter stabilization under red posture; intake controlled; reunification tent activated.","red"),
    -- +8 added
    T("Elder check-in circuit; medicine delivery; home repair requests logged; follow-up visits scheduled.","green"),
    T("School buddy program setup; mentors paired; attendance tracked; guardian contacts logged.","green"),
    T("Micro-grant assistance for small vendors; eligibility explained; Temple Bank receipts issued.","green"),
    T("Aid line surge plan; entry metering; high-need triage; conflict scripts deployed.","yellow"),
    T("Fraudulent charity collectors sweep; donations recovered; offenders documented; public warning posted.","yellow"),
    T("Harassment at aid stations response; escorts assigned; offenders barred; reports sealed.","yellow"),
    T("Disaster relief activation: shelter staffing; supply staging; daily reporting; rumor suppression enforced.","red"),
    T("Mass displacement response: registration booth; family reunification; trauma referrals; records sealed.","red"),
  },

  ["Temple Illuminata"] = {
    T("Illumination vigil: lantern discipline and public calm guidance posted; safe routes marked.","green"),
    T("Counseling for anxious households; referral paths established; follow-up log kept.","green"),
    T("Archive testimonies and signs; witness statements catalogued; custody rules posted.","green"),
    T("Night watch of sacred precinct; entry control enforced; conduct rules posted at gates.","yellow"),
    T("Omen bulletin: correct false ‘dark star’ claim; discourage panic gatherings; scripts distributed.","yellow"),
    T("Audit unsanctioned night gatherings; compliance restrictions issued; repeat venues logged.","yellow"),
    T("Post-incident narrative discipline; rumor sources mapped; correction bulletins deployed.","yellow"),
    T("Emergency messaging after ‘demonic symbol’ report; scripts distributed; calm stations opened.","red"),
    T("Crisis crowd dispersal after night surge; guidance routes opened; reunification tent active.","red"),
    T("High-risk escort for sensitive nocturnal rite during unrest; perimeter observers posted; decoy route used.","red"),
    -- +8 added
    T("Public symposium moderation; debate rules posted; minutes archived; speaker list verified.","green"),
    T("Clarity counseling for civic leaders; action plan sealed; follow-up check scheduled.","green"),
    T("Apprentice study circle; reading list issued; mentor pairing logged; conduct rules posted.","green"),
    T("Heckler disruption response at lecture; removals enforced; incident log filed; next session relocated.","yellow"),
    T("Forged ‘Temple order’ correction; official stamps verified; printer path traced; retraction posted.","yellow"),
    T("Threat triage against philosophers/teachers; security escort arranged; venue access restricted.","yellow"),
    T("Attack on lecture venue response: injuries triaged; perpetrators prosecuted; continuity plan issued; public calm statement posted.","red"),
    T("Citywide incitement campaign response: counter-messaging; arrests; curfew guidance; safe corridors protected.","red"),
  },

  ["Temple Aprobelle"] = {
    T("Shrine-leader briefing: weekly rumor-control cycle; clarifications posted; complaint intake organized.","green"),
    T("Grief-cadre home visits after accident; de-escalation coaching delivered; referrals filed.","green"),
    T("School immunization coordination: schedule posted; consent ledger reconciled; follow-up reminders issued.","green"),
    T("Clinic-queue stabilization drill: entry metering tested; conflict prevention scripts practiced.","yellow"),
    T("Behavior audit of district office after complaint surge; corrective training issued; sign-off obtained.","yellow"),
    T("Pattern-analysis intake: schism texts logged; signature correlation packet forwarded; printer stalls mapped.","yellow"),
    T("Anomaly-response dispatch after contamination claims: care paired with narrative discipline; panic triggers logged.","yellow"),
    T("Silencing-cadre activated after panic flare; controlled messaging + care stations deployed; retaliation pledges collected.","red"),
    T("Emergency stabilization in evacuation/fever center: calming rites + rumor suppression; clinic corridor protected.","red"),
    T("High-risk psychological stabilization after mass scare: rumor nodes neutralized; supervised dispersal enforced; evidence chain sealed.","red"),
    -- +8 added
    T("Speaker coaching for ward announcements; panic triggers removed; approved phrasing distributed.","green"),
    T("Trust survey intake at market edges; key grievances logged; referrals routed to Harmony Sanctum.","green"),
    T("School liaison check: bullying/harassment trend review; intervention plan filed; follow-up scheduled.","green"),
    T("Targeted pamphlet purge after printer ID; removal teams assigned; replacement postings issued; suspects logged.","yellow"),
    T("Crowd agitation forecast; patrol liaison; calm broadcast cadence set; escalation thresholds defined.","yellow"),
    T("Workplace intimidation response; protective escort plan; offender sanctions recommended; compliance checks scheduled.","yellow"),
    T("Mass panic containment: calm centers expanded; hourly briefings; agitator roundup coordinated; retaliatory impulses reduced.","red"),
    T("Post-attack trauma response: debrief circles; retaliation bans; witness relocation; compliance sweep scheduled.","red"),
  },
}

    --------------------------------------------------------------------------------
    -- 12.1) NOCTIC VAMPIRISM / NOCTIC-RABREV TASK INSERTS
    --
    -- These tasks add Noctic Vampirism and raw Noctic-Rabrev events to the cults
    -- most likely to intervene: dream, health, purification, wetland, funerary,
    -- hidden-knowledge, legal, charity, and counter-fraud orders.
    --------------------------------------------------------------------------------

    local function addCultTasks(cultName, list)
        tasksByCult[cultName] = tasksByCult[cultName] or {}
        for _, t in ipairs(list) do
            table.insert(tasksByCult[cultName], t)
        end
    end

    addCultTasks("Reverie Nebulous", {
        T("[[Noctic Vampirism]] dream-entanglement intake: affected households given sleep-boundary cards; severe nightmare cases referred to [[Temple_Aprobelle|Temple Aprobelle]].","green"),
        T("Noctic dream-circle for registered patients; blood-hunger language redirected into supervised journaling; family witnesses briefed.","green"),
        T("Nightmare cluster triage after suspected raw [[Noctic-Rabrev]] smoke exposure; trigger map compiled; clinic referrals issued.","yellow"),
        T("Shared-vision complaint linked to Noctic leaf use; dream narratives separated from rumor claims; speaker scripts distributed.","yellow"),
        T("Noctic trance-gathering compliance review; unsanctioned sleep rites suspended; repeat venues logged for Temple Bank follow-up.","yellow"),
        T("Crisis rest-ward activation after mass Noctic panic dream; supervised sleep zones opened; clinic corridor protected.","red"),
        T("Violent blood-hunger dream episode contained; families separated safely; witness shielding and trauma referrals arranged.","red"),
        T("High-risk dream-breach report involving suspected nocticine-Δ exposure; Aprobelle liaisons activated; public narrative held neutral.","red"),
    })

    addCultTasks("Sanctum Vitalis", {
        T("[[Noctic Vampirism]] clinic screen: photophobia, anemia, fever, lesions, and blood-craving symptoms logged; dietary guidance issued.","green"),
        T("Registered Noctic patient follow-up: milk, iron food, and blood-substitute stabilization plan checked; family care note filed.","green"),
        T("Raw [[Noctic-Rabrev]] exposure triage at wetland clinic; symptomatic patients separated from ordinary fever queue.","yellow"),
        T("Congenital Noctic case review; guardian consent ledger reconciled; lifelong monitoring schedule updated.","yellow"),
        T("Market illness cluster assessed for raw-leaf adulteration; sampling escorted; vendor recall notice prepared.","yellow"),
        T("Emergency hemorrhagic decline response in serious Noctic case; triage expansion, family shielding, and mortality ledger precautions enacted.","red"),
        T("Necrotic lesion cluster after unauthorized induction rite; quarantine boundary marked; cult-health evaluation ordered.","red"),
        T("High-risk fever center support after Noctic outbreak rumor; panic narratives suppressed while medical intake remains open.","red"),
    })

    addCultTasks("Temple Alabaster", {
        T("Refined nocticine tincture custody check; dosage ledger reconciled; [[Temple Bank of the Reformed Stripping Path|Temple Bank]] seal verified.","green"),
        T("Post-Eidolan recovery clinic review using licensed [[Noctic-Rabrev]] concentrate; patient response notes sealed under medical custody.","green"),
        T("Purity inspection of Noctic balms and tinctures; raw-leaf contamination warnings posted; suspect jars removed.","yellow"),
        T("Therapeutic nocticine distribution audit; storage seals verified; unauthorized substitutions routed to Golden Shadows.","yellow"),
        T("Contaminated Noctic medicine recall; affected households contacted; clinic-stabilization instructions posted.","yellow"),
        T("Emergency support for serious Noctic Vampirism collapse; calm ward opened; hemorrhage supplies and spiritual grounding teams staged.","red"),
        T("Crisis purification rite after counterfeit nocticine batch harms pilgrims; evidence preserved; public reassurance issued.","red"),
        T("High-risk medical custody transfer for unstable Noctic patient; decoy route used; receiving clinic countersigns sealed packet.","red"),
    })

    addCultTasks("Mystery of the Verdant Embrace", {
        T("[[Noctic-Rabrev]] field-blessing with safety lecture; flower and raw-leaf handling rules reissued to cultivators.","green"),
        T("Blood Orchard footpath inspection; leaf-storage huts checked; runoff risks and wetland access hazards logged.","green"),
        T("Noctic crop custody audit after missing leaf bundles; field stewards interviewed; harvest tally reconciled.","yellow"),
        T("Wetland illness cluster mapped near Noctic fields; raw-leaf exposure questions added to intake; clinic referrals issued.","yellow"),
        T("Flood-damaged Noctic storage shed inspected; wet leaves quarantined; safe flower product separated from restricted material.","yellow"),
        T("Emergency Blood Orchard closure after unauthorized raw-leaf distribution; field gates sealed; cultivator list turned over to Temple Bank.","red"),
        T("Poisoning event tied to unprocessed Noctic leaves; antidote routing, source trace, and water access controls initiated.","red"),
        T("High-risk wetland outbreak response: care kits staged, Noctic custody chain rebuilt, misinformation suppressed in shrine districts.","red"),
    })

    addCultTasks("Mystery of the Stygian Veil", {
        T("Funerary guidance for household of controlled Noctic patient; blood-offering limits explained; mourning schedule recorded.","green"),
        T("Noctic transformation counseling at cemetery gate; symbolic death language separated from unsafe raw-leaf use.","green"),
        T("Terminal Noctic vigil supervision; family witnesses logged; raw-leaf access barred during mourning rites.","yellow"),
        T("Blood-rite compliance check after ancestral-contact claims; permitted substitutes verified; coercion complaints recorded.","yellow"),
        T("Rumor suppression after ‘Noctic dead returning’ claim; evidence bulletin posted; graveyard panic triggers tracked.","yellow"),
        T("Crisis response after terminal Noctic transformation death; grief triage and crowd discipline established before nightfall.","red"),
        T("Emergency calm stations at tomb precinct after blood-hunger panic; supervised dispersal and clinic access protected.","red"),
        T("High-risk memorial for Noctic fatalities; retaliation prevention enforced; family shielding and witness support arranged.","red"),
    })

    addCultTasks("Order of the Umbral Oracle", {
        T("Noctic night-watch at shuttered shrine; sleepwalk reports logged; entry rules posted for anxious households.","green"),
        T("Covered-mirror guidance for suspected Noctic cases; families advised on safe vigils, naming restraint, and witness logs.","green"),
        T("Skødan-style mirror scare assessed after Noctic fever report; false-revelation claims documented without public amplification.","yellow"),
        T("Starless vigil compliance review; raw [[Noctic-Rabrev]] use questioned; repeat venues logged for Temple Bank follow-up.","yellow"),
        T("Dream-mapping dispute involving Noctic patient; testimony sealed; dangerous prophecy language corrected.","yellow"),
        T("Crisis nocturnal dispersal after mirror-haunting rumor; safe corridors opened; panic narratives suppressed.","red"),
        T("Serious Noctic fever episode during covered-mirror rite; patient removed safely; household witnesses debriefed.","red"),
        T("High-risk hidden-name panic in [[List of cities in Bassaridia Vaeringheim#Skøda|Skøda]]; Aprobelle and Sanctum Vitalis liaisons activated under seal.","red"),
    })

    addCultTasks("Conclave Illuminara", {
        T("Tarsican phase briefing for Noctic stewards; raw-leaf rites rescheduled away from high-anxiety night windows.","green"),
        T("Lunar calendar note issued for registered Noctic patients; dream disturbances and light-sensitivity triggers explained.","green"),
        T("Nocticine-Δ rumor correction after celestial alignment claims; hoarding and unsafe induction warnings posted.","yellow"),
        T("Moon-token and Noctic permit verification at night vigil; counterfeit ritual permissions seized.","yellow"),
        T("Controlled interpretation session after Noctic dream surge; public cycle-framing delivered to reduce panic.","yellow"),
        T("Emergency anti-panic bulletin after eclipse-linked Noctic outbreak claim; calm stations opened; clinic routes protected.","red"),
        T("High-risk nocturnal rite escort for registered Noctic patients; perimeter observers posted; false prophecy claims suppressed.","red"),
        T("Crisis interpretive response in Skødan mirror-vigil district; Tarsican language used to separate revelation from deception.","red"),
    })

    addCultTasks("Guild of Golden Shadows", {
        T("Quiet audit of [[Noctic-Rabrev]] market stalls; forged raw-leaf permissions logged; suspect brokers mapped.","green"),
        T("Noctic Fleet manifest cross-check; flower extract, tincture, and raw-leaf crates reconciled against Temple Bank seals.","green"),
        T("Undercover purchase of counterfeit Noctic induction permit; printer path traced; custody memo prepared.","yellow"),
        T("Raw-leaf diversion inquiry after Blood Orchard discrepancy; brokers interviewed; sealed attribution packet filed.","yellow"),
        T("Counterfeit nocticine tincture sweep; suspect vials seized; affected vendors routed to Temple Alabaster inspection.","yellow"),
        T("Night extraction of threatened Noctic witness; safe route screened; temporary housing arranged under seal.","red"),
        T("Major Noctic smuggling ring disruption: ledger seizure, broker detention support, and public market guidance coordinated.","red"),
        T("High-risk port-gate fraud surge involving raw Noctic leaf crates; inspection teams reinforced; evidence chain preserved.","red"),
    })

    addCultTasks("Temple Aprobelle", {
        T("[[Noctic Vampirism]] rumor-control briefing: discourage vigilante panic; explain registration, clinic access, and family support routes.","green"),
        T("Household anxiety visits after Noctic diagnosis; de-escalation coaching delivered; ostracism risks logged.","green"),
        T("Pattern-analysis intake for Noctic panic stories; dream claims, blood-hunger rumors, and raw-leaf reports separated.","yellow"),
        T("Noctic outbreak messaging cycle: official symptoms listed; false contagion claims corrected; clinic queues stabilized.","yellow"),
        T("Behavior audit after harassment of registered Noctic patients; corrective training issued; offenders warned.","yellow"),
        T("Silencing-cadre activated after Noctic panic flare; controlled messaging and care stations deployed before night crowds form.","red"),
        T("Emergency psychological stabilization after serious Noctic blood-hunger incident; retaliation bans and witness relocation arranged.","red"),
        T("High-risk mass scare after alleged Noctic attack; rumor nodes neutralized; supervised dispersal enforced; evidence chain sealed.","red"),
    })

    addCultTasks("Harmony Sanctum", {
        T("Family mediation after Noctic diagnosis; care duties, privacy expectations, and visitor rules written into household concord.","green"),
        T("Neighborhood explanation circle for registered Noctic household; stigma reduced; clinic and Temple Bank contacts posted.","green"),
        T("Market-lane calming after Noctic rumor; queue discipline restored; harassment complaints logged.","yellow"),
        T("Inter-family dispute over raw-leaf inheritance; compromise storage terms drafted; Temple Bank audit requested.","yellow"),
        T("Noctic clinic crowd-lane plan reviewed; patient privacy route and public waiting area separated.","yellow"),
        T("Emergency anti-reprisal pact after blood-hunger scare; escort corridors established; no-retaliation oaths collected.","red"),
        T("Mass grievance intake after Noctic outbreak panic; testimony queue metered; witness shielding arranged; outcomes logged.","red"),
        T("Crisis reconciliation hearing after violence against Noctic patient; restitution ledger initiated; family protection arranged.","red"),
    })

    addCultTasks("Azure Sentinel Sect", {
        T("Canal escort for licensed Noctic medical supplies; handoff signatures collected; chain-of-custody verified.","green"),
        T("Waterfront Noctic storage inspection after rain; flood exposure risks logged; damaged crates elevated.","green"),
        T("Storm-drain and wet-market sweep near Noctic vendors; runoff contamination warnings posted.","yellow"),
        T("Ferry-ramp crowd control during Noctic clinic transfer; privacy corridor and rescue lane maintained.","yellow"),
        T("Suspicious skiff check involving raw Noctic leaf bundles; manifests reviewed; contraband seized under seal.","yellow"),
        T("Emergency flood response at Noctic-Rabrev storehouse; restricted leaf material secured before dispersal through canals.","red"),
        T("Port lockdown support after Noctic smuggling violence; civilian zones cleared; clinic route protected.","red"),
        T("Mass casualty waterfront incident involving contaminated Noctic crates; triage staging and perimeter discipline enforced.","red"),
    })

    addCultTasks("Rex Catonis", {
        T("Noctic registration clinic: voluntary induction rules explained; lawful forms checked; appeal route posted.","green"),
        T("Public notice board update on raw [[Noctic-Rabrev]] restrictions; false decrees removed; clerk sign-off obtained.","green"),
        T("Hearing docket triage for unauthorized Noctic induction complaint; mediation attempted where lawful; evidence preserved.","yellow"),
        T("Raw-leaf permit compliance sweep; expired permissions suspended; corrective notices posted to shrine gates.","yellow"),
        T("Coerced induction allegation reviewed; victim protection options posted; suspect descriptions routed to Ironclad.","yellow"),
        T("Mass legal response after Noctic outbreak: unlawful vendors processed, custody ledgers seized, and public guidance issued.","red"),
        T("Coordinated violence response after Noctic panic; detention processing, court schedule triage, and after-action report filed.","red"),
        T("Emergency decree against unsafe raw-leaf distribution read publicly; clinic corridors protected during enforcement.","red"),
    })

    addCultTasks("Sanctum Delphica", {
        T("Noctic case archive update: diagnosis records, raw-leaf permissions, and family witness notes catalogued.","green"),
        T("Scribe training on Noctic custody language; distinguish raw leaf, flower extract, tincture, and nocticine concentrate.","green"),
        T("Doctrinal quarantine assistance after Noctic rumor; entry controls and approved signage posted.","yellow"),
        T("Forgery detection sweep for Noctic induction papers; suspect stamps catalogued; printer path traced.","yellow"),
        T("Witness packet assembled for raw-leaf desecration or coercive induction case; evidence collated and sealed.","yellow"),
        T("Emergency archive lockdown after Noctic registry breach; relocation plan activated; public narrative kept neutral.","red"),
        T("Mass panic after forged Noctic warning decree; authoritative retraction posted; printer network pursued; calm stations opened.","red"),
        T("High-risk transfer of Noctic medical ledgers; route denial markers deployed; decoy timing staged.","red"),
    })

    addCultTasks("Ordo Amicitia", {
        T("Milk and iron-food distribution for Noctic households; needs list updated; stigma-free delivery route assigned.","green"),
        T("Family support visits for congenital Noctic cases; caregiver relief scheduled; privacy rules posted.","green"),
        T("Aid line surge plan after Noctic diagnosis cluster; high-need triage and conflict scripts deployed.","yellow"),
        T("Fraudulent charity collectors using Noctic outbreak claims exposed; donations recovered; public warning posted.","yellow"),
        T("Shelter intake support for families displaced by Noctic panic; family reunification and trauma referrals recorded.","yellow"),
        T("Disaster relief activation after Noctic outbreak in wetland district; supply staging and rumor suppression enforced.","red"),
        T("Mass displacement response after raw-leaf scare; registration booth opened; care packages and witness shielding arranged.","red"),
        T("High-risk relief escort for registered Noctic patients leaving hostile neighborhood; safe housing arranged under seal.","red"),
    })

local function shouldAZSGoCovert(nextInt, sev, threat)
	-- Only under red posture and with a real threat record present
	if sev ~= "red" or not threat then return false end

	local u = urgencyScore(threat.urg)
	if u < 3 then return false end -- HIGH/CRITICAL only

	-- CRITICAL always qualifies; otherwise look for strong violence/sabotage signals
	local ev = string.lower(tostring(threat.event or ""))
	local signals =
		ev:match("sabot") or ev:match("arson") or ev:match("bomb") or ev:match("terror") or
		ev:match("assassin") or ev:match("hit") or ev:match("murder") or
		ev:match("insurgent") or ev:match("pirate") or ev:match("rogue")

	local qualifies = (u >= 4) or signals
	if not qualifies then return false end

	-- “Sometimes”: deterministic chance, higher for CRITICAL
	local chance = (u >= 4) and 25 or 15  -- percent
	return (nextInt() % 100) < chance
end

--------------------------------------------------------------------------------
-- 13) CITY PROFILES (bias order selection)
--------------------------------------------------------------------------------
local cityProfiles = {
	["Vaeringheim"]    = {weights={AUM=3,HSA=2,AZS=2,GGS=2,SVI=2,SDE=2,TAP=3}},
	["Somniumpolis"]   = {weights={RNB=3,SVI=2,GGS=2,TAP=3,AZS=1}},
	["Luminaria"]      = {weights={AUM=3,CON=2,HSA=2,TAP=2,SVI=1}},
	["Lunalis Sancta"] = {weights={SDE=3,CON=2,TAP=2,HSA=1}},
	["Delphica"]       = {weights={SDE=3,TAP=2,CON=1,SVI=1}},
	["Thermosalem"]    = {weights={SVI=2,TAP=2,AZS=2,HSA=1}},
	["Pyralis"]        = {weights={IGA=2,SVI=2,TAP=2,AZS=2,AUM=1}},
	["Keybir-Aviv"]    = {weights={AZS=3,TAP=2,GGS=2,HSA=1}},
	["Diamandis"]      = {weights={TAP=2,SVI=2,AUM=1,RNB=1,MIR=1}},
	-- Haifan Bassaridia / Southern Strait profiles
	-- These make Southern Strait-relevant cults more likely when the city row
	-- is one of the Haifan Bassaridian ports or inland-linked market cities.
	["Keybir-Aviv"] = {weights={AZS=4,GGS=3,TAP=3,HSA=2,SDE=2,SVI=1}},
	["Tel-Amin"] = {weights={GGS=4,AZS=3,VDE=3,HSA=2,TAP=2,SVI=1}},
	["Diamandis"] = {weights={TAP=3,SVI=3,AUM=2,RNB=2,MIR=2,STY=1}},
	["Jogi"] = {weights={GGS=4,TAP=3,AZS=3,MIR=2,SVI=2,SDE=1}},
	["Lewisburg"] = {weights={HSA=3,GGS=3,TAP=2,SDE=2,AZS=2,ACC=1}},
	["Thermosalem"] = {weights={SVI=4,TAP=3,AZS=2,HSA=2,ALB=2,CAT=1}},
	["Akróstadium"] = {weights={HSA=3,TAP=3,MIR=3,GGS=2,AZS=1,CICL=1}},
	["Sufriya"] = {weights={VDE=3,AZS=3,SVI=2,TAP=2,GGS=2,ALB=1}},
	["Lykopolis"] = {weights={SDE=3,TAP=3,GGS=2,HSA=2,AZS=2,CAT=1}},
	["Tonar"]          = {weights={SDE=2,SVI=2,VDE=2,TAP=1,CICL=1}},
	["Bashkim"]        = {weights={VDE=3,SVI=2,TAP=1}},
	["Ourid"]          = {weights={VDE=3,SVI=2,TAP=1}},
	["Ardclach"]       = {weights={AZS=2,SVI=2,TAP=1,CON=1}},

	-- Alperkin necromancy / ancestor-rite hotspots
	-- These weights do not make ordinary ancestor veneration illicit.
	-- They only make the relevant investigative, funerary, archival,
	-- psychological-stability, purification, and health orders more likely
	-- in cities where forbidden necromancy or corrupted ancestor rites may appear.
	["The Alpazkigz"] = {weights={STY=5, UMB=5, SDE=3, TAP=3, SVI=2, ALB=2, VDE=2, RNB=1}},
	["Odiferia"]     = {weights={UMB=6, STY=5, SDE=4, TAP=4, SVI=3, ALB=3, VDE=3, GGS=2}},
	["Erythros"]     = {weights={STY=5, UMB=4, CON=3, SDE=3, TAP=3, RNB=2}},
	["Nexa"]         = {weights={SDE=6, UMB=4, STY=3, TAP=3, ILL=2, EOF=2}},
	["Pyralis"]      = {weights={STY=5, UMB=4, SDE=3, IGA=3, TAP=3, SVI=2, ALB=2}},
	["Acheron"]      = {weights={STY=6, UMB=5, SDE=3, TAP=3, SVI=3, ALB=2, VDE=2}},
	["Aureum"]       = {weights={GGS=4, SDE=4, UMB=4, STY=3, TAP=3, ALB=3, SVI=2, ILL=2}},
	["Somniumpolis"] = {weights={RNB=5, UMB=4, STY=4, TAP=4, SDE=3, SVI=2, ALB=2}},
}

local function getProfile(cityName)
	return cityProfiles[cityName] or {
		weights={AUM=1,HSA=1,IGA=1,CHS=1,GGS=1,AZS=1,RNB=1,EOF=1,UMB=1,VDE=1,CON=1,SVI=1,ALB=1,CICL=1,ACC=1,STY=1,SYL=1,MIR=1,CAT=1,SDE=1,AMI=1,ILL=1,TAP=1}
	}
end

--------------------------------------------------------------------------------
-- 14) ORDERS (missionary-capable cult list)
-- NOTE: These names must match GeneralPortOperations ritualCults list + Temple Aprobelle.
--------------------------------------------------------------------------------
local orders = {
	{key="AUM", name="Order of Aurora Mystica"},
	{key="HSA", name="Harmony Sanctum"},
	{key="IGA", name="Ignis Aeternum"},
	{key="CHS", name="Celestial Harmony Sect"},
	{key="GGS", name="Guild of Golden Shadows"},
	{key="AZS", name="Azure Sentinel Sect"},
	{key="RNB", name="Reverie Nebulous"},
	{key="EOF", name="Eon Fellowship"},
	{key="UMB", name="Order of the Umbral Oracle"},
	{key="VDE", name="Mystery of the Verdant Embrace"},
	{key="CON", name="Conclave Illuminara"},
	{key="SVI", name="Sanctum Vitalis"},
	{key="ALB", name="Temple Alabaster"},
	{key="CICL", name="Court of the Ironclad"},
	{key="ACC", name="Accord Concordia"},
	{key="STY", name="Mystery of the Stygian Veil"},
	{key="SYL", name="Sylvan Fellowship"},
	{key="MIR", name="Mystery of Red Mirth"},
	{key="CAT", name="Rex Catonis"},
	{key="SDE", name="Sanctum Delphica"},
	{key="AMI", name="Ordo Amicitia"},
	{key="ILL", name="Temple Illuminata"},
	{key="TAP", name="Temple Aprobelle"},
}

local orderByName = {}
for _, o in ipairs(orders) do orderByName[o.name] = o end

--------------------------------------------------------------------------------
-- 15) SEVERITY + ORDER/TASK CHOICE
--------------------------------------------------------------------------------
    --------------------------------------------------------------------------------
    -- 14.1) NOCTIC CITY / CULT WEIGHTING
    --
    -- Noctic Vampirism is more likely to appear where Noctic-Rabrev, Alperkin
    -- inheritance, dream rites, raw-leaf custody, or Skødan mirror-vigil customs
    -- are already locally important.
    --------------------------------------------------------------------------------

    local nocticCityRisk = {
        ["Somniumpolis"]    = 8,
        ["Odiferia"]        = 8,
        ["Erythros"]        = 7,
        ["Acheron"]         = 6,
        ["Norsolyra"]       = 6,
        ["Skøda"]           = 6,
        ["The Alpazkigz"]    = 6,
        ["Nexa"]            = 4,
        ["Pyralis"]         = 4,
        ["Vaeringheim"]     = 3,
        ["Jogi"]            = 3,
        ["Aurelia"]         = 3,
    }

    local nocticOrderBoost = {
        RNB = 4, -- Reverie Nebulous: dream entanglement, nightmares, trance
        SVI = 4, -- Sanctum Vitalis: clinic response, anemia, lesions, fever
        ALB = 3, -- Temple Alabaster: nocticine medicine and purity controls
        VDE = 4, -- Verdant Embrace: Noctic-Rabrev fields, wetlands, plant custody
        STY = 3, -- Stygian Veil: funerary rites, terminal transformation
        UMB = 3, -- Umbral Oracle: night rites, shadow, hidden revelation
        CON = 3, -- Conclave Illuminara: Tarsica, lunar timing, nocticines
        GGS = 3, -- Golden Shadows: smuggling, forged permits, counter-fraud
        TAP = 4, -- Temple Aprobelle: panic control, trauma, rumor suppression
        HSA = 2, -- Harmony Sanctum: family/community conflict stabilization
        SDE = 2, -- Sanctum Delphica: registries, documents, doctrine quarantine
        AMI = 2, -- Ordo Amicitia: relief for afflicted households
        AZS = 1, -- Azure Sentinel: canal/port custody and wetland movement
        CAT = 1, -- Rex Catonis: legal registration and enforcement
    }

    local nocticCitySpecificBoost = {
        ["Somniumpolis"] = {RNB=5, TAP=4, SVI=4, ALB=3, VDE=3},
        ["Odiferia"]    = {VDE=6, SVI=4, TAP=4, ALB=3, GGS=3},
        ["Erythros"]    = {CON=4, RNB=4, STY=3, TAP=3, SDE=2},
        ["Acheron"]     = {STY=6, SVI=3, ALB=3, UMB=3, TAP=3},
        ["Norsolyra"]   = {VDE=5, RNB=4, SVI=3, SYL=2, TAP=3},
        ["Skøda"]       = {UMB=6, CON=4, STY=4, SDE=3, TAP=3, GGS=2},
        ["The Alpazkigz"] = {VDE=4, STY=4, RNB=4, TAP=3, SVI=3},
        ["Jogi"]        = {GGS=3, MIR=2, TAP=3, SVI=2},
        ["Aurelia"]     = {CON=3, SDE=2, TAP=3, RNB=2},
        ["Vaeringheim"] = {GGS=3, TAP=3, SVI=2, ALB=2, CAT=2},
    }

    local function addNocticBoostsToWeights(weighted, seen, cityName)
        local risk = nocticCityRisk[cityName] or 0
        if risk <= 0 then return end

        local citySpecific = nocticCitySpecificBoost[cityName] or {}
        local riskBump = math.floor(risk / 2)

        for _, it in ipairs(weighted) do
            local base = nocticOrderBoost[it.key] or 0
            local localBoost = citySpecific[it.key] or 0
            if base > 0 or localBoost > 0 then
                it.w = it.w + base + localBoost + riskBump
            end
        end

        for key, base in pairs(nocticOrderBoost) do
            if not seen[key] then
                local localBoost = citySpecific[key] or 0
                if base > 0 or localBoost > 0 then
                    weighted[#weighted+1] = {key=key, w=base + localBoost + riskBump}
                    seen[key] = true
                end
            end
        end
    end

	--------------------------------------------------------------------------------
	-- 14.2) ALPERKIN NECROMANCY / ANCESTOR-RITE DISTURBANCE WEIGHTING
	--
	-- This system distinguishes sanctioned Alperkin ancestor rites from forbidden
	-- necromancy. It increases the odds that the correct cults are selected in
	-- known hotspots, but the actual necromancy task remains rare and is handled
	-- by maybeAlperkinNecromancyTask().
	--------------------------------------------------------------------------------
	local necromancyCityRisk = {
		["The Alpazkigz"] = 10, -- Amanitian Lowlands / Gloom Forest ancestor rites
		["Odiferia"]     = 10, -- Southern Wetlands: forbidden swamp necromancy
		["Pyralis"]      = 8,  -- Volcanic Tooth Caverns / preserved ancestors
		["Erythros"]     = 8,  -- tomb spirits and ancestor voices
		["Acheron"]      = 8,  -- revenants, roadside apparitions, canyon tombs
		["Nexa"]         = 7,  -- forbidden scholarly necromancy
		["Aureum"]       = 7,  -- elite corpse-preservation experiments
		["Somniumpolis"] = 7,  -- dream-necromancy and dead-speaking dreams
	}

	local necromancyOrderBoost = {
		UMB = 6, -- Order of the Umbral Oracle: occult investigation / spirit-binding
		STY = 6, -- Mystery of the Stygian Veil: funerary custody / tombs / the dead
		SDE = 4, -- Sanctum Delphica: forbidden rites, manuscripts, quarantine
		TAP = 4, -- Temple Aprobelle: panic, hysteria, public order
		SVI = 2, -- Sanctum Vitalis: corpse handling / disease concerns
		ALB = 2, -- Temple Alabaster: purification / contamination concerns
		RNB = 2, -- Reverie Nebulous: dream-necromancy
		VDE = 1, -- Verdant Embrace: wetlands, Gloom Forest, grave-grove settings
		GGS = 1, -- Golden Shadows: elite funding, trafficking, covert evidence
		CON = 1, -- Conclave Illuminara: nocturnal interpretation / lunar timing
	}

	local necromancyCitySpecificBoost = {
		["The Alpazkigz"] = {STY=5, UMB=5, SDE=3, TAP=3, VDE=2},
		["Odiferia"]     = {UMB=6, STY=5, SDE=4, TAP=4, SVI=3, ALB=3, VDE=3},
		["Pyralis"]      = {STY=5, UMB=4, SDE=3, IGA=2, ALB=2},
		["Erythros"]     = {STY=5, UMB=4, CON=3, SDE=3, TAP=3},
		["Acheron"]      = {STY=6, UMB=5, SVI=3, ALB=2, VDE=2},
		["Nexa"]         = {SDE=6, UMB=4, STY=3, ILL=2, EOF=2},
		["Aureum"]       = {GGS=4, SDE=4, UMB=4, ALB=3, SVI=2},
		["Somniumpolis"] = {RNB=5, UMB=4, STY=4, TAP=4, SDE=3},
	}

	local function addNecromancyBoostsToWeights(weighted, seen, cityName)
		local risk = necromancyCityRisk[cityName] or 0
		if risk <= 0 then return end

		local citySpecific = necromancyCitySpecificBoost[cityName] or {}
		local riskBump = math.floor(risk / 2)

		for _, it in ipairs(weighted) do
			local base = necromancyOrderBoost[it.key] or 0
			local localBoost = citySpecific[it.key] or 0
			if base > 0 or localBoost > 0 then
				it.w = it.w + base + localBoost + riskBump
			end
		end

		for key, base in pairs(necromancyOrderBoost) do
			if not seen[key] then
				local localBoost = citySpecific[key] or 0
				if base > 0 or localBoost > 0 then
					weighted[#weighted+1] = {key=key, w=base + localBoost + riskBump}
					seen[key] = true
				end
			end
		end
	end

local function inferSeverity(cityTier, taskSev, threatUrg)
	local sev = tostring(taskSev or "green")
	if cityTier == "major" and sev == "green" then sev = "yellow" end
	if urgencyScore(threatUrg) >= 3 then
		sev = "red"
	elseif urgencyScore(threatUrg) == 2 and sev == "green" then
		sev = "yellow"
	end
	return sev
end

    local function pickOrder(nextInt, cityName, threat)
        local profile = getProfile(cityName)
        local weighted = {}
        local seen = {}

        for key, w in pairs(profile.weights or {}) do
            weighted[#weighted+1] = {key=key, w=tonumber(w) or 0}
            seen[key] = true
        end

        -- Noctic city bump: prefer the cults most likely to handle raw Noctic-Rabrev,
        -- Noctic Vampirism, dream disturbance, health evaluation, registration,
        -- smuggling, and family/community stabilization.
        addNocticBoostsToWeights(weighted, seen, cityName)

			-- Alperkin necromancy bump: prefer the cults most likely to distinguish
			-- sanctioned ancestor rites from forbidden spirit-binding, corpse animation,
			-- grave desecration, illicit resurrection, and prohibited funerary texts.
			addNecromancyBoostsToWeights(weighted, seen, cityName)

        -- Threat bump: prefer Aprobelle + safety/health + shadows.
        if threat and urgencyScore(threat.urg) >= 2 then
            for _, it in ipairs(weighted) do
                if it.key == "TAP" or it.key == "SVI" or it.key == "AZS" or it.key == "GGS" or it.key == "SDE" then
                    it.w = it.w + 2
                end
            end
        end

        local pickedKey = pickWeighted(nextInt, weighted).key

        for _, o in ipairs(orders) do
            if o.key == pickedKey then return o end
        end

        return orders[1]
    end

local function pickTask(nextInt, cultName, sev, usedIdx)
  local pool = tasksByCult[cultName] or tasksByCult["Harmony Sanctum"] or {}

  local weighted = {}
  for idx, t in ipairs(pool) do
    if not (usedIdx and usedIdx[idx]) then
      local w = 1
      if t.sev == sev then w = 4
      elseif sev == "red" and t.sev == "yellow" then w = 2
      elseif sev == "yellow" and t.sev == "green" then w = 2 end
      table.insert(weighted, {t=t, w=w, idx=idx})
    end
  end

  if #weighted == 0 then
    for idx, t in ipairs(pool) do
      local w = 1
      if t.sev == sev then w = 4
      elseif sev == "red" and t.sev == "yellow" then w = 2
      elseif sev == "yellow" and t.sev == "green" then w = 2 end
      table.insert(weighted, {t=t, w=w, idx=idx})
    end
  end

  if #weighted == 0 then
    return {text="Routine rites and civic support (no task pool configured).", sev="green"}, nil
  end

  local picked = pickWeighted(nextInt, weighted)
  return picked.t, picked.idx
end

	--------------------------------------------------------------------------------
	-- 15.0) RARE ALPERKIN NECROMANCY / FORBIDDEN ANCESTOR-RITE OPERATIONS
	--
	-- These are rare city-specific overrides. They should not replace ordinary
	-- Alperkin ancestor traditions. The text deliberately distinguishes sanctioned
	-- ancestor rites from forbidden necromancy involving spirit-binding, corpse
	-- animation, grave desecration, illicit resurrection, or prohibited texts.
	--------------------------------------------------------------------------------
	local necromancyResponderKeys = {
		UMB = true,
		STY = true,
		SDE = true,
		TAP = true,
		SVI = true,
		ALB = true,
		RNB = true,
		GGS = true,
	}

	local necromancyResponderLabel = {
		UMB = "Umbral Oracle occult-investigation mission",
		STY = "Stygian Veil funerary-custody mission",
		SDE = "Delphica restricted-rite review",
		TAP = "Aprobelle panic-control intervention",
		SVI = "Vitalis corpse-handling and disease-screening support",
		ALB = "Alabaster purification and contamination-control support",
		RNB = "Reverie Nebulous dream-necromancy assessment",
		GGS = "Golden Shadows covert evidence inquiry",
	}

	local necromancyEventsByCity = {
		["The Alpazkigz"] = {
			T("ancestor-rite auditors are dispatched after elders report that preserved ancestral remains have been addressed through coercive rather than sanctioned rites; grave stewards separate accepted ancestor veneration from suspected spirit-binding.", "yellow"),
			T("Gloom Forest burial paths are placed under watch after witnesses hear ancestral voices repeating commands from sealed family tombs; custody seals are checked and unauthorized night offerings are removed.", "yellow"),
			T("a red-posture investigation begins after a prohibited rite allegedly binds ancestral spirits to exposed remains near a sacred burial grove; movement is restricted while funerary stewards and occult investigators secure the site.", "red"),
		},
		["Odiferia"] = {
			T("wetland grave markers are found overturned beside binding cords, bone charms, and forbidden swamp-script; missionaries seal the path, protect witnesses, and separate local dark Alp customs from illicit necromancy.", "yellow"),
			T("isolated swamp shrines are inspected after reports of rogue necromancers attempting to bargain with preserved dead; unauthorized offerings are seized and spirit-binding claims are recorded under seal.", "yellow"),
			T("a red-level swamp cult response is activated after grave desecration, corpse movement, and forced ancestral invocations are reported in the southern wetlands; quarantine ropes are posted and night travel is suspended.", "red"),
		},
		["Pyralis"] = {
			T("Volcanic Tooth Cavern stewards request assistance after preserved ancestral remains are found repositioned beside ash-script invocations; relic custody is rebuilt and unauthorized tomb access is suspended.", "yellow"),
			T("mummified elite remains in a heated cavern tomb show signs of ritual tampering; missionaries verify which ancestor rites were sanctioned and which inscriptions match forbidden resurrection formulae.", "yellow"),
			T("a red-posture tomb lockdown is ordered after a volcanic ancestor cult attempts to animate preserved remains using ember-offerings and prohibited names; cavern entrances are sealed pending doctrinal review.", "red"),
		},
		["Erythros"] = {
			T("tomb-spirit reports intensify after families hear ancestral voices from sealed crypts; Stygian custody stewards verify offerings while Umbral witnesses test for coercive spirit-binding.", "yellow"),
			T("ancestor-song vigils are paused after a chant leader repeats names not recorded in the family death ledgers; missionaries distinguish accepted memorial practice from necromantic invocation.", "yellow"),
			T("a red-level response begins after tomb voices identify the living by hidden names and demand blood-offerings; the precinct is cleared, witnesses are shielded, and ritual transcripts are quarantined.", "red"),
		},
		["Nexa"] = {
			T("a private study circle is investigated after students copy funerary diagrams from restricted manuscripts; pages are seized and participants are redirected into sanctioned theological review.", "yellow"),
			T("forbidden scholarly necromancy is suspected after marginal notes describe corpse animation as a calendrical experiment; Delphica scribes quarantine the archive shelf and record all readers.", "yellow"),
			T("a red-posture archive lockdown follows evidence of an attempted resurrection rite using prohibited commentaries; manuscripts are relocated under escort and public explanation is kept deliberately narrow.", "red"),
		},
		["Acheron"] = {
			T("roadside apparitions near canyon tombs prompt funerary patrols; missionaries interview travelers, inspect grave ledges, and remove coercive offerings left for wandering dead.", "yellow"),
			T("revenant claims spread after miners report figures walking from abandoned cliff burrows; Stygian stewards compare burial records while Umbral observers check for binding marks.", "yellow"),
			T("a red-level canyon response is triggered after remains are found arranged beside petrified roots and spoken-name tablets; the road is closed and corpse custody is transferred under seal.", "red"),
		},
		["Aureum"] = {
			T("elite-funded corpse-preservation experiments are reviewed after private patrons request unlawful ancestral consultation; Golden Shadows auditors trace payments while Delphica scribes seize ritual notes.", "yellow"),
			T("a hidden preservation chamber beneath a wealthy household is inspected for illicit resurrection instruments; medical, purification, and funerary witnesses are assigned before remains are moved.", "yellow"),
			T("a red-posture intervention begins after evidence suggests a patron funded spirit-binding to keep a dead adviser communicative; ledgers are seized and the household is placed under ritual quarantine.", "red"),
		},
		["Somniumpolis"] = {
			T("dream-necromancy reports are triaged after mourners claim the dead are speaking through shared dreams; Reverie guides record narratives while Umbral witnesses screen for coercive invocations.", "yellow"),
			T("sleep-circle permits are reviewed after a grieving household attempts to summon an ancestor through dream-binding; counselors separate mourning visions from forbidden manipulation of the dead.", "yellow"),
			T("a red-level dream-necromancy response is activated after multiple sleepers repeat the same dead voice demanding physical remains; the household is stabilized and ritual notes are sealed.", "red"),
		},
	}

	local necromancyJointCrisis = {
		T("Exceptional Alperkin necromancy crisis: the Order of the Umbral Oracle, Mystery of the Stygian Veil, and Sanctum Delphica respond jointly after evidence emerges that a prohibited rite has successfully bound ancestral spirits to physical remains; ritual sites are quarantined, movement is restricted, corpse custody is rebuilt, and all manuscripts are placed under doctrinal seal.", "red"),
		T("Exceptional cross-cult necromancy investigation: Umbral Oracle investigators, Stygian Veil funerary stewards, and Delphica archivists assume joint control after corpse animation, grave desecration, and forbidden resurrection formulae are linked to the same Alperkin ritual network; public statements stress that sanctioned ancestor rites remain lawful.", "red"),
	}

	local function maybeAlperkinNecromancyTask(nextInt, cityName, order, desiredSev)
		local risk = necromancyCityRisk[cityName] or 0
		if risk <= 0 then return nil end
		if not order or not necromancyResponderKeys[order.key] then return nil end

		-- Extremely rare joint crisis. Only the three principal responder cults
		-- should carry this as the visible row order.
		if order.key == "UMB" or order.key == "STY" or order.key == "SDE" then
			local crisisChance = 2
			if risk >= 10 then
				crisisChance = 7
			elseif risk >= 8 then
				crisisChance = 4
			end
			if (nextInt() % 1000) < crisisChance then
				return pickFromList(nextInt, necromancyJointCrisis, nil)
			end
		end

		-- Ordinary necromancy incidents remain rare. Higher-risk cities and
		-- already-heightened days increase the chance slightly.
		local chance = math.floor(risk / 2)
		if desiredSev == "yellow" then
			chance = chance + 3
		elseif desiredSev == "red" then
			chance = chance + 6
		end

		if (nextInt() % 100) >= chance then
			return nil
		end

		local pool = necromancyEventsByCity[cityName]
		if not pool or #pool == 0 then return nil end

		local chosen = pickFromList(nextInt, pool, nil)
		local label = necromancyResponderLabel[order.key] or "Alperkin necromancy-response mission"

		return T(
			label .. ": " .. chosen.text ..
			" Sanctioned ancestor rites remain distinguished from forbidden necromancy involving spirit-binding, corpse animation, grave desecration, illicit resurrection, or prohibited funerary texts.",
			chosen.sev or "yellow"
		)
	end

--------------------------------------------------------------------------------
-- 15.1) SOUTHERN STRAIT MISSIONARY OPERATIONS
--
-- These special operations may appear only when the generated city row is one
-- of the Haifan Bassaridian cities. The city remains the listed station, but
-- the task text may describe movement across the Southern Strait, former
-- Maritime Markets corridors, pilgrimage routes, inland transfer points,
-- quay markets, ferry lines, canal mouths, or rail-linked cargo routes.
--------------------------------------------------------------------------------

local southernStraitMissionLabels = {
	"Southern Strait missionary circuit",
	"Southern Strait pilgrimage-route mission",
	"Former Maritime Markets liaison mission",
	"Southern Strait dockside intervention",
	"Southern Strait inland-route mission",
	"Southern Strait shrine-and-quay mission",
	"Southern Strait corridor-stabilization mission",
}

local southernStraitGenericOps = {
	T("missionaries move between shrine piers, ferry queues, inland transfer offices, and market lanes to stabilize lawful religious movement while recording interference by brokers, smugglers, or hostile cult agents.","yellow"),
	T("missionaries conduct a multi-stop route audit through quay markets, road-transfer yards, and rail-linked cargo offices; local stewards collect testimony while Temple Bank clerks verify custody records.","yellow"),
	T("missionaries establish a temporary calm station near a port-to-inland transfer point, separating pilgrims, cargo handlers, and anxious families while reports are routed back through the local Haifan station.","green"),
}

local southernStraitOpsByCultKey = {
	AUM = {
		T("Order of Aurora Mystica missionaries conduct dawn reassurance rites across multiple harbor shrines, inspect lamp stations for false-signal use, and gather civic complaints from travelers arriving before inspection hours.","green"),
		T("Order of Aurora Mystica missionaries run a first-light grievance circuit from the quay to inland registry offices; witnesses reporting intimidation by brokers or smugglers are moved through protected morning routes.","yellow"),
		T("Order of Aurora Mystica missionaries bless harbor beacons and shrine lamps while correcting rumors that false lights or unusual dawn colors signal divine approval for illicit passage.","yellow"),
	},

	HSA = {
		T("Harmony Sanctum missionaries mediate a dispute between dockworkers, ferry crews, shrine porters, and caravan handlers; neutral lanes are marked so trade and pilgrimage traffic can continue without retaliation.","green"),
		T("Harmony Sanctum missionaries establish a reconciliation table for families, vendors, and sailors divided by smuggling accusations; testimony is separated from rumor before being routed to Temple Bank clerks.","yellow"),
		T("Harmony Sanctum missionaries conduct a multi-ward crowd discipline mission through quay markets and inland caravan yards, using steward lanes to prevent merchant quarrels from becoming anti-Bassaridian unrest.","yellow"),
	},

	IGA = {
		T("Ignis Aeternum missionaries inspect lantern stores, brazier permits, and warehouse fuel rooms along the Southern Strait after rumors of arson cover for smuggling transfers.","yellow"),
		T("Ignis Aeternum missionaries conduct firewatch rites at cargo depots and harbor shrines, marking illegal fuel caches and unsafe pyre materials for seizure by local authorities.","yellow"),
		T("Ignis Aeternum missionaries support emergency evacuation after a dockside fire scare, keeping torch-bearing celebrants away from bonded warehouses and preserving evidence of possible sabotage.","red"),
	},

	CHS = {
		T("Celestial Harmony Sect missionaries correct false omen charts circulating among sailors, pilgrims, and night-ferry crews; approved sky-sign guidance is posted at shrine piers and inland waiting halls.","green"),
		T("Celestial Harmony Sect missionaries conduct a night-navigation reassurance circuit, separating legitimate star-lore from coded smuggler signals used near the former Maritime Markets routes.","yellow"),
		T("Celestial Harmony Sect missionaries open quiet stations for travelers frightened by sky-sign rumors while clerks record which brokers spread the panic through ferry lines.","yellow"),
	},

GGS = {
	T("Guild of Golden Shadows missionaries trace forged shrine permits, false port vouchers, and suspicious cargo seals through dockside broker houses and inland rail offices.","yellow"),
	T("Guild of Golden Shadows missionaries conduct a covert ledger comparison between market scribes, ferry agents, and warehouse clerks to expose cargo-identity laundering in former Maritime Markets networks.","yellow"),
	T("Guild of Golden Shadows missionaries identify a counterfeit Temple seal chain moving through pilgrimage baggage; suspect stamps are catalogued and witness statements sealed for review.","red"),

	T("Guild of Golden Shadows missionaries convene a Southern Strait trade-agreement parley with ferry stewards, orchard carriers, quay merchants, shrine porters, and inland caravan representatives; proposed terms are written into a local compact and forwarded to [[General Port of Lake Morovia]] registry clerks.","green"),
	T("Guild of Golden Shadows missionaries establish a provisional trade agreement between a harbor community and nearby inland producers, using voucher reconciliation, witness seals, and Temple Bank notation to prevent old Maritime Markets brokers from distorting the terms.","yellow"),
	T("Guild of Golden Shadows missionaries promote [[General Port of Lake Morovia]] membership among local merchants, small-craft owners, shrine suppliers, and caravan brokers by explaining registry protections, lawful access to supervised markets, and the commercial benefits of audited participation.","green"),
	T("Guild of Golden Shadows missionaries conduct a General Port investment and membership briefing in a quay market, identifying trustworthy local partners, recording investor interest, and separating legitimate community capital from smuggler-backed broker money.","yellow"),
	T("Guild of Golden Shadows missionaries organize a trade-normalization circuit across ferry landings and inland transfer yards, encouraging local communities to enter General Port membership while documenting forged contracts, intimidation, and false debt claims.","yellow"),
	T("Guild of Golden Shadows missionaries broker a community-to-port supply arrangement for lawful agricultural, fishery, craft, or shrine goods; General Port membership forms, cargo-origin notes, and Temple Bank witness records are completed on site.","green"),
	T("Guild of Golden Shadows missionaries investigate resistance to General Port membership after rumors spread that registry enrollment will erase local trading privileges; the mission corrects false claims while preserving valid community exemptions for review.","yellow"),
	T("Guild of Golden Shadows missionaries expose a broker faction attempting to sabotage a new General Port trade agreement by planting false investment notes, counterfeit seals, and misleading debt ledgers among local merchants.","red"),
	T("Guild of Golden Shadows missionaries escort local community representatives through the first stage of General Port enrollment, protecting membership papers, investment pledges, and cargo ledgers from theft, intimidation, or alteration by former Maritime Markets intermediaries.","yellow"),
	T("Guild of Golden Shadows missionaries create a confidential list of community merchants suitable for future General Port investment partnerships, excluding suspected smugglers, forged-voucher carriers, and broker agents tied to unlawful corridor disruption.","yellow"),
},

	AZS = {
		T("Azure Sentinel Sect missionaries inspect ferry queues, canal approaches, rope lanes, pilgrim piers, and water-safety markers while documenting unsafe crossings used by smugglers.","yellow"),
		T("Azure Sentinel Sect missionaries conduct a waterfront safety circuit across the Southern Strait, marking storm lanes, rescue points, and suspicious skiff landings for follow-up by port authorities.","yellow"),
		T("Azure Sentinel Sect missionaries respond to a dangerous ferry-ramp surge near a shrine pier, opening rescue lanes and separating pilgrims from unregistered cargo handlers.","red"),
	},

	RNB = {
		T("Reverie Nebulous missionaries record nightmare claims among sailors, ferry workers, and dockside families, distinguishing dream contagion rumors from Noctic cargo panic and ordinary smuggling fear.","yellow"),
		T("Reverie Nebulous missionaries open supervised rest spaces for distressed travelers moving through the Southern Strait pilgrimage routes; rumor patterns are mapped by quay and ferry station.","yellow"),
		T("Reverie Nebulous missionaries intervene after a mass dream-panic episode in a harbor ward, moving witnesses away from crowd pressure and preserving testimony under seal.","red"),
	},

	EOF = {
		T("Eon Fellowship missionaries reconcile old Maritime Markets family oaths with current Bassaridian registry expectations, recording continuity pledges from ship families and hereditary port stewards.","green"),
		T("Eon Fellowship missionaries conduct an intergenerational testimony circuit along the Southern Strait, preserving family records before broker houses can alter lineage claims or debt histories.","yellow"),
		T("Eon Fellowship missionaries stabilize a dispute over ancestral trading privileges by redirecting claimants into formal Temple Bank testimony and registry review.","yellow"),
	},

	UMB = {
		T("Order of the Umbral Oracle missionaries conduct a night-route inquiry through alleys, ferry shelters, and shrine back rooms where courier rumors suggest hidden anti-Bassaridian coordination.","yellow"),
		T("Order of the Umbral Oracle missionaries interview night watchmen, lamplighters, and shrine attendants to separate legitimate nocturnal rites from covert courier drops.","yellow"),
		T("Order of the Umbral Oracle missionaries activate a red-posture shadow-watch after masked agitators attempt to use pilgrimage crowds as cover for illicit ritual movement.","red"),
	},

	VDE = {
		T("Mystery of the Verdant Embrace missionaries inspect plant offerings, medicinal herbs, orchard cargo, and market produce moving from the quay into inland shrine routes.","green"),
		T("Mystery of the Verdant Embrace missionaries trace suspicious plant bundles through wet markets and ferry landings, checking whether ritual herbs are being used to conceal contraband.","yellow"),
		T("Mystery of the Verdant Embrace missionaries secure a contaminated produce consignment after rumors connect it to illicit Noctic-Rabrev handling along the Southern Strait.","red"),
	},

	CON = {
		T("Conclave Illuminara missionaries review Tarsica-timed pilgrimage movements, ferry schedules, and night-vigil routes to prevent lunar observances from being exploited by smugglers.","green"),
		T("Conclave Illuminara missionaries correct false lunar-tide claims used by brokers to justify unauthorized night crossings; revised timing guidance is posted at harbor shrines.","yellow"),
		T("Conclave Illuminara missionaries open a controlled moonlight vigil route after panic spreads through a ferry queue; stewards record names and prevent unsupervised dispersal.","yellow"),
	},

	SVI = {
		T("Sanctum Vitalis missionaries escort clinic packets, triage notes, and medical supplies between harbor clinics, ferry stations, and inland care posts along the Southern Strait.","yellow"),
		T("Sanctum Vitalis missionaries conduct health screening at a pilgrimage landing, separating fever, exhaustion, Noctic concerns, and ordinary travel sickness before rumors can spread.","yellow"),
		T("Sanctum Vitalis missionaries activate a red-posture medical transfer after hostile crowds threaten registered patients moving from a dockside clinic to an inland shelter.","red"),
	},

	ALB = {
		T("Temple Alabaster missionaries inspect sealed tinctures, balms, medicines, and purity records moving through Southern Strait warehouses and clinic piers.","green"),
		T("Temple Alabaster missionaries identify counterfeit medicine jars in dockside cargo and redirect the consignment through Temple Bank custody for purity review.","yellow"),
		T("Temple Alabaster missionaries conduct an emergency medicine-chain audit after a forged clinic seal appears in ferry freight moving inland.","red"),
	},

	CICL = {
		T("Court of the Ironclad missionaries establish a protective witness lane for threatened shrine clerks, ferry stewards, and registry witnesses reporting smuggling activity.","yellow"),
		T("Court of the Ironclad missionaries support a disciplined perimeter around a seized cargo stack, preventing vigilante interference while legal testimony is gathered.","yellow"),
		T("Court of the Ironclad missionaries activate crisis protection for a high-risk detainee transfer connected to pirate recruiting near a pilgrimage route.","red"),
	},

	ACC = {
		T("Accord Concordia missionaries negotiate temporary passage terms between ferry operators, caravan leaders, shrine porters, and market stewards to keep lawful movement open.","green"),
		T("Accord Concordia missionaries draft a written concord between rival broker families accused of obstructing pilgrims and lawful cargo moving through the Southern Strait.","yellow"),
		T("Accord Concordia missionaries convene an emergency truce after dockside violence threatens to spread into inland caravan yards and shrine courtyards.","red"),
	},

	STY = {
		T("Mystery of the Stygian Veil missionaries conduct death-at-sea rites for recovered bodies while ensuring mourning families are not exploited by brokers or rumor sellers.","yellow"),
		T("Mystery of the Stygian Veil missionaries escort funerary offerings through a contested harbor ward, separating legitimate mourning from illicit ritual theater.","yellow"),
		T("Mystery of the Stygian Veil missionaries stabilize a red-posture mortuary transfer after violent rumors spread around unidentified remains from the Southern Strait.","red"),
	},

	SYL = {
		T("Sylvan Fellowship missionaries inspect coastal groves, animal lanes, and shrine gardens near ferry roads where displaced wildlife and pilgrim traffic have begun to clash.","green"),
		T("Sylvan Fellowship missionaries guide a low-impact pilgrimage route around sensitive shoreline habitat while documenting illegal cutting, trapping, and smuggler paths.","yellow"),
		T("Sylvan Fellowship missionaries coordinate emergency animal-control and relief stations after floodwater drives coastal creatures into a crowded shrine district.","red"),
	},

	MIR = {
		T("Mystery of Red Mirth missionaries manage reveler lanes during a dockside festival, separating licensed celebration from smuggler cover and unsafe intoxication.","yellow"),
		T("Mystery of Red Mirth missionaries conduct a merriment-control circuit from ferry markets to inland tavern lanes, using steward chants to prevent crowd surges.","yellow"),
		T("Mystery of Red Mirth missionaries activate a crisis dispersal rite after festival crowds are used to conceal courier drops and provoke anti-registry disorder.","red"),
	},

	CAT = {
		T("Rex Catonis missionaries open a legal registration table for ferry workers, cargo handlers, and disputed household claims connected to Southern Strait movement.","green"),
		T("Rex Catonis missionaries review petitions over seized cargo, blocked pilgrimage routes, and alleged abuse by brokers, routing valid claims into formal hearing dockets.","yellow"),
		T("Rex Catonis missionaries conduct an emergency rights-and-duties reading after a seizure dispute threatens to turn into a dockside riot.","red"),
	},

	SDE = {
		T("Sanctum Delphica missionaries audit shrine credentials, restricted rite permits, teaching licenses, and courier papers moving through the Southern Strait corridor.","yellow"),
		T("Sanctum Delphica missionaries correct unsafe doctrine claims posted near ferry stations and market shrines, replacing them with approved quarantine and registry language.","yellow"),
		T("Sanctum Delphica missionaries lock down a compromised archive packet after forged ritual permissions are discovered in inland-route baggage.","red"),
	},

	AMI = {
		T("Ordo Amicitia missionaries distribute food, water, blankets, and family-support vouchers to pilgrims delayed by inspection queues or disrupted ferry schedules.","green"),
		T("Ordo Amicitia missionaries escort displaced families between dockside shelters, shrine courtyards, and inland aid offices while shielding them from broker recruitment.","yellow"),
		T("Ordo Amicitia missionaries activate a red-posture relief corridor after smuggling violence strands families near a port-to-inland transfer point.","red"),
	},

	ILL = {
		T("Temple Illuminata missionaries inspect teaching booths, gold-market charity records, and illumination lamps used for public instruction near Southern Strait trade lanes.","green"),
		T("Temple Illuminata missionaries correct false prosperity sermons used by brokers to lure pilgrims into unauthorized cargo schemes and debt traps.","yellow"),
		T("Temple Illuminata missionaries secure educational records and donation ledgers after a counterfeit charity ring is uncovered in a quay market.","red"),
	},

	TAP = {
		T("Temple Aprobelle missionaries conduct a rumor-control circuit across ferry queues, quay markets, shrine lanes, and inland transfer offices after panic spreads about smuggling raids.","yellow"),
		T("Temple Aprobelle missionaries map rumor nodes tied to pirate brokers, false cult warnings, and anti-Bassaridian gossip, then issue corrective language through local stewards.","yellow"),
		T("Temple Aprobelle missionaries activate a crisis social-harmony response after crowd panic threatens to block lawful pilgrimage and cargo movement through the Southern Strait.","red"),
	},
}

local function maybeSouthernStraitCultTask(nextInt, cityName, order, desiredSev)
	if not isHaifanBassaridiaCity(cityName) then return nil end
	if not order or not order.key then return nil end

	-- Frequency control:
	-- Green days: occasional; Yellow days: common; Red days: very common.
	local chance = 35
	if desiredSev == "yellow" then
		chance = 48
	elseif desiredSev == "red" then
		chance = 65
	end

	if (nextInt() % 100) >= chance then
		return nil
	end

	local pool = southernStraitOpsByCultKey[order.key] or southernStraitGenericOps
	local chosen = pickFromList(nextInt, pool, nil) or southernStraitGenericOps[1]
	local label = pickFromList(nextInt, southernStraitMissionLabels, nil) or "Southern Strait missionary circuit"

	return T(
		label .. ": " .. chosen.text ..
		" Mission remains staged and logged through " .. cityLink(cityName) ..
		", but may operate across the wider Southern Strait, former Maritime Markets corridors, pilgrimage routes, ferry lines, and inland transfer points.",
		chosen.sev or desiredSev or "yellow"
	)
end

--------------------------------------------------------------------------------
-- 16) TEMPLE BANK NOTES
--------------------------------------------------------------------------------
local bankNotes = {
	"Stipend disbursed after checklist verification; receipt countersigned by local steward.",
	"Voucher reconciliation complete; supplies logged and stored under dual-key protocol.",
	"Observer note: crowd discipline improved after posted guidance and steward coaching.",
	"Repair authorization issued; labor scheduled at off-peak hours.",
	"Custody ledger updated; medicines/artifacts sealed and moved to secure storage.",
	"Training memo filed; corrective instruction issued to volunteers for queue discipline.",
}

--------------------------------------------------------------------------------
-- 17) BUILD ONE CITY ROW
--------------------------------------------------------------------------------
local function buildCityRow(di, c, threatMap, includeThreat, showEcon, econSignals, ctx)
	local cityName = c.name
	local seed = hash31(string.format("mops:%d:%03d:%s", di.year, di.dayOfYear, cityName))
	local nextInt = lcg(seed)
	local th = includeThreat and threatMap and threatMap[cityName] or nil
        local o = pickOrder(nextInt, cityName, th)
        ctx.usedTasksByCult[o.name] = ctx.usedTasksByCult[o.name] or {}
        local used = ctx.usedTasksByCult[o.name]

        local threatUrg = th and th.urg or ""

        -- Preliminary severity gives pickTask a useful target before the exact
        -- task-driven severity is known.
        local prelimSev = "green"
        if c.tier == "major" then prelimSev = "yellow" end
        if urgencyScore(threatUrg) >= 3 then
            prelimSev = "red"
        elseif urgencyScore(threatUrg) == 2 and prelimSev == "green" then
            prelimSev = "yellow"
        end

local baseTask, pickedIdx = pickTask(nextInt, o.name, prelimSev, used)
	-- Rare Alperkin necromancy / forbidden ancestor-rite override.
	-- This happens before the Southern Strait override because it is tied to
	-- specific Alperkin and core hotspot cities rather than corridor operations.
	local necromancyTask = maybeAlperkinNecromancyTask(nextInt, cityName, o, prelimSev)
	if necromancyTask then
		baseTask = necromancyTask
		pickedIdx = nil
	end

-- If this missionary assignment is stationed in Haifan Bassaridia, it may
-- become a Southern Strait-wide cult-specific operation. This does not move
-- the city row; it only expands the operating area described in the task.
local southernStraitTask = maybeSouthernStraitCultTask(nextInt, cityName, o, prelimSev)
if southernStraitTask then
	baseTask = southernStraitTask
	pickedIdx = nil
end

if pickedIdx then used[pickedIdx] = true end

local sev = inferSeverity(c.tier, baseTask.sev, threatUrg)

	-- WX overlay
	local opsBadge, wxData = opsCell(cityName, di.year, di.dayOfYear)
	sev = bumpSevWeather(sev, wxData)
	local taskText = baseTask.text .. weatherTaskOverlay(wxData)

	-- AZS special case: rare clandestine/assassin operations under severe threat
	if o.key == "AZS" and shouldAZSGoCovert(nextInt, sev, th) then
		local covert = pickFromList(nextInt, azsCovertOps, nil)
		if covert and covert.text then
			taskText = covert.text .. weatherTaskOverlay(wxData)
			sev = "red"
		end
	end

	-- unit
	local unitName = pickOrgUnit(nextInt, sev)
	local unitMeta = org[unitName]
	local unitIndex = (nextInt() % 12) + 1
	local unitCode = string.format("%s-%s%d", o.key, unitMeta.code, unitIndex)

	-- bank note
	local bank = pickFromList(nextInt, bankNotes, nil)

	-- econ
	local econ = ""
	if showEcon and econSignals and econSignals[cityName] then
		econ = econSignals[cityName].summary or ""
	end

	return {
		style = sevStyle(sev),
		date = di.text,
		city = string.format("%s %s", getCityFlagMarkup(cityName), cityLink(cityName)),
		region = c.region,
		ops = opsBadge,
		order = getCultFlagMarkup(o.name) .. cultLink(o.name),
		unit = string.format("%s (%d) [%s]", unitName, unitMeta.size, unitCode),
		task = taskText,
		bank = bank,
		threat = (includeThreat and th and th.event) and th.event or "",
		threatUrg = (includeThreat and th and th.urg) and th.urg or "",
		sev = sev,
		econ = econ,
	}
end

--------------------------------------------------------------------------------
-- 18) MAIN TABLE
--------------------------------------------------------------------------------
function p.makeTable(frame)
	local args = (frame and frame.args) or {}
local regionFilter = tostring(args.region or "All")
local showLegend = tostring(args.showlegend or "yes"):lower() ~= "no"
local includeThreat = tostring(args.includeThreat or "no"):lower() == "yes"
local showEcon = tostring(args.showEcon or "no"):lower() == "yes"
local showRelations = tostring(args.showRelations or args.cultRelations or "no"):lower() == "yes"
local limit = tonumber(args.limit or "")
local di = getDateInfo(args)

	-- Threat map (only if requested)
	local threatMap = {}
	if includeThreat then
		local ok, rel = pcall(require, "Module:ReligiousEvents")
		if ok and rel and type(rel.makeTable) == "function" then
			threatMap = parseReligiousEventsTable(rel.makeTable())
		end
	end

	-- Econ signals (expensive; only if requested)
	local econSignals = {}
	if showEcon then
		econSignals = getEconSignals()
	end

	local out = {}
	table.insert(out, "{| class='wikitable sortable'")
	local header = "! style='white-space:nowrap;' | Date !! City !! Region !! Ops Alert !! Mystery (Cult) !! Unit !! Tasking"
	if showEcon then header = header .. " !! Economic signal" end
	header = header .. " !! Temple Bank Notes"
	if includeThreat then header = header .. " !! Threat Watch" end
	header = header .. " !! Severity"
	table.insert(out, header)

	local count = 0
	for _, c in ipairs(cityOrder) do
		if regionPass(regionFilter, c.region) then
			local row = buildCityRow(di, c, threatMap, includeThreat, showEcon, econSignals, ctx)
			table.insert(out, "|-" .. row.style)
			local line = string.format("| %s || %s || %s || %s || %s || %s || %s",
				row.date, row.city, row.region, row.ops, row.order, row.unit, row.task
			)
			if showEcon then line = line .. " || " .. (row.econ ~= "" and row.econ or "n/a") end
			line = line .. " || " .. row.bank
			if includeThreat then
				local threatCell = (row.threat ~= "" and (row.threat .. (row.threatUrg ~= "" and (" ("..row.threatUrg..")") or ""))) or "No reports / n.a."
				line = line .. " || " .. threatCell
			end
			line = line .. " || " .. row.sev
			table.insert(out, line)

			count = count + 1
			if limit and count >= limit then break end
		end
	end

	table.insert(out, "|}")

	if showLegend then
		table.insert(out, "\n<div style='margin-top:0.5em; font-size:90%;'>")
		table.insert(out, "<b>Legend:</b> ")
		table.insert(out, "<span style='background:#d4edda;padding:2px 6px;border:1px solid #c3e6cb;'>Green</span> = Routine rites / civic support&nbsp; ")
		table.insert(out, "<span style='background:#fff3cd;padding:2px 6px;border:1px solid #ffeeba;'>Yellow</span> = Heightened oversight / rumor control / targeted intervention&nbsp; ")
		table.insert(out, "<span style='background:#f8d7da;padding:2px 6px;border:1px solid #f5c6cb;'>Red</span> = Crisis response / unrest / outbreak / high-risk operations")
		table.insert(out, "<br/>Ops Alert badge reflects forecast-driven operational risk and aligns with War League / Hatch posture.")
		table.insert(out, "<br/>For overseas deployments please see [[Bassaridian_involvement_in_Corum#Corumian_Missionary_Deployment_Reports|Corumian Missionary Deployment Reports]].")
		table.insert(out, "</div>")
	end

if di.calendarEvent and di.calendarEvent ~= "" then
	table.insert(out, string.format("\n<div style='margin-top:0.5em; font-size:90%%;'><b>Calendar note:</b> %s</div>", di.calendarEvent))
end

if showRelations then
	table.insert(out, "\n== Daily cult-relations pressure ==")
	table.insert(out, renderCultRelations(args))
end

return table.concat(out, "\n")
end

--------------------------------------------------------------------------------
-- 19) SINGLE CITY VIEW
--------------------------------------------------------------------------------
function p.makeCity(frame)
	local args = (frame and frame.args) or {}
	local city = tostring(args.city or "")
	if city == "" then return "''Error: city parameter required.''" end
	if not citySet[city] then return "''Error: unknown city (must match canonical name).''" end
	local includeThreat = tostring(args.includeThreat or "no"):lower() == "yes"
	local showEcon = tostring(args.showEcon or "no"):lower() == "yes"

	local di = getDateInfo()

	local threatMap = {}
	if includeThreat then
		local ok, rel = pcall(require, "Module:ReligiousEvents")
		if ok and rel and type(rel.makeTable) == "function" then
			threatMap = parseReligiousEventsTable(rel.makeTable())
		end
	end
	local econSignals = {}
	if showEcon then econSignals = getEconSignals() end

	local region, tier = "Core", "minor"
	for _, c in ipairs(cityOrder) do
		if c.name == city then region, tier = c.region, c.tier break end
	end

	local row = buildCityRow(di, {name=city, region=region, tier=tier}, threatMap, includeThreat, showEcon, econSignals, { usedTasksByCult = {} })
	local out = {}
	table.insert(out, "{| class='wikitable'")
	local header = "! Date !! City !! Region !! Ops Alert !! Mystery (Cult) !! Unit !! Tasking"
	if showEcon then header = header .. " !! Economic signal" end
	header = header .. " !! Temple Bank Notes"
	if includeThreat then header = header .. " !! Threat Watch" end
	header = header .. " !! Severity"
	table.insert(out, header)
	table.insert(out, "|-" .. row.style)

	local line = string.format("| %s || %s || %s || %s || %s || %s || %s",
		row.date, row.city, row.region, row.ops, row.order, row.unit, row.task
	)
	if showEcon then line = line .. " || " .. (row.econ ~= "" and row.econ or "n/a") end
	line = line .. " || " .. row.bank
	if includeThreat then
		local threatCell = (row.threat ~= "" and (row.threat .. (row.threatUrg ~= "" and (" ("..row.threatUrg..")") or ""))) or "No reports / n.a."
		line = line .. " || " .. threatCell
	end
	line = line .. " || " .. row.sev
	table.insert(out, line)
	table.insert(out, "|}")
	return table.concat(out, "\n")
end

--------------------------------------------------------------------------------
-- 20) ORDER-ONLY VIEW
--------------------------------------------------------------------------------
function p.makeOrder(frame)
	local args = (frame and frame.args) or {}
	local wanted = tostring(args.order or "")
	if wanted == "" then
		return "''Error: order parameter required (use exact order name).''"
	end
	local wantedOrder = orderByName[wanted]
	if not wantedOrder then
		return "''Error: unknown order name (must match module order names).''"
	end
	local includeThreat = tostring(args.includeThreat or "no"):lower() == "yes"
	local showEcon = tostring(args.showEcon or "no"):lower() == "yes"

	local di = getDateInfo()

	local threatMap = {}
	if includeThreat then
		local ok, rel = pcall(require, "Module:ReligiousEvents")
		if ok and rel and type(rel.makeTable) == "function" then
			threatMap = parseReligiousEventsTable(rel.makeTable())
		end
	end
	local econSignals = {}
	if showEcon then econSignals = getEconSignals() end

	local out = {}
	table.insert(out, "{| class='wikitable sortable'")
	local header = "! Date !! City !! Region !! Ops Alert !! Unit !! Tasking"
	if showEcon then header = header .. " !! Economic signal" end
	header = header .. " !! Temple Bank Notes"
	if includeThreat then header = header .. " !! Threat Watch" end
	header = header .. " !! Severity"
	table.insert(out, header)

	for _, c in ipairs(cityOrder) do
		local row = buildCityRow(di, c, threatMap, includeThreat, showEcon, econSignals, { usedTasksByCult = {} })
		-- Compare by cultLink target (stable) rather than raw text
		if row.order:find(cultLink(wantedOrder.name), 1, true) or row.order:find(wantedOrder.name, 1, true) then
			table.insert(out, "|-" .. row.style)
			local line = string.format("| %s || %s || %s || %s || %s || %s",
				row.date, row.city, row.region, row.ops, row.unit, row.task
			)
			if showEcon then
				line = line .. " || " .. (row.econ ~= "" and row.econ or "n/a")
			end
			line = line .. " || " .. row.bank
			if includeThreat then
				local threatCell = (row.threat ~= "" and (row.threat .. (row.threatUrg ~= "" and (" ("..row.threatUrg..")") or ""))) or "No reports / n.a."
				line = line .. " || " .. threatCell
			end
			line = line .. " || " .. row.sev
			table.insert(out, line)
		end
	end

	table.insert(out, "|}")
	return table.concat(out, "\n")
end

--------------------------------------------------------------------------------
-- 21) CULT RELATIONS PROXY FUNCTIONS
-- These keep [[Module:CultRelationsDailyBVR]] separate, but allow pages to call
-- cult relations through this missionary module when desired.
--------------------------------------------------------------------------------

function p.cultRelations(frame)
	if not cultRel or type(cultRel.makeTable) ~= "function" then
		return "''[[Module:CultRelationsDailyBVR]] is unavailable.''"
	end

	local ok, result = pcall(cultRel.makeTable, frame)
	if ok then
		return result
	end

	return "''Cult-relations display error: " .. tostring(result) .. "''"
end

function p.cultRelationPair(frame)
	if not cultRel or type(cultRel.makePair) ~= "function" then
		return "''[[Module:CultRelationsDailyBVR]] is unavailable or does not expose makePair.''"
	end

	local ok, result = pcall(cultRel.makePair, frame)
	if ok then
		return result
	end

	return "''Cult-relation pair display error: " .. tostring(result) .. "''"
end

function p.cultRelationEvents(frame)
	if not cultRel or type(cultRel.eventLog) ~= "function" then
		return "''[[Module:CultRelationsDailyBVR]] is unavailable or does not expose eventLog.''"
	end

	local ok, result = pcall(cultRel.eventLog, frame)
	if ok then
		return result
	end

	return "''Cult-relations event-log error: " .. tostring(result) .. "''"
end

function p.cultRelationMatrix(frame)
	if not cultRel or type(cultRel.makeMatrix) ~= "function" then
		return "''[[Module:CultRelationsDailyBVR]] is unavailable or does not expose makeMatrix.''"
	end

	local ok, result = pcall(cultRel.makeMatrix, frame)
	if ok then
		return result
	end

	return "''Cult-relations matrix error: " .. tostring(result) .. "''"
end

return p