<!-- SOLID CITY · part 30 -->
gL, armR, legR]  (exactly citizens.js c.limbs order; rotation.x / .z drive them)
 *     arms pivot at the shoulder (±0.38, 1.48, 0), legs at the hip (±0.15, 0.76, 0), hanging down.
 *     Also named 'arm-1', 'leg-1', 'arm1', 'leg1' like the player avatar in city.js.
 *   userData.emblem  = the chest sigil mesh (use as c.emb3; citizens.js toggles its visibility)
 *   userData.glowMat = vertex-coloured MeshBasicMaterial for glowing parts (use as c.glowMat; ship trim keeps its gold)
 * The cape sways and the aura breathes by themselves (onBeforeRender), so no extra update hook is needed.
 * Name tag, hit box, speech bubble, ship and scale stay with citizens.js.
 * Light: canvas texture only, merged vertex-coloured geometry, 4 materials, ~600 triangles.
 * THE LAW: every piece's smallest dimension is at least 0.13 (userData.pieces / userData.checkLaw()).
 */

export const MINIMUM = 0.13;

const COL = { black: 0x16121a, gun: 0x34303a, gold: 0xd8aa48, red: 0x8e0c1c, redDeep: 0x5a0610, skin: 0x221a20 };
const GLOW = { gold: 0xf2c04e, red: 0xff1a30 };

function Parts(T, srgb) { this.T = T; this.srgb = !!srgb; this.list = []; this.pieces = []; }  // srgb: colours given as screen sRGB (for unlit MeshBasic glow)
/* add a piece: geometry already built at its real size; we record its own dimensions BEFORE merging */
Parts.prototype.add = function (name, geo, color, x, y, z, rx, ry, rz) {
  const T = this.T;
  geo.computeBoundingBox(); const s = new T.Vector3(); geo.boundingBox.getSize(s);
  this.pieces.push({ name: name, dims: [+s.x.toFixed(3), +s.y.toFixed(3), +s.z.toFixed(3)], min: +Math.min(s.x, s.y, s.z).toFixed(3) });
  const g = geo.index ? geo.toNonIndexed() : geo;
  g.applyMatrix4(new T.Matrix4().compose(new T.Vector3(x || 0, y || 0, z || 0), new T.Quaternion().setFromEuler(new T.Euler(rx || 0, ry || 0, rz || 0)), new T.Vector3(1, 1, 1)));
  const c = new T.Color(color); if (this.srgb) c.convertSRGBToLinear(); const n = g.attributes.position.count, a = new Float32Array(n * 3);
  for (let i = 0; i < n; i++) { a[i * 3] = c.r; a[i * 3 + 1] = c.g; a[i * 3 + 2] = c.b; }
  g.setAttribute('color', new T.BufferAttribute(a, 3));
  this.list.push(g); return this;
};
Parts.prototype.merge = function () {
  const T = this.T; let n = 0; this.list.forEach(function (g) { n += g.attributes.position.count; });
  const P = new Float32Array(n * 3), N = new Float32Array(n * 3), C = new Float32Array(n * 3), U = new Float32Array(n * 2); let o = 0;
  this.list.forEach(function (g) {
    const k = g.attributes.position.count;
    P.set(g.attributes.position.array, o * 3); N.set(g.attributes.normal.array, o * 3); C.set(g.attributes.color.array, o * 3);
    if (g.attributes.uv) U.set(g.attributes.uv.array, o * 2);
    o += k; g.dispose();
  });
  const out = new T.BufferGeometry();
  out.setAttribute('position', new T.BufferAttribute(P, 3)); out.setAttribute('normal', new T.BufferAttribute(N, 3));
  out.setAttribute('color', new T.BufferAttribute(C, 3)); out.setAttribute('uv', new T.BufferAttribute(U, 2));
  out.computeBoundingSphere(); out.computeBoundingBox(); return out;
};

function sigilTexture(T) {
  const c = document.createElement('canvas'); c.width = c.height = 128; const g = c.getContext('2d');
  g.fillStyle = '#0b0709'; g.fillRect(0, 0, 128, 128);
  g.strokeStyle = '#d8aa48'; g.lineWidth = 8; g.strokeRect(6, 6, 116, 116);
  g.strokeStyle = '#ff2a40'; g.lineWidth = 5; g.beginPath(); g.arc(64, 66, 44, 0, Math.PI * 2); g.stroke();
  g.fillStyle = '#ffcc33'; g.textAlign = 'center'; g.textBaseline = 'middle'; g.font = '700 74px Georgia, "Times New Roman", serif'; g.fillText('P', 64, 70);
  g.fillStyle = '#0b0709'; g.fillRect(0, 126, 2, 2);   // dark texel for the plate's sides
  const t = new T.CanvasTexture(c); t.encoding = T.sRGBEncoding; t.anisotropy = 4; return t;
}

export function createKingPlayaAvatar(opts) {
  opts = opts || {};
  const T = opts.THREE || (typeof window !== 'undefined' && window.THREE);
  if (!T) throw new Error('createKingPlayaAvatar: pass { THREE }');
  const curve = typeof opts.curve === 'function' ? opts.curve : function (m) { return m; };
  const B = function (w, h, d) { return new T.BoxGeometry(w, h, d); };
  const allPieces = [];
  function take(parts, label) { parts.pieces.forEach(function (p) { p.part = label; allPieces.push(p); }); return parts.merge(); }

  /* ---- body (one mesh): torso, tabard robe, head, mantle, belt, boots-free ---- */
  const body = new Parts(T);
  body.add('torso', B(0.6, 0.8, 0.32), COL.black, 0, 1.15, 0);
  body.add('chest armour', B(0.5, 0.42, 0.13), COL.gun, 0, 1.27, 0.125);
  body.add('robe (tabard)', new T.CylinderGeometry(0.33, 0.45, 0.52, 6), COL.black, 0, 0.62, 0, 0, Math.PI / 6, 0);
  body.add('robe front panel', B(0.2, 0.46, 0.13), COL.red, 0, 0.6, 0.3, -0.17, 0, 0);
  body.add('neck', new T.CylinderGeometry(0.1, 0.12, 0.14, 6), COL.skin, 0, 1.58, 0);
  body.add('head', B(0.34, 0.36, 0.34), COL.black, 0, 1.78, 0);
  body.add('high 