@nodaro/prompts 1.13.0 → 1.15.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.
Files changed (69) hide show
  1. package/dist/index.cjs +719 -255
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +390 -2
  4. package/dist/index.d.ts +390 -2
  5. package/dist/index.js +689 -257
  6. package/dist/index.js.map +1 -1
  7. package/package.json +2 -2
  8. package/src/__tests__/adult-only-ratchet.test.ts +110 -0
  9. package/src/__tests__/age-floor.test.ts +347 -0
  10. package/src/__tests__/catalog-id-guard.test.ts +146 -0
  11. package/src/__tests__/catalog-overlay.test.ts +163 -0
  12. package/src/__tests__/dod-replace-pack-acceptance.test.ts +17 -0
  13. package/src/__tests__/fixtures/parameter-hint-golden.json +2 -2
  14. package/src/__tests__/minor-age-floor.test.ts +136 -0
  15. package/src/__tests__/multi-picker-spec.test.ts +21 -1
  16. package/src/__tests__/person-pack-adult-only.test.ts +45 -0
  17. package/src/__tests__/person-regional-aesthetic.test.ts +2 -1
  18. package/src/__tests__/picker-analyzer-registry.test.ts +14 -0
  19. package/src/__tests__/provider-prompt-doctrine.test.ts +39 -0
  20. package/src/action-fx.ts +2 -1
  21. package/src/aesthetic.ts +2 -1
  22. package/src/age-floor.ts +296 -0
  23. package/src/age-signal.ts +36 -0
  24. package/src/atmosphere.ts +2 -1
  25. package/src/backdrop.ts +2 -1
  26. package/src/camera-format.ts +2 -1
  27. package/src/camera-motions.ts +2 -1
  28. package/src/catalog-id-guard.ts +189 -0
  29. package/src/catalog-overlay.ts +148 -0
  30. package/src/catalog-packs.ts +27 -2
  31. package/src/character-fx.ts +2 -1
  32. package/src/color-look.ts +2 -1
  33. package/src/composition-effects.ts +2 -1
  34. package/src/era.ts +2 -1
  35. package/src/exposure-settings.ts +2 -1
  36. package/src/framing.ts +2 -1
  37. package/src/gemini-omni-inputs.ts +11 -3
  38. package/src/held-prop.ts +3 -1
  39. package/src/index.ts +4 -0
  40. package/src/instrumentation.ts +5 -4
  41. package/src/lens.ts +2 -1
  42. package/src/lighting.ts +2 -1
  43. package/src/loop-subject.ts +3 -1
  44. package/src/materials.ts +2 -1
  45. package/src/mood.ts +16 -5
  46. package/src/music-genre.ts +5 -4
  47. package/src/music-mood.ts +4 -3
  48. package/src/parameter-prompt-hint.ts +13 -57
  49. package/src/person-packs.ts +14 -2
  50. package/src/person.ts +75 -38
  51. package/src/photo-genre.ts +14 -3
  52. package/src/photographer.ts +2 -1
  53. package/src/picker-analyzer-registry.ts +46 -0
  54. package/src/picker-catalogs.ts +11 -0
  55. package/src/pose.ts +17 -6
  56. package/src/post-process-effects.ts +2 -1
  57. package/src/prompt-builder.ts +3 -3
  58. package/src/prompt-wizard-categories.ts +6 -0
  59. package/src/provider-prompt-doctrine.ts +51 -2
  60. package/src/render-quality.ts +2 -1
  61. package/src/setting.ts +3 -1
  62. package/src/shared-catalog-overlay.ts +83 -0
  63. package/src/style.ts +17 -1
  64. package/src/styling.ts +62 -30
  65. package/src/subject-registry.ts +2 -2
  66. package/src/temporal.ts +2 -1
  67. package/src/transitions.ts +2 -1
  68. package/src/voice-character.ts +6 -5
  69. package/src/voice-delivery.ts +4 -3
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { ANIMAL_SUBCATEGORY_LABELS, ANIMAL_SUBCATEGORY_ORDER, VEHICLE_SUBCATEGORY_LABELS, VEHICLE_SUBCATEGORY_ORDER, WEAPON_SUBCATEGORY_LABELS, WEAPON_SUBCATEGORY_ORDER, FURNITURE_SUBCATEGORY_LABELS, FURNITURE_SUBCATEGORY_ORDER, VIDEO_REF_LIMITS_BY_PROVIDER, SOCIAL_POST_NODE_TYPES, FURNITURE, WEAPONS, VEHICLES, ANIMALS, PASSTHROUGH_TYPES, registerCatalogSidecars, resetCatalogSidecars, pickIds, getAnimalPromptHint, getFurniture, getWeapon, getVehicle, DEFAULT_USAGE_MODE, usageModeDirective, DEFAULT_LOCATION_USAGE_MODE, imageReferenceLimit, knownImageSlugsFromRefs, findImageMentionTokens, knownEntitySlugsFromRefs, findEntityMentionTokens, findCharacterMentionTokens, findLocationMentionTokens, NATIVE_NEGATIVE_PROMPT_MODELS, getMaxNegativePromptChars, getMaxImagePromptChars, MODELS_WITH_REFERENCE_IMAGE_SUPPORT, getAnimalTerm, roleToPhrase, resolveDefaultRole, SEEDANCE_2_REF_LIMITS, NON_EN_LOCALE_IDS, readPromptAffixes, setRegisteredPersonPackFields, PLACEHOLDER_CHARACTER_NAME, REFERENCE_ROLE_PRESETS, imageMentionSlugForRef, entityMentionSlugForRef, defaultRoleForSource, locationReferencePhotoKindLabel, resolveNodeRefs, normalizeRoleSlug } from '@nodaro/shared';
1
+ import { ANIMAL_SUBCATEGORY_LABELS, ANIMAL_SUBCATEGORY_ORDER, VEHICLE_SUBCATEGORY_LABELS, VEHICLE_SUBCATEGORY_ORDER, WEAPON_SUBCATEGORY_LABELS, WEAPON_SUBCATEGORY_ORDER, FURNITURE_SUBCATEGORY_LABELS, FURNITURE_SUBCATEGORY_ORDER, SOCIAL_POST_NODE_TYPES, FURNITURE, WEAPONS, VEHICLES, ANIMALS, getAnimalPromptHint, PASSTHROUGH_TYPES, registerCatalogSidecars, resetCatalogSidecars, getAnimalTerm, getVehicle, getWeapon, getFurniture, pickIds, DEFAULT_USAGE_MODE, usageModeDirective, DEFAULT_LOCATION_USAGE_MODE, imageReferenceLimit, knownImageSlugsFromRefs, findImageMentionTokens, knownEntitySlugsFromRefs, findEntityMentionTokens, findCharacterMentionTokens, findLocationMentionTokens, NATIVE_NEGATIVE_PROMPT_MODELS, getMaxNegativePromptChars, getMaxImagePromptChars, MODELS_WITH_REFERENCE_IMAGE_SUPPORT, roleToPhrase, resolveDefaultRole, SEEDANCE_2_REF_LIMITS, NON_EN_LOCALE_IDS, readPromptAffixes, setRegisteredPersonPackFields, PLACEHOLDER_CHARACTER_NAME, REFERENCE_ROLE_PRESETS, imageMentionSlugForRef, entityMentionSlugForRef, defaultRoleForSource, locationReferencePhotoKindLabel, VIDEO_REF_LIMITS_BY_PROVIDER, resolveNodeRefs, normalizeRoleSlug } from '@nodaro/shared';
2
2
 
3
3
  var __defProp = Object.defineProperty;
4
4
  var __export = (target, all) => {
@@ -105,6 +105,148 @@ function resolveTerm(entry) {
105
105
  if (entry.promptHint === "") return "";
106
106
  return entry.term ?? deriveTerm(entry.label);
107
107
  }
108
+ function withTerm(o) {
109
+ return { ...o, term: resolveTerm(o) };
110
+ }
111
+ var packs = [];
112
+ var version = 0;
113
+ var listeners = /* @__PURE__ */ new Set();
114
+ function bump() {
115
+ version++;
116
+ for (const l of listeners) l();
117
+ }
118
+ function registerCatalogPack(pack) {
119
+ if (packs.some((p) => p.id === pack.id)) throw new Error(`duplicate catalog pack id "${pack.id}"`);
120
+ packs = [...packs, pack];
121
+ registerCatalogSidecars(pack.catalogId, pack.sidecars);
122
+ bump();
123
+ }
124
+ function getRegisteredCatalogPacks() {
125
+ return packs;
126
+ }
127
+ function resetCatalogPacks() {
128
+ packs = [];
129
+ resetCatalogSidecars();
130
+ bump();
131
+ }
132
+ function catalogPacksVersion() {
133
+ return version;
134
+ }
135
+ function hasCatalogPacksFor(catalogId) {
136
+ return packs.some((p) => p.catalogId === catalogId);
137
+ }
138
+ function subscribeCatalogPacks(listener) {
139
+ listeners.add(listener);
140
+ return () => {
141
+ listeners.delete(listener);
142
+ };
143
+ }
144
+ function cloneCatalog(c) {
145
+ return {
146
+ ...c,
147
+ options: c.options ? c.options.map(withTerm) : void 0,
148
+ dimensions: c.dimensions ? c.dimensions.map((d) => ({ ...d, options: d.options.map(withTerm) })) : void 0
149
+ };
150
+ }
151
+ function applyExtend(c, pack) {
152
+ if (c.kind === "single") {
153
+ return { ...c, options: [...c.options ?? [], ...(pack.options ?? []).map(withTerm)] };
154
+ }
155
+ const byField = new Map((c.dimensions ?? []).map((d) => [d.field, { ...d, options: [...d.options] }]));
156
+ for (const dim of pack.dimensions ?? []) {
157
+ const added = dim.options.map(withTerm);
158
+ const existing = byField.get(dim.field);
159
+ if (existing) existing.options = [...existing.options, ...added];
160
+ else byField.set(dim.field, { ...dim, options: added });
161
+ }
162
+ const dims = [...byField.values()];
163
+ return { ...c, dimensions: dims, fields: dims.map((d) => d.field) };
164
+ }
165
+ function applyDeny(c, denyIds) {
166
+ const deny = new Set(denyIds);
167
+ if (c.kind === "single") return { ...c, options: (c.options ?? []).filter((o) => !deny.has(o.id)) };
168
+ return { ...c, dimensions: (c.dimensions ?? []).map((d) => ({ ...d, options: d.options.filter((o) => !deny.has(o.id)) })) };
169
+ }
170
+ function composePickerCatalogs(base, activePacks) {
171
+ const known = new Set(base.map((c) => c.catalogId));
172
+ for (const pack of activePacks) {
173
+ if (!known.has(pack.catalogId)) throw new Error(`catalog pack "${pack.id}" targets unknown catalog id "${pack.catalogId}"`);
174
+ }
175
+ return base.map((c) => {
176
+ let out = cloneCatalog(c);
177
+ for (const pack of activePacks) {
178
+ if (pack.catalogId !== c.catalogId) continue;
179
+ if (pack.mode === "replace" && pack.catalog) out = cloneCatalog(pack.catalog);
180
+ else if (pack.mode === "extend") out = applyExtend(out, pack);
181
+ else if (pack.mode === "deny") out = applyDeny(out, pack.denyIds ?? []);
182
+ }
183
+ return out;
184
+ });
185
+ }
186
+
187
+ // src/catalog-overlay.ts
188
+ var resolver = null;
189
+ function setComposedCatalogResolver(fn) {
190
+ resolver = fn;
191
+ }
192
+ var memo = null;
193
+ function composedOptionIndex(catalogId) {
194
+ const v = catalogPacksVersion();
195
+ if (!memo || memo.v !== v) memo = { v, byCatalog: /* @__PURE__ */ new Map() };
196
+ const hit = memo.byCatalog.get(catalogId);
197
+ if (hit) return hit;
198
+ if (!resolver) {
199
+ throw new Error(
200
+ `[catalog-overlay] catalog "${catalogId}" is curated by a pack but the composed-catalog resolver is not installed \u2014 refusing to resolve against the stock catalog`
201
+ );
202
+ }
203
+ const cat = resolver(catalogId);
204
+ const index = /* @__PURE__ */ new Map();
205
+ if (cat) {
206
+ for (const o of cat.options ?? []) index.set(o.id, o);
207
+ for (const d of cat.dimensions ?? []) for (const o of d.options) if (!index.has(o.id)) index.set(o.id, o);
208
+ }
209
+ memo.byCatalog.set(catalogId, index);
210
+ return index;
211
+ }
212
+ function overlayEntry(catalogId, id, base) {
213
+ if (!hasCatalogPacksFor(catalogId)) return base;
214
+ const opt = composedOptionIndex(catalogId).get(id);
215
+ if (!opt) return void 0;
216
+ if (!base) return opt;
217
+ return {
218
+ ...base,
219
+ label: opt.label,
220
+ promptHint: opt.promptHint,
221
+ term: opt.term,
222
+ ...opt.description !== void 0 ? { description: opt.description } : {},
223
+ ...opt.category !== void 0 ? { category: opt.category } : {}
224
+ };
225
+ }
226
+ function composedHas(catalogId, id) {
227
+ if (!hasCatalogPacksFor(catalogId)) return true;
228
+ return composedOptionIndex(catalogId).has(id);
229
+ }
230
+ function composedOption(catalogId, id) {
231
+ if (!hasCatalogPacksFor(catalogId)) return void 0;
232
+ return composedOptionIndex(catalogId).get(id);
233
+ }
234
+ function curateEntries(catalogId, base) {
235
+ if (!hasCatalogPacksFor(catalogId)) return base;
236
+ const index = composedOptionIndex(catalogId);
237
+ const out = [];
238
+ for (const e of base) {
239
+ const opt = index.get(e.id);
240
+ if (!opt) continue;
241
+ out.push({
242
+ ...e,
243
+ label: opt.label,
244
+ ...opt.description !== void 0 ? { description: opt.description } : {},
245
+ ..."promptHint" in e && opt.promptHint !== void 0 ? { promptHint: opt.promptHint } : {}
246
+ });
247
+ }
248
+ return out;
249
+ }
108
250
 
109
251
  // src/framing.ts
110
252
  var FRAMINGS = [
@@ -629,7 +771,7 @@ var FRAMING_CATEGORY_LABELS = {
629
771
  var framingById = new Map(FRAMINGS.map((f) => [f.id, f]));
630
772
  function getFraming(id) {
631
773
  if (!id) return void 0;
632
- return framingById.get(id);
774
+ return overlayEntry("framing", id, framingById.get(id));
633
775
  }
634
776
  function getFramingLabel(id, fallback) {
635
777
  const f = getFraming(id);
@@ -800,7 +942,7 @@ var LIGHTING_CATEGORY_LABELS = {
800
942
  var lightingById = new Map(LIGHTINGS.map((l) => [l.id, l]));
801
943
  function getLighting(id) {
802
944
  if (!id) return void 0;
803
- return lightingById.get(id);
945
+ return overlayEntry("lighting", id, lightingById.get(id));
804
946
  }
805
947
  function getLightingLabel(id, fallback) {
806
948
  const l = getLighting(id);
@@ -884,7 +1026,7 @@ var LENSES = [
884
1026
  var lensById = new Map(LENSES.map((l) => [l.id, l]));
885
1027
  function getLens(id) {
886
1028
  if (!id) return void 0;
887
- return lensById.get(id);
1029
+ return overlayEntry("lens", id, lensById.get(id));
888
1030
  }
889
1031
  function getLensLabel(id, fallback) {
890
1032
  const l = getLens(id);
@@ -942,7 +1084,7 @@ var CAMERA_FORMATS = [
942
1084
  var formatById = new Map(CAMERA_FORMATS.map((f) => [f.id, f]));
943
1085
  function getCameraFormat(id) {
944
1086
  if (!id) return void 0;
945
- return formatById.get(id);
1087
+ return overlayEntry("camera-format", id, formatById.get(id));
946
1088
  }
947
1089
  function getCameraFormatLabel(id, fallback) {
948
1090
  const f = getCameraFormat(id);
@@ -1018,7 +1160,7 @@ var COLOR_LOOK_CATEGORY_LABELS = {
1018
1160
  var colorLookById = new Map(COLOR_LOOKS.map((c) => [c.id, c]));
1019
1161
  function getColorLook(id) {
1020
1162
  if (!id) return void 0;
1021
- return colorLookById.get(id);
1163
+ return overlayEntry("color-look", id, colorLookById.get(id));
1022
1164
  }
1023
1165
  function getColorLookLabel(id, fallback) {
1024
1166
  const c = getColorLook(id);
@@ -1080,7 +1222,7 @@ var ATMOSPHERES = [
1080
1222
  var atmosphereById = new Map(ATMOSPHERES.map((a) => [a.id, a]));
1081
1223
  function getAtmosphere(id) {
1082
1224
  if (!id) return void 0;
1083
- return atmosphereById.get(id);
1225
+ return overlayEntry("atmosphere", id, atmosphereById.get(id));
1084
1226
  }
1085
1227
  function getAtmosphereLabel(id, fallback) {
1086
1228
  const a = getAtmosphere(id);
@@ -1669,7 +1811,7 @@ var ACTION_FX_IDS = ACTION_FX.map((fx) => fx.id);
1669
1811
  var actionFxById = new Map(ACTION_FX.map((fx) => [fx.id, fx]));
1670
1812
  function getActionFx(id) {
1671
1813
  if (!id) return void 0;
1672
- return actionFxById.get(id);
1814
+ return overlayEntry("action-fx", id, actionFxById.get(id));
1673
1815
  }
1674
1816
  function getActionFxLabel(id, fallback) {
1675
1817
  const fx = getActionFx(id);
@@ -1764,12 +1906,17 @@ var STYLES = [
1764
1906
  { id: "pastel", label: "Pastel", description: "Degas-era soft chalk pastel", promptHint: "rendered as a soft chalk pastel drawing, dry powdery pigment laid in feathered strokes on tinted paper with luminous dusty colors, gently blended edges and the dreamy Degas-era atmosphere of dancers in stage light", term: "soft pastel drawing" },
1765
1907
  { id: "acrylic-paint", label: "Acrylic Paint", description: "Fast-drying opaque acrylic on canvas", promptHint: "rendered as an acrylic painting on canvas, fast-drying opaque pigment with crisp sharp edges, confident quick brush strokes, high-key saturated color and a flatter more graphic finish than traditional oil paint", term: "acrylic painting" },
1766
1908
  { id: "mixed-media", label: "Mixed Media", description: "Collage + paint + ink hybrid", promptHint: "rendered as a mixed-media artwork combining torn paper collage, acrylic paint, ink and graphite on a layered substrate, heterogeneous textures, visible tape and stitching, and an exuberant hand-assembled studio-art quality" },
1767
- { id: "manga", label: "Manga", description: "Inked B&W Japanese comic panel", promptHint: "rendered as inked manga panel art, crisp black ink on white with confident line weight variation, screen-tone dot patterns for shading, dramatic speed lines and the distinctly Japanese black-and-white comic aesthetic \u2014 separate from full-color anime" }
1909
+ { id: "manga", label: "Manga", description: "Inked B&W Japanese comic panel", promptHint: "rendered as inked manga panel art, crisp black ink on white with confident line weight variation, screen-tone dot patterns for shading, dramatic speed lines and the distinctly Japanese black-and-white comic aesthetic \u2014 separate from full-color anime" },
1910
+ { id: "early-color-photo", label: "Early Color Photo", description: "Prokudin-Gorsky / autochrome early-1900s color", promptHint: "rendered as an early-1900s color photograph in the Prokudin-Gorsky / autochrome tradition \u2014 soft three-colour-separation registration, muted dye-toned palette, fine grain and a gentle antique warmth", term: "early autochrome color photograph" }
1768
1911
  ];
1769
1912
  var styleById = new Map(STYLES.map((s) => [s.id, s]));
1770
1913
  function getStyle(id) {
1771
1914
  if (!id) return void 0;
1772
- return styleById.get(id);
1915
+ return overlayEntry("style", id, styleById.get(id));
1916
+ }
1917
+ function isDeniedStyleId(value) {
1918
+ if (!value) return false;
1919
+ return styleById.has(value) && getStyle(value) === void 0;
1773
1920
  }
1774
1921
  function getStyleLabel(id, fallback) {
1775
1922
  const s = getStyle(id);
@@ -1830,6 +1977,7 @@ var SETTINGS = [
1830
1977
  { id: "parking-lot", label: "Parking Lot", category: "urban", description: "Suburban parking lot at dusk", promptHint: "set in an empty suburban parking lot at dusk with sodium-vapor lamps casting orange pools, scattered shopping carts and painted lane lines" },
1831
1978
  { id: "penthouse", label: "Penthouse", category: "urban", description: "Luxury penthouse with skyline view", promptHint: "set in a luxury penthouse interior with panoramic skyline views, marble floors, modernist furniture and low warm ambient light" },
1832
1979
  { id: "gas-station", label: "Gas Station", category: "urban", description: "Lonely highway gas station at night", promptHint: "set at a lonely highway gas station at night with a fluorescent canopy, bug-swarmed sodium lamps and cracked asphalt" },
1980
+ { id: "open-air-market", label: "Open-Air Market", category: "urban", description: "Bustling market of vendor stalls under canopies", term: "open-air market", promptHint: "set in a bustling open-air market \u2014 rows of vendor stalls under thatched and canvas canopies, produce piled high, warm dusty light and crowds moving between the stalls" },
1833
1981
  // -------------------- Nature --------------------
1834
1982
  { id: "forest", label: "Forest Clearing", category: "nature", description: "Sunlit mossy clearing", promptHint: "set in a sunlit forest clearing with moss-covered stones, dappled light through tall trees and a soft carpet of fallen leaves" },
1835
1983
  { id: "beach", label: "Beach", category: "nature", description: "Wide sandy beach with surf", promptHint: "set on a wide sandy beach with gentle breaking surf, footprints in wet sand and a pastel horizon" },
@@ -1858,7 +2006,7 @@ var SETTINGS = [
1858
2006
  var settingById = new Map(SETTINGS.map((s) => [s.id, s]));
1859
2007
  function getSetting(id) {
1860
2008
  if (!id) return void 0;
1861
- return settingById.get(id);
2009
+ return overlayEntry("setting", id, settingById.get(id));
1862
2010
  }
1863
2011
  function getSettingLabel(id, fallback) {
1864
2012
  const s = getSetting(id);
@@ -2120,7 +2268,8 @@ var LOOP_SUBJECTS = [
2120
2268
  }
2121
2269
  ];
2122
2270
  function getLoopSubject(id) {
2123
- return LOOP_SUBJECTS.find((s) => s.id === id);
2271
+ if (!id) return void 0;
2272
+ return overlayEntry("loop-subject", id, LOOP_SUBJECTS.find((s) => s.id === id));
2124
2273
  }
2125
2274
  function getLoopSubjectLabel(id) {
2126
2275
  return getLoopSubject(id)?.label ?? id;
@@ -2131,70 +2280,6 @@ function getLoopSubjectPromptHint(id) {
2131
2280
  function getLoopSubjectTerm(id) {
2132
2281
  return resolveTerm(getLoopSubject(id));
2133
2282
  }
2134
- function withTerm(o) {
2135
- return { ...o, term: resolveTerm(o) };
2136
- }
2137
- var packs = [];
2138
- var version = 0;
2139
- function registerCatalogPack(pack) {
2140
- if (packs.some((p) => p.id === pack.id)) throw new Error(`duplicate catalog pack id "${pack.id}"`);
2141
- packs = [...packs, pack];
2142
- version++;
2143
- registerCatalogSidecars(pack.catalogId, pack.sidecars);
2144
- }
2145
- function getRegisteredCatalogPacks() {
2146
- return packs;
2147
- }
2148
- function resetCatalogPacks() {
2149
- packs = [];
2150
- version++;
2151
- resetCatalogSidecars();
2152
- }
2153
- function catalogPacksVersion() {
2154
- return version;
2155
- }
2156
- function cloneCatalog(c) {
2157
- return {
2158
- ...c,
2159
- options: c.options ? c.options.map(withTerm) : void 0,
2160
- dimensions: c.dimensions ? c.dimensions.map((d) => ({ ...d, options: d.options.map(withTerm) })) : void 0
2161
- };
2162
- }
2163
- function applyExtend(c, pack) {
2164
- if (c.kind === "single") {
2165
- return { ...c, options: [...c.options ?? [], ...(pack.options ?? []).map(withTerm)] };
2166
- }
2167
- const byField = new Map((c.dimensions ?? []).map((d) => [d.field, { ...d, options: [...d.options] }]));
2168
- for (const dim of pack.dimensions ?? []) {
2169
- const added = dim.options.map(withTerm);
2170
- const existing = byField.get(dim.field);
2171
- if (existing) existing.options = [...existing.options, ...added];
2172
- else byField.set(dim.field, { ...dim, options: added });
2173
- }
2174
- const dims = [...byField.values()];
2175
- return { ...c, dimensions: dims, fields: dims.map((d) => d.field) };
2176
- }
2177
- function applyDeny(c, denyIds) {
2178
- const deny = new Set(denyIds);
2179
- if (c.kind === "single") return { ...c, options: (c.options ?? []).filter((o) => !deny.has(o.id)) };
2180
- return { ...c, dimensions: (c.dimensions ?? []).map((d) => ({ ...d, options: d.options.filter((o) => !deny.has(o.id)) })) };
2181
- }
2182
- function composePickerCatalogs(base, activePacks) {
2183
- const known = new Set(base.map((c) => c.catalogId));
2184
- for (const pack of activePacks) {
2185
- if (!known.has(pack.catalogId)) throw new Error(`catalog pack "${pack.id}" targets unknown catalog id "${pack.catalogId}"`);
2186
- }
2187
- return base.map((c) => {
2188
- let out = cloneCatalog(c);
2189
- for (const pack of activePacks) {
2190
- if (pack.catalogId !== c.catalogId) continue;
2191
- if (pack.mode === "replace" && pack.catalog) out = cloneCatalog(pack.catalog);
2192
- else if (pack.mode === "extend") out = applyExtend(out, pack);
2193
- else if (pack.mode === "deny") out = applyDeny(out, pack.denyIds ?? []);
2194
- }
2195
- return out;
2196
- });
2197
- }
2198
2283
  var personPacks = [];
2199
2284
  var version2 = 0;
2200
2285
  function syncPersonPackFields() {
@@ -2217,7 +2302,8 @@ function registerPersonPack(pack) {
2217
2302
  description: e.description,
2218
2303
  category: e.group,
2219
2304
  promptHint: e.promptHint,
2220
- term: resolveTerm(e)
2305
+ term: resolveTerm(e),
2306
+ adultOnly: e.adultOnly
2221
2307
  };
2222
2308
  const existing = byField.get(dim.field);
2223
2309
  if (existing) existing.options.push(opt);
@@ -2239,7 +2325,7 @@ function resetPersonPacks() {
2239
2325
  syncPersonPackFields();
2240
2326
  }
2241
2327
  function personPacksVersion() {
2242
- return version2;
2328
+ return version2 * 1000003 + catalogPacksVersion();
2243
2329
  }
2244
2330
  function dimensionByField() {
2245
2331
  const out = {};
@@ -2259,7 +2345,8 @@ function personEntriesFromDims(dims) {
2259
2345
  description: o.description ?? "",
2260
2346
  group: o.category,
2261
2347
  promptHint: o.promptHint,
2262
- dimension
2348
+ dimension,
2349
+ adultOnly: o.adultOnly
2263
2350
  });
2264
2351
  }
2265
2352
  }
@@ -2296,6 +2383,36 @@ function getRegisteredPersonDimensionLabels() {
2296
2383
  return extra.length === 0 ? PERSON_DIMENSION_LABELS : { ...PERSON_DIMENSION_LABELS, ...Object.fromEntries(extra) };
2297
2384
  }
2298
2385
 
2386
+ // src/age-signal.ts
2387
+ var ADULT_ONLY_FLAG = "adultOnly";
2388
+ var ADULT_AGE_IDS = /* @__PURE__ */ new Set([
2389
+ "age-early-20s",
2390
+ "age-late-20s",
2391
+ "age-20s",
2392
+ "age-30s",
2393
+ "age-40s",
2394
+ "age-50s",
2395
+ "age-60s",
2396
+ "age-elderly"
2397
+ ]);
2398
+ var MINOR_IMPLYING_TYPE_IDS = /* @__PURE__ */ new Set([
2399
+ "alice-wonderland",
2400
+ "dorothy-oz",
2401
+ "peter-pan",
2402
+ "magical-girl",
2403
+ "prince",
2404
+ "princess"
2405
+ ]);
2406
+ function isMinorAge(value) {
2407
+ if (!value) return false;
2408
+ const { age, customAge, type } = value;
2409
+ if (typeof age === "string" && age.length > 0) {
2410
+ if (age === "age-custom") return !(typeof customAge === "number" && Number.isFinite(customAge) && customAge >= 20);
2411
+ return !ADULT_AGE_IDS.has(age);
2412
+ }
2413
+ return typeof type === "string" && MINOR_IMPLYING_TYPE_IDS.has(type);
2414
+ }
2415
+
2299
2416
  // src/person.ts
2300
2417
  var PEOPLE = [
2301
2418
  // -------------------- Type (primary subject descriptor) --------------------
@@ -2332,7 +2449,7 @@ var PEOPLE = [
2332
2449
  { id: "stylish-influencer", label: "Stylish Influencer", group: "Realistic \u2014 Style", dimension: "type", description: "Polished social-media tastemaker", promptHint: "a stylish social-media influencer \u2014 polished personal-brand styling, trend-forward outfit, expressive natural posing for the camera, the curated put-together look of a content creator" },
2333
2450
  { id: "stunning-model", label: "Stunning Model", group: "Realistic \u2014 Style", dimension: "type", description: "Fashion-model aesthetic", promptHint: "a stunning fashion model with editorial poise, refined features, and high-fashion presence" },
2334
2451
  { id: "supermodel", label: "Supermodel", group: "Realistic \u2014 Style", dimension: "type", description: "Iconic, top-tier runway and cover star", promptHint: "a supermodel \u2014 iconic top-tier presence with magnetic statement features, a striking silhouette and the unmistakable runway-and-cover aura that commands attention" },
2335
- { id: "femme-fatale", label: "Femme Fatale", group: "Realistic \u2014 Style", dimension: "type", description: "Alluring, dangerous noir seductress", promptHint: "a femme fatale \u2014 alluring, mysterious, and dangerous with classic noir seduction" },
2452
+ { id: "femme-fatale", label: "Femme Fatale", group: "Realistic \u2014 Style", dimension: "type", description: "Alluring, dangerous noir seductress", promptHint: "a femme fatale \u2014 alluring, mysterious, and dangerous with classic noir seduction", adultOnly: true },
2336
2453
  { id: "tough-guy", label: "Tough Guy", group: "Realistic \u2014 Style", dimension: "type", description: "Hardened, tough man", promptHint: "a tough, hardened man" },
2337
2454
  // Aesthetic archetypes that traditionally read older — hints kept gender +
2338
2455
  // look-focused so the Age dimension stays in control of the literal age.
@@ -2343,7 +2460,7 @@ var PEOPLE = [
2343
2460
  { id: "twink", label: "Twink", group: "Realistic \u2014 Style", dimension: "type", description: "Slim, smooth, soft gay-male type", promptHint: "a twink \u2014 slim, smooth, soft masculine presence with a delicate, gender-fluid vibe" },
2344
2461
  { id: "soft-butch", label: "Soft Butch", group: "Realistic \u2014 Style", dimension: "type", description: "Gentle masculine-of-center woman", promptHint: "a soft-butch woman \u2014 gentle masculine-of-center styling and energy" },
2345
2462
  { id: "tomboy", label: "Tomboy", group: "Realistic \u2014 Style", dimension: "type", description: "Boyish styling and energy on a feminine body", promptHint: "a tomboy \u2014 boyish styling and energy on a feminine body" },
2346
- { id: "thick", label: "Thick", group: "Realistic \u2014 Style", dimension: "type", description: "Voluptuous body-positive figure with curves", promptHint: "a thick, voluptuous body-positive figure with full curves", term: "thick voluptuous figure" },
2463
+ { id: "thick", label: "Thick", group: "Realistic \u2014 Style", dimension: "type", description: "Voluptuous body-positive figure with curves", promptHint: "a thick, voluptuous body-positive figure with full curves", term: "thick voluptuous figure", adultOnly: true },
2347
2464
  { id: "androgynous", label: "Androgynous", group: "Realistic \u2014 Style", dimension: "type", description: "Deliberately blended masculine + feminine cues", promptHint: "an androgynous figure with deliberately blended masculine and feminine cues", term: "androgynous figure" },
2348
2465
  { id: "goth", label: "Goth", group: "Realistic \u2014 Style", dimension: "type", description: "Goth subculture aesthetic \u2014 dark, romantic, dramatic", promptHint: "a goth figure with a dark romantic dramatic subculture aesthetic" },
2349
2466
  { id: "bear", label: "Bear", group: "Realistic \u2014 Style", dimension: "type", description: "Big, bearded, hairy masculine type", promptHint: "a bear \u2014 a big, bearded, hairy man with a warm masculine presence", term: "big bearded hairy man" },
@@ -2351,7 +2468,7 @@ var PEOPLE = [
2351
2468
  { id: "caveman", label: "Caveman", group: "Primitive / Wild", dimension: "type", description: "Stone Age primitive man", promptHint: "a caveman \u2014 Stone Age primitive in animal furs, with shaggy hair, a heavy brow and a sturdy frame" },
2352
2469
  { id: "cavewoman", label: "Cavewoman", group: "Primitive / Wild", dimension: "type", description: "Stone Age primitive woman", promptHint: "a cavewoman \u2014 Stone Age primitive in animal furs, with shaggy hair and a sturdy frame" },
2353
2470
  { id: "apeman", label: "Apeman", group: "Primitive / Wild", dimension: "type", description: "Half-human half-ape hybrid", promptHint: "an apeman \u2014 half-human half-ape hybrid with simian features, broad shoulders and a heavy stooped build" },
2354
- { id: "feral-wildman", label: "Feral Wild-Human", group: "Primitive / Wild", dimension: "type", description: "Overgrown, primal feral human", promptHint: "a feral wild-human \u2014 overgrown matted hair, primal eyes and a near-naked, bestial bearing" },
2471
+ { id: "feral-wildman", label: "Feral Wild-Human", group: "Primitive / Wild", dimension: "type", description: "Overgrown, primal feral human", promptHint: "a feral wild-human \u2014 overgrown matted hair, primal eyes and a near-naked, bestial bearing", adultOnly: true },
2355
2472
  { id: "neanderthal", label: "Neanderthal", group: "Primitive / Wild", dimension: "type", description: "Pre-modern human species", promptHint: "a neanderthal \u2014 heavy brow, broad nose, stocky pre-modern human build with weather-toughened skin" },
2356
2473
  // ----- Fantasy (humanoid) -----
2357
2474
  { id: "elf-man", label: "Elf (Male)", group: "Fantasy", dimension: "type", description: "Masculine pointed-ear humanoid", promptHint: "a male elf \u2014 slender masculine humanoid with delicately pointed ears, fine sharp features and an ethereal poise", term: "male elf" },
@@ -2365,7 +2482,7 @@ var PEOPLE = [
2365
2482
  { id: "faun", label: "Faun / Satyr", group: "Fantasy", dimension: "type", description: "Goat-legged horned humanoid", promptHint: "a faun \u2014 humanoid with goat legs, small curling horns and a woodland presence", term: "faun" },
2366
2483
  { id: "centaur", label: "Centaur", group: "Fantasy", dimension: "type", description: "Human torso on a horse body", promptHint: "a centaur \u2014 human torso fused to the body of a horse" },
2367
2484
  { id: "vampire-man", label: "Vampire (Male)", group: "Fantasy", dimension: "type", description: "Masculine fanged immortal", promptHint: "a male vampire \u2014 pale, fanged immortal man in formal evening wear with gothic elegance and a quiet predatory poise", term: "male vampire" },
2368
- { id: "vampire-woman", label: "Vampire (Female)", group: "Fantasy", dimension: "type", description: "Feminine fanged immortal", promptHint: "a female vampire \u2014 pale, fanged immortal woman in flowing gothic attire with seductive elegance and a quiet predatory poise", term: "female vampire" },
2485
+ { id: "vampire-woman", label: "Vampire (Female)", group: "Fantasy", dimension: "type", description: "Feminine fanged immortal", promptHint: "a female vampire \u2014 pale, fanged immortal woman in flowing gothic attire with seductive elegance and a quiet predatory poise", term: "female vampire", adultOnly: true },
2369
2486
  { id: "vampire", label: "Vampire", group: "Fantasy", dimension: "type", description: "Pale, fanged immortal", promptHint: "a vampire \u2014 pale, fanged immortal with a pristine gothic elegance and a quiet predatory poise" },
2370
2487
  { id: "werewolf", label: "Werewolf", group: "Fantasy", dimension: "type", description: "Partially transformed lycanthrope", promptHint: "a werewolf \u2014 partially transformed humanoid with wolfish snout, fur, claws and a feral musculature" },
2371
2488
  { id: "mermaid", label: "Mermaid", group: "Fantasy", dimension: "type", description: "Female humanoid with a fish tail", promptHint: "a mermaid \u2014 humanoid woman from the waist up with a long iridescent fish tail below" },
@@ -2597,22 +2714,22 @@ var PEOPLE = [
2597
2714
  { id: "mass-full", label: "Full", dimension: "body-mass", description: "Full-figured", promptHint: "full-figured", term: "full-figured" },
2598
2715
  { id: "mass-heavy", label: "Heavy", dimension: "body-mass", description: "Heavy-set, large build", promptHint: "heavy-set build", term: "heavy-set build" },
2599
2716
  // ----- Bust (applicable subjects; ungated like Facial Hair, neutral = no-op) -----
2600
- { id: "bust-small", label: "Small", dimension: "bust", description: "Small bust", promptHint: "small bust", term: "small bust" },
2717
+ { id: "bust-small", label: "Small", dimension: "bust", description: "Small bust", promptHint: "small bust", term: "small bust", adultOnly: true },
2601
2718
  { id: "bust-average", label: "Average", dimension: "bust", description: "Average bust", promptHint: "" },
2602
- { id: "bust-full", label: "Full", dimension: "bust", description: "Full bust", promptHint: "full bust", term: "full bust" },
2603
- { id: "bust-very-full", label: "Very Full", dimension: "bust", description: "Very full bust", promptHint: "very full bust", term: "very full bust" },
2719
+ { id: "bust-full", label: "Full", dimension: "bust", description: "Full bust", promptHint: "full bust", term: "full bust", adultOnly: true },
2720
+ { id: "bust-very-full", label: "Very Full", dimension: "bust", description: "Very full bust", promptHint: "very full bust", term: "very full bust", adultOnly: true },
2604
2721
  // ----- Waist -----
2605
- { id: "waist-defined", label: "Defined", dimension: "waist", description: "Defined, cinched waist", promptHint: "defined waist", term: "defined waist" },
2722
+ { id: "waist-defined", label: "Defined", dimension: "waist", description: "Defined, cinched waist", promptHint: "defined waist", term: "defined waist", adultOnly: true },
2606
2723
  { id: "waist-average", label: "Average", dimension: "waist", description: "Average waist", promptHint: "" },
2607
- { id: "waist-straight", label: "Straight", dimension: "waist", description: "Straight, undefined waist", promptHint: "straight waistline", term: "straight waistline" },
2724
+ { id: "waist-straight", label: "Straight", dimension: "waist", description: "Straight, undefined waist", promptHint: "straight waistline", term: "straight waistline", adultOnly: true },
2608
2725
  // ----- Hips -----
2609
- { id: "hips-narrow", label: "Narrow", dimension: "hips", description: "Narrow hips", promptHint: "narrow hips", term: "narrow hips" },
2726
+ { id: "hips-narrow", label: "Narrow", dimension: "hips", description: "Narrow hips", promptHint: "narrow hips", term: "narrow hips", adultOnly: true },
2610
2727
  { id: "hips-balanced", label: "Balanced", dimension: "hips", description: "Balanced hips", promptHint: "" },
2611
- { id: "hips-wide", label: "Wide", dimension: "hips", description: "Wide hips", promptHint: "wide hips", term: "wide hips" },
2728
+ { id: "hips-wide", label: "Wide", dimension: "hips", description: "Wide hips", promptHint: "wide hips", term: "wide hips", adultOnly: true },
2612
2729
  // ----- Silhouette (overall body shape; optional, no neutral) -----
2613
- { id: "silhouette-hourglass", label: "Hourglass", dimension: "silhouette", description: "Balanced bust + hips, defined waist", promptHint: "hourglass silhouette", term: "hourglass silhouette" },
2730
+ { id: "silhouette-hourglass", label: "Hourglass", dimension: "silhouette", description: "Balanced bust + hips, defined waist", promptHint: "hourglass silhouette", term: "hourglass silhouette", adultOnly: true },
2614
2731
  { id: "silhouette-rectangular", label: "Rectangular", dimension: "silhouette", description: "Straight up-and-down silhouette", promptHint: "rectangular silhouette", term: "rectangular silhouette" },
2615
- { id: "silhouette-pear", label: "Pear", dimension: "silhouette", description: "Hips wider than shoulders", promptHint: "pear-shaped silhouette", term: "pear-shaped silhouette" },
2732
+ { id: "silhouette-pear", label: "Pear", dimension: "silhouette", description: "Hips wider than shoulders", promptHint: "pear-shaped silhouette", term: "pear-shaped silhouette", adultOnly: true },
2616
2733
  { id: "silhouette-inverted", label: "Inverted Triangle", dimension: "silhouette", description: "Broad shoulders, narrow hips", promptHint: "inverted-triangle silhouette", term: "inverted-triangle silhouette" },
2617
2734
  { id: "silhouette-athletic", label: "Athletic", dimension: "silhouette", description: "Lean, toned, V-taper", promptHint: "athletic silhouette", term: "athletic silhouette" },
2618
2735
  // -------------------- Face Shape --------------------
@@ -2699,17 +2816,17 @@ var PEOPLE = [
2699
2816
  { id: "lips-small", label: "Small", dimension: "lip-shape", description: "Petite mouth", promptHint: "a small petite mouth", term: "small petite mouth" },
2700
2817
  // -------------------- Lip State (what the lips are doing / wearing) --------------------
2701
2818
  { id: "lip-state-chapped", label: "Chapped", dimension: "lip-state", description: "Cracked, dry, weather-worn lips", promptHint: "with chapped, cracked, weather-worn dry lips", term: "chapped dry lips" },
2702
- { id: "lip-state-glossy", label: "Glossy", dimension: "lip-state", description: "High-shine, wet-look lips", promptHint: "with high-shine glossy wet-look lips", term: "glossy wet-look lips" },
2819
+ { id: "lip-state-glossy", label: "Glossy", dimension: "lip-state", description: "High-shine, wet-look lips", promptHint: "with high-shine glossy wet-look lips", term: "glossy wet-look lips", adultOnly: true },
2703
2820
  { id: "lip-state-bare", label: "Bare", dimension: "lip-state", description: "Natural, untreated, no makeup", promptHint: "with bare, natural, untreated lips", term: "bare natural lips" },
2704
2821
  { id: "lip-state-bold-red", label: "Bold Red", dimension: "lip-state", description: "Saturated red lipstick statement", promptHint: "with a bold, saturated red lipstick statement", term: "bold red lipstick" },
2705
- { id: "lip-state-bitten", label: "Bitten", dimension: "lip-state", description: "Slight playful lip-bite, lower lip caught", promptHint: "playfully biting the lower lip", term: "biting the lower lip" },
2706
- { id: "lip-state-parted", label: "Parted", dimension: "lip-state", description: "Lips slightly parted, breath of air", promptHint: "with lips slightly parted, taking a soft breath", term: "lips slightly parted" },
2822
+ { id: "lip-state-bitten", label: "Bitten", dimension: "lip-state", description: "Slight playful lip-bite, lower lip caught", promptHint: "playfully biting the lower lip", term: "biting the lower lip", adultOnly: true },
2823
+ { id: "lip-state-parted", label: "Parted", dimension: "lip-state", description: "Lips slightly parted, breath of air", promptHint: "with lips slightly parted, taking a soft breath", term: "lips slightly parted", adultOnly: true },
2707
2824
  { id: "lip-state-pursed", label: "Pursed", dimension: "lip-state", description: "Lips pressed and pushed forward", promptHint: "with lips pursed, pressed and pushed forward", term: "pursed lips" },
2708
2825
  { id: "lip-state-bold-black", label: "Bold Black", dimension: "lip-state", description: "Saturated black lipstick statement (goth / avant-garde)", promptHint: "with a bold, saturated black lipstick statement, goth / avant-garde", term: "bold black lipstick" },
2709
2826
  { id: "lip-state-burgundy", label: "Burgundy", dimension: "lip-state", description: "Deep wine-red lipstick", promptHint: "with deep wine-red burgundy lipstick", term: "burgundy lipstick" },
2710
2827
  { id: "lip-state-plum", label: "Plum", dimension: "lip-state", description: "Dark purple-toned lipstick", promptHint: "with dark purple-toned plum lipstick", term: "plum lipstick" },
2711
2828
  { id: "lip-state-mauve", label: "Mauve", dimension: "lip-state", description: "Muted mauve / dusty-pink lipstick", promptHint: "with muted mauve, dusty-pink lipstick", term: "mauve lipstick" },
2712
- { id: "lip-state-pouting", label: "Pouting", dimension: "lip-state", description: "Full pout", promptHint: "with a full pouting expression", term: "full pouting expression" },
2829
+ { id: "lip-state-pouting", label: "Pouting", dimension: "lip-state", description: "Full pout", promptHint: "with a full pouting expression", term: "full pouting expression", adultOnly: true },
2713
2830
  // -------------------- Hair Color --------------------
2714
2831
  // Blonde family (light → warm → cool)
2715
2832
  { id: "hair-platinum", label: "Platinum", dimension: "hair-color", description: "Very pale, icy platinum", promptHint: "platinum blonde hair, very pale and icy", term: "platinum blonde hair" },
@@ -2816,7 +2933,7 @@ var PEOPLE = [
2816
2933
  { id: "eyes-black", label: "Black", dimension: "eye-color", description: "Truly black iris (deeper than brown)", promptHint: "jet-black eyes with a truly black iris, deeper than brown", term: "jet-black eyes" },
2817
2934
  // -------------------- Eye State (what the eyes are doing / where they look) --------------------
2818
2935
  { id: "eye-state-closed", label: "Closed", dimension: "eye-state", description: "Eyes fully closed, peaceful", promptHint: "with eyes fully closed in a peaceful expression", term: "eyes fully closed" },
2819
- { id: "eye-state-half-lidded", label: "Half-lidded", dimension: "eye-state", description: "Heavy-lidded sleepy gaze", promptHint: "with heavy half-lidded sleepy eyes", term: "half-lidded sleepy eyes" },
2936
+ { id: "eye-state-half-lidded", label: "Half-lidded", dimension: "eye-state", description: "Heavy-lidded sleepy gaze", promptHint: "with heavy half-lidded sleepy eyes", term: "half-lidded sleepy eyes", adultOnly: true },
2820
2937
  { id: "eye-state-wide-eyed", label: "Wide-eyed", dimension: "eye-state", description: "Eyes wide open, alert / surprised", promptHint: "with eyes wide open, alert and surprised" },
2821
2938
  { id: "eye-state-staring-camera", label: "Staring at Camera", dimension: "eye-state", description: "Direct unbroken eye contact with the lens", promptHint: "staring directly at the camera with unbroken eye contact" },
2822
2939
  { id: "eye-state-gazing-away", label: "Gazing Away", dimension: "eye-state", description: "Looking off-camera, contemplative", promptHint: "gazing off-camera with a contemplative expression", term: "gazing off-camera" },
@@ -2836,20 +2953,20 @@ var PEOPLE = [
2836
2953
  { id: "texture-wrinkled", label: "Wrinkled", dimension: "skin-texture", description: "Aged, deeply lined skin", promptHint: "with deep wrinkles and aged skin texture", term: "deeply wrinkled aged skin" },
2837
2954
  { id: "texture-goosebumps", label: "Goosebumps", dimension: "skin-texture", description: "Raised goosebumps on skin", promptHint: "with goosebumps raised on the skin" },
2838
2955
  { id: "texture-dewy", label: "Dewy", dimension: "skin-texture", description: "Glowing, dewy fresh skin", promptHint: "with dewy, glowing skin and a fresh sheen", term: "dewy glowing skin" },
2839
- { id: "texture-glistening", label: "Glistening", dimension: "skin-texture", description: "Sweat or oil sheen", promptHint: "with glistening skin, sweat or oil catching the light", term: "glistening skin" },
2956
+ { id: "texture-glistening", label: "Glistening", dimension: "skin-texture", description: "Sweat or oil sheen", promptHint: "with glistening skin, sweat or oil catching the light", term: "glistening skin", adultOnly: true },
2840
2957
  { id: "texture-weathered", label: "Weathered", dimension: "skin-texture", description: "Sun-aged rough skin", promptHint: "with weathered, sun-worn rough skin", term: "weathered sun-worn skin" },
2841
2958
  { id: "texture-porcelain", label: "Porcelain", dimension: "skin-texture", description: "Flawless near-translucent porcelain", promptHint: "with flawless, near-translucent porcelain skin", term: "porcelain skin" },
2842
2959
  { id: "texture-sun-kissed", label: "Sun-kissed", dimension: "skin-texture", description: "Warm tan with healthy glow", promptHint: "with sun-kissed skin, warmly tanned with a healthy glow", term: "sun-kissed skin" },
2843
2960
  { id: "texture-tanned", label: "Tanned", dimension: "skin-texture", description: "Deeply, evenly tanned skin", promptHint: "with deeply, evenly bronzed tanned skin", term: "deeply tanned skin" },
2844
- { id: "texture-tan-lines", label: "Tan Lines", dimension: "skin-texture", description: "Visible tan lines from swimwear", promptHint: "with visible swimwear tan lines on the skin", term: "visible swimwear tan lines" },
2961
+ { id: "texture-tan-lines", label: "Tan Lines", dimension: "skin-texture", description: "Visible tan lines from swimwear", promptHint: "with visible swimwear tan lines on the skin", term: "visible swimwear tan lines", adultOnly: true },
2845
2962
  { id: "texture-freckled", label: "Freckled", dimension: "skin-texture", description: "Freckles across cheeks and nose", promptHint: "with visible freckles scattered across cheeks and the bridge of the nose", term: "freckled skin" },
2846
2963
  { id: "texture-ruddy", label: "Ruddy", dimension: "skin-texture", description: "Pink-flushed cheeks and nose", promptHint: "with ruddy, warm pink-flushed cheeks and nose", term: "ruddy flushed complexion" },
2847
2964
  { id: "texture-ashen", label: "Ashen / Pale", dimension: "skin-texture", description: "Cool pale, near-bloodless undertone", promptHint: "with ashen, cool pale, near-bloodless skin", term: "ashen pale skin" },
2848
- { id: "texture-oily", label: "Oily / Shiny", dimension: "skin-texture", description: "Slight oil sheen on T-zone", promptHint: "with a slight oily sheen across the T-zone", term: "oily shiny skin" },
2965
+ { id: "texture-oily", label: "Oily / Shiny", dimension: "skin-texture", description: "Slight oil sheen on T-zone", promptHint: "with a slight oily sheen across the T-zone", term: "oily shiny skin", adultOnly: true },
2849
2966
  { id: "texture-matte", label: "Matte", dimension: "skin-texture", description: "Poreless matte finish", promptHint: "with a poreless, matte skin finish", term: "matte skin finish" },
2850
2967
  { id: "texture-blemished", label: "Blemished", dimension: "skin-texture", description: "Visible blemishes, real-skin imperfections", promptHint: "with visible blemishes and natural real-skin imperfections", term: "blemished skin" },
2851
2968
  { id: "texture-baby-soft", label: "Baby-soft", dimension: "skin-texture", description: "Smooth, fine-pored youthful skin", promptHint: "with baby-soft, fine-pored, youthful smooth skin", term: "baby-soft skin" },
2852
- { id: "texture-shower-fresh-wet", label: "Shower-Fresh Wet", dimension: "skin-texture", description: "Just-out-of-shower wet skin with water beads", promptHint: "with just-out-of-the-shower wet skin, water beading on the surface and rolling in slow droplets down the curves of the body", term: "shower-fresh wet skin" },
2969
+ { id: "texture-shower-fresh-wet", label: "Shower-Fresh Wet", dimension: "skin-texture", description: "Just-out-of-shower wet skin with water beads", promptHint: "with just-out-of-the-shower wet skin, water beading on the surface and rolling in slow droplets down the curves of the body", term: "shower-fresh wet skin", adultOnly: true },
2853
2970
  { id: "texture-acne-scarred", label: "Acne-scarred", dimension: "skin-texture", description: "Visible acne scarring (distinct from blemished \u2014 healed scar pattern)", promptHint: "with visible acne scarring, healed pitted-skin texture and uneven surface from past breakouts", term: "acne-scarred skin" },
2854
2971
  // -------------------- Distinctive Features --------------------
2855
2972
  { id: "feature-glasses", label: "Glasses", dimension: "distinctive-features", description: "Wears glasses", promptHint: "wearing glasses", term: "wearing glasses" },
@@ -2870,18 +2987,18 @@ var PEOPLE = [
2870
2987
  { id: "feature-sleeve-tattoo", label: "Sleeve Tattoo", dimension: "distinctive-features", description: "Full one-arm tattoo sleeve", promptHint: "with a full tattoo sleeve covering one arm in intricate ink" },
2871
2988
  { id: "feature-face-tattoo", label: "Face Tattoo", dimension: "distinctive-features", description: "Small face tattoo near eye or cheekbone", promptHint: "with a small face tattoo near the eye or cheekbone" },
2872
2989
  { id: "feature-hand-tattoos", label: "Hand Tattoos", dimension: "distinctive-features", description: "Knuckle / finger tattoos", promptHint: "with hand tattoos across knuckles and fingers" },
2873
- { id: "feature-back-tattoo", label: "Back Tattoo", dimension: "distinctive-features", description: "Large piece across the back", promptHint: "with a large back-piece tattoo across the upper back" },
2874
- { id: "feature-chest-tattoo", label: "Chest Tattoo", dimension: "distinctive-features", description: "Tattoo across chest / d\xE9colletage", promptHint: "with a tattoo across the chest and d\xE9colletage" },
2875
- { id: "feature-leg-tattoo", label: "Leg Tattoo", dimension: "distinctive-features", description: "Visible tattoo on leg / calf / thigh", promptHint: "with a visible tattoo on the leg, running across the calf or thigh" },
2990
+ { id: "feature-back-tattoo", label: "Back Tattoo", dimension: "distinctive-features", description: "Large piece across the back", promptHint: "with a large back-piece tattoo across the upper back", adultOnly: true },
2991
+ { id: "feature-chest-tattoo", label: "Chest Tattoo", dimension: "distinctive-features", description: "Tattoo across chest / d\xE9colletage", promptHint: "with a tattoo across the chest and d\xE9colletage", adultOnly: true },
2992
+ { id: "feature-leg-tattoo", label: "Leg Tattoo", dimension: "distinctive-features", description: "Visible tattoo on leg / calf / thigh", promptHint: "with a visible tattoo on the leg, running across the calf or thigh", adultOnly: true },
2876
2993
  { id: "feature-forearm-tattoo", label: "Forearm Tattoo", dimension: "distinctive-features", description: "Tattoo on inner or outer forearm", promptHint: "with a tattoo on the forearm, visible on the inner or outer side" },
2877
2994
  { id: "feature-visible-piercings", label: "Visible Piercings", dimension: "distinctive-features", description: "Multiple visible piercings (septum, nose, lip, multi-ear)", promptHint: "with multiple visible piercings \u2014 septum, nose, lip, and multi-ear", term: "multiple visible piercings" },
2878
2995
  { id: "feature-ear-piercings", label: "Ear Piercings", dimension: "distinctive-features", description: "Multiple stacked ear piercings (cartilage, helix, conch)", promptHint: "with multiple stacked ear piercings \u2014 cartilage, helix, and conch", term: "multiple stacked ear piercings" },
2879
2996
  { id: "feature-lip-piercing", label: "Lip Piercing", dimension: "distinctive-features", description: "Lip ring / labret stud", promptHint: "with a lip piercing, a small ring or labret stud at the lip" },
2880
2997
  { id: "feature-nostril-piercing", label: "Nostril Piercing", dimension: "distinctive-features", description: "Single nostril stud or ring", promptHint: "with a single nostril piercing, a small stud or ring at the nostril" },
2881
- { id: "feature-bare-shoulders", label: "Bare Shoulders", dimension: "distinctive-features", description: "Bare shoulders exposed", promptHint: "with bare shoulders exposed, the line of the collarbone and shoulder muscles uncovered" },
2882
- { id: "feature-collarbone-visible", label: "Collarbone Visible", dimension: "distinctive-features", description: "Prominent collarbone catching light", promptHint: "with a prominent collarbone clearly defined and catching the light", term: "visible collarbone" },
2883
- { id: "feature-midriff-visible", label: "Midriff Visible", dimension: "distinctive-features", description: "Exposed midriff between top and bottom", promptHint: "wearing a cropped style with the midriff visible", term: "visible midriff" },
2884
- { id: "feature-navel-visible", label: "Navel Visible", dimension: "distinctive-features", description: "Visible navel on bare stomach", promptHint: "with the navel visible", term: "visible navel" },
2998
+ { id: "feature-bare-shoulders", label: "Bare Shoulders", dimension: "distinctive-features", description: "Bare shoulders exposed", promptHint: "with bare shoulders exposed, the line of the collarbone and shoulder muscles uncovered", adultOnly: true },
2999
+ { id: "feature-collarbone-visible", label: "Collarbone Visible", dimension: "distinctive-features", description: "Prominent collarbone catching light", promptHint: "with a prominent collarbone clearly defined and catching the light", term: "visible collarbone", adultOnly: true },
3000
+ { id: "feature-midriff-visible", label: "Midriff Visible", dimension: "distinctive-features", description: "Exposed midriff between top and bottom", promptHint: "wearing a cropped style with the midriff visible", term: "visible midriff", adultOnly: true },
3001
+ { id: "feature-navel-visible", label: "Navel Visible", dimension: "distinctive-features", description: "Visible navel on bare stomach", promptHint: "with the navel visible", term: "visible navel", adultOnly: true },
2885
3002
  { id: "feature-elongated-neck", label: "Elongated Neck", dimension: "distinctive-features", description: "Long swan-like neck", promptHint: "with an elongated, swan-like neck, long and gracefully extended", term: "elongated swan-like neck" },
2886
3003
  { id: "feature-under-eye-circles", label: "Under-Eye Circles", dimension: "distinctive-features", description: "Subtle dark circles under the eyes (distinct from puffy eye-bags)", promptHint: "with subtle dark circles under the eyes", term: "dark under-eye circles" },
2887
3004
  { id: "feature-fangs", label: "Fangs", dimension: "distinctive-features", description: "Visible fangs (vampire / character archetype)", promptHint: "with visible fangs at the canines, character / vampire archetype", term: "visible fangs at the canines" },
@@ -2956,10 +3073,13 @@ var PEOPLE = [
2956
3073
  { id: "mumbai-bollywood", label: "Mumbai Bollywood", group: "Asia", dimension: "regional-aesthetic", description: "Mumbai Indian film-industry aesthetic", promptHint: "a Mumbai Bollywood aesthetic \u2014 Indian film-industry vibe, vibrant statement-glamour", term: "mumbai bollywood aesthetic" },
2957
3074
  { id: "south-india-traditional", label: "South India Traditional", group: "Asia", dimension: "regional-aesthetic", description: "Tamil / Kerala temple-town classical aesthetic", promptHint: "a South Indian traditional aesthetic \u2014 Tamil / Kerala temple-town vibe, classical refinement", term: "south indian traditional aesthetic" },
2958
3075
  { id: "bangkok-street", label: "Bangkok Street", group: "Asia", dimension: "regional-aesthetic", description: "Bangkok Thai night-market neon-urban aesthetic", promptHint: "a Bangkok street aesthetic \u2014 Thai night-market energy, neon-and-warmth urban vibe", term: "bangkok street aesthetic" },
3076
+ // ----- Central Asia -----
3077
+ { id: "samarkand-silk-road", label: "Samarkand Silk Road", group: "Central Asia", dimension: "regional-aesthetic", description: "Uzbek Silk Road bazaar aesthetic (Samarkand / Bukhara)", promptHint: "a Central Asian Silk Road aesthetic \u2014 Samarkand / Bukhara bazaar vibe, ikat-and-suzani textiles, sun-baked adobe and blue-tiled madrasa mood", term: "samarkand silk road aesthetic" },
3078
+ { id: "tashkent-modern", label: "Tashkent Modern", group: "Central Asia", dimension: "regional-aesthetic", description: "Contemporary Uzbek metropolitan Central Asian aesthetic", promptHint: "a modern Tashkent aesthetic \u2014 contemporary Uzbek metropolitan vibe, Soviet-modern-meets-Silk-Road blend, warm steppe-city confidence", term: "tashkent modern aesthetic" },
2959
3079
  // ----- Latin America -----
2960
3080
  { id: "carioca-rio", label: "Carioca (Rio)", group: "Latin America", dimension: "regional-aesthetic", description: "Rio de Janeiro beach-and-favela-music Brazilian aesthetic", promptHint: "a Carioca aesthetic \u2014 Rio de Janeiro beach-and-favela-music vibe, sun-warmed Brazilian energy", term: "carioca aesthetic" },
2961
3081
  { id: "paulista", label: "Paulista (S\xE3o Paulo)", group: "Latin America", dimension: "regional-aesthetic", description: "S\xE3o Paulo metropolitan Brazilian creative-class aesthetic", promptHint: "a Paulista aesthetic \u2014 S\xE3o Paulo metropolitan vibe, urban-Brazilian creative-class polish", term: "paulista aesthetic" },
2962
- { id: "buenos-aires-tango", label: "Buenos Aires Tango", group: "Latin America", dimension: "regional-aesthetic", description: "Buenos Aires Recoleta / San Telmo tango aesthetic (feminine)", promptHint: "a Buenos Aires tango aesthetic \u2014 Recoleta / San Telmo vibe, sultry porte\xF1a confidence", term: "buenos aires tango aesthetic" },
3082
+ { id: "buenos-aires-tango", label: "Buenos Aires Tango", group: "Latin America", dimension: "regional-aesthetic", description: "Buenos Aires Recoleta / San Telmo tango aesthetic (feminine)", promptHint: "a Buenos Aires tango aesthetic \u2014 Recoleta / San Telmo vibe, sultry porte\xF1a confidence", term: "buenos aires tango aesthetic", adultOnly: true },
2963
3083
  { id: "porte\xF1o", label: "Porte\xF1o", group: "Latin America", dimension: "regional-aesthetic", description: "Buenos Aires refined milonguero aesthetic (masculine)", promptHint: "a Porte\xF1o aesthetic \u2014 Buenos Aires vibe, refined milonguero presence", term: "porte\xF1o aesthetic" },
2964
3084
  { id: "mexico-city-chic", label: "Mexico City Chic", group: "Latin America", dimension: "regional-aesthetic", description: "CDMX Roma / Polanco modern-Mexican aesthetic", promptHint: "a Mexico City chic aesthetic \u2014 CDMX Roma / Polanco vibe, modern-Mexican polish", term: "mexico city chic aesthetic" },
2965
3085
  { id: "chilango-traditional", label: "Chilango Traditional", group: "Latin America", dimension: "regional-aesthetic", description: "Mexico City warm folk-and-feast aesthetic", promptHint: "a Chilango traditional aesthetic \u2014 Mexico City vibe, warm folk-and-feast mood", term: "chilango traditional aesthetic" },
@@ -3204,7 +3324,7 @@ function emitIndependentFragments(value, fragmentFor) {
3204
3324
  const navel = ids.includes("feature-navel-visible");
3205
3325
  for (const id of ids) {
3206
3326
  if (midriff && navel && (id === "feature-midriff-visible" || id === "feature-navel-visible")) {
3207
- if (id === "feature-midriff-visible") out.push("wearing a cropped style, midriff and navel visible");
3327
+ if (id === "feature-midriff-visible" && fragmentFor(id) !== "") out.push("wearing a cropped style, midriff and navel visible");
3208
3328
  continue;
3209
3329
  }
3210
3330
  const fragment = fragmentFor(id);
@@ -3216,6 +3336,8 @@ function collectPersonFragments(data, fragmentFor) {
3216
3336
  const hints = [];
3217
3337
  const pre = typeof data.preText === "string" ? data.preText.trim() : "";
3218
3338
  if (pre) hints.push(pre);
3339
+ const floored = isMinorAge(data);
3340
+ const emit = floored ? (id) => typeof id === "string" && getPerson(id)?.adultOnly === true ? "" : fragmentFor(id) : fragmentFor;
3219
3341
  const fieldByDimension = getRegisteredPersonFieldByDimension();
3220
3342
  for (const dimension of getRegisteredPersonDimensionOrder()) {
3221
3343
  const field = fieldByDimension[dimension];
@@ -3224,34 +3346,34 @@ function collectPersonFragments(data, fragmentFor) {
3224
3346
  if (dimension === "age") {
3225
3347
  const ageId = typeof raw === "string" ? raw : void 0;
3226
3348
  const customAge = typeof data.customAge === "number" ? data.customAge : void 0;
3227
- const h = buildAgeFragment(ageId, customAge, fragmentFor);
3349
+ const h = buildAgeFragment(ageId, customAge, emit);
3228
3350
  if (h) hints.push(h);
3229
3351
  continue;
3230
3352
  }
3231
3353
  if (dimension === "ethnicity") {
3232
- const h = buildEthnicityFragment(raw, fragmentFor);
3354
+ const h = buildEthnicityFragment(raw, emit);
3233
3355
  if (h) hints.push(h);
3234
3356
  continue;
3235
3357
  }
3236
3358
  if (dimension === "hair-color") {
3237
- const h = buildHairColorFragment(raw, fragmentFor);
3359
+ const h = buildHairColorFragment(raw, emit);
3238
3360
  if (h) hints.push(h);
3239
3361
  continue;
3240
3362
  }
3241
3363
  if (dimension === "eye-color") {
3242
- for (const h of buildEyeColorFragments(raw, fragmentFor)) hints.push(h);
3364
+ for (const h of buildEyeColorFragments(raw, emit)) hints.push(h);
3243
3365
  continue;
3244
3366
  }
3245
3367
  if (dimension === "distinctive-features" || dimension === "lip-state" || dimension === "eye-state" || dimension === "skin-texture" || dimension === "regional-aesthetic") {
3246
- for (const h of emitIndependentFragments(raw, fragmentFor)) hints.push(h);
3368
+ for (const h of emitIndependentFragments(raw, emit)) hints.push(h);
3247
3369
  continue;
3248
3370
  }
3249
3371
  if (typeof raw !== "string" || raw.length === 0) continue;
3250
- const hint = fragmentFor(raw);
3372
+ const hint = emit(raw);
3251
3373
  if (hint) hints.push(hint);
3252
3374
  }
3253
3375
  if (typeof data.lips === "string" && data.lips.length > 0 && !data.lipFullness && !data.lipShape) {
3254
- const legacyLips = fragmentFor(data.lips);
3376
+ const legacyLips = emit(data.lips);
3255
3377
  if (legacyLips) hints.push(legacyLips);
3256
3378
  }
3257
3379
  const post = typeof data.postText === "string" ? data.postText.trim() : "";
@@ -3333,17 +3455,17 @@ var MOODS = [
3333
3455
  { id: "determined", label: "Determined", category: "intense", description: "Resolute, focused will", promptHint: "with a determined, resolute expression and iron focus", term: "determined, resolute expression" },
3334
3456
  { id: "passionate", label: "Passionate", category: "intense", description: "Burning passion", promptHint: "with a passionate, burning expression full of conviction", term: "passionate expression" },
3335
3457
  { id: "brooding", label: "Brooding", category: "intense", description: "Dark, brooding melancholy", promptHint: "with a brooding, dark expression and moody introspection", term: "brooding expression" },
3336
- { id: "seductive", label: "Seductive", category: "intense", description: "Alluring, seductive", promptHint: "with a seductive, alluring expression and lidded gaze", term: "seductive expression" },
3458
+ { id: "seductive", label: "Seductive", category: "intense", description: "Alluring, seductive", promptHint: "with a seductive, alluring expression and lidded gaze", term: "seductive expression", adultOnly: true },
3337
3459
  { id: "defiant", label: "Defiant", category: "intense", description: "Defiant, unyielding", promptHint: "with a defiant, unyielding expression and challenging stance", term: "defiant expression" },
3338
- { id: "sultry", label: "Sultry", category: "intense", description: "Smoldering, heavy-lidded", promptHint: "with a sultry, smoldering expression, heavy-lidded gaze and lips softly parted", term: "sultry expression" },
3339
- { id: "smoldering", label: "Smoldering", category: "intense", description: "Coiled, slow-burning intensity", promptHint: "with a smoldering, coiled expression, slow-burning intensity behind half-closed eyes and a still, predatory calm", term: "smoldering intensity" },
3460
+ { id: "sultry", label: "Sultry", category: "intense", description: "Smoldering, heavy-lidded", promptHint: "with a sultry, smoldering expression, heavy-lidded gaze and lips softly parted", term: "sultry expression", adultOnly: true },
3461
+ { id: "smoldering", label: "Smoldering", category: "intense", description: "Coiled, slow-burning intensity", promptHint: "with a smoldering, coiled expression, slow-burning intensity behind half-closed eyes and a still, predatory calm", term: "smoldering intensity", adultOnly: true },
3340
3462
  { id: "sinister", label: "Sinister", category: "intense", description: "Dark, malicious, threatening", promptHint: "with a sinister expression, a slow crooked smile and eyes glinting with dark malicious intent", term: "sinister expression" },
3341
3463
  { id: "wiccan-mystical", label: "Wiccan / Mystical", category: "intense", description: "Quietly otherworldly, occult", promptHint: "with a quietly mystical, otherworldly expression, eyes distant and knowing as if reading something the camera cannot see", term: "occult, otherworldly mystical expression" },
3342
3464
  { id: "lazy-shy", label: "Lazy Shy", category: "neutral", description: "Drowsy, soft, half-shy", promptHint: "with a soft, drowsy half-shy expression, eyes lowered and barely-there smile, languid and unbothered", term: "drowsy, half-shy expression" },
3343
3465
  { id: "awe", label: "Awe", category: "intense", description: "Wonder, reverent", promptHint: "with an awestruck expression of wonder, mouth slightly agape and wide-eyed", term: "awestruck wonder" },
3344
3466
  { id: "shocked", label: "Shocked", category: "intense", description: "Surprised, mouth open", promptHint: "with a shocked, surprised expression, eyes wide, brows raised and mouth open", term: "shocked expression" },
3345
3467
  // -------------------- Micro-emotions --------------------
3346
- { id: "flirty", label: "Flirty", category: "positive", description: "Playful flirtation, lingering smile, sustained eye contact", promptHint: "with a flirty expression, a lingering playful smile and sustained, knowing eye contact with the camera", term: "flirty expression" },
3468
+ { id: "flirty", label: "Flirty", category: "positive", description: "Playful flirtation, lingering smile, sustained eye contact", promptHint: "with a flirty expression, a lingering playful smile and sustained, knowing eye contact with the camera", term: "flirty expression", adultOnly: true },
3347
3469
  { id: "suspicious", label: "Suspicious", category: "negative", description: "Wary distrust, narrowed eyes, side-eye", promptHint: "with a suspicious, wary expression, eyes narrowed in distrust and a sidelong side-eye glance", term: "suspicious, wary expression" },
3348
3470
  { id: "resigned", label: "Resigned", category: "neutral", description: "Quiet acceptance of an unpleasant situation, sigh", promptHint: "with a resigned expression of quiet acceptance, shoulders softly dropped mid-sigh and a tired half-closed gaze", term: "resigned expression of quiet acceptance" },
3349
3471
  { id: "conflicted", label: "Conflicted", category: "neutral", description: "Visible internal struggle, brow furrowed, gaze unfocused", promptHint: "with a conflicted expression of visible internal struggle, brow furrowed and gaze unfocused, caught between two impulses", term: "conflicted, torn expression" }
@@ -3351,7 +3473,7 @@ var MOODS = [
3351
3473
  var moodById = new Map(MOODS.map((m) => [m.id, m]));
3352
3474
  function getMood(id) {
3353
3475
  if (!id) return void 0;
3354
- return moodById.get(id);
3476
+ return overlayEntry("mood", id, moodById.get(id));
3355
3477
  }
3356
3478
  function getMoodLabel(id, fallback) {
3357
3479
  const m = getMood(id);
@@ -3443,8 +3565,8 @@ var POSES = [
3443
3565
  { id: "cross-legged", label: "Cross-legged", category: "seated", description: "Seated cross-legged on floor", promptHint: "sitting cross-legged on the ground", term: "sitting cross-legged" },
3444
3566
  { id: "kneeling", label: "Kneeling", category: "seated", description: "Kneeling on the ground", promptHint: "kneeling on one or both knees" },
3445
3567
  { id: "crouching", label: "Crouching", category: "seated", description: "Crouched low", promptHint: "crouched low with knees bent" },
3446
- { id: "lounging", label: "Lounging", category: "seated", description: "Reclined, relaxed sitting", promptHint: "lounging in a relaxed, reclined position" },
3447
- { id: "sitting-edge-of-bed", label: "Sitting on Edge of Bed", category: "seated", description: "Perched on the edge of a bed", promptHint: "perched on the edge of a bed, hands resting on the mattress", term: "sitting on the edge of a bed" },
3568
+ { id: "lounging", label: "Lounging", category: "seated", description: "Reclined, relaxed sitting", promptHint: "lounging in a relaxed, reclined position", adultOnly: true },
3569
+ { id: "sitting-edge-of-bed", label: "Sitting on Edge of Bed", category: "seated", description: "Perched on the edge of a bed", promptHint: "perched on the edge of a bed, hands resting on the mattress", term: "sitting on the edge of a bed", adultOnly: true },
3448
3570
  { id: "chair-arm-drape", label: "Legs Draped Over Chair", category: "seated", description: "Legs draped over chair arm", promptHint: "sitting sideways in a chair with legs draped casually over one arm", term: "legs draped over the chair arm" },
3449
3571
  { id: "elbow-propped", label: "Cheek on Propped Elbow", category: "seated", description: "Cheek resting on a propped elbow", promptHint: "seated with cheek resting against a propped-up elbow, contemplative", term: "cheek resting on a propped elbow" },
3450
3572
  { id: "lying-on-stomach-reading", label: "Lying Prone Reading", category: "seated", description: "Lying prone, propped on elbows reading", promptHint: "lying on the stomach, propped up on elbows while reading", term: "lying prone propped on elbows, reading" },
@@ -3467,13 +3589,13 @@ var POSES = [
3467
3589
  { id: "throwing", label: "Throwing", category: "action", description: "Mid-throw motion", promptHint: "caught mid-throw, body coiled and releasing", term: "caught mid-throw" },
3468
3590
  { id: "leaping", label: "Leaping", category: "action", description: "Leaping forward dynamically", promptHint: "leaping forward dynamically with body extended" },
3469
3591
  { id: "dramatic-action", label: "Dramatic Action", category: "action", description: "Exaggerated action pose", promptHint: "in a dramatic, exaggerated action pose full of motion", term: "dramatic exaggerated action pose" },
3470
- { id: "biting-lip", label: "Biting Lip", category: "action", description: "Slight playful lip-bite", promptHint: "biting the lower lip with a subtle playful expression", term: "biting the lower lip" },
3592
+ { id: "biting-lip", label: "Biting Lip", category: "action", description: "Slight playful lip-bite", promptHint: "biting the lower lip with a subtle playful expression", term: "biting the lower lip", adultOnly: true },
3471
3593
  { id: "mid-laugh", label: "Mid-Laugh", category: "action", description: "Caught mid-laugh, head back", promptHint: "caught mid-laugh with head tipped back, eyes crinkled" },
3472
3594
  { id: "pointing-at-camera", label: "Pointing at Camera", category: "action", description: "Pointing directly at camera", promptHint: "pointing one finger directly at the camera, arm extended", term: "pointing directly at the camera" },
3473
3595
  { id: "tongue-out", label: "Sticking Tongue Out", category: "action", description: "Playful tongue-out expression", promptHint: "sticking the tongue out playfully" },
3474
3596
  { id: "thinking", label: "Thinking", category: "action", description: "Hand on chin, contemplative", promptHint: "in a thinking pose with hand on chin, gaze contemplative", term: "thinking pose, hand on chin" },
3475
3597
  // -------------------- Resting --------------------
3476
- { id: "lying-down", label: "Lying Down", category: "resting", description: "Lying flat", promptHint: "lying down flat, relaxed" },
3598
+ { id: "lying-down", label: "Lying Down", category: "resting", description: "Lying flat", promptHint: "lying down flat, relaxed", adultOnly: true },
3477
3599
  { id: "sleeping", label: "Sleeping", category: "resting", description: "Eyes closed, sleeping", promptHint: "sleeping peacefully with eyes closed" },
3478
3600
  { id: "hugging", label: "Hugging", category: "resting", description: "Embracing another", promptHint: "hugging or embracing another person" },
3479
3601
  { id: "looking-away", label: "Looking Away", category: "resting", description: "Head turned, looking away", promptHint: "head turned, looking off away from the camera" },
@@ -3495,7 +3617,7 @@ var POSES = [
3495
3617
  { id: "leaning-back", label: "Leaning Back", category: "body-lean", description: "Torso leaning back slightly", promptHint: "with the torso leaning back at a slight backward angle", term: "torso leaning slightly back" },
3496
3618
  { id: "leaning-forward", label: "Leaning Forward", category: "body-lean", description: "Torso leaning toward camera", promptHint: "with the torso leaning forward toward the camera", term: "torso leaning forward" },
3497
3619
  { id: "body-lean-contrapposto", label: "Contrapposto", category: "body-lean", description: "Weight on one leg, hip pushed out", promptHint: "with the weight on one leg and one hip pushed out, classical contrapposto", term: "contrapposto, hip pushed out" },
3498
- { id: "arched-back", label: "Arched Back", category: "body-lean", description: "Back gently arched, chest forward", promptHint: "with the back gently arched, chest forward" },
3620
+ { id: "arched-back", label: "Arched Back", category: "body-lean", description: "Back gently arched, chest forward", promptHint: "with the back gently arched, chest forward", adultOnly: true },
3499
3621
  { id: "shoulder-rolled-forward", label: "Shoulder Rolled Forward", category: "body-lean", description: "One shoulder rolled forward", promptHint: "with one shoulder rolled forward, asymmetric stance", term: "one shoulder rolled forward" },
3500
3622
  // -------------------- Head Tilt --------------------
3501
3623
  { id: "tilted-up", label: "Tilted Up", category: "head-tilt", description: "Head tipped slightly upward", promptHint: "with the head tipped slightly upward", term: "head tipped slightly up" },
@@ -3521,7 +3643,7 @@ var POSES = [
3521
3643
  var poseById = new Map(POSES.map((p) => [p.id, p]));
3522
3644
  function getPose(id) {
3523
3645
  if (!id) return void 0;
3524
- return poseById.get(id);
3646
+ return overlayEntry("pose", id, poseById.get(id));
3525
3647
  }
3526
3648
  function getPoseLabel(id, fallback) {
3527
3649
  const p = getPose(id);
@@ -3720,14 +3842,14 @@ var STYLINGS = [
3720
3842
  { id: "jewelry-pearl", label: "Pearl", dimension: "jewelry", description: "Pearl necklace / earrings", promptHint: "wearing a pearl necklace and pearl earrings", term: "pearl jewelry" },
3721
3843
  { id: "jewelry-chunky", label: "Chunky", dimension: "jewelry", description: "Chunky chains and rings", promptHint: "wearing chunky chains and oversized rings", term: "chunky chains and rings" },
3722
3844
  { id: "jewelry-smart-watch", label: "Smart Watch", dimension: "jewelry", description: "Modern smart watch on wrist", promptHint: "wearing a modern smart watch on the wrist with a sleek digital face and silicone band" },
3723
- { id: "jewelry-choker-leather", label: "Leather Choker", dimension: "jewelry", description: "Black leather choker close to throat", promptHint: "wearing a black leather choker fitted close around the throat" },
3724
- { id: "jewelry-choker-velvet", label: "Velvet Choker", dimension: "jewelry", description: "Velvet ribbon choker, dressy and vintage", promptHint: "wearing a velvet ribbon choker around the throat, a dressy vintage touch with a small pendant at the front" },
3725
- { id: "jewelry-choker-chain", label: "Chain Choker", dimension: "jewelry", description: "Thin metal-chain choker", promptHint: "wearing a thin metal-chain choker tight around the throat" },
3845
+ { id: "jewelry-choker-leather", label: "Leather Choker", dimension: "jewelry", description: "Black leather choker close to throat", promptHint: "wearing a black leather choker fitted close around the throat", adultOnly: true },
3846
+ { id: "jewelry-choker-velvet", label: "Velvet Choker", dimension: "jewelry", description: "Velvet ribbon choker, dressy and vintage", promptHint: "wearing a velvet ribbon choker around the throat, a dressy vintage touch with a small pendant at the front", adultOnly: true },
3847
+ { id: "jewelry-choker-chain", label: "Chain Choker", dimension: "jewelry", description: "Thin metal-chain choker", promptHint: "wearing a thin metal-chain choker tight around the throat", adultOnly: true },
3726
3848
  { id: "jewelry-stud-earrings", label: "Stud Earrings", dimension: "jewelry", description: "Small simple studs", promptHint: "wearing small simple stud earrings" },
3727
3849
  { id: "jewelry-hoop-earrings", label: "Hoop Earrings", dimension: "jewelry", description: "Gold or silver hoops", promptHint: "wearing gold or silver hoop earrings" },
3728
3850
  { id: "jewelry-ear-cuff", label: "Ear Cuff", dimension: "jewelry", description: "Sculptural cuff climbing the cartilage", promptHint: "wearing a sculptural ear cuff climbing the cartilage of one ear" },
3729
3851
  { id: "jewelry-septum-ring", label: "Septum Ring", dimension: "jewelry", description: "Septum nose ring", promptHint: "wearing a septum nose ring through the center of the nose" },
3730
- { id: "jewelry-body-chain", label: "Body Chain", dimension: "jewelry", description: "Thin metallic chain across torso", promptHint: "wearing a thin metallic body chain draped across the torso" },
3852
+ { id: "jewelry-body-chain", label: "Body Chain", dimension: "jewelry", description: "Thin metallic chain across torso", promptHint: "wearing a thin metallic body chain draped across the torso", adultOnly: true },
3731
3853
  // -------------------- Nails --------------------
3732
3854
  { id: "nails-polished", label: "Polished", dimension: "nails", description: "Clear or nude polished nails", promptHint: "with polished nude nails", term: "polished nude nails" },
3733
3855
  { id: "nails-red", label: "Red", dimension: "nails", description: "Bright red nail polish", promptHint: "with bright red nail polish", term: "red nail polish" },
@@ -3760,9 +3882,9 @@ var STYLINGS = [
3760
3882
  { id: "fabric-chiffon", label: "Chiffon", dimension: "fabric", description: "Sheer flowing chiffon", promptHint: "wearing sheer chiffon with a lightweight floating drape and soft translucent layers" },
3761
3883
  { id: "fabric-fur", label: "Fur", dimension: "fabric", description: "Thick plush fur", promptHint: "wearing thick plush fur with long dense strands and natural variation", term: "fur garment" },
3762
3884
  { id: "fabric-sequins", label: "Sequins", dimension: "fabric", description: "Sparkling sequin fabric", promptHint: "wearing sparkling sequined fabric catching light with countless tiny reflective facets" },
3763
- { id: "fabric-latex", label: "Latex", dimension: "fabric", description: "Glossy latex", promptHint: "wearing glossy latex with a high-shine liquid look clinging to the body" },
3885
+ { id: "fabric-latex", label: "Latex", dimension: "fabric", description: "Glossy latex", promptHint: "wearing glossy latex with a high-shine liquid look clinging to the body", adultOnly: true },
3764
3886
  // -------------------- Outfit (single-pick complete look — overrides individual pieces) --------------------
3765
- { id: "outfit-school-uniform", label: "School Uniform", dimension: "outfit", description: "Classic school uniform set", promptHint: "dressed in a classic school uniform \u2014 collared blouse, pleated skirt and knee-high socks" },
3887
+ { id: "outfit-school-uniform", label: "School Uniform", dimension: "outfit", description: "Classic school uniform set", promptHint: "dressed in a classic school uniform \u2014 collared blouse, pleated skirt and knee-high socks", adultOnly: true },
3766
3888
  { id: "outfit-business-suit", label: "Business Suit", dimension: "outfit", description: "Tailored two-piece business suit", promptHint: "dressed in a sharp tailored two-piece business suit with a crisp shirt underneath" },
3767
3889
  { id: "outfit-tuxedo", label: "Tuxedo", dimension: "outfit", description: "Black-tie tuxedo", promptHint: "dressed in a black tuxedo with satin lapels and a bow tie" },
3768
3890
  { id: "outfit-evening-gown", label: "Evening Gown", dimension: "outfit", description: "Floor-length formal gown", promptHint: "dressed in a floor-length evening gown with a fitted bodice and a flowing skirt" },
@@ -3776,9 +3898,9 @@ var STYLINGS = [
3776
3898
  { id: "outfit-athleisure", label: "Athleisure Set", dimension: "outfit", description: "Matching athleisure top and bottom", promptHint: "dressed in a matching athleisure set \u2014 a fitted top and leggings in the same tone" },
3777
3899
  { id: "outfit-tracksuit", label: "Tracksuit", dimension: "outfit", description: "Two-piece zip-front tracksuit", promptHint: "dressed in a two-piece tracksuit \u2014 a zip-front jacket and matching joggers" },
3778
3900
  { id: "outfit-soccer-jersey", label: "Soccer Jersey", dimension: "outfit", description: "National-team football jersey with crest", promptHint: "wearing a national-team soccer jersey \u2014 team colors and trim with the team crest on the chest" },
3779
- { id: "outfit-bikini", label: "Bikini", dimension: "outfit", description: "Two-piece swimsuit", promptHint: "in a two-piece bikini" },
3780
- { id: "outfit-one-piece-swim", label: "One-Piece Swimsuit", dimension: "outfit", description: "Fitted one-piece swimsuit", promptHint: "in a fitted one-piece swimsuit" },
3781
- { id: "outfit-lingerie", label: "Lingerie Set", dimension: "outfit", description: "Matching lingerie set", promptHint: "in a matching lingerie set" },
3901
+ { id: "outfit-bikini", label: "Bikini", dimension: "outfit", description: "Two-piece swimsuit", promptHint: "in a two-piece bikini", adultOnly: true },
3902
+ { id: "outfit-one-piece-swim", label: "One-Piece Swimsuit", dimension: "outfit", description: "Fitted one-piece swimsuit", promptHint: "in a fitted one-piece swimsuit", adultOnly: true },
3903
+ { id: "outfit-lingerie", label: "Lingerie Set", dimension: "outfit", description: "Matching lingerie set", promptHint: "in a matching lingerie set", adultOnly: true },
3782
3904
  { id: "outfit-pajamas", label: "Pajamas", dimension: "outfit", description: "Soft cotton pajama set", promptHint: "in a soft cotton pajama set \u2014 a button-up top and matching pants" },
3783
3905
  { id: "outfit-bathrobe", label: "Bathrobe", dimension: "outfit", description: "Plush terrycloth robe", promptHint: "wrapped in a plush terrycloth bathrobe tied at the waist" },
3784
3906
  { id: "outfit-kimono", label: "Kimono", dimension: "outfit", description: "Traditional Japanese kimono", promptHint: "dressed in a traditional Japanese kimono with an obi sash" },
@@ -3796,6 +3918,9 @@ var STYLINGS = [
3796
3918
  { id: "outfit-fairy", label: "Fairy", dimension: "outfit", description: "Fantasy fairy: gauzy wings, flower crown, ethereal dress", promptHint: "wearing a fantasy fairy costume \u2014 gauzy translucent wings, a flower crown, and an ethereal flowing dress", term: "fairy costume with wings" },
3797
3919
  { id: "outfit-mermaid", label: "Mermaid", dimension: "outfit", description: "Fantasy mermaid: scaled tail/skirt, shell top, flowing hair", promptHint: "wearing a fantasy mermaid costume \u2014 a scaled tail or fitted scaled skirt, a shell top, and long flowing hair", term: "mermaid costume" },
3798
3920
  { id: "outfit-pharaoh", label: "Pharaoh Regalia", dimension: "outfit", description: "Ancient Egyptian royalty: usekh collar, pectoral, pleated kilt", promptHint: "wearing ancient Egyptian pharaoh regalia \u2014 a broad beaded usekh collar, a jeweled falcon pectoral, and a pleated linen shendyt kilt with golden arm cuffs" },
3921
+ { id: "outfit-workwear-overalls", label: "Workwear Overalls", dimension: "outfit", description: "Denim bib overalls over a plaid flannel shirt", promptHint: "dressed in a farmer's workwear outfit \u2014 denim bib overalls over a checked plaid flannel shirt, sturdy and worn-in", term: "denim overalls and plaid shirt" },
3922
+ { id: "outfit-chapan", label: "Chapan Robe", dimension: "outfit", description: "Central Asian long quilted ikat robe", promptHint: "dressed in a traditional Central Asian chapan \u2014 a long quilted robe with an ikat-striped weave, tied at the waist with a sash" },
3923
+ { id: "outfit-caftan", label: "Caftan", dimension: "outfit", description: "Long flowing Middle-Eastern / North-African robe", promptHint: "dressed in a long flowing caftan robe, a full-length garment worn across the Middle East and North Africa" },
3799
3924
  // -------------------- Top (upper-body garment) --------------------
3800
3925
  { id: "top-tshirt", label: "T-Shirt", dimension: "top", description: "Plain crewneck t-shirt", promptHint: "wearing a fitted plain crewneck t-shirt with short sleeves" },
3801
3926
  { id: "top-tank", label: "Tank Top", dimension: "top", description: "Scoop-neck tank top", promptHint: "wearing a fitted scoop-neck tank top with thin shoulder straps" },
@@ -3808,14 +3933,14 @@ var STYLINGS = [
3808
3933
  { id: "top-polo", label: "Polo Shirt", dimension: "top", description: "Short-sleeve polo", promptHint: "wearing a fitted polo shirt with a small ribbed collar and short sleeves" },
3809
3934
  { id: "top-tube-top", label: "Tube Top", dimension: "top", description: "Strapless tube top", promptHint: "wearing a strapless tube top fitted across the chest" },
3810
3935
  { id: "top-camisole", label: "Camisole", dimension: "top", description: "Thin satin cami", promptHint: "wearing a thin satin camisole with delicate spaghetti straps" },
3811
- { id: "top-corset", label: "Corset", dimension: "top", description: "Structured corset top", promptHint: "wearing a structured corset top with boning and visible lacing" },
3812
- { id: "top-bra-top", label: "Bra Top", dimension: "top", description: "Bra-style top showing midriff", promptHint: "wearing a fitted bra-style top with the midriff exposed" },
3936
+ { id: "top-corset", label: "Corset", dimension: "top", description: "Structured corset top", promptHint: "wearing a structured corset top with boning and visible lacing", adultOnly: true },
3937
+ { id: "top-bra-top", label: "Bra Top", dimension: "top", description: "Bra-style top showing midriff", promptHint: "wearing a fitted bra-style top with the midriff exposed", adultOnly: true },
3813
3938
  { id: "top-sports-bra", label: "Sports Bra", dimension: "top", description: "Athletic sports bra", promptHint: "wearing a structured sports bra with athletic strapping" },
3814
- { id: "top-bikini-top", label: "Bikini Top", dimension: "top", description: "Triangle bikini top", promptHint: "wearing a triangle bikini top tied behind the neck and back" },
3939
+ { id: "top-bikini-top", label: "Bikini Top", dimension: "top", description: "Triangle bikini top", promptHint: "wearing a triangle bikini top tied behind the neck and back", adultOnly: true },
3815
3940
  { id: "top-bodysuit", label: "Bodysuit", dimension: "top", description: "Fitted one-piece torso garment", promptHint: "wearing a fitted bodysuit, a one-piece torso garment snapped at the inseam" },
3816
3941
  { id: "top-leotard", label: "Leotard", dimension: "top", description: "Dance-style fitted top with sleeves", promptHint: "wearing a dance-style leotard, a fitted one-piece top with sleeves" },
3817
- { id: "top-mesh-top", label: "Mesh Top", dimension: "top", description: "Sheer mesh fabric top", promptHint: "wearing a sheer mesh top, the open-weave fabric letting skin show through" },
3818
- { id: "top-sheer-top", label: "Sheer Top", dimension: "top", description: "Translucent fabric top", promptHint: "wearing a sheer translucent top with a soft drape, hinting at the silhouette underneath" },
3942
+ { id: "top-mesh-top", label: "Mesh Top", dimension: "top", description: "Sheer mesh fabric top", promptHint: "wearing a sheer mesh top, the open-weave fabric letting skin show through", adultOnly: true },
3943
+ { id: "top-sheer-top", label: "Sheer Top", dimension: "top", description: "Translucent fabric top", promptHint: "wearing a sheer translucent top with a soft drape, hinting at the silhouette underneath", adultOnly: true },
3819
3944
  { id: "top-bandeau", label: "Bandeau", dimension: "top", description: "Strapless tube top", promptHint: "wearing a strapless bandeau top wrapped across the chest" },
3820
3945
  // -------------------- Bottom (lower-body garment) --------------------
3821
3946
  { id: "bottom-jeans", label: "Jeans", dimension: "bottom", description: "Classic blue jeans", promptHint: "in classic five-pocket blue jeans with a straight leg cut" },
@@ -3855,11 +3980,11 @@ var STYLINGS = [
3855
3980
  { id: "outerwear-fur-coat", label: "Fur Coat", dimension: "outerwear", description: "Plush fur coat", promptHint: "wearing a plush fur coat with a deep textured pile" },
3856
3981
  // -------------------- Legwear (between bottom and footwear) --------------------
3857
3982
  { id: "legwear-bare", label: "Bare Legs", dimension: "legwear", description: "Bare legs, no hosiery", promptHint: "with bare legs, no hosiery" },
3858
- { id: "legwear-sheer-tights", label: "Sheer Tights", dimension: "legwear", description: "Sheer nude pantyhose", promptHint: "with sheer nude pantyhose" },
3983
+ { id: "legwear-sheer-tights", label: "Sheer Tights", dimension: "legwear", description: "Sheer nude pantyhose", promptHint: "with sheer nude pantyhose", adultOnly: true },
3859
3984
  { id: "legwear-opaque-tights", label: "Opaque Tights", dimension: "legwear", description: "Solid opaque tights", promptHint: "with opaque black tights covering the legs" },
3860
- { id: "legwear-fishnets", label: "Fishnets", dimension: "legwear", description: "Diamond-mesh fishnets", promptHint: "with fishnet stockings, the skin showing through the diamond mesh", term: "fishnet stockings" },
3861
- { id: "legwear-thigh-highs", label: "Thigh-High Stockings", dimension: "legwear", description: "Thigh-high stockings", promptHint: "with thigh-high stockings stopping mid-thigh" },
3862
- { id: "legwear-lace-top-stockings", label: "Lace-Top Stockings", dimension: "legwear", description: "Lace-banded thigh-highs", promptHint: "with lace-topped thigh-high stockings cinched mid-thigh" },
3985
+ { id: "legwear-fishnets", label: "Fishnets", dimension: "legwear", description: "Diamond-mesh fishnets", promptHint: "with fishnet stockings, the skin showing through the diamond mesh", term: "fishnet stockings", adultOnly: true },
3986
+ { id: "legwear-thigh-highs", label: "Thigh-High Stockings", dimension: "legwear", description: "Thigh-high stockings", promptHint: "with thigh-high stockings stopping mid-thigh", adultOnly: true },
3987
+ { id: "legwear-lace-top-stockings", label: "Lace-Top Stockings", dimension: "legwear", description: "Lace-banded thigh-highs", promptHint: "with lace-topped thigh-high stockings cinched mid-thigh", adultOnly: true },
3863
3988
  { id: "legwear-knee-highs", label: "Knee-High Socks", dimension: "legwear", description: "Knee-high socks", promptHint: "with knee-high socks pulled up the calf" },
3864
3989
  { id: "legwear-crew-socks", label: "Crew Socks", dimension: "legwear", description: "Mid-calf crew socks", promptHint: "with simple crew socks visible above the shoes" },
3865
3990
  { id: "legwear-ankle-socks", label: "Ankle Socks", dimension: "legwear", description: "Low-cut ankle socks", promptHint: "with low-cut ankle socks barely showing above the shoes" },
@@ -3873,7 +3998,7 @@ var STYLINGS = [
3873
3998
  { id: "footwear-platforms", label: "Platform Shoes", dimension: "footwear", description: "Tall platform soles", promptHint: "in platform shoes with thick chunky soles" },
3874
3999
  { id: "footwear-ankle-boots", label: "Ankle Boots", dimension: "footwear", description: "Fitted ankle boots", promptHint: "in fitted black ankle boots" },
3875
4000
  { id: "footwear-knee-boots", label: "Knee-High Boots", dimension: "footwear", description: "Tall knee-high boots", promptHint: "in tall knee-high leather boots" },
3876
- { id: "footwear-thigh-high-boots", label: "Thigh-High Boots", dimension: "footwear", description: "Thigh-high boots", promptHint: "in over-the-knee thigh-high leather boots" },
4001
+ { id: "footwear-thigh-high-boots", label: "Thigh-High Boots", dimension: "footwear", description: "Thigh-high boots", promptHint: "in over-the-knee thigh-high leather boots", adultOnly: true },
3877
4002
  { id: "footwear-combat-boots", label: "Combat Boots", dimension: "footwear", description: "Laced combat boots", promptHint: "in laced black combat boots with a chunky lugged sole" },
3878
4003
  { id: "footwear-chelsea-boots", label: "Chelsea Boots", dimension: "footwear", description: "Slip-on Chelsea boots", promptHint: "in clean Chelsea boots with elastic side panels" },
3879
4004
  { id: "footwear-cowboy-boots", label: "Cowboy Boots", dimension: "footwear", description: "Pointed-toe cowboy boots", promptHint: "in pointed-toe cowboy boots with a stacked heel and decorative stitching" },
@@ -3892,18 +4017,18 @@ var STYLINGS = [
3892
4017
  { id: "footwear-espadrilles", label: "Espadrilles", dimension: "footwear", description: "Woven jute-sole canvas summer shoe", promptHint: "in woven jute-sole espadrilles with a canvas upper, a casual summer shoe" },
3893
4018
  // -------------------- Wardrobe State (modifier — composes with any garment) --------------------
3894
4019
  { id: "state-oversized", label: "Oversized", dimension: "wardrobe-state", description: "Loose, oversized fit", promptHint: "the clothing loose and oversized, draping freely well past the body", term: "oversized fit" },
3895
- { id: "state-fitted", label: "Fitted", dimension: "wardrobe-state", description: "Form-fitting silhouette", promptHint: "the clothing fitted and form-conscious, hugging the contours of the body", term: "form-fitting clothing" },
3896
- { id: "state-cropped", label: "Cropped", dimension: "wardrobe-state", description: "Top cropped at midriff", promptHint: "the top cropped above the midriff with the stomach visible", term: "cropped top" },
3897
- { id: "state-sheer", label: "Sheer", dimension: "wardrobe-state", description: "Translucent fabric", promptHint: "the fabric translucent and sheer, hinting at the silhouette underneath", term: "sheer clothing" },
3898
- { id: "state-wet", label: "Wet", dimension: "wardrobe-state", description: "Soaked, water-clinging", promptHint: "the clothing soaked and wet, the fabric clinging to the body and dripping water", term: "wet clothing" },
4020
+ { id: "state-fitted", label: "Fitted", dimension: "wardrobe-state", description: "Form-fitting silhouette", promptHint: "the clothing fitted and form-conscious, hugging the contours of the body", term: "form-fitting clothing", adultOnly: true },
4021
+ { id: "state-cropped", label: "Cropped", dimension: "wardrobe-state", description: "Top cropped at midriff", promptHint: "the top cropped above the midriff with the stomach visible", term: "cropped top", adultOnly: true },
4022
+ { id: "state-sheer", label: "Sheer", dimension: "wardrobe-state", description: "Translucent fabric", promptHint: "the fabric translucent and sheer, hinting at the silhouette underneath", term: "sheer clothing", adultOnly: true },
4023
+ { id: "state-wet", label: "Wet", dimension: "wardrobe-state", description: "Soaked, water-clinging", promptHint: "the clothing soaked and wet, the fabric clinging to the body and dripping water", term: "wet clothing", adultOnly: true },
3899
4024
  { id: "state-ripped", label: "Ripped", dimension: "wardrobe-state", description: "Torn and frayed", promptHint: "the fabric torn and ripped at the seams, with frayed edges", term: "ripped clothing" },
3900
4025
  { id: "state-distressed", label: "Distressed", dimension: "wardrobe-state", description: "Worn, faded, weathered", promptHint: "the clothing distressed and weathered, with faded color and worn-down edges", term: "distressed clothing" },
3901
4026
  { id: "state-vintage", label: "Vintage", dimension: "wardrobe-state", description: "Worn, retro character", promptHint: "the clothing carrying vintage character \u2014 softened color, broken-in fit, retro silhouette", term: "vintage clothing" },
3902
4027
  { id: "state-tucked-in", label: "Tucked In", dimension: "wardrobe-state", description: "Top tucked into bottom", promptHint: "the top neatly tucked into the bottom", term: "top tucked in" },
3903
4028
  { id: "state-half-tucked", label: "Half-Tucked", dimension: "wardrobe-state", description: "Front-tucked, back loose", promptHint: "the top half-tucked \u2014 front neatly tucked in, back hanging loose", term: "top half-tucked" },
3904
- { id: "state-off-shoulder", label: "Off-Shoulder", dimension: "wardrobe-state", description: "Slipped off one shoulder", promptHint: "the top slipped off one shoulder, baring the skin and collarbone", term: "off-the-shoulder top" },
3905
- { id: "state-halter-neck", label: "Halter Neck", dimension: "wardrobe-state", description: "Straps tied behind the neck", promptHint: "the top cut as a halter \u2014 straps tied behind the neck, shoulders and upper back bare", term: "halter neckline" },
3906
- { id: "state-plunging-neck", label: "Plunging Neckline", dimension: "wardrobe-state", description: "Deep plunging V neckline", promptHint: "the neckline cut in a deep plunging V" },
4029
+ { id: "state-off-shoulder", label: "Off-Shoulder", dimension: "wardrobe-state", description: "Slipped off one shoulder", promptHint: "the top slipped off one shoulder, baring the skin and collarbone", term: "off-the-shoulder top", adultOnly: true },
4030
+ { id: "state-halter-neck", label: "Halter Neck", dimension: "wardrobe-state", description: "Straps tied behind the neck", promptHint: "the top cut as a halter \u2014 straps tied behind the neck, shoulders and upper back bare", term: "halter neckline", adultOnly: true },
4031
+ { id: "state-plunging-neck", label: "Plunging Neckline", dimension: "wardrobe-state", description: "Deep plunging V neckline", promptHint: "the neckline cut in a deep plunging V", adultOnly: true },
3907
4032
  { id: "state-unbuttoned", label: "Unbuttoned", dimension: "wardrobe-state", description: "Open / unbuttoned", promptHint: "the outerwear hanging open and unbuttoned, falling loose at the sides", term: "worn open and unbuttoned" },
3908
4033
  { id: "state-rolled-sleeves", label: "Rolled Sleeves", dimension: "wardrobe-state", description: "Sleeves rolled up forearm", promptHint: "the sleeves rolled up to the forearm", term: "rolled-up sleeves" },
3909
4034
  { id: "state-layered", label: "Layered", dimension: "wardrobe-state", description: "Multiple stacked layers", promptHint: "the outfit composed of multiple stacked layers, each piece visible at the edges", term: "layered outfit" }
@@ -3970,6 +4095,8 @@ var STYLING_FIELD_BY_DIMENSION = {
3970
4095
  "wardrobe-state": "wardrobeState"
3971
4096
  };
3972
4097
  var MAX_SELECTED_BY_STYLING_DIMENSION = {
4098
+ headwear: 2,
4099
+ // a hat layered over a wrap/turban
3973
4100
  jewelry: 3,
3974
4101
  "wardrobe-state": 3,
3975
4102
  "hair-state": 2
@@ -3980,7 +4107,7 @@ function getStylingDimensionLimit(dimension) {
3980
4107
  var stylingById = new Map(STYLINGS.map((s) => [s.id, s]));
3981
4108
  function getStyling(id) {
3982
4109
  if (!id) return void 0;
3983
- return stylingById.get(id);
4110
+ return overlayEntry("styling", id, stylingById.get(id));
3984
4111
  }
3985
4112
  function getStylingLabel(id, fallback) {
3986
4113
  const s = getStyling(id);
@@ -4001,17 +4128,19 @@ function collectStylingFragments(data, fragmentFor) {
4001
4128
  if (pre) out.push(pre);
4002
4129
  const lipStateRaw = data.lipState;
4003
4130
  const boldRedAlready = Array.isArray(lipStateRaw) ? lipStateRaw.includes("lip-state-bold-red") : lipStateRaw === "lip-state-bold-red";
4131
+ const floored = isMinorAge(data);
4132
+ const emit = floored ? (id) => getStyling(id)?.adultOnly === true ? "" : fragmentFor(id) : fragmentFor;
4004
4133
  for (const dimension of STYLING_DIMENSION_ORDER) {
4005
4134
  const field = STYLING_FIELD_BY_DIMENSION[dimension];
4006
4135
  const raw = data[field];
4007
4136
  if (dimension === "makeup" && boldRedAlready && raw === "makeup-bold-lips") continue;
4008
4137
  if (typeof raw === "string" && raw.length > 0) {
4009
- const fragment = fragmentFor(raw);
4138
+ const fragment = emit(raw);
4010
4139
  if (fragment) out.push(fragment);
4011
4140
  } else if (Array.isArray(raw)) {
4012
4141
  for (const item of raw) {
4013
4142
  if (typeof item !== "string" || item.length === 0) continue;
4014
- const fragment = fragmentFor(item);
4143
+ const fragment = emit(item);
4015
4144
  if (fragment) out.push(fragment);
4016
4145
  }
4017
4146
  }
@@ -4068,7 +4197,7 @@ var TEMPORAL_CATEGORY_LABELS = {
4068
4197
  var temporalById = new Map(TEMPORALS.map((t) => [t.id, t]));
4069
4198
  function getTemporal(id) {
4070
4199
  if (!id) return void 0;
4071
- return temporalById.get(id);
4200
+ return overlayEntry("temporal", id, temporalById.get(id));
4072
4201
  }
4073
4202
  function getTemporalLabel(id, fallback) {
4074
4203
  const t = getTemporal(id);
@@ -4661,7 +4790,7 @@ var motionById = new Map(
4661
4790
  );
4662
4791
  function getCameraMotion(id) {
4663
4792
  if (!id) return void 0;
4664
- return motionById.get(id);
4793
+ return overlayEntry("camera-motions", id, motionById.get(id));
4665
4794
  }
4666
4795
  function getCameraMotionLabel(id, fallback) {
4667
4796
  const m = getCameraMotion(id);
@@ -5348,7 +5477,7 @@ var transitionById = new Map(
5348
5477
  );
5349
5478
  function getTransition(id) {
5350
5479
  if (!id) return void 0;
5351
- return transitionById.get(id);
5480
+ return overlayEntry("transitions", id, transitionById.get(id));
5352
5481
  }
5353
5482
  function getTransitionLabel(id, fallback) {
5354
5483
  const t = getTransition(id);
@@ -5875,7 +6004,7 @@ var characterFxById = new Map(
5875
6004
  );
5876
6005
  function getCharacterFx(id) {
5877
6006
  if (!id) return void 0;
5878
- return characterFxById.get(id);
6007
+ return overlayEntry("character-fx", id, characterFxById.get(id));
5879
6008
  }
5880
6009
  function getCharacterFxLabel(id, fallback) {
5881
6010
  const c = getCharacterFx(id);
@@ -6015,7 +6144,7 @@ var MATERIALS = [
6015
6144
  var materialById = new Map(MATERIALS.map((m) => [m.id, m]));
6016
6145
  function getMaterial(id) {
6017
6146
  if (!id) return void 0;
6018
- return materialById.get(id);
6147
+ return overlayEntry("materials", id, materialById.get(id));
6019
6148
  }
6020
6149
  function getMaterialLabel(id, fallback) {
6021
6150
  const m = getMaterial(id);
@@ -6074,6 +6203,35 @@ var MATERIAL_CATEGORY_ORDER = [
6074
6203
  "natural",
6075
6204
  "exotic"
6076
6205
  ];
6206
+ function curatedAnimalPromptHint(id) {
6207
+ if (!id) return "";
6208
+ if (!composedHas("animals", id)) return "";
6209
+ return composedOption("animals", id)?.promptHint ?? getAnimalPromptHint(id);
6210
+ }
6211
+ function curatedAnimalTerm(id) {
6212
+ if (!id) return "";
6213
+ if (!composedHas("animals", id)) return "";
6214
+ return composedOption("animals", id)?.term ?? getAnimalTerm(id);
6215
+ }
6216
+ function objectEntityText(catalogId, id, stock, stockHint, stockTerm) {
6217
+ if (!id || !composedHas(catalogId, id)) return { hint: "", term: "" };
6218
+ const composed = composedOption(catalogId, id);
6219
+ if (composed) return { hint: composed.promptHint, term: composed.term };
6220
+ if (!stock) return { hint: "", term: "" };
6221
+ return { hint: stockHint(stock), term: stockTerm(stock) };
6222
+ }
6223
+ function objectEntityTerm(entry) {
6224
+ return entry.term ?? deriveTerm(entry.label);
6225
+ }
6226
+ function curatedVehicleText(id) {
6227
+ return objectEntityText("vehicles", id, getVehicle(id), (e) => `featuring a ${e.label.toLowerCase()}, ${e.description}`, objectEntityTerm);
6228
+ }
6229
+ function curatedWeaponText(id) {
6230
+ return objectEntityText("weapons", id, getWeapon(id), (e) => `with a ${e.label.toLowerCase()}, ${e.description}`, objectEntityTerm);
6231
+ }
6232
+ function curatedFurnitureText(id) {
6233
+ return objectEntityText("furniture", id, getFurniture(id), (e) => `including a ${e.label.toLowerCase()}, ${e.description}`, objectEntityTerm);
6234
+ }
6077
6235
 
6078
6236
  // src/photo-genre.ts
6079
6237
  var PHOTO_GENRES = [
@@ -6091,7 +6249,7 @@ var PHOTO_GENRES = [
6091
6249
  { id: "brand-jil-sander", label: "Jil Sander Minimalism", category: "editorial", description: "Jil Sander \u2014 minimalist architectural muted", promptHint: "shot in the language of a Jil Sander editorial, minimalist composition, muted ecru and stone palette, architectural wardrobe with clean unbroken lines and the brand's quiet austere mood" },
6092
6250
  { id: "brand-vivienne-tam", label: "Vivienne Tam Style", category: "editorial", description: "Vivienne Tam \u2014 orientalist ornate fashion", promptHint: "shot in the language of a Vivienne Tam editorial, orientalist motifs and dragon embroidery, ornate East-Asian fashion detailing and a saturated jewel-toned palette" },
6093
6251
  { id: "brand-jacquemus", label: "Jacquemus Style", category: "editorial", description: "Jacquemus \u2014 sun-soaked surrealist playful", promptHint: "shot in the language of a Jacquemus campaign, sun-soaked Mediterranean light, surrealist scale play with oversized hats and miniature bags, terracotta and ochre palette and a playful French Riviera mood" },
6094
- { id: "brand-helmut-newton", label: "Helmut Newton Style", category: "editorial", description: "Helmut Newton \u2014 high-contrast B&W provocation", promptHint: "shot in the signature language of Helmut Newton, high-contrast black-and-white, hard direct flash carving every line, statuesque pose, glamorous wardrobe and the cool confrontational eroticism of his 1970s editorial work" },
6252
+ { id: "brand-helmut-newton", label: "Helmut Newton Style", category: "editorial", description: "Helmut Newton \u2014 high-contrast B&W provocation", promptHint: "shot in the signature language of Helmut Newton, high-contrast black-and-white, hard direct flash carving every line, statuesque pose, glamorous wardrobe and the cool confrontational eroticism of his 1970s editorial work", adultOnly: true },
6095
6253
  { id: "brand-harpers-bazaar", label: "Harper's Bazaar Style", category: "editorial", description: "Harper's Bazaar \u2014 high-fashion glossy", promptHint: "shot in the signature language of a Harper's Bazaar editorial, high-fashion glossy production, bold graphic backdrops, dramatic single-source key light and the brand's distinctively confident composition" },
6096
6254
  // -------------------- Documentary / Candid --------------------
6097
6255
  { id: "paparazzi", label: "Paparazzi", category: "documentary", description: "Flash-blown tabloid candid", term: "paparazzi flash candid", promptHint: "shot as a paparazzi tabloid candid, harsh on-camera flash blowing out the highlights, slight motion blur, low candid angle, blurred crowd in the background and grainy cropped tabloid framing" },
@@ -6108,7 +6266,7 @@ var PHOTO_GENRES = [
6108
6266
  { id: "mugshot", label: "Mugshot", category: "studio-formal", description: "Police booking-style portrait", promptHint: "shot as a police booking mugshot, harsh fluorescent overhead lighting, height chart on the wall behind, dead-square frontal framing and a flat institutional color cast" },
6109
6267
  { id: "wedding-portrait", label: "Wedding Portrait", category: "studio-formal", description: "Romantic bridal-style portrait", promptHint: "shot as a wedding portrait, soft golden-hour backlight, romantic shallow depth of field, dreamy pastel color grade and an intimate posed embrace or solo bridal stance" },
6110
6268
  { id: "family-portrait", label: "Family Portrait", category: "studio-formal", description: "Posed family group shot", promptHint: "shot as a posed family portrait, warm even studio lighting, neutral backdrop, coordinated wardrobe and the group arranged in a balanced pyramid composition" },
6111
- { id: "glamour-portrait", label: "Glamour Portrait", category: "studio-formal", description: "Soft-focus glamour portrait", promptHint: "shot as a glamour portrait with soft diffused beauty lighting, a high-key warm tone, gentle skin retouch, voluminous hair and a sultry over-the-shoulder pose" },
6269
+ { id: "glamour-portrait", label: "Glamour Portrait", category: "studio-formal", description: "Soft-focus glamour portrait", promptHint: "shot as a glamour portrait with soft diffused beauty lighting, a high-key warm tone, gentle skin retouch, voluminous hair and a sultry over-the-shoulder pose", adultOnly: true },
6112
6270
  { id: "film-noir", label: "Film Noir", category: "studio-formal", description: "Hard-shadow noir portrait", promptHint: "shot in a film-noir portrait idiom, hard single-source key light casting deep shadows, venetian-blind shadow patterns, monochrome high-contrast palette and a brooding closed-off pose" },
6113
6271
  // -------------------- Selfie sub-types --------------------
6114
6272
  { id: "mirror-selfie", label: "Mirror Selfie", category: "selfie", description: "Phone-in-mirror full-body selfie", promptHint: "shot as a smartphone mirror selfie, the phone visible in one raised hand reflected in the mirror, full or three-quarter body framing, a domestic bedroom or bathroom background and natural ambient light" },
@@ -6137,7 +6295,7 @@ var photoGenreById = new Map(
6137
6295
  );
6138
6296
  function getPhotoGenre(id) {
6139
6297
  if (!id) return void 0;
6140
- return photoGenreById.get(id);
6298
+ return overlayEntry("photo-genre", id, photoGenreById.get(id));
6141
6299
  }
6142
6300
  function getPhotoGenreLabel(id, fallback) {
6143
6301
  const g = getPhotoGenre(id);
@@ -6223,7 +6381,7 @@ var BACKDROPS = [
6223
6381
  var backdropById = new Map(BACKDROPS.map((b) => [b.id, b]));
6224
6382
  function getBackdrop(id) {
6225
6383
  if (!id) return void 0;
6226
- return backdropById.get(id);
6384
+ return overlayEntry("backdrop", id, backdropById.get(id));
6227
6385
  }
6228
6386
  function getBackdropLabel(id, fallback) {
6229
6387
  const b = getBackdrop(id);
@@ -6324,12 +6482,13 @@ var HELD_PROPS = [
6324
6482
  { id: "flashlight", label: "Flashlight", category: "occupational", description: "Modern flashlight cutting a beam", promptHint: "holding a modern flashlight raised forward in one hand, a sharp white beam cutting through the darkness ahead and side-lighting the face", term: "holding a flashlight raised forward" },
6325
6483
  { id: "compass", label: "Compass", category: "occupational", description: "Vintage handheld nautical compass", promptHint: "holding a vintage brass nautical compass open in one cupped palm at chest height, the needle clearly visible as the eyes drift down to read the bearing", term: "holding an open brass nautical compass" },
6326
6484
  { id: "bow-and-arrow", label: "Bow and Arrow", category: "occupational", description: "Drawn archery bow with arrow nocked", promptHint: "holding an archery bow drawn at full tension with one hand on the grip and the other pulling the string back to the cheek, an arrow nocked and aimed forward", term: "drawing an archery bow with a nocked arrow" },
6327
- { id: "shield", label: "Shield", category: "occupational", description: "Handheld medieval shield", promptHint: "holding a medieval shield raised across the body with one arm strapped through the back, the front face angled forward in a defensive stance", term: "holding a raised medieval shield" }
6485
+ { id: "shield", label: "Shield", category: "occupational", description: "Handheld medieval shield", promptHint: "holding a medieval shield raised across the body with one arm strapped through the back, the front face angled forward in a defensive stance", term: "holding a raised medieval shield" },
6486
+ { id: "work-gloves", label: "Work Gloves", category: "occupational", description: "Worn leather work gloves held in hand", promptHint: "holding a worn pair of tan leather work gloves in both hands at waist height, the thick weathered leather clearly visible", term: "holding a pair of leather work gloves" }
6328
6487
  ];
6329
6488
  var heldPropById = new Map(HELD_PROPS.map((p) => [p.id, p]));
6330
6489
  function getHeldProp(id) {
6331
6490
  if (!id) return void 0;
6332
- return heldPropById.get(id);
6491
+ return overlayEntry("held-prop", id, heldPropById.get(id));
6333
6492
  }
6334
6493
  function getHeldPropLabel(id, fallback) {
6335
6494
  const p = getHeldProp(id);
@@ -6911,7 +7070,7 @@ var photographerById = new Map(
6911
7070
  );
6912
7071
  function getPhotographer(id) {
6913
7072
  if (!id) return void 0;
6914
- return photographerById.get(id);
7073
+ return overlayEntry("photographer", id, photographerById.get(id));
6915
7074
  }
6916
7075
  function getPhotographerLabel(id, fallback) {
6917
7076
  const p = getPhotographer(id);
@@ -7291,7 +7450,7 @@ var aestheticById = new Map(
7291
7450
  );
7292
7451
  function getAesthetic(id) {
7293
7452
  if (!id) return void 0;
7294
- return aestheticById.get(id);
7453
+ return overlayEntry("aesthetic", id, aestheticById.get(id));
7295
7454
  }
7296
7455
  function getAestheticLabel(id, fallback) {
7297
7456
  const a = getAesthetic(id);
@@ -7558,7 +7717,7 @@ var ERAS = [
7558
7717
  var eraById = new Map(ERAS.map((e) => [e.id, e]));
7559
7718
  function getEra(id) {
7560
7719
  if (!id) return void 0;
7561
- return eraById.get(id);
7720
+ return overlayEntry("era", id, eraById.get(id));
7562
7721
  }
7563
7722
  function getEraLabel(id, fallback) {
7564
7723
  const e = getEra(id);
@@ -7626,7 +7785,7 @@ var exposureById = new Map(
7626
7785
  );
7627
7786
  function getExposure(id) {
7628
7787
  if (!id) return void 0;
7629
- return exposureById.get(id);
7788
+ return overlayEntry("exposure-settings", id, exposureById.get(id));
7630
7789
  }
7631
7790
  function getExposureLabel(id, fallback) {
7632
7791
  const e = getExposure(id);
@@ -7706,7 +7865,7 @@ var renderQualityById = new Map(
7706
7865
  );
7707
7866
  function getRenderQuality(id) {
7708
7867
  if (!id) return void 0;
7709
- return renderQualityById.get(id);
7868
+ return overlayEntry("render-quality", id, renderQualityById.get(id));
7710
7869
  }
7711
7870
  function getRenderQualityLabel(id, fallback) {
7712
7871
  const r = getRenderQuality(id);
@@ -7754,7 +7913,7 @@ var compositionEffectById = new Map(
7754
7913
  );
7755
7914
  function getCompositionEffect(id) {
7756
7915
  if (!id) return void 0;
7757
- return compositionEffectById.get(id);
7916
+ return overlayEntry("composition-effects", id, compositionEffectById.get(id));
7758
7917
  }
7759
7918
  function getCompositionEffectLabel(id, fallback) {
7760
7919
  const c = getCompositionEffect(id);
@@ -7796,7 +7955,7 @@ var postProcessById = new Map(
7796
7955
  );
7797
7956
  function getPostProcessEffect(id) {
7798
7957
  if (!id) return void 0;
7799
- return postProcessById.get(id);
7958
+ return overlayEntry("post-process-effects", id, postProcessById.get(id));
7800
7959
  }
7801
7960
  function getPostProcessEffectLabel(id, fallback) {
7802
7961
  const p = getPostProcessEffect(id);
@@ -8371,20 +8530,20 @@ var GENRE_BY_ID = new Map(MUSIC_GENRES.map((g) => [g.id, g]));
8371
8530
  var ERA_BY_ID = new Map(MUSIC_ERAS.map((e) => [e.id, e]));
8372
8531
  function getMusicGenre(id) {
8373
8532
  if (!id) return void 0;
8374
- return GENRE_BY_ID.get(id);
8533
+ return overlayEntry("music-genre", id, GENRE_BY_ID.get(id));
8375
8534
  }
8376
8535
  function getMusicGenreLabel(id) {
8377
8536
  if (!id) return "";
8378
- return GENRE_BY_ID.get(id)?.label ?? id;
8537
+ return getMusicGenre(id)?.label ?? id;
8379
8538
  }
8380
8539
  function getMusicSubgenre(genreId, subgenreId) {
8381
8540
  if (!genreId || !subgenreId) return void 0;
8382
8541
  const genre = GENRE_BY_ID.get(genreId);
8383
- return genre?.subgenres.find((s) => s.id === subgenreId);
8542
+ return overlayEntry("music-genre", subgenreId, genre?.subgenres.find((s) => s.id === subgenreId));
8384
8543
  }
8385
8544
  function getMusicEra(id) {
8386
8545
  if (!id) return void 0;
8387
- return ERA_BY_ID.get(id);
8546
+ return overlayEntry("music-genre", id, ERA_BY_ID.get(id));
8388
8547
  }
8389
8548
  function getMusicGenreTerm(id) {
8390
8549
  return resolveTerm(getMusicGenre(id ?? void 0));
@@ -8521,13 +8680,13 @@ var ENERGY_BY_ID = new Map(MUSIC_ENERGIES.map((x) => [x.id, x]));
8521
8680
  var EMOTION_BY_ID = new Map(MUSIC_EMOTIONS.map((x) => [x.id, x]));
8522
8681
  var VIBE_BY_ID = new Map(MUSIC_VIBES.map((x) => [x.id, x]));
8523
8682
  function getMusicEnergy(id) {
8524
- return id ? ENERGY_BY_ID.get(id) : void 0;
8683
+ return id ? overlayEntry("music-mood", id, ENERGY_BY_ID.get(id)) : void 0;
8525
8684
  }
8526
8685
  function getMusicEmotion(id) {
8527
- return id ? EMOTION_BY_ID.get(id) : void 0;
8686
+ return id ? overlayEntry("music-mood", id, EMOTION_BY_ID.get(id)) : void 0;
8528
8687
  }
8529
8688
  function getMusicVibe(id) {
8530
- return id ? VIBE_BY_ID.get(id) : void 0;
8689
+ return id ? overlayEntry("music-mood", id, VIBE_BY_ID.get(id)) : void 0;
8531
8690
  }
8532
8691
  function getMusicEnergyTerm(id) {
8533
8692
  return resolveTerm(id ? getMusicEnergy(id) : void 0);
@@ -8784,16 +8943,16 @@ var PRODUCTION_BY_ID = new Map(PRODUCTION_STYLES.map((x) => [x.id, x]));
8784
8943
  var VOCAL_BY_ID = new Map(VOCAL_PRESENCE.map((x) => [x.id, x]));
8785
8944
  var SINGING_STYLE_BY_ID = new Map(SINGING_STYLES.map((x) => [x.id, x]));
8786
8945
  function getInstrument(id) {
8787
- return id ? INSTRUMENT_BY_ID.get(id) : void 0;
8946
+ return id ? overlayEntry("instrumentation", id, INSTRUMENT_BY_ID.get(id)) : void 0;
8788
8947
  }
8789
8948
  function getProductionStyle(id) {
8790
- return id ? PRODUCTION_BY_ID.get(id) : void 0;
8949
+ return id ? overlayEntry("instrumentation", id, PRODUCTION_BY_ID.get(id)) : void 0;
8791
8950
  }
8792
8951
  function getVocalPresence(id) {
8793
- return id ? VOCAL_BY_ID.get(id) : void 0;
8952
+ return id ? overlayEntry("instrumentation", id, VOCAL_BY_ID.get(id)) : void 0;
8794
8953
  }
8795
8954
  function getSingingStyle(id) {
8796
- return id ? SINGING_STYLE_BY_ID.get(id) : void 0;
8955
+ return id ? overlayEntry("instrumentation", id, SINGING_STYLE_BY_ID.get(id)) : void 0;
8797
8956
  }
8798
8957
  function getInstrumentTerm(id) {
8799
8958
  return resolveTerm(id ? getInstrument(id) : void 0);
@@ -8999,19 +9158,19 @@ var LANGUAGE_BY_ID = new Map(VOICE_LANGUAGES.map((x) => [x.id, x]));
8999
9158
  var ACCENT_BY_ID = new Map(VOICE_ACCENTS.map((x) => [x.id, x]));
9000
9159
  var TIMBRE_BY_ID = new Map(VOICE_TIMBRES.map((x) => [x.id, x]));
9001
9160
  function getVoiceAge(id) {
9002
- return id ? AGE_BY_ID.get(id) : void 0;
9161
+ return id ? overlayEntry("voice-character", id, AGE_BY_ID.get(id)) : void 0;
9003
9162
  }
9004
9163
  function getVoiceGender(id) {
9005
- return id ? GENDER_BY_ID.get(id) : void 0;
9164
+ return id ? overlayEntry("voice-character", id, GENDER_BY_ID.get(id)) : void 0;
9006
9165
  }
9007
9166
  function getVoiceLanguage(id) {
9008
- return id ? LANGUAGE_BY_ID.get(id) : void 0;
9167
+ return id ? overlayEntry("voice-character", id, LANGUAGE_BY_ID.get(id)) : void 0;
9009
9168
  }
9010
9169
  function getVoiceAccent(id) {
9011
- return id ? ACCENT_BY_ID.get(id) : void 0;
9170
+ return id ? overlayEntry("voice-character", id, ACCENT_BY_ID.get(id)) : void 0;
9012
9171
  }
9013
9172
  function getVoiceTimbre(id) {
9014
- return id ? TIMBRE_BY_ID.get(id) : void 0;
9173
+ return id ? overlayEntry("voice-character", id, TIMBRE_BY_ID.get(id)) : void 0;
9015
9174
  }
9016
9175
  function getVoiceAgeTerm(id) {
9017
9176
  return resolveTerm(getVoiceAge(id ?? void 0));
@@ -9158,13 +9317,13 @@ var PACE_BY_ID = new Map(VOICE_PACES.map((x) => [x.id, x]));
9158
9317
  var EMOTION_BY_ID2 = new Map(VOICE_EMOTIONS.map((x) => [x.id, x]));
9159
9318
  var ARCHETYPE_BY_ID = new Map(VOICE_ARCHETYPES.map((x) => [x.id, x]));
9160
9319
  function getVoicePace(id) {
9161
- return id ? PACE_BY_ID.get(id) : void 0;
9320
+ return id ? overlayEntry("voice-delivery", id, PACE_BY_ID.get(id)) : void 0;
9162
9321
  }
9163
9322
  function getVoiceEmotion(id) {
9164
- return id ? EMOTION_BY_ID2.get(id) : void 0;
9323
+ return id ? overlayEntry("voice-delivery", id, EMOTION_BY_ID2.get(id)) : void 0;
9165
9324
  }
9166
9325
  function getVoiceArchetype(id) {
9167
- return id ? ARCHETYPE_BY_ID.get(id) : void 0;
9326
+ return id ? overlayEntry("voice-delivery", id, ARCHETYPE_BY_ID.get(id)) : void 0;
9168
9327
  }
9169
9328
  function getVoicePaceTerm(id) {
9170
9329
  return resolveTerm(getVoicePace(id ?? void 0));
@@ -9230,6 +9389,7 @@ function toOptions(arr, categoryField) {
9230
9389
  };
9231
9390
  if (e.description) opt.description = e.description;
9232
9391
  if (categoryField) opt.category = e[categoryField];
9392
+ if (e.adultOnly) opt.adultOnly = true;
9233
9393
  return opt;
9234
9394
  });
9235
9395
  }
@@ -9755,6 +9915,7 @@ function getPickerCatalog(nodeTypeOrCatalogId) {
9755
9915
  const all = getRegisteredPickerCatalogs();
9756
9916
  return all.find((c) => c.nodeType === nodeTypeOrCatalogId) ?? all.find((c) => c.catalogId === nodeTypeOrCatalogId);
9757
9917
  }
9918
+ setComposedCatalogResolver(getPickerCatalog);
9758
9919
  function listPickerCatalogs() {
9759
9920
  return getRegisteredPickerCatalogs();
9760
9921
  }
@@ -9845,9 +10006,6 @@ function readHintMode(data) {
9845
10006
  function byMode(mode, full, compact) {
9846
10007
  return mode === "compact" ? compact : full;
9847
10008
  }
9848
- function objectEntityTerm(entry) {
9849
- return entry.term ?? deriveTerm(entry.label);
9850
- }
9851
10009
  function getParameterPromptHint(node, ctx) {
9852
10010
  return resolveParameterHint(node, ctx);
9853
10011
  }
@@ -9972,36 +10130,23 @@ function resolveBaseHint(type, data, mode = "full") {
9972
10130
  return withCustomText(data, byMode(mode, getLoopSubjectPromptHint, getLoopSubjectTerm)(asStr(data.loopSubject)));
9973
10131
  case "material":
9974
10132
  return withCustomText(data, buildMaterialHints(data.material, mode));
9975
- // Animal is the one Object-entity catalog whose phrasing has a single
9976
- // owner: `@nodaro/shared`'s `getAnimalPromptHint` / `getAnimalTerm`, which
9977
- // the picker-catalog funnel calls too. Both getters already return "" on a
9978
- // miss, so the entry lookup and the `animal ? … : ""` guard are the
9979
- // getters' job now, not this switch's.
10133
+ // The four object-entity catalogs are owned by @nodaro/shared and overlaid
10134
+ // in shared-catalog-overlay.ts — the ONE module every prompt-text read of
10135
+ // them goes through (the subject fold uses it too). Each returns "" on a
10136
+ // miss, so the entry lookup is the module's job, not this switch's.
9980
10137
  case "animal":
9981
- return withCustomText(
9982
- data,
9983
- byMode(mode, getAnimalPromptHint, getAnimalTerm)(asStr(data.animal))
9984
- );
10138
+ return withCustomText(data, byMode(mode, curatedAnimalPromptHint, curatedAnimalTerm)(asStr(data.animal)));
9985
10139
  case "vehicle": {
9986
- const vehicle = getVehicle(asStr(data.vehicle));
9987
- return withCustomText(
9988
- data,
9989
- vehicle ? byMode(mode, `featuring a ${vehicle.label.toLowerCase()}, ${vehicle.description}`, objectEntityTerm(vehicle)) : ""
9990
- );
10140
+ const t = curatedVehicleText(asStr(data.vehicle));
10141
+ return withCustomText(data, byMode(mode, t.hint, t.term));
9991
10142
  }
9992
10143
  case "weapon": {
9993
- const weapon = getWeapon(asStr(data.weapon));
9994
- return withCustomText(
9995
- data,
9996
- weapon ? byMode(mode, `with a ${weapon.label.toLowerCase()}, ${weapon.description}`, objectEntityTerm(weapon)) : ""
9997
- );
10144
+ const t = curatedWeaponText(asStr(data.weapon));
10145
+ return withCustomText(data, byMode(mode, t.hint, t.term));
9998
10146
  }
9999
10147
  case "furniture": {
10000
- const furniture = getFurniture(asStr(data.furniture));
10001
- return withCustomText(
10002
- data,
10003
- furniture ? byMode(mode, `including a ${furniture.label.toLowerCase()}, ${furniture.description}`, objectEntityTerm(furniture)) : ""
10004
- );
10148
+ const t = curatedFurnitureText(asStr(data.furniture));
10149
+ return withCustomText(data, byMode(mode, t.hint, t.term));
10005
10150
  }
10006
10151
  case "photo-genre":
10007
10152
  return withCustomText(data, byMode(mode, getPhotoGenrePromptHint, getPhotoGenreTerm)(asStr(data.photoGenre)));
@@ -11472,7 +11617,7 @@ ${directives}`;
11472
11617
  }
11473
11618
  }
11474
11619
  const styleText2 = style?.trim();
11475
- const styleLine2 = styleText2 ? `Style: ${getStylePromptHint(styleText2) || styleText2}` : "";
11620
+ const styleLine2 = styleText2 && !isDeniedStyleId(styleText2) ? `Style: ${getStylePromptHint(styleText2) || styleText2}` : "";
11476
11621
  const negPrompt2 = negativePrompt?.trim();
11477
11622
  let nativeNegativePrompt2;
11478
11623
  let avoidLine2 = "";
@@ -11554,7 +11699,7 @@ ${directives}`;
11554
11699
  prompt = section.length > 0 ? `${wrapped}${STYLE_SECTION_GAP}${section}` : wrapped;
11555
11700
  }
11556
11701
  const styleText = style?.trim();
11557
- const styleLine = styleText ? `Style: ${getStylePromptHint(styleText) || styleText}` : "";
11702
+ const styleLine = styleText && !isDeniedStyleId(styleText) ? `Style: ${getStylePromptHint(styleText) || styleText}` : "";
11558
11703
  const negPrompt = negativePrompt?.trim();
11559
11704
  let nativeNegativePrompt;
11560
11705
  let avoidLine = "";
@@ -12026,6 +12171,8 @@ function renderLens(l) {
12026
12171
  if (l.aperture) bits.push(`f/${l.aperture}`);
12027
12172
  return bits.length > 0 ? `Lens: ${bits.join(", ")}.` : "";
12028
12173
  }
12174
+
12175
+ // src/subject-registry.ts
12029
12176
  var viaFragmentBuilder = (build) => (subject, mode) => {
12030
12177
  const clause = build(subject, mode).filter((s) => s.length > 0).join(", ");
12031
12178
  return clause.length > 0 ? [clause] : [];
@@ -12048,7 +12195,7 @@ var SUBJECT_FIELDS = [
12048
12195
  { key: "styling", kind: "group", surface: "both", render: viaFragmentBuilder(buildStylingHints) },
12049
12196
  { key: "heldProp", kind: "ids", surface: "both", maxPicks: 2, render: viaListBuilder2(buildHeldPropHints) },
12050
12197
  { key: "material", kind: "ids", surface: "both", maxPicks: 2, render: viaStringBuilder2(buildMaterialHints) },
12051
- { key: "animal", kind: "ids", surface: "both", maxPicks: 1, render: perId2(getAnimalPromptHint, getAnimalTerm) }
12198
+ { key: "animal", kind: "ids", surface: "both", maxPicks: 1, render: perId2(curatedAnimalPromptHint, curatedAnimalTerm) }
12052
12199
  ];
12053
12200
  var SUBJECT_FOLD_KEYS = SUBJECT_FIELDS.map((f) => f.key);
12054
12201
  var IDS_ROWS = SUBJECT_FIELDS.filter((f) => f.kind === "ids");
@@ -13020,10 +13167,13 @@ function resolveSeedance2Inputs(args) {
13020
13167
  droppedRefImages
13021
13168
  };
13022
13169
  }
13023
- var GEMINI_OMNI_INPUT_SLOTS = VIDEO_REF_LIMITS_BY_PROVIDER["gemini-omni-video"]?.images ?? 7;
13170
+ var DEFAULT_GEMINI_OMNI_PROVIDER = "gemini-omni-video";
13171
+ function geminiOmniInputSlots(provider) {
13172
+ return VIDEO_REF_LIMITS_BY_PROVIDER[provider ?? DEFAULT_GEMINI_OMNI_PROVIDER]?.images ?? 7;
13173
+ }
13024
13174
  function resolveGeminiOmniI2vInputs(args) {
13025
13175
  const refs = (args.refImageUrls ?? []).filter((u) => typeof u === "string" && u.length > 0);
13026
- const slots = GEMINI_OMNI_INPUT_SLOTS - (args.videoConnected ? 2 : 0);
13176
+ const slots = geminiOmniInputSlots(args.provider) - (args.videoConnected ? 2 : 0);
13027
13177
  const refSlots = Math.max(0, slots - 1);
13028
13178
  const kept = refs.slice(0, refSlots);
13029
13179
  const droppedRefImages = refs.length - kept.length;
@@ -13057,6 +13207,114 @@ function resolveVeoI2vInputs(args) {
13057
13207
  droppedEndFrame: Boolean(args.endFrameUrl)
13058
13208
  };
13059
13209
  }
13210
+
13211
+ // src/catalog-id-guard.ts
13212
+ var SIDE_FIELDS = [
13213
+ // pose.ts resolves all four sub-picks through getPosePromptHint
13214
+ ["pose", "handPosition", "pose"],
13215
+ ["pose", "bodyLean", "pose"],
13216
+ ["pose", "headTilt", "pose"],
13217
+ ["pose", "activity", "pose"]
13218
+ ];
13219
+ var DIRECTION_ALIASES = {
13220
+ framingId: "framing",
13221
+ framingAngleId: "framing",
13222
+ lightingId: "lighting",
13223
+ lensId: "lens",
13224
+ cameraFormatId: "camera-format"
13225
+ };
13226
+ var tableMemo = null;
13227
+ function fieldTable() {
13228
+ if (tableMemo) return tableMemo;
13229
+ const byNodeType = /* @__PURE__ */ new Map();
13230
+ const byField = /* @__PURE__ */ new Map();
13231
+ const put = (nodeType, field, catalogId) => {
13232
+ let m = byNodeType.get(nodeType);
13233
+ if (!m) byNodeType.set(nodeType, m = /* @__PURE__ */ new Map());
13234
+ m.set(field, catalogId);
13235
+ if (!byField.has(field)) byField.set(field, catalogId);
13236
+ };
13237
+ for (const c of PICKER_CATALOGS) {
13238
+ if (c.valueField) put(c.nodeType, c.valueField, c.catalogId);
13239
+ for (const d of c.dimensions ?? []) put(c.nodeType, d.field, c.catalogId);
13240
+ for (const f of c.fields ?? []) put(c.nodeType, f, c.catalogId);
13241
+ }
13242
+ for (const [nodeType, field, catalogId] of SIDE_FIELDS) put(nodeType, field, catalogId);
13243
+ for (const [alias, catalogId] of Object.entries(DIRECTION_ALIASES)) if (!byField.has(alias)) byField.set(alias, catalogId);
13244
+ tableMemo = { byNodeType, byField };
13245
+ return tableMemo;
13246
+ }
13247
+ var idsMemo = null;
13248
+ function composedIds(catalogId) {
13249
+ const v = catalogPacksVersion();
13250
+ if (!idsMemo || idsMemo.v !== v) idsMemo = { v, byCatalog: /* @__PURE__ */ new Map() };
13251
+ const hit = idsMemo.byCatalog.get(catalogId);
13252
+ if (hit) return hit;
13253
+ const cat = getPickerCatalog(catalogId);
13254
+ const ids = /* @__PURE__ */ new Set();
13255
+ for (const o of cat?.options ?? []) ids.add(o.id);
13256
+ for (const d of cat?.dimensions ?? []) for (const o of d.options) ids.add(o.id);
13257
+ idsMemo.byCatalog.set(catalogId, ids);
13258
+ return ids;
13259
+ }
13260
+ function values(v) {
13261
+ if (typeof v === "string") return v ? [v] : [];
13262
+ if (Array.isArray(v)) return v.filter((x) => typeof x === "string" && x.length > 0);
13263
+ return [];
13264
+ }
13265
+ function catalogGuardActive() {
13266
+ return getRegisteredCatalogPacks().length > 0;
13267
+ }
13268
+ function checkRecord(nodeType, nodeId, data, fields, out) {
13269
+ for (const [field, catalogId] of fields) {
13270
+ const raw = data[field];
13271
+ if (raw === void 0 || raw === null) continue;
13272
+ const offered = composedIds(catalogId);
13273
+ for (const id of values(raw)) {
13274
+ if (!offered.has(id)) out.push({ nodeId, nodeType, field, id, catalogId });
13275
+ }
13276
+ }
13277
+ }
13278
+ function findForeignCatalogIds(nodes) {
13279
+ if (!catalogGuardActive() || !nodes) return [];
13280
+ const table = fieldTable();
13281
+ const out = [];
13282
+ for (const node of nodes) {
13283
+ const type = typeof node.type === "string" ? node.type : "";
13284
+ const nodeId = typeof node.id === "string" ? node.id : void 0;
13285
+ const data = node.data && typeof node.data === "object" ? node.data : null;
13286
+ if (!type || !data) continue;
13287
+ const own = table.byNodeType.get(type);
13288
+ if (own) checkRecord(type, nodeId, data, own, out);
13289
+ for (const key of ["direction", "subject"]) {
13290
+ const rec = data[key];
13291
+ if (rec && typeof rec === "object" && !Array.isArray(rec)) {
13292
+ checkRecord(type, nodeId, rec, table.byField, out);
13293
+ }
13294
+ }
13295
+ }
13296
+ return out;
13297
+ }
13298
+ function findForeignCatalogIdsInBody(nodeType, body) {
13299
+ if (!catalogGuardActive() || !body) return [];
13300
+ const table = fieldTable();
13301
+ const out = [];
13302
+ for (const key of ["direction", "subject"]) {
13303
+ const rec = body[key];
13304
+ if (rec && typeof rec === "object" && !Array.isArray(rec)) {
13305
+ checkRecord(nodeType, void 0, rec, table.byField, out);
13306
+ }
13307
+ }
13308
+ return out;
13309
+ }
13310
+ function foreignCatalogIdMessage(found) {
13311
+ const parts = [...new Set(found.map((f) => `${f.field}="${f.id}"`))];
13312
+ return `This deployment does not offer the following picker value${parts.length === 1 ? "" : "s"}: ${parts.join(", ")}. Choose from the options shown in the picker.`;
13313
+ }
13314
+ function __resetCatalogIdGuardForTests() {
13315
+ tableMemo = null;
13316
+ idsMemo = null;
13317
+ }
13060
13318
  function packOptionIds(pack) {
13061
13319
  if (pack.options) return pack.options.map((o) => o.id);
13062
13320
  if (pack.dimensions) return pack.dimensions.flatMap((d) => d.options.map((o) => o.id));
@@ -13080,6 +13338,108 @@ function computePackSidecarCoverage(pack) {
13080
13338
  return { total: ids.length * NON_EN_LOCALE_IDS.length, missing, exempted };
13081
13339
  }
13082
13340
 
13341
+ // src/age-floor.ts
13342
+ var ADULT_SWEPT_CATALOG_IDS = ["styling", "mood", "pose", "photo-genre"];
13343
+ var ADULT_SWEPT_CATALOG_ID_SET = new Set(ADULT_SWEPT_CATALOG_IDS);
13344
+ function optionsOf(catalog) {
13345
+ if (catalog.kind === "single") return catalog.options ?? [];
13346
+ return (catalog.dimensions ?? []).flatMap((d) => d.options);
13347
+ }
13348
+ function getAdultOnlyEntries() {
13349
+ const catalogEntries = getRegisteredPickerCatalogs().filter((c) => ADULT_SWEPT_CATALOG_ID_SET.has(c.catalogId)).flatMap(optionsOf);
13350
+ const all = [...getRegisteredPeople(), ...catalogEntries];
13351
+ return all.filter((e) => e.adultOnly === true);
13352
+ }
13353
+ function getAdultOnlyIds() {
13354
+ return new Set(getAdultOnlyEntries().map((e) => e.id));
13355
+ }
13356
+ function getAdultOnlyHintStrings() {
13357
+ const out = /* @__PURE__ */ new Set();
13358
+ for (const e of getAdultOnlyEntries()) {
13359
+ if (e.promptHint) out.add(e.promptHint.toLowerCase());
13360
+ }
13361
+ return [...out].filter((s) => s.length >= 8).sort((a, b) => b.length - a.length);
13362
+ }
13363
+ var cachedMinorHints = null;
13364
+ var cachedMinorHintSourceCount = -1;
13365
+ function getMinorAgeHintStrings() {
13366
+ const people = getRegisteredPeople();
13367
+ if (cachedMinorHints && cachedMinorHintSourceCount === people.length) return cachedMinorHints;
13368
+ const out = /* @__PURE__ */ new Set();
13369
+ for (const e of people) {
13370
+ const minorAge = e.dimension === "age" && !ADULT_AGE_IDS.has(e.id);
13371
+ const minorType = e.dimension === "type" && MINOR_IMPLYING_TYPE_IDS.has(e.id);
13372
+ if (!minorAge && !minorType) continue;
13373
+ if (e.promptHint) out.add(e.promptHint.toLowerCase());
13374
+ }
13375
+ cachedMinorHintSourceCount = people.length;
13376
+ cachedMinorHints = [...out].filter((s) => s.length >= 8).sort((a, b) => b.length - a.length);
13377
+ return cachedMinorHints;
13378
+ }
13379
+ function escapeReSource(s) {
13380
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
13381
+ }
13382
+ function needleTokenSource(needle) {
13383
+ return needle.trim().split(/[\s-]+/).filter((t) => t.length > 0).map(escapeReSource).join("[\\s-]+");
13384
+ }
13385
+ function buildNeedleAlternationSource(needles) {
13386
+ const alts = needles.map(needleTokenSource).filter((s) => s.length > 0);
13387
+ if (alts.length === 0) return null;
13388
+ return `(?<![\\w-])(?:${alts.join("|")})(?![\\w-])`;
13389
+ }
13390
+ var cachedMinorNeedleRe = null;
13391
+ var cachedMinorNeedleCount = -1;
13392
+ function minorNeedleRegex() {
13393
+ const needles = getMinorAgeHintStrings();
13394
+ if (cachedMinorNeedleRe && cachedMinorNeedleCount === needles.length) return cachedMinorNeedleRe;
13395
+ cachedMinorNeedleCount = needles.length;
13396
+ const src = buildNeedleAlternationSource(needles);
13397
+ cachedMinorNeedleRe = src === null ? null : new RegExp(src, "i");
13398
+ return cachedMinorNeedleRe;
13399
+ }
13400
+ var NUMERIC_AGE_RE = /(?<![\w-])(\d{1,3})(?:\s*-\s*\d{1,3})?\s*-?\s*(?:years?\s*-?\s*old|yrs?\s*-?\s*old|y(?:\.\s*o\.?|\/o|o)|years?\s+of\s+age)(?![\w-])/gi;
13401
+ var PREFIXED_AGE_RE = /(?<![\w-])aged?\s+(?:of\s+)?(\d{1,3})(?![\w-])/gi;
13402
+ var AGE_NUMBER_RULES = [NUMERIC_AGE_RE, PREFIXED_AGE_RE];
13403
+ var IN_THEIR_TEENS_RE = /(?<![\w-])in[\s-]+their[\s-]+teens(?![\w-])/i;
13404
+ function containsMinorAgeHint(text) {
13405
+ if (typeof text !== "string" || text.trim().length === 0) return false;
13406
+ const re = minorNeedleRegex();
13407
+ if (re && re.test(text)) return true;
13408
+ for (const rule of AGE_NUMBER_RULES) {
13409
+ for (const m of text.matchAll(rule)) {
13410
+ const n = Number(m[1]);
13411
+ if (Number.isFinite(n) && n < 20) return true;
13412
+ }
13413
+ }
13414
+ return IN_THEIR_TEENS_RE.test(text);
13415
+ }
13416
+ var FLOORED_PICKER_KEYS = ["person", ...ADULT_SWEPT_CATALOG_IDS];
13417
+ function stripIds(obj, drop) {
13418
+ const out = {};
13419
+ for (const [k, v] of Object.entries(obj)) {
13420
+ if (typeof v === "string") {
13421
+ if (!drop.has(v)) out[k] = v;
13422
+ } else if (Array.isArray(v)) {
13423
+ const kept = v.filter((x) => !(typeof x === "string" && drop.has(x)));
13424
+ if (kept.length > 0) out[k] = kept;
13425
+ } else {
13426
+ out[k] = v;
13427
+ }
13428
+ }
13429
+ return out;
13430
+ }
13431
+ function applyMinorAgeFloorToPickerValues(values2) {
13432
+ const person = values2.person;
13433
+ if (!person || typeof person !== "object" || !isMinorAge(person)) return values2;
13434
+ const drop = getAdultOnlyIds();
13435
+ const out = { ...values2 };
13436
+ for (const key of FLOORED_PICKER_KEYS) {
13437
+ const v = out[key];
13438
+ if (v && typeof v === "object" && !Array.isArray(v)) out[key] = stripIds(v, drop);
13439
+ }
13440
+ return out;
13441
+ }
13442
+
13083
13443
  // ../../node_modules/zod/v4/classic/external.js
13084
13444
  var external_exports = {};
13085
13445
  __export(external_exports, {
@@ -13761,8 +14121,8 @@ function assert(_) {
13761
14121
  }
13762
14122
  function getEnumValues(entries) {
13763
14123
  const numericValues = Object.values(entries).filter((v) => typeof v === "number");
13764
- const values = Object.entries(entries).filter(([k, _]) => numericValues.indexOf(+k) === -1).map(([_, v]) => v);
13765
- return values;
14124
+ const values2 = Object.entries(entries).filter(([k, _]) => numericValues.indexOf(+k) === -1).map(([_, v]) => v);
14125
+ return values2;
13766
14126
  }
13767
14127
  function joinValues(array2, separator = "|") {
13768
14128
  return array2.map((val) => stringifyPrimitive(val)).join(separator);
@@ -16420,10 +16780,10 @@ var $ZodDiscriminatedUnion = /* @__PURE__ */ $constructor("$ZodDiscriminatedUnio
16420
16780
  const opts = def.options;
16421
16781
  const map2 = /* @__PURE__ */ new Map();
16422
16782
  for (const o of opts) {
16423
- const values = o._zod.propValues?.[def.discriminator];
16424
- if (!values || values.size === 0)
16783
+ const values2 = o._zod.propValues?.[def.discriminator];
16784
+ if (!values2 || values2.size === 0)
16425
16785
  throw new Error(`Invalid discriminated union option at index "${def.options.indexOf(o)}"`);
16426
- for (const v of values) {
16786
+ for (const v of values2) {
16427
16787
  if (map2.has(v)) {
16428
16788
  throw new Error(`Duplicate discriminator value "${String(v)}"`);
16429
16789
  }
@@ -16681,11 +17041,11 @@ var $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
16681
17041
  return payload;
16682
17042
  }
16683
17043
  const proms = [];
16684
- const values = def.keyType._zod.values;
16685
- if (values) {
17044
+ const values2 = def.keyType._zod.values;
17045
+ if (values2) {
16686
17046
  payload.value = {};
16687
17047
  const recordKeys = /* @__PURE__ */ new Set();
16688
- for (const key of values) {
17048
+ for (const key of values2) {
16689
17049
  if (typeof key === "string" || typeof key === "number" || typeof key === "symbol") {
16690
17050
  recordKeys.add(typeof key === "number" ? key.toString() : key);
16691
17051
  const keyResult = def.keyType._zod.run({ value: key, issues: [] }, ctx);
@@ -16889,10 +17249,10 @@ function handleSetResult(result, final) {
16889
17249
  }
16890
17250
  var $ZodEnum = /* @__PURE__ */ $constructor("$ZodEnum", (inst, def) => {
16891
17251
  $ZodType.init(inst, def);
16892
- const values = getEnumValues(def.entries);
16893
- const valuesSet = new Set(values);
17252
+ const values2 = getEnumValues(def.entries);
17253
+ const valuesSet = new Set(values2);
16894
17254
  inst._zod.values = valuesSet;
16895
- inst._zod.pattern = new RegExp(`^(${values.filter((k) => propertyKeyTypes.has(typeof k)).map((o) => typeof o === "string" ? escapeRegex(o) : o.toString()).join("|")})$`);
17255
+ inst._zod.pattern = new RegExp(`^(${values2.filter((k) => propertyKeyTypes.has(typeof k)).map((o) => typeof o === "string" ? escapeRegex(o) : o.toString()).join("|")})$`);
16896
17256
  inst._zod.parse = (payload, _ctx) => {
16897
17257
  const input = payload.value;
16898
17258
  if (valuesSet.has(input)) {
@@ -16900,7 +17260,7 @@ var $ZodEnum = /* @__PURE__ */ $constructor("$ZodEnum", (inst, def) => {
16900
17260
  }
16901
17261
  payload.issues.push({
16902
17262
  code: "invalid_value",
16903
- values,
17263
+ values: values2,
16904
17264
  input,
16905
17265
  inst
16906
17266
  });
@@ -16912,12 +17272,12 @@ var $ZodLiteral = /* @__PURE__ */ $constructor("$ZodLiteral", (inst, def) => {
16912
17272
  if (def.values.length === 0) {
16913
17273
  throw new Error("Cannot create literal schema with no valid values");
16914
17274
  }
16915
- const values = new Set(def.values);
16916
- inst._zod.values = values;
17275
+ const values2 = new Set(def.values);
17276
+ inst._zod.values = values2;
16917
17277
  inst._zod.pattern = new RegExp(`^(${def.values.map((o) => typeof o === "string" ? escapeRegex(o) : o ? escapeRegex(o.toString()) : String(o)).join("|")})$`);
16918
17278
  inst._zod.parse = (payload, _ctx) => {
16919
17279
  const input = payload.value;
16920
- if (values.has(input)) {
17280
+ if (values2.has(input)) {
16921
17281
  return payload;
16922
17282
  }
16923
17283
  payload.issues.push({
@@ -24218,8 +24578,8 @@ function _set(Class2, valueType, params) {
24218
24578
  });
24219
24579
  }
24220
24580
  // @__NO_SIDE_EFFECTS__
24221
- function _enum(Class2, values, params) {
24222
- const entries = Array.isArray(values) ? Object.fromEntries(values.map((v) => [v, v])) : values;
24581
+ function _enum(Class2, values2, params) {
24582
+ const entries = Array.isArray(values2) ? Object.fromEntries(values2.map((v) => [v, v])) : values2;
24223
24583
  return new Class2({
24224
24584
  type: "enum",
24225
24585
  entries,
@@ -24963,12 +25323,12 @@ var dateProcessor = (_schema, ctx, _json, _params) => {
24963
25323
  };
24964
25324
  var enumProcessor = (schema, _ctx, json2, _params) => {
24965
25325
  const def = schema._zod.def;
24966
- const values = getEnumValues(def.entries);
24967
- if (values.every((v) => typeof v === "number"))
25326
+ const values2 = getEnumValues(def.entries);
25327
+ if (values2.every((v) => typeof v === "number"))
24968
25328
  json2.type = "number";
24969
- if (values.every((v) => typeof v === "string"))
25329
+ if (values2.every((v) => typeof v === "string"))
24970
25330
  json2.type = "string";
24971
- json2.enum = values;
25331
+ json2.enum = values2;
24972
25332
  };
24973
25333
  var literalProcessor = (schema, ctx, json2, _params) => {
24974
25334
  const def = schema._zod.def;
@@ -26651,9 +27011,9 @@ var ZodEnum = /* @__PURE__ */ $constructor("ZodEnum", (inst, def) => {
26651
27011
  inst.enum = def.entries;
26652
27012
  inst.options = Object.values(def.entries);
26653
27013
  const keys = new Set(Object.keys(def.entries));
26654
- inst.extract = (values, params) => {
27014
+ inst.extract = (values2, params) => {
26655
27015
  const newEntries = {};
26656
- for (const value of values) {
27016
+ for (const value of values2) {
26657
27017
  if (keys.has(value)) {
26658
27018
  newEntries[value] = def.entries[value];
26659
27019
  } else
@@ -26666,9 +27026,9 @@ var ZodEnum = /* @__PURE__ */ $constructor("ZodEnum", (inst, def) => {
26666
27026
  entries: newEntries
26667
27027
  });
26668
27028
  };
26669
- inst.exclude = (values, params) => {
27029
+ inst.exclude = (values2, params) => {
26670
27030
  const newEntries = { ...def.entries };
26671
- for (const value of values) {
27031
+ for (const value of values2) {
26672
27032
  if (keys.has(value)) {
26673
27033
  delete newEntries[value];
26674
27034
  } else
@@ -26682,8 +27042,8 @@ var ZodEnum = /* @__PURE__ */ $constructor("ZodEnum", (inst, def) => {
26682
27042
  });
26683
27043
  };
26684
27044
  });
26685
- function _enum2(values, params) {
26686
- const entries = Array.isArray(values) ? Object.fromEntries(values.map((v) => [v, v])) : values;
27045
+ function _enum2(values2, params) {
27046
+ const entries = Array.isArray(values2) ? Object.fromEntries(values2.map((v) => [v, v])) : values2;
26687
27047
  return new ZodEnum({
26688
27048
  type: "enum",
26689
27049
  entries,
@@ -27597,6 +27957,13 @@ var personCleanup = (patch, mode) => {
27597
27957
  } else if ("age" in patch && patch.age !== "age-custom") {
27598
27958
  patch.customAge = void 0;
27599
27959
  }
27960
+ if (isMinorAge(patch)) {
27961
+ const drop = getAdultOnlyIds();
27962
+ for (const [k, v] of Object.entries(patch)) {
27963
+ if (typeof v === "string" && drop.has(v)) patch[k] = void 0;
27964
+ else if (Array.isArray(v)) patch[k] = v.filter((x) => !(typeof x === "string" && drop.has(x)));
27965
+ }
27966
+ }
27600
27967
  };
27601
27968
  var PICKER_ANALYZER_REGISTRY = {
27602
27969
  person: {
@@ -27862,6 +28229,23 @@ var GAPS_SCHEMA = external_exports.object({
27862
28229
  ).max(8).default([])
27863
28230
  }).default({ missingItems: [], missingCategories: [] });
27864
28231
  var MULTI_CACHE = /* @__PURE__ */ new Map();
28232
+ function pickerDisplayName(type) {
28233
+ return type.split("-").map((w) => w.length > 0 ? w[0].toUpperCase() + w.slice(1) : w).join(" ");
28234
+ }
28235
+ function buildOtherPickersLegend(sorted) {
28236
+ const otherTypes = PICKER_TYPES.filter((t) => !sorted.includes(t));
28237
+ if (otherTypes.length === 0) return "";
28238
+ const lines = otherTypes.map((type) => {
28239
+ const descriptor = PICKER_ANALYZER_REGISTRY[type];
28240
+ if (descriptor.kind === "flat") {
28241
+ return `- ${type}: ${descriptor.label}`;
28242
+ }
28243
+ const dims = descriptor.order.map((k) => descriptor.labels[k]).filter(Boolean).join(", ");
28244
+ return `- ${type}: ${pickerDisplayName(type)}${dims ? ` \u2014 ${dims}` : ""}`;
28245
+ });
28246
+ return `Non-wired pickers \u2014 use one of these keys in a gap's \`picker\` when an attribute belongs to it:
28247
+ ${lines.join("\n")}`;
28248
+ }
27865
28249
  function buildMultiPickerAnalyzerSpec(types) {
27866
28250
  const sorted = [...new Set(types)].sort();
27867
28251
  const key = sorted.join(",");
@@ -27879,7 +28263,8 @@ ${buildPickerLegend(spec)}`);
27879
28263
  const result = {
27880
28264
  schema: external_exports.object(shape).strict(),
27881
28265
  toolName: "emit_pickers",
27882
- legend: legendParts.join("\n\n")
28266
+ legend: legendParts.join("\n\n"),
28267
+ otherPickersLegend: buildOtherPickersLegend(sorted)
27883
28268
  };
27884
28269
  MULTI_CACHE.set(key, result);
27885
28270
  return result;
@@ -28285,8 +28670,8 @@ Sources: Google Cloud "Ultimate prompting guide for Veo 3.1"
28285
28670
  KIE VEO API docs (docs.kie.ai/veo3-api/generate-veo-3-video). Captured 2026-08-09.`
28286
28671
  };
28287
28672
  var GEMINI_OMNI_DOCTRINE = {
28288
- providers: ["gemini-omni-video"],
28289
- heading: "Gemini Omni Video (gemini-omni-video)",
28673
+ providers: ["gemini-omni-video", "gemini-omni-flash"],
28674
+ heading: "Gemini Omni (gemini-omni-video, gemini-omni-flash)",
28290
28675
  tips: [
28291
28676
  "Multimodal Google video with native audio: text-to-video, image-to-video, and video-edit through the same prompt surface. 4/6/8/10s; 720p/1080p or 4K tier.",
28292
28677
  "Structure like the platform default: subject \u2192 action \u2192 scene \u2192 lighting \u2192 camera \u2192 style. Quote dialogue lines to have them spoken; describe SFX/ambience plainly in the prompt.",
@@ -28308,6 +28693,7 @@ subject \u2192 action \u2192 scene/environment \u2192 lighting \u2192 camera mov
28308
28693
 
28309
28694
  **Duration & tiers**
28310
28695
  - 4 / 6 / 8 / 10 seconds. 720p/1080p tier or the pricier 4K tier \u2014 pick 4K only when the deliverable needs it (nearly 2\xD7 the credits).
28696
+ - gemini-omni-flash is the faster/cheaper tier with the identical request surface \u2014 same 4/6/8/10s, same 720p/1080p and 4K tiers, same video-edit path. Everything above applies verbatim.
28311
28697
 
28312
28698
  Source: KIE gemini-omni-video market contract (parameters + live behavior probed for the
28313
28699
  aspect-ratio hard-reject, see providers/kie/video.ts). Captured 2026-08-09.`
@@ -28377,6 +28763,45 @@ push-in (intimacy/tension), pull-out (scale/isolation), tracking shot, orbit, fi
28377
28763
  Source: Alibaba Cloud Model Studio \u2014 "Text-to-video / image-to-video prompt guide"
28378
28764
  (alibabacloud.com/help/en/model-studio/text-to-video-prompt). Captured 2026-08-09.`
28379
28765
  };
28766
+ var WAN_3_DOCTRINE = {
28767
+ providers: ["wan-3", "wan-3-prime"],
28768
+ heading: "Wan 3.0 (wan-3, wan-3-prime)",
28769
+ tips: [
28770
+ "Two INPUT MODES, exclusive on the wire: first/last frame, OR reference mode (images + videos + audio). With any reference wired the platform folds the frame into the references and names it in the prompt.",
28771
+ `References bind by ordinal token in array order: Image1, Image2, Video1, Audio1 \u2014 no space, unlike Wan 2.x's "Image 1". Name every wired asset or it may be ignored.`,
28772
+ "Reference caps: 10 images / 5 videos / 5 audio clips; each video and each audio clip 1-15s, with \u226415s combined per array. With reference videos, input seconds + output duration \u2264 30.",
28773
+ "2-30 seconds (default 5); 480p/720p/1080p; aspect adaptive (default, matches the input media) or 16:9 / 4:3 / 1:1 / 3:4 / 9:16. Prompt cap 20,000 chars \u2014 excess is truncated silently.",
28774
+ '`audio` is a boolean, ON by default: the clip comes back with an ambient/SFX track. Cue the sound you want in the prompt, or state the exclusion ("no music") \u2014 it is not a dialogue guarantee.',
28775
+ "wan-3-prime is the HIGH-SPEED tier: identical surface and limits, faster turnaround at a higher per-second rate. It is not a quality upgrade \u2014 choose it for latency, not for looks."
28776
+ ],
28777
+ doctrine: `Prompt structure (no public Wan 3.0 prompt guide exists \u2014 the KIE API contract is the
28778
+ doctrine source, like MiniMax H3 and HappyHorse; platform-standard structure applies):
28779
+ subject \u2192 action \u2192 scene/environment \u2192 lighting \u2192 camera movement \u2192 style \u2192 constraints.
28780
+
28781
+ **Modes (mutually exclusive at the provider)**
28782
+ - Frame mode: first_frame_url, optionally with last_frame_url, and NO references \u2014 the frames anchor the shot exactly, so describe MOTION and camera, not the still.
28783
+ - Reference mode: image / video / audio reference arrays. The provider CANNOT take these together with the first/last frame parameters, so when both are wired the platform folds \u2014 the frame is appended to the reference images (after the caller's own, ordinals unchanged) and bound in the prompt as the opening/closing frame. Write for reference mode whenever a reference is attached.
28784
+ - Text-only runs are supported and are the model's default mode.
28785
+
28786
+ **Reference binding**
28787
+ - Assets bind by ORDINAL TOKEN in array order: Image1, Image2, \u2026, Video1, \u2026, Audio1, \u2026. Note the format has NO space \u2014 Wan 2.x's "Image 1" is a different generation and does not apply here.
28788
+ - Write the binding into the prompt explicitly ("Image1 walks into the room described in Image2"); an unnamed reference may simply be ignored.
28789
+ - Caps: up to 10 images, 5 videos, 5 audio clips. Each video and each audio clip must be 1-15s with \u226415s combined per array. Audio should not be the only media input \u2014 pair it with an image or a video.
28790
+
28791
+ **Duration, resolution, aspect**
28792
+ - 2-30 seconds (provider default 5). With reference videos there is an extra ceiling: input video duration + output duration \u2264 30 seconds.
28793
+ - 480p / 720p / 1080p. Aspect "adaptive" (the default \u2014 the model selects the ratio from the input media and intent) or 16:9 / 4:3 / 1:1 / 3:4 / 9:16. There is no 21:9.
28794
+ - Prompts accept Chinese and English, up to 20,000 characters; anything beyond is truncated silently, so front-load the load-bearing content.
28795
+
28796
+ **Audio**
28797
+ - The "audio" boolean defaults ON and produces an ambient/SFX track with the clip. Describe the soundscape you want plainly ("rain on glass, distant traffic"), or state the exclusion, or turn the toggle off. The contract documents no lip-synced dialogue guarantee \u2014 plan spoken lines as a separate TTS + lip-sync pass.
28798
+
28799
+ **Tiers**
28800
+ - wan-3 and wan-3-prime take identical inputs. Prime trades a higher per-second rate for faster turnaround; it is not documented as a quality tier.
28801
+
28802
+ Source: KIE Wan 3.0 market contract (docs.kie.ai/market/wan/3-0-video,
28803
+ docs.kie.ai/market/wan/3-0-video-prime). Captured 2026-09-01.`
28804
+ };
28380
28805
  var HAPPYHORSE_DOCTRINE = {
28381
28806
  providers: ["happyhorse", "happyhorse-i2v", "happyhorse-ref2v", "happyhorse-edit"],
28382
28807
  heading: "HappyHorse 1.1 (happyhorse, happyhorse-i2v, happyhorse-ref2v)",
@@ -28432,6 +28857,7 @@ var PROVIDER_PROMPT_DOCTRINES = [
28432
28857
  GEMINI_OMNI_DOCTRINE,
28433
28858
  GROK_IMAGINE_DOCTRINE,
28434
28859
  WAN_DOCTRINE,
28860
+ WAN_3_DOCTRINE,
28435
28861
  HAPPYHORSE_DOCTRINE,
28436
28862
  RUNWAY_KIE_DOCTRINE
28437
28863
  ];
@@ -28656,6 +29082,9 @@ var PROVIDER_CAPABILITIES = {
28656
29082
  "ltx-2.3-pro": "Lightricks LTX 2.3 Pro \u2014 text/image/audio\u2192video, 6\u201310s, up to 4K",
28657
29083
  "ltx-2.3-fast": "Lightricks LTX 2.3 Fast \u2014 text/image\u2192video, 6\u201320s, up to 4K",
28658
29084
  "gemini-omni-video": "Google Gemini Omni \u2014 multimodal video with native audio, 4\u201310s, up to 4K.",
29085
+ "gemini-omni-flash": "Google Gemini Omni Flash \u2014 faster, cheaper Omni tier; multimodal video with native audio, 4\u201310s, up to 4K.",
29086
+ "wan-3": "Wan 3.0 \u2014 multimodal refs (10 images / 5 videos / 5 audio) or first+last frame, native audio, 2\u201330s, 480p/720p/1080p",
29087
+ "wan-3-prime": "Wan 3.0 Prime \u2014 high-speed Wan 3.0 tier; same surface, faster turnaround at a higher rate",
28659
29088
  "grok-imagine-video-1.5": "Grok Imagine 1.5 \u2014 image-to-video only; requires an input image"
28660
29089
  },
28661
29090
  "image-to-video": {
@@ -28689,6 +29118,9 @@ var PROVIDER_CAPABILITIES = {
28689
29118
  "ltx-2.3-pro": "Lightricks LTX 2.3 Pro \u2014 start/end frame i2v + audio\u2192video, 6\u201310s, up to 4K",
28690
29119
  "ltx-2.3-fast": "Lightricks LTX 2.3 Fast \u2014 start/end frame i2v, 6\u201320s, up to 4K",
28691
29120
  "gemini-omni-video": "Google Gemini Omni \u2014 multimodal video with native audio, 4\u201310s, up to 4K.",
29121
+ "gemini-omni-flash": "Google Gemini Omni Flash \u2014 faster, cheaper Omni tier; multimodal video with native audio, 4\u201310s, up to 4K.",
29122
+ "wan-3": "Wan 3.0 \u2014 multimodal refs (10 images / 5 videos / 5 audio) or first+last frame, native audio, 2\u201330s, 480p/720p/1080p",
29123
+ "wan-3-prime": "Wan 3.0 Prime \u2014 high-speed Wan 3.0 tier; same surface, faster turnaround at a higher rate",
28692
29124
  "grok-imagine-video-1.5": "Grok Imagine 1.5 \u2014 stylized animation, 1\u201315s, 480p/720p (image required)"
28693
29125
  },
28694
29126
  "video-to-video": {
@@ -34161,6 +34593,6 @@ function buildSurroundFillPrompt(direction, userPrompt) {
34161
34593
  return `${scene}This is a partial frame: the ${carried} portion contains real, finished pixels and the ${painted} portion is flat gray that MUST be painted in. Paint ONLY the ${painted} gray region as a natural, seamless continuation of the ${carried} portion \u2014 same scene, same perspective, continuing the horizon, geometry, and content across the boundary with no break. Keep the ${carried} portion completely unchanged. CRITICAL: do NOT change the lighting, exposure, white balance, or time of day. Match the ${carried} portion's EXACT light, color temperature, and contrast across the whole frame \u2014 if it is flat overcast daylight, keep flat overcast daylight. No golden hour, no sunset, no warm relight, no cinematic regrade. The seam between the ${carried} and ${painted} portions must be invisible. No people, no text, no labels, no watermarks.`;
34162
34594
  }
34163
34595
 
34164
- export { ACTION_FX, ACTION_FX_CATEGORY_LABELS, ACTION_FX_CATEGORY_ORDER, ACTION_FX_IDS, AESTHETICS, AESTHETIC_CATEGORY_LABELS, AESTHETIC_CATEGORY_ORDER, AESTHETIC_IDS, ALL_PICKER_WIRING, ANALYZABLE_PICKER_TYPES, ANGLE_LABELS, ASPECT_RATIO_LABELS, ATMOSPHERES, ATMOSPHERE_IDS, AUDIO_WIZARD_CATEGORIES, BACKDROPS, BACKDROP_CATEGORY_LABELS, BACKDROP_CATEGORY_ORDER, BACKDROP_IDS, BRAND_PRESETS, BRAND_PRESET_IDS, BRAND_PRESET_META, CAMERA_FORMATS, CAMERA_FORMAT_IDS, CAMERA_MOTIONS, CAMERA_MOTION_CATEGORY_LABELS, CAMERA_MOTION_CATEGORY_ORDER, CAMERA_MOTION_IDS, CHARACTER_FX, CHARACTER_FX_CATEGORY_LABELS, CHARACTER_FX_CATEGORY_ORDER, CHARACTER_FX_DURATIONS, CHARACTER_FX_IDS, CHARACTER_FX_INTENSITIES, CHARACTER_FX_POSITIONS, CINEMATIC_LOOK_TAIL, COLOR_LOOKS, COLOR_LOOK_CATEGORY_LABELS, COLOR_LOOK_CATEGORY_ORDER, COLOR_LOOK_IDS, COMPOSITION_EFFECTS, COMPOSITION_EFFECT_IDS, DEFAULT_IDENTITY_LOCK, DEFAULT_TEMPLATES, DIRECTION_ARRAY_CEILING, DIRECTION_FIELDS, DIRECTION_ID_MAX_CHARS, DIRECTION_KEYS, ERAS, ERA_CATEGORY_LABELS, ERA_CATEGORY_ORDER, ERA_IDS, EXPOSURE_CATEGORY_LABELS, EXPOSURE_CATEGORY_ORDER, EXPOSURE_FIELD_BY_CATEGORY, EXPOSURE_IDS, EXPOSURE_SETTINGS, FACTORY_PRESETS, FACTORY_SNIPPETS, FILM_STILL_PREFIX, FILM_STYLE_KEYS, FRAMINGS, FRAMING_CATEGORY_LABELS, FRAMING_CATEGORY_ORDER, FRAMING_FIELD_BY_CATEGORY, FRAMING_IDS, GAPS_SCHEMA, HELD_PROPS, HELD_PROP_CATEGORY_LABELS, HELD_PROP_CATEGORY_ORDER, HELD_PROP_IDS, IMAGE_HINT_MODE_DEFAULT, IMAGE_REFERENCE_PROMPT_DOCTRINE, IMAGE_WIZARD_CATEGORIES, INSTRUMENTATION_DEFAULT_DATA, INSTRUMENTS, INSTRUMENT_CATEGORY_LABELS, INSTRUMENT_CATEGORY_ORDER, LENSES, LENS_IDS, LIGHTINGS, LIGHTING_CATEGORY_LABELS, LIGHTING_CATEGORY_ORDER, LIGHTING_FIELD_BY_CATEGORY, LIGHTING_IDS, LLM_CHAT_WIZARD_CATEGORIES, LOOP_SUBJECTS, LOOP_SUBJECT_CATEGORY_LABELS, LOOP_SUBJECT_CATEGORY_ORDER, MATERIALS, MATERIAL_CATEGORY_LABELS, MATERIAL_CATEGORY_ORDER, MATERIAL_IDS, MAX_SELECTED_BY_DIMENSION, MAX_SELECTED_BY_FRAMING_CATEGORY, MAX_SELECTED_BY_STYLING_DIMENSION, MAX_SUBJECT_KEYS, MOODS, MOOD_CATEGORY_LABELS, MOOD_CATEGORY_ORDER, MOOD_IDS, MOVEMENT_LABELS, MULTI_PICKER_WIRING, MUSIC_EMOTIONS, MUSIC_ENERGIES, MUSIC_ERAS, MUSIC_GENRES, MUSIC_GENRE_CATEGORY_LABELS, MUSIC_GENRE_CATEGORY_ORDER, MUSIC_GENRE_DEFAULT_DATA, MUSIC_MOOD_DEFAULT_DATA, MUSIC_VIBES, MUSIC_WIZARD_CATEGORIES, NODE_PROMPT_CANDIDATE_FIELDS, NODE_PROMPT_FIELDS, OBJECT_ANGLE_PRESETS, OBJECT_ANGLE_PROMPTS, OBJECT_ASSET_PRESETS, OBJECT_ASSET_PROMPTS, OBJECT_MATERIAL_PRESETS, OBJECT_MATERIAL_PROMPTS, OBJECT_VARIATION_PRESETS, OBJECT_VARIATION_PROMPTS, PEOPLE, PERSON_DIMENSION_LABELS, PERSON_DIMENSION_ORDER, PERSON_DIMENSION_SECTIONS, PERSON_FIELD_BY_DIMENSION, PERSON_IDS, PHOTOGRAPHERS, PHOTOGRAPHER_CATEGORY_LABELS, PHOTOGRAPHER_CATEGORY_ORDER, PHOTOGRAPHER_IDS, PHOTO_GENRES, PHOTO_GENRE_CATEGORY_LABELS, PHOTO_GENRE_CATEGORY_ORDER, PHOTO_GENRE_IDS, PICKER_ANALYZER_FAMILIES, PICKER_ANALYZER_REGISTRY, PICKER_CATALOGS, PICKER_TYPES, POSES, POSE_CATEGORY_LABELS, POSE_CATEGORY_ORDER, POSE_IDS, POST_PROCESS_EFFECTS, POST_PROCESS_EFFECT_IDS, PRODUCTION_STYLES, PROMPT_AFFIX_CORE_FIELD_OVERRIDES, PROMPT_AFFIX_NODE_TYPES, PROMPT_HINT_SEPARATOR, PROVIDER_CAPABILITIES, PROVIDER_PROMPT_DOCTRINES, REFERENCE_IMAGE_ROLES, REFERENCE_RULES, REFERENCE_RULES_MULTI_PERSON, REF_BINDING, RENDER_QUALITIES, RENDER_QUALITY_IDS, SCENE_FRAME_RULE, SCENE_PROMPT_MAX_LENGTH, SETTINGS, SETTING_CATEGORY_LABELS, SETTING_IDS, SHOT_LABELS, SINGING_STYLES, SINGLE_PICKER_WIRING, SNIPPET_MEDIA_VALUES, STYLES, STYLE_IDS, STYLE_PRESETS, STYLE_SECTION_GAP, STYLE_SECTION_HEADER, STYLINGS, STYLING_DIMENSION_LABELS, STYLING_DIMENSION_ORDER, STYLING_FIELD_BY_DIMENSION, STYLING_IDS, SUBJECT_ARRAY_CEILING, SUBJECT_CUSTOM_AGE_KEY, SUBJECT_FIELDS, SUBJECT_FOLD_KEYS, SUBJECT_ID_MAX_CHARS, SUBJECT_IMAGE_HINT_MODE_DEFAULT, SUBJECT_KEYS, SUBJECT_KEY_MAX_CHARS, SUBJECT_VIDEO_HINT_MODE_DEFAULT, TEMPORALS, TEMPORAL_CATEGORY_LABELS, TEMPORAL_CATEGORY_ORDER, TEMPORAL_FIELD_BY_CATEGORY, TEMPORAL_IDS, TERM_MAX_CHARS, TEXT_WIZARD_CATEGORIES, TRANSITIONS, TRANSITION_CATEGORY_LABELS, TRANSITION_CATEGORY_ORDER, TRANSITION_DURATIONS, TRANSITION_IDS, TRANSITION_INTENSITIES, TRANSITION_POSITIONS, VIDEO_HINT_MODE_DEFAULT, VIDEO_WIZARD_CATEGORIES, VOCAL_PRESENCE, VOCAL_PRESENCE_INSTRUMENTAL_ID, VOICE_ACCENTS, VOICE_AGES, VOICE_ARCHETYPES, VOICE_CHARACTER_DEFAULT_DATA, VOICE_DELIVERY_DEFAULT_DATA, VOICE_EMOTIONS, VOICE_GENDERS, VOICE_LANGUAGES, VOICE_PACES, VOICE_TIMBRES, WARDROBE, WARDROBE_CATEGORY_LABELS, WARDROBE_DIMENSION_ORDER, WARDROBE_FIELD_BY_DIMENSION, appendField, appendMusicMeta, applyPickerJson, applyPromptAffixes, applyReferenceOrderToVideo, applyTemplate, asBodyClauses, assembleImageInput, assembleSunoInput, buildActionFxHints, buildActionFxTerms, buildAestheticHints, buildAestheticTerms, buildAgeHint, buildAtmosphereHints, buildAtmosphereTerms, buildCharacterPrompt, buildCreaturePrompt, buildExposureHints, buildExposureTerms, buildFaceTemplateInputs, buildFramingHints, buildFramingTerms, buildHeldPropHints, buildHeldPropTerms, buildIdentityDirectives, buildIdentityLockLine, buildImagePrompt, buildImagePromptSegments, buildImagePromptWithOverflow, buildInstrumentationHints, buildInstrumentationTerms, buildLightingHints, buildLightingTerms, buildLocationMotionPrompt, buildLocationPrompt, buildLocationRefinePrompt, buildMaterialHints, buildMaterialTerms, buildMoodHints, buildMoodTerms, buildMotionPrompt, buildMultiPickerAnalyzerSpec, buildMusicGenreHints, buildMusicGenreTerms, buildMusicMoodHints, buildMusicMoodTerms, buildObjectMotionPrompt, buildObjectPrompt, buildPersonHints, buildPersonTerms, buildPhotographerHints, buildPhotographerTerms, buildPickerAnalyzerSpec, buildPickerLegend, buildPickerZodSchema, buildPoseHints, buildPoseTerms, buildPostProcessHints, buildPostProcessTerms, buildReferenceBlocks, buildScenePrompt, buildStylingHints, buildStylingTerms, buildSurroundFillPrompt, buildTemporalHints, buildTemporalTerms, buildVoiceCharacterHints, buildVoiceCharacterTerms, buildVoiceDeliveryHints, buildVoiceDeliveryTerms, buildWardrobeHints, catalogPacksVersion, characterLockToRefLock, collectIdentityLockClause, composeCameraMotionHintFromConnections, composeCameraMotionTermFromConnections, composeCharacterFxHintFromConnections, composeNegative, composePickerCatalogs, composeSectionedPrompt, composeSoundHintFromConnections, composeTransitionHintFromConnections, composeVideoPromptText, computeLlmChatFields, computeNodePrompt, computePackSidecarCoverage, deriveTerm, directionFieldsForSurface, endsInsideStyleSection, expandImagePositionRefs, expandImageRefTokens, filmStillPrefix, getActionFx, getActionFxLabel, getActionFxPromptHint, getActionFxTerm, getAesthetic, getAestheticLabel, getAestheticPromptHint, getAestheticTerm, getAtmosphere, getAtmosphereLabel, getAtmospherePromptHint, getAtmosphereTerm, getBackdrop, getBackdropLabel, getBackdropPromptHint, getBackdropTerm, getCameraFormat, getCameraFormatLabel, getCameraFormatPromptHint, getCameraFormatTerm, getCameraMotion, getCameraMotionLabel, getCameraMotionPromptHint, getCameraMotionTerm, getCategoriesForNodeType, getCharacterFx, getCharacterFxLabel, getCharacterFxPromptHint, getCharacterFxTerm, getColorLook, getColorLookLabel, getColorLookPromptHint, getColorLookTerm, getCompositionEffect, getCompositionEffectLabel, getCompositionEffectPromptHint, getCompositionEffectTerm, getEffectiveSunoCustomMode, getEra, getEraLabel, getEraPromptHint, getEraTerm, getExposure, getExposureLabel, getExposurePromptHint, getExposureTerm, getFactoryPresets, getFactorySnippets, getFraming, getFramingCategoryLimit, getFramingLabel, getFramingPromptHint, getFramingTerm, getHeldProp, getHeldPropLabel, getHeldPropPromptHint, getHeldPropTerm, getIdentityLockClause, getInstrument, getInstrumentTerm, getLens, getLensLabel, getLensPromptHint, getLensTerm, getLighting, getLightingLabel, getLightingPromptHint, getLightingTerm, getLoopSubject, getLoopSubjectLabel, getLoopSubjectPromptHint, getLoopSubjectTerm, getMaterial, getMaterialLabel, getMaterialPromptHint, getMaterialTerm, getMood, getMoodLabel, getMoodPromptHint, getMoodTerm, getMusicEmotion, getMusicEmotionTerm, getMusicEnergy, getMusicEnergyTerm, getMusicEra, getMusicEraTerm, getMusicGenre, getMusicGenreLabel, getMusicGenreTerm, getMusicSubgenre, getMusicSubgenreTerm, getMusicVibe, getMusicVibeTerm, getParameterPromptHint, getPerson, getPersonDimensionLimit, getPersonLabel, getPersonPromptHint, getPersonTerm, getPhotoGenre, getPhotoGenreLabel, getPhotoGenrePromptHint, getPhotoGenreTerm, getPhotographer, getPhotographerLabel, getPhotographerPromptHint, getPhotographerTerm, getPickerAnalyzer, getPickerCatalog, getPickerWiring, getPose, getPoseLabel, getPosePromptHint, getPoseTerm, getPostProcessEffect, getPostProcessEffectLabel, getPostProcessEffectPromptHint, getPostProcessEffectTerm, getProductionStyle, getProductionStyleTerm, getPromptDoctrine, getPromptFields, getPromptTips, getRegisteredCatalogPacks, getRegisteredPeople, getRegisteredPersonDimensionLabels, getRegisteredPersonDimensionOrder, getRegisteredPersonFieldByDimension, getRegisteredPickerCatalogs, getRegisteredSubjectKeys, getRenderQuality, getRenderQualityLabel, getRenderQualityPromptHint, getRenderQualityTerm, getSetting, getSettingLabel, getSettingPromptHint, getSettingTerm, getSingingStyle, getSingingStyleTerm, getSnippetMedia, getStyle, getStyleLabel, getStylePreset, getStylePromptHint, getStyleTerm, getStyling, getStylingDimensionLimit, getStylingLabel, getStylingPromptHint, getStylingTerm, getTemporal, getTemporalLabel, getTemporalPromptHint, getTemporalTerm, getTransition, getTransitionLabel, getTransitionPromptHint, getTransitionTerm, getVocalPresence, getVocalPresenceTerm, getVoiceAccent, getVoiceAccentTerm, getVoiceAge, getVoiceAgeTerm, getVoiceArchetype, getVoiceArchetypeTerm, getVoiceEmotion, getVoiceEmotionTerm, getVoiceGender, getVoiceGenderTerm, getVoiceLanguage, getVoiceLanguageTerm, getVoicePace, getVoicePaceTerm, getVoiceTimbre, getVoiceTimbreTerm, getWardrobeEntriesByDimension, getWardrobeEntry, getWardrobePromptHint, groupFactoryPresets, hasUpstreamCharacter, identityRefsSentence, insertBeforeStyleSection, isAnalyzablePicker, isInstrumentalVocal, isSuspiciousDerivedTerm, isVantageFraming, isWizardSupported, joinPromptHints, joinPromptParts, keepableDirectionHints, listPickerCatalogs, migratePersonValue, modeForFamily, nodeHasInlinePrompt, nodeHasPromptField, nodeSupportsPromptAffixes, normalizeSubjectFields, partitionStyleClauses, personPacksVersion, pickerFanoutTargets, projectAllCatalogs, projectPickerCatalog, promptAffixCoreField, promptBindsFirstFrame, promptFieldCarriesAffixes, promptPartSeparator, readDirectionFields, readStructuredFields, readSubjectFields, referenceRulesBlock, registerCatalogPack, registerPersonPack, renderDirectionHintClauses, renderDirectionHints, renderStructuredFields, renderStyleSection, renderSubjectHints, resetCatalogPacks, resetPersonPacks, resolveBrandInput, resolveCharacterMentions, resolveGeminiOmniI2vInputs, resolveLocationMentions, resolvePrompt, resolveRefIdTokens, resolveReferenceTokens, resolveSeedance2Inputs, resolveTemplate, resolveTerm, resolveVeoI2vInputs, resolveVideoReferenceCore, sectionedClauseCosts, splitStyleSection, styleSectionFromClauses, styleSlotFor, subjectFieldsForSurface, summarizePickerCatalogs, toIdentityLockMode, truncateForField, truncateText, withForcedIdentityLock };
34596
+ export { ACTION_FX, ACTION_FX_CATEGORY_LABELS, ACTION_FX_CATEGORY_ORDER, ACTION_FX_IDS, ADULT_AGE_IDS, ADULT_ONLY_FLAG, ADULT_SWEPT_CATALOG_IDS, AESTHETICS, AESTHETIC_CATEGORY_LABELS, AESTHETIC_CATEGORY_ORDER, AESTHETIC_IDS, ALL_PICKER_WIRING, ANALYZABLE_PICKER_TYPES, ANGLE_LABELS, ASPECT_RATIO_LABELS, ATMOSPHERES, ATMOSPHERE_IDS, AUDIO_WIZARD_CATEGORIES, BACKDROPS, BACKDROP_CATEGORY_LABELS, BACKDROP_CATEGORY_ORDER, BACKDROP_IDS, BRAND_PRESETS, BRAND_PRESET_IDS, BRAND_PRESET_META, CAMERA_FORMATS, CAMERA_FORMAT_IDS, CAMERA_MOTIONS, CAMERA_MOTION_CATEGORY_LABELS, CAMERA_MOTION_CATEGORY_ORDER, CAMERA_MOTION_IDS, CHARACTER_FX, CHARACTER_FX_CATEGORY_LABELS, CHARACTER_FX_CATEGORY_ORDER, CHARACTER_FX_DURATIONS, CHARACTER_FX_IDS, CHARACTER_FX_INTENSITIES, CHARACTER_FX_POSITIONS, CINEMATIC_LOOK_TAIL, COLOR_LOOKS, COLOR_LOOK_CATEGORY_LABELS, COLOR_LOOK_CATEGORY_ORDER, COLOR_LOOK_IDS, COMPOSITION_EFFECTS, COMPOSITION_EFFECT_IDS, DEFAULT_IDENTITY_LOCK, DEFAULT_TEMPLATES, DIRECTION_ARRAY_CEILING, DIRECTION_FIELDS, DIRECTION_ID_MAX_CHARS, DIRECTION_KEYS, ERAS, ERA_CATEGORY_LABELS, ERA_CATEGORY_ORDER, ERA_IDS, EXPOSURE_CATEGORY_LABELS, EXPOSURE_CATEGORY_ORDER, EXPOSURE_FIELD_BY_CATEGORY, EXPOSURE_IDS, EXPOSURE_SETTINGS, FACTORY_PRESETS, FACTORY_SNIPPETS, FILM_STILL_PREFIX, FILM_STYLE_KEYS, FLOORED_PICKER_KEYS, FRAMINGS, FRAMING_CATEGORY_LABELS, FRAMING_CATEGORY_ORDER, FRAMING_FIELD_BY_CATEGORY, FRAMING_IDS, GAPS_SCHEMA, HELD_PROPS, HELD_PROP_CATEGORY_LABELS, HELD_PROP_CATEGORY_ORDER, HELD_PROP_IDS, IMAGE_HINT_MODE_DEFAULT, IMAGE_REFERENCE_PROMPT_DOCTRINE, IMAGE_WIZARD_CATEGORIES, INSTRUMENTATION_DEFAULT_DATA, INSTRUMENTS, INSTRUMENT_CATEGORY_LABELS, INSTRUMENT_CATEGORY_ORDER, LENSES, LENS_IDS, LIGHTINGS, LIGHTING_CATEGORY_LABELS, LIGHTING_CATEGORY_ORDER, LIGHTING_FIELD_BY_CATEGORY, LIGHTING_IDS, LLM_CHAT_WIZARD_CATEGORIES, LOOP_SUBJECTS, LOOP_SUBJECT_CATEGORY_LABELS, LOOP_SUBJECT_CATEGORY_ORDER, MATERIALS, MATERIAL_CATEGORY_LABELS, MATERIAL_CATEGORY_ORDER, MATERIAL_IDS, MAX_SELECTED_BY_DIMENSION, MAX_SELECTED_BY_FRAMING_CATEGORY, MAX_SELECTED_BY_STYLING_DIMENSION, MAX_SUBJECT_KEYS, MINOR_IMPLYING_TYPE_IDS, MOODS, MOOD_CATEGORY_LABELS, MOOD_CATEGORY_ORDER, MOOD_IDS, MOVEMENT_LABELS, MULTI_PICKER_WIRING, MUSIC_EMOTIONS, MUSIC_ENERGIES, MUSIC_ERAS, MUSIC_GENRES, MUSIC_GENRE_CATEGORY_LABELS, MUSIC_GENRE_CATEGORY_ORDER, MUSIC_GENRE_DEFAULT_DATA, MUSIC_MOOD_DEFAULT_DATA, MUSIC_VIBES, MUSIC_WIZARD_CATEGORIES, NODE_PROMPT_CANDIDATE_FIELDS, NODE_PROMPT_FIELDS, OBJECT_ANGLE_PRESETS, OBJECT_ANGLE_PROMPTS, OBJECT_ASSET_PRESETS, OBJECT_ASSET_PROMPTS, OBJECT_MATERIAL_PRESETS, OBJECT_MATERIAL_PROMPTS, OBJECT_VARIATION_PRESETS, OBJECT_VARIATION_PROMPTS, PEOPLE, PERSON_DIMENSION_LABELS, PERSON_DIMENSION_ORDER, PERSON_DIMENSION_SECTIONS, PERSON_FIELD_BY_DIMENSION, PERSON_IDS, PHOTOGRAPHERS, PHOTOGRAPHER_CATEGORY_LABELS, PHOTOGRAPHER_CATEGORY_ORDER, PHOTOGRAPHER_IDS, PHOTO_GENRES, PHOTO_GENRE_CATEGORY_LABELS, PHOTO_GENRE_CATEGORY_ORDER, PHOTO_GENRE_IDS, PICKER_ANALYZER_FAMILIES, PICKER_ANALYZER_REGISTRY, PICKER_CATALOGS, PICKER_TYPES, POSES, POSE_CATEGORY_LABELS, POSE_CATEGORY_ORDER, POSE_IDS, POST_PROCESS_EFFECTS, POST_PROCESS_EFFECT_IDS, PRODUCTION_STYLES, PROMPT_AFFIX_CORE_FIELD_OVERRIDES, PROMPT_AFFIX_NODE_TYPES, PROMPT_HINT_SEPARATOR, PROVIDER_CAPABILITIES, PROVIDER_PROMPT_DOCTRINES, REFERENCE_IMAGE_ROLES, REFERENCE_RULES, REFERENCE_RULES_MULTI_PERSON, REF_BINDING, RENDER_QUALITIES, RENDER_QUALITY_IDS, SCENE_FRAME_RULE, SCENE_PROMPT_MAX_LENGTH, SETTINGS, SETTING_CATEGORY_LABELS, SETTING_IDS, SHOT_LABELS, SINGING_STYLES, SINGLE_PICKER_WIRING, SNIPPET_MEDIA_VALUES, STYLES, STYLE_IDS, STYLE_PRESETS, STYLE_SECTION_GAP, STYLE_SECTION_HEADER, STYLINGS, STYLING_DIMENSION_LABELS, STYLING_DIMENSION_ORDER, STYLING_FIELD_BY_DIMENSION, STYLING_IDS, SUBJECT_ARRAY_CEILING, SUBJECT_CUSTOM_AGE_KEY, SUBJECT_FIELDS, SUBJECT_FOLD_KEYS, SUBJECT_ID_MAX_CHARS, SUBJECT_IMAGE_HINT_MODE_DEFAULT, SUBJECT_KEYS, SUBJECT_KEY_MAX_CHARS, SUBJECT_VIDEO_HINT_MODE_DEFAULT, TEMPORALS, TEMPORAL_CATEGORY_LABELS, TEMPORAL_CATEGORY_ORDER, TEMPORAL_FIELD_BY_CATEGORY, TEMPORAL_IDS, TERM_MAX_CHARS, TEXT_WIZARD_CATEGORIES, TRANSITIONS, TRANSITION_CATEGORY_LABELS, TRANSITION_CATEGORY_ORDER, TRANSITION_DURATIONS, TRANSITION_IDS, TRANSITION_INTENSITIES, TRANSITION_POSITIONS, VIDEO_HINT_MODE_DEFAULT, VIDEO_WIZARD_CATEGORIES, VOCAL_PRESENCE, VOCAL_PRESENCE_INSTRUMENTAL_ID, VOICE_ACCENTS, VOICE_AGES, VOICE_ARCHETYPES, VOICE_CHARACTER_DEFAULT_DATA, VOICE_DELIVERY_DEFAULT_DATA, VOICE_EMOTIONS, VOICE_GENDERS, VOICE_LANGUAGES, VOICE_PACES, VOICE_TIMBRES, WARDROBE, WARDROBE_CATEGORY_LABELS, WARDROBE_DIMENSION_ORDER, WARDROBE_FIELD_BY_DIMENSION, __resetCatalogIdGuardForTests, appendField, appendMusicMeta, applyMinorAgeFloorToPickerValues, applyPickerJson, applyPromptAffixes, applyReferenceOrderToVideo, applyTemplate, asBodyClauses, assembleImageInput, assembleSunoInput, buildActionFxHints, buildActionFxTerms, buildAestheticHints, buildAestheticTerms, buildAgeHint, buildAtmosphereHints, buildAtmosphereTerms, buildCharacterPrompt, buildCreaturePrompt, buildExposureHints, buildExposureTerms, buildFaceTemplateInputs, buildFramingHints, buildFramingTerms, buildHeldPropHints, buildHeldPropTerms, buildIdentityDirectives, buildIdentityLockLine, buildImagePrompt, buildImagePromptSegments, buildImagePromptWithOverflow, buildInstrumentationHints, buildInstrumentationTerms, buildLightingHints, buildLightingTerms, buildLocationMotionPrompt, buildLocationPrompt, buildLocationRefinePrompt, buildMaterialHints, buildMaterialTerms, buildMoodHints, buildMoodTerms, buildMotionPrompt, buildMultiPickerAnalyzerSpec, buildMusicGenreHints, buildMusicGenreTerms, buildMusicMoodHints, buildMusicMoodTerms, buildNeedleAlternationSource, buildObjectMotionPrompt, buildObjectPrompt, buildPersonHints, buildPersonTerms, buildPhotographerHints, buildPhotographerTerms, buildPickerAnalyzerSpec, buildPickerLegend, buildPickerZodSchema, buildPoseHints, buildPoseTerms, buildPostProcessHints, buildPostProcessTerms, buildReferenceBlocks, buildScenePrompt, buildStylingHints, buildStylingTerms, buildSurroundFillPrompt, buildTemporalHints, buildTemporalTerms, buildVoiceCharacterHints, buildVoiceCharacterTerms, buildVoiceDeliveryHints, buildVoiceDeliveryTerms, buildWardrobeHints, catalogGuardActive, catalogPacksVersion, characterLockToRefLock, collectIdentityLockClause, composeCameraMotionHintFromConnections, composeCameraMotionTermFromConnections, composeCharacterFxHintFromConnections, composeNegative, composePickerCatalogs, composeSectionedPrompt, composeSoundHintFromConnections, composeTransitionHintFromConnections, composeVideoPromptText, composedHas, composedOption, composedOptionIndex, computeLlmChatFields, computeNodePrompt, computePackSidecarCoverage, containsMinorAgeHint, curateEntries, curatedAnimalPromptHint, curatedAnimalTerm, curatedFurnitureText, curatedVehicleText, curatedWeaponText, deriveTerm, directionFieldsForSurface, endsInsideStyleSection, expandImagePositionRefs, expandImageRefTokens, filmStillPrefix, findForeignCatalogIds, findForeignCatalogIdsInBody, foreignCatalogIdMessage, getActionFx, getActionFxLabel, getActionFxPromptHint, getActionFxTerm, getAdultOnlyEntries, getAdultOnlyHintStrings, getAdultOnlyIds, getAesthetic, getAestheticLabel, getAestheticPromptHint, getAestheticTerm, getAtmosphere, getAtmosphereLabel, getAtmospherePromptHint, getAtmosphereTerm, getBackdrop, getBackdropLabel, getBackdropPromptHint, getBackdropTerm, getCameraFormat, getCameraFormatLabel, getCameraFormatPromptHint, getCameraFormatTerm, getCameraMotion, getCameraMotionLabel, getCameraMotionPromptHint, getCameraMotionTerm, getCategoriesForNodeType, getCharacterFx, getCharacterFxLabel, getCharacterFxPromptHint, getCharacterFxTerm, getColorLook, getColorLookLabel, getColorLookPromptHint, getColorLookTerm, getCompositionEffect, getCompositionEffectLabel, getCompositionEffectPromptHint, getCompositionEffectTerm, getEffectiveSunoCustomMode, getEra, getEraLabel, getEraPromptHint, getEraTerm, getExposure, getExposureLabel, getExposurePromptHint, getExposureTerm, getFactoryPresets, getFactorySnippets, getFraming, getFramingCategoryLimit, getFramingLabel, getFramingPromptHint, getFramingTerm, getHeldProp, getHeldPropLabel, getHeldPropPromptHint, getHeldPropTerm, getIdentityLockClause, getInstrument, getInstrumentTerm, getLens, getLensLabel, getLensPromptHint, getLensTerm, getLighting, getLightingLabel, getLightingPromptHint, getLightingTerm, getLoopSubject, getLoopSubjectLabel, getLoopSubjectPromptHint, getLoopSubjectTerm, getMaterial, getMaterialLabel, getMaterialPromptHint, getMaterialTerm, getMinorAgeHintStrings, getMood, getMoodLabel, getMoodPromptHint, getMoodTerm, getMusicEmotion, getMusicEmotionTerm, getMusicEnergy, getMusicEnergyTerm, getMusicEra, getMusicEraTerm, getMusicGenre, getMusicGenreLabel, getMusicGenreTerm, getMusicSubgenre, getMusicSubgenreTerm, getMusicVibe, getMusicVibeTerm, getParameterPromptHint, getPerson, getPersonDimensionLimit, getPersonLabel, getPersonPromptHint, getPersonTerm, getPhotoGenre, getPhotoGenreLabel, getPhotoGenrePromptHint, getPhotoGenreTerm, getPhotographer, getPhotographerLabel, getPhotographerPromptHint, getPhotographerTerm, getPickerAnalyzer, getPickerCatalog, getPickerWiring, getPose, getPoseLabel, getPosePromptHint, getPoseTerm, getPostProcessEffect, getPostProcessEffectLabel, getPostProcessEffectPromptHint, getPostProcessEffectTerm, getProductionStyle, getProductionStyleTerm, getPromptDoctrine, getPromptFields, getPromptTips, getRegisteredCatalogPacks, getRegisteredPeople, getRegisteredPersonDimensionLabels, getRegisteredPersonDimensionOrder, getRegisteredPersonFieldByDimension, getRegisteredPickerCatalogs, getRegisteredSubjectKeys, getRenderQuality, getRenderQualityLabel, getRenderQualityPromptHint, getRenderQualityTerm, getSetting, getSettingLabel, getSettingPromptHint, getSettingTerm, getSingingStyle, getSingingStyleTerm, getSnippetMedia, getStyle, getStyleLabel, getStylePreset, getStylePromptHint, getStyleTerm, getStyling, getStylingDimensionLimit, getStylingLabel, getStylingPromptHint, getStylingTerm, getTemporal, getTemporalLabel, getTemporalPromptHint, getTemporalTerm, getTransition, getTransitionLabel, getTransitionPromptHint, getTransitionTerm, getVocalPresence, getVocalPresenceTerm, getVoiceAccent, getVoiceAccentTerm, getVoiceAge, getVoiceAgeTerm, getVoiceArchetype, getVoiceArchetypeTerm, getVoiceEmotion, getVoiceEmotionTerm, getVoiceGender, getVoiceGenderTerm, getVoiceLanguage, getVoiceLanguageTerm, getVoicePace, getVoicePaceTerm, getVoiceTimbre, getVoiceTimbreTerm, getWardrobeEntriesByDimension, getWardrobeEntry, getWardrobePromptHint, groupFactoryPresets, hasCatalogPacksFor, hasUpstreamCharacter, identityRefsSentence, insertBeforeStyleSection, isAnalyzablePicker, isDeniedStyleId, isInstrumentalVocal, isMinorAge, isSuspiciousDerivedTerm, isVantageFraming, isWizardSupported, joinPromptHints, joinPromptParts, keepableDirectionHints, listPickerCatalogs, migratePersonValue, modeForFamily, nodeHasInlinePrompt, nodeHasPromptField, nodeSupportsPromptAffixes, normalizeSubjectFields, overlayEntry, partitionStyleClauses, personPacksVersion, pickerFanoutTargets, projectAllCatalogs, projectPickerCatalog, promptAffixCoreField, promptBindsFirstFrame, promptFieldCarriesAffixes, promptPartSeparator, readDirectionFields, readStructuredFields, readSubjectFields, referenceRulesBlock, registerCatalogPack, registerPersonPack, renderDirectionHintClauses, renderDirectionHints, renderStructuredFields, renderStyleSection, renderSubjectHints, resetCatalogPacks, resetPersonPacks, resolveBrandInput, resolveCharacterMentions, resolveGeminiOmniI2vInputs, resolveLocationMentions, resolvePrompt, resolveRefIdTokens, resolveReferenceTokens, resolveSeedance2Inputs, resolveTemplate, resolveTerm, resolveVeoI2vInputs, resolveVideoReferenceCore, sectionedClauseCosts, setComposedCatalogResolver, splitStyleSection, styleSectionFromClauses, styleSlotFor, subjectFieldsForSurface, subscribeCatalogPacks, summarizePickerCatalogs, toIdentityLockMode, truncateForField, truncateText, withForcedIdentityLock };
34165
34597
  //# sourceMappingURL=index.js.map
34166
34598
  //# sourceMappingURL=index.js.map