// numerology.jsx - повна матриця долі Azoth AI на 22 аркани
const MATRIX_ARCANA = {
1: { name: "Маг", key: "воля, ініціація, здатність запускати процес" },
2: { name: "Жриця", key: "інтуїція, пам'ять, тиша, приховане знання" },
3: { name: "Імператриця", key: "творення, тіло, ресурс, плодючість" },
4: { name: "Імператор", key: "структура, межі, порядок, відповідальність" },
5: { name: "Ієрофант", key: "традиція, навчання, посвята, моральний закон" },
6: { name: "Закохані", key: "вибір, союз, цінності, узгодження серця" },
7: { name: "Колісниця", key: "рух, перемога, воля, дисципліна шляху" },
8: { name: "Справедливість", key: "закон, рівновага, причинність, чесна міра" },
9: { name: "Самітник", key: "мудрість, самостійність, внутрішній ліхтар" },
10: { name: "Колесо Фортуни", key: "цикл, шанс, поворот, ритм долі" },
11: { name: "Сила", key: "життєва енергія, м'яка влада, приборкання інстинкту" },
12: { name: "Повішений", key: "пауза, служіння, інший погляд, добровільна жертва" },
13: { name: "Смерть", key: "завершення, трансформація, очищення форми" },
14: { name: "Помірність", key: "алхімія, баланс, зцілення, точна пропорція" },
15: { name: "Диявол", key: "бажання, залежність, матерія, тіньова угода" },
16: { name: "Вежа", key: "злам, правда удару, звільнення від хибної споруди" },
17: { name: "Зоря", key: "натхнення, віра, ясність, м'яке відновлення" },
18: { name: "Місяць", key: "сни, страхи, рідова пам'ять, образи несвідомого" },
19: { name: "Сонце", key: "сила життя, видимість, радість, щедрість" },
20: { name: "Суд", key: "поклик, пробудження, голос роду, відповідь душі" },
21: { name: "Світ", key: "цілісність, завершення, інтеграція, велике коло" },
22: { name: "Блазень", key: "нульовий крок, свобода, довіра, новий цикл" },
};
const CHAKRAS = [
["Сахасрара", "Місія"],
["Аджна", "Доля, егрегори"],
["Вішудха", "Доля, егрегори"],
["Анахата", "Стосунки, картина світу"],
["Маніпура", "Статус, відповідальність"],
["Свадхістана", "Дитяча любов та радість"],
["Муладхара", "Тіло, матерія"],
];
function digitSum(value) {
return String(Math.abs(Number(value) || 0)).split("").reduce((sum, ch) => sum + Number(ch), 0);
}
function base22(value) {
let n = Math.abs(Number(value) || 0);
if (n === 0) return 22;
while (n > 22) n = digitSum(n);
return n || 22;
}
function sum22(...values) {
return base22(values.reduce((sum, value) => sum + (Number(value) || 0), 0));
}
function validBirthDate(day, month, year) {
if (year < 1900 || year > 2099 || month < 1 || month > 12 || day < 1 || day > 31) return null;
const dt = new Date(Date.UTC(year, month - 1, day));
if (dt.getUTCFullYear() !== year || dt.getUTCMonth() !== month - 1 || dt.getUTCDate() !== day) return null;
return { day, month, year };
}
function extractBirthDate(text) {
const value = String(text || "");
let match = value.match(/\b(\d{1,2})[.\-/](\d{1,2})[.\-/](\d{4})\b/);
if (match) return validBirthDate(Number(match[1]), Number(match[2]), Number(match[3]));
match = value.match(/\b(\d{4})-(\d{1,2})-(\d{1,2})\b/);
if (match) return validBirthDate(Number(match[3]), Number(match[2]), Number(match[1]));
return null;
}
function formatBirthDate(date) {
const d = String(date.day).padStart(2, "0");
const m = String(date.month).padStart(2, "0");
return `${d}.${m}.${date.year}`;
}
function calculateAge(date) {
const now = new Date();
let age = now.getFullYear() - date.year;
const monthPassed = now.getMonth() + 1 > date.month || (now.getMonth() + 1 === date.month && now.getDate() >= date.day);
if (!monthPassed) age -= 1;
return Math.max(0, age);
}
function matrixNode(id, label, short, value, x, y, formula, size = "inner", tone = "") {
return {
id, label, short, value,
arcana: MATRIX_ARCANA[value],
x, y, formula, size, tone,
};
}
function interpolate(p1, p2, ratio) {
return { x: p1.x + (p2.x - p1.x) * ratio, y: p1.y + (p2.y - p1.y) * ratio };
}
function buildAgeTicks(vertices, center, top, bottom) {
const result = [];
for (let age = 5; age <= 75; age += 5) {
if (age % 10 === 0) continue;
const segment = Math.floor(age / 10);
const ratio = (age % 10) / 10;
const p = interpolate(vertices[segment], vertices[(segment + 1) % vertices.length], ratio);
result.push({
age,
value: sum22(vertices[segment].value, vertices[(segment + 1) % vertices.length].value, center, age / 5),
x: p.x,
y: p.y,
});
}
return [
{ age: 0, value: vertices[0].value, x: vertices[0].x, y: vertices[0].y },
...result,
{ age: 80, value: sum22(top, bottom), x: vertices[0].x, y: vertices[0].y + 4 },
];
}
function buildAgeBands(vertices, center) {
const bands = [];
const formatAgePoint = value => Number.isInteger(value) ? String(value) : String(value).replace(".5", ".5");
for (let segment = 0; segment < vertices.length; segment += 1) {
const from = vertices[segment];
const to = vertices[(segment + 1) % vertices.length];
const decade = segment * 10;
const middle = sum22(from.value, to.value);
const left = sum22(from.value, middle);
const right = sum22(middle, to.value);
const ageSteps = [
{ ratio: 0.175, start: 1, end: 2.5, value: sum22(from.value, left) },
{ ratio: 0.3, start: 2.5, end: 3.5, value: left },
{ ratio: 0.375, start: 3.5, end: 4, value: sum22(left, middle) },
{ ratio: 0.5, point: 5, value: middle, major: true },
{ ratio: 0.675, start: 6, end: 7.5, value: sum22(middle, right) },
{ ratio: 0.8, start: 7.5, end: 8.5, value: right },
{ ratio: 0.875, start: 8.5, end: 9, value: sum22(right, to.value) },
];
ageSteps.forEach((step, index) => {
const age = decade + (step.point || ((step.start + step.end) / 2));
const p = interpolate(from, to, step.ratio);
const label = step.major
? `${decade + step.point} років`
: `${formatAgePoint(decade + step.start)}-${formatAgePoint(decade + step.end)}`;
bands.push({
age,
label,
value: step.value,
major: Boolean(step.major),
segment,
index,
x: p.x,
y: p.y,
});
});
}
return bands;
}
function buildHealthRows(core) {
const { a, b, c, d, e, ab, bc, cd } = core;
const rows = [
[CHAKRAS[0], a, b, ab],
[CHAKRAS[1], sum22(b, e), c, sum22(a, e)],
[CHAKRAS[2], sum22(a, e), sum22(b, e), d],
[CHAKRAS[3], sum22(a, c), bc, cd],
[CHAKRAS[4], e, e, c],
[CHAKRAS[5], ab, sum22(b, e), sum22(a, c)],
[CHAKRAS[6], c, d, cd],
];
const totals = rows.reduce((acc, row) => {
acc[0] += row[1]; acc[1] += row[2]; acc[2] += row[3];
return acc;
}, [0, 0, 0]).map(base22);
return {
rows: rows.map(([chakra, physical, energy, emotion]) => ({
chakra: chakra[0],
theme: chakra[1],
physical,
energy,
emotion,
})),
totals,
};
}
function buildDestinyMatrix(text) {
const date = extractBirthDate(text);
if (!date) return null;
const a = base22(date.day);
const b = base22(date.month);
const c = base22(digitSum(date.year));
const d = sum22(a, b, c);
const e = sum22(a, b, c, d);
const ab = sum22(a, b);
const bc = sum22(b, c);
const cd = sum22(c, d);
const ad = sum22(a, d);
const core = { a, b, c, d, e, ab, bc, cd, ad };
const outerNodes = [
matrixNode("day", "День народження / портрет", "0 років", a, 8, 50, "день народження", "outer", "west"),
matrixNode("ab", "Вузол 10 років", "10 років", ab, 24, 24, "день + місяць", "outer"),
matrixNode("month", "Місяць / душа", "20 років", b, 50, 8, "місяць народження", "outer", "north"),
matrixNode("bc", "Вузол 30 років", "30 років", bc, 76, 24, "місяць + рік", "outer"),
matrixNode("year", "Рік / соціум", "40 років", c, 92, 50, "сума цифр року", "outer", "east"),
matrixNode("cd", "Вузол 50 років", "50 років", cd, 76, 76, "рік + нижній код", "outer"),
matrixNode("destiny", "Земний код / підсумок", "60 років", d, 50, 92, "день + місяць + рік", "outer", "south"),
matrixNode("ad", "Вузол 70 років", "70 років", ad, 24, 76, "день + нижній код", "outer"),
];
const centerNode = matrixNode("center", "Центр матриці", "центр", e, 50, 50, "день + місяць + рік + нижній код", "center");
const innerNodes = [
matrixNode("v1", "Ментальний ключ", "розум", c, 50, 20, "річна енергія на вертикалі"),
matrixNode("v2", "Душевний фільтр", "душа", sum22(b, e), 50, 29, "місяць + центр"),
matrixNode("v3", "Верхній ресурс", "ресурс", bc, 50, 39, "місяць + рік"),
matrixNode("v4", "Тілесна опора", "тіло", sum22(b, e), 50, 64, "місяць + центр"),
matrixNode("v5", "Матеріальна вісь", "матерія", c, 50, 74, "рік"),
matrixNode("h1", "Лівий ресурс", "ресурс", sum22(b, e), 18, 50, "місяць + центр"),
matrixNode("h2", "Особиста сила", "сила", sum22(a, e), 26, 50, "день + центр"),
matrixNode("h3", "Внутрішній конфлікт", "урок", sum22(a, c), 38, 50, "день + рік"),
matrixNode("h4", "Соціальний прояв", "прояв", c, 58, 50, "рік"),
matrixNode("h5", "Професійний канал", "праця", sum22(c, e), 66, 50, "рік + центр"),
matrixNode("h6", "Ресурс реалізації", "ресурс", sum22(c, e), 78, 50, "рік + центр"),
matrixNode("h7", "Зовнішній результат", "результат", d, 86, 50, "нижній код"),
matrixNode("m1", "Лінія чоловічого роду: вузол 1", "чол.", sum22(ab, d), 28, 36, "верхній лівий вузол + нижній код"),
matrixNode("m2", "Лінія чоловічого роду: вузол 2", "чол.", d, 35, 43, "нижній код"),
matrixNode("f1", "Лінія жіночого роду: вузол 1", "жін.", bc, 72, 36, "верхній правий вузол"),
matrixNode("f2", "Лінія жіночого роду: вузол 2", "жін.", sum22(bc, c), 65, 43, "верхній правий вузол + рік"),
matrixNode("love1", "Лінія стосунків", "любов", sum22(b, e), 57, 72, "місяць + центр", "small", "love"),
matrixNode("love2", "Серцевий урок", "серце", sum22(a, c), 62, 64, "день + рік", "small", "love"),
matrixNode("love3", "Канал любові", "любов", ad, 68, 56, "день + нижній код", "small", "love"),
matrixNode("money1", "Грошовий вузол", "гроші", sum22(c, e), 72, 48, "рік + центр", "small", "money"),
matrixNode("money2", "Матеріальний ключ", "гроші", sum22(c, d, e), 79, 43, "рік + нижній код + центр", "small", "money"),
];
const channels = [
matrixNode("love", "Канал стосунків", "Любов", ad, 0, 0, "день + нижній код"),
matrixNode("money", "Грошовий канал", "Гроші", bc, 0, 0, "місяць + рік"),
matrixNode("purpose", "Призначення", "Шлях", sum22(b, e), 0, 0, "місяць + центр"),
matrixNode("shadow", "Тіньовий урок", "Тінь", e, 0, 0, "центр матриці"),
];
const health = buildHealthRows(core);
const purpose = [
{
title: "Особисте",
value: e,
rows: [
["Небо", sum22(b, d)],
["Земля", sum22(a, c)],
["Синтез", sum22(e, a, c)],
],
},
{
title: "Соціальне",
value: c,
rows: [
["Чоловічий рід", sum22(ab, cd)],
["Жіночий рід", sum22(bc, ad)],
["Реалізація", sum22(c, e)],
],
},
];
const allNodes = [...outerNodes, centerNode, ...innerNodes, ...channels];
return {
type: "destiny_matrix",
date: formatBirthDate(date),
age: calculateAge(date),
rawDate: date,
method: "Azoth Matrix 22 full: день A, місяць B, рік C; нижній код D=A+B+C; центр E=A+B+C+D; кутові вузли AB, BC, CD, AD; усі значення приводяться до 1-22 через суму цифр, 22 зберігається як окремий аркан.",
core,
outerNodes,
centerNode,
innerNodes,
nodes: allNodes,
channels,
health,
purpose,
ageTicks: buildAgeTicks(outerNodes, e, b, d),
ageBands: buildAgeBands(outerNodes, e),
byId: Object.fromEntries(allNodes.map(node => [node.id, node])),
};
}
function shouldBuildDestinyMatrix(active, text) {
const lc = String(text || "").toLowerCase();
if (!extractBirthDate(text)) return false;
const explicitMatrix = /матриц|матриця|матрица|matrix\s*(xxii|22|of destiny)|матриц\w*\s+(дол|судьб)|дол[іи]\s+матриц|судьб\w*\s+матриц|карта\s+дол[іи]/.test(lc);
if (explicitMatrix) return true;
const explicitNatal = /натал|natal|birth chart|карта народження|карта рождения|астро(?:логічн|логическ|logical)?\s*карт|гороскоп|асценд|ascendant|mc\b|доми|houses|планет/.test(lc);
if (explicitNatal) return false;
return active === "numero" && /(матриц|матриця|матрица|дол[іи]|судьб)/.test(lc);
}
function nodeContext(node) {
return `${node.label}: ${node.value} (${node.arcana.name}) - ${node.arcana.key}; формула: ${node.formula}`;
}
function buildMatrixContext(matrix) {
matrix = ensureFullMatrix(matrix);
if (!matrix) return "";
const coreText = matrix.outerNodes.concat([matrix.centerNode]).map(nodeContext).join("; ");
const innerText = matrix.innerNodes.map(nodeContext).join("; ");
const channelText = matrix.channels.map(nodeContext).join("; ");
const healthText = matrix.health.rows
.map(row => `${row.chakra} (${row.theme}): фізика ${row.physical}, енергія ${row.energy}, емоція ${row.emotion}`)
.join("; ");
const purposeText = matrix.purpose
.map(block => `${block.title}: ${block.value}; ${block.rows.map(([label, value]) => `${label} ${value}`).join(", ")}`)
.join("; ");
const ageText = matrix.ageTicks
.filter(tick => tick.age % 10 === 0 || tick.age % 15 === 0 || tick.age === matrix.age)
.map(tick => `${tick.age} років: ${tick.value}`)
.join("; ");
const ageBandText = (matrix.ageBands || [])
.map(band => `${band.label}: ${band.value}`)
.join("; ");
return `\n\n[Повна Матриця долі вже розрахована інтерфейсом Azoth AI для дати ${matrix.date}, вік ${matrix.age}. Метод: ${matrix.method} Головний контур: ${coreText}. Внутрішні лінії: ${innerText}. Канали: ${channelText}. Карта здоров'я/чакр як символічна езотерична карта, не медицина: ${healthText}; результат фізика ${matrix.health.totals[0]}, енергія ${matrix.health.totals[1]}, емоція ${matrix.health.totals[2]}. Призначення: ${purposeText}. Вікові акценти: ${ageText}. Малий віковий контур: ${ageBandText}. Тлумач саме ці числа, позиції й канали. Не перераховуй і не підміняй значення. Якщо користувач не просить коротко, дай детальний практичний розбір для езотерика-консультанта: головні осі, зовнішній контур, центр, родові лінії, гроші, стосунки, тінь, здоров'я/чакри як символічні зони, вікові періоди, питання для клієнтської сесії, рекомендації. Пиши без фаталізму.]`;
}
function ensureFullMatrix(matrix) {
if (!matrix) return null;
if (matrix.outerNodes && matrix.centerNode && matrix.health && matrix.purpose) return matrix;
const seed = matrix.date || (matrix.rawDate ? formatBirthDate(matrix.rawDate) : "");
return buildDestinyMatrix(seed) || matrix;
}
const MATRIX_CHART_COORDS = {
day: { x: 92, y: 500, r: 51 },
ab: { x: 240, y: 240, r: 47 },
month: { x: 500, y: 88, r: 52 },
bc: { x: 760, y: 240, r: 47 },
year: { x: 930, y: 500, r: 50 },
cd: { x: 760, y: 760, r: 47 },
destiny: { x: 500, y: 912, r: 52 },
ad: { x: 240, y: 760, r: 47 },
center: { x: 500, y: 500, r: 56 },
v1: { x: 500, y: 176, r: 28 },
v2: { x: 500, y: 248, r: 28 },
v3: { x: 500, y: 354, r: 27 },
v4: { x: 500, y: 646, r: 27 },
v5: { x: 500, y: 724, r: 28 },
h1: { x: 188, y: 500, r: 27 },
h2: { x: 246, y: 500, r: 29 },
h3: { x: 366, y: 500, r: 28 },
h4: { x: 600, y: 500, r: 28 },
h5: { x: 665, y: 500, r: 28 },
h6: { x: 746, y: 500, r: 28 },
h7: { x: 825, y: 500, r: 27 },
m1: { x: 286, y: 323, r: 29 },
m2: { x: 354, y: 391, r: 28 },
f1: { x: 714, y: 323, r: 29 },
f2: { x: 646, y: 391, r: 28 },
love1: { x: 574, y: 734, r: 26 },
love2: { x: 652, y: 652, r: 26 },
love3: { x: 724, y: 574, r: 26 },
money1: { x: 716, y: 446, r: 26 },
money2: { x: 804, y: 388, r: 26 },
};
function chartCoord(node) {
return MATRIX_CHART_COORDS[node.id] || {
x: node.x * 10,
y: node.y * 10,
r: node.size === "small" ? 25 : 28,
};
}
function chartPoints(nodes, ids) {
const byId = Object.fromEntries(nodes.map(node => [node.id, node]));
return ids
.map(id => byId[id])
.filter(Boolean)
.map(node => {
const p = chartCoord(node);
return `${p.x},${p.y}`;
})
.join(" ");
}
function outwardPoint(item, offset = 54) {
const base = { x: item.x * 10, y: item.y * 10 };
const dx = base.x - 500;
const dy = base.y - 500;
const len = Math.sqrt(dx * dx + dy * dy) || 1;
return {
x: base.x + (dx / len) * offset,
y: base.y + (dy / len) * offset,
};
}
function MatrixChartNode({ node, index }) {
const p = chartCoord(node);
const kind = node.size || "inner";
const tone = node.tone ? ` dmatrix__chart-node--${node.tone}` : "";
const showLabel = kind === "outer" || kind === "center";
return (
| Чакра | Фізика | Енергія | Емоція |
|---|---|---|---|
| {row.chakra}{row.theme} | {row.physical} | {row.energy} | {row.emotion} |
| РезультатЗагальне поле | {matrix.health.totals[0]} | {matrix.health.totals[1]} | {matrix.health.totals[2]} |