// bracelet.jsx - crystal alchemy bracelet builder for Azoth AI const BRACELET_STONES = { black_tourmaline: { name: "Чорний турмалін", colors: ["#171313", "#32281d"], element: "Земля", planets: "Сатурн / Марс", tags: ["protection", "grounding", "boundary", "shadow"], power: "захисний контур, заземлення, відсікання шуму", caution: "не замінює реальний захист і тверезі рішення", }, obsidian: { name: "Обсидіан", colors: ["#0a0909", "#2a211e"], element: "Вогонь землі", planets: "Плутон / Сатурн", tags: ["truth", "shadow", "cut", "protection"], power: "жорстка правда, тіньова робота, зріз ілюзій", caution: "для чутливих клієнтів вводити м'яко, не як фатальний камінь", }, smoky_quartz: { name: "Димчастий кварц", colors: ["#4a3b32", "#8a735c"], element: "Земля / Повітря", planets: "Сатурн", tags: ["grounding", "calm", "shadow", "release"], power: "зняття надлишкового напруження, тверезість, стабілізація", caution: "уникати медичних обіцянок", }, hematite: { name: "Гематит", colors: ["#272523", "#7f776d"], element: "Земля / Залізо", planets: "Марс / Сатурн", tags: ["will", "body", "grounding", "discipline"], power: "тілесна опора, воля, дисципліна, межа", caution: "важкий металевий акцент; не для легкого романтичного браслета", }, amethyst: { name: "Аметист", colors: ["#4a2f63", "#9a73bd"], element: "Повітря / Дух", planets: "Юпітер / Нептун", tags: ["intuition", "clarity", "dream", "spiritual"], power: "інтуїтивна чистота, сон, спостереження, духовна тверезість", caution: "тривале сонце може бліднити колір", }, clear_quartz: { name: "Гірський кришталь", colors: ["#d9d2c3", "#fff8e2"], element: "Дух", planets: "Сонце / Меркурій", tags: ["amplifier", "clarity", "focus", "programming"], power: "підсилювач формули, ясність наміру, провідник", caution: "підсилює хаос так само, як ясність; потребує чіткої програми", }, rose_quartz: { name: "Рожевий кварц", colors: ["#c78b91", "#f0c2c0"], element: "Вода", planets: "Венера", tags: ["love", "heart", "softness", "repair"], power: "серцеве пом'якшення, довіра, ніжність, відновлення контакту", caution: "не використовувати для прив'язки іншої людини", }, citrine: { name: "Цитрин", colors: ["#b67818", "#f0c25a"], element: "Вогонь", planets: "Сонце / Юпітер", tags: ["money", "confidence", "visibility", "joy"], power: "сонячна впевненість, гроші, видимість, радісна дія", caution: "на ринку багато термообробленого аметисту; це не скасовує символ, але важливо для якості", }, tiger_eye: { name: "Тигрове око", colors: ["#5a3516", "#d3a12f"], element: "Вогонь / Земля", planets: "Сонце / Марс", tags: ["confidence", "money", "courage", "focus"], power: "сміливість, фокус, ділова хватка, тепла оборона", caution: "не робити надто агресивним для вже різких клієнтів", }, labradorite: { name: "Лабрадорит", colors: ["#263a3d", "#4f877e"], element: "Вода / Ефір", planets: "Місяць / Уран", tags: ["intuition", "magic", "protection", "liminal"], power: "окультний щит, бачення межі, робота з переходами", caution: "берегти від ударів; перелив залежить від якості намистин", }, moonstone: { name: "Місячний камінь", colors: ["#d8d4c6", "#8ca2b0"], element: "Вода", planets: "Місяць", tags: ["cycles", "intuition", "feminine", "calm"], power: "цикли, рецептивність, сон, м'яке інтуїтивне поле", caution: "м'який камінь; не перевантажувати жорсткою щоденною носкою", }, garnet: { name: "Гранат", colors: ["#4b1018", "#9b2234"], element: "Вогонь / Кров", planets: "Марс / Плутон", tags: ["vitality", "passion", "will", "body"], power: "життєва сила, пристрасть, витривалість, повернення в тіло", caution: "не подавати як лікування крові або тіла", }, carnelian: { name: "Сердолік", colors: ["#a33d1f", "#e68a3a"], element: "Вогонь", planets: "Марс / Сонце", tags: ["action", "creativity", "sexuality", "confidence"], power: "запуск дії, творчість, голос тіла, сміливість", caution: "може бути занадто гарячим для нервового запиту", }, lapis: { name: "Лазурит", colors: ["#142b6f", "#c8a85a"], element: "Повітря / Небо", planets: "Юпітер / Сатурн", tags: ["wisdom", "authority", "truth", "voice"], power: "мудрість, авторитет, правдива мова, вчительська позиція", caution: "часто фарбований або реконструйований; перевіряти якість", }, sodalite: { name: "Содаліт", colors: ["#243f7b", "#eef0df"], element: "Повітря", planets: "Меркурій", tags: ["logic", "speech", "calm", "study"], power: "логіка, холодна мова, навчання, порядок думки", caution: "не робити головним у серцевих запитах", }, amazonite: { name: "Амазоніт", colors: ["#4c9f93", "#8ed1bf"], element: "Вода / Повітря", planets: "Меркурій / Венера", tags: ["speech", "boundary", "calm", "heart"], power: "спокійна комунікація, м'яка межа, голос без конфлікту", caution: "берегти від агресивної хімії", }, malachite: { name: "Малахіт", colors: ["#143d24", "#27a45f"], element: "Земля / Венера", planets: "Венера / Плутон", tags: ["transformation", "heart", "money", "shadow"], power: "серцева трансформація, сильне поле змін, ресурс", caution: "не класти у воду/сіль, пил малахіту токсичний; тільки готові поліровані намистини", }, aventurine: { name: "Зелений авантюрин", colors: ["#3f7b48", "#9bc783"], element: "Земля / Серце", planets: "Венера / Юпітер", tags: ["luck", "money", "heart", "growth"], power: "м'яка удача, ріст, серцева підприємливість", caution: "не замінює дії та фінансовий розрахунок", }, pyrite: { name: "Пірит", colors: ["#6d5626", "#d4aa3c"], element: "Вогонь / Метал", planets: "Сонце / Марс", tags: ["money", "protection", "will", "status"], power: "грошовий щит, статус, сонячна самоповага, дія", caution: "не мочити; може тьмяніти і окиснюватися", }, sapphire: { name: "Сапфір", colors: ["#061639", "#1f5ab5"], element: "Небо / Закон", planets: "Сатурн / Юпітер", tags: ["authority", "wealth", "strategy", "status"], power: "імператорська стратегія, статус, холодна влада, фінансова дисципліна", caution: "для дорогого браслета перевіряти походження, огранку, сертифікат і сумісність з металом", }, ruby: { name: "Рубін", colors: ["#4d0614", "#d7283d"], element: "Вогонь / Кров", planets: "Сонце / Марс", tags: ["will", "wealth", "command", "vitality"], power: "воля переможця, наказова сила, сміливий матеріальний рух", caution: "сильний гарячий камінь; у формулі має бути заземлення і межа", }, emerald: { name: "Смарагд", colors: ["#06381e", "#34a95a"], element: "Земля / Венера", planets: "Венера / Меркурій", tags: ["wealth", "growth", "networks", "value"], power: "ріст капіталу, угоди, цінність, притягання правильних союзів", caution: "крихкий у ювелірній роботі; краще брати захищену посадку або вставки-акценти", }, diamond: { name: "Діамант", colors: ["#d7d2c5", "#ffffff"], element: "Дух / Сонце", planets: "Сонце / Венера", tags: ["amplifier", "clarity", "status", "seal"], power: "незламна печать наміру, ясність, преміальний статус, фіксація формули", caution: "не є обов'язковим для сили формули; якщо бюджет менший, замінити чистим гірським кришталем", }, onyx: { name: "Онікс", colors: ["#070707", "#1d1a17"], element: "Земля", planets: "Сатурн", tags: ["discipline", "protection", "boundary", "focus"], power: "сухий захист, самоконтроль, межа, витримка", caution: "у слабкому емоційному стані може здаватися холодним", }, jasper: { name: "Яшма", colors: ["#7b2d22", "#c1844a"], element: "Земля", planets: "Земля / Марс", tags: ["endurance", "body", "grounding", "support"], power: "витривалість, щоденна опора, стабільна земля", caution: "добре як база, слабше як єдиний магічний акцент", }, aquamarine: { name: "Аквамарин", colors: ["#7fb7bd", "#c5e7df"], element: "Вода / Повітря", planets: "Місяць / Меркурій", tags: ["speech", "calm", "travel", "clarity"], power: "чиста мова, спокій, переговори, легкість дихання символічно", caution: "не подавати як лікування горла або дихання", }, }; const BRACELET_PROFILES = [ { id: "imperial_money", name: "Імператорський матеріальний шлях і фінансова влада", rx: /(імператор|император|влада|власть|фінансов[а-яіїєґё\s-]{0,28}(систем|імпер|импер|влад)|финансов[а-яіїєґё\s-]{0,28}(систем|импер|влад)|систем[а-яіїєґё\s-]{0,28}(фінансов|финансов|влад)|статус[а-яіїєґё\s-]{0,28}(влад|систем|імпер|импер))/i, stones: ["sapphire", "diamond", "ruby", "emerald", "pyrite", "citrine", "lapis", "tiger_eye"] }, { id: "breakthrough", name: "Прорив блоків і прискорення шляху", rx: /(злам|слом|ч[іи]ткод|швидш|ускор|пришвид|прорив|прорыв|бар'єр|барьер|блок|перешкод|препятств|відкрити шлях|открыть путь|шлях|путь)/i, stones: ["obsidian", "black_tourmaline", "hematite", "garnet", "tiger_eye", "clear_quartz", "pyrite", "ruby"] }, { id: "protection", name: "Захист і межа", rx: /(захист|защит|оберіг|оберег|негатив|сглаз|пристр|порч|атака|ворог|страх|тінь|тень)/i, stones: ["black_tourmaline", "obsidian", "smoky_quartz", "hematite", "labradorite", "onyx"] }, { id: "money", name: "Удача, багатство і проявленість", rx: /(грош|деньг|money|бізнес|бизнес|кар'єр|карьер|успіх|успех|статус|продаж|клієнт|клиент|матеріальн|материальн|капітал|капитал|багат|богат|wealth|abundance|удач|тал[іи]сман|дорог|преміум|премиум|ювелір|ювелир|справжн|настоящ)/i, stones: ["citrine", "pyrite", "tiger_eye", "aventurine", "emerald", "lapis", "hematite"] }, { id: "love", name: "Серце і стосунки", rx: /(любов|отнош|стосунк|серц|пара|шлюб|брак|примир|довіра|довер|самоцін|самоцен)/i, stones: ["rose_quartz", "moonstone", "amazonite", "aventurine", "malachite", "amethyst"] }, { id: "intuition", name: "Інтуїція і окультний зір", rx: /(інтуїц|интуиц|бач|видін|сновид|сон|таро|магі|магич|канал|медіум|медиум|практик|езотер)/i, stones: ["amethyst", "labradorite", "moonstone", "clear_quartz", "lapis", "black_tourmaline"] }, { id: "confidence", name: "Воля і впевненість", rx: /(уверенн|впевнен|воля|смілив|смел|харизм|страх сцени|голос|дія|действ)/i, stones: ["tiger_eye", "carnelian", "citrine", "garnet", "hematite", "clear_quartz"] }, { id: "calm", name: "Спокій і збирання нервової сили", rx: /(спок|спокой|тривог|тревог|стрес|сон|нерв|гармон|баланс|панік|паник)/i, stones: ["smoky_quartz", "amethyst", "amazonite", "sodalite", "moonstone", "aquamarine"] }, { id: "truth", name: "Правда і рішення", rx: /(правд|рішення|решени|ясн|вибір|выбор|обман|иллюз|ілюз|розібратись|разобрат)/i, stones: ["obsidian", "lapis", "sodalite", "clear_quartz", "smoky_quartz", "hematite"] }, { id: "vitality", name: "Життєва сила", rx: /(енергі|энерг|сила|ресурс|витривал|устал|втом|апат|сексуальн|тіло|тело)/i, stones: ["garnet", "carnelian", "jasper", "citrine", "hematite", "tiger_eye"] }, ]; const BRACELET_ZODIAC = [ ["Козоріг", 120, ["onyx", "hematite", "smoky_quartz"]], ["Водолій", 219, ["amethyst", "labradorite", "aquamarine"]], ["Риби", 320, ["amethyst", "moonstone", "aquamarine"]], ["Овен", 421, ["carnelian", "garnet", "hematite"]], ["Телець", 521, ["rose_quartz", "aventurine", "malachite"]], ["Близнюки", 621, ["sodalite", "amazonite", "clear_quartz"]], ["Рак", 722, ["moonstone", "rose_quartz", "aquamarine"]], ["Лев", 823, ["citrine", "tiger_eye", "pyrite"]], ["Діва", 923, ["sodalite", "jasper", "clear_quartz"]], ["Терези", 1023, ["rose_quartz", "lapis", "amazonite"]], ["Скорпіон", 1122, ["obsidian", "garnet", "malachite"]], ["Стрілець", 1222, ["lapis", "amethyst", "citrine"]], ["Козоріг", 1231, ["onyx", "hematite", "smoky_quartz"]], ]; const BRACELET_LIFE_PATH = { 1: ["carnelian", "citrine"], 2: ["moonstone", "rose_quartz"], 3: ["citrine", "amazonite"], 4: ["hematite", "jasper"], 5: ["tiger_eye", "aquamarine"], 6: ["rose_quartz", "aventurine"], 7: ["amethyst", "labradorite"], 8: ["pyrite", "onyx"], 9: ["lapis", "smoky_quartz"], 11: ["clear_quartz", "amethyst"], 22: ["hematite", "clear_quartz"], }; const BRACELET_ARCANA_STONES = { 1: ["clear_quartz", "carnelian"], 2: ["moonstone", "amethyst"], 3: ["rose_quartz", "malachite"], 4: ["hematite", "onyx"], 5: ["lapis", "sodalite"], 6: ["rose_quartz", "amazonite"], 7: ["tiger_eye", "hematite"], 8: ["onyx", "clear_quartz"], 9: ["amethyst", "smoky_quartz"], 10: ["citrine", "aventurine"], 11: ["garnet", "tiger_eye"], 12: ["amethyst", "moonstone"], 13: ["obsidian", "smoky_quartz"], 14: ["aquamarine", "clear_quartz"], 15: ["black_tourmaline", "obsidian"], 16: ["hematite", "black_tourmaline"], 17: ["labradorite", "amethyst"], 18: ["moonstone", "labradorite"], 19: ["citrine", "pyrite"], 20: ["lapis", "clear_quartz"], 21: ["aventurine", "clear_quartz"], 22: ["aquamarine", "labradorite"], }; function braceletBirthDate(text) { if (window.extractBirthDate) return window.extractBirthDate(text); const match = String(text || "").match(/\b(\d{1,2})[.\-/](\d{1,2})[.\-/](\d{4})\b/); if (!match) return null; return { day: Number(match[1]), month: Number(match[2]), year: Number(match[3]) }; } function braceletDateString(date) { if (!date) return "без дати"; return `${String(date.day).padStart(2, "0")}.${String(date.month).padStart(2, "0")}.${date.year}`; } function braceletDigitSum(value) { return String(Math.abs(Number(value) || 0)).split("").reduce((sum, ch) => sum + Number(ch), 0); } function braceletLifePath(date) { if (!date) return null; let n = braceletDigitSum(`${date.day}${date.month}${date.year}`); while (![11, 22].includes(n) && n > 9) n = braceletDigitSum(n); return n || null; } function braceletSunSign(date) { if (!date) return null; const md = date.month * 100 + date.day; const row = BRACELET_ZODIAC.find(item => md <= item[1]); return row ? { name: row[0], stones: row[2] } : null; } function braceletAddScore(scores, id, points, reason) { if (!BRACELET_STONES[id]) return; const current = scores[id] || { id, score: 0, reasons: [] }; current.score += points; if (reason && !current.reasons.includes(reason)) current.reasons.push(reason); scores[id] = current; } function braceletIntent(text) { const q = String(text || ""); const matches = BRACELET_PROFILES.filter(profile => profile.rx.test(q)); if (matches.length) return matches.slice(0, 3); return [ { id: "integrative", name: "Підсвітити сильні сторони і прикрити слабкі місця", stones: ["clear_quartz", "amethyst", "tiger_eye", "black_tourmaline", "rose_quartz", "hematite"], }, ]; } function braceletTitleForProfiles(profiles = []) { const ids = profiles.map(profile => (typeof profile === "string" ? profile : profile.id)).filter(Boolean); if (ids.includes("imperial_money")) return "Імператорський алхімічний браслет"; if (ids.includes("money") && ids.includes("breakthrough")) return "Алхімічний браслет достатку і прориву"; if (ids.includes("money")) return "Алхімічний браслет удачі та багатства"; if (ids.includes("breakthrough")) return "Алхімічний браслет прориву"; if (ids.includes("protection")) return "Алхімічний браслет захисту"; if (ids.includes("love")) return "Алхімічний браслет серця"; if (ids.includes("intuition")) return "Алхімічний браслет окультного зору"; return "Алхімічний браслет"; } function braceletHardwareConfig(profiles = [], primaryProfile = "", options = {}) { const ids = profiles.map(profile => (typeof profile === "string" ? profile : profile.id)).filter(Boolean); const isImperial = options.isImperial || ids.includes("imperial_money"); const isBreakthrough = options.isBreakthrough || ids.includes("breakthrough"); const isLove = primaryProfile === "love" || ids.includes("love"); const isMoney = primaryProfile === "money" || ids.includes("money"); if (isImperial) { return { threadMaterial: "чорний шовковий шнур поверх ювелірного тросика", threadColor: "#19120a", metalTone: "жовте золото", metalColor: "#d8ae38", metalShadow: "#6f4e14", clasp: "золотий карабін 585/750 зі страховим ланцюжком", connectors: "золоті кільця 2 мм між сильними акцентами; чорні стопери біля печаті", spacers: [ { name: "золотий розділювач", role: "сонячний міст між владою і дією", color: "#d8ae38" }, { name: "чорний стопер", role: "сатурніанська межа біля печаті", color: "#171313" }, { name: "мікро-кільце", role: "фіксує дорогі вставки без тертя каменів", color: "#f1d36b" }, ], assembly: [ "почати від карабіна: золоте кільце, стопер, головний камінь", "між сапфіром, рубіном і діамантом ставити металевий міст", "біля захисної печаті ставити чорний стопер з двох боків", "закрити формулу страховим ланцюжком і вузлом під металевою кришкою", ], }; } if (isMoney && isBreakthrough) { return { threadMaterial: "чорний ювелірний тросик або шнур із золотим вузлом-печаттю", threadColor: "#16110b", metalTone: "позолота + темна сталь", metalColor: "#c49a31", metalShadow: "#2f2a23", clasp: "позолочений карабін із темним страховим кільцем", connectors: "позолочені містки між грошовими акцентами; темні стопери біля захисту", spacers: [ { name: "позолочений міст", role: "веде удачу в матеріальний результат", color: "#c49a31" }, { name: "темний стопер", role: "не дає прориву розсипатися в хаос", color: "#1b1814" }, { name: "сонячне кільце", role: "підсилює цитрин і пірит", color: "#e3b743" }, ], assembly: [ "почати з головного грошового каменя і позолоченого містка", "між гарячим проривом і холодним захистом ставити розділювач", "захисний камінь закрити темним стопером з двох боків", "вузол або замок фіксувати на внутрішньому боці як печать наміру", ], }; } if (isBreakthrough) { return { threadMaterial: "чорний еластомер 0.8 мм у два проходи або ювелірний тросик", threadColor: "#130f0d", metalTone: "темна сталь + латунь", metalColor: "#a9822a", metalShadow: "#34302a", clasp: "магнітний замок із темної сталі або міцний прихований вузол", connectors: "латунні розділювачі біля вогняних каменів; темні кільця біля захисту", spacers: [ { name: "латунний міст", role: "проштовхує дію між гарячими каменями", color: "#b98a2a" }, { name: "темне кільце", role: "відтинає зайвий шум після печаті", color: "#282522" }, { name: "вузол-фіксатор", role: "тримає контур під напругою", color: "#0f0d0b" }, ], assembly: [ "першим ставити головний камінь, далі металевий міст", "гарячі камені не зіштовхувати напряму з холодними: між ними розділювач", "печать замикати темним кільцем до і після каменя", "вузол закривати кришкою або ховати під найбільш темною намистиною", ], }; } if (isLove) { return { threadMaterial: "м'який прозорий еластомер 0.7-0.8 мм", threadColor: "#d8b5ad", metalTone: "рожеве золото або мідь", metalColor: "#c58a64", metalShadow: "#6b3d2c", clasp: "невеликий магнітний замок у рожевому металі", connectors: "тонкі мідні або rose-gold розділювачі між серцевими каменями", spacers: [ { name: "рожевий міст", role: "пом'якшує контакт каменів", color: "#d49a7b" }, { name: "мідне кільце", role: "венеріанський провідник", color: "#b66f45" }, ], assembly: [ "не перевантажувати металом, залишити каменям м'який контакт", "центральний камінь ставити без гострих сусідів", "вузол сховати на внутрішньому боці браслета", ], }; } return { threadMaterial: "еластомер 0.8 мм у два проходи або ювелірний тросик", threadColor: "#171411", metalTone: isMoney ? "латунь / позолота" : "срібло або темна сталь", metalColor: isMoney ? "#c49a31" : "#a9a39a", metalShadow: isMoney ? "#735314" : "#423f3a", clasp: isMoney ? "позолочений карабін або магнітний замок" : "малий карабін, магнітний замок або прихований вузол", connectors: isMoney ? "позолочені розділювачі біля грошових акцентів" : "тонкі розділювачі тільки між великими або крихкими намистинами", spacers: [ { name: isMoney ? "позолочений міст" : "металевий міст", role: "розділяє акценти і береже камені", color: isMoney ? "#c49a31" : "#a9a39a" }, { name: "стопер", role: "фіксує печать і вузол", color: "#201b16" }, ], assembly: [ "почати з замка або вузла, далі головний камінь формули", "між великими акцентами ставити тонкий розділювач", "захисну печать закрити стопером", "після примірки підтягнути нитку і сховати вузол", ], }; } function buildBraceletBlueprint(text, matrix) { const date = braceletBirthDate(text); const lifePath = braceletLifePath(date); const sun = braceletSunSign(date); const profiles = braceletIntent(text); const scores = {}; profiles.forEach((profile, profileIndex) => { profile.stones.forEach((id, index) => { braceletAddScore(scores, id, 8 - profileIndex * 2 - Math.min(index, 4), `запит: ${profile.name}`); }); }); if (sun) { sun.stones.forEach((id, index) => braceletAddScore(scores, id, 4 - index, `сонячний знак: ${sun.name}`)); } if (lifePath && BRACELET_LIFE_PATH[lifePath]) { BRACELET_LIFE_PATH[lifePath].forEach((id, index) => braceletAddScore(scores, id, 4 - index, `шлях життя: ${lifePath}`)); } if (matrix && matrix.centerNode && BRACELET_ARCANA_STONES[matrix.centerNode.value]) { BRACELET_ARCANA_STONES[matrix.centerNode.value].forEach((id, index) => { braceletAddScore(scores, id, 4 - index, `центр Матриці XXII: ${matrix.centerNode.value}`); }); } if (matrix && matrix.channels) { matrix.channels.slice(0, 4).forEach(node => { (BRACELET_ARCANA_STONES[node.value] || []).slice(0, 1).forEach(id => { braceletAddScore(scores, id, 1, `${node.short}: ${node.value}`); }); }); } if (profiles.some(profile => profile.id === "imperial_money")) { [ ["sapphire", 6], ["diamond", 6], ["ruby", 5], ["emerald", 5], ["pyrite", 4], ["citrine", 3], ].forEach(([id, points]) => braceletAddScore(scores, id, points, "пріоритет поточного імператорського запиту")); } if (profiles.some(profile => profile.id === "breakthrough")) { [ ["obsidian", 4], ["black_tourmaline", 4], ["hematite", 3], ["garnet", 2], ].forEach(([id, points]) => braceletAddScore(scores, id, points, "пріоритет поточного запиту на прорив")); } if (!profiles.some(profile => profile.id === "imperial_money") && profiles.some(profile => profile.id === "money")) { [ ["citrine", 6], ["pyrite", 5], ["tiger_eye", 4], ["emerald", 4], ["aventurine", 3], ["lapis", 2], ].forEach(([id, points]) => braceletAddScore(scores, id, points, "пріоритет поточного запиту на удачу і багатство")); } braceletAddScore(scores, "clear_quartz", 1, "провідник формули"); braceletAddScore(scores, "black_tourmaline", 1, "базовий захисний замок"); const ranked = Object.values(scores).sort((a, b) => b.score - a.score || a.id.localeCompare(b.id)); const selected = ranked.slice(0, 6).map(item => ({ ...BRACELET_STONES[item.id], id: item.id, score: item.score, reasons: item.reasons, })); const primaryProfile = profiles[0]?.id || ""; const isImperial = profiles.some(profile => profile.id === "imperial_money"); const isBreakthrough = profiles.some(profile => profile.id === "breakthrough"); const isMoney = profiles.some(profile => profile.id === "money"); let anchor = selected[0]; if (!isImperial && isMoney) { anchor = selected.find(stone => ["citrine", "pyrite", "emerald", "tiger_eye"].includes(stone.id)) || anchor; } const amplifier = selected.find(stone => stone.id === "diamond") || selected.find(stone => stone.id === "clear_quartz") || (!isImperial && isMoney ? selected.find(stone => ["pyrite", "tiger_eye", "citrine"].includes(stone.id) && stone.id !== anchor.id) : null) || selected[selected.length - 1]; const seal = selected.find(stone => ["black_tourmaline", "onyx", "hematite", "obsidian"].includes(stone.id) && stone.id !== anchor.id) || selected.find(stone => ["black_tourmaline", "onyx", "hematite", "obsidian"].includes(stone.id)) || selected[1] || anchor; const beadCount = lifePath === 11 || lifePath === 22 || isImperial ? 22 : (primaryProfile === "protection" ? 21 : 19); const hardware = braceletHardwareConfig(profiles, primaryProfile, { isImperial, isBreakthrough }); const metal = isImperial ? "жовте золото 585/750 або важка позолочена латунь; чорні розділювачі тільки як сатурніанська межа" : (primaryProfile === "money" ? "латунь або позолочені розділювачі" : (primaryProfile === "love" ? "мідь або рожеве золото як венеріанський акцент" : "темна сталь, срібло або без металу")); const thread = isImperial ? "ювелірний тросик або шовковий шнур з металевим замком; еластомер тільки для тестової версії" : "еластомер 0.8 мм у два проходи або ювелірний тросик, якщо камені важкі"; const sequence = []; for (let i = 0; i < beadCount; i += 1) { let stone = selected[(i + 1) % selected.length] || anchor; let role = "ритмічна намистина"; if (i === 0 || i === Math.floor(beadCount / 2)) { stone = anchor; role = i === 0 ? "головний замок наміру" : "дзеркальний замок"; } else if (i % 6 === 0) { stone = amplifier; role = "підсилювач"; } else if (i === beadCount - 1 || i === Math.floor(beadCount / 3)) { stone = seal; role = "захисна печать"; } sequence.push({ index: i + 1, id: stone.id, name: stone.name, colors: stone.colors, role }); } return { type: "crystal_bracelet", title: braceletTitleForProfiles(profiles), date: braceletDateString(date), lifePath, sunSign: sun ? sun.name : null, intent: profiles.map(profile => profile.name).join(" + "), stones: selected, anchor, amplifier, seal, beadCount, beadSize: isImperial ? "8 мм база; 10 мм центральні замки; діамант/сапфір/рубін можна ставити як ювелірні вставки 3-5 мм" : "8 мм як універсальна база; 6 мм для м'якого щоденного носіння; 10 мм для ритуального акценту", metal, thread, ...hardware, sequence, profiles: profiles.map(profile => profile.id), createdAt: new Date().toISOString(), }; } function shouldBuildBracelet(active, text) { const raw = String(text || ""); const q = raw.toLowerCase(); const explicitBracelet = /(браслет|браслед|намист|бусин|bead|bracelet)/i.test(q); const shoppingIntent = /(випиши|выпиши|список|перелік|перечень|що\s+купити|что\s+купить|купити|купить|куплю|купиш|закуп|покуп|замов|заказ|магазин|шопінг|shopping|матеріал|материал|фурнітур|фурнитур)/i.test(q) && /(браслет|камн|камін|камен|кристал|кристалл|мінерал|минерал|намист|бусин|нитк|замок|розділювач|разделител|сапфір|сапфир|цитрин|пірит|пирит|гематит|турмалін|турмалин|обсидіан|обсидиан)/i.test(q); const assembly = /(збери|собери|підібрати|подобрать|підбери|подбери|формул[ау]|схем[ау]|зроби|сделай|створи|создай).{0,60}(?:браслет|камн|камін|камен|кристал|кристалл|мінерал|минерал|crystal|gemstone|stone)/i.test(q); const stoneBracelet = /(камн|камін|камен|кристал|кристалл|мінерал|минерал|crystal|gemstone|stone).{0,80}(?:браслет|намист|бусин|носити|носить|зап'яст|запяст|wrist|оберіг|оберег|амулет|талісман|талисман)/i.test(q); const altarContext = /(вівтар|вивтар|алтар|altar|столик|скатерт|свічк|свеч|вогонь|огонь|вода|герб|рожа|роза|пелюст|лепест|дар(?:и|ы)?\s+роду|родов)/i.test(q); const hasBirthDate = !!braceletBirthDate(raw); const hasSpecificIntent = braceletIntent(raw).some(profile => profile.id !== "integrative"); const designVerb = /(розроб|разработ|збери|собери|зроб|сдел|створ|созд|підбер|подбер|підібрат|подобрат|формул|схем|спроєкт|спроект)/i.test(q); const visualVerb = /(намал|нарис|покаж|зобраз|изобраз|візуал|визуал|картин)/i.test(q); if (!explicitBracelet && altarContext) return false; if (shoppingIntent) return false; if (!explicitBracelet && !assembly && !stoneBracelet) return false; return assembly || stoneBracelet || (explicitBracelet && (designVerb || hasBirthDate || hasSpecificIntent || (visualVerb && (hasBirthDate || hasSpecificIntent)))); } function buildBraceletContext(bracelet) { if (!bracelet) return ""; const hardware = braceletHardwareConfig(bracelet.profiles || [], bracelet.profiles?.[0] || "", { isImperial: (bracelet.profiles || []).includes("imperial_money"), isBreakthrough: (bracelet.profiles || []).includes("breakthrough"), }); const clasp = bracelet.clasp || hardware.clasp; const connectors = bracelet.connectors || hardware.connectors; const threadMaterial = bracelet.threadMaterial || hardware.threadMaterial || bracelet.thread; const assembly = (bracelet.assembly || hardware.assembly || []).join("; "); const stoneText = bracelet.stones.map(stone => ( `${stone.name}: ${stone.power}; стихія ${stone.element}; планети ${stone.planets}; причини підбору: ${stone.reasons.join(", ")}` )).join("; "); const sequenceText = bracelet.sequence.map(item => `${item.index}. ${item.name} (${item.role})`).join("; "); const cautionText = bracelet.stones.map(stone => `${stone.name}: ${stone.caution}`).join("; "); return `\n\n[Алхімічний браслет уже зібраний інтерфейсом Azoth AI для ПОТОЧНОГО запиту. Не замінюй цю формулу старими браслетами з історії або пам'яті. Намір: ${bracelet.intent}. Дата: ${bracelet.date}; сонячний знак: ${bracelet.sunSign || "не визначено"}; шлях життя: ${bracelet.lifePath || "не визначено"}. Формула каменів: ${stoneText}. Головний камінь: ${bracelet.anchor.name}. Підсилювач: ${bracelet.amplifier.name}. Захисна печать: ${bracelet.seal.name}. Кількість намистин: ${bracelet.beadCount}; розмір: ${bracelet.beadSize}; метал/розділювачі: ${bracelet.metal}; нитка/основа: ${threadMaterial}; замок: ${clasp}; конектори: ${connectors}; схема фурнітури: ${assembly}. Порядок намистин по колу: ${sequenceText}. Правила безпеки і догляду: ${cautionText}. Якщо є прикріплене фото клієнта або руки, зв'яжи формулу браслета з видимими людськими ознаками, але не вигадуй те, чого не видно. Дай практику докладний звіт українською: чому саме така формула, що вона підсилює, що захищає, як зібрати, як очистити, як зарядити, як носити, які питання поставити клієнту і як перевірити результат. Окремо опиши нитку, замок, конектори, розділювачі, метал і порядок збірки. Познач камені як традиційні езотеричні відповідності, а не медичну гарантію.]`; } function BraceletView({ bracelet }) { if (!bracelet || !bracelet.sequence) return null; const size = 420; const center = size / 2; const radiusX = 154; const radiusY = 124; const profileIds = bracelet.profiles || []; const hardware = braceletHardwareConfig(profileIds, profileIds[0] || "", { isImperial: profileIds.includes("imperial_money"), isBreakthrough: profileIds.includes("breakthrough"), }); const threadMaterial = bracelet.threadMaterial || hardware.threadMaterial || bracelet.thread || "ювелірний тросик"; const metalTone = bracelet.metalTone || hardware.metalTone || bracelet.metal || "метал"; const metalColor = bracelet.metalColor || hardware.metalColor || "#c49a31"; const metalShadow = bracelet.metalShadow || hardware.metalShadow || "#6f4e14"; const clasp = bracelet.clasp || hardware.clasp; const connectors = bracelet.connectors || hardware.connectors; const spacers = bracelet.spacers || hardware.spacers || []; const assembly = bracelet.assembly || hardware.assembly || []; const beads = bracelet.sequence.map((item, i) => { const angle = -Math.PI / 2 + (Math.PI * 2 * i) / bracelet.sequence.length; const x = center + Math.cos(angle) * radiusX; const y = center + Math.sin(angle) * radiusY; const r = item.role.includes("замок") ? 18 : item.role.includes("печать") ? 15 : 13; return { ...item, x, y, r, angle }; }); const links = beads.map((bead, i) => { const next = beads[(i + 1) % beads.length]; const x = (bead.x + next.x) / 2; const y = (bead.y + next.y) / 2; const angle = Math.atan2(next.y - bead.y, next.x - bead.x) * 180 / Math.PI; const major = bead.role.includes("замок") || next.role.includes("замок") || bead.role.includes("печать") || i % 6 === 2; return { x, y, angle, major }; }); const stoneCounts = bracelet.stones.map(stone => ({ ...stone, count: bracelet.sequence.filter(item => item.id === stone.id).length, })); return (