@usenavii/core 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -189,6 +189,63 @@ var ANCHORS = {
189
189
  groundY: 94,
190
190
  cheekY: 53,
191
191
  cheekOffset: 12
192
+ },
193
+ // Squircle — FULL-BLEED. Body fills 0-100, face composed as ID portrait:
194
+ // upper-third eye line, lower-third mouth, generous cheek/eye spread.
195
+ squircle: {
196
+ cx: 50,
197
+ eyeY: 44,
198
+ eyeOffset: 13,
199
+ eyeScale: 1,
200
+ mouthY: 62,
201
+ mouthSpan: 8,
202
+ topperX: 50,
203
+ topperY: 8,
204
+ groundY: 98,
205
+ cheekY: 52,
206
+ cheekOffset: 24
207
+ },
208
+ // Pumpkin — round body w/ slight horizontal lobing. Face mid-low, stem on top.
209
+ pumpkin: {
210
+ cx: 50,
211
+ eyeY: 52,
212
+ eyeOffset: 11,
213
+ eyeScale: 1,
214
+ mouthY: 66,
215
+ mouthSpan: 9,
216
+ topperX: 50,
217
+ topperY: 18,
218
+ groundY: 88,
219
+ cheekY: 60,
220
+ cheekOffset: 20
221
+ },
222
+ // Ghost — tall wavy silhouette. Face high (under the "hood"), wavy hem below.
223
+ ghost: {
224
+ cx: 50,
225
+ eyeY: 42,
226
+ eyeOffset: 8,
227
+ eyeScale: 1.05,
228
+ mouthY: 54,
229
+ mouthSpan: 6,
230
+ topperX: 50,
231
+ topperY: 12,
232
+ groundY: 92,
233
+ cheekY: 50,
234
+ cheekOffset: 14
235
+ },
236
+ // SkullHead — slightly elongated egg w/ deep eye sockets.
237
+ skullHead: {
238
+ cx: 50,
239
+ eyeY: 50,
240
+ eyeOffset: 10,
241
+ eyeScale: 1,
242
+ mouthY: 70,
243
+ mouthSpan: 7,
244
+ topperX: 50,
245
+ topperY: 16,
246
+ groundY: 90,
247
+ cheekY: 60,
248
+ cheekOffset: 16
192
249
  }
193
250
  };
194
251
 
@@ -209,27 +266,49 @@ var BODY_PATHS = {
209
266
  // Taro — gourd shape: small head bulge, fuller bottom
210
267
  taro: "M50 14 C58 14 64 22 64 30 C64 36 60 40 60 46 C60 54 76 60 78 76 C80 88 66 91 50 91 C34 91 20 88 22 76 C24 60 40 54 40 46 C40 40 36 36 36 30 C36 22 42 14 50 14 Z",
211
268
  // Wisp — tall narrow body, slight bottom flare, ghost-like
212
- wisp: "M50 12 C60 12 66 24 66 40 C66 60 74 78 70 90 C64 96 36 96 30 90 C26 78 34 60 34 40 C34 24 40 12 50 12 Z"
269
+ wisp: "M50 12 C60 12 66 24 66 40 C66 60 74 78 70 90 C64 96 36 96 30 90 C26 78 34 60 34 40 C34 24 40 12 50 12 Z",
270
+ // Squircle — FULL-BLEED corporate plate. Fills entire viewport with tight
271
+ // corner radius (~4px). Reads as a tile / ID photo not a contained avatar.
272
+ // Pairs with `flat: true` packs (Office) — face floats on a wall of color.
273
+ squircle: "M4 0 C2 0 0 2 0 4 C0 35 0 65 0 96 C0 98 2 100 4 100 C35 100 65 100 96 100 C98 100 100 98 100 96 C100 65 100 35 100 4 C100 2 98 0 96 0 C65 0 35 0 4 0 Z",
274
+ // Pumpkin — round, slightly wider than tall, with subtle horizontal "lobes"
275
+ // for the iconic carved-pumpkin gourd shape. Stem area kept clear at top.
276
+ pumpkin: "M50 18 C70 18 86 30 86 52 C86 74 70 88 50 88 C30 88 14 74 14 52 C14 30 30 18 50 18 Z",
277
+ // Ghost — soft rounded top with wavy bottom hem (3 humps), evoking a sheet.
278
+ ghost: "M50 12 C68 12 78 24 78 42 C78 60 80 76 80 88 L74 84 L68 90 L62 84 L56 90 L50 84 L44 90 L38 84 L32 90 L26 84 L20 88 C20 76 22 60 22 42 C22 24 32 12 50 12 Z",
279
+ // SkullHead — egg-ish shape, slight pinch at jaw for skull silhouette.
280
+ skullHead: "M50 16 C68 16 80 30 80 50 C80 64 76 72 70 78 L68 86 L60 88 L60 82 L40 82 L40 88 L32 86 L30 78 C24 72 20 64 20 50 C20 30 32 16 50 16 Z"
213
281
  };
214
- function renderBodyDefs(_id, _palette, gradId) {
282
+ function renderBodyDefs(_id, palette, gradId, opts) {
283
+ if (opts?.flat) {
284
+ return `<radialGradient id="${gradId}"><stop offset="0%" stop-color="${palette.bodyFrom}" /><stop offset="100%" stop-color="${palette.bodyFrom}" /></radialGradient>`;
285
+ }
215
286
  return `
216
287
  <radialGradient id="${gradId}" cx="42%" cy="32%" r="68%">
217
- <stop offset="0%" stop-color="${_palette.bodyFrom}" />
218
- <stop offset="100%" stop-color="${_palette.bodyTo}" />
288
+ <stop offset="0%" stop-color="${palette.bodyFrom}" />
289
+ <stop offset="100%" stop-color="${palette.bodyTo}" />
219
290
  </radialGradient>`.trim();
220
291
  }
221
- function renderBody(id, palette, gradId) {
292
+ function renderBody(id, palette, gradId, opts) {
222
293
  const d = BODY_PATHS[id];
223
294
  const a = ANCHORS[id];
295
+ const flat = opts?.flat === true;
224
296
  const outlineColor = withAlpha(palette.ink, 0.18);
225
- return [
226
- // Ground shadow — soft ellipse just below body, grounds the figure
227
- `<ellipse cx="${a.cx}" cy="${a.groundY + 4}" rx="22" ry="2.6" fill="${palette.ink}" opacity="0.16" />`,
228
- // Body fill
229
- `<path d="${d}" fill="url(#${gradId})" stroke="${outlineColor}" stroke-width="0.7" />`,
230
- // Sheen — small light spot upper-left, scaled to body
231
- `<ellipse cx="${a.cx - 12}" cy="${a.eyeY - 14}" rx="11" ry="7" fill="#FFFFFF" opacity="0.22" transform="rotate(-18 ${a.cx - 12} ${a.eyeY - 14})" />`
232
- ].join("");
297
+ const parts = [];
298
+ if (!flat) {
299
+ parts.push(
300
+ `<ellipse cx="${a.cx}" cy="${a.groundY + 4}" rx="22" ry="2.6" fill="${palette.ink}" opacity="0.16" />`
301
+ );
302
+ }
303
+ parts.push(
304
+ flat ? `<path d="${d}" fill="url(#${gradId})" />` : `<path d="${d}" fill="url(#${gradId})" stroke="${outlineColor}" stroke-width="0.7" />`
305
+ );
306
+ if (!flat) {
307
+ parts.push(
308
+ `<ellipse cx="${a.cx - 12}" cy="${a.eyeY - 14}" rx="11" ry="7" fill="#FFFFFF" opacity="0.22" transform="rotate(-18 ${a.cx - 12} ${a.eyeY - 14})" />`
309
+ );
310
+ }
311
+ return parts.join("");
233
312
  }
234
313
  function withAlpha(hex, alpha) {
235
314
  const h = hex.replace("#", "");
@@ -242,12 +321,13 @@ function withAlpha(hex, alpha) {
242
321
  }
243
322
 
244
323
  // src/parts/eyes.ts
245
- function renderEyes(id, palette, anchor) {
324
+ function renderEyes(id, palette, anchor, opts) {
246
325
  const lx = anchor.cx - anchor.eyeOffset;
247
326
  const rx = anchor.cx + anchor.eyeOffset;
248
327
  const y = anchor.eyeY;
249
328
  const s = anchor.eyeScale;
250
329
  const ink = palette.ink;
330
+ const sw = opts?.strokeMul ?? 1;
251
331
  switch (id) {
252
332
  case "round":
253
333
  return [
@@ -269,24 +349,24 @@ function renderEyes(id, palette, anchor) {
269
349
  ].join("");
270
350
  case "squint":
271
351
  return [
272
- arc(lx - 4.5, y, lx, y - 3.5, lx + 4.5, y, ink, 1.8),
273
- arc(rx - 4.5, y, rx, y - 3.5, rx + 4.5, y, ink, 1.8)
352
+ arc(lx - 4.5, y, lx, y - 3.5, lx + 4.5, y, ink, 1.8 * sw),
353
+ arc(rx - 4.5, y, rx, y - 3.5, rx + 4.5, y, ink, 1.8 * sw)
274
354
  ].join("");
275
355
  case "wink":
276
356
  return [
277
357
  sclera(lx, y, 4 * s, 4.5 * s),
278
358
  pupil(lx, y, 2.2 * s, ink),
279
359
  glint(lx + 1, y - 1),
280
- arc(rx - 4, y, rx, y - 3.5, rx + 4, y, ink, 1.8)
360
+ arc(rx - 4, y, rx, y - 3.5, rx + 4, y, ink, 1.8 * sw)
281
361
  ].join("");
282
362
  case "sleepy":
283
363
  return [
284
364
  // Heavier upper lid — half-closed
285
- `<path d="M${lx - 4} ${y - 0.5} Q${lx} ${y + 2} ${lx + 4} ${y - 0.5}" stroke="${ink}" stroke-width="1.7" stroke-linecap="round" fill="none" />`,
286
- `<path d="M${rx - 4} ${y - 0.5} Q${rx} ${y + 2} ${rx + 4} ${y - 0.5}" stroke="${ink}" stroke-width="1.7" stroke-linecap="round" fill="none" />`,
365
+ `<path d="M${lx - 4} ${y - 0.5} Q${lx} ${y + 2} ${lx + 4} ${y - 0.5}" stroke="${ink}" stroke-width="${1.7 * sw}" stroke-linecap="round" fill="none" />`,
366
+ `<path d="M${rx - 4} ${y - 0.5} Q${rx} ${y + 2} ${rx + 4} ${y - 0.5}" stroke="${ink}" stroke-width="${1.7 * sw}" stroke-linecap="round" fill="none" />`,
287
367
  // tiny visible pupils
288
- `<circle cx="${lx}" cy="${y + 0.5}" r="0.9" fill="${ink}" />`,
289
- `<circle cx="${rx}" cy="${y + 0.5}" r="0.9" fill="${ink}" />`
368
+ `<circle cx="${lx}" cy="${y + 0.5}" r="${0.9 * sw}" fill="${ink}" />`,
369
+ `<circle cx="${rx}" cy="${y + 0.5}" r="${0.9 * sw}" fill="${ink}" />`
290
370
  ].join("");
291
371
  case "star":
292
372
  return [starEye(lx, y, ink), starEye(rx, y, ink)].join("");
@@ -303,20 +383,20 @@ function renderEyes(id, palette, anchor) {
303
383
  ].join("");
304
384
  case "dot":
305
385
  return [
306
- `<circle cx="${lx}" cy="${y}" r="${1.4 * s}" fill="${ink}" />`,
307
- `<circle cx="${rx}" cy="${y}" r="${1.4 * s}" fill="${ink}" />`
386
+ `<circle cx="${lx}" cy="${y}" r="${1.4 * s * sw}" fill="${ink}" />`,
387
+ `<circle cx="${rx}" cy="${y}" r="${1.4 * s * sw}" fill="${ink}" />`
308
388
  ].join("");
309
389
  case "cross":
310
- return [crossEye(lx, y, ink), crossEye(rx, y, ink)].join("");
390
+ return [crossEye(lx, y, ink, sw), crossEye(rx, y, ink, sw)].join("");
311
391
  }
312
392
  }
313
393
  function heartEye(cx, cy, color) {
314
394
  const s = 2;
315
395
  return `<path d="M${cx} ${cy + s * 1.4} L${cx - s * 1.8} ${cy - s * 0.2} A${s} ${s} 0 0 1 ${cx} ${cy - s * 0.6} A${s} ${s} 0 0 1 ${cx + s * 1.8} ${cy - s * 0.2} Z" fill="${color}" />`;
316
396
  }
317
- function crossEye(cx, cy, color) {
397
+ function crossEye(cx, cy, color, sw = 1) {
318
398
  const s = 2.4;
319
- return `<g stroke="${color}" stroke-width="1.6" stroke-linecap="round"><line x1="${cx - s}" y1="${cy - s}" x2="${cx + s}" y2="${cy + s}" /><line x1="${cx - s}" y1="${cy + s}" x2="${cx + s}" y2="${cy - s}" /></g>`;
399
+ return `<g stroke="${color}" stroke-width="${1.6 * sw}" stroke-linecap="round"><line x1="${cx - s}" y1="${cy - s}" x2="${cx + s}" y2="${cy + s}" /><line x1="${cx - s}" y1="${cy + s}" x2="${cx + s}" y2="${cy - s}" /></g>`;
320
400
  }
321
401
  function sclera(cx, cy, rx, ry) {
322
402
  return `<ellipse cx="${cx}" cy="${cy}" rx="${rx}" ry="${ry}" fill="#FFFFFF" />`;
@@ -336,41 +416,68 @@ function starEye(cx, cy, color) {
336
416
  }
337
417
 
338
418
  // src/parts/mouth.ts
339
- function renderMouth(id, palette, anchor, curveScale = 1) {
419
+ function renderMouth(id, palette, anchor, curveScale = 1, opts) {
340
420
  const cx = anchor.cx;
341
421
  const y = anchor.mouthY;
342
422
  const w = anchor.mouthSpan * curveScale;
343
423
  const ink = palette.ink;
424
+ const sw = opts?.strokeMul ?? 1;
425
+ const base = 1.8 * sw;
344
426
  switch (id) {
345
427
  case "smile":
346
- return `<path d="M${cx - w} ${y} Q${cx} ${y + 5} ${cx + w} ${y}" stroke="${ink}" stroke-width="1.8" stroke-linecap="round" fill="none" />`;
428
+ return `<path d="M${cx - w} ${y} Q${cx} ${y + 5} ${cx + w} ${y}" stroke="${ink}" stroke-width="${base}" stroke-linecap="round" fill="none" />`;
347
429
  case "grin":
348
- return `<path d="M${cx - w - 1} ${y - 2} Q${cx} ${y + 7} ${cx + w + 1} ${y - 2}" stroke="${ink}" stroke-width="1.8" stroke-linecap="round" fill="none" />`;
430
+ return `<path d="M${cx - w - 1} ${y - 2} Q${cx} ${y + 7} ${cx + w + 1} ${y - 2}" stroke="${ink}" stroke-width="${base}" stroke-linecap="round" fill="none" />`;
349
431
  case "open":
350
432
  return [
351
- `<path d="M${cx - w - 1} ${y - 2} Q${cx} ${y + 9} ${cx + w + 1} ${y - 2}" stroke="${ink}" stroke-width="1.8" stroke-linecap="round" fill="${ink}" fill-opacity="0.55" />`,
433
+ `<path d="M${cx - w - 1} ${y - 2} Q${cx} ${y + 9} ${cx + w + 1} ${y - 2}" stroke="${ink}" stroke-width="${base}" stroke-linecap="round" fill="${ink}" fill-opacity="0.55" />`,
352
434
  `<ellipse cx="${cx}" cy="${y + 3}" rx="${w * 0.55}" ry="1.8" fill="#F472B6" opacity="0.75" />`
353
435
  ].join("");
354
436
  case "flat":
355
- return `<path d="M${cx - w + 1} ${y} L${cx + w - 1} ${y}" stroke="${ink}" stroke-width="1.8" stroke-linecap="round" fill="none" />`;
437
+ return `<path d="M${cx - w + 1} ${y} L${cx + w - 1} ${y}" stroke="${ink}" stroke-width="${base}" stroke-linecap="round" fill="none" />`;
356
438
  case "smirk":
357
- return `<path d="M${cx - w} ${y} Q${cx} ${y + 3} ${cx + w + 1} ${y - 2}" stroke="${ink}" stroke-width="1.8" stroke-linecap="round" fill="none" />`;
439
+ return `<path d="M${cx - w} ${y} Q${cx} ${y + 3} ${cx + w + 1} ${y - 2}" stroke="${ink}" stroke-width="${base}" stroke-linecap="round" fill="none" />`;
358
440
  case "awe":
359
441
  return `<ellipse cx="${cx}" cy="${y + 1}" rx="${w * 0.45}" ry="3.2" fill="${ink}" opacity="0.85" />`;
360
442
  case "tongue":
361
443
  return [
362
- `<path d="M${cx - w} ${y} Q${cx} ${y + 6} ${cx + w} ${y}" stroke="${ink}" stroke-width="1.8" stroke-linecap="round" fill="none" />`,
363
- `<path d="M${cx - 2} ${y + 4} Q${cx} ${y + 9} ${cx + 2} ${y + 4} Z" fill="#F472B6" stroke="${ink}" stroke-width="0.6" />`
444
+ `<path d="M${cx - w} ${y} Q${cx} ${y + 6} ${cx + w} ${y}" stroke="${ink}" stroke-width="${base}" stroke-linecap="round" fill="none" />`,
445
+ `<path d="M${cx - 2} ${y + 4} Q${cx} ${y + 9} ${cx + 2} ${y + 4} Z" fill="#F472B6" stroke="${ink}" stroke-width="${0.6 * sw}" />`
364
446
  ].join("");
365
447
  case "tooth":
366
448
  return [
367
- `<path d="M${cx - w} ${y} Q${cx} ${y + 5} ${cx + w} ${y}" stroke="${ink}" stroke-width="1.8" stroke-linecap="round" fill="none" />`,
368
- `<rect x="${cx - 1.2}" y="${y + 0.4}" width="2.4" height="2.6" rx="0.4" fill="#FFFFFF" stroke="${ink}" stroke-width="0.4" />`
449
+ `<path d="M${cx - w} ${y} Q${cx} ${y + 5} ${cx + w} ${y}" stroke="${ink}" stroke-width="${base}" stroke-linecap="round" fill="none" />`,
450
+ `<rect x="${cx - 1.2}" y="${y + 0.4}" width="2.4" height="2.6" rx="0.4" fill="#FFFFFF" stroke="${ink}" stroke-width="${0.4 * sw}" />`
369
451
  ].join("");
370
452
  case "wave":
371
- return `<path d="M${cx - w} ${y + 1} Q${cx - w / 2} ${y - 1.5} ${cx} ${y + 1} Q${cx + w / 2} ${y + 3.5} ${cx + w} ${y + 1}" stroke="${ink}" stroke-width="1.8" stroke-linecap="round" fill="none" />`;
453
+ return `<path d="M${cx - w} ${y + 1} Q${cx - w / 2} ${y - 1.5} ${cx} ${y + 1} Q${cx + w / 2} ${y + 3.5} ${cx + w} ${y + 1}" stroke="${ink}" stroke-width="${base}" stroke-linecap="round" fill="none" />`;
372
454
  case "dot":
373
- return `<circle cx="${cx}" cy="${y + 1}" r="1.2" fill="${ink}" />`;
455
+ return `<circle cx="${cx}" cy="${y + 1}" r="${1.2 * sw}" fill="${ink}" />`;
456
+ case "jagged": {
457
+ const half = w + 1;
458
+ const top = y - 1;
459
+ const bot = y + 5;
460
+ const step = half * 2 / 8;
461
+ const x0 = cx - half;
462
+ const points = [];
463
+ points.push(`${x0} ${top}`);
464
+ for (let i = 1; i <= 8; i++) {
465
+ const px = x0 + step * i;
466
+ const py = i % 2 === 1 ? bot : top;
467
+ points.push(`${px.toFixed(2)} ${py}`);
468
+ }
469
+ return `<path d="M${points.join(" L")} L${cx + half} ${top} Z" fill="${ink}" />`;
470
+ }
471
+ case "fangs": {
472
+ const half = w - 1;
473
+ return [
474
+ `<path d="M${cx - half} ${y} L${cx + half} ${y}" stroke="${ink}" stroke-width="${base}" stroke-linecap="round" fill="none" />`,
475
+ // Left fang
476
+ `<path d="M${cx - 2.5} ${y + 0.4} L${cx - 1.2} ${y + 4.5} L${cx - 0.2} ${y + 0.4} Z" fill="#FFFFFF" stroke="${ink}" stroke-width="${0.5 * sw}" />`,
477
+ // Right fang
478
+ `<path d="M${cx + 0.2} ${y + 0.4} L${cx + 1.2} ${y + 4.5} L${cx + 2.5} ${y + 0.4} Z" fill="#FFFFFF" stroke="${ink}" stroke-width="${0.5 * sw}" />`
479
+ ].join("");
480
+ }
374
481
  }
375
482
  }
376
483
 
@@ -407,7 +514,8 @@ function renderAntenna(id, anchor, palette) {
407
514
  }
408
515
 
409
516
  // src/parts/accessory.ts
410
- function renderAccessory(id, palette, anchor) {
517
+ function renderAccessory(id, palette, anchor, opts) {
518
+ const sw = opts?.strokeMul ?? 1;
411
519
  switch (id) {
412
520
  case "none":
413
521
  return "";
@@ -442,10 +550,11 @@ function renderAccessory(id, palette, anchor) {
442
550
  const rx = anchor.cx + anchor.eyeOffset;
443
551
  const y = anchor.eyeY;
444
552
  const r = 6;
553
+ const gw = 1.2 * sw;
445
554
  return [
446
- `<circle cx="${lx}" cy="${y}" r="${r}" fill="none" stroke="${palette.ink}" stroke-width="1.2" />`,
447
- `<circle cx="${rx}" cy="${y}" r="${r}" fill="none" stroke="${palette.ink}" stroke-width="1.2" />`,
448
- `<line x1="${lx + r}" y1="${y}" x2="${rx - r}" y2="${y}" stroke="${palette.ink}" stroke-width="1.2" />`,
555
+ `<circle cx="${lx}" cy="${y}" r="${r}" fill="none" stroke="${palette.ink}" stroke-width="${gw}" />`,
556
+ `<circle cx="${rx}" cy="${y}" r="${r}" fill="none" stroke="${palette.ink}" stroke-width="${gw}" />`,
557
+ `<line x1="${lx + r}" y1="${y}" x2="${rx - r}" y2="${y}" stroke="${palette.ink}" stroke-width="${gw}" />`,
449
558
  // subtle lens fill
450
559
  `<circle cx="${lx}" cy="${y}" r="${r - 1}" fill="#FFFFFF" opacity="0.18" />`,
451
560
  `<circle cx="${rx}" cy="${y}" r="${r - 1}" fill="#FFFFFF" opacity="0.18" />`
@@ -463,6 +572,20 @@ function renderAccessory(id, palette, anchor) {
463
572
  case "mole": {
464
573
  return `<circle cx="${anchor.cx - anchor.cheekOffset * 0.6}" cy="${anchor.cheekY + 2}" r="0.9" fill="${palette.ink}" />`;
465
574
  }
575
+ case "earring": {
576
+ const ex = anchor.cheekOffset + 4;
577
+ const ey = anchor.cheekY + 4;
578
+ const lx = anchor.cx - ex;
579
+ const rx = anchor.cx + ex;
580
+ return [
581
+ // Left earring — small stud + drop
582
+ `<circle cx="${lx}" cy="${ey}" r="${1.1 * sw}" fill="${palette.accent}" stroke="${palette.ink}" stroke-width="${0.4 * sw}" />`,
583
+ `<ellipse cx="${lx}" cy="${ey + 3.2}" rx="${1.3 * sw}" ry="${2 * sw}" fill="${palette.accent}" stroke="${palette.ink}" stroke-width="${0.4 * sw}" />`,
584
+ // Right earring
585
+ `<circle cx="${rx}" cy="${ey}" r="${1.1 * sw}" fill="${palette.accent}" stroke="${palette.ink}" stroke-width="${0.4 * sw}" />`,
586
+ `<ellipse cx="${rx}" cy="${ey + 3.2}" rx="${1.3 * sw}" ry="${2 * sw}" fill="${palette.accent}" stroke="${palette.ink}" stroke-width="${0.4 * sw}" />`
587
+ ].join("");
588
+ }
466
589
  }
467
590
  function dot(cx, cy, color) {
468
591
  return `<circle cx="${cx}" cy="${cy}" r="0.85" fill="${color}" opacity="0.55" />`;
@@ -556,6 +679,84 @@ function renderTopper(id, anchor, palette) {
556
679
  `<path d="M${cx - 5} ${topY + 4} L${cx - 6} ${topY - 4} M${cx - 6} ${topY - 4} L${cx - 10} ${topY - 6} M${cx - 6} ${topY - 4} L${cx - 6} ${topY - 9} M${cx - 6} ${topY - 9} L${cx - 8} ${topY - 11} M${cx - 6} ${topY - 9} L${cx - 3} ${topY - 11}" stroke="${ink}" stroke-width="1.3" stroke-linecap="round" fill="none" />`,
557
680
  `<path d="M${cx + 5} ${topY + 4} L${cx + 6} ${topY - 4} M${cx + 6} ${topY - 4} L${cx + 10} ${topY - 6} M${cx + 6} ${topY - 4} L${cx + 6} ${topY - 9} M${cx + 6} ${topY - 9} L${cx + 8} ${topY - 11} M${cx + 6} ${topY - 9} L${cx + 3} ${topY - 11}" stroke="${ink}" stroke-width="1.3" stroke-linecap="round" fill="none" />`
558
681
  ].join("");
682
+ case "bob": {
683
+ const eyeY = anchor.eyeY;
684
+ const tt = topY - 4;
685
+ const bt = eyeY + 6;
686
+ return [
687
+ // Main hair cap — slightly asymmetric for soft look
688
+ `<path d="M${cx - 24} ${bt} Q${cx - 26} ${eyeY - 4} ${cx - 22} ${tt + 4} Q${cx - 14} ${tt - 2} ${cx} ${tt - 3} Q${cx + 14} ${tt - 2} ${cx + 22} ${tt + 4} Q${cx + 26} ${eyeY - 4} ${cx + 24} ${bt} Q${cx + 18} ${eyeY + 2} ${cx + 14} ${eyeY - 2} Q${cx} ${eyeY - 8} ${cx - 14} ${eyeY - 2} Q${cx - 18} ${eyeY + 2} ${cx - 24} ${bt} Z" fill="${ink}" opacity="0.92" />`,
689
+ // Subtle highlight strand
690
+ `<path d="M${cx - 14} ${tt + 4} Q${cx - 6} ${tt + 2} ${cx + 2} ${tt + 6}" stroke="${palette.accent}" stroke-width="0.6" fill="none" opacity="0.25" />`
691
+ ].join("");
692
+ }
693
+ case "bun": {
694
+ const baseY = topY + 4;
695
+ const bunY = topY - 8;
696
+ return [
697
+ // Hair base on crown
698
+ `<path d="M${cx - 16} ${baseY} Q${cx} ${topY - 4} ${cx + 16} ${baseY} Q${cx + 12} ${baseY - 4} ${cx} ${baseY - 6} Q${cx - 12} ${baseY - 4} ${cx - 16} ${baseY} Z" fill="${ink}" opacity="0.92" />`,
699
+ // Bun disc
700
+ `<ellipse cx="${cx}" cy="${bunY}" rx="6" ry="5" fill="${ink}" opacity="0.95" />`,
701
+ // Bun wrap detail
702
+ `<ellipse cx="${cx}" cy="${bunY - 0.5}" rx="3.5" ry="2.5" fill="none" stroke="${palette.accent}" stroke-width="0.4" opacity="0.4" />`
703
+ ].join("");
704
+ }
705
+ case "witchHat": {
706
+ const tipY = topY - 26;
707
+ const baseY = topY + 2;
708
+ return [
709
+ // Cone — slight curve, tilts right
710
+ `<path d="M${cx - 14} ${baseY} Q${cx - 4} ${baseY - 10} ${cx + 4} ${tipY} Q${cx + 2} ${baseY - 4} ${cx + 14} ${baseY} Z" fill="${ink}" opacity="0.96" />`,
711
+ // Brim — wide flat oval w/ slight curve
712
+ `<ellipse cx="${cx}" cy="${baseY + 2}" rx="22" ry="3.4" fill="${ink}" opacity="0.96" />`,
713
+ // Band across cone base
714
+ `<rect x="${cx - 14}" y="${baseY - 4}" width="28" height="3" fill="${palette.accent}" opacity="0.85" />`,
715
+ // Buckle
716
+ `<rect x="${cx - 2}" y="${baseY - 4}" width="4" height="3" fill="${palette.bodyFrom}" stroke="${ink}" stroke-width="0.4" />`,
717
+ // Star/moon sparkle near tip
718
+ `<circle cx="${cx + 2}" cy="${tipY + 6}" r="0.9" fill="${palette.accent}" opacity="0.9" />`
719
+ ].join("");
720
+ }
721
+ case "pumpkinStem": {
722
+ return [
723
+ // Main stem — slightly curved
724
+ `<path d="M${cx - 2} ${topY + 4} Q${cx} ${topY - 2} ${cx + 1} ${topY - 8} L${cx + 3} ${topY - 8} Q${cx + 4} ${topY} ${cx + 2} ${topY + 4} Z" fill="#3F6F2C" stroke="${ink}" stroke-width="0.5" />`,
725
+ // Leaf curling off
726
+ `<path d="M${cx + 3} ${topY - 4} Q${cx + 9} ${topY - 8} ${cx + 12} ${topY - 4} Q${cx + 8} ${topY - 2} ${cx + 3} ${topY - 2} Z" fill="#4A8035" stroke="${ink}" stroke-width="0.4" />`,
727
+ // Vein on leaf
728
+ `<path d="M${cx + 5} ${topY - 3} L${cx + 11} ${topY - 5}" stroke="#2D5020" stroke-width="0.4" />`
729
+ ].join("");
730
+ }
731
+ case "ghostSheet": {
732
+ return [
733
+ // Sheet cap — wider than body, hangs lower at sides
734
+ `<path d="M${cx - 22} ${topY + 8} Q${cx - 26} ${topY - 4} ${cx - 14} ${topY - 10} Q${cx} ${topY - 14} ${cx + 14} ${topY - 10} Q${cx + 26} ${topY - 4} ${cx + 22} ${topY + 8} Q${cx + 12} ${topY + 4} ${cx} ${topY + 6} Q${cx - 12} ${topY + 4} ${cx - 22} ${topY + 8} Z" fill="${palette.accent}" stroke="${ink}" stroke-width="0.6" opacity="0.9" />`,
735
+ // Fold shadows
736
+ `<path d="M${cx - 12} ${topY - 6} Q${cx - 10} ${topY - 2} ${cx - 14} ${topY + 4}" stroke="${ink}" stroke-width="0.45" fill="none" opacity="0.35" />`,
737
+ `<path d="M${cx + 12} ${topY - 6} Q${cx + 10} ${topY - 2} ${cx + 14} ${topY + 4}" stroke="${ink}" stroke-width="0.45" fill="none" opacity="0.35" />`
738
+ ].join("");
739
+ }
740
+ case "ponytail": {
741
+ const eyeY = anchor.eyeY;
742
+ const fh = eyeY - 7;
743
+ const crownY = topY;
744
+ const baseX = cx + 18;
745
+ const baseY = crownY + 6;
746
+ return [
747
+ // Sleek hair cap — narrower than bob, hugs the crown, soft hairline.
748
+ `<path d="M${cx - 22} ${fh} Q${cx - 24} ${crownY - 2} ${cx - 12} ${crownY - 4} L${cx + 14} ${crownY - 4} Q${cx + 24} ${crownY} ${cx + 22} ${fh} Q${cx + 10} ${fh - 1} ${cx} ${fh + 2} Q${cx - 10} ${fh - 1} ${cx - 22} ${fh} Z" fill="${ink}" opacity="0.94" />`,
749
+ // Subtle highlight sweeping back toward the tie
750
+ `<path d="M${cx - 12} ${crownY - 2} Q${cx} ${crownY - 3} ${baseX - 2} ${baseY - 2}" stroke="${palette.accent}" stroke-width="0.5" fill="none" opacity="0.3" />`,
751
+ // Ponytail tie — small ring where the hair gathers
752
+ `<ellipse cx="${baseX}" cy="${baseY}" rx="3" ry="2.4" fill="${ink}" opacity="0.95" />`,
753
+ `<ellipse cx="${baseX}" cy="${baseY}" rx="1.4" ry="1.1" fill="${palette.accent}" opacity="0.32" />`,
754
+ // Tail — long tapered strand curving down and slightly out
755
+ `<path d="M${baseX - 1} ${baseY + 2} Q${baseX + 5} ${baseY + 10} ${baseX + 8} ${baseY + 20} Q${baseX + 9} ${baseY + 28} ${baseX + 4} ${baseY + 30} Q${baseX + 1} ${baseY + 22} ${baseX - 3} ${baseY + 12} Z" fill="${ink}" opacity="0.92" />`,
756
+ // Inner highlight following the tail's flow direction
757
+ `<path d="M${baseX + 2} ${baseY + 6} Q${baseX + 5} ${baseY + 16} ${baseX + 6} ${baseY + 24}" stroke="${palette.accent}" stroke-width="0.5" fill="none" opacity="0.25" />`
758
+ ].join("");
759
+ }
559
760
  }
560
761
  }
561
762
 
@@ -626,6 +827,20 @@ function renderOutfit(id, anchor, palette) {
626
827
  `<circle cx="${cx}" cy="${cy + 7}" r="1.6" fill="${accent}" stroke="${ink}" stroke-width="0.5" />`,
627
828
  `<circle cx="${cx}" cy="${cy + 7}" r="0.7" fill="${palette.blush}" />`
628
829
  ].join("");
830
+ case "tie": {
831
+ const knotTop = cy - 3;
832
+ const knotBot = cy + 1;
833
+ return [
834
+ // Shirt-collar peek behind the tie (so tie reads as worn over a shirt)
835
+ `<path d="M${cx - 11} ${cy - 2} L${cx - 3} ${knotBot} L${cx + 3} ${knotBot} L${cx + 11} ${cy - 2} L${cx + 6} ${cy + 6} L${cx - 6} ${cy + 6} Z" fill="${accent}" stroke="${ink}" stroke-width="0.55" />`,
836
+ // Knot — small trapezoid centered
837
+ `<path d="M${cx - 3.2} ${knotTop} L${cx + 3.2} ${knotTop} L${cx + 2.4} ${knotBot} L${cx - 2.4} ${knotBot} Z" fill="${palette.bodyTo}" stroke="${ink}" stroke-width="0.5" />`,
838
+ // Blade — narrower at top, widens, then pointed tip at bottom
839
+ `<path d="M${cx - 2.4} ${knotBot} L${cx + 2.4} ${knotBot} L${cx + 3.4} ${cy + 6} L${cx + 2.8} ${cy + 12} L${cx} ${cy + 15} L${cx - 2.8} ${cy + 12} L${cx - 3.4} ${cy + 6} Z" fill="${palette.bodyTo}" stroke="${ink}" stroke-width="0.5" />`,
840
+ // Subtle highlight stripe down the blade
841
+ `<path d="M${cx} ${knotBot + 0.5} L${cx} ${cy + 13.5}" stroke="${ink}" stroke-width="0.35" opacity="0.35" />`
842
+ ].join("");
843
+ }
629
844
  }
630
845
  }
631
846
 
@@ -639,6 +854,9 @@ var BODY_IDS = [
639
854
  "dumpling",
640
855
  "taro",
641
856
  "wisp"
857
+ // NOTE: 'squircle' is intentionally NOT in the base pool — pack-only body so
858
+ // existing seeds keep their original picks (no determinism shift). Packs can
859
+ // opt-in via `picks.body: ['squircle']`.
642
860
  ];
643
861
  var EYE_IDS = [
644
862
  "round",
@@ -692,31 +910,550 @@ var TOPPER_IDS = [
692
910
  "antlers"
693
911
  ];
694
912
 
913
+ // src/packs/office.ts
914
+ var palettes = [
915
+ // Near-grayscale with cool blue undertone
916
+ { id: "office:graphite", bodyFrom: "#989BA2", bodyTo: "#696D72", accent: "#FFFFFF", ink: "#1A1C1F", blush: "#A09D99" },
917
+ // Warm stone — barely tinted neutral
918
+ { id: "office:stone", bodyFrom: "#A5A29D", bodyTo: "#74726F", accent: "#FFFFFF", ink: "#1C1B19", blush: "#A8A5A0" },
919
+ // Muted sage — barely-green grey
920
+ { id: "office:sage", bodyFrom: "#9EA299", bodyTo: "#6F726B", accent: "#FFFFFF", ink: "#1B1D19", blush: "#A3A099" },
921
+ // Taupe — desaturated warm grey
922
+ { id: "office:taupe", bodyFrom: "#ACA098", bodyTo: "#78716C", accent: "#FFFFFF", ink: "#1E1B18", blush: "#AFA59C" },
923
+ // Cool slate — neutral grey with hint of blue
924
+ { id: "office:slate", bodyFrom: "#9DA2AA", bodyTo: "#6C7077", accent: "#FFFFFF", ink: "#1A1C20", blush: "#A0A2A6" }
925
+ ];
926
+ var officePack = {
927
+ id: "office",
928
+ name: "Office",
929
+ description: "Corporate ID-badge. Flat, muted, square silhouette. Mix of professional outfits + hairstyles.",
930
+ emoji: "\u{1F4BC}",
931
+ palettes,
932
+ paletteExclusive: true,
933
+ flat: true,
934
+ bgColor: "#FFFFFF",
935
+ featureStroke: 1.35,
936
+ picks: {
937
+ body: ["squircle"],
938
+ eyes: ["dot", "sleepy", "squint"],
939
+ mouth: ["flat", "dot"],
940
+ antenna: ["none"],
941
+ // Gender + style variety comes from accessory + outfit mix, NOT hair toppers
942
+ // (hair rendered as solid silhouettes reads as helmets at small sizes).
943
+ accessory: ["none", "glasses", "mole", "freckles", "earring", "earring"],
944
+ topper: ["none"],
945
+ background: ["solid"],
946
+ // Outfit mix — tie reads masc, necklace/bowtie/collar neutral-or-femme. No scarf (too bulky).
947
+ outfit: ["tie", "necklace", "collar", "bowtie"]
948
+ },
949
+ styleHints: {
950
+ masc: {
951
+ outfit: ["tie", "bowtie"],
952
+ accessory: ["none", "glasses", "mole", "freckles"]
953
+ },
954
+ femme: {
955
+ outfit: ["necklace"],
956
+ accessory: ["earring", "glasses", "mole"]
957
+ },
958
+ neutral: {
959
+ outfit: ["collar", "bowtie", "necklace"],
960
+ accessory: ["none", "glasses", "freckles"]
961
+ }
962
+ }
963
+ };
964
+
965
+ // src/packs/office-bright.ts
966
+ var palettes2 = [
967
+ // Cobalt — vibrant deep blue
968
+ { id: "office-bright:cobalt", bodyFrom: "#4F8DFF", bodyTo: "#1E40AF", accent: "#FFFFFF", ink: "#0A1638", blush: "#7BA7FF" },
969
+ // Emerald — vivid green
970
+ { id: "office-bright:emerald", bodyFrom: "#3CCB85", bodyTo: "#047857", accent: "#FFFFFF", ink: "#062418", blush: "#76DAA8" },
971
+ // Fuchsia — hot magenta-pink
972
+ { id: "office-bright:fuchsia", bodyFrom: "#F0529C", bodyTo: "#BE185D", accent: "#FFFFFF", ink: "#3A0820", blush: "#F58FBC" },
973
+ // Amber — vivid orange-yellow
974
+ { id: "office-bright:amber", bodyFrom: "#FFB347", bodyTo: "#C2700B", accent: "#FFFFFF", ink: "#3B1F03", blush: "#FFC97A" },
975
+ // Violet — saturated purple
976
+ { id: "office-bright:violet", bodyFrom: "#9F7AEA", bodyTo: "#6D28D9", accent: "#FFFFFF", ink: "#1A0A3A", blush: "#BFA0F0" }
977
+ ];
978
+ var officeBrightPack = {
979
+ id: "office-bright",
980
+ name: "Office Bright",
981
+ description: "Vivid corporate variant of Office. Saturated brand-style palettes, same flat ID-badge composition.",
982
+ emoji: "\u{1F3A8}",
983
+ palettes: palettes2,
984
+ paletteExclusive: true,
985
+ flat: true,
986
+ bgColor: "#FFFFFF",
987
+ featureStroke: 1.35,
988
+ picks: {
989
+ body: ["squircle"],
990
+ eyes: ["dot", "sleepy", "squint"],
991
+ mouth: ["flat", "dot"],
992
+ antenna: ["none"],
993
+ accessory: ["none", "glasses", "mole", "freckles", "earring", "earring"],
994
+ topper: ["none"],
995
+ background: ["solid"],
996
+ outfit: ["tie", "necklace", "collar", "bowtie"]
997
+ },
998
+ styleHints: {
999
+ masc: {
1000
+ outfit: ["tie", "bowtie"],
1001
+ accessory: ["none", "glasses", "mole", "freckles"]
1002
+ },
1003
+ femme: {
1004
+ outfit: ["necklace"],
1005
+ accessory: ["earring", "glasses", "mole"]
1006
+ },
1007
+ neutral: {
1008
+ outfit: ["collar", "bowtie", "necklace"],
1009
+ accessory: ["none", "glasses", "freckles"]
1010
+ }
1011
+ }
1012
+ };
1013
+
1014
+ // src/packs/halloween.ts
1015
+ var palettes3 = [
1016
+ // Vivid pumpkin orange — high saturation against dark plate
1017
+ { id: "halloween:pumpkin", bodyFrom: "#FF8C2A", bodyTo: "#D85A00", accent: "#FFE9C4", ink: "#1A0700", blush: "#FFA76A" },
1018
+ // Electric witch purple
1019
+ { id: "halloween:witch", bodyFrom: "#B266FF", bodyTo: "#6A1FB8", accent: "#F0D9FF", ink: "#0E031F", blush: "#D6A0FF" },
1020
+ // Acid slime — radioactive Halloween green
1021
+ { id: "halloween:slime", bodyFrom: "#B3F23A", bodyTo: "#5E9F00", accent: "#E8FFB8", ink: "#091300", blush: "#D8FF7C" },
1022
+ // Blood crimson — deep saturated red
1023
+ { id: "halloween:blood", bodyFrom: "#E83248", bodyTo: "#8E0014", accent: "#FFD6DA", ink: "#1F0004", blush: "#FF8A98" },
1024
+ // Bone — pale warm white, ghost-like
1025
+ { id: "halloween:bone", bodyFrom: "#F5EEDB", bodyTo: "#C8B89A", accent: "#FFFEF5", ink: "#1A1612", blush: "#E8D9B8" }
1026
+ ];
1027
+ var halloweenPack = {
1028
+ id: "halloween",
1029
+ name: "Halloween",
1030
+ description: "Spooky themed pack. Pumpkin/ghost/skull bodies, jagged grins, witch hats, dark night plate.",
1031
+ emoji: "\u{1F383}",
1032
+ palettes: palettes3,
1033
+ paletteExclusive: true,
1034
+ flat: true,
1035
+ bgColor: "#0E0A1A",
1036
+ // deep night-purple
1037
+ featureStroke: 1.4,
1038
+ picks: {
1039
+ body: ["pumpkin", "ghost", "skullHead"],
1040
+ eyes: ["cross", "dot", "sleepy", "wide"],
1041
+ mouth: ["jagged", "fangs", "flat", "dot"],
1042
+ antenna: ["none"],
1043
+ accessory: ["none", "sparkle", "eyepatch", "mole"],
1044
+ topper: ["none", "witchHat", "pumpkinStem", "ghostSheet", "horns", "antlers"],
1045
+ background: ["solid"],
1046
+ outfit: ["none", "collar", "scarf"]
1047
+ },
1048
+ styleHints: {
1049
+ // 'masc' → creepy / skeletal vibe
1050
+ masc: {
1051
+ outfit: ["none", "collar"],
1052
+ accessory: ["none", "eyepatch"],
1053
+ topper: ["horns", "antlers", "witchHat"]
1054
+ },
1055
+ // 'femme' → witchy / sparkly vibe
1056
+ femme: {
1057
+ outfit: ["none", "scarf"],
1058
+ accessory: ["sparkle", "mole"],
1059
+ topper: ["witchHat", "ghostSheet", "pumpkinStem"]
1060
+ },
1061
+ // 'neutral' → balanced classic Halloween — pumpkin or none topper
1062
+ neutral: {
1063
+ outfit: ["none"],
1064
+ accessory: ["none", "sparkle"],
1065
+ topper: ["none", "witchHat", "pumpkinStem"]
1066
+ }
1067
+ }
1068
+ };
1069
+
1070
+ // src/packs/pastel.ts
1071
+ var palettes4 = [
1072
+ // Cotton candy — soft pink, the most kawaii signal
1073
+ { id: "pastel:cotton-candy", bodyFrom: "#FFB4D4", bodyTo: "#FF7AAB", accent: "#FFF0F6", ink: "#5E1C40", blush: "#FFC6DC" },
1074
+ // Butter — warm pastel yellow
1075
+ { id: "pastel:butter", bodyFrom: "#FFE07F", bodyTo: "#E6B12E", accent: "#FFFBE6", ink: "#5C4810", blush: "#FFE5A0" },
1076
+ // Mint cream — soft green
1077
+ { id: "pastel:mint-cream", bodyFrom: "#A8E9C8", bodyTo: "#5BC499", accent: "#EFFEF5", ink: "#194433", blush: "#C2EED4" },
1078
+ // Lavender — gentle purple
1079
+ { id: "pastel:lavender", bodyFrom: "#C8B5F0", bodyTo: "#9275D4", accent: "#F6F0FE", ink: "#321F58", blush: "#D8C6F0" },
1080
+ // Peach bloom — warm coral peach
1081
+ { id: "pastel:peach-bloom", bodyFrom: "#FFC1A0", bodyTo: "#F08F6A", accent: "#FFF1E8", ink: "#6E2F12", blush: "#FFC6AC" },
1082
+ // Sky drop — pastel baby blue
1083
+ { id: "pastel:sky-drop", bodyFrom: "#A8D9F5", bodyTo: "#6BB1E0", accent: "#EFF8FE", ink: "#15324A", blush: "#BCDDF0" }
1084
+ ];
1085
+ var pastelPack = {
1086
+ id: "pastel",
1087
+ name: "Pastel",
1088
+ description: "Kawaii cozy companion. Round chubby bodies, big eyes, blushy cheeks, soft cream plate. Finch-like.",
1089
+ emoji: "\u{1F338}",
1090
+ palettes: palettes4,
1091
+ paletteExclusive: true,
1092
+ flat: true,
1093
+ bgColor: "#FBF6F0",
1094
+ // warm cream plate
1095
+ featureStroke: 1.3,
1096
+ picks: {
1097
+ body: ["dumpling", "orb", "pebble"],
1098
+ // Big expressive eyes — wide/round read as kawaii, sleepy/wink soften it.
1099
+ // Drop oval (too anime) + cross (too aggressive) for cohesion.
1100
+ eyes: ["round", "wide", "sleepy", "wink", "heart", "star"],
1101
+ // Tiny mouths — smile/dot/tongue. No grin/awe (too loud).
1102
+ mouth: ["smile", "dot", "tongue", "smirk"],
1103
+ antenna: ["none", "classic", "curl"],
1104
+ // Blush weighted heavy — every pastel avatar gets blushy by default
1105
+ accessory: ["blush", "blush", "blush", "freckles", "sparkle", "none"],
1106
+ // Round ears / headband / halo / tuft give character variety
1107
+ topper: ["none", "roundEars", "ears", "headband", "halo", "tuft"],
1108
+ background: ["solid"],
1109
+ // Outfit stays minimal — collar gives "shirt" peek
1110
+ outfit: ["none", "collar", "sunflower", "necklace"]
1111
+ },
1112
+ styleHints: {
1113
+ // 'masc' → softer kawaii: minimal toppers, dot/sleepy eyes
1114
+ masc: {
1115
+ eyes: ["round", "sleepy", "wink"],
1116
+ accessory: ["blush", "freckles", "none"],
1117
+ topper: ["none", "tuft", "cap"],
1118
+ outfit: ["none", "collar"]
1119
+ },
1120
+ // 'femme' → full kawaii: heart/star eyes, ears+halo, sparkles
1121
+ femme: {
1122
+ eyes: ["heart", "star", "wide"],
1123
+ accessory: ["blush", "sparkle"],
1124
+ topper: ["roundEars", "ears", "halo", "headband"],
1125
+ outfit: ["none", "sunflower", "necklace"]
1126
+ },
1127
+ // 'neutral' → balanced
1128
+ neutral: {
1129
+ eyes: ["round", "wide", "sleepy"],
1130
+ accessory: ["blush", "freckles"],
1131
+ topper: ["none", "roundEars", "tuft"],
1132
+ outfit: ["none", "collar"]
1133
+ }
1134
+ }
1135
+ };
1136
+
1137
+ // src/packs/neon.ts
1138
+ var palettes5 = [
1139
+ // Hot pink — synthwave signature
1140
+ { id: "neon:pink", bodyFrom: "#FF6FD8", bodyTo: "#D6168C", accent: "#FFE5F5", ink: "#1F0214", blush: "#FFB3DD" },
1141
+ // Acid lime — radioactive green
1142
+ { id: "neon:lime", bodyFrom: "#D9FF3D", bodyTo: "#5FB800", accent: "#F5FFD0", ink: "#0F1500", blush: "#E5FF8A" },
1143
+ // Electric cyan — Tron blue
1144
+ { id: "neon:cyan", bodyFrom: "#4DEFFF", bodyTo: "#0099C9", accent: "#D6FBFF", ink: "#00141C", blush: "#A5EEFA" },
1145
+ // Sodium — bright street-lamp yellow-orange
1146
+ { id: "neon:sodium", bodyFrom: "#FFEC4D", bodyTo: "#E59700", accent: "#FFFADB", ink: "#221400", blush: "#FFE082" },
1147
+ // Magenta violet — saturated purple
1148
+ { id: "neon:violet", bodyFrom: "#C66DFF", bodyTo: "#6B14D6", accent: "#EFDDFF", ink: "#0F0024", blush: "#D8B0FF" },
1149
+ // Plasma orange-red — fiery
1150
+ { id: "neon:plasma", bodyFrom: "#FF6A3D", bodyTo: "#C42500", accent: "#FFD9CC", ink: "#1F0500", blush: "#FFA98A" }
1151
+ ];
1152
+ var neonPack = {
1153
+ id: "neon",
1154
+ name: "Neon",
1155
+ description: "Cyberpunk gaming PFP. Vivid neon bodies on a dark plate with soft glow halo. Sharp + intense.",
1156
+ emoji: "\u26A1",
1157
+ palettes: palettes5,
1158
+ paletteExclusive: true,
1159
+ flat: true,
1160
+ bgColor: "#0A0A14",
1161
+ // deep night plate
1162
+ featureStroke: 1.5,
1163
+ glow: true,
1164
+ picks: {
1165
+ body: ["tall", "wisp", "taro"],
1166
+ eyes: ["star", "wide", "cross", "dot"],
1167
+ mouth: ["grin", "awe", "tooth", "smirk"],
1168
+ antenna: ["spike", "double", "none"],
1169
+ accessory: ["sparkle", "glasses", "eyepatch", "none"],
1170
+ topper: ["none", "antlers", "horn", "horns", "tuft", "cap"],
1171
+ background: ["solid"],
1172
+ outfit: ["none", "scarf", "necklace"]
1173
+ },
1174
+ styleHints: {
1175
+ // 'masc' → aggressive: cross eyes, horns, spike antenna, eyepatch
1176
+ masc: {
1177
+ accessory: ["eyepatch", "glasses", "none"],
1178
+ topper: ["horns", "horn", "antlers"],
1179
+ outfit: ["none"]
1180
+ },
1181
+ // 'femme' → glamour: sparkle, halo, soft tuft
1182
+ femme: {
1183
+ accessory: ["sparkle", "glasses"],
1184
+ topper: ["tuft", "halo"],
1185
+ outfit: ["necklace", "scarf", "none"]
1186
+ },
1187
+ // 'neutral' → balanced edgy
1188
+ neutral: {
1189
+ accessory: ["glasses", "sparkle", "none"],
1190
+ topper: ["none", "cap", "tuft"],
1191
+ outfit: ["none"]
1192
+ }
1193
+ }
1194
+ };
1195
+
1196
+ // src/packs/mono.ts
1197
+ var palettes6 = [
1198
+ // Obsidian — near-black body, WHITE face features for contrast
1199
+ { id: "mono:obsidian", bodyFrom: "#2A2A2A", bodyTo: "#0A0A0A", accent: "#FAFAFA", ink: "#F2F2F2", blush: "#5A5A5A" },
1200
+ // Graphite — dark warm grey body, light face
1201
+ { id: "mono:graphite", bodyFrom: "#4A4A4A", bodyTo: "#222222", accent: "#F4F4F4", ink: "#EBEBEB", blush: "#7A7A7A" },
1202
+ // Slate — mid-grey body, dark face (good contrast either way; going dark for editorial)
1203
+ { id: "mono:slate", bodyFrom: "#8E8E8E", bodyTo: "#5A5A5A", accent: "#F8F8F8", ink: "#0A0A0A", blush: "#A8A8A8" },
1204
+ // Silver — mid-light grey, dark face
1205
+ { id: "mono:silver", bodyFrom: "#B8B8B8", bodyTo: "#888888", accent: "#FCFCFC", ink: "#1A1A1A", blush: "#D0D0D0" },
1206
+ // Fog — palest grey, dark face
1207
+ { id: "mono:fog", bodyFrom: "#E0E0E0", bodyTo: "#B0B0B0", accent: "#FFFFFF", ink: "#2A2A2A", blush: "#EDEDED" },
1208
+ // Ash — warm sand-grey, dark face with brown hint
1209
+ { id: "mono:ash", bodyFrom: "#A39A8E", bodyTo: "#6A6055", accent: "#F8F4EF", ink: "#1A1612", blush: "#B8AFA5" }
1210
+ ];
1211
+ var monoPack = {
1212
+ id: "mono",
1213
+ name: "Mono",
1214
+ description: "Editorial minimal. Clean grayscale silhouette on warm white plate, thin lines, no decoration.",
1215
+ emoji: "\u{1F5A4}",
1216
+ palettes: palettes6,
1217
+ paletteExclusive: true,
1218
+ flat: true,
1219
+ bgColor: "#FAFAF8",
1220
+ // warm near-white plate
1221
+ featureStroke: 1.15,
1222
+ // subtle but visible (vs Office 1.35, Neon 1.5)
1223
+ picks: {
1224
+ body: ["squircle"],
1225
+ eyes: ["dot"],
1226
+ mouth: ["flat", "dot"],
1227
+ antenna: ["none"],
1228
+ accessory: ["none", "none", "mole", "glasses", "freckles"],
1229
+ topper: ["none"],
1230
+ background: ["solid"],
1231
+ outfit: ["none"]
1232
+ },
1233
+ styleHints: {
1234
+ // 'masc' → bare silhouette, nothing
1235
+ masc: {
1236
+ accessory: ["none"],
1237
+ outfit: ["none"],
1238
+ topper: ["none"]
1239
+ },
1240
+ // 'femme' → one subtle accent (mole or freckles)
1241
+ femme: {
1242
+ accessory: ["mole", "freckles"],
1243
+ outfit: ["none"],
1244
+ topper: ["none"]
1245
+ },
1246
+ // 'neutral' → glasses option
1247
+ neutral: {
1248
+ accessory: ["none", "glasses"],
1249
+ outfit: ["none"],
1250
+ topper: ["none"]
1251
+ }
1252
+ }
1253
+ };
1254
+
1255
+ // src/packs/earth.ts
1256
+ var palettes7 = [
1257
+ // Sage — calm soft green
1258
+ { id: "earth:sage", bodyFrom: "#A8C49A", bodyTo: "#6D8E60", accent: "#F2F7EA", ink: "#1F2E18", blush: "#C8D7B4" },
1259
+ // Clay — terracotta warmth
1260
+ { id: "earth:clay", bodyFrom: "#D49072", bodyTo: "#9C4F2E", accent: "#FBEBDD", ink: "#3D1A0B", blush: "#E5BCA5" },
1261
+ // Sand — warm beige
1262
+ { id: "earth:sand", bodyFrom: "#E1CC9F", bodyTo: "#A88B5A", accent: "#FFF5E0", ink: "#4A3818", blush: "#EBD8B0" },
1263
+ // Moss — deeper forest green
1264
+ { id: "earth:moss", bodyFrom: "#82A570", bodyTo: "#3F6038", accent: "#EAF4DC", ink: "#0D1F08", blush: "#B9D2A1" },
1265
+ // Terracotta — fiery clay, more saturated
1266
+ { id: "earth:terracotta", bodyFrom: "#D87555", bodyTo: "#A53F22", accent: "#FFE5D8", ink: "#380F02", blush: "#EBA188" },
1267
+ // Mushroom — warm dusty mauve-brown
1268
+ { id: "earth:mushroom", bodyFrom: "#B59B8A", bodyTo: "#7D6353", accent: "#F4ECE3", ink: "#2C1C13", blush: "#C6B0A0" }
1269
+ ];
1270
+ var earthPack = {
1271
+ id: "earth",
1272
+ name: "Earth",
1273
+ description: "Wellness companion. Warm earthy palettes, soft round body, sleepy meditative face, leaf + halo toppers.",
1274
+ emoji: "\u{1F33F}",
1275
+ palettes: palettes7,
1276
+ paletteExclusive: true,
1277
+ flat: true,
1278
+ bgColor: "#FBF6EE",
1279
+ // warm cream plate
1280
+ featureStroke: 1.2,
1281
+ picks: {
1282
+ body: ["orb", "dumpling", "pebble"],
1283
+ eyes: ["sleepy", "sleepy", "dot"],
1284
+ mouth: ["smile", "dot", "flat"],
1285
+ antenna: ["none"],
1286
+ accessory: ["blush", "blush", "freckles", "mole", "sparkle", "none"],
1287
+ topper: ["none", "leaf", "halo", "headband", "tuft"],
1288
+ background: ["solid"],
1289
+ outfit: ["none", "collar", "scarf", "necklace"]
1290
+ },
1291
+ styleHints: {
1292
+ // 'masc' → minimal earth: dot eyes, no topper, no accessory
1293
+ masc: {
1294
+ accessory: ["none", "freckles", "mole"],
1295
+ topper: ["none", "tuft"],
1296
+ outfit: ["none", "collar"]
1297
+ },
1298
+ // 'femme' → wellness teacher: sleepy + leaf/halo + blush + sparkle
1299
+ femme: {
1300
+ accessory: ["blush", "sparkle"],
1301
+ topper: ["leaf", "halo", "headband"],
1302
+ outfit: ["none", "scarf", "necklace"]
1303
+ },
1304
+ // 'neutral' → balanced calm
1305
+ neutral: {
1306
+ accessory: ["blush", "freckles"],
1307
+ topper: ["none", "leaf", "tuft"],
1308
+ outfit: ["none", "collar"]
1309
+ }
1310
+ }
1311
+ };
1312
+
1313
+ // src/packs/index.ts
1314
+ var BUILT_IN_PACKS = [
1315
+ officePack,
1316
+ officeBrightPack,
1317
+ halloweenPack,
1318
+ pastelPack,
1319
+ neonPack,
1320
+ monoPack,
1321
+ earthPack
1322
+ ];
1323
+ var PACK_REGISTRY = Object.fromEntries(
1324
+ BUILT_IN_PACKS.map((p) => [p.id, p])
1325
+ );
1326
+ function resolvePacks(ids) {
1327
+ if (!ids || ids.length === 0) return [];
1328
+ const seen = /* @__PURE__ */ new Set();
1329
+ const result = [];
1330
+ for (const id of ids) {
1331
+ if (seen.has(id)) continue;
1332
+ const pack = PACK_REGISTRY[id];
1333
+ if (pack) {
1334
+ seen.add(id);
1335
+ result.push(pack);
1336
+ }
1337
+ }
1338
+ return result;
1339
+ }
1340
+
695
1341
  // src/select.ts
1342
+ function applyStyleHint(pool, packs, hint, partKey) {
1343
+ for (const pack of packs) {
1344
+ const subset = pack.styleHints?.[hint]?.[partKey];
1345
+ if (subset && subset.length > 0) {
1346
+ const narrowed = pool.filter((id) => subset.includes(id));
1347
+ if (narrowed.length > 0) return narrowed;
1348
+ }
1349
+ }
1350
+ return pool;
1351
+ }
1352
+ function resolvePartPool(basePool, packs, partKey) {
1353
+ const constraints = packs.map((p) => p.picks?.[partKey]).filter((list) => Array.isArray(list) && list.length > 0);
1354
+ if (constraints.length === 0) return basePool;
1355
+ let pool = constraints[0];
1356
+ for (let i = 1; i < constraints.length; i++) {
1357
+ pool = pool.filter((id) => constraints[i].includes(id));
1358
+ }
1359
+ if (pool.length > 0) return pool;
1360
+ const seen = /* @__PURE__ */ new Set();
1361
+ const union = [];
1362
+ for (const list of constraints) {
1363
+ for (const id of list) {
1364
+ if (!seen.has(id)) {
1365
+ seen.add(id);
1366
+ union.push(id);
1367
+ }
1368
+ }
1369
+ }
1370
+ return union.length > 0 ? union : basePool;
1371
+ }
696
1372
  function selectAvatar(seed2, options = {}) {
697
1373
  const rng = createRng(seed2);
698
- const paletteOverride = options.paletteId ? PALETTE_BY_ID[options.paletteId] : void 0;
699
- const palette = paletteOverride ?? rng.pick(PALETTES);
700
- const body = rng.pick(BODY_IDS);
701
- const eyes = rng.pick(EYE_IDS);
702
- const mouth = rng.pick(MOUTH_IDS);
703
- const antenna = rng.pick(ANTENNA_IDS);
704
- const accessory = rng.pick(ACCESSORY_IDS);
1374
+ const enabledPacks = resolvePacks(options.packs);
1375
+ const packPalettes = [];
1376
+ let exclusivePackPalettes = null;
1377
+ for (const pack of enabledPacks) {
1378
+ if (pack.palettes && pack.palettes.length > 0) {
1379
+ packPalettes.push(...pack.palettes);
1380
+ if (pack.paletteExclusive) {
1381
+ exclusivePackPalettes = exclusivePackPalettes ?? [];
1382
+ exclusivePackPalettes.push(...pack.palettes);
1383
+ }
1384
+ }
1385
+ }
1386
+ const palettePool = exclusivePackPalettes ? exclusivePackPalettes : packPalettes.length > 0 ? [...PALETTES, ...packPalettes] : PALETTES;
1387
+ const paletteByIdLookup = options.paletteId ? PALETTE_BY_ID[options.paletteId] ?? packPalettes.find((p) => p.id === options.paletteId) : void 0;
1388
+ const paletteOverride = options.palette ?? paletteByIdLookup;
1389
+ const palette = paletteOverride ?? rng.pick(palettePool);
1390
+ const bodyPool = resolvePartPool(BODY_IDS, enabledPacks, "body");
1391
+ const eyesPool = resolvePartPool(EYE_IDS, enabledPacks, "eyes");
1392
+ const mouthPool = resolvePartPool(MOUTH_IDS, enabledPacks, "mouth");
1393
+ const antennaPool = resolvePartPool(ANTENNA_IDS, enabledPacks, "antenna");
1394
+ let accessoryPool = resolvePartPool(ACCESSORY_IDS, enabledPacks, "accessory");
1395
+ const backgroundPool = resolvePartPool(BACKGROUND_IDS, enabledPacks, "background");
1396
+ let topperPool = resolvePartPool(TOPPER_IDS, enabledPacks, "topper");
1397
+ const styleHint = options.style;
1398
+ if (styleHint) {
1399
+ accessoryPool = applyStyleHint(accessoryPool, enabledPacks, styleHint, "accessory");
1400
+ topperPool = applyStyleHint(topperPool, enabledPacks, styleHint, "topper");
1401
+ }
1402
+ const body = rng.pick(bodyPool);
1403
+ const eyes = rng.pick(eyesPool);
1404
+ const mouth = rng.pick(mouthPool);
1405
+ const antenna = rng.pick(antennaPool);
1406
+ const accessory = rng.pick(accessoryPool);
705
1407
  let background;
706
1408
  if (typeof options.background === "string") {
707
1409
  background = options.background;
708
1410
  } else if (options.background && typeof options.background === "object") {
709
1411
  background = "solid";
710
1412
  } else {
711
- background = rng.pick(BACKGROUND_IDS);
1413
+ background = rng.pick(backgroundPool);
712
1414
  }
713
- const topperRaw = rng.pick(TOPPER_IDS);
1415
+ const topperRaw = rng.pick(topperPool);
714
1416
  const topper = antenna !== "none" && topperRaw !== "none" && topperRaw !== "leaf" ? "none" : topperRaw;
715
1417
  const hueShift = Math.round(rng.range(-30, 30));
716
1418
  const bodyScale = Number(rng.range(0.92, 1.08).toFixed(3));
717
1419
  const eyeGapShift = Number(rng.range(-2, 2).toFixed(2));
718
1420
  const mouthCurveScale = Number(rng.range(0.85, 1.15).toFixed(3));
719
1421
  const antennaTilt = Math.round(rng.range(-8, 8));
1422
+ const outfitConstraints = enabledPacks.map((p) => p.picks?.outfit).filter((list) => Array.isArray(list) && list.length > 0);
1423
+ let outfit = "none";
1424
+ if (outfitConstraints.length > 0) {
1425
+ let pool = outfitConstraints[0];
1426
+ for (let i = 1; i < outfitConstraints.length; i++) {
1427
+ pool = pool.filter((id) => outfitConstraints[i].includes(id));
1428
+ }
1429
+ if (pool.length === 0) {
1430
+ const seen = /* @__PURE__ */ new Set();
1431
+ const union = [];
1432
+ for (const list of outfitConstraints) {
1433
+ for (const id of list) {
1434
+ if (!seen.has(id)) {
1435
+ seen.add(id);
1436
+ union.push(id);
1437
+ }
1438
+ }
1439
+ }
1440
+ pool = union;
1441
+ }
1442
+ if (styleHint) {
1443
+ pool = applyStyleHint(pool, enabledPacks, styleHint, "outfit");
1444
+ }
1445
+ outfit = pool.length > 0 ? rng.pick(pool) : "none";
1446
+ }
1447
+ let flat;
1448
+ let bgColor;
1449
+ let featureStroke;
1450
+ let glow;
1451
+ for (const pack of enabledPacks) {
1452
+ if (pack.flat) flat = true;
1453
+ if (pack.bgColor) bgColor = pack.bgColor;
1454
+ if (pack.featureStroke) featureStroke = pack.featureStroke;
1455
+ if (pack.glow) glow = true;
1456
+ }
720
1457
  return {
721
1458
  seed: seed2,
722
1459
  palette,
@@ -727,12 +1464,16 @@ function selectAvatar(seed2, options = {}) {
727
1464
  accessory,
728
1465
  background,
729
1466
  topper,
730
- outfit: "none",
1467
+ outfit,
731
1468
  hueShift,
732
1469
  bodyScale,
733
1470
  eyeGapShift,
734
1471
  mouthCurveScale,
735
- antennaTilt
1472
+ antennaTilt,
1473
+ ...flat ? { flat: true } : {},
1474
+ ...bgColor ? { bgColor } : {},
1475
+ ...featureStroke ? { featureStroke } : {},
1476
+ ...glow ? { glow: true } : {}
736
1477
  };
737
1478
  }
738
1479
 
@@ -802,31 +1543,42 @@ function renderAvatarInner(spec, options = {}) {
802
1543
  ...baseAnchor,
803
1544
  eyeOffset: baseAnchor.eyeOffset + (spec.eyeGapShift ?? 0)
804
1545
  };
1546
+ const strokeMul = spec.featureStroke ?? 1;
805
1547
  const antennaSvg = renderAntenna(spec.antenna, anchor, spec.palette);
806
- const accessorySvg = renderAccessory(spec.accessory, spec.palette, anchor);
807
- const bodyMarkup = renderBody(spec.body, spec.palette, gradId);
808
- const bodyTransform = transformBody(spec.bodyScale ?? 1, anchor);
1548
+ const accessorySvg = renderAccessory(spec.accessory, spec.palette, anchor, { strokeMul });
1549
+ const flat = spec.flat === true;
1550
+ const glow = spec.glow === true;
1551
+ const glowId = `navii-glow-${id}`;
1552
+ const bodyMarkup = renderBody(spec.body, spec.palette, gradId, { flat });
1553
+ const effectiveBodyScale = flat ? 1 : spec.bodyScale ?? 1;
1554
+ const bodyTransform = transformBody(effectiveBodyScale, anchor);
809
1555
  const bodyFilter = spec.hueShift && spec.hueShift !== 0 ? ` filter="url(#${hueId})"` : "";
810
1556
  const bodyWrapped = `<g${bodyTransform}${bodyFilter}><g class="body">${bodyMarkup}</g></g>`;
1557
+ const glowLayer = glow ? `<g${bodyTransform} filter="url(#${glowId})" opacity="0.85"><g class="body-glow">${bodyMarkup.replace(/fill="url\(#[^"]+\)"/g, `fill="${spec.palette.bodyFrom}"`)}</g></g>` : "";
811
1558
  const antennaWrapped = antennaSvg ? `<g${transformAntenna(spec.antennaTilt ?? 0, anchor)}><g class="antenna">${antennaSvg}</g></g>` : "";
812
1559
  const tileBg = resolveTileBg(options.tileBg, spec.palette);
813
1560
  const tileCircle = tileBg ? `<circle cx="50" cy="50" r="50" fill="${tileBg}" />` : "";
814
1561
  const outfitSvg = renderOutfit(spec.outfit ?? "none", anchor, spec.palette);
1562
+ const packPlate = spec.bgColor ? `<rect x="0" y="0" width="100" height="100" fill="${spec.bgColor}" />` : "";
1563
+ const backgroundMarkup = spec.bgColor ? "" : renderBackground(spec.background, spec.palette, bgOverride);
815
1564
  const parts = [
816
1565
  tileCircle,
817
- renderBackground(spec.background, spec.palette, bgOverride),
1566
+ packPlate,
1567
+ backgroundMarkup,
1568
+ glowLayer,
818
1569
  bodyWrapped,
819
1570
  // outfit sits on the body but below the face, so face features stay readable
820
1571
  outfitSvg,
821
1572
  renderTopper(spec.topper, anchor, spec.palette),
822
- wrap("eyes", renderEyes(spec.eyes, spec.palette, anchor)),
823
- renderMouth(spec.mouth, spec.palette, anchor, spec.mouthCurveScale ?? 1),
1573
+ wrap("eyes", renderEyes(spec.eyes, spec.palette, anchor, { strokeMul })),
1574
+ renderMouth(spec.mouth, spec.palette, anchor, spec.mouthCurveScale ?? 1, { strokeMul }),
824
1575
  antennaWrapped,
825
1576
  accessorySvg && spec.accessory === "sparkle" ? wrap("sparkle", accessorySvg) : accessorySvg
826
1577
  ].join("");
827
1578
  const defs = [
828
- renderBodyDefs(spec.body, spec.palette, gradId),
829
- spec.hueShift && spec.hueShift !== 0 ? `<filter id="${hueId}" color-interpolation-filters="sRGB"><feColorMatrix type="hueRotate" values="${spec.hueShift}" /></filter>` : ""
1579
+ renderBodyDefs(spec.body, spec.palette, gradId, { flat }),
1580
+ spec.hueShift && spec.hueShift !== 0 ? `<filter id="${hueId}" color-interpolation-filters="sRGB"><feColorMatrix type="hueRotate" values="${spec.hueShift}" /></filter>` : "",
1581
+ glow ? `<filter id="${glowId}" x="-30%" y="-30%" width="160%" height="160%"><feGaussianBlur stdDeviation="4" /></filter>` : ""
830
1582
  ].join("");
831
1583
  return [
832
1584
  `<defs>${defs}</defs>`,
@@ -950,8 +1702,18 @@ function createAvatar(seed2, options = {}) {
950
1702
  }
951
1703
  return renderAvatar(selectAvatar(seed2, options), options);
952
1704
  }
1705
+ function random(options = {}) {
1706
+ const seed2 = randomSeed();
1707
+ return { svg: createAvatar(seed2, options), seed: seed2 };
1708
+ }
1709
+ function randomSeed() {
1710
+ const c = globalThis.crypto;
1711
+ if (c && typeof c.randomUUID === "function") return c.randomUUID();
1712
+ return Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2);
1713
+ }
953
1714
  var Navii = {
954
1715
  create: createAvatar,
1716
+ random,
955
1717
  render: renderAvatar,
956
1718
  select: selectAvatar,
957
1719
  group: renderGroup,
@@ -959,14 +1721,18 @@ var Navii = {
959
1721
  build
960
1722
  };
961
1723
 
1724
+ exports.BUILT_IN_PACKS = BUILT_IN_PACKS;
962
1725
  exports.Navii = Navii;
1726
+ exports.PACK_REGISTRY = PACK_REGISTRY;
963
1727
  exports.build = build;
964
1728
  exports.createAvatar = createAvatar;
965
1729
  exports.createRng = createRng;
966
1730
  exports.cyrb53 = cyrb53;
1731
+ exports.random = random;
967
1732
  exports.renderAvatar = renderAvatar;
968
1733
  exports.renderAvatarInner = renderAvatarInner;
969
1734
  exports.renderGroup = renderGroup;
1735
+ exports.resolvePacks = resolvePacks;
970
1736
  exports.seed = seed;
971
1737
  exports.selectAvatar = selectAvatar;
972
1738
  //# sourceMappingURL=index.cjs.map