/* Skyhearth Codex — data layer
 * The 34-form Rank-2 (Adult) roster, a v0 snapshot of sim_r2_roster.py (§4),
 * plus the §6 derivations and §7 reveal-tier model. Numbers live HERE only.
 * Exposed on window for the babel canvas scripts. */

// ---- element meta (canonical raising data, app.jsx ELEMENTS) ----------------
// `pie` = the element's mechanical COLOR (numbers/adults §11.9); `axis` = its
// weight/bond temperament. Traits express a form's element color.
const CX_ELEM = {
  fire:      { name: 'Fire',      primal: 'Ifrit',     glyph: '△', color: '#b8632a', icon: 'assets/icons/fire.png',      niche: 'break · forge',   pie: 'lifespan → power',        axis: 'lean · clingy' },
  water:     { name: 'Water',     primal: 'Leviathan', glyph: '~', color: '#355e7d', icon: 'assets/icons/water.png',     niche: 'haul · tide',     pie: 'adaptability',            axis: 'steady' },
  earth:     { name: 'Earth',     primal: 'Titan',     glyph: '□', color: '#7a5230', icon: 'assets/icons/earth.png',     niche: 'carry · dig',     pie: 'Hull · durability',       axis: 'heavy · steady' },
  nature:    { name: 'Nature',    primal: 'Lakshmi',   glyph: '✿', color: '#5d7a3f', icon: 'assets/icons/nature.png',    niche: 'forage · mend',   pie: 'food · sustenance',       axis: 'steady' },
  ice:       { name: 'Ice',       primal: 'Shiva',     glyph: '◊', color: '#5a7d9a', icon: 'assets/icons/ice.png',       niche: 'still · keep',    pie: 'brittle slow-power',      axis: 'clingy' },
  lightning: { name: 'Lightning', primal: 'Ramuh',     glyph: '╱', color: '#9a7d2a', icon: 'assets/icons/lightning.png', niche: 'speed · spark',   pie: 'tempo · action-economy',  axis: 'low-bond · clingy' },
  wind:      { name: 'Wind',      primal: 'Garuda',    glyph: '≋', color: '#2a6f8a', icon: 'assets/icons/wind.png',      niche: 'scout · lift',    pie: 'movement · range',        axis: 'lean' },
  holy:      { name: 'Holy',      primal: 'Alexander', glyph: '✦', color: '#8c7a3a', icon: 'assets/icons/holy.png',      niche: 'convince · ward', pie: 'bond-scaling power',      axis: 'high-bond' },
};

// ---- canonical adult portraits (identities, §10) ----------------------------
// The user-supplied Rank-2 artwork for the 1-element (pure) and 2-element (dual)
// adults, keyed by form id. White-background illustrations — render through the
// #sh-dropwhite filter so the white drops to alpha (same as the harness sheets).
// `creature` is the canonical animal archetype carried in each file name.
const CX_ART = {
  pure_fire:   { src:'assets/adults/pure_fire.jpeg',   creature:'Great Cat' },
  pure_water:  { src:'assets/adults/pure_water.jpeg',  creature:'Whale'     },
  pure_earth:  { src:'assets/adults/pure_earth.jpeg',  creature:'Tortoise'  },
  pure_nature: { src:'assets/adults/pure_nature.jpeg', creature:'Stag'      },
  pure_ice:    { src:'assets/adults/pure_ice.jpeg',    creature:'Fox'       },
  pure_lit:    { src:'assets/adults/pure_lit.jpeg',    creature:'Bird'      },
  pure_wind:   { src:'assets/adults/pure_wind.jpeg',   creature:'Dragonlet' },
  pure_holy:   { src:'assets/adults/pure_holy.jpeg',   creature:'Owl'       },

  d_earth_nature: { src:'assets/adults/d_earth_nature.jpeg', creature:'Bear'     },
  d_water_earth:  { src:'assets/adults/d_water_earth.jpeg',  creature:'Tortoise' },
  d_water_nature: { src:'assets/adults/d_water_nature.jpeg', creature:'Heron'    },
  d_nature_ice:   { src:'assets/adults/d_nature_ice.jpeg',   creature:'Moose'    },
  d_water_ice:    { src:'assets/adults/d_water_ice.jpeg',    creature:'Shark'    },
  d_ice_lit:      { src:'assets/adults/d_ice_lit.jpeg',      creature:'Wolf'     },
  d_lit_wind:     { src:'assets/adults/d_lit_wind.jpeg',     creature:'Eagle'    },
  d_wind_holy:    { src:'assets/adults/d_wind_holy.jpeg',    creature:'Owl'      },
  d_holy_fire:    { src:'assets/adults/d_holy_fire.jpeg',    creature:'Lion'     },
  d_holy_lit:     { src:'assets/adults/d_holy_lit.jpeg',     creature:'Stag'     },
  d_fire_wind:    { src:'assets/adults/d_fire_wind.jpeg',    creature:'Fox'      },
  d_earth_lit:    { src:'assets/adults/d_earth_lit.jpeg',    creature:'Rhino'    },
  d_fire_lit:     { src:'assets/adults/d_fire_lit.jpeg',     creature:'Tiger'    },
  d_water_lit:    { src:'assets/adults/d_water_lit.jpeg',    creature:'Serpent'  },
  d_earth_wind:   { src:'assets/adults/d_earth_wind.jpeg',   creature:'Goat'     },
  d_nature_holy:  { src:'assets/adults/d_nature_holy.jpeg',  creature:'Butterfly'},
};

// ---- §6 derivation tables ---------------------------------------------------
const GATE_VALUE = { trivial: 5, easy: 9, std: 15, hard: 24, brutal: 34, mythic: 44 };
const GATE_RANK  = { trivial: 0, easy: 1, std: 2, hard: 3, brutal: 4, mythic: 5 };

// Dual element-threshold BUDGET by gate tier — the sum the two veins must total
// (= 2× the old symmetric value: std 49/49, hard 55/55, brutal 60/60, mythic 72/72).
// How that budget splits between the two veins is the per-form `split` lever
// (lead share, 0.50→0.70); the total stays fixed by tier, so a lopsided dual is
// the SAME difficulty overall — just shaped to lean on its dominant element.
const DUAL_TOTAL = { trivial: 70, easy: 84, std: 98, hard: 110, brutal: 120, mythic: 144 };

// Per-form lead-vein share for duals (elements[0] = lead). 0.50 = even 49/49;
// up to 0.70 = a 30/70 lean toward the form's defining element. First-pass/tunable.
const DUAL_SPLIT = {
  d_earth_nature: 0.50, d_water_earth: 0.55, d_water_nature: 0.60, d_nature_ice: 0.62,
  d_water_ice:    0.66, d_ice_lit:     0.54, d_lit_wind:     0.58, d_wind_holy:   0.52,
  d_holy_fire:    0.60, d_holy_lit:    0.62, d_fire_wind:    0.60, d_earth_lit:   0.70,
  d_fire_lit:     0.64, d_water_lit:   0.58, d_earth_wind:   0.62, d_nature_holy: 0.55,
};
const FOOD_VALUE = { frugal: 3, normal: 0, hungry: -3 };
const BOND_VALUE = { steady: 3, normal: 0, clingy: -3 };

// rest is stored as {slots, tod:[]}. tod lists the SLEEP slot(s).
function restValue(rest) {
  if (rest.slots === 0) return 12;
  if (rest.slots === 2) return -12;
  // 1 slot: +1 if it sleeps off-Night (a timing specialist), else 0.
  return rest.tod[0] === 'Night' ? 0 : 1;
}
function restLabel(rest) {
  if (rest.slots === 0) return 'Tireless · 0 sleep';
  if (rest.slots === 2) return 'Double · ' + rest.tod.join('+');
  return (rest.tod[0] === 'Night' ? 'Once · Night' : 'Shifted · ' + rest.tod[0]);
}
function timeAwakePct(rest) { return Math.round((4 - rest.slots) / 4 * 100); }

// bonus per element from total + concentration + element list.
function bonusDist(f) {
  const els = f.elements;
  if (f.profile === 'junk') {
    const each = Math.round(f.bonusTotal / 4);
    return { fire: each, water: each, earth: each, nature: each };
  }
  if (els.length === 0) return {};
  if (els.length === 1) return { [els[0]]: f.bonusTotal };
  if (els.length === 2) {
    const lead = Math.round(f.bonusTotal * f.conc);
    return { [els[0]]: lead, [els[1]]: f.bonusTotal - lead };
  }
  // 3 elements (conc 0.40 lead, rest split)
  const lead = Math.round(f.bonusTotal * f.conc);
  const rem = f.bonusTotal - lead, e2 = Math.round(rem / 2);
  return { [els[0]]: lead, [els[1]]: e2, [els[2]]: rem - e2 };
}

function derive(f) {
  const dist = bonusDist(f);
  const effBonus = f.profile === 'junk' ? Math.round(f.bonusTotal * 0.40) : f.bonusTotal;
  const rv = restValue(f.rest);
  const lv = (f.life - 10) * 2;
  const fv = FOOD_VALUE[f.food];
  const bv = BOND_VALUE[f.bond];
  const net = effBonus + rv + lv + fv + bv;
  const gate = GATE_VALUE[f.gate];
  const margin = net - gate;

  // R3 route: a concentrated hard+ bonus (≥18 into one element) advances that primal.
  const leadEl = f.elements[0];
  const leadBonus = leadEl ? (dist[leadEl] || 0) : 0;
  const routesR3 = (GATE_RANK[f.gate] >= 3) && leadBonus >= 18 && f.profile !== 'junk';
  const r3 = routesR3 ? CX_ELEM[leadEl].primal : null;

  // verdict
  let verdict;
  if (f.profile === 'mote' || f.profile === 'lesser') verdict = 'basic';
  else if (f.profile === 'junk') verdict = 'off-ramp';
  else if (margin >= 8) verdict = 'efficient';
  else if (margin >= -7) verdict = 'fair';
  else if (margin >= -15 || routesR3) verdict = 'prestige';
  else verdict = 'trap';

  return {
    dist, effBonus, restV: rv, lifeV: lv, foodV: fv, bondV: bv,
    net, gate, margin, r3, verdict,
    niche: f.elements.map(e => CX_ELEM[e].niche.split(' · ')[0]),
  };
}

// ---- the 34 (v0 snapshot of §4) --------------------------------------------
// rest tod values: Night | Morning | Afternoon | Evening
function F(o) { return o; }
const FORMS = [
  // Pure standards (8)
  F({ id:'pure_fire',  slot:'Fire',  profile:'pure', variant:'std', elements:['fire'],  role:'Glass',     gate:'hard',  bonusTotal:26, conc:0.95, life:8,  rest:{slots:1,tod:['Night']},               food:'hungry', bond:'normal', avail:'start'  }),
  F({ id:'pure_water', slot:'Water', profile:'pure', variant:'std', elements:['water'], role:'Workhorse', gate:'hard',  bonusTotal:22, conc:0.95, life:10, rest:{slots:1,tod:['Night']},               food:'normal', bond:'normal', avail:'start'  }),
  F({ id:'pure_earth', slot:'Earth', profile:'pure', variant:'std', elements:['earth'], role:'Engine',    gate:'hard',  bonusTotal:32, conc:0.95, life:12, rest:{slots:2,tod:['Night','Evening']},      food:'hungry', bond:'steady', avail:'start'  }),
  F({ id:'pure_nature',slot:'Nature',profile:'pure', variant:'std', elements:['nature'],role:'Caretaker', gate:'easy',  bonusTotal:16, conc:0.95, life:11, rest:{slots:1,tod:['Night']},               food:'frugal', bond:'steady', avail:'start'  }),
  F({ id:'pure_ice',   slot:'Ice',   profile:'pure', variant:'std', elements:['ice'],   role:'Shifted',   gate:'hard',  bonusTotal:24, conc:0.95, life:10, rest:{slots:1,tod:['Morning']},             food:'normal', bond:'normal', avail:'locked' }),
  F({ id:'pure_lit',   slot:'Lightning',profile:'pure',variant:'std',elements:['lightning'],role:'Sprinter',gate:'hard',bonusTotal:22, conc:0.95, life:8,  rest:{slots:0,tod:[]},                      food:'hungry', bond:'clingy', avail:'locked' }),
  F({ id:'pure_wind',  slot:'Wind',  profile:'pure', variant:'std', elements:['wind'],  role:'Shifted',   gate:'hard',  bonusTotal:24, conc:0.95, life:10, rest:{slots:1,tod:['Afternoon']},           food:'frugal', bond:'normal', avail:'locked' }),
  F({ id:'pure_holy',  slot:'Holy',  profile:'pure', variant:'std', elements:['holy'],  role:'Engine',    gate:'hard',  bonusTotal:30, conc:0.95, life:12, rest:{slots:2,tod:['Night','Morning']},      food:'normal', bond:'steady', avail:'locked' }),

  // Dual standards (16)
  F({ id:'d_earth_nature', slot:'Earth+Nature', profile:'dual', variant:'std', elements:['earth','nature'], role:'Caretaker', gate:'std',   bonusTotal:18, conc:0.55, life:12, rest:{slots:1,tod:['Night']},          food:'frugal', bond:'steady', avail:'start'  }),
  F({ id:'d_water_earth',  slot:'Water+Earth',  profile:'dual', variant:'std', elements:['water','earth'],  role:'Workhorse', gate:'std',   bonusTotal:20, conc:0.55, life:11, rest:{slots:1,tod:['Night']},          food:'normal', bond:'normal', avail:'gated'  }),
  F({ id:'d_water_nature', slot:'Water+Nature', profile:'dual', variant:'std', elements:['water','nature'], role:'Workhorse', gate:'hard',  bonusTotal:24, conc:0.55, life:11, rest:{slots:1,tod:['Night']},          food:'normal', bond:'normal', avail:'gated'  }),
  F({ id:'d_nature_ice',   slot:'Nature+Ice',   profile:'dual', variant:'std', elements:['nature','ice'],   role:'Shifted',   gate:'std',   bonusTotal:20, conc:0.55, life:11, rest:{slots:1,tod:['Morning']},        food:'frugal', bond:'normal', avail:'locked' }),
  F({ id:'d_water_ice',    slot:'Water+Ice',    profile:'dual', variant:'std', elements:['water','ice'],    role:'Engine',    gate:'hard',  bonusTotal:32, conc:0.55, life:11, rest:{slots:2,tod:['Night','Afternoon']},food:'hungry', bond:'steady', avail:'locked' }),
  F({ id:'d_ice_lit',      slot:'Ice+Lightning',profile:'dual', variant:'std', elements:['ice','lightning'],role:'Shifted',   gate:'hard',  bonusTotal:26, conc:0.55, life:10, rest:{slots:1,tod:['Evening']},        food:'hungry', bond:'normal', avail:'locked' }),
  F({ id:'d_lit_wind',     slot:'Lightning+Wind',profile:'dual',variant:'std', elements:['lightning','wind'],role:'Workhorse',gate:'std',   bonusTotal:22, conc:0.55, life:10, rest:{slots:1,tod:['Night']},          food:'normal', bond:'normal', avail:'locked' }),
  F({ id:'d_wind_holy',    slot:'Wind+Holy',    profile:'dual', variant:'std', elements:['wind','holy'],    role:'Engine',    gate:'hard',  bonusTotal:32, conc:0.55, life:12, rest:{slots:2,tod:['Night','Morning']}, food:'normal', bond:'steady', avail:'locked' }),
  F({ id:'d_holy_fire',    slot:'Holy+Fire',    profile:'dual', variant:'std', elements:['holy','fire'],    role:'Glass',     gate:'brutal',bonusTotal:32, conc:0.55, life:9,  rest:{slots:1,tod:['Night']},          food:'hungry', bond:'normal', avail:'locked' }),
  F({ id:'d_holy_lit',     slot:'Holy+Lightning',profile:'dual',variant:'std', elements:['holy','lightning'],role:'Diva',     gate:'hard',  bonusTotal:28, conc:0.55, life:10, rest:{slots:1,tod:['Afternoon']},      food:'hungry', bond:'clingy', avail:'locked' }),
  F({ id:'d_fire_wind',    slot:'Fire+Wind',    profile:'dual', variant:'std', elements:['fire','wind'],    role:'Glass',     gate:'hard',  bonusTotal:30, conc:0.55, life:9,  rest:{slots:1,tod:['Evening']},        food:'hungry', bond:'normal', avail:'locked' }),
  F({ id:'d_earth_lit',    slot:'Earth+Lightning',profile:'dual',variant:'std',elements:['earth','lightning'],role:'Engine',  gate:'hard',  bonusTotal:32, conc:0.55, life:12, rest:{slots:2,tod:['Night','Evening']}, food:'hungry', bond:'steady', avail:'locked' }),
  F({ id:'d_fire_lit',     slot:'Fire+Lightning',profile:'dual',variant:'std', elements:['fire','lightning'],role:'Glass',    gate:'hard',  bonusTotal:32, conc:0.55, life:8,  rest:{slots:1,tod:['Night']},          food:'hungry', bond:'clingy', avail:'locked' }),
  F({ id:'d_water_lit',    slot:'Water+Lightning',profile:'dual',variant:'std',elements:['water','lightning'],role:'Workhorse',gate:'mythic',bonusTotal:30, conc:0.55, life:10, rest:{slots:1,tod:['Night']},          food:'normal', bond:'normal', avail:'locked' }),
  F({ id:'d_earth_wind',   slot:'Earth+Wind',   profile:'dual', variant:'std', elements:['earth','wind'],   role:'Workhorse', gate:'mythic',bonusTotal:30, conc:0.55, life:11, rest:{slots:1,tod:['Night']},          food:'normal', bond:'normal', avail:'locked' }),
  F({ id:'d_nature_holy',  slot:'Nature+Holy',  profile:'dual', variant:'std', elements:['nature','holy'],  role:'Diva',      gate:'mythic',bonusTotal:32, conc:0.55, life:11, rest:{slots:1,tod:['Morning']},        food:'hungry', bond:'clingy', avail:'locked' }),

  // Rare-2nd (4)
  F({ id:'r_fire',  slot:'Fire',  profile:'pure', variant:'rare', elements:['fire'],  role:'Sprinter',  gate:'hard',  bonusTotal:26, conc:0.95, life:4,  rest:{slots:0,tod:[]},              food:'hungry', bond:'clingy', avail:'exotic', exotic:true }),
  F({ id:'r_earth', slot:'Earth', profile:'pure', variant:'rare', elements:['earth'], role:'Caretaker', gate:'easy',  bonusTotal:14, conc:0.95, life:9,  rest:{slots:1,tod:['Night']},       food:'frugal', bond:'steady', avail:'locked' }),
  F({ id:'r_water', slot:'Water', profile:'pure', variant:'rare', elements:['water'], role:'Diva',      gate:'mythic',bonusTotal:34, conc:0.95, life:10, rest:{slots:1,tod:['Night']},       food:'hungry', bond:'clingy', avail:'locked' }),
  F({ id:'r_holy',  slot:'Holy',  profile:'pure', variant:'rare', elements:['holy'],  role:'Exotic',    gate:'brutal',bonusTotal:34, conc:0.95, life:6,  rest:{slots:1,tod:['Morning']},     food:'hungry', bond:'clingy', avail:'exotic', exotic:true }),

  // Multi 3+ (4)
  F({ id:'m_fire_earth_lit', slot:'Fire+Earth+Lightning', profile:'multi', variant:'std', elements:['fire','earth','lightning'], role:'Engine',    gate:'mythic',bonusTotal:40, conc:0.40, life:12, rest:{slots:2,tod:['Night','Evening']}, food:'hungry', bond:'steady', avail:'locked' }),
  F({ id:'m_water_nature_ice',slot:'Water+Nature+Ice',    profile:'multi', variant:'std', elements:['water','nature','ice'],     role:'Caretaker', gate:'hard',  bonusTotal:28, conc:0.40, life:12, rest:{slots:1,tod:['Night']},          food:'frugal', bond:'steady', avail:'locked' }),
  F({ id:'m_wind_holy_lit',  slot:'Wind+Holy+Lightning',  profile:'multi', variant:'std', elements:['wind','holy','lightning'],  role:'Diva',      gate:'mythic',bonusTotal:36, conc:0.40, life:10, rest:{slots:1,tod:['Afternoon']},      food:'hungry', bond:'clingy', avail:'locked' }),
  F({ id:'m_earth_water_nature',slot:'Earth+Water+Nature',profile:'multi', variant:'std', elements:['earth','water','nature'],   role:'Workhorse', gate:'hard',  bonusTotal:28, conc:0.40, life:11, rest:{slots:1,tod:['Night']},          food:'normal', bond:'normal', avail:'locked' }),

  // Junk (2)
  F({ id:'junk',      slot:'Formless', profile:'junk', variant:'only', elements:[], role:'Off-ramp', gate:'trivial', bonusTotal:16, conc:0.25, life:8, rest:{slots:1,tod:['Night']}, food:'normal', bond:'normal', avail:'start'  }),
  F({ id:'junk_rare', slot:'Formless', profile:'junk', variant:'rare', elements:[], role:'Off-ramp', gate:'easy',    bonusTotal:18, conc:0.25, life:6, rest:{slots:0,tod:[]},        food:'hungry', bond:'clingy', avail:'exotic', exotic:true }),

  // Rank 0 — the Nascent mote (always known; the cycle's start)
  F({ id:'mote', rank:0, slot:'Drift-Mote', profile:'mote', variant:'only', elements:[], role:'Nascent', gate:'trivial', bonusTotal:0, conc:0.25, life:1, rest:{slots:1,tod:['Night']}, food:'normal', bond:'normal', avail:'start' }),

  // Rank 1 — the 8 Lesser forms, one pure vein each (the rung below Adult)
  F({ id:'lesser_fire',  rank:1, slot:'Fire',      profile:'lesser', variant:'std', elements:['fire'],      role:'Lesser', gate:'easy', bonusTotal:6, conc:0.95, life:3, rest:{slots:1,tod:['Night']}, food:'normal', bond:'normal', avail:'start'  }),
  F({ id:'lesser_water', rank:1, slot:'Water',     profile:'lesser', variant:'std', elements:['water'],     role:'Lesser', gate:'easy', bonusTotal:6, conc:0.95, life:3, rest:{slots:1,tod:['Night']}, food:'normal', bond:'normal', avail:'start'  }),
  F({ id:'lesser_earth', rank:1, slot:'Earth',     profile:'lesser', variant:'std', elements:['earth'],     role:'Lesser', gate:'easy', bonusTotal:6, conc:0.95, life:3, rest:{slots:1,tod:['Night']}, food:'normal', bond:'normal', avail:'start'  }),
  F({ id:'lesser_nature',rank:1, slot:'Nature',    profile:'lesser', variant:'std', elements:['nature'],    role:'Lesser', gate:'easy', bonusTotal:6, conc:0.95, life:3, rest:{slots:1,tod:['Night']}, food:'normal', bond:'normal', avail:'start'  }),
  F({ id:'lesser_ice',   rank:1, slot:'Ice',       profile:'lesser', variant:'std', elements:['ice'],       role:'Lesser', gate:'easy', bonusTotal:6, conc:0.95, life:3, rest:{slots:1,tod:['Night']}, food:'normal', bond:'normal', avail:'locked' }),
  F({ id:'lesser_lit',   rank:1, slot:'Lightning', profile:'lesser', variant:'std', elements:['lightning'], role:'Lesser', gate:'easy', bonusTotal:6, conc:0.95, life:3, rest:{slots:1,tod:['Night']}, food:'normal', bond:'normal', avail:'locked' }),
  F({ id:'lesser_wind',  rank:1, slot:'Wind',      profile:'lesser', variant:'std', elements:['wind'],      role:'Lesser', gate:'easy', bonusTotal:6, conc:0.95, life:3, rest:{slots:1,tod:['Night']}, food:'normal', bond:'normal', avail:'locked' }),
  F({ id:'lesser_holy',  rank:1, slot:'Holy',      profile:'lesser', variant:'std', elements:['holy'],      role:'Lesser', gate:'easy', bonusTotal:6, conc:0.95, life:3, rest:{slots:1,tod:['Night']}, food:'normal', bond:'normal', avail:'locked' }),
];

// ---- diegetic trigger lore (placeholders; voice = rescued-citizen memory) ---
// Each form's secret trigger reads as what a recovered townsfolk remembers.
const CITIZENS = {
  fire:'Dunmar, the slag-cook', water:'Tova, the tide-reader', earth:'Esk, the burrow-warden',
  nature:'Pell, the orchard-keeper', ice:'Yarrow, the frost-monk', lightning:'Hask, the storm-tapper',
  wind:'Maelis, the kite-witch', holy:'Sister Halla, of the Cathedral of Hours',
};
const LORE = {
  pure_nature:   { who:'Pell, the orchard-keeper', text:'“Never starve it, never strike it. Let the green run deep and bring it home full ten dawns running — that\u2019s the whole of it.”' },
  d_earth_nature:{ who:'Pell, the orchard-keeper', text:'“Two veins, even-matched, and the spirit must have hauled its own weight back from the reach more than once. Mine did it the season the well ran dry.”' },
  pure_fire:     { who:'Dunmar, the slag-cook',    text:'“You drive the fire alone \u2014 no water near it, ever \u2014 and you push it ragged. It only turns the day it nearly burns out and doesn\u2019t.”' },
  pure_earth:    { who:'Esk, the burrow-warden',   text:'“Deep, patient stone. It sleeps twice a day and you let it. Carry until the harness groans, then carry once more.”' },
  pure_lit:      { who:'Hask, the storm-tapper',   text:'“It never sleeps and it never settles. Run it to the far nodes and back inside a single turning of the clock \u2014 do that and it cracks open.”' },
  d_holy_fire:   { who:'Sister Halla',             text:'“Light and flame, kept in one harness without either devouring the other. Few endure the keeping. Fewer still ask twice.”' },
  pure_holy:     { who:'Sister Halla',             text:'“It wakes before dawn and works the dark hours. Win over more souls than you lose, and on the twelfth day it answers.”' },
  d_nature_holy: { who:'Sister Halla',             text:'“The kindest face and the cruellest hunger. It turns only for a keeper who never once let it go cold or unloved \u2014 and it will test that.”' },
  junk:          { who:'nobody in particular',     text:'“That one? It just\u2026 happens. Feed a spirit of everything and nothing in equal measure and you get the grey shape. No secret to it.”' },
};
function triggerLore(f) {
  if (LORE[f.id]) return LORE[f.id];
  const lead = f.elements[0];
  const who = CITIZENS[lead] || 'an elder no one can quite place';
  const ev = f.rest.slots === 2 ? 'after it has worked itself to sleep twice in a day'
           : f.rest.slots === 0 ? 'only on the days it refuses to rest at all'
           : f.gate === 'brutal' ? 'and even then, only once in a long while'
           : 'when the lived history lines up just so';
  return { who, text:`“It comes for those who let the ${CX_ELEM[lead] ? CX_ELEM[lead].name.toLowerCase() : 'vein'} run deepest ${ev}. The exact way of it I half-remember — ask me again when my head clears.”` };
}

// ---- authored secret triggers + traits (numbers/adults §11.10–11.11) --------
// The 2026-06-02 hand-authored layer. A trigger = element FLOORS (asymmetric)
// + LOCKED lived-history events + CAPS (off-elements/lived-history you must NOT
// exceed — the "dodge-the-leak" texture that enforces the element color).
// CUT vocabulary (hunger, days_survived, hull_lost, rescues-as-triggers) is gone.

// lived-history event builders (LOCKED vocabulary)
const _PR = { k:'pr' }, _NF = { k:'nf' };
const _eu   = (el, val) => ({ k:'eu', el, val });        // element_usage(el) ≥ val
const _tod  = (t, val)  => ({ k:'tod', t, val });        // tod_discipline(ToD) ≥ val
const _exp  = (val)     => ({ k:'exp', val });           // explored ≥ val
const _wt   = (val)     => ({ k:'wt', val });            // weight(lean|heavy)
const _bond = (tier)    => ({ k:'bond', tier });         // bond ≥ tier (mid|high|Bonded)
// cap builders (constraints — must stay AT or BELOW)
const _cEl   = (el, max) => ({ k:'capEl', el, max });    // off-element value ≤ max
const _cWt   = (max)     => ({ k:'capWt', max });        // weight ≤ max (lean)
const _cBond = (max)     => ({ k:'capBond', max });      // bond ≤ max (low|normal)
const _cEu   = (el, max) => ({ k:'capEu', el, max });    // element_usage(el) ≤ max

function _elName(el) { return CX_ELEM[el] ? CX_ELEM[el].name : el; }
function eventText(e) {
  switch (e.k) {
    case 'pr':   return 'raised pure — never split a training (dodged the leak)';
    case 'nf':   return 'never failed a check this life';
    case 'eu':   return `won ≥ ${e.val} ${_elName(e.el)} checks`;
    case 'tod':  return `acted mostly in ${e.t} (≥ ${e.val} actions)`;
    case 'exp':  return `explored ≥ ${e.val} nodes this life`;
    case 'wt':   return e.val === 'heavy' ? 'grew heavy (carried its weight)' : 'stayed lean';
    case 'bond': return `bond reached ${e.tier}`;
    default:     return '';
  }
}
function capText(c) {
  switch (c.k) {
    case 'capEl':   return `${_elName(c.el)} ≤ ${c.max}`;
    case 'capWt':   return `weight ≤ ${c.max}`;
    case 'capBond': return `bond ≤ ${c.max}`;
    case 'capEu':   return `${_elName(c.el)} checks ≤ ${c.max}`;
    default:        return '';
  }
}

// §11.10 — element FLOORS + events + caps, per form id.
const TRIGGERS = {
  // Pures (1–4, 9–12) — pure_raising is every pure's signature event
  pure_fire:   { floors:{ fire:121 },   events:[_PR, _eu('fire',40)],              caps:[_cWt('lean')] },
  pure_water:  { floors:{ water:121 },  events:[_PR, _eu('water',20), _NF],        caps:[] },
  pure_earth:  { floors:{ earth:121 },  events:[_PR, _wt('heavy')],                caps:[] },
  pure_nature: { floors:{ nature:90 },  events:[_PR, _bond('mid')],                caps:[] },
  pure_ice:    { floors:{ ice:121 },    events:[_PR, _NF],                         caps:[_cBond('normal')] },
  pure_lit:    { floors:{ lightning:121 }, events:[_PR, _tod('Afternoon',6)],      caps:[_cBond('low')] },
  pure_wind:   { floors:{ wind:121 },   events:[_PR, _exp(10)],                    caps:[_cWt('lean')] },
  pure_holy:   { floors:{ holy:121 },   events:[_PR, _bond('Bonded')],             caps:[_cEu('fire',8), _cWt('lean')] },
  // Dual standards (5–8, 13–25) — asymmetric floors, NOT pure_raising
  d_earth_nature: { floors:{ earth:56, nature:44 },     events:[_wt('heavy'), _bond('mid')],   caps:[_cEl('fire',15)] },
  d_water_earth:  { floors:{ earth:54, water:46 },      events:[_wt('heavy'), _exp(8)],        caps:[_cEl('nature',20)] },
  d_water_nature: { floors:{ water:58, nature:46 },     events:[_exp(6)],                      caps:[_cEl('earth',18)] },
  d_nature_ice:   { floors:{ nature:54, ice:44 },       events:[_tod('Night',5)],              caps:[_cEl('fire',12)] },
  d_water_ice:    { floors:{ water:60, ice:46 },        events:[_tod('Morning',4)],            caps:[_cEl('nature',16)] },
  d_ice_lit:      { floors:{ lightning:56, ice:46 },    events:[_eu('lightning',16)],          caps:[_cBond('low'), _cEl('nature',14)] },
  d_lit_wind:     { floors:{ lightning:53, wind:45 },   events:[_exp(7)],                      caps:[_cBond('low'), _cEl('earth',14)] },
  d_wind_holy:    { floors:{ wind:58, holy:50 },        events:[_bond('high'), _exp(9)],       caps:[_cEl('earth',18)] },
  d_holy_fire:    { floors:{ holy:64, fire:56 },        events:[_bond('high')],                caps:[_cWt('lean'), _cEl('earth',14)] },
  d_holy_lit:     { floors:{ holy:62, lightning:54 },   events:[_bond('high')],                caps:[_cEl('earth',12), _cEl('water',18)] },
  d_fire_wind:    { floors:{ fire:57, wind:49 },        events:[_exp(8)],                      caps:[_cWt('lean'), _cEl('earth',12)] },
  d_earth_lit:    { floors:{ earth:58, lightning:46 },  events:[_wt('heavy'), _eu('earth',12)],caps:[_cEl('fire',14)] },
  d_fire_lit:     { floors:{ fire:64, lightning:56 },   events:[_eu('fire',14)],               caps:[_cWt('lean'), _cBond('low'), _cEl('holy',8)] },
  d_water_lit:    { floors:{ water:72, lightning:64 },  events:[_eu('water',22), _eu('lightning',10), _exp(12)], caps:[_cBond('low'), _cEl('nature',12), _cEl('holy',10)] },
  d_earth_wind:   { floors:{ earth:76, wind:64 },       events:[_wt('heavy'), _exp(14)],       caps:[_cEl('fire',10), _cEl('ice',12)] },
  d_nature_holy:  { floors:{ nature:78, holy:64 },      events:[_bond('Bonded'), _eu('holy',12)], caps:[_cEl('lightning',12), _cWt('lean')] },
  // Rare-2nd (26–29)
  r_fire:  { floors:{ fire:128 },  events:[_PR, _eu('fire',12)], caps:[_cWt('lean'), _cBond('low')] },
  r_earth: { floors:{ earth:80 },  events:[_PR, _wt('heavy')],   caps:[_cEl('fire',15)] },
  r_water: { floors:{ water:155 }, events:[_PR, _exp(15)],       caps:[_cEl('earth',12)] },
  r_holy:  { floors:{ holy:130 },  events:[_PR, _bond('high')],  caps:[_cEl('lightning',10)] },
  // Multi (30–33)
  m_fire_earth_lit:    { floors:{ fire:50, earth:47, lightning:44 }, events:[_wt('heavy')],            caps:[_cEl('water',16), _cEl('nature',12)] },
  m_water_nature_ice:  { floors:{ nature:52, water:48, ice:44 },     events:[_bond('mid')],            caps:[_cEl('fire',14), _cEl('lightning',12)] },
  m_wind_holy_lit:     { floors:{ wind:50, holy:47, lightning:44 }, events:[_bond('high'), _exp(10)],  caps:[_cEl('earth',14), _cEl('nature',12)] },
  m_earth_water_nature:{ floors:{ earth:49, water:46, nature:45 },   events:[_wt('heavy')],            caps:[_cEl('holy',14), _cEl('lightning',12)] },
  // Junk (6, 34) — cumulative all-element sum, no aim
  junk:      { floors:{}, sum:170, events:[],         caps:[] },
  junk_rare: { floors:{}, sum:130, events:[_exp(6)],  caps:[] },
};

// §11.10 trait one-liner + §11.11 mechanic-registry key. kind: active | passive | none.
// `nyi` = references an unbuilt system; `town` = acts town-side; `reg` = registry primitive.
const TRAITS = {
  pure_fire:   { name:'Slot Refund',      kind:'active',  text:'A clean hazard clear (Sol1) refunds its time slot.', reg:'slot-refund', scarce:true },
  pure_water:  { name:'Cheap Finesse',    kind:'passive', text:'Always holds the cheap finesse route on off-element hazards — never forced to brute.', reg:'always-cheap-hazard-route', scarce:true },
  pure_earth:  { name:'Deepstone Hull',   kind:'passive', text:'+20 max Hull.', reg:'+max Hull' },
  pure_nature: { name:'Bountiful Forage', kind:'passive', text:'+50% forage yield.', reg:'forage-yield' },
  pure_ice:    { name:'Glass Edge',       kind:'passive', text:'Clean guaranteed success → +50% payout; a failed power-check costs ×2 Hull.', reg:'brittle-check-amplifier', scarce:true },
  pure_lit:    { name:'Chain Strike',     kind:'active',  text:'Once/day, a clean win grants a free bonus action at the node.', reg:'chain-strike', scarce:true },
  pure_wind:   { name:'Groundless Step',  kind:'active',  text:'Once/day, the first travel hop is free.', reg:'free-opening-move', scarce:true },
  pure_holy:   { name:'Devoted Blessing', kind:'passive', text:'Convince-progress scales with bond tier (×1 / ×1.5 / ×2). The raw check is untouched.', reg:'bond-tier convince multiplier', scarce:true },

  d_earth_nature:{ name:'Mending Forage', kind:'passive', text:'Each forage restores +4 Hull.', reg:'forage-restores-Hull' },
  d_water_earth: { name:"Builder's Return", kind:'active', nyi:true, town:true, text:'Each expedition returns +1 building material to town.', reg:'expedition→building-material' },
  d_water_nature:{ name:"Forager's Read", kind:'active', nyi:true, text:'Forage also delivers a free scout.', reg:'forage-grants-free-scout' },
  d_nature_ice:  { kind:'none' },
  d_water_ice:   { kind:'none' },
  d_ice_lit:     { kind:'none' },
  d_lit_wind:    { kind:'none' },
  d_wind_holy:   { name:"Devotion's Wing", kind:'active', text:'Once/day, spend bond → the next travel hop covers 2 nodes.', reg:'bond-consume', scarce:true },
  d_holy_fire:   { name:'Burning Devotion', kind:'active', text:'Once/day, spend bond → a clean check pays out ×2.', reg:'bond-consume', scarce:true },
  d_holy_lit:    { name:'Devoted Surge', kind:'active', text:'Once/day, spend bond → a bonus action slot.', reg:'bond-consume', scarce:true },
  d_fire_wind:   { kind:'none' },
  d_earth_lit:   { name:'Ironclad', kind:'passive', text:'+8 max Hull.', reg:'+max Hull' },
  d_fire_lit:    { kind:'none' },
  d_water_lit:   { kind:'none' },
  d_earth_wind:  { name:'Stone-Dense Harness', kind:'passive', text:'+10 max Hull.', reg:'+max Hull' },
  d_nature_holy: { name:'Devoted Larder', kind:'passive', text:'Forage scales ×1 / ×1.5 / ×2 by bond tier (tier-read, not consumed).', reg:'bond-tier-forage-scaling' },

  r_fire:  { name:'Total Burn',     kind:'active',  text:'Slot-refund on ANY clean clear (Sol1 or Sol2), with no cap.', reg:'slot-refund' },
  r_earth: { name:'Steadfast Shell', kind:'passive', text:'+6 max Hull.', reg:'+max Hull' },
  r_water: { kind:'none' },
  r_holy:  { name:'First Light', kind:'passive', text:'Begins every expedition at Bonded (max) bond; decay then applies.', reg:'expedition-start bond-initialization', scarce:true },

  m_fire_earth_lit:    { name:'Iron-Forged Harness', kind:'passive', text:'+6 max Hull.', reg:'+max Hull' },
  m_water_nature_ice:  { name:'Cold Larder', kind:'passive', text:'+25% forage.', reg:'forage-yield' },
  m_wind_holy_lit:     { kind:'none' },
  m_earth_water_nature:{ name:"Forager's Mending", kind:'passive', text:'+2 Hull per forage.', reg:'forage-restores-Hull' },

  junk:      { name:'Idle Hands', kind:'active', nyi:true, town:true, text:'Town gains +1 staple/day while this form is home.', reg:'town staple trickle' },
  junk_rare: { kind:'none' },
};

function triggerSpec(f) {
  if (f.profile === 'mote')   return { gates:[], sum:null, events:[{ text:'the spirit always begins here — no trigger' }], caps:[], gateTier:'trivial' };
  if (f.profile === 'lesser') return { gates:[{ el:f.elements[0], val:20 }], sum:null, events:[{ text:'let one vein run ahead of the rest' }], caps:[], gateTier:f.gate };
  const T = TRIGGERS[f.id];
  if (!T) {  // defensive fallback (should not hit for the 34)
    return { gates: f.elements.map(e => ({ el:e, val:49 })), sum: f.profile==='junk' ? 170 : null,
             events:[], caps:[], gateTier:f.gate };
  }
  const gates = Object.keys(T.floors || {}).map(el => ({ el, val:T.floors[el] }));
  const events = (T.events || []).map(e => ({ text:eventText(e), kind:e.k }));
  const caps = (T.caps || []).map(c => ({ text:capText(c), kind:c.k, el:c.el }));
  return { gates, sum: T.sum != null ? T.sum : null, events, caps, gateTier:f.gate };
}

// §11.11 — the form's trait (what it DOES on ascend). 'none' = trigger+stats carry it.
function traitOf(f) {
  if (f.rank !== 2) return { kind:'none' };
  return TRAITS[f.id] || { kind:'none' };
}

const VERDICT_META = {
  basic:     { label:'—',          color:'var(--ink-faded)', note:'' },
  efficient: { label:'Efficient', color:'#4d6e3f', note:'strong for its gate — a crowd-pleaser' },
  fair:      { label:'Fair',      color:'#2b466a', note:'priced right' },
  prestige:  { label:'Prestige',  color:'#b08c3c', note:'hard for modest power — a collector\u2019s form' },
  trap:      { label:'Trap',      color:'#a12b22', note:'UNINTENDED — fix it' },
  'off-ramp':{ label:'Off-ramp',  color:'#8a6e4a', note:'weak by design — the pressure valve' },
};
const AVAIL_META = {
  start:  { label:'Starter — early', color:'#4d6e3f' },
  gated:  { label:'Starter — gated', color:'#b08c3c' },
  locked: { label:'Locked vein',     color:'#2b466a' },
  exotic: { label:'Exotic — rare',   color:'#a12b22' },
};

// profile display labels ("junk" is an internal term — never shown)
const PROFILE_LABEL = { mote:'Nascent', lesser:'Lesser', pure:'Pure', dual:'Dual', multi:'Multi', junk:'Formless' };
const plabel = (p) => PROFILE_LABEL[p] || p;

// attach rank default (existing 34 are Rank-2 Adults) + derivations
FORMS.forEach(f => { if (f.rank == null) f.rank = 2; });
// attach the dual split lever (centralised in DUAL_SPLIT; default even)
FORMS.forEach(f => { if (f.profile === 'dual' && f.split == null) f.split = DUAL_SPLIT[f.id] != null ? DUAL_SPLIT[f.id] : 0.50; });
FORMS.forEach(f => { f.d = derive(f); });
// attach the authored §11.10 trait (what the form does on ascend)
FORMS.forEach(f => { f.trait = traitOf(f); });
// attach canonical portrait (pure + dual adults) where art exists
FORMS.forEach(f => { f.art = CX_ART[f.id] || null; });

Object.assign(window, {
  CX_ELEM, FORMS, derive, bonusDist, restLabel, restValue, timeAwakePct,
  triggerLore, triggerSpec, traitOf, eventText, capText, TRIGGERS, TRAITS,
  VERDICT_META, AVAIL_META, GATE_VALUE, PROFILE_LABEL, plabel,
  CX_ART, DUAL_TOTAL, DUAL_SPLIT,
});
