From af7d63717ee08f913f01ccffd07cba8c5aea5a5a Mon Sep 17 00:00:00 2001 From: Nose Date: Sat, 7 Mar 2026 01:09:00 -0800 Subject: [PATCH] Initial commit --- .gitattributes | 2 + .gitignore | 3 + app.js | 3492 ++++ app/astro-calcs.js | 163 + app/calendar-events.js | 96 + app/card-images.js | 654 + app/data-service.js | 372 + app/quiz-calendars.js | 511 + app/styles.css | 3560 ++++ app/tarot-database.js | 1341 ++ app/ui-alphabet.js | 2081 +++ app/ui-calendar.js | 2528 +++ app/ui-cube.js | 1901 +++ app/ui-cycles.js | 350 + app/ui-elements.js | 454 + app/ui-enochian.js | 459 + app/ui-gods.js | 618 + app/ui-holidays.js | 1107 ++ app/ui-iching.js | 882 + app/ui-kabbalah.js | 1153 ++ app/ui-natal.js | 185 + app/ui-now.js | 686 + app/ui-planets.js | 898 + app/ui-quiz.js | 1484 ++ app/ui-tarot.js | 2314 +++ app/ui-zodiac.js | 590 + asset/img/enochian/char(65).png | Bin 0 -> 562 bytes asset/img/enochian/char(66).png | Bin 0 -> 770 bytes asset/img/enochian/char(67).png | Bin 0 -> 717 bytes asset/img/enochian/char(68).png | Bin 0 -> 642 bytes asset/img/enochian/char(69).png | Bin 0 -> 395 bytes asset/img/enochian/char(70).png | Bin 0 -> 547 bytes asset/img/enochian/char(71).png | Bin 0 -> 652 bytes asset/img/enochian/char(72).png | Bin 0 -> 796 bytes asset/img/enochian/char(73).png | Bin 0 -> 361 bytes asset/img/enochian/char(76).png | Bin 0 -> 594 bytes asset/img/enochian/char(77).png | Bin 0 -> 554 bytes asset/img/enochian/char(78).png | Bin 0 -> 604 bytes asset/img/enochian/char(79).png | Bin 0 -> 552 bytes asset/img/enochian/char(80).png | Bin 0 -> 662 bytes asset/img/enochian/char(81).png | Bin 0 -> 430 bytes asset/img/enochian/char(82).png | Bin 0 -> 679 bytes asset/img/enochian/char(83).png | Bin 0 -> 502 bytes asset/img/enochian/char(84).png | Bin 0 -> 431 bytes asset/img/enochian/char(85).png | Bin 0 -> 639 bytes asset/img/enochian/char(86).png | Bin 0 -> 639 bytes asset/img/enochian/char(88).png | Bin 0 -> 381 bytes asset/img/enochian/char(90).png | Bin 0 -> 587 bytes data/MANIFEST.json | 50 + data/alchemy/elementals.json | 54 + data/alchemy/elements.json | 42 + data/alchemy/symbols.json | 109 + data/alchemy/terms.json | 78 + data/alphabets.json | 755 + data/astrology/houses.json | 170 + data/astrology/planets.json | 233 + data/astrology/retrograde.json | 244 + data/astrology/zodiac.json | 326 + data/astronomy-cycles.json | 228 + data/calendar-holidays.json | 1673 ++ data/calendar-months.json | 379 + data/celestial-holidays.json | 203 + data/chakras.json | 120 + data/decans.json | 54 + data/enochian/dictionary.json | 22640 ++++++++++++++++++++++++++ data/enochian/letters.json | 212 + data/enochian/tablets.json | 376 + data/gd/degrees.json | 14 + data/gd/grades.json | 115 + data/gematria-ciphers.json | 33 + data/geomancy/houses.json | 75 + data/geomancy/tetragrams.json | 990 ++ data/gods.json | 578 + data/hebrew-calendar.json | 239 + data/hebrewLetters.json | 353 + data/i-ching.json | 1581 ++ data/islamic-calendar.json | 136 + data/kabbalah/angelicOrders.json | 79 + data/kabbalah/archangels.json | 101 + data/kabbalah/cube.json | 230 + data/kabbalah/fourWorlds.json | 112 + data/kabbalah/godNames.json | 72 + data/kabbalah/kabbalah-tree.json | 500 + data/kabbalah/kerubim.json | 54 + data/kabbalah/paths.json | 250 + data/kabbalah/sephirot.json | 324 + data/kabbalah/seventyTwoAngels.json | 107 + data/kabbalah/souls.json | 88 + data/kabbalah/tribesOfIsrael.json | 100 + data/numbers.json | 120 + data/pentagram.svg | 155 + data/planet-science.json | 269 + data/planetary-correspondences.json | 86 + data/playing-cards-52.json | 65 + data/sabian-symbols.json | 2166 +++ data/signs.json | 164 + data/tarot-database.json | 756 + data/wheel-of-year.json | 338 + index.html | 781 + package-lock.json | 1339 ++ package.json | 23 + scripts/generate-decks-registry.cjs | 516 + 102 files changed, 68739 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 app.js create mode 100644 app/astro-calcs.js create mode 100644 app/calendar-events.js create mode 100644 app/card-images.js create mode 100644 app/data-service.js create mode 100644 app/quiz-calendars.js create mode 100644 app/styles.css create mode 100644 app/tarot-database.js create mode 100644 app/ui-alphabet.js create mode 100644 app/ui-calendar.js create mode 100644 app/ui-cube.js create mode 100644 app/ui-cycles.js create mode 100644 app/ui-elements.js create mode 100644 app/ui-enochian.js create mode 100644 app/ui-gods.js create mode 100644 app/ui-holidays.js create mode 100644 app/ui-iching.js create mode 100644 app/ui-kabbalah.js create mode 100644 app/ui-natal.js create mode 100644 app/ui-now.js create mode 100644 app/ui-planets.js create mode 100644 app/ui-quiz.js create mode 100644 app/ui-tarot.js create mode 100644 app/ui-zodiac.js create mode 100644 asset/img/enochian/char(65).png create mode 100644 asset/img/enochian/char(66).png create mode 100644 asset/img/enochian/char(67).png create mode 100644 asset/img/enochian/char(68).png create mode 100644 asset/img/enochian/char(69).png create mode 100644 asset/img/enochian/char(70).png create mode 100644 asset/img/enochian/char(71).png create mode 100644 asset/img/enochian/char(72).png create mode 100644 asset/img/enochian/char(73).png create mode 100644 asset/img/enochian/char(76).png create mode 100644 asset/img/enochian/char(77).png create mode 100644 asset/img/enochian/char(78).png create mode 100644 asset/img/enochian/char(79).png create mode 100644 asset/img/enochian/char(80).png create mode 100644 asset/img/enochian/char(81).png create mode 100644 asset/img/enochian/char(82).png create mode 100644 asset/img/enochian/char(83).png create mode 100644 asset/img/enochian/char(84).png create mode 100644 asset/img/enochian/char(85).png create mode 100644 asset/img/enochian/char(86).png create mode 100644 asset/img/enochian/char(88).png create mode 100644 asset/img/enochian/char(90).png create mode 100644 data/MANIFEST.json create mode 100644 data/alchemy/elementals.json create mode 100644 data/alchemy/elements.json create mode 100644 data/alchemy/symbols.json create mode 100644 data/alchemy/terms.json create mode 100644 data/alphabets.json create mode 100644 data/astrology/houses.json create mode 100644 data/astrology/planets.json create mode 100644 data/astrology/retrograde.json create mode 100644 data/astrology/zodiac.json create mode 100644 data/astronomy-cycles.json create mode 100644 data/calendar-holidays.json create mode 100644 data/calendar-months.json create mode 100644 data/celestial-holidays.json create mode 100644 data/chakras.json create mode 100644 data/decans.json create mode 100644 data/enochian/dictionary.json create mode 100644 data/enochian/letters.json create mode 100644 data/enochian/tablets.json create mode 100644 data/gd/degrees.json create mode 100644 data/gd/grades.json create mode 100644 data/gematria-ciphers.json create mode 100644 data/geomancy/houses.json create mode 100644 data/geomancy/tetragrams.json create mode 100644 data/gods.json create mode 100644 data/hebrew-calendar.json create mode 100644 data/hebrewLetters.json create mode 100644 data/i-ching.json create mode 100644 data/islamic-calendar.json create mode 100644 data/kabbalah/angelicOrders.json create mode 100644 data/kabbalah/archangels.json create mode 100644 data/kabbalah/cube.json create mode 100644 data/kabbalah/fourWorlds.json create mode 100644 data/kabbalah/godNames.json create mode 100644 data/kabbalah/kabbalah-tree.json create mode 100644 data/kabbalah/kerubim.json create mode 100644 data/kabbalah/paths.json create mode 100644 data/kabbalah/sephirot.json create mode 100644 data/kabbalah/seventyTwoAngels.json create mode 100644 data/kabbalah/souls.json create mode 100644 data/kabbalah/tribesOfIsrael.json create mode 100644 data/numbers.json create mode 100644 data/pentagram.svg create mode 100644 data/planet-science.json create mode 100644 data/planetary-correspondences.json create mode 100644 data/playing-cards-52.json create mode 100644 data/sabian-symbols.json create mode 100644 data/signs.json create mode 100644 data/tarot-database.json create mode 100644 data/wheel-of-year.json create mode 100644 index.html create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 scripts/generate-decks-registry.cjs diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..7b6f29f --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +asset/tarot deck/** filter=lfs diff=lfs merge=lfs -text +asset/tarot[[:space:]]deck/** filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..738cada --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +asset/tarot deck/* +asset\tarot deck\* diff --git a/app.js b/app.js new file mode 100644 index 0000000..2278fff --- /dev/null +++ b/app.js @@ -0,0 +1,3492 @@ +const { getCenteredWeekStartDay, getDateKey, getMoonPhaseName } = window.TarotCalc; +const { loadReferenceData, loadMagickDataset } = window.TarotDataService; +const { buildWeekEvents } = window.TarotEventBuilder; +const { updateNowPanel } = window.TarotNowUi; +const { ensureTarotSection } = window.TarotSectionUi || {}; +const { ensurePlanetSection } = window.PlanetSectionUi || {}; +const { ensureCyclesSection } = window.CyclesSectionUi || {}; +const { ensureElementsSection } = window.ElementsSectionUi || {}; +const { ensureIChingSection } = window.IChingSectionUi || {}; +const { ensureKabbalahSection } = window.KabbalahSectionUi || {}; +const { ensureCubeSection } = window.CubeSectionUi || {}; +const { ensureAlphabetSection } = window.AlphabetSectionUi || {}; +const { ensureZodiacSection } = window.ZodiacSectionUi || {}; +const { ensureQuizSection, registerQuizCategory } = window.QuizSectionUi || {}; +const { ensureGodsSection } = window.GodsSectionUi || {}; +const { ensureEnochianSection } = window.EnochianSectionUi || {}; +const { ensureCalendarSection } = window.CalendarSectionUi || {}; +const { ensureHolidaySection } = window.HolidaySectionUi || {}; +const { ensureNatalPanel } = window.TarotNatalUi || {}; + +const statusEl = document.getElementById("status"); +const monthStripEl = document.getElementById("month-strip"); +const calendarEl = document.getElementById("calendar"); +const calendarSectionEl = document.getElementById("calendar-section"); +const holidaySectionEl = document.getElementById("holiday-section"); +const tarotSectionEl = document.getElementById("tarot-section"); +const astronomySectionEl = document.getElementById("astronomy-section"); +const natalSectionEl = document.getElementById("natal-section"); +const planetSectionEl = document.getElementById("planet-section"); +const cyclesSectionEl = document.getElementById("cycles-section"); +const elementsSectionEl = document.getElementById("elements-section"); +const ichingSectionEl = document.getElementById("iching-section"); +const kabbalahSectionEl = document.getElementById("kabbalah-section"); +const kabbalahTreeSectionEl = document.getElementById("kabbalah-tree-section"); +const cubeSectionEl = document.getElementById("cube-section"); +const alphabetSectionEl = document.getElementById("alphabet-section"); +const numbersSectionEl = document.getElementById("numbers-section"); +const zodiacSectionEl = document.getElementById("zodiac-section"); +const quizSectionEl = document.getElementById("quiz-section"); +const godsSectionEl = document.getElementById("gods-section"); +const enochianSectionEl = document.getElementById("enochian-section"); +const openCalendarEl = document.getElementById("open-calendar"); +const openCalendarMonthsEl = document.getElementById("open-calendar-months"); +const openHolidaysEl = document.getElementById("open-holidays"); +const openTarotEl = document.getElementById("open-tarot"); +const openTarotCardsEl = document.getElementById("open-tarot-cards"); +const openTarotSpreadEl = document.getElementById("open-tarot-spread"); +const openAstronomyEl = document.getElementById("open-astronomy"); +const openPlanetsEl = document.getElementById("open-planets"); +const openCyclesEl = document.getElementById("open-cycles"); +const openElementsEl = document.getElementById("open-elements"); +const openIChingEl = document.getElementById("open-iching"); +const openKabbalahEl = document.getElementById("open-kabbalah"); +const openKabbalahTreeEl = document.getElementById("open-kabbalah-tree"); +const openKabbalahCubeEl = document.getElementById("open-kabbalah-cube"); +const openAlphabetEl = document.getElementById("open-alphabet"); +const openNumbersEl = document.getElementById("open-numbers"); +const openZodiacEl = document.getElementById("open-zodiac"); +const openNatalEl = document.getElementById("open-natal"); +const openQuizEl = document.getElementById("open-quiz"); +const openGodsEl = document.getElementById("open-gods"); +const openEnochianEl = document.getElementById("open-enochian"); +const openSettingsEl = document.getElementById("open-settings"); +const closeSettingsEl = document.getElementById("close-settings"); +const settingsPopupEl = document.getElementById("settings-popup"); +const settingsPopupCardEl = document.getElementById("settings-popup-card"); +const topbarDropdownEls = Array.from(document.querySelectorAll(".topbar-dropdown")); +const latEl = document.getElementById("lat"); +const lngEl = document.getElementById("lng"); +const timeFormatEl = document.getElementById("time-format"); +const birthDateEl = document.getElementById("birth-date"); +const tarotDeckEl = document.getElementById("tarot-deck"); +const saveSettingsEl = document.getElementById("save-settings"); +const useLocationEl = document.getElementById("use-location"); +const nowSkyLayerEl = document.getElementById("now-sky-layer"); +const nowPanelEl = document.getElementById("now-panel"); +const tarotBrowseViewEl = document.getElementById("tarot-browse-view"); +const tarotSpreadViewEl = document.getElementById("tarot-spread-view"); +const tarotSpreadBackEl = document.getElementById("tarot-spread-back"); +const tarotSpreadBtnThreeEl = document.getElementById("tarot-spread-btn-three"); +const tarotSpreadBtnCelticEl = document.getElementById("tarot-spread-btn-celtic"); +const tarotSpreadRedrawEl = document.getElementById("tarot-spread-redraw"); +const tarotSpreadMeaningsEl = document.getElementById("tarot-spread-meanings"); +const tarotSpreadBoardEl = document.getElementById("tarot-spread-board"); +const numbersCountEl = document.getElementById("numbers-count"); +const numbersListEl = document.getElementById("numbers-list"); +const numbersDetailNameEl = document.getElementById("numbers-detail-name"); +const numbersDetailTypeEl = document.getElementById("numbers-detail-type"); +const numbersDetailSummaryEl = document.getElementById("numbers-detail-summary"); +const numbersDetailBodyEl = document.getElementById("numbers-detail-body"); +const numbersSpecialPanelEl = document.getElementById("numbers-special-panel"); + +const nowElements = { + nowHourEl: document.getElementById("now-hour"), + nowHourTarotEl: document.getElementById("now-hour-tarot"), + nowCountdownEl: document.getElementById("now-countdown"), + nowHourNextEl: document.getElementById("now-hour-next"), + nowHourCardEl: document.getElementById("now-hour-card"), + nowMoonEl: document.getElementById("now-moon"), + nowMoonTarotEl: document.getElementById("now-moon-tarot"), + nowMoonCountdownEl: document.getElementById("now-moon-countdown"), + nowMoonNextEl: document.getElementById("now-moon-next"), + nowMoonCardEl: document.getElementById("now-moon-card"), + nowDecanEl: document.getElementById("now-decan"), + nowDecanTarotEl: document.getElementById("now-decan-tarot"), + nowDecanCountdownEl: document.getElementById("now-decan-countdown"), + nowDecanNextEl: document.getElementById("now-decan-next"), + nowDecanCardEl: document.getElementById("now-decan-card"), + nowStatsSabianEl: document.getElementById("now-stats-sabian"), + nowStatsPlanetsEl: document.getElementById("now-stats-planets") +}; + +const baseWeekOptions = { + hourStart: 0, + hourEnd: 24, + eventView: ["allday", "time"], + taskView: false +}; + +const PLANET_CALENDAR_ORDER = ["saturn", "jupiter", "mars", "sol", "venus", "mercury", "luna"]; +const SETTINGS_STORAGE_KEY = "tarot-time-settings-v1"; +const DEFAULT_TAROT_DECK = "ceremonial-magick"; +const SIDEBAR_COLLAPSE_STORAGE_PREFIX = "tarot-sidebar-collapsed:"; +const DETAIL_COLLAPSE_STORAGE_PREFIX = "tarot-detail-collapsed:"; +const DEFAULT_DATASET_ENTRY_COLLAPSED = true; +const DEFAULT_DATASET_DETAIL_COLLAPSED = false; +const DEFAULT_SETTINGS = { + latitude: 51.5074, + longitude: -0.1278, + timeFormat: "minutes", + birthDate: "", + tarotDeck: DEFAULT_TAROT_DECK +}; + +const PLANET_CALENDAR_STYLES = { + saturn: { + name: "♄ Saturn", + color: "#f4f4f5", + backgroundColor: "#0a0a0a", + borderColor: "#0a0a0a" + }, + jupiter: { + name: "♃ Jupiter", + color: "#eff6ff", + backgroundColor: "#1d4ed8", + borderColor: "#1d4ed8" + }, + mars: { + name: "♂ Mars", + color: "#fff1f2", + backgroundColor: "#dc2626", + borderColor: "#dc2626" + }, + sol: { + name: "☉ Sol", + color: "#111827", + backgroundColor: "#facc15", + borderColor: "#eab308" + }, + venus: { + name: "♀ Venus", + color: "#ecfdf5", + backgroundColor: "#16a34a", + borderColor: "#15803d" + }, + mercury: { + name: "☿ Mercury", + color: "#111827", + backgroundColor: "#fb923c", + borderColor: "#f97316" + }, + luna: { + name: "☾ Luna", + color: "#111827", + backgroundColor: "#e2e8f0", + borderColor: "#cbd5e1" + } +}; + +const planetaryCalendars = PLANET_CALENDAR_ORDER.map((planetId) => { + const style = PLANET_CALENDAR_STYLES[planetId]; + return { + id: `planet-${planetId}`, + name: style.name, + color: style.color, + backgroundColor: style.backgroundColor, + dragBackgroundColor: style.backgroundColor, + borderColor: style.borderColor + }; +}); + +const calendar = new tui.Calendar("#calendar", { + defaultView: "week", + usageStatistics: false, + isReadOnly: true, + useFormPopup: false, + useDetailPopup: false, + gridSelection: false, + calendars: [ + ...planetaryCalendars, + { + id: "planetary", + name: "Planetary (Fallback)", + color: "#f4f4f5", + backgroundColor: "#52525b", + dragBackgroundColor: "#52525b", + borderColor: "#52525b" + }, + { + id: "astrology", + name: "Astrology & Tarot", + color: "#18181b", + backgroundColor: "#fcd34d", + dragBackgroundColor: "#fcd34d", + borderColor: "#fcd34d" + } + ], + week: { + ...baseWeekOptions, + startDayOfWeek: getCenteredWeekStartDay(new Date()) + } +}); + +let referenceData = null; +let magickDataset = null; +let currentGeo = null; +let nowInterval = null; +let centeredDayKey = getDateKey(new Date()); +let renderInProgress = false; +let currentTimeFormat = "minutes"; +let currentSettings = { ...DEFAULT_SETTINGS }; +let monthStripResizeFrame = null; +let lastNowSkyGeoKey = ""; +let lastNowSkySourceUrl = ""; +let activeSection = "home"; +let activeTarotSpread = null; // null = browse view; "three-card" | "celtic-cross" = spread view +let activeTarotSpreadDraw = []; +let numbersSectionInitialized = false; +let activeNumberValue = 0; +const NUMBERS_SPECIAL_BASE_VALUES = [1, 2, 3, 4]; +const numbersSpecialFlipState = new Map(); + +const DEFAULT_NUMBER_ENTRIES = Array.from({ length: 10 }, (_, value) => ({ + value, + label: `${value}`, + opposite: 9 - value, + digitalRoot: value, + summary: "", + keywords: [], + associations: { + kabbalahNode: value === 0 ? 10 : value, + playingSuit: "hearts" + } +})); + +function normalizeNumberValue(value) { + const parsed = Number(value); + if (!Number.isFinite(parsed)) { + return 0; + } + const normalized = Math.trunc(parsed); + if (normalized < 0) { + return 0; + } + if (normalized > 9) { + return 9; + } + return normalized; +} + +function normalizeNumberEntry(rawEntry) { + if (!rawEntry || typeof rawEntry !== "object") { + return null; + } + + const value = normalizeNumberValue(rawEntry.value); + const oppositeRaw = Number(rawEntry.opposite); + const opposite = Number.isFinite(oppositeRaw) + ? normalizeNumberValue(oppositeRaw) + : (9 - value); + const digitalRootRaw = Number(rawEntry.digitalRoot); + const digitalRoot = Number.isFinite(digitalRootRaw) + ? normalizeNumberValue(digitalRootRaw) + : value; + const kabbalahNodeRaw = Number(rawEntry?.associations?.kabbalahNode); + const kabbalahNode = Number.isFinite(kabbalahNodeRaw) + ? Math.max(1, Math.trunc(kabbalahNodeRaw)) + : (value === 0 ? 10 : value); + const tarotTrumpNumbersRaw = Array.isArray(rawEntry?.associations?.tarotTrumpNumbers) + ? rawEntry.associations.tarotTrumpNumbers + : []; + const tarotTrumpNumbers = Array.from(new Set( + tarotTrumpNumbersRaw + .map((item) => Number(item)) + .filter((item) => Number.isFinite(item)) + .map((item) => Math.trunc(item)) + )); + const playingSuitRaw = String(rawEntry?.associations?.playingSuit || "").trim().toLowerCase(); + const playingSuit = ["hearts", "diamonds", "clubs", "spades"].includes(playingSuitRaw) + ? playingSuitRaw + : "hearts"; + + return { + value, + label: String(rawEntry.label || value), + opposite, + digitalRoot, + summary: String(rawEntry.summary || ""), + keywords: Array.isArray(rawEntry.keywords) + ? rawEntry.keywords.map((keyword) => String(keyword || "").trim()).filter(Boolean) + : [], + associations: { + kabbalahNode, + tarotTrumpNumbers, + playingSuit + } + }; +} + +function getNumbersDatasetEntries() { + const numbersData = magickDataset?.grouped?.numbers; + const rawEntries = Array.isArray(numbersData) + ? numbersData + : (Array.isArray(numbersData?.entries) ? numbersData.entries : []); + + const normalizedEntries = rawEntries + .map((entry) => normalizeNumberEntry(entry)) + .filter(Boolean) + .sort((left, right) => left.value - right.value); + + return normalizedEntries.length + ? normalizedEntries + : DEFAULT_NUMBER_ENTRIES; +} + +function getNumberEntryByValue(value) { + const entries = getNumbersDatasetEntries(); + const normalized = normalizeNumberValue(value); + return entries.find((entry) => entry.value === normalized) || entries[0] || null; +} + +function getCalendarMonthLinksForNumber(value) { + const normalized = normalizeNumberValue(value); + const calendarGroups = [ + { + calendarId: "gregorian", + calendarLabel: "Gregorian", + months: Array.isArray(referenceData?.calendarMonths) ? referenceData.calendarMonths : [] + }, + { + calendarId: "hebrew", + calendarLabel: "Hebrew", + months: Array.isArray(referenceData?.hebrewCalendar?.months) ? referenceData.hebrewCalendar.months : [] + }, + { + calendarId: "islamic", + calendarLabel: "Islamic", + months: Array.isArray(referenceData?.islamicCalendar?.months) ? referenceData.islamicCalendar.months : [] + }, + { + calendarId: "wheel-of-year", + calendarLabel: "Wheel of the Year", + months: Array.isArray(referenceData?.wheelOfYear?.months) ? referenceData.wheelOfYear.months : [] + } + ]; + + const links = []; + calendarGroups.forEach((group) => { + group.months.forEach((month) => { + const monthOrder = Number(month?.order); + const normalizedOrder = Number.isFinite(monthOrder) ? Math.trunc(monthOrder) : null; + const monthRoot = normalizedOrder != null ? computeDigitalRoot(normalizedOrder) : null; + if (monthRoot !== normalized) { + return; + } + + links.push({ + calendarId: group.calendarId, + calendarLabel: group.calendarLabel, + monthId: String(month.id || "").trim(), + monthName: String(month.name || month.id || "Month").trim(), + monthOrder: normalizedOrder + }); + }); + }); + + return links.filter((link) => link.monthId); +} + +const PLAYING_SUIT_SYMBOL = { + hearts: "♥", + diamonds: "♦", + clubs: "♣", + spades: "♠" +}; + +const PLAYING_SUIT_LABEL = { + hearts: "Hearts", + diamonds: "Diamonds", + clubs: "Clubs", + spades: "Spades" +}; + +const PLAYING_SUIT_TO_TAROT = { + hearts: "Cups", + diamonds: "Pentacles", + clubs: "Wands", + spades: "Swords" +}; + +const PLAYING_RANKS = [ + { rank: "A", rankLabel: "Ace", rankValue: 1 }, + { rank: "2", rankLabel: "Two", rankValue: 2 }, + { rank: "3", rankLabel: "Three", rankValue: 3 }, + { rank: "4", rankLabel: "Four", rankValue: 4 }, + { rank: "5", rankLabel: "Five", rankValue: 5 }, + { rank: "6", rankLabel: "Six", rankValue: 6 }, + { rank: "7", rankLabel: "Seven", rankValue: 7 }, + { rank: "8", rankLabel: "Eight", rankValue: 8 }, + { rank: "9", rankLabel: "Nine", rankValue: 9 }, + { rank: "10", rankLabel: "Ten", rankValue: 10 }, + { rank: "J", rankLabel: "Jack", rankValue: null }, + { rank: "Q", rankLabel: "Queen", rankValue: null }, + { rank: "K", rankLabel: "King", rankValue: null } +]; + +function rankLabelToTarotMinorRank(rankLabel) { + const key = String(rankLabel || "").trim().toLowerCase(); + if (key === "10" || key === "ten") return "Princess"; + if (key === "j" || key === "jack") return "Prince"; + if (key === "q" || key === "queen") return "Queen"; + if (key === "k" || key === "king") return "Knight"; + return String(rankLabel || "").trim(); +} + +function buildFallbackPlayingDeckEntries() { + const entries = []; + Object.keys(PLAYING_SUIT_SYMBOL).forEach((suit) => { + PLAYING_RANKS.forEach((rank) => { + const tarotSuit = PLAYING_SUIT_TO_TAROT[suit]; + const tarotRank = rankLabelToTarotMinorRank(rank.rankLabel); + entries.push({ + id: `${rank.rank}${PLAYING_SUIT_SYMBOL[suit]}`, + suit, + suitLabel: PLAYING_SUIT_LABEL[suit], + suitSymbol: PLAYING_SUIT_SYMBOL[suit], + rank: rank.rank, + rankLabel: rank.rankLabel, + rankValue: rank.rankValue, + tarotSuit, + tarotCard: `${tarotRank} of ${tarotSuit}` + }); + }); + }); + return entries; +} + +function getPlayingDeckEntries() { + const deckData = magickDataset?.grouped?.["playing-cards-52"]; + const rawEntries = Array.isArray(deckData) + ? deckData + : (Array.isArray(deckData?.entries) ? deckData.entries : []); + + if (!rawEntries.length) { + return buildFallbackPlayingDeckEntries(); + } + + return rawEntries + .map((entry) => { + const suit = String(entry?.suit || "").trim().toLowerCase(); + const rankLabel = String(entry?.rankLabel || "").trim(); + const rank = String(entry?.rank || "").trim(); + if (!suit || !rank) { + return null; + } + + const suitSymbol = String(entry?.suitSymbol || PLAYING_SUIT_SYMBOL[suit] || "").trim(); + const tarotSuit = String(entry?.tarotSuit || PLAYING_SUIT_TO_TAROT[suit] || "").trim(); + const tarotCard = String(entry?.tarotCard || "").trim(); + const rankValueRaw = Number(entry?.rankValue); + const rankValue = Number.isFinite(rankValueRaw) ? Math.trunc(rankValueRaw) : null; + + return { + id: String(entry?.id || `${rank}${suitSymbol}`).trim(), + suit, + suitLabel: String(entry?.suitLabel || PLAYING_SUIT_LABEL[suit] || suit).trim(), + suitSymbol, + rank, + rankLabel: rankLabel || rank, + rankValue, + tarotSuit, + tarotCard: tarotCard || `${rankLabelToTarotMinorRank(rankLabel || rank)} of ${tarotSuit}` + }; + }) + .filter(Boolean); +} + +function findPlayingCardBySuitAndValue(entries, suit, value) { + const normalizedSuit = String(suit || "").trim().toLowerCase(); + const targetValue = Number(value); + return entries.find((entry) => entry.suit === normalizedSuit && Number(entry.rankValue) === targetValue) || null; +} + +function buildNumbersSpecialCardSlots(playingSuit) { + const suit = String(playingSuit || "hearts").trim().toLowerCase(); + const selectedSuit = ["hearts", "diamonds", "clubs", "spades"].includes(suit) ? suit : "hearts"; + const deckEntries = getPlayingDeckEntries(); + + const cardEl = document.createElement("div"); + cardEl.className = "numbers-detail-card numbers-special-card-section"; + + const headingEl = document.createElement("strong"); + headingEl.textContent = "4 Card Arrangement"; + + const subEl = document.createElement("div"); + subEl.className = "numbers-detail-text numbers-detail-text--muted"; + subEl.textContent = `Click a card to flip to its opposite (${PLAYING_SUIT_LABEL[selectedSuit]} ↔ ${PLAYING_SUIT_TO_TAROT[selectedSuit]}).`; + + const boardEl = document.createElement("div"); + boardEl.className = "numbers-special-board"; + + NUMBERS_SPECIAL_BASE_VALUES.forEach((baseValue) => { + const oppositeValue = 9 - baseValue; + const frontCard = findPlayingCardBySuitAndValue(deckEntries, selectedSuit, baseValue); + const backCard = findPlayingCardBySuitAndValue(deckEntries, selectedSuit, oppositeValue); + if (!frontCard || !backCard) { + return; + } + + const slotKey = `${selectedSuit}:${baseValue}`; + const isFlipped = Boolean(numbersSpecialFlipState.get(slotKey)); + + const faceBtn = document.createElement("button"); + faceBtn.type = "button"; + faceBtn.className = `numbers-special-card${isFlipped ? " is-flipped" : ""}`; + faceBtn.setAttribute("aria-pressed", isFlipped ? "true" : "false"); + faceBtn.setAttribute("aria-label", `${frontCard.rankLabel} of ${frontCard.suitLabel}. Click to flip to ${backCard.rankLabel}.`); + faceBtn.dataset.suit = selectedSuit; + + const innerEl = document.createElement("div"); + innerEl.className = "numbers-special-card-inner"; + + const frontFaceEl = document.createElement("div"); + frontFaceEl.className = "numbers-special-card-face numbers-special-card-face--front"; + + const frontRankEl = document.createElement("div"); + frontRankEl.className = "numbers-special-card-rank"; + frontRankEl.textContent = frontCard.rankLabel; + + const frontSuitEl = document.createElement("div"); + frontSuitEl.className = "numbers-special-card-suit"; + frontSuitEl.textContent = frontCard.suitSymbol; + + const frontMetaEl = document.createElement("div"); + frontMetaEl.className = "numbers-special-card-meta"; + frontMetaEl.textContent = frontCard.tarotCard; + + frontFaceEl.append(frontRankEl, frontSuitEl, frontMetaEl); + + const backFaceEl = document.createElement("div"); + backFaceEl.className = "numbers-special-card-face numbers-special-card-face--back"; + + const backTagEl = document.createElement("div"); + backTagEl.className = "numbers-special-card-tag"; + backTagEl.textContent = "Opposite"; + + const backRankEl = document.createElement("div"); + backRankEl.className = "numbers-special-card-rank"; + backRankEl.textContent = backCard.rankLabel; + + const backSuitEl = document.createElement("div"); + backSuitEl.className = "numbers-special-card-suit"; + backSuitEl.textContent = backCard.suitSymbol; + + const backMetaEl = document.createElement("div"); + backMetaEl.className = "numbers-special-card-meta"; + backMetaEl.textContent = backCard.tarotCard; + + backFaceEl.append(backTagEl, backRankEl, backSuitEl, backMetaEl); + + innerEl.append(frontFaceEl, backFaceEl); + faceBtn.append(innerEl); + + faceBtn.addEventListener("click", () => { + const next = !Boolean(numbersSpecialFlipState.get(slotKey)); + numbersSpecialFlipState.set(slotKey, next); + faceBtn.classList.toggle("is-flipped", next); + faceBtn.setAttribute("aria-pressed", next ? "true" : "false"); + }); + + boardEl.appendChild(faceBtn); + }); + + if (!boardEl.childElementCount) { + const emptyEl = document.createElement("div"); + emptyEl.className = "numbers-detail-text numbers-detail-text--muted"; + emptyEl.textContent = "No card slots available for this mapping yet."; + boardEl.appendChild(emptyEl); + } + + cardEl.append(headingEl, subEl, boardEl); + return cardEl; +} + +function renderNumbersSpecialPanel(value) { + if (!numbersSpecialPanelEl) { + return; + } + + const entry = getNumberEntryByValue(value); + const playingSuit = entry?.associations?.playingSuit || "hearts"; + const boardCardEl = buildNumbersSpecialCardSlots(playingSuit); + numbersSpecialPanelEl.replaceChildren(boardCardEl); +} + +function computeDigitalRoot(value) { + let current = Math.abs(Math.trunc(Number(value))); + if (!Number.isFinite(current)) { + return null; + } + while (current >= 10) { + current = String(current) + .split("") + .reduce((sum, digit) => sum + Number(digit), 0); + } + return current; +} + +function describeDigitalRootReduction(value) { + let current = Math.abs(Math.trunc(Number(value))); + if (!Number.isFinite(current)) { + return ""; + } + + if (current < 10) { + return `${current} → ${current}`; + } + + const parts = [`${current}`]; + while (current >= 10) { + const digits = String(current).split("").map((digit) => Number(digit)); + const sum = digits.reduce((acc, digit) => acc + digit, 0); + parts.push(`${digits.join(" + ")} = ${sum}`); + current = sum; + } + + return parts.join(" → "); +} + +function parseTarotCardNumber(rawValue) { + if (typeof rawValue === "number") { + return Number.isFinite(rawValue) ? Math.trunc(rawValue) : null; + } + + if (typeof rawValue === "string") { + const trimmed = rawValue.trim(); + if (!trimmed || !/^-?\d+$/.test(trimmed)) { + return null; + } + return Number(trimmed); + } + + return null; +} + +const TAROT_RANK_NUMBER_MAP = { + ace: 1, + two: 2, + three: 3, + four: 4, + five: 5, + six: 6, + seven: 7, + eight: 8, + nine: 9, + ten: 10 +}; + +function extractTarotCardNumericValue(card) { + const directNumber = parseTarotCardNumber(card?.number); + if (directNumber !== null) { + return directNumber; + } + + const rankKey = String(card?.rank || "").trim().toLowerCase(); + if (Object.prototype.hasOwnProperty.call(TAROT_RANK_NUMBER_MAP, rankKey)) { + return TAROT_RANK_NUMBER_MAP[rankKey]; + } + + const numerologyRelation = Array.isArray(card?.relations) + ? card.relations.find((relation) => String(relation?.type || "").trim().toLowerCase() === "numerology") + : null; + const relationValue = Number(numerologyRelation?.data?.value); + if (Number.isFinite(relationValue)) { + return Math.trunc(relationValue); + } + + return null; +} + +function getAlphabetPositionLinksForDigitalRoot(targetRoot) { + const alphabets = magickDataset?.grouped?.alphabets; + if (!alphabets || typeof alphabets !== "object") { + return []; + } + + const links = []; + + const addLink = (alphabetLabel, entry, buttonLabel, detail) => { + const index = Number(entry?.index); + if (!Number.isFinite(index)) { + return; + } + + const normalizedIndex = Math.trunc(index); + if (computeDigitalRoot(normalizedIndex) !== targetRoot) { + return; + } + + links.push({ + alphabet: alphabetLabel, + index: normalizedIndex, + label: buttonLabel, + detail + }); + }; + + const toTitle = (value) => String(value || "") + .trim() + .replace(/[_-]+/g, " ") + .replace(/\s+/g, " ") + .toLowerCase() + .replace(/\b([a-z])/g, (match, ch) => ch.toUpperCase()); + + const englishEntries = Array.isArray(alphabets.english) ? alphabets.english : []; + englishEntries.forEach((entry) => { + const letter = String(entry?.letter || "").trim(); + if (!letter) { + return; + } + + addLink( + "English", + entry, + `${letter}`, + { + alphabet: "english", + englishLetter: letter + } + ); + }); + + const greekEntries = Array.isArray(alphabets.greek) ? alphabets.greek : []; + greekEntries.forEach((entry) => { + const greekName = String(entry?.name || "").trim(); + if (!greekName) { + return; + } + + const glyph = String(entry?.char || "").trim(); + const displayName = String(entry?.displayName || toTitle(greekName)).trim(); + addLink( + "Greek", + entry, + glyph ? `${displayName} - ${glyph}` : displayName, + { + alphabet: "greek", + greekName + } + ); + }); + + const hebrewEntries = Array.isArray(alphabets.hebrew) ? alphabets.hebrew : []; + hebrewEntries.forEach((entry) => { + const hebrewLetterId = String(entry?.hebrewLetterId || "").trim(); + if (!hebrewLetterId) { + return; + } + + const glyph = String(entry?.char || "").trim(); + const name = String(entry?.name || hebrewLetterId).trim(); + const displayName = toTitle(name); + addLink( + "Hebrew", + entry, + glyph ? `${displayName} - ${glyph}` : displayName, + { + alphabet: "hebrew", + hebrewLetterId + } + ); + }); + + const arabicEntries = Array.isArray(alphabets.arabic) ? alphabets.arabic : []; + arabicEntries.forEach((entry) => { + const arabicName = String(entry?.name || "").trim(); + if (!arabicName) { + return; + } + + const glyph = String(entry?.char || "").trim(); + const displayName = toTitle(arabicName); + addLink( + "Arabic", + entry, + glyph ? `${displayName} - ${glyph}` : displayName, + { + alphabet: "arabic", + arabicName + } + ); + }); + + const enochianEntries = Array.isArray(alphabets.enochian) ? alphabets.enochian : []; + enochianEntries.forEach((entry) => { + const enochianId = String(entry?.id || "").trim(); + if (!enochianId) { + return; + } + + const title = String(entry?.title || enochianId).trim(); + const displayName = toTitle(title); + addLink( + "Enochian", + entry, + `${displayName}`, + { + alphabet: "enochian", + enochianId + } + ); + }); + + return links.sort((left, right) => { + if (left.index !== right.index) { + return left.index - right.index; + } + const alphabetCompare = left.alphabet.localeCompare(right.alphabet); + if (alphabetCompare !== 0) { + return alphabetCompare; + } + return left.label.localeCompare(right.label); + }); +} + +function getTarotCardsForDigitalRoot(targetRoot, numberEntry = null) { + if (typeof ensureTarotSection === "function" && referenceData) { + ensureTarotSection(referenceData, magickDataset); + } + + const allCards = window.TarotSectionUi?.getCards?.() || []; + const explicitTrumpNumbers = Array.isArray(numberEntry?.associations?.tarotTrumpNumbers) + ? numberEntry.associations.tarotTrumpNumbers + .map((value) => Number(value)) + .filter((value) => Number.isFinite(value)) + .map((value) => Math.trunc(value)) + : []; + + const filteredCards = explicitTrumpNumbers.length + ? allCards.filter((card) => { + const numberValue = parseTarotCardNumber(card?.number); + return card?.arcana === "Major" && numberValue !== null && explicitTrumpNumbers.includes(numberValue); + }) + : allCards.filter((card) => { + const numberValue = extractTarotCardNumericValue(card); + return numberValue !== null && computeDigitalRoot(numberValue) === targetRoot; + }); + + return filteredCards + .sort((left, right) => { + const leftNumber = extractTarotCardNumericValue(left); + const rightNumber = extractTarotCardNumericValue(right); + if (leftNumber !== rightNumber) { + return (leftNumber ?? 0) - (rightNumber ?? 0); + } + if (left?.arcana !== right?.arcana) { + return left?.arcana === "Major" ? -1 : 1; + } + return String(left?.name || "").localeCompare(String(right?.name || "")); + }); +} + +function renderNumbersList() { + if (!numbersListEl) { + return; + } + + const entries = getNumbersDatasetEntries(); + if (!entries.some((entry) => entry.value === activeNumberValue)) { + activeNumberValue = entries[0]?.value ?? 0; + } + + const fragment = document.createDocumentFragment(); + entries.forEach((entry) => { + const button = document.createElement("button"); + button.type = "button"; + button.className = `planet-list-item${entry.value === activeNumberValue ? " is-selected" : ""}`; + button.dataset.numberValue = String(entry.value); + button.setAttribute("role", "option"); + button.setAttribute("aria-selected", entry.value === activeNumberValue ? "true" : "false"); + + const nameEl = document.createElement("span"); + nameEl.className = "planet-list-name"; + nameEl.textContent = `${entry.label}`; + + const metaEl = document.createElement("span"); + metaEl.className = "planet-list-meta"; + metaEl.textContent = `Opposite ${entry.opposite}`; + + button.append(nameEl, metaEl); + fragment.appendChild(button); + }); + + numbersListEl.replaceChildren(fragment); + if (numbersCountEl) { + numbersCountEl.textContent = `${entries.length} entries`; + } +} + +function renderNumberDetail(value) { + const entry = getNumberEntryByValue(value); + if (!entry) { + return; + } + + const normalized = entry.value; + const opposite = entry.opposite; + const rootTarget = normalizeNumberValue(entry.digitalRoot); + + if (numbersDetailNameEl) { + numbersDetailNameEl.textContent = `Number ${normalized} · ${entry.label}`; + } + + if (numbersDetailTypeEl) { + numbersDetailTypeEl.textContent = `Opposite: ${opposite}`; + } + + if (numbersDetailSummaryEl) { + numbersDetailSummaryEl.textContent = entry.summary || ""; + } + + renderNumbersSpecialPanel(normalized); + + if (!numbersDetailBodyEl) { + return; + } + + numbersDetailBodyEl.replaceChildren(); + + const pairCardEl = document.createElement("div"); + pairCardEl.className = "numbers-detail-card"; + + const pairHeadingEl = document.createElement("strong"); + pairHeadingEl.textContent = "Number Pair"; + + const pairTextEl = document.createElement("div"); + pairTextEl.className = "numbers-detail-text"; + pairTextEl.textContent = `Opposite: ${opposite}`; + + const keywordText = entry.keywords.length + ? `Keywords: ${entry.keywords.join(", ")}` + : "Keywords: --"; + const pairKeywordsEl = document.createElement("div"); + pairKeywordsEl.className = "numbers-detail-text numbers-detail-text--muted"; + pairKeywordsEl.textContent = keywordText; + + const oppositeBtn = document.createElement("button"); + oppositeBtn.type = "button"; + oppositeBtn.className = "numbers-nav-btn"; + oppositeBtn.textContent = `Open Opposite Number ${opposite}`; + oppositeBtn.addEventListener("click", () => { + selectNumberEntry(opposite); + }); + + pairCardEl.append(pairHeadingEl, pairTextEl, pairKeywordsEl, oppositeBtn); + + const kabbalahCardEl = document.createElement("div"); + kabbalahCardEl.className = "numbers-detail-card"; + + const kabbalahHeadingEl = document.createElement("strong"); + kabbalahHeadingEl.textContent = "Kabbalah Link"; + + const kabbalahNode = Number(entry?.associations?.kabbalahNode); + const kabbalahTextEl = document.createElement("div"); + kabbalahTextEl.className = "numbers-detail-text"; + kabbalahTextEl.textContent = `Tree node target: ${kabbalahNode}`; + + const kabbalahBtn = document.createElement("button"); + kabbalahBtn.type = "button"; + kabbalahBtn.className = "numbers-nav-btn"; + kabbalahBtn.textContent = `Open Kabbalah Tree Node ${kabbalahNode}`; + kabbalahBtn.addEventListener("click", () => { + document.dispatchEvent(new CustomEvent("nav:kabbalah-path", { + detail: { pathNo: kabbalahNode } + })); + }); + + kabbalahCardEl.append(kabbalahHeadingEl, kabbalahTextEl, kabbalahBtn); + + const alphabetCardEl = document.createElement("div"); + alphabetCardEl.className = "numbers-detail-card"; + + const alphabetHeadingEl = document.createElement("strong"); + alphabetHeadingEl.textContent = "Alphabet Links"; + + const alphabetLinksWrapEl = document.createElement("div"); + alphabetLinksWrapEl.className = "numbers-links-wrap"; + + const alphabetLinks = getAlphabetPositionLinksForDigitalRoot(rootTarget); + if (!alphabetLinks.length) { + const emptyAlphabetEl = document.createElement("div"); + emptyAlphabetEl.className = "numbers-detail-text numbers-detail-text--muted"; + emptyAlphabetEl.textContent = "No alphabet position entries found for this digital root yet."; + alphabetLinksWrapEl.appendChild(emptyAlphabetEl); + } else { + alphabetLinks.forEach((link) => { + const button = document.createElement("button"); + button.type = "button"; + button.className = "numbers-nav-btn"; + button.textContent = `${link.alphabet}: ${link.label}`; + button.addEventListener("click", () => { + document.dispatchEvent(new CustomEvent("nav:alphabet", { + detail: link.detail + })); + }); + alphabetLinksWrapEl.appendChild(button); + }); + } + + alphabetCardEl.append(alphabetHeadingEl, alphabetLinksWrapEl); + + const tarotCardEl = document.createElement("div"); + tarotCardEl.className = "numbers-detail-card"; + + const tarotHeadingEl = document.createElement("strong"); + tarotHeadingEl.textContent = "Tarot Links"; + + const tarotLinksWrapEl = document.createElement("div"); + tarotLinksWrapEl.className = "numbers-links-wrap"; + + const tarotCards = getTarotCardsForDigitalRoot(rootTarget, entry); + if (!tarotCards.length) { + const emptyEl = document.createElement("div"); + emptyEl.className = "numbers-detail-text numbers-detail-text--muted"; + emptyEl.textContent = "No tarot numeric entries found yet for this root. Add card numbers to map them."; + tarotLinksWrapEl.appendChild(emptyEl); + } else { + tarotCards.forEach((card) => { + const button = document.createElement("button"); + button.type = "button"; + button.className = "numbers-nav-btn"; + button.textContent = `${card.name}`; + button.addEventListener("click", () => { + document.dispatchEvent(new CustomEvent("nav:tarot-trump", { + detail: { cardName: card.name } + })); + }); + tarotLinksWrapEl.appendChild(button); + }); + } + + tarotCardEl.append(tarotHeadingEl, tarotLinksWrapEl); + + const calendarCardEl = document.createElement("div"); + calendarCardEl.className = "numbers-detail-card"; + + const calendarHeadingEl = document.createElement("strong"); + calendarHeadingEl.textContent = "Calendar Links"; + + const calendarLinksWrapEl = document.createElement("div"); + calendarLinksWrapEl.className = "numbers-links-wrap"; + + const calendarLinks = getCalendarMonthLinksForNumber(normalized); + if (!calendarLinks.length) { + const emptyCalendarEl = document.createElement("div"); + emptyCalendarEl.className = "numbers-detail-text numbers-detail-text--muted"; + emptyCalendarEl.textContent = "No calendar months currently mapped to this number."; + calendarLinksWrapEl.appendChild(emptyCalendarEl); + } else { + calendarLinks.forEach((link) => { + const button = document.createElement("button"); + button.type = "button"; + button.className = "numbers-nav-btn"; + button.textContent = `${link.calendarLabel}: ${link.monthName} (Month ${link.monthOrder})`; + button.addEventListener("click", () => { + document.dispatchEvent(new CustomEvent("nav:calendar-month", { + detail: { + calendarId: link.calendarId, + monthId: link.monthId + } + })); + }); + calendarLinksWrapEl.appendChild(button); + }); + } + + calendarCardEl.append(calendarHeadingEl, calendarLinksWrapEl); + + numbersDetailBodyEl.append(pairCardEl, kabbalahCardEl, alphabetCardEl, tarotCardEl, calendarCardEl); +} + +function selectNumberEntry(value) { + const entry = getNumberEntryByValue(value); + activeNumberValue = entry ? entry.value : 0; + renderNumbersList(); + renderNumberDetail(activeNumberValue); +} + +function ensureNumbersSection() { + if (!numbersListEl) { + return; + } + + if (!numbersSectionInitialized) { + numbersListEl.addEventListener("click", (event) => { + const target = event.target; + if (!(target instanceof Node)) { + return; + } + const button = target instanceof Element + ? target.closest(".planet-list-item") + : null; + if (!(button instanceof HTMLButtonElement)) { + return; + } + const value = Number(button.dataset.numberValue); + if (!Number.isFinite(value)) { + return; + } + selectNumberEntry(value); + }); + + numbersSectionInitialized = true; + } + + renderNumbersList(); + renderNumberDetail(activeNumberValue); +} + +const THREE_CARD_POSITIONS = [ + { pos: "past", label: "Past" }, + { pos: "present", label: "Present" }, + { pos: "future", label: "Future" } +]; + +const CELTIC_CROSS_POSITIONS = [ + { pos: "crown", label: "Crown" }, + { pos: "out", label: "Outcome" }, + { pos: "past", label: "Recent Past" }, + { pos: "present", label: "Present" }, + { pos: "near-fut", label: "Near Future" }, + { pos: "hope", label: "Hopes & Fears" }, + { pos: "chall", label: "Challenge" }, + { pos: "env", label: "Environment" }, + { pos: "found", label: "Foundation" }, + { pos: "self", label: "Self" } +]; + +function normalizeTarotSpread(value) { + return value === "celtic-cross" ? "celtic-cross" : "three-card"; +} + +function drawNFromDeck(n) { + const allCards = window.TarotSectionUi?.getCards?.() || []; + if (!allCards.length) return []; + + const shuffled = [...allCards]; + for (let index = shuffled.length - 1; index > 0; index -= 1) { + const swapIndex = Math.floor(Math.random() * (index + 1)); + [shuffled[index], shuffled[swapIndex]] = [shuffled[swapIndex], shuffled[index]]; + } + + return shuffled.slice(0, n).map((card) => ({ + ...card, + reversed: Math.random() < 0.3 + })); +} + +function escapeHtml(value) { + return String(value || "") + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/\"/g, """) + .replace(/'/g, "'"); +} + +function getSpreadPositions(spreadId) { + return spreadId === "celtic-cross" ? CELTIC_CROSS_POSITIONS : THREE_CARD_POSITIONS; +} + +function regenerateTarotSpreadDraw() { + const normalizedSpread = normalizeTarotSpread(activeTarotSpread); + const positions = getSpreadPositions(normalizedSpread); + const cards = drawNFromDeck(positions.length); + activeTarotSpreadDraw = positions.map((position, index) => ({ + position, + card: cards[index] || null + })); +} + +function renderTarotSpreadMeanings() { + if (!tarotSpreadMeaningsEl) { + return; + } + + if (!activeTarotSpreadDraw.length || activeTarotSpreadDraw.some((entry) => !entry.card)) { + tarotSpreadMeaningsEl.innerHTML = ""; + return; + } + + tarotSpreadMeaningsEl.innerHTML = activeTarotSpreadDraw.map((entry) => { + const positionLabel = escapeHtml(entry.position.label).toUpperCase(); + const card = entry.card; + const cardName = escapeHtml(card.name || "Unknown Card"); + const meaningText = escapeHtml(card.reversed ? (card.meanings?.reversed || card.summary || "--") : (card.meanings?.upright || card.summary || "--")); + const keywords = Array.isArray(card.keywords) + ? card.keywords.map((keyword) => String(keyword || "").trim()).filter(Boolean) + : []; + const keywordMarkup = keywords.length + ? `
Keywords: ${escapeHtml(keywords.join(", "))}
` + : ""; + const orientationMarkup = card.reversed + ? " (Reversed)" + : ""; + + return `
` + + `
${positionLabel}: ${cardName}${orientationMarkup}
` + + `
${meaningText}
` + + keywordMarkup + + `
`; + }).join(""); +} + +function renderTarotSpread() { + if (!tarotSpreadBoardEl) return; + const normalizedSpread = normalizeTarotSpread(activeTarotSpread); + const isCeltic = normalizedSpread === "celtic-cross"; + + if (!activeTarotSpreadDraw.length) { + regenerateTarotSpreadDraw(); + } + + tarotSpreadBoardEl.className = `tarot-spread-board tarot-spread-board--${isCeltic ? "celtic" : "three"}`; + + if (!activeTarotSpreadDraw.length || activeTarotSpreadDraw.some((entry) => !entry.card)) { + tarotSpreadBoardEl.innerHTML = `
Tarot deck not loaded yet — open Cards first, then return to Spread.
`; + if (tarotSpreadMeaningsEl) { + tarotSpreadMeaningsEl.innerHTML = ""; + } + return; + } + + renderTarotSpreadMeanings(); + + tarotSpreadBoardEl.innerHTML = activeTarotSpreadDraw.map((entry) => { + const position = entry.position; + const card = entry.card; + const imgSrc = window.TarotCardImages?.resolveTarotCardImage?.(card.name); + const reversed = card.reversed; + const wrapClass = reversed ? "spread-card-wrap is-reversed" : "spread-card-wrap"; + const imgHtml = imgSrc + ? `${escapeHtml(card.name)}` + : `
${escapeHtml(card.name)}
`; + const reversedTag = reversed ? `Reversed` : ""; + return `
` + + `
${escapeHtml(position.label)}
` + + `
${imgHtml}
` + + `
${escapeHtml(card.name)}${reversedTag}
` + + `
`; + }).join(""); +} + +function applyTarotSpreadViewState() { + const isSpreadOpen = activeTarotSpread !== null; + const isCeltic = activeTarotSpread === "celtic-cross"; + const isTarotActive = activeSection === "tarot"; + + if (tarotBrowseViewEl) tarotBrowseViewEl.hidden = isSpreadOpen; + if (tarotSpreadViewEl) tarotSpreadViewEl.hidden = !isSpreadOpen; + + if (tarotSpreadBtnThreeEl) tarotSpreadBtnThreeEl.classList.toggle("is-active", isSpreadOpen && !isCeltic); + if (tarotSpreadBtnCelticEl) tarotSpreadBtnCelticEl.classList.toggle("is-active", isSpreadOpen && isCeltic); + + if (openTarotCardsEl) openTarotCardsEl.classList.toggle("is-active", isTarotActive && !isSpreadOpen); + if (openTarotSpreadEl) openTarotSpreadEl.classList.toggle("is-active", isTarotActive && isSpreadOpen); +} + +function showTarotCardsView() { + activeTarotSpread = null; + activeTarotSpreadDraw = []; + applyTarotSpreadViewState(); + if (typeof ensureTarotSection === "function" && referenceData) { + ensureTarotSection(referenceData, magickDataset); + } + const detailPanelEl = document.querySelector("#tarot-browse-view .tarot-detail-panel"); + if (detailPanelEl instanceof HTMLElement) { + detailPanelEl.scrollTop = 0; + } +} + +function showTarotSpreadView(spreadId = "three-card") { + activeTarotSpread = normalizeTarotSpread(spreadId); + regenerateTarotSpreadDraw(); + applyTarotSpreadViewState(); + if (typeof ensureTarotSection === "function" && referenceData) { + ensureTarotSection(referenceData, magickDataset); + } + renderTarotSpread(); +} + +function setTarotSpread(spreadId, openTarotSection = false) { + if (openTarotSection) { + setActiveSection("tarot"); + } + showTarotSpreadView(spreadId); +} + +const DEFAULT_WEEKDAY_RULERS = { + 0: { symbol: "☉", name: "Sol" }, + 1: { symbol: "☾", name: "Luna" }, + 2: { symbol: "♂", name: "Mars" }, + 3: { symbol: "☿", name: "Mercury" }, + 4: { symbol: "♃", name: "Jupiter" }, + 5: { symbol: "♀", name: "Venus" }, + 6: { symbol: "♄", name: "Saturn" } +}; + +function getWeekdayIndexFromName(weekdayName) { + const normalized = String(weekdayName || "").trim().toLowerCase(); + if (normalized === "sunday") return 0; + if (normalized === "monday") return 1; + if (normalized === "tuesday") return 2; + if (normalized === "wednesday") return 3; + if (normalized === "thursday") return 4; + if (normalized === "friday") return 5; + if (normalized === "saturday") return 6; + return null; +} + +function buildWeekdayRulerLookup(planets) { + const lookup = { ...DEFAULT_WEEKDAY_RULERS }; + if (!planets || typeof planets !== "object") { + return lookup; + } + + Object.values(planets).forEach((planet) => { + const weekdayIndex = getWeekdayIndexFromName(planet?.weekday); + if (weekdayIndex === null) { + return; + } + + lookup[weekdayIndex] = { + symbol: planet?.symbol || lookup[weekdayIndex].symbol, + name: planet?.name || lookup[weekdayIndex].name + }; + }); + + return lookup; +} + +function clamp(value, min, max) { + return Math.min(max, Math.max(min, value)); +} + +function lerp(start, end, t) { + return start + (end - start) * t; +} + +function lerpRgb(from, to, t) { + return [ + Math.round(lerp(from[0], to[0], t)), + Math.round(lerp(from[1], to[1], t)), + Math.round(lerp(from[2], to[2], t)) + ]; +} + +function rgbString(rgb) { + return `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`; +} + +function getActiveGeoForRuler() { + if (currentGeo) { + return currentGeo; + } + + try { + return parseGeoInput(); + } catch { + return null; + } +} + +function buildSunRulerGradient(geo, date) { + if (!window.SunCalc || !geo || !date) { + return null; + } + + const dayStart = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0); + const sampleCount = 48; + const samples = []; + + for (let index = 0; index <= sampleCount; index += 1) { + const sampleDate = new Date(dayStart.getTime() + index * 30 * 60 * 1000); + const position = window.SunCalc.getPosition(sampleDate, geo.latitude, geo.longitude); + const altitudeDeg = (position.altitude * 180) / Math.PI; + samples.push(altitudeDeg); + } + + const maxAltitude = Math.max(...samples); + + const NIGHT = [6, 7, 10]; + const PRE_DAWN = [22, 26, 38]; + const SUN_RED = [176, 45, 36]; + const SUN_ORANGE = [246, 133, 54]; + const SKY_BLUE = [58, 134, 255]; + + const nightFloor = -8; + const twilightEdge = -2; + const redToOrangeEdge = 2; + const orangeToBlueEdge = 8; + const daylightRange = Math.max(1, maxAltitude - orangeToBlueEdge); + + const stops = samples.map((altitudeDeg, index) => { + let color; + + if (altitudeDeg <= nightFloor) { + color = NIGHT; + } else if (altitudeDeg <= twilightEdge) { + const t = clamp((altitudeDeg - nightFloor) / (twilightEdge - nightFloor), 0, 1); + color = lerpRgb(NIGHT, PRE_DAWN, t); + } else if (altitudeDeg <= redToOrangeEdge) { + const t = clamp((altitudeDeg - twilightEdge) / (redToOrangeEdge - twilightEdge), 0, 1); + color = lerpRgb(PRE_DAWN, SUN_RED, t); + } else if (altitudeDeg <= orangeToBlueEdge) { + const t = clamp((altitudeDeg - redToOrangeEdge) / (orangeToBlueEdge - redToOrangeEdge), 0, 1); + color = lerpRgb(SUN_RED, SUN_ORANGE, t); + } else { + const t = clamp((altitudeDeg - orangeToBlueEdge) / daylightRange, 0, 1); + color = lerpRgb(SUN_ORANGE, SKY_BLUE, t); + } + + const pct = ((index / sampleCount) * 100).toFixed(2); + return `${rgbString(color)} ${pct}%`; + }); + + return `linear-gradient(to bottom, ${stops.join(", ")})`; +} + +function applySunRulerGradient(referenceDate = new Date()) { + const geo = getActiveGeoForRuler(); + if (!geo) { + return; + } + + const gradient = buildSunRulerGradient(geo, referenceDate); + if (!gradient) { + return; + } + + const rulerColumns = document.querySelectorAll(".toastui-calendar-timegrid-time-column"); + rulerColumns.forEach((column) => { + column.style.backgroundImage = gradient; + column.style.backgroundRepeat = "no-repeat"; + column.style.backgroundSize = "100% 100%"; + }); +} + +function normalizeDateLike(value) { + if (value instanceof Date) { + return value; + } + if (value && typeof value.getTime === "function") { + return new Date(value.getTime()); + } + return new Date(value); +} + +function getTimeParts(dateLike) { + const date = normalizeDateLike(dateLike); + const hours = date.getHours(); + const minutes = date.getMinutes(); + return { + hours, + minutes, + totalMinutes: hours * 60 + minutes + }; +} + +function formatHourStyle(dateLike) { + const { totalMinutes } = getTimeParts(dateLike); + return `${Math.floor(totalMinutes / 60)}hr`; +} + +function formatMinuteStyle(dateLike) { + const { totalMinutes } = getTimeParts(dateLike); + return `${totalMinutes}m`; +} + +function formatSecondStyle(dateLike) { + const { totalMinutes } = getTimeParts(dateLike); + const totalSeconds = totalMinutes * 60; + return `${totalSeconds}s`; +} + +function formatCalendarTime(dateLike) { + if (currentTimeFormat === "hours") { + return formatHourStyle(dateLike); + } + if (currentTimeFormat === "seconds") { + return formatSecondStyle(dateLike); + } + return formatMinuteStyle(dateLike); +} + +function formatCalendarTimeFromTemplatePayload(payload) { + if (payload && typeof payload.hour === "number") { + const hours = payload.hour; + const minutes = typeof payload.minutes === "number" ? payload.minutes : 0; + const totalMinutes = hours * 60 + minutes; + + if (currentTimeFormat === "hours") { + return `${Math.floor(totalMinutes / 60)}hr`; + } + + if (currentTimeFormat === "seconds") { + return `${totalMinutes * 60}s`; + } + + return `${totalMinutes}m`; + } + + if (payload && payload.time) { + return formatCalendarTime(payload.time); + } + + if (currentTimeFormat === "hours") { + return "12am"; + } + if (currentTimeFormat === "seconds") { + return "0s"; + } + return "0m"; +} + +function getMoonPhaseGlyph(phaseName) { + if (phaseName === "New Moon") return "🌑"; + if (phaseName === "Waxing Crescent") return "🌒"; + if (phaseName === "First Quarter") return "🌓"; + if (phaseName === "Waxing Gibbous") return "🌔"; + if (phaseName === "Full Moon") return "🌕"; + if (phaseName === "Waning Gibbous") return "🌖"; + if (phaseName === "Last Quarter") return "🌗"; + return "🌘"; +} + +function applyDynamicNowIndicatorVisual(referenceDate = new Date()) { + if (!currentGeo || !window.SunCalc) { + return; + } + + const labelEl = document.querySelector( + ".toastui-calendar-timegrid-time-column .toastui-calendar-timegrid-current-time" + ); + const markerEl = document.querySelector( + ".toastui-calendar-timegrid .toastui-calendar-timegrid-now-indicator .toastui-calendar-timegrid-now-indicator-marker" + ); + + if (!labelEl || !markerEl) { + return; + } + + const sunPosition = window.SunCalc.getPosition(referenceDate, currentGeo.latitude, currentGeo.longitude); + const sunAltitudeDeg = (sunPosition.altitude * 180) / Math.PI; + const isSunMode = sunAltitudeDeg >= -4; + + let icon = "☀️"; + let visualKey = "sun-0"; + + labelEl.classList.remove("is-sun", "is-moon"); + markerEl.classList.remove("is-sun", "is-moon"); + + if (isSunMode) { + const intensity = clamp((sunAltitudeDeg + 4) / 70, 0, 1); + const intensityPercent = Math.round(intensity * 100); + + icon = "☀️"; + visualKey = `sun-${intensityPercent}`; + + labelEl.classList.add("is-sun"); + markerEl.classList.add("is-sun"); + + labelEl.style.setProperty("--sun-glow-size", `${Math.round(8 + intensity * 16)}px`); + labelEl.style.setProperty("--sun-glow-alpha", (0.35 + intensity * 0.55).toFixed(2)); + markerEl.style.setProperty("--sun-marker-glow-size", `${Math.round(10 + intensity * 24)}px`); + markerEl.style.setProperty("--sun-marker-ray-opacity", (0.45 + intensity * 0.5).toFixed(2)); + + labelEl.title = `Sun altitude ${sunAltitudeDeg.toFixed(1)}°`; + } else { + const moonIllum = window.SunCalc.getMoonIllumination(referenceDate); + const moonPct = Math.round(moonIllum.fraction * 100); + const moonPhaseName = getMoonPhaseName(moonIllum.phase); + + icon = getMoonPhaseGlyph(moonPhaseName); + visualKey = `moon-${moonPct}-${moonPhaseName}`; + + labelEl.classList.add("is-moon"); + markerEl.classList.add("is-moon"); + + labelEl.style.setProperty("--moon-glow-alpha", (0.2 + moonIllum.fraction * 0.45).toFixed(2)); + markerEl.style.setProperty("--moon-glow-alpha", (0.2 + moonIllum.fraction * 0.45).toFixed(2)); + + labelEl.title = `${moonPhaseName} (${moonPct}%)`; + } + + if (labelEl.dataset.celestialKey !== visualKey) { + labelEl.innerHTML = [ + '', + `${icon}`, + "" + ].join(""); + labelEl.dataset.celestialKey = visualKey; + } +} + +function convertAxisTimeToMinutes(text) { + const normalized = String(text || "").trim().toLowerCase(); + if (!normalized) { + return null; + } + + const minuteMatch = normalized.match(/^(\d{1,4})m$/); + if (minuteMatch) { + return `${Number(minuteMatch[1])}m`; + } + + const secondMatch = normalized.match(/^(\d{1,6})s$/); + if (secondMatch) { + return `${Math.floor(Number(secondMatch[1]) / 60)}m`; + } + + const hourMatch = normalized.match(/^(\d{1,2})hr$/); + if (hourMatch) { + return `${Number(hourMatch[1]) * 60}m`; + } + + const ampmMatch = normalized.match(/^(\d{1,2})(?::(\d{2}))?(?::(\d{2}))?\s*(am|pm)$/); + if (ampmMatch) { + let hour = Number(ampmMatch[1]) % 12; + const minutes = Number(ampmMatch[2] || "0"); + const suffix = ampmMatch[3]; + if (suffix === "pm") { + hour += 12; + } + return `${hour * 60 + minutes}m`; + } + + const twentyFourMatch = normalized.match(/^(\d{1,2}):(\d{2})(?::(\d{2}))?$/); + if (twentyFourMatch) { + const hour = Number(twentyFourMatch[1]); + const minutes = Number(twentyFourMatch[2]); + return `${hour * 60 + minutes}m`; + } + + return null; +} + +function convertAxisTimeToSeconds(text) { + const minuteLabel = convertAxisTimeToMinutes(text); + if (!minuteLabel) { + return null; + } + + const minutes = Number(minuteLabel.replace("m", "")); + if (Number.isNaN(minutes)) { + return null; + } + + return `${minutes * 60}s`; +} + +function convertAxisTimeToHours(text) { + const minuteLabel = convertAxisTimeToMinutes(text); + if (!minuteLabel) { + return null; + } + + const minutes = Number(minuteLabel.replace("m", "")); + if (Number.isNaN(minutes)) { + return null; + } + + return `${Math.floor(minutes / 60)}hr`; +} + +function forceAxisLabelFormat() { + const labelNodes = document.querySelectorAll( + ".toastui-calendar-timegrid-time-column .toastui-calendar-timegrid-time-label" + ); + + labelNodes.forEach((node) => { + if (!node.dataset.originalLabel) { + node.dataset.originalLabel = node.textContent; + } + + if (currentTimeFormat === "minutes") { + const converted = convertAxisTimeToMinutes(node.dataset.originalLabel); + if (converted) { + node.textContent = converted; + } + } else if (currentTimeFormat === "seconds") { + const converted = convertAxisTimeToSeconds(node.dataset.originalLabel); + if (converted) { + node.textContent = converted; + } + } else if (currentTimeFormat === "hours") { + const converted = convertAxisTimeToHours(node.dataset.originalLabel); + if (converted) { + node.textContent = converted; + } + } else { + node.textContent = node.dataset.originalLabel; + } + }); +} + +function getVisibleWeekDates() { + if (typeof calendar.getDateRangeStart !== "function") { + return []; + } + + const rangeStart = calendar.getDateRangeStart(); + if (!rangeStart) { + return []; + } + + const startDateLike = normalizeDateLike(rangeStart); + const startDate = new Date( + startDateLike.getFullYear(), + startDateLike.getMonth(), + startDateLike.getDate(), + 0, + 0, + 0, + 0 + ); + + return Array.from({ length: 7 }, (_, dayOffset) => { + const day = new Date(startDate); + day.setDate(startDate.getDate() + dayOffset); + return day; + }); +} + +function buildMonthSpans(days) { + if (!Array.isArray(days) || days.length === 0) { + return []; + } + + const monthFormatter = new Intl.DateTimeFormat(undefined, { + month: "long", + year: "numeric" + }); + + const spans = []; + let currentStart = 1; + let currentMonth = days[0].getMonth(); + let currentYear = days[0].getFullYear(); + + for (let index = 1; index <= days.length; index += 1) { + const day = days[index]; + const monthChanged = !day || day.getMonth() !== currentMonth || day.getFullYear() !== currentYear; + + if (!monthChanged) { + continue; + } + + const spanEnd = index; + spans.push({ + start: currentStart, + end: spanEnd, + label: monthFormatter.format(new Date(currentYear, currentMonth, 1)) + }); + + if (day) { + currentStart = index + 1; + currentMonth = day.getMonth(); + currentYear = day.getFullYear(); + } + } + + return spans; +} + +function syncMonthStripGeometry() { + if (!monthStripEl) { + return; + } + + const calendarEl = document.getElementById("calendar"); + if (!calendarEl) { + return; + } + + const dayNameItems = calendarEl.querySelectorAll( + ".toastui-calendar-week-view-day-names .toastui-calendar-day-name-item.toastui-calendar-week" + ); + + if (dayNameItems.length < 7) { + monthStripEl.style.paddingLeft = "0"; + monthStripEl.style.paddingRight = "0"; + return; + } + + const calendarRect = calendarEl.getBoundingClientRect(); + const firstRect = dayNameItems[0].getBoundingClientRect(); + const lastRect = dayNameItems[6].getBoundingClientRect(); + + const leftPad = Math.max(0, firstRect.left - calendarRect.left); + const rightPad = Math.max(0, calendarRect.right - lastRect.right); + + monthStripEl.style.paddingLeft = `${leftPad}px`; + monthStripEl.style.paddingRight = `${rightPad}px`; +} + +function updateMonthStrip() { + if (!monthStripEl) { + return; + } + + const days = getVisibleWeekDates(); + const spans = buildMonthSpans(days); + + monthStripEl.replaceChildren(); + + if (!spans.length) { + return; + } + + const trackEl = document.createElement("div"); + trackEl.className = "month-strip-track"; + + spans.forEach((span) => { + const segmentEl = document.createElement("div"); + segmentEl.className = "month-strip-segment"; + segmentEl.style.gridColumn = `${span.start} / ${span.end + 1}`; + segmentEl.textContent = span.label; + trackEl.appendChild(segmentEl); + }); + + monthStripEl.appendChild(trackEl); + syncMonthStripGeometry(); +} + +function createCalendarTemplates() { + const weekdayRulerLookup = buildWeekdayRulerLookup(referenceData?.planets); + + // TIME / SIGN / NAME formatter for week time plates. + // This intentionally keeps each event compact and visually consistent. + const getPlateFields = (event) => { + const fromRawSign = event?.raw?.planetSymbol; + const fromRawName = event?.raw?.planetName; + + if (fromRawSign || fromRawName) { + return { + sign: fromRawSign || "", + name: fromRawName || "" + }; + } + + // Fallback parser for any time event that does not provide `raw` planet metadata. + // Example title pattern: "♂ Mars · The Tower" + const title = String(event?.title || "").trim(); + const beforeTarot = title.split("·")[0].trim(); + const parts = beforeTarot.split(/\s+/).filter(Boolean); + + if (parts.length >= 2) { + return { + sign: parts[0], + name: parts.slice(1).join(" ") + }; + } + + return { + sign: "", + name: beforeTarot + }; + }; + + // Returns exactly three lines for the event block text: + // 1) TIME 2) SIGN 3) NAME + const formatEventPlateText = (event) => { + const timeLabel = formatCalendarTime(event.start); + const { sign, name } = getPlateFields(event); + const safeName = name || String(event?.title || "").trim(); + const safeSign = sign || "•"; + return `${timeLabel}\n${safeSign}\n${safeName}`; + }; + + const renderWeekDayHeader = (weekDayNameData) => { + const dateNumber = String(weekDayNameData?.date ?? "").padStart(2, "0"); + const dayLabel = String(weekDayNameData?.dayName || ""); + const ruler = weekdayRulerLookup[weekDayNameData?.day] || { symbol: "•", name: "" }; + + return [ + '
', + `${dateNumber}`, + `${dayLabel}`, + `${ruler.symbol}`, + "
" + ].join(""); + }; + + return { + timegridDisplayPrimaryTime: (props) => formatCalendarTimeFromTemplatePayload(props), + timegridDisplayTime: (props) => formatCalendarTimeFromTemplatePayload(props), + timegridNowIndicatorLabel: (props) => formatCalendarTimeFromTemplatePayload(props), + weekDayName: (weekDayNameData) => renderWeekDayHeader(weekDayNameData), + time: (event) => formatEventPlateText(event) + }; +} + +function applyTimeFormatTemplates() { + calendar.setOptions({ + template: createCalendarTemplates() + }); + calendar.render(); + + requestAnimationFrame(() => { + forceAxisLabelFormat(); + applySunRulerGradient(); + applyDynamicNowIndicatorVisual(); + updateMonthStrip(); + requestAnimationFrame(() => { + forceAxisLabelFormat(); + applySunRulerGradient(); + applyDynamicNowIndicatorVisual(); + updateMonthStrip(); + }); + }); +} + +function setStatus(text) { + if (!statusEl) { + return; + } + + statusEl.textContent = text; +} + +function normalizeGeoForSky(geo) { + const latitude = Number(geo?.latitude); + const longitude = Number(geo?.longitude); + + if (!Number.isFinite(latitude) || !Number.isFinite(longitude)) { + return null; + } + + return { + latitude: Number(latitude.toFixed(4)), + longitude: Number(longitude.toFixed(4)) + }; +} + +function buildStellariumObserverUrl(geo) { + const normalizedGeo = normalizeGeoForSky(geo); + if (!normalizedGeo) { + return ""; + } + + const stellariumUrl = new URL("https://stellarium-web.org/"); + stellariumUrl.searchParams.set("lat", String(normalizedGeo.latitude)); + stellariumUrl.searchParams.set("lng", String(normalizedGeo.longitude)); + stellariumUrl.searchParams.set("elev", "0"); + stellariumUrl.searchParams.set("date", new Date().toISOString()); + stellariumUrl.searchParams.set("az", "0"); + stellariumUrl.searchParams.set("alt", "90"); + stellariumUrl.searchParams.set("fov", "180"); + + return stellariumUrl.toString(); +} + +function syncNowSkyBackground(geo, force = false) { + if (!nowSkyLayerEl || !geo) { + return; + } + + const normalizedGeo = normalizeGeoForSky(geo); + if (!normalizedGeo) { + return; + } + + const geoKey = `${normalizedGeo.latitude.toFixed(4)},${normalizedGeo.longitude.toFixed(4)}`; + const stellariumUrl = buildStellariumObserverUrl(normalizedGeo); + if (!stellariumUrl) { + return; + } + + if (!force && geoKey === lastNowSkyGeoKey && stellariumUrl === lastNowSkySourceUrl) { + return; + } + + if (stellariumUrl === lastNowSkySourceUrl) { + return; + } + + nowSkyLayerEl.src = stellariumUrl; + lastNowSkyGeoKey = geoKey; + lastNowSkySourceUrl = stellariumUrl; +} + +function syncNowPanelTheme(referenceDate = new Date()) { + if (!nowPanelEl) { + return; + } + + if (!currentGeo || !window.SunCalc) { + nowPanelEl.classList.remove("is-day"); + nowPanelEl.classList.add("is-night"); + return; + } + + const sunPosition = window.SunCalc.getPosition(referenceDate, currentGeo.latitude, currentGeo.longitude); + const sunAltitudeDeg = (sunPosition.altitude * 180) / Math.PI; + const isDaytime = sunAltitudeDeg >= -4; + + nowPanelEl.classList.toggle("is-day", isDaytime); + nowPanelEl.classList.toggle("is-night", !isDaytime); +} + +function openSettingsPopup() { + if (!settingsPopupEl) { + return; + } + + settingsPopupEl.hidden = false; + if (openSettingsEl) { + openSettingsEl.setAttribute("aria-expanded", "true"); + } +} + +function closeSettingsPopup() { + if (!settingsPopupEl) { + return; + } + + settingsPopupEl.hidden = true; + if (openSettingsEl) { + openSettingsEl.setAttribute("aria-expanded", "false"); + } +} + +function loadSidebarCollapsedState(storageKey) { + try { + const raw = window.localStorage?.getItem(storageKey); + if (raw === "1") { + return true; + } + if (raw === "0") { + return false; + } + return null; + } catch { + return null; + } +} + +function saveSidebarCollapsedState(storageKey, collapsed) { + try { + window.localStorage?.setItem(storageKey, collapsed ? "1" : "0"); + } catch { + // Ignore storage failures silently. + } +} + +function initializeSidebarPopouts() { + const layouts = document.querySelectorAll(".planet-layout, .tarot-layout, .kab-layout"); + + layouts.forEach((layout, index) => { + if (!(layout instanceof HTMLElement)) { + return; + } + + const panel = Array.from(layout.children).find((child) => ( + child instanceof HTMLElement + && child.matches("aside.planet-list-panel, aside.tarot-list-panel, aside.kab-tree-panel") + )); + + if (!(panel instanceof HTMLElement) || panel.dataset.sidebarPopoutReady === "1") { + return; + } + + const header = panel.querySelector(".planet-list-header, .tarot-list-header"); + if (!(header instanceof HTMLElement)) { + return; + } + + panel.dataset.sidebarPopoutReady = "1"; + + const sectionId = layout.closest("section")?.id || `layout-${index + 1}`; + const panelId = panel.id || `${sectionId}-entry-panel`; + panel.id = panelId; + + const storageKey = `${SIDEBAR_COLLAPSE_STORAGE_PREFIX}${sectionId}`; + + const collapseBtn = document.createElement("button"); + collapseBtn.type = "button"; + collapseBtn.className = "sidebar-toggle-inline"; + collapseBtn.textContent = "Hide Panel"; + collapseBtn.setAttribute("aria-label", "Hide entry panel"); + collapseBtn.setAttribute("aria-controls", panelId); + header.appendChild(collapseBtn); + + const openBtn = document.createElement("button"); + openBtn.type = "button"; + openBtn.className = "sidebar-popout-open"; + openBtn.textContent = "Show Panel"; + openBtn.setAttribute("aria-label", "Show entry panel"); + openBtn.setAttribute("aria-controls", panelId); + openBtn.hidden = true; + layout.appendChild(openBtn); + + const applyCollapsedState = (collapsed, persist = true) => { + layout.classList.toggle("layout-sidebar-collapsed", collapsed); + collapseBtn.setAttribute("aria-expanded", collapsed ? "false" : "true"); + openBtn.setAttribute("aria-expanded", collapsed ? "false" : "true"); + openBtn.hidden = !collapsed; + + if (persist) { + saveSidebarCollapsedState(storageKey, collapsed); + } + }; + + collapseBtn.addEventListener("click", () => { + applyCollapsedState(true); + }); + + openBtn.addEventListener("click", () => { + applyCollapsedState(false); + }); + + const storedCollapsed = loadSidebarCollapsedState(storageKey); + applyCollapsedState(storedCollapsed == null ? DEFAULT_DATASET_ENTRY_COLLAPSED : storedCollapsed, false); + }); +} + +function initializeDetailPopouts() { + const layouts = document.querySelectorAll(".planet-layout, .tarot-layout, .kab-layout"); + + layouts.forEach((layout, index) => { + if (!(layout instanceof HTMLElement)) { + return; + } + + const detailPanel = Array.from(layout.children).find((child) => ( + child instanceof HTMLElement + && child.matches("section.planet-detail-panel, section.tarot-detail-panel, section.kab-detail-panel") + )); + + if (!(detailPanel instanceof HTMLElement) || detailPanel.dataset.detailPopoutReady === "1") { + return; + } + + const heading = detailPanel.querySelector(".planet-detail-heading, .tarot-detail-heading"); + if (!(heading instanceof HTMLElement)) { + return; + } + + detailPanel.dataset.detailPopoutReady = "1"; + + const sectionId = layout.closest("section")?.id || `layout-${index + 1}`; + const panelId = detailPanel.id || `${sectionId}-detail-panel`; + detailPanel.id = panelId; + + const detailStorageKey = `${DETAIL_COLLAPSE_STORAGE_PREFIX}${sectionId}`; + const sidebarStorageKey = `${SIDEBAR_COLLAPSE_STORAGE_PREFIX}${sectionId}`; + + const collapseBtn = document.createElement("button"); + collapseBtn.type = "button"; + collapseBtn.className = "detail-toggle-inline"; + collapseBtn.textContent = "Hide Detail"; + collapseBtn.setAttribute("aria-label", "Hide detail panel"); + collapseBtn.setAttribute("aria-controls", panelId); + heading.appendChild(collapseBtn); + + const openBtn = document.createElement("button"); + openBtn.type = "button"; + openBtn.className = "detail-popout-open"; + openBtn.textContent = "Show Detail"; + openBtn.setAttribute("aria-label", "Show detail panel"); + openBtn.setAttribute("aria-controls", panelId); + openBtn.hidden = true; + layout.appendChild(openBtn); + + const applyCollapsedState = (collapsed, persist = true) => { + if (collapsed && layout.classList.contains("layout-sidebar-collapsed")) { + layout.classList.remove("layout-sidebar-collapsed"); + const sidebarOpenBtn = layout.querySelector(".sidebar-popout-open"); + if (sidebarOpenBtn instanceof HTMLButtonElement) { + sidebarOpenBtn.hidden = true; + sidebarOpenBtn.setAttribute("aria-expanded", "true"); + } + const sidebarCollapseBtn = layout.querySelector(".sidebar-toggle-inline"); + if (sidebarCollapseBtn instanceof HTMLButtonElement) { + sidebarCollapseBtn.setAttribute("aria-expanded", "true"); + } + saveSidebarCollapsedState(sidebarStorageKey, false); + } + + layout.classList.toggle("layout-detail-collapsed", collapsed); + collapseBtn.setAttribute("aria-expanded", collapsed ? "false" : "true"); + openBtn.setAttribute("aria-expanded", collapsed ? "false" : "true"); + openBtn.hidden = !collapsed; + + if (persist) { + saveSidebarCollapsedState(detailStorageKey, collapsed); + } + }; + + collapseBtn.addEventListener("click", () => { + applyCollapsedState(true); + }); + + openBtn.addEventListener("click", () => { + applyCollapsedState(false); + }); + + const storedCollapsed = loadSidebarCollapsedState(detailStorageKey); + const shouldForceOpenForTarot = sectionId === "tarot-section"; + const initialCollapsed = shouldForceOpenForTarot + ? false + : (storedCollapsed == null ? DEFAULT_DATASET_DETAIL_COLLAPSED : storedCollapsed); + applyCollapsedState(initialCollapsed, false); + }); +} + +function setActiveSection(nextSection) { + const normalized = nextSection === "home" || nextSection === "calendar" || nextSection === "holidays" || nextSection === "tarot" || nextSection === "astronomy" || nextSection === "planets" || nextSection === "cycles" || nextSection === "natal" || nextSection === "elements" || nextSection === "iching" || nextSection === "kabbalah" || nextSection === "kabbalah-tree" || nextSection === "cube" || nextSection === "alphabet" || nextSection === "numbers" || nextSection === "zodiac" || nextSection === "quiz" || nextSection === "gods" || nextSection === "enochian" + ? nextSection + : "home"; + activeSection = normalized; + + const isHomeOpen = activeSection === "home"; + const isCalendarOpen = activeSection === "calendar"; + const isHolidaysOpen = activeSection === "holidays"; + const isCalendarMenuOpen = isCalendarOpen || isHolidaysOpen; + const isTarotOpen = activeSection === "tarot"; + const isAstronomyOpen = activeSection === "astronomy"; + const isPlanetOpen = activeSection === "planets"; + const isCyclesOpen = activeSection === "cycles"; + const isNatalOpen = activeSection === "natal"; + const isZodiacOpen = activeSection === "zodiac"; + const isAstronomyMenuOpen = isAstronomyOpen || isPlanetOpen || isCyclesOpen || isZodiacOpen || isNatalOpen; + const isElementsOpen = activeSection === "elements"; + const isIChingOpen = activeSection === "iching"; + const isKabbalahOpen = activeSection === "kabbalah"; + const isKabbalahTreeOpen = activeSection === "kabbalah-tree"; + const isCubeOpen = activeSection === "cube"; + const isKabbalahMenuOpen = isKabbalahOpen || isKabbalahTreeOpen || isCubeOpen; + const isAlphabetOpen = activeSection === "alphabet"; + const isNumbersOpen = activeSection === "numbers"; + const isQuizOpen = activeSection === "quiz"; + const isGodsOpen = activeSection === "gods"; + const isEnochianOpen = activeSection === "enochian"; + + if (calendarSectionEl) { + calendarSectionEl.hidden = !isCalendarOpen; + } + + if (holidaySectionEl) { + holidaySectionEl.hidden = !isHolidaysOpen; + } + + if (tarotSectionEl) { + tarotSectionEl.hidden = !isTarotOpen; + } + + if (astronomySectionEl) { + astronomySectionEl.hidden = !isAstronomyOpen; + } + + if (planetSectionEl) { + planetSectionEl.hidden = !isPlanetOpen; + } + + if (cyclesSectionEl) { + cyclesSectionEl.hidden = !isCyclesOpen; + } + + if (natalSectionEl) { + natalSectionEl.hidden = !isNatalOpen; + } + + if (elementsSectionEl) { + elementsSectionEl.hidden = !isElementsOpen; + } + + if (ichingSectionEl) { + ichingSectionEl.hidden = !isIChingOpen; + } + + if (kabbalahSectionEl) { + kabbalahSectionEl.hidden = !isKabbalahOpen; + } + + if (kabbalahTreeSectionEl) { + kabbalahTreeSectionEl.hidden = !isKabbalahTreeOpen; + } + + if (cubeSectionEl) { + cubeSectionEl.hidden = !isCubeOpen; + } + + if (alphabetSectionEl) { + alphabetSectionEl.hidden = !isAlphabetOpen; + } + + if (numbersSectionEl) { + numbersSectionEl.hidden = !isNumbersOpen; + } + + if (zodiacSectionEl) { + zodiacSectionEl.hidden = !isZodiacOpen; + } + + if (quizSectionEl) { + quizSectionEl.hidden = !isQuizOpen; + } + + if (godsSectionEl) { + godsSectionEl.hidden = !isGodsOpen; + } + + if (enochianSectionEl) { + enochianSectionEl.hidden = !isEnochianOpen; + } + + if (nowPanelEl) { + nowPanelEl.hidden = !isHomeOpen; + } + + if (monthStripEl) { + monthStripEl.hidden = !isHomeOpen; + } + + if (calendarEl) { + calendarEl.hidden = !isHomeOpen; + } + + if (openCalendarEl) { + openCalendarEl.setAttribute("aria-pressed", isCalendarMenuOpen ? "true" : "false"); + } + + if (openCalendarMonthsEl) { + openCalendarMonthsEl.classList.toggle("is-active", isCalendarOpen); + } + + if (openHolidaysEl) { + openHolidaysEl.classList.toggle("is-active", isHolidaysOpen); + } + + if (openTarotEl) { + openTarotEl.setAttribute("aria-pressed", isTarotOpen ? "true" : "false"); + } + + applyTarotSpreadViewState(); + + if (openAstronomyEl) { + openAstronomyEl.setAttribute("aria-pressed", isAstronomyMenuOpen ? "true" : "false"); + } + + if (openPlanetsEl) { + openPlanetsEl.classList.toggle("is-active", isPlanetOpen); + } + + if (openCyclesEl) { + openCyclesEl.classList.toggle("is-active", isCyclesOpen); + } + + if (openElementsEl) { + openElementsEl.setAttribute("aria-pressed", isElementsOpen ? "true" : "false"); + } + + if (openIChingEl) { + openIChingEl.setAttribute("aria-pressed", isIChingOpen ? "true" : "false"); + } + + if (openKabbalahEl) { + openKabbalahEl.setAttribute("aria-pressed", isKabbalahMenuOpen ? "true" : "false"); + } + + if (openKabbalahTreeEl) { + openKabbalahTreeEl.classList.toggle("is-active", isKabbalahTreeOpen); + } + + if (openKabbalahCubeEl) { + openKabbalahCubeEl.classList.toggle("is-active", isCubeOpen); + } + + if (openAlphabetEl) { + openAlphabetEl.setAttribute("aria-pressed", isAlphabetOpen ? "true" : "false"); + } + + if (openNumbersEl) { + openNumbersEl.setAttribute("aria-pressed", isNumbersOpen ? "true" : "false"); + } + + if (openZodiacEl) { + openZodiacEl.classList.toggle("is-active", isZodiacOpen); + } + + if (openNatalEl) { + openNatalEl.classList.toggle("is-active", isNatalOpen); + } + + if (openQuizEl) { + openQuizEl.setAttribute("aria-pressed", isQuizOpen ? "true" : "false"); + } + + if (openGodsEl) { + openGodsEl.setAttribute("aria-pressed", isGodsOpen ? "true" : "false"); + } + + if (openEnochianEl) { + openEnochianEl.setAttribute("aria-pressed", isEnochianOpen ? "true" : "false"); + } + + if (!isHomeOpen) { + closeSettingsPopup(); + } + + if (isCalendarOpen) { + if (typeof ensureCalendarSection === "function" && referenceData) { + ensureCalendarSection(referenceData, magickDataset); + } + return; + } + + if (isHolidaysOpen) { + if (typeof ensureHolidaySection === "function" && referenceData) { + ensureHolidaySection(referenceData, magickDataset); + } + return; + } + + if (isTarotOpen) { + if (typeof ensureTarotSection === "function" && referenceData) { + ensureTarotSection(referenceData, magickDataset); + } + if (activeTarotSpread !== null) { + renderTarotSpread(); + } + return; + } + + if (isPlanetOpen) { + if (typeof ensurePlanetSection === "function" && referenceData) { + ensurePlanetSection(referenceData, magickDataset); + } + return; + } + + if (isCyclesOpen) { + if (typeof ensureCyclesSection === "function" && referenceData) { + ensureCyclesSection(referenceData); + } + return; + } + + if (isElementsOpen) { + if (typeof ensureElementsSection === "function" && magickDataset) { + ensureElementsSection(magickDataset); + } + return; + } + + if (isIChingOpen) { + if (typeof ensureIChingSection === "function" && referenceData) { + ensureIChingSection(referenceData); + } + return; + } + + if (isKabbalahTreeOpen) { + if (typeof ensureKabbalahSection === "function" && magickDataset) { + ensureKabbalahSection(magickDataset); + } + return; + } + + if (isCubeOpen) { + if (typeof ensureCubeSection === "function" && magickDataset) { + ensureCubeSection(magickDataset, referenceData); + } + return; + } + + if (isAlphabetOpen) { + if (typeof ensureAlphabetSection === "function" && magickDataset) { + ensureAlphabetSection(magickDataset, referenceData); + } + return; + } + + if (isNumbersOpen) { + ensureNumbersSection(); + return; + } + + if (isZodiacOpen) { + if (typeof ensureZodiacSection === "function" && referenceData && magickDataset) { + ensureZodiacSection(referenceData, magickDataset); + } + return; + } + + if (isNatalOpen) { + if (typeof ensureNatalPanel === "function") { + ensureNatalPanel(referenceData); + } + return; + } + + if (isQuizOpen) { + if (typeof ensureQuizSection === "function" && referenceData && magickDataset) { + ensureQuizSection(referenceData, magickDataset); + } + return; + } + + if (isGodsOpen) { + if (typeof ensureGodsSection === "function" && magickDataset) { + ensureGodsSection(magickDataset, referenceData); + } + return; + } + + if (isEnochianOpen) { + if (typeof ensureEnochianSection === "function" && magickDataset) { + ensureEnochianSection(magickDataset, referenceData); + } + return; + } + + requestAnimationFrame(() => { + calendar.render(); + updateMonthStrip(); + syncNowPanelTheme(new Date()); + }); +} + +function applyCenteredWeekWindow(date) { + const startDayOfWeek = getCenteredWeekStartDay(date); + calendar.setOptions({ + week: { + ...baseWeekOptions, + startDayOfWeek + } + }); + applyTimeFormatTemplates(); + calendar.changeView("week"); + calendar.setDate(date); +} + +function parseGeoInput() { + const latitude = Number(latEl.value); + const longitude = Number(lngEl.value); + + if (Number.isNaN(latitude) || Number.isNaN(longitude)) { + throw new Error("Latitude/Longitude must be valid numbers."); + } + + return { latitude, longitude }; +} + +function normalizeTimeFormat(value) { + if (value === "hours") { + return "hours"; + } + + if (value === "seconds") { + return "seconds"; + } + + return "minutes"; +} + +function normalizeBirthDate(value) { + const normalized = String(value || "").trim(); + if (!normalized) { + return ""; + } + + return /^\d{4}-\d{2}-\d{2}$/.test(normalized) ? normalized : ""; +} + +function getKnownTarotDeckIds() { + const knownDeckIds = new Set(); + const deckOptions = window.TarotCardImages?.getDeckOptions?.(); + + if (Array.isArray(deckOptions)) { + deckOptions.forEach((option) => { + const id = String(option?.id || "").trim().toLowerCase(); + if (id) { + knownDeckIds.add(id); + } + }); + } + + if (!knownDeckIds.size) { + knownDeckIds.add(DEFAULT_TAROT_DECK); + } + + return knownDeckIds; +} + +function getFallbackTarotDeckId() { + const deckOptions = window.TarotCardImages?.getDeckOptions?.(); + if (Array.isArray(deckOptions)) { + for (let i = 0; i < deckOptions.length; i += 1) { + const id = String(deckOptions[i]?.id || "").trim().toLowerCase(); + if (id) { + return id; + } + } + } + + return DEFAULT_TAROT_DECK; +} + +function normalizeTarotDeck(value) { + const normalized = String(value || "").trim().toLowerCase(); + const knownDeckIds = getKnownTarotDeckIds(); + + if (knownDeckIds.has(normalized)) { + return normalized; + } + + return getFallbackTarotDeckId(); +} + +function parseStoredNumber(value, fallback) { + const parsed = Number(value); + return Number.isFinite(parsed) ? parsed : fallback; +} + +function normalizeSettings(settings) { + return { + latitude: parseStoredNumber(settings?.latitude, DEFAULT_SETTINGS.latitude), + longitude: parseStoredNumber(settings?.longitude, DEFAULT_SETTINGS.longitude), + timeFormat: normalizeTimeFormat(settings?.timeFormat), + birthDate: normalizeBirthDate(settings?.birthDate), + tarotDeck: normalizeTarotDeck(settings?.tarotDeck) + }; +} + +function getResolvedTimeZone() { + try { + const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; + return String(timeZone || ""); + } catch { + return ""; + } +} + +function buildBirthDateParts(birthDate) { + const normalized = normalizeBirthDate(birthDate); + if (!normalized) { + return null; + } + + const [year, month, day] = normalized.split("-").map((value) => Number(value)); + if (!year || !month || !day) { + return null; + } + + const localNoon = new Date(year, month - 1, day, 12, 0, 0, 0); + const utcNoon = new Date(Date.UTC(year, month - 1, day, 12, 0, 0, 0)); + + return { + year, + month, + day, + isoDate: normalized, + localNoonIso: localNoon.toISOString(), + utcNoonIso: utcNoon.toISOString(), + timezoneOffsetMinutesAtNoon: localNoon.getTimezoneOffset() + }; +} + +function buildNatalContext(settings) { + const normalized = normalizeSettings(settings); + const birthDateParts = buildBirthDateParts(normalized.birthDate); + const timeZone = getResolvedTimeZone(); + + return { + latitude: normalized.latitude, + longitude: normalized.longitude, + birthDate: normalized.birthDate || null, + birthDateParts, + timeZone: timeZone || "UTC", + timezoneOffsetMinutesNow: new Date().getTimezoneOffset(), + timezoneOffsetMinutesAtBirthDateNoon: birthDateParts?.timezoneOffsetMinutesAtNoon ?? null + }; +} + +function emitSettingsUpdated(settings) { + const normalized = normalizeSettings(settings); + const natalContext = buildNatalContext(normalized); + document.dispatchEvent(new CustomEvent("settings:updated", { + detail: { + settings: normalized, + natalContext + } + })); +} + +function loadSavedSettings() { + try { + const raw = window.localStorage.getItem(SETTINGS_STORAGE_KEY); + if (!raw) { + return { ...DEFAULT_SETTINGS }; + } + + const parsed = JSON.parse(raw); + return normalizeSettings(parsed); + } catch { + return { ...DEFAULT_SETTINGS }; + } +} + +function saveSettings(settings) { + try { + const normalized = normalizeSettings(settings); + window.localStorage.setItem(SETTINGS_STORAGE_KEY, JSON.stringify(normalized)); + return true; + } catch { + return false; + } +} + +function syncTarotDeckInputOptions() { + if (!tarotDeckEl) { + return; + } + + const deckOptions = window.TarotCardImages?.getDeckOptions?.(); + const previousValue = String(tarotDeckEl.value || "").trim().toLowerCase(); + tarotDeckEl.innerHTML = ""; + + if (!Array.isArray(deckOptions) || !deckOptions.length) { + const emptyOption = document.createElement("option"); + emptyOption.value = DEFAULT_TAROT_DECK; + emptyOption.textContent = "No deck manifests found"; + tarotDeckEl.appendChild(emptyOption); + tarotDeckEl.disabled = true; + return; + } + + tarotDeckEl.disabled = false; + + deckOptions.forEach((option) => { + const id = String(option?.id || "").trim().toLowerCase(); + if (!id) { + return; + } + + const label = String(option?.label || id); + const optionEl = document.createElement("option"); + optionEl.value = id; + optionEl.textContent = label; + tarotDeckEl.appendChild(optionEl); + }); + + const normalizedPrevious = normalizeTarotDeck(previousValue); + tarotDeckEl.value = normalizedPrevious; +} + +function applySettingsToInputs(settings) { + syncTarotDeckInputOptions(); + const normalized = normalizeSettings(settings); + latEl.value = String(normalized.latitude); + lngEl.value = String(normalized.longitude); + timeFormatEl.value = normalized.timeFormat; + birthDateEl.value = normalized.birthDate; + if (tarotDeckEl) { + tarotDeckEl.value = normalized.tarotDeck; + } + if (window.TarotCardImages?.setActiveDeck) { + window.TarotCardImages.setActiveDeck(normalized.tarotDeck); + } + currentTimeFormat = normalized.timeFormat; + currentSettings = normalized; +} + +function getSettingsFromInputs() { + const latitude = Number(latEl.value); + const longitude = Number(lngEl.value); + + if (Number.isNaN(latitude) || Number.isNaN(longitude)) { + throw new Error("Latitude/Longitude must be valid numbers."); + } + + return normalizeSettings({ + latitude, + longitude, + timeFormat: normalizeTimeFormat(timeFormatEl.value), + birthDate: normalizeBirthDate(birthDateEl.value), + tarotDeck: normalizeTarotDeck(tarotDeckEl?.value) + }); +} + +function handleSaveSettings() { + try { + const settings = getSettingsFromInputs(); + applySettingsToInputs(settings); + syncNowSkyBackground({ latitude: settings.latitude, longitude: settings.longitude }, true); + const didPersist = saveSettings(settings); + emitSettingsUpdated(currentSettings); + if (activeSection !== "home") { + setActiveSection(activeSection); + } + closeSettingsPopup(); + void renderWeek(); + + if (!didPersist) { + setStatus("Settings applied for this session. Browser storage is unavailable."); + } + } catch (error) { + setStatus(error.message || "Unable to save settings."); + } +} + +function startNowTicker() { + if (nowInterval) { + clearInterval(nowInterval); + } + + const tick = () => { + if (!referenceData || !currentGeo || renderInProgress) { + return; + } + + const now = new Date(); + syncNowPanelTheme(now); + const currentDayKey = getDateKey(now); + if (currentDayKey !== centeredDayKey) { + centeredDayKey = currentDayKey; + void renderWeek(); + return; + } + + updateNowPanel(referenceData, currentGeo, nowElements, currentTimeFormat); + applyDynamicNowIndicatorVisual(now); + }; + + tick(); + nowInterval = setInterval(tick, 1000); +} + +async function renderWeek() { + if (renderInProgress) { + return; + } + + renderInProgress = true; + + try { + currentGeo = parseGeoInput(); + syncNowPanelTheme(new Date()); + syncNowSkyBackground(currentGeo); + + if (!referenceData || !magickDataset) { + setStatus("Loading planetary, sign and decan tarot correspondences..."); + const [loadedReference, loadedMagick] = await Promise.all([ + referenceData ? Promise.resolve(referenceData) : loadReferenceData(), + magickDataset + ? Promise.resolve(magickDataset) + : loadMagickDataset().catch(() => null) + ]); + + referenceData = loadedReference; + magickDataset = loadedMagick; + } + + if (typeof ensureTarotSection === "function") { + ensureTarotSection(referenceData, magickDataset); + } + + if (typeof ensurePlanetSection === "function") { + ensurePlanetSection(referenceData, magickDataset); + } + + if (typeof ensureCyclesSection === "function") { + ensureCyclesSection(referenceData); + } + + if (typeof ensureIChingSection === "function") { + ensureIChingSection(referenceData); + } + + if (typeof ensureCalendarSection === "function") { + ensureCalendarSection(referenceData, magickDataset); + } + + if (typeof ensureHolidaySection === "function") { + ensureHolidaySection(referenceData, magickDataset); + } + + if (typeof ensureNatalPanel === "function") { + ensureNatalPanel(referenceData); + } + + if (typeof ensureQuizSection === "function") { + ensureQuizSection(referenceData, magickDataset); + } + + const anchorDate = new Date(); + centeredDayKey = getDateKey(anchorDate); + + applyCenteredWeekWindow(anchorDate); + + const events = buildWeekEvents(currentGeo, referenceData, anchorDate); + calendar.clear(); + calendar.createEvents(events); + applySunRulerGradient(anchorDate); + updateMonthStrip(); + requestAnimationFrame(updateMonthStrip); + + setStatus(`Rendered ${events.length} planetary + tarot events for lat ${currentGeo.latitude}, lng ${currentGeo.longitude}.`); + startNowTicker(); + } catch (error) { + setStatus(error.message || "Failed to render calendar."); + } finally { + renderInProgress = false; + } +} + +function requestGeoLocation() { + if (!navigator.geolocation) { + setStatus("Geolocation not available in this browser."); + return; + } + + setStatus("Getting your location..."); + navigator.geolocation.getCurrentPosition( + ({ coords }) => { + latEl.value = coords.latitude.toFixed(4); + lngEl.value = coords.longitude.toFixed(4); + syncNowSkyBackground({ latitude: coords.latitude, longitude: coords.longitude }, true); + setStatus("Location set from browser. Click Save Settings to refresh."); + }, + (err) => { + const detail = err?.message || `code ${err?.code ?? "unknown"}`; + setStatus(`Could not get location (${detail}).`); + }, + { enableHighAccuracy: true, timeout: 10000 } + ); +} + +function setTopbarDropdownOpen(dropdownEl, isOpen) { + if (!(dropdownEl instanceof HTMLElement)) { + return; + } + + dropdownEl.classList.toggle("is-open", Boolean(isOpen)); + const trigger = dropdownEl.querySelector("button[aria-haspopup='menu']"); + if (trigger) { + trigger.setAttribute("aria-expanded", isOpen ? "true" : "false"); + } +} + +function closeTopbarDropdowns(exceptEl = null) { + topbarDropdownEls.forEach((dropdownEl) => { + if (exceptEl && dropdownEl === exceptEl) { + return; + } + setTopbarDropdownOpen(dropdownEl, false); + }); +} + +function bindTopbarDropdownInteractions() { + if (!topbarDropdownEls.length) { + return; + } + + topbarDropdownEls.forEach((dropdownEl) => { + const trigger = dropdownEl.querySelector("button[aria-haspopup='menu']"); + if (!(trigger instanceof HTMLElement)) { + return; + } + + setTopbarDropdownOpen(dropdownEl, false); + + dropdownEl.addEventListener("mouseenter", () => { + setTopbarDropdownOpen(dropdownEl, true); + }); + + dropdownEl.addEventListener("mouseleave", () => { + setTopbarDropdownOpen(dropdownEl, false); + }); + + dropdownEl.addEventListener("focusout", (event) => { + const nextTarget = event.relatedTarget; + if (!(nextTarget instanceof Node) || !dropdownEl.contains(nextTarget)) { + setTopbarDropdownOpen(dropdownEl, false); + } + }); + + trigger.addEventListener("click", (event) => { + event.stopPropagation(); + const nextOpen = !dropdownEl.classList.contains("is-open"); + closeTopbarDropdowns(dropdownEl); + setTopbarDropdownOpen(dropdownEl, nextOpen); + }); + + const menuItems = dropdownEl.querySelectorAll(".topbar-dropdown-menu [role='menuitem']"); + menuItems.forEach((menuItem) => { + menuItem.addEventListener("click", () => { + closeTopbarDropdowns(); + }); + }); + }); +} + +if (saveSettingsEl) { + saveSettingsEl.addEventListener("click", handleSaveSettings); +} + +useLocationEl.addEventListener("click", requestGeoLocation); + +if (openSettingsEl) { + openSettingsEl.addEventListener("click", (event) => { + event.stopPropagation(); + if (settingsPopupEl?.hidden) { + openSettingsPopup(); + } else { + closeSettingsPopup(); + } + }); +} + +if (openTarotEl) { + openTarotEl.addEventListener("click", () => { + if (activeSection === "tarot") { + setActiveSection("home"); + } else { + setActiveSection("tarot"); + showTarotCardsView(); + } + }); +} + +if (openTarotCardsEl) { + openTarotCardsEl.addEventListener("click", () => { + setActiveSection("tarot"); + showTarotCardsView(); + }); +} + +if (openTarotSpreadEl) { + openTarotSpreadEl.addEventListener("click", () => { + setTarotSpread("three-card", true); + }); +} + +if (tarotSpreadBackEl) { + tarotSpreadBackEl.addEventListener("click", () => { + showTarotCardsView(); + }); +} + +if (tarotSpreadBtnThreeEl) { + tarotSpreadBtnThreeEl.addEventListener("click", () => { + showTarotSpreadView("three-card"); + }); +} + +if (tarotSpreadBtnCelticEl) { + tarotSpreadBtnCelticEl.addEventListener("click", () => { + showTarotSpreadView("celtic-cross"); + }); +} + +if (tarotSpreadRedrawEl) { + tarotSpreadRedrawEl.addEventListener("click", () => { + regenerateTarotSpreadDraw(); + renderTarotSpread(); + }); +} + +if (openAstronomyEl) { + openAstronomyEl.addEventListener("click", () => { + setActiveSection(activeSection === "astronomy" ? "home" : "astronomy"); + }); +} + +if (openPlanetsEl) { + openPlanetsEl.addEventListener("click", () => { + setActiveSection(activeSection === "planets" ? "home" : "planets"); + }); +} + +if (openCyclesEl) { + openCyclesEl.addEventListener("click", () => { + setActiveSection(activeSection === "cycles" ? "home" : "cycles"); + }); +} + +if (openElementsEl) { + openElementsEl.addEventListener("click", () => { + setActiveSection(activeSection === "elements" ? "home" : "elements"); + }); +} + +if (openIChingEl) { + openIChingEl.addEventListener("click", () => { + setActiveSection(activeSection === "iching" ? "home" : "iching"); + }); +} + +if (openKabbalahEl) { + openKabbalahEl.addEventListener("click", () => { + setActiveSection(activeSection === "kabbalah" ? "home" : "kabbalah"); + }); +} + +if (openKabbalahTreeEl) { + openKabbalahTreeEl.addEventListener("click", () => { + setActiveSection(activeSection === "kabbalah-tree" ? "home" : "kabbalah-tree"); + }); +} + +if (openKabbalahCubeEl) { + openKabbalahCubeEl.addEventListener("click", () => { + setActiveSection(activeSection === "cube" ? "home" : "cube"); + }); +} + +if (openAlphabetEl) { + openAlphabetEl.addEventListener("click", () => { + setActiveSection(activeSection === "alphabet" ? "home" : "alphabet"); + }); +} + +if (openNumbersEl) { + openNumbersEl.addEventListener("click", () => { + setActiveSection(activeSection === "numbers" ? "home" : "numbers"); + }); +} + +if (openZodiacEl) { + openZodiacEl.addEventListener("click", () => { + setActiveSection(activeSection === "zodiac" ? "home" : "zodiac"); + }); +} + +if (openNatalEl) { + openNatalEl.addEventListener("click", () => { + setActiveSection(activeSection === "natal" ? "home" : "natal"); + }); +} + +if (openQuizEl) { + openQuizEl.addEventListener("click", () => { + setActiveSection(activeSection === "quiz" ? "home" : "quiz"); + }); +} + +if (openGodsEl) { + openGodsEl.addEventListener("click", () => { + setActiveSection(activeSection === "gods" ? "home" : "gods"); + }); +} + +if (openEnochianEl) { + openEnochianEl.addEventListener("click", () => { + setActiveSection(activeSection === "enochian" ? "home" : "enochian"); + }); +} + +if (openCalendarEl) { + openCalendarEl.addEventListener("click", () => { + const isCalendarMenuActive = activeSection === "calendar" || activeSection === "holidays"; + setActiveSection(isCalendarMenuActive ? "home" : "calendar"); + }); +} + +if (openCalendarMonthsEl) { + openCalendarMonthsEl.addEventListener("click", () => { + setActiveSection(activeSection === "calendar" ? "home" : "calendar"); + }); +} + +if (openHolidaysEl) { + openHolidaysEl.addEventListener("click", () => { + setActiveSection(activeSection === "holidays" ? "home" : "holidays"); + }); +} + +bindTopbarDropdownInteractions(); + +document.addEventListener("nav:cube", (e) => { + if (typeof ensureCubeSection === "function" && magickDataset) { + ensureCubeSection(magickDataset, referenceData); + } + + setActiveSection("cube"); + + const detail = e?.detail || {}; + requestAnimationFrame(() => { + const ui = window.CubeSectionUi; + const selected = ui?.selectPlacement?.(detail); + if (!selected && detail?.wallId) { + ui?.selectWallById?.(detail.wallId); + } + }); +}); + +document.addEventListener("nav:zodiac", (e) => { + if (typeof ensureZodiacSection === "function" && referenceData && magickDataset) { + ensureZodiacSection(referenceData, magickDataset); + } + setActiveSection("zodiac"); + const signId = e?.detail?.signId; + if (signId) { + requestAnimationFrame(() => { + window.ZodiacSectionUi?.selectBySignId?.(signId); + }); + } +}); + +document.addEventListener("nav:alphabet", (e) => { + if (typeof ensureAlphabetSection === "function" && magickDataset) { + ensureAlphabetSection(magickDataset, referenceData); + } + setActiveSection("alphabet"); + + const alphabet = e?.detail?.alphabet; + const hebrewLetterId = e?.detail?.hebrewLetterId; + const greekName = e?.detail?.greekName; + const englishLetter = e?.detail?.englishLetter; + const arabicName = e?.detail?.arabicName; + const enochianId = e?.detail?.enochianId; + + requestAnimationFrame(() => { + const ui = window.AlphabetSectionUi; + if ((alphabet === "hebrew" || (!alphabet && hebrewLetterId)) && hebrewLetterId) { + ui?.selectLetterByHebrewId?.(hebrewLetterId); + return; + } + if (alphabet === "greek" && greekName) { + ui?.selectGreekLetterByName?.(greekName); + return; + } + if (alphabet === "english" && englishLetter) { + ui?.selectEnglishLetter?.(englishLetter); + return; + } + if (alphabet === "arabic" && arabicName) { + ui?.selectArabicLetter?.(arabicName); + return; + } + if (alphabet === "enochian" && enochianId) { + ui?.selectEnochianLetter?.(enochianId); + } + }); +}); + +document.addEventListener("nav:number", (e) => { + const rawValue = e?.detail?.value; + const normalizedValue = normalizeNumberValue(rawValue); + if (normalizedValue === null) { + return; + } + + setActiveSection("numbers"); + requestAnimationFrame(() => { + selectNumberEntry(normalizedValue); + }); +}); + +document.addEventListener("nav:iching", (e) => { + if (typeof ensureIChingSection === "function" && referenceData) { + ensureIChingSection(referenceData); + } + + setActiveSection("iching"); + + const hexagramNumber = e?.detail?.hexagramNumber; + const planetaryInfluence = e?.detail?.planetaryInfluence; + + requestAnimationFrame(() => { + const ui = window.IChingSectionUi; + if (hexagramNumber != null) { + ui?.selectByHexagramNumber?.(hexagramNumber); + return; + } + if (planetaryInfluence) { + ui?.selectByPlanetaryInfluence?.(planetaryInfluence); + } + }); +}); + +document.addEventListener("nav:gods", (e) => { + if (typeof ensureGodsSection === "function" && magickDataset) { + ensureGodsSection(magickDataset, referenceData); + } + setActiveSection("gods"); + const godId = e?.detail?.godId; + const godName = e?.detail?.godName; + const pathNo = e?.detail?.pathNo; + requestAnimationFrame(() => { + const ui = window.GodsSectionUi; + const viaId = godId ? ui?.selectById?.(godId) : false; + const viaName = !viaId && godName ? ui?.selectByName?.(godName) : false; + if (!viaId && !viaName && pathNo != null) { + ui?.selectByPathNo?.(pathNo); + } + }); +}); + +document.addEventListener("nav:calendar-month", (e) => { + const calendarId = e?.detail?.calendarId; + const monthId = e?.detail?.monthId; + if (!monthId) return; + + if (typeof ensureCalendarSection === "function" && referenceData) { + ensureCalendarSection(referenceData, magickDataset); + } + + setActiveSection("calendar"); + + requestAnimationFrame(() => { + if (calendarId) { + window.CalendarSectionUi?.selectCalendarType?.(calendarId); + } + window.CalendarSectionUi?.selectByMonthId?.(monthId); + }); +}); + +document.addEventListener("nav:kabbalah-path", (e) => { + const pathNo = e?.detail?.pathNo; + const { ensureKabbalahSection } = window.KabbalahSectionUi || {}; + if (typeof ensureKabbalahSection === "function" && magickDataset) { + ensureKabbalahSection(magickDataset); + } + setActiveSection("kabbalah-tree"); + if (pathNo != null) { + requestAnimationFrame(() => { + window.KabbalahSectionUi?.selectNode?.(pathNo); + }); + } +}); + +document.addEventListener("nav:planet", (e) => { + const planetId = e?.detail?.planetId; + if (!planetId) return; + if (typeof ensurePlanetSection === "function" && referenceData) { + ensurePlanetSection(referenceData, magickDataset); + } + setActiveSection("planets"); + requestAnimationFrame(() => { + window.PlanetSectionUi?.selectByPlanetId?.(planetId); + }); +}); + +document.addEventListener("nav:elements", (e) => { + const elementId = e?.detail?.elementId; + if (!elementId) { + return; + } + + if (typeof ensureElementsSection === "function" && magickDataset) { + ensureElementsSection(magickDataset); + } + + setActiveSection("elements"); + + requestAnimationFrame(() => { + window.ElementsSectionUi?.selectByElementId?.(elementId); + }); +}); + +document.addEventListener("nav:tarot-trump", (e) => { + if (typeof ensureTarotSection === "function" && referenceData) { + ensureTarotSection(referenceData, magickDataset); + } + setActiveSection("tarot"); + const { trumpNumber, cardName } = e?.detail || {}; + requestAnimationFrame(() => { + if (trumpNumber != null) { + window.TarotSectionUi?.selectCardByTrump?.(trumpNumber); + } else if (cardName) { + window.TarotSectionUi?.selectCardByName?.(cardName); + } + }); +}); + +document.addEventListener("kab:view-trump", (e) => { + setActiveSection("tarot"); + const trumpNumber = e?.detail?.trumpNumber; + if (trumpNumber != null) { + if (typeof ensureTarotSection === "function" && referenceData) { + ensureTarotSection(referenceData, magickDataset); + } + requestAnimationFrame(() => { + window.TarotSectionUi?.selectCardByTrump?.(trumpNumber); + }); + } +}); + +document.addEventListener("tarot:view-kab-path", (e) => { + setActiveSection("kabbalah-tree"); + const pathNumber = e?.detail?.pathNumber; + if (pathNumber != null) { + requestAnimationFrame(() => { + const kabbalahUi = window.KabbalahSectionUi; + if (typeof kabbalahUi?.selectNode === "function") { + kabbalahUi.selectNode(pathNumber); + } else { + kabbalahUi?.selectPathByNumber?.(pathNumber); + } + }); + } +}); + +if (closeSettingsEl) { + closeSettingsEl.addEventListener("click", closeSettingsPopup); +} + +document.addEventListener("click", (event) => { + const clickTarget = event.target; + if (clickTarget instanceof Node && topbarDropdownEls.some((dropdownEl) => dropdownEl.contains(clickTarget))) { + return; + } + closeTopbarDropdowns(); + + if (!settingsPopupEl || settingsPopupEl.hidden) { + return; + } + + if (!(clickTarget instanceof Node)) { + return; + } + + if (settingsPopupCardEl?.contains(clickTarget) || openSettingsEl?.contains(clickTarget)) { + return; + } + + closeSettingsPopup(); +}); + +document.addEventListener("keydown", (event) => { + if (event.key === "Escape") { + closeTopbarDropdowns(); + closeSettingsPopup(); + } +}); + +window.addEventListener("resize", () => { + if (monthStripResizeFrame) { + cancelAnimationFrame(monthStripResizeFrame); + } + monthStripResizeFrame = requestAnimationFrame(() => { + monthStripResizeFrame = null; + updateMonthStrip(); + }); +}); + +window.TarotNatal = { + ...(window.TarotNatal || {}), + getSettings() { + return { ...currentSettings }; + }, + getContext() { + return buildNatalContext(currentSettings); + }, + buildContextFromSettings(settings) { + return buildNatalContext(settings); + } +}; + +const initialSettings = loadSavedSettings(); +applySettingsToInputs(initialSettings); +emitSettingsUpdated(currentSettings); +initializeSidebarPopouts(); +initializeDetailPopouts(); +syncNowSkyBackground({ latitude: initialSettings.latitude, longitude: initialSettings.longitude }, true); +setActiveSection("home"); + +void renderWeek(); diff --git a/app/astro-calcs.js b/app/astro-calcs.js new file mode 100644 index 0000000..404b0cc --- /dev/null +++ b/app/astro-calcs.js @@ -0,0 +1,163 @@ +(function () { + const DAY_IN_MS = 24 * 60 * 60 * 1000; + const START = ["sol", "luna", "mars", "mercury", "jupiter", "venus", "saturn"]; + const CHALDEAN = ["saturn", "jupiter", "mars", "sol", "venus", "mercury", "luna"]; + + function toTitleCase(value) { + if (!value) return ""; + return value.charAt(0).toUpperCase() + value.slice(1); + } + + function getDateKey(date) { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, "0"); + const day = String(date.getDate()).padStart(2, "0"); + return `${year}-${month}-${day}`; + } + + function getCenteredWeekStartDay(date) { + return (date.getDay() + 4) % 7; + } + + function minutesBetween(a, b) { + return (a.getTime() - b.getTime()) / 60000; + } + + function getMoonPhaseName(phase) { + if (phase < 0.03 || phase > 0.97) return "New Moon"; + if (phase < 0.22) return "Waxing Crescent"; + if (phase < 0.28) return "First Quarter"; + if (phase < 0.47) return "Waxing Gibbous"; + if (phase < 0.53) return "Full Moon"; + if (phase < 0.72) return "Waning Gibbous"; + if (phase < 0.78) return "Last Quarter"; + return "Waning Crescent"; + } + + function parseMonthDay(monthDay) { + const [month, day] = monthDay.split("-").map(Number); + return { month, day }; + } + + function isDateInSign(date, sign) { + const { month: startMonth, day: startDay } = parseMonthDay(sign.start); + const { month: endMonth, day: endDay } = parseMonthDay(sign.end); + const month = date.getMonth() + 1; + const day = date.getDate(); + const wrapsYear = startMonth > endMonth; + + if (!wrapsYear) { + const afterStart = month > startMonth || (month === startMonth && day >= startDay); + const beforeEnd = month < endMonth || (month === endMonth && day <= endDay); + return afterStart && beforeEnd; + } + + const afterStart = month > startMonth || (month === startMonth && day >= startDay); + const beforeEnd = month < endMonth || (month === endMonth && day <= endDay); + return afterStart || beforeEnd; + } + + function getSignStartDate(date, sign) { + const { month: startMonth, day: startDay } = parseMonthDay(sign.start); + const month = date.getMonth() + 1; + const day = date.getDate(); + const wrapsYear = startMonth > parseMonthDay(sign.end).month; + + let year = date.getFullYear(); + if (wrapsYear && (month < startMonth || (month === startMonth && day < startDay))) { + year -= 1; + } + + return new Date(year, startMonth - 1, startDay); + } + + function getSignForDate(date, signs) { + return signs.find((sign) => isDateInSign(date, sign)) || null; + } + + function groupDecansBySign(decans) { + const map = {}; + for (const decan of decans) { + if (!map[decan.signId]) { + map[decan.signId] = []; + } + map[decan.signId].push(decan); + } + + for (const signId of Object.keys(map)) { + map[signId].sort((a, b) => a.index - b.index); + } + return map; + } + + function getDecanForDate(date, signs, decansBySign) { + const sign = getSignForDate(date, signs); + if (!sign) return null; + + const signDecans = decansBySign[sign.id] || []; + if (!signDecans.length) { + return { sign, decan: null }; + } + + const signStartDate = getSignStartDate(date, sign); + const daysSinceSignStart = Math.floor((date.getTime() - signStartDate.getTime()) / DAY_IN_MS); + let index = Math.floor(daysSinceSignStart / 10) + 1; + if (index < 1) index = 1; + if (index > 3) index = 3; + + const decan = signDecans.find((entry) => entry.index === index) || signDecans[0]; + return { sign, decan }; + } + + function calcPlanetaryHoursForDayAndLocation(date, geo) { + const sunCalc = window.SunCalc; + if (!sunCalc) { + throw new Error("SunCalc library is not loaded."); + } + + const solar = sunCalc.getTimes(date, geo.latitude, geo.longitude); + const nextDay = new Date(date.getTime() + DAY_IN_MS); + const solarNext = sunCalc.getTimes(nextDay, geo.latitude, geo.longitude); + const dayOfWeek = date.getDay(); + const chaldeanStartPos = CHALDEAN.indexOf(START[dayOfWeek]); + + const dayHourInMinutes = minutesBetween(solar.sunset, solar.sunrise) / 12; + const nightHourInMinutes = minutesBetween(solarNext.sunrise, solar.sunset) / 12; + + const hours = []; + for (let hour = 0; hour < 12; hour += 1) { + const start = new Date(solar.sunrise.getTime() + dayHourInMinutes * hour * 60_000); + const end = new Date(start.getTime() + dayHourInMinutes * 60_000); + hours.push({ + start, + end, + planetId: CHALDEAN[(chaldeanStartPos + hour) % 7], + isDaylight: true + }); + } + + for (let hour = 12; hour < 24; hour += 1) { + const start = new Date(solar.sunset.getTime() + nightHourInMinutes * (hour - 12) * 60_000); + const end = new Date(start.getTime() + nightHourInMinutes * 60_000); + hours.push({ + start, + end, + planetId: CHALDEAN[(chaldeanStartPos + hour) % 7], + isDaylight: false + }); + } + + return hours; + } + + window.TarotCalc = { + DAY_IN_MS, + toTitleCase, + getDateKey, + getCenteredWeekStartDay, + getMoonPhaseName, + groupDecansBySign, + getDecanForDate, + calcPlanetaryHoursForDayAndLocation + }; +})(); diff --git a/app/calendar-events.js b/app/calendar-events.js new file mode 100644 index 0000000..d57aa85 --- /dev/null +++ b/app/calendar-events.js @@ -0,0 +1,96 @@ +(function () { + const { + DAY_IN_MS, + toTitleCase, + getMoonPhaseName, + getDecanForDate, + calcPlanetaryHoursForDayAndLocation + } = window.TarotCalc; + + const PLANET_CALENDAR_IDS = new Set(["saturn", "jupiter", "mars", "sol", "venus", "mercury", "luna"]); + const BACKFILL_DAYS = 4; + const FORECAST_DAYS = 7; + + function buildWeekEvents(geo, referenceData, anchorDate) { + const baseDate = anchorDate || new Date(); + const events = []; + let runningId = 1; + + for (let offset = -BACKFILL_DAYS; offset <= FORECAST_DAYS; offset += 1) { + const date = new Date(baseDate.getTime() + offset * DAY_IN_MS); + const hours = calcPlanetaryHoursForDayAndLocation(date, geo); + const moonIllum = window.SunCalc.getMoonIllumination(date); + const moonPhase = getMoonPhaseName(moonIllum.phase); + const sunInfo = getDecanForDate(date, referenceData.signs, referenceData.decansBySign); + + const moonTarot = referenceData.planets.luna?.tarot?.majorArcana || "The High Priestess"; + events.push({ + id: `moon-${offset}`, + calendarId: "astrology", + category: "allday", + title: `Moon: ${moonPhase} (${Math.round(moonIllum.fraction * 100)}%) · ${moonTarot}`, + start: new Date(date.getFullYear(), date.getMonth(), date.getDate()), + end: new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59), + isReadOnly: true + }); + + if (sunInfo?.sign) { + const rulerPlanet = referenceData.planets[sunInfo.sign.rulingPlanetId]; + const bodyParts = [ + `${sunInfo.sign.symbol} ${toTitleCase(sunInfo.sign.element)} ${toTitleCase(sunInfo.sign.modality)}`, + rulerPlanet ? `Sign ruler: ${rulerPlanet.symbol} ${rulerPlanet.name}` : `Sign ruler: ${sunInfo.sign.rulingPlanetId}` + ]; + + if (sunInfo.decan) { + const decanRuler = referenceData.planets[sunInfo.decan.rulerPlanetId]; + const decanText = decanRuler + ? `Decan ${sunInfo.decan.index}: ${sunInfo.decan.tarotMinorArcana} (${decanRuler.symbol} ${decanRuler.name})` + : `Decan ${sunInfo.decan.index}: ${sunInfo.decan.tarotMinorArcana}`; + bodyParts.unshift(decanText); + } + + events.push({ + id: `sun-${offset}`, + calendarId: "astrology", + category: "allday", + title: `Sun in ${sunInfo.sign.name} · ${sunInfo.sign.tarot.majorArcana}`, + body: bodyParts.join("\n"), + start: new Date(date.getFullYear(), date.getMonth(), date.getDate()), + end: new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59), + isReadOnly: true + }); + } + + for (const hour of hours) { + const planet = referenceData.planets[hour.planetId]; + if (!planet) continue; + + const calendarId = PLANET_CALENDAR_IDS.has(hour.planetId) + ? `planet-${hour.planetId}` + : "planetary"; + + events.push({ + id: `ph-${runningId++}`, + calendarId, + category: "time", + title: `${planet.symbol} ${planet.name} · ${planet.tarot.majorArcana}`, + body: `${planet.weekday} current · ${hour.isDaylight ? "Day" : "Night"} hour\n${planet.magickTypes}`, + raw: { + planetSymbol: planet.symbol, + planetName: planet.name, + tarotName: planet.tarot.majorArcana + }, + start: hour.start, + end: hour.end, + isReadOnly: true + }); + } + } + + return events; + } + + window.TarotEventBuilder = { + buildWeekEvents + }; +})(); diff --git a/app/card-images.js b/app/card-images.js new file mode 100644 index 0000000..02246a4 --- /dev/null +++ b/app/card-images.js @@ -0,0 +1,654 @@ +(function () { + const DEFAULT_DECK_ID = "ceremonial-magick"; + + const trumpNumberByCanonicalName = { + fool: 0, + magus: 1, + magician: 1, + "high priestess": 2, + empress: 3, + emperor: 4, + hierophant: 5, + lovers: 6, + chariot: 7, + lust: 8, + strength: 8, + hermit: 9, + fortune: 10, + "wheel of fortune": 10, + justice: 11, + "hanged man": 12, + death: 13, + art: 14, + temperance: 14, + devil: 15, + tower: 16, + star: 17, + moon: 18, + sun: 19, + aeon: 20, + judgement: 20, + judgment: 20, + universe: 21, + world: 21 + }; + + const pipValueByToken = { + ace: 1, + two: 2, + three: 3, + four: 4, + five: 5, + six: 6, + seven: 7, + eight: 8, + nine: 9, + ten: 10, + "2": 2, + "3": 3, + "4": 4, + "5": 5, + "6": 6, + "7": 7, + "8": 8, + "9": 9, + "10": 10 + }; + + const rankWordByPipValue = { + 1: "Ace", + 2: "Two", + 3: "Three", + 4: "Four", + 5: "Five", + 6: "Six", + 7: "Seven", + 8: "Eight", + 9: "Nine", + 10: "Ten" + }; + + const trumpRomanToNumber = { + I: 1, + II: 2, + III: 3, + IV: 4, + V: 5, + VI: 6, + VII: 7, + VIII: 8, + IX: 9, + X: 10, + XI: 11, + XII: 12, + XIII: 13, + XIV: 14, + XV: 15, + XVI: 16, + XVII: 17, + XVIII: 18, + XIX: 19, + XX: 20, + XXI: 21 + }; + + const suitSearchAliasesById = { + wands: ["wands"], + cups: ["cups"], + swords: ["swords"], + disks: ["disks", "pentacles", "coins"] + }; + + const DECK_REGISTRY_PATH = "asset/tarot deck/decks.json"; + + const deckManifestSources = buildDeckManifestSources(); + + const manifestCache = new Map(); + let activeDeckId = DEFAULT_DECK_ID; + + function canonicalMajorName(cardName) { + return String(cardName || "") + .trim() + .toLowerCase() + .replace(/^the\s+/, "") + .replace(/\s+/g, " "); + } + + function canonicalMinorName(cardName) { + const parsedMinor = parseMinorCard(cardName); + if (!parsedMinor) { + return ""; + } + + return `${String(parsedMinor.rankKey || "").trim().toLowerCase()} of ${parsedMinor.suitId}`; + } + + function toTitleCase(value) { + const normalized = String(value || "").trim().toLowerCase(); + if (!normalized) { + return ""; + } + return normalized.charAt(0).toUpperCase() + normalized.slice(1); + } + + function normalizeDeckId(deckId) { + const normalized = String(deckId || "").trim().toLowerCase(); + if (deckManifestSources[normalized]) { + return normalized; + } + + if (deckManifestSources[DEFAULT_DECK_ID]) { + return DEFAULT_DECK_ID; + } + + const fallbackId = Object.keys(deckManifestSources)[0]; + return fallbackId || DEFAULT_DECK_ID; + } + + function normalizeTrumpNumber(value) { + const parsed = Number(value); + if (!Number.isInteger(parsed) || parsed < 0 || parsed > 21) { + return null; + } + + return parsed; + } + + function parseTrumpNumberKey(value) { + const normalized = String(value || "").trim().toUpperCase(); + if (!normalized) { + return null; + } + + if (/^\d+$/.test(normalized)) { + return normalizeTrumpNumber(Number(normalized)); + } + + if (Object.prototype.hasOwnProperty.call(trumpRomanToNumber, normalized)) { + return normalizeTrumpNumber(trumpRomanToNumber[normalized]); + } + + return null; + } + + function normalizeSuitId(suitInput) { + const suit = String(suitInput || "").trim().toLowerCase(); + if (suit === "pentacles") { + return "disks"; + } + return suit; + } + + function resolveDeckOptions(optionsOrDeckId) { + let resolvedDeckId = activeDeckId; + let trumpNumber = null; + + if (typeof optionsOrDeckId === "string") { + resolvedDeckId = normalizeDeckId(optionsOrDeckId); + } else if (optionsOrDeckId && typeof optionsOrDeckId === "object") { + if (optionsOrDeckId.deckId) { + resolvedDeckId = normalizeDeckId(optionsOrDeckId.deckId); + } + trumpNumber = normalizeTrumpNumber(optionsOrDeckId.trumpNumber); + } + + return { resolvedDeckId, trumpNumber }; + } + + function parseMinorCard(cardName) { + const match = String(cardName || "") + .trim() + .match(/^(ace|two|three|four|five|six|seven|eight|nine|ten|knight|queen|prince|princess|king|page|[2-9]|10)\s+of\s+(cups|wands|swords|pentacles|disks)$/i); + + if (!match) { + return null; + } + + const rankToken = String(match[1] || "").toLowerCase(); + const suitId = normalizeSuitId(match[2]); + const pipValue = pipValueByToken[rankToken] ?? null; + + if (Number.isFinite(pipValue)) { + const rankWord = rankWordByPipValue[pipValue] || ""; + return { + suitId, + pipValue, + court: "", + rankWord, + rankKey: rankWord.toLowerCase() + }; + } + + const courtWord = toTitleCase(rankToken); + if (!courtWord) { + return null; + } + + return { + suitId, + pipValue: null, + court: rankToken, + rankWord: courtWord, + rankKey: rankToken + }; + } + + function applyTemplate(template, variables) { + return String(template || "") + .replace(/\{([a-zA-Z0-9_]+)\}/g, (_, token) => { + const value = variables[token]; + return value == null ? "" : String(value); + }); + } + + function readManifestJsonSync(path) { + try { + const request = new XMLHttpRequest(); + request.open("GET", encodeURI(path), false); + request.send(null); + + const okStatus = (request.status >= 200 && request.status < 300) || request.status === 0; + if (!okStatus || !request.responseText) { + return null; + } + + return JSON.parse(request.responseText); + } catch { + return null; + } + } + + function toDeckSourceMap(sourceList) { + const sourceMap = {}; + if (!Array.isArray(sourceList)) { + return sourceMap; + } + + sourceList.forEach((entry) => { + const id = String(entry?.id || "").trim().toLowerCase(); + const basePath = String(entry?.basePath || "").trim().replace(/\/$/, ""); + const manifestPath = String(entry?.manifestPath || "").trim(); + if (!id || !basePath || !manifestPath) { + return; + } + + sourceMap[id] = { + id, + label: String(entry?.label || id), + basePath, + manifestPath + }; + }); + + return sourceMap; + } + + function buildDeckManifestSources() { + const registry = readManifestJsonSync(DECK_REGISTRY_PATH); + const registryDecks = Array.isArray(registry) + ? registry + : (Array.isArray(registry?.decks) ? registry.decks : null); + + return toDeckSourceMap(registryDecks); + } + + function normalizeDeckManifest(source, rawManifest) { + if (!rawManifest || typeof rawManifest !== "object") { + return null; + } + + const rawNameOverrides = rawManifest.nameOverrides; + const nameOverrides = {}; + if (rawNameOverrides && typeof rawNameOverrides === "object") { + Object.entries(rawNameOverrides).forEach(([rawKey, rawValue]) => { + const key = canonicalMajorName(rawKey); + const value = String(rawValue || "").trim(); + if (key && value) { + nameOverrides[key] = value; + } + }); + } + + const rawMajorNameOverridesByTrump = rawManifest.majorNameOverridesByTrump; + const majorNameOverridesByTrump = {}; + if (rawMajorNameOverridesByTrump && typeof rawMajorNameOverridesByTrump === "object") { + Object.entries(rawMajorNameOverridesByTrump).forEach(([rawKey, rawValue]) => { + const trumpNumber = parseTrumpNumberKey(rawKey); + const value = String(rawValue || "").trim(); + if (Number.isInteger(trumpNumber) && value) { + majorNameOverridesByTrump[trumpNumber] = value; + } + }); + } + + const rawMinorNameOverrides = rawManifest.minorNameOverrides; + const minorNameOverrides = {}; + if (rawMinorNameOverrides && typeof rawMinorNameOverrides === "object") { + Object.entries(rawMinorNameOverrides).forEach(([rawKey, rawValue]) => { + const key = canonicalMinorName(rawKey); + const value = String(rawValue || "").trim(); + if (key && value) { + minorNameOverrides[key] = value; + } + }); + } + + return { + id: source.id, + label: String(rawManifest.label || source.label || source.id), + basePath: String(source.basePath || "").replace(/\/$/, ""), + majors: rawManifest.majors || {}, + minors: rawManifest.minors || {}, + nameOverrides, + minorNameOverrides, + majorNameOverridesByTrump + }; + } + + function getDeckManifest(deckId) { + const normalizedDeckId = normalizeDeckId(deckId); + if (manifestCache.has(normalizedDeckId)) { + return manifestCache.get(normalizedDeckId); + } + + const source = deckManifestSources[normalizedDeckId]; + if (!source) { + manifestCache.set(normalizedDeckId, null); + return null; + } + + const rawManifest = readManifestJsonSync(source.manifestPath); + const normalizedManifest = normalizeDeckManifest(source, rawManifest); + manifestCache.set(normalizedDeckId, normalizedManifest); + return normalizedManifest; + } + + function getRankIndex(minorRule, parsedMinor) { + if (!minorRule || !parsedMinor) { + return null; + } + + const lowerRankWord = String(parsedMinor.rankWord || "").toLowerCase(); + const lowerRankKey = String(parsedMinor.rankKey || "").toLowerCase(); + + const indexByKey = minorRule.rankIndexByKey; + if (indexByKey && typeof indexByKey === "object") { + const mapped = Number(indexByKey[lowerRankKey]); + if (Number.isInteger(mapped) && mapped >= 0) { + return mapped; + } + } + + const rankOrder = Array.isArray(minorRule.rankOrder) ? minorRule.rankOrder : []; + for (let i = 0; i < rankOrder.length; i += 1) { + const candidate = String(rankOrder[i] || "").toLowerCase(); + if (candidate && (candidate === lowerRankWord || candidate === lowerRankKey)) { + return i; + } + } + + return null; + } + + function resolveMajorFile(manifest, canonicalName) { + const majorRule = manifest?.majors; + if (!majorRule || typeof majorRule !== "object") { + return null; + } + + if (majorRule.mode === "canonical-map") { + const cards = majorRule.cards || {}; + const fileName = cards[canonicalName]; + return typeof fileName === "string" && fileName ? fileName : null; + } + + const trumpNo = trumpNumberByCanonicalName[canonicalName]; + if (!Number.isInteger(trumpNo) || trumpNo < 0 || trumpNo > 21) { + return null; + } + + if (majorRule.mode === "trump-map") { + const cards = majorRule.cards || {}; + const fileName = cards[String(trumpNo)] ?? cards[trumpNo]; + return typeof fileName === "string" && fileName ? fileName : null; + } + + if (majorRule.mode === "trump-template") { + const numberPad = Number.isInteger(majorRule.numberPad) ? majorRule.numberPad : 2; + const template = String(majorRule.template || "{number}.png"); + const number = String(trumpNo).padStart(numberPad, "0"); + return applyTemplate(template, { + trump: trumpNo, + number + }); + } + + return null; + } + + function resolveMinorFile(manifest, parsedMinor) { + const minorRule = manifest?.minors; + if (!minorRule || typeof minorRule !== "object") { + return null; + } + + const rankIndex = getRankIndex(minorRule, parsedMinor); + if (!Number.isInteger(rankIndex) || rankIndex < 0) { + return null; + } + + if (minorRule.mode === "suit-base-and-rank-order") { + const suitBaseRaw = Number(minorRule?.suitBase?.[parsedMinor.suitId]); + if (!Number.isFinite(suitBaseRaw)) { + return null; + } + + const numberPad = Number.isInteger(minorRule.numberPad) ? minorRule.numberPad : 2; + const cardNumber = String(suitBaseRaw + rankIndex).padStart(numberPad, "0"); + const suitWord = String(minorRule?.suitLabel?.[parsedMinor.suitId] || toTitleCase(parsedMinor.suitId)); + const template = String(minorRule.template || "{number}_{rank} {suit}.webp"); + + return applyTemplate(template, { + number: cardNumber, + rank: parsedMinor.rankWord, + rankKey: parsedMinor.rankKey, + suit: suitWord, + suitId: parsedMinor.suitId, + index: rankIndex + 1 + }); + } + + if (minorRule.mode === "suit-prefix-and-rank-order") { + const suitPrefix = minorRule?.suitPrefix?.[parsedMinor.suitId]; + if (!suitPrefix) { + return null; + } + + const indexStart = Number.isInteger(minorRule.indexStart) ? minorRule.indexStart : 1; + const indexPad = Number.isInteger(minorRule.indexPad) ? minorRule.indexPad : 2; + const suitIndex = String(indexStart + rankIndex).padStart(indexPad, "0"); + const template = String(minorRule.template || "{suit}{index}.png"); + + return applyTemplate(template, { + suit: suitPrefix, + suitId: parsedMinor.suitId, + index: suitIndex, + rank: parsedMinor.rankWord, + rankKey: parsedMinor.rankKey + }); + } + + if (minorRule.mode === "suit-base-number-template") { + const suitBaseRaw = Number(minorRule?.suitBase?.[parsedMinor.suitId]); + if (!Number.isFinite(suitBaseRaw)) { + return null; + } + + const numberPad = Number.isInteger(minorRule.numberPad) ? minorRule.numberPad : 2; + const cardNumber = String(suitBaseRaw + rankIndex).padStart(numberPad, "0"); + const template = String(minorRule.template || "{number}.png"); + + return applyTemplate(template, { + number: cardNumber, + suitId: parsedMinor.suitId, + rank: parsedMinor.rankWord, + rankKey: parsedMinor.rankKey, + index: rankIndex + }); + } + + return null; + } + + function resolveWithDeck(deckId, cardName) { + const manifest = getDeckManifest(deckId); + if (!manifest) { + return null; + } + + const canonical = canonicalMajorName(cardName); + const majorFile = resolveMajorFile(manifest, canonical); + if (majorFile) { + return `${manifest.basePath}/${majorFile}`; + } + + const parsedMinor = parseMinorCard(cardName); + if (!parsedMinor) { + return null; + } + + const minorFile = resolveMinorFile(manifest, parsedMinor); + if (!minorFile) { + return null; + } + + return `${manifest.basePath}/${minorFile}`; + } + + function resolveTarotCardImage(cardName) { + const activePath = resolveWithDeck(activeDeckId, cardName); + if (activePath) { + return encodeURI(activePath); + } + + if (activeDeckId !== DEFAULT_DECK_ID) { + const fallbackPath = resolveWithDeck(DEFAULT_DECK_ID, cardName); + if (fallbackPath) { + return encodeURI(fallbackPath); + } + } + + return null; + } + + function resolveDisplayNameWithDeck(deckId, cardName, trumpNumber) { + const manifest = getDeckManifest(deckId); + const fallbackName = String(cardName || "").trim(); + if (!manifest) { + return fallbackName; + } + + let resolvedTrumpNumber = normalizeTrumpNumber(trumpNumber); + if (!Number.isInteger(resolvedTrumpNumber)) { + const canonical = canonicalMajorName(cardName); + resolvedTrumpNumber = normalizeTrumpNumber(trumpNumberByCanonicalName[canonical]); + } + + if (Number.isInteger(resolvedTrumpNumber)) { + const byTrump = manifest?.majorNameOverridesByTrump?.[resolvedTrumpNumber]; + if (byTrump) { + return byTrump; + } + } + + const canonical = canonicalMajorName(cardName); + const override = manifest?.nameOverrides?.[canonical]; + if (override) { + return override; + } + + const minorKey = canonicalMinorName(cardName); + const minorOverride = manifest?.minorNameOverrides?.[minorKey]; + if (minorOverride) { + return minorOverride; + } + + return fallbackName; + } + + function getTarotCardSearchAliases(cardName, optionsOrDeckId) { + const fallbackName = String(cardName || "").trim(); + if (!fallbackName) { + return []; + } + + const { resolvedDeckId, trumpNumber } = resolveDeckOptions(optionsOrDeckId); + const aliases = new Set(); + aliases.add(fallbackName); + + const displayName = String(resolveDisplayNameWithDeck(resolvedDeckId, fallbackName, trumpNumber) || "").trim(); + if (displayName) { + aliases.add(displayName); + } + + const canonicalMajor = canonicalMajorName(fallbackName); + const resolvedTrumpNumber = Number.isInteger(normalizeTrumpNumber(trumpNumber)) + ? normalizeTrumpNumber(trumpNumber) + : normalizeTrumpNumber(trumpNumberByCanonicalName[canonicalMajor]); + + if (Number.isInteger(resolvedTrumpNumber)) { + aliases.add(canonicalMajor); + aliases.add(`the ${canonicalMajor}`); + aliases.add(`trump ${resolvedTrumpNumber}`); + } + + const parsedMinor = parseMinorCard(fallbackName); + if (parsedMinor) { + const suitAliases = suitSearchAliasesById[parsedMinor.suitId] || [parsedMinor.suitId]; + suitAliases.forEach((suitAlias) => { + aliases.add(`${parsedMinor.rankKey} of ${suitAlias}`); + if (Number.isInteger(parsedMinor.pipValue)) { + aliases.add(`${parsedMinor.pipValue} of ${suitAlias}`); + } + }); + } + + return Array.from(aliases); + } + + function getTarotCardDisplayName(cardName, optionsOrDeckId) { + const { resolvedDeckId, trumpNumber } = resolveDeckOptions(optionsOrDeckId); + + return resolveDisplayNameWithDeck(resolvedDeckId, cardName, trumpNumber); + } + + function setActiveDeck(deckId) { + activeDeckId = normalizeDeckId(deckId); + getDeckManifest(activeDeckId); + return activeDeckId; + } + + function getDeckOptions() { + return Object.values(deckManifestSources).map((source) => { + const manifest = getDeckManifest(source.id); + return { + id: source.id, + label: manifest?.label || source.label + }; + }); + } + + document.addEventListener("settings:updated", (event) => { + const nextDeck = event?.detail?.settings?.tarotDeck; + setActiveDeck(nextDeck); + }); + + window.TarotCardImages = { + resolveTarotCardImage, + getTarotCardDisplayName, + getTarotCardSearchAliases, + setActiveDeck, + getDeckOptions, + getActiveDeck: () => activeDeckId + }; +})(); diff --git a/app/data-service.js b/app/data-service.js new file mode 100644 index 0000000..b89b1b9 --- /dev/null +++ b/app/data-service.js @@ -0,0 +1,372 @@ +(function () { + let magickManifestCache = null; + let magickDataCache = null; + + const DATA_ROOT = "data"; + const MAGICK_ROOT = DATA_ROOT; + + const TAROT_TRUMP_NUMBER_BY_NAME = { + "the fool": 0, + fool: 0, + "the magus": 1, + magus: 1, + magician: 1, + "the high priestess": 2, + "high priestess": 2, + "the empress": 3, + empress: 3, + "the emperor": 4, + emperor: 4, + "the hierophant": 5, + hierophant: 5, + "the lovers": 6, + lovers: 6, + "the chariot": 7, + chariot: 7, + strength: 8, + lust: 8, + "the hermit": 9, + hermit: 9, + fortune: 10, + "wheel of fortune": 10, + justice: 11, + "the hanged man": 12, + "hanged man": 12, + death: 13, + temperance: 14, + art: 14, + "the devil": 15, + devil: 15, + "the tower": 16, + tower: 16, + "the star": 17, + star: 17, + "the moon": 18, + moon: 18, + "the sun": 19, + sun: 19, + aeon: 20, + judgement: 20, + judgment: 20, + universe: 21, + world: 21, + "the world": 21 + }; + + const HEBREW_BY_TRUMP_NUMBER = { + 0: { hebrewLetterId: "alef", kabbalahPathNumber: 11 }, + 1: { hebrewLetterId: "bet", kabbalahPathNumber: 12 }, + 2: { hebrewLetterId: "gimel", kabbalahPathNumber: 13 }, + 3: { hebrewLetterId: "dalet", kabbalahPathNumber: 14 }, + 4: { hebrewLetterId: "he", kabbalahPathNumber: 15 }, + 5: { hebrewLetterId: "vav", kabbalahPathNumber: 16 }, + 6: { hebrewLetterId: "zayin", kabbalahPathNumber: 17 }, + 7: { hebrewLetterId: "het", kabbalahPathNumber: 18 }, + 8: { hebrewLetterId: "tet", kabbalahPathNumber: 19 }, + 9: { hebrewLetterId: "yod", kabbalahPathNumber: 20 }, + 10: { hebrewLetterId: "kaf", kabbalahPathNumber: 21 }, + 11: { hebrewLetterId: "lamed", kabbalahPathNumber: 22 }, + 12: { hebrewLetterId: "mem", kabbalahPathNumber: 23 }, + 13: { hebrewLetterId: "nun", kabbalahPathNumber: 24 }, + 14: { hebrewLetterId: "samekh", kabbalahPathNumber: 25 }, + 15: { hebrewLetterId: "ayin", kabbalahPathNumber: 26 }, + 16: { hebrewLetterId: "pe", kabbalahPathNumber: 27 }, + 17: { hebrewLetterId: "tsadi", kabbalahPathNumber: 28 }, + 18: { hebrewLetterId: "qof", kabbalahPathNumber: 29 }, + 19: { hebrewLetterId: "resh", kabbalahPathNumber: 30 }, + 20: { hebrewLetterId: "shin", kabbalahPathNumber: 31 }, + 21: { hebrewLetterId: "tav", kabbalahPathNumber: 32 } + }; + + const ICHING_PLANET_BY_PLANET_ID = { + sol: "Sun", + luna: "Moon", + mercury: "Mercury", + venus: "Venus", + mars: "Mars", + jupiter: "Jupiter", + saturn: "Saturn", + earth: "Earth", + uranus: "Uranus", + neptune: "Neptune", + pluto: "Pluto" + }; + + async function fetchJson(path) { + const response = await fetch(path); + if (!response.ok) { + throw new Error(`Failed to load ${path} (${response.status})`); + } + return response.json(); + } + + function buildObjectPath(target, pathParts, value) { + let cursor = target; + for (let index = 0; index < pathParts.length - 1; index += 1) { + const part = pathParts[index]; + if (!cursor[part] || typeof cursor[part] !== "object") { + cursor[part] = {}; + } + cursor = cursor[part]; + } + cursor[pathParts[pathParts.length - 1]] = value; + } + + function normalizeTarotName(value) { + return String(value || "") + .trim() + .toLowerCase() + .replace(/\s+/g, " "); + } + + function resolveTarotTrumpNumber(cardName) { + const key = normalizeTarotName(cardName); + if (!key) { + return null; + } + if (Object.prototype.hasOwnProperty.call(TAROT_TRUMP_NUMBER_BY_NAME, key)) { + return TAROT_TRUMP_NUMBER_BY_NAME[key]; + } + + const withoutLeadingThe = key.replace(/^the\s+/, ""); + if (Object.prototype.hasOwnProperty.call(TAROT_TRUMP_NUMBER_BY_NAME, withoutLeadingThe)) { + return TAROT_TRUMP_NUMBER_BY_NAME[withoutLeadingThe]; + } + + return null; + } + + function enrichAssociation(associations) { + if (!associations || typeof associations !== "object") { + return associations; + } + + const next = { ...associations }; + + if (next.tarotCard) { + const trumpNumber = resolveTarotTrumpNumber(next.tarotCard); + if (trumpNumber != null) { + if (!Number.isFinite(Number(next.tarotTrumpNumber))) { + next.tarotTrumpNumber = trumpNumber; + } + + const hebrew = HEBREW_BY_TRUMP_NUMBER[trumpNumber]; + if (hebrew) { + if (!next.hebrewLetterId) { + next.hebrewLetterId = hebrew.hebrewLetterId; + } + if (!Number.isFinite(Number(next.kabbalahPathNumber))) { + next.kabbalahPathNumber = hebrew.kabbalahPathNumber; + } + } + } + } + + const planetId = String(next.planetId || "").trim().toLowerCase(); + if (!next.iChingPlanetaryInfluence && planetId) { + const influence = ICHING_PLANET_BY_PLANET_ID[planetId]; + if (influence) { + next.iChingPlanetaryInfluence = influence; + } + } + + return next; + } + + function enrichCalendarMonth(month) { + const events = Array.isArray(month?.events) + ? month.events.map((event) => ({ + ...event, + associations: enrichAssociation(event?.associations) + })) + : []; + + return { + ...month, + associations: enrichAssociation(month?.associations), + events + }; + } + + function enrichCelestialHoliday(holiday) { + return { + ...holiday, + associations: enrichAssociation(holiday?.associations) + }; + } + + function enrichCalendarHoliday(holiday) { + return { + ...holiday, + associations: enrichAssociation(holiday?.associations) + }; + } + + async function loadMagickManifest() { + if (magickManifestCache) { + return magickManifestCache; + } + + magickManifestCache = await fetchJson(`${MAGICK_ROOT}/MANIFEST.json`); + return magickManifestCache; + } + + async function loadMagickDataset() { + if (magickDataCache) { + return magickDataCache; + } + + const manifest = await loadMagickManifest(); + const files = Array.isArray(manifest?.files) ? manifest.files : []; + const jsonFiles = files.filter((file) => file.endsWith(".json")); + + const entries = await Promise.all( + jsonFiles.map(async (relativePath) => { + const data = await fetchJson(`${MAGICK_ROOT}/${relativePath}`); + return [relativePath, data]; + }) + ); + + const grouped = {}; + entries.forEach(([relativePath, data]) => { + const noExtensionPath = relativePath.replace(/\.json$/i, ""); + const pathParts = noExtensionPath.split("/").filter(Boolean); + if (!pathParts.length) { + return; + } + buildObjectPath(grouped, pathParts, data); + }); + + magickDataCache = { + manifest, + grouped, + files: Object.fromEntries(entries) + }; + + return magickDataCache; + } + + async function loadReferenceData() { + const { groupDecansBySign } = window.TarotCalc; + const [ + planetsJson, + signsJson, + decansJson, + sabianJson, + planetScienceJson, + iChingJson, + calendarMonthsJson, + celestialHolidaysJson, + calendarHolidaysJson, + astronomyCyclesJson, + tarotDatabaseJson, + hebrewCalendarJson, + islamicCalendarJson, + wheelOfYearJson + ] = await Promise.all([ + fetchJson(`${DATA_ROOT}/planetary-correspondences.json`), + fetchJson(`${DATA_ROOT}/signs.json`), + fetchJson(`${DATA_ROOT}/decans.json`), + fetchJson(`${DATA_ROOT}/sabian-symbols.json`), + fetchJson(`${DATA_ROOT}/planet-science.json`), + fetchJson(`${DATA_ROOT}/i-ching.json`), + fetchJson(`${DATA_ROOT}/calendar-months.json`), + fetchJson(`${DATA_ROOT}/celestial-holidays.json`), + fetchJson(`${DATA_ROOT}/calendar-holidays.json`).catch(() => ({})), + fetchJson(`${DATA_ROOT}/astronomy-cycles.json`).catch(() => ({})), + fetchJson(`${DATA_ROOT}/tarot-database.json`).catch(() => ({})), + fetchJson(`${DATA_ROOT}/hebrew-calendar.json`).catch(() => ({})), + fetchJson(`${DATA_ROOT}/islamic-calendar.json`).catch(() => ({})), + fetchJson(`${DATA_ROOT}/wheel-of-year.json`).catch(() => ({})) + ]); + + const planets = planetsJson.planets || {}; + const signs = signsJson.signs || []; + const decans = decansJson.decans || []; + const sabianSymbols = Array.isArray(sabianJson?.symbols) ? sabianJson.symbols : []; + const planetScience = Array.isArray(planetScienceJson?.planets) + ? planetScienceJson.planets + : []; + const iChing = { + trigrams: Array.isArray(iChingJson?.trigrams) ? iChingJson.trigrams : [], + hexagrams: Array.isArray(iChingJson?.hexagrams) ? iChingJson.hexagrams : [], + correspondences: { + meta: iChingJson?.correspondences?.meta && typeof iChingJson.correspondences.meta === "object" + ? iChingJson.correspondences.meta + : {}, + tarotToTrigram: Array.isArray(iChingJson?.correspondences?.tarotToTrigram) + ? iChingJson.correspondences.tarotToTrigram + : [] + } + }; + + const calendarMonths = Array.isArray(calendarMonthsJson?.months) + ? calendarMonthsJson.months.map((month) => enrichCalendarMonth(month)) + : []; + + const celestialHolidays = Array.isArray(celestialHolidaysJson?.holidays) + ? celestialHolidaysJson.holidays.map((holiday) => enrichCelestialHoliday(holiday)) + : []; + + const calendarHolidays = Array.isArray(calendarHolidaysJson?.holidays) + ? calendarHolidaysJson.holidays.map((holiday) => enrichCalendarHoliday(holiday)) + : []; + + const astronomyCycles = astronomyCyclesJson && typeof astronomyCyclesJson === "object" + ? astronomyCyclesJson + : {}; + + const tarotDatabase = tarotDatabaseJson && typeof tarotDatabaseJson === "object" + ? tarotDatabaseJson + : {}; + + const sourceMeanings = tarotDatabase.meanings && typeof tarotDatabase.meanings === "object" + ? tarotDatabase.meanings + : {}; + + if (!sourceMeanings.majorByTrumpNumber || typeof sourceMeanings.majorByTrumpNumber !== "object") { + sourceMeanings.majorByTrumpNumber = {}; + } + + const existingByCardName = sourceMeanings.byCardName && typeof sourceMeanings.byCardName === "object" + ? sourceMeanings.byCardName + : {}; + + sourceMeanings.byCardName = existingByCardName; + + tarotDatabase.meanings = sourceMeanings; + + const hebrewCalendar = hebrewCalendarJson && typeof hebrewCalendarJson === "object" + ? hebrewCalendarJson + : {}; + const islamicCalendar = islamicCalendarJson && typeof islamicCalendarJson === "object" + ? islamicCalendarJson + : {}; + const wheelOfYear = wheelOfYearJson && typeof wheelOfYearJson === "object" + ? wheelOfYearJson + : {}; + + return { + planets, + signs, + decansBySign: groupDecansBySign(decans), + sabianSymbols, + planetScience, + iChing, + calendarMonths, + celestialHolidays, + calendarHolidays, + astronomyCycles, + tarotDatabase, + hebrewCalendar, + islamicCalendar, + wheelOfYear + }; + } + + window.TarotDataService = { + loadReferenceData, + loadMagickManifest, + loadMagickDataset + }; +})(); diff --git a/app/quiz-calendars.js b/app/quiz-calendars.js new file mode 100644 index 0000000..7acb5ab --- /dev/null +++ b/app/quiz-calendars.js @@ -0,0 +1,511 @@ +/* quiz-calendars.js — Dynamic quiz category plugin for calendar systems */ +/* Registers Hebrew, Islamic, and Wheel of the Year quiz categories with the quiz engine */ +(function () { + "use strict"; + + // ----- shared utilities (mirrored from ui-quiz.js since they aren't exported) ----- + + function normalizeOption(value) { + return String(value || "").trim(); + } + + function normalizeKey(value) { + return normalizeOption(value).toLowerCase(); + } + + function toUniqueOptionList(values) { + const seen = new Set(); + const unique = []; + (values || []).forEach((value) => { + const formatted = normalizeOption(value); + if (!formatted) return; + const key = normalizeKey(formatted); + if (seen.has(key)) return; + seen.add(key); + unique.push(formatted); + }); + return unique; + } + + function shuffle(list) { + const clone = list.slice(); + for (let i = clone.length - 1; i > 0; i -= 1) { + const j = Math.floor(Math.random() * (i + 1)); + [clone[i], clone[j]] = [clone[j], clone[i]]; + } + return clone; + } + + function buildOptions(correctValue, poolValues) { + const correct = normalizeOption(correctValue); + if (!correct) return null; + const uniquePool = toUniqueOptionList(poolValues || []); + if (!uniquePool.some((v) => normalizeKey(v) === normalizeKey(correct))) { + uniquePool.push(correct); + } + const distractors = uniquePool.filter((v) => normalizeKey(v) !== normalizeKey(correct)); + if (distractors.length < 3) return null; + const selected = shuffle(distractors).slice(0, 3); + const options = shuffle([correct, ...selected]); + const correctIndex = options.findIndex((v) => normalizeKey(v) === normalizeKey(correct)); + if (correctIndex < 0 || options.length < 4) return null; + return { options, correctIndex }; + } + + /** + * Build a validated quiz question template. + * Returns null if there aren't enough distractors for a 4-choice question. + */ + function makeTemplate(key, categoryId, category, prompt, answer, pool) { + const correctStr = normalizeOption(answer); + const promptStr = normalizeOption(prompt); + if (!key || !categoryId || !promptStr || !correctStr) return null; + + const uniquePool = toUniqueOptionList(pool || []); + if (!uniquePool.some((v) => normalizeKey(v) === normalizeKey(correctStr))) { + uniquePool.push(correctStr); + } + const distractorCount = uniquePool.filter((v) => normalizeKey(v) !== normalizeKey(correctStr)).length; + if (distractorCount < 3) return null; + + return { + key, + categoryId, + category, + promptByDifficulty: promptStr, + answerByDifficulty: correctStr, + poolByDifficulty: uniquePool + }; + } + + function ordinal(n) { + const num = Number(n); + if (!Number.isFinite(num)) return String(n); + const s = ["th", "st", "nd", "rd"]; + const v = num % 100; + return num + (s[(v - 20) % 10] || s[v] || s[0]); + } + + function getCalendarHolidayEntries(referenceData, calendarId) { + const all = Array.isArray(referenceData?.calendarHolidays) ? referenceData.calendarHolidays : []; + const target = String(calendarId || "").trim().toLowerCase(); + return all.filter((holiday) => String(holiday?.calendarId || "").trim().toLowerCase() === target); + } + + // ---- Hebrew Calendar Quiz -------------------------------------------------------- + + function buildHebrewCalendarQuiz(referenceData) { + const months = Array.isArray(referenceData?.hebrewCalendar?.months) + ? referenceData.hebrewCalendar.months + : []; + + if (months.length < 4) return []; + + const bank = []; + const categoryId = "hebrew-calendar-months"; + const category = "Hebrew Calendar"; + + const regularMonths = months.filter((m) => !m.leapYearOnly); + + const namePool = toUniqueOptionList(regularMonths.map((m) => m.name)); + const orderPool = toUniqueOptionList(regularMonths.map((m) => ordinal(m.order))); + const nativeNamePool = toUniqueOptionList(regularMonths.map((m) => m.nativeName).filter(Boolean)); + const zodiacPool = toUniqueOptionList( + regularMonths.map((m) => m.zodiacSign ? m.zodiacSign.charAt(0).toUpperCase() + m.zodiacSign.slice(1) : "").filter(Boolean) + ); + const tribePool = toUniqueOptionList(regularMonths.map((m) => m.tribe).filter(Boolean)); + const sensePool = toUniqueOptionList(regularMonths.map((m) => m.sense).filter(Boolean)); + + regularMonths.forEach((month) => { + const name = month.name; + const orderStr = ordinal(month.order); + const nativeName = month.nativeName; + const zodiac = month.zodiacSign + ? month.zodiacSign.charAt(0).toUpperCase() + month.zodiacSign.slice(1) + : null; + + // "Which month is Nisan in the Hebrew calendar?" → "1st" + if (namePool.length >= 4 && orderPool.length >= 4) { + const t = makeTemplate( + `hebrew-month-order:${month.id}`, + categoryId, + category, + `${name} is the ___ month of the Hebrew religious year`, + orderStr, + orderPool + ); + if (t) bank.push(t); + } + + // "The 1st month of the Hebrew calendar is" → "Nisan" + if (namePool.length >= 4 && orderPool.length >= 4) { + const t = makeTemplate( + `hebrew-order-to-name:${month.id}`, + categoryId, + category, + `The ${orderStr} month of the Hebrew religious year is`, + name, + namePool + ); + if (t) bank.push(t); + } + + // Native name → month name + if (nativeName && nativeNamePool.length >= 4) { + const t = makeTemplate( + `hebrew-native-name:${month.id}`, + categoryId, + category, + `The Hebrew month written as "${nativeName}" is`, + name, + namePool + ); + if (t) bank.push(t); + } + + // Zodiac association + if (zodiac && zodiacPool.length >= 4) { + const t = makeTemplate( + `hebrew-month-zodiac:${month.id}`, + categoryId, + category, + `The Hebrew month of ${name} corresponds to the zodiac sign`, + zodiac, + zodiacPool + ); + if (t) bank.push(t); + } + + // Tribe of Israel + if (month.tribe && tribePool.length >= 4) { + const t = makeTemplate( + `hebrew-month-tribe:${month.id}`, + categoryId, + category, + `The Hebrew month of ${name} is associated with the tribe of`, + month.tribe, + tribePool + ); + if (t) bank.push(t); + } + + // Sense + if (month.sense && sensePool.length >= 4) { + const t = makeTemplate( + `hebrew-month-sense:${month.id}`, + categoryId, + category, + `The sense associated with the Hebrew month of ${name} is`, + month.sense, + sensePool + ); + if (t) bank.push(t); + } + }); + + // Holiday repository-based questions (which month does X fall in?) + const monthNameById = new Map(regularMonths.map((month) => [String(month.id), month.name])); + const allObservances = getCalendarHolidayEntries(referenceData, "hebrew") + .map((holiday) => { + const monthName = monthNameById.get(String(holiday?.monthId || "")); + const obsName = String(holiday?.name || "").trim(); + if (!monthName || !obsName) { + return null; + } + return { obsName, monthName }; + }) + .filter(Boolean); + + if (namePool.length >= 4) { + allObservances.forEach(({ obsName, monthName }) => { + const t = makeTemplate( + `hebrew-obs-month:${obsName.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "")}`, + categoryId, + category, + `${obsName} occurs in which Hebrew month`, + monthName, + namePool + ); + if (t) bank.push(t); + }); + } + + return bank; + } + + // ---- Islamic Calendar Quiz ------------------------------------------------------- + + function buildIslamicCalendarQuiz(referenceData) { + const months = Array.isArray(referenceData?.islamicCalendar?.months) + ? referenceData.islamicCalendar.months + : []; + + if (months.length < 4) return []; + + const bank = []; + const categoryId = "islamic-calendar-months"; + const category = "Islamic Calendar"; + + const namePool = toUniqueOptionList(months.map((m) => m.name)); + const orderPool = toUniqueOptionList(months.map((m) => ordinal(m.order))); + const meaningPool = toUniqueOptionList(months.map((m) => m.meaning).filter(Boolean)); + + months.forEach((month) => { + const name = month.name; + const orderStr = ordinal(month.order); + + // Order → name + const t1 = makeTemplate( + `islamic-order-to-name:${month.id}`, + categoryId, + category, + `The ${orderStr} month of the Islamic calendar is`, + name, + namePool + ); + if (t1) bank.push(t1); + + // Name → order + const t2 = makeTemplate( + `islamic-month-order:${month.id}`, + categoryId, + category, + `${name} is the ___ month of the Islamic calendar`, + orderStr, + orderPool + ); + if (t2) bank.push(t2); + + // Meaning of name + if (month.meaning && meaningPool.length >= 4) { + const t3 = makeTemplate( + `islamic-month-meaning:${month.id}`, + categoryId, + category, + `The name "${name}" in Arabic means`, + month.meaning, + meaningPool + ); + if (t3) bank.push(t3); + } + + // Sacred month identification + if (month.sacred) { + const yesNoPool = ["Yes — warfare prohibited", "No", "Partially sacred", "Conditionally sacred"]; + const t4 = makeTemplate( + `islamic-sacred-${month.id}`, + categoryId, + category, + `Is ${name} one of the four sacred months (Al-Ashhur Al-Hurum)?`, + "Yes — warfare prohibited", + yesNoPool + ); + if (t4) bank.push(t4); + } + }); + + // Observance-based: "Ramadan is the Islamic month of ___" type + const observanceFacts = [ + { q: "The Islamic month of obligatory fasting (Sawm) is", a: "Ramadan" }, + { q: "Eid al-Fitr is celebrated in which Islamic month", a: "Shawwal" }, + { q: "Eid al-Adha falls in which Islamic month", a: "Dhu al-Hijja" }, + { q: "The Hajj pilgrimage takes place in which month", a: "Dhu al-Hijja" }, + { q: "The Prophet Muhammad's birth (Mawlid al-Nabi) is in", a: "Rabi' al-Awwal" }, + { q: "Ashura falls in which Islamic month", a: "Muharram" }, + { q: "Laylat al-Mi'raj (Night of Ascension) is in which month", a: "Rajab" }, + { q: "The Islamic New Year (Hijri New Year) begins in", a: "Muharram" } + ]; + + observanceFacts.forEach(({ q, a }) => { + if (namePool.some((n) => normalizeKey(n) === normalizeKey(a))) { + const t = makeTemplate( + `islamic-fact:${q.slice(0, 30).toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "")}`, + categoryId, + category, + q, + a, + namePool + ); + if (t) bank.push(t); + } + }); + + return bank; + } + + // ---- Wheel of the Year Quiz ------------------------------------------------------ + + function buildWheelOfYearQuiz(referenceData) { + const months = Array.isArray(referenceData?.wheelOfYear?.months) + ? referenceData.wheelOfYear.months + : []; + + if (months.length < 4) return []; + + const bank = []; + const categoryId = "wheel-of-year"; + const category = "Wheel of the Year"; + + const namePool = toUniqueOptionList(months.map((m) => m.name)); + const typePool = toUniqueOptionList(months.map((m) => m.type ? m.type.charAt(0).toUpperCase() + m.type.slice(1) : "").filter(Boolean)); + const elementPool = toUniqueOptionList( + months.map((m) => m.element || (m.associations && m.associations.element) || "").filter(Boolean) + ); + const datePool = toUniqueOptionList(months.map((m) => m.date).filter(Boolean)); + + months.forEach((month) => { + const name = month.name; + const date = month.date; + const element = month.element || ""; + const direction = month.associations?.direction || ""; + const directionPool = toUniqueOptionList(months.map((m) => m.associations?.direction || "").filter(Boolean)); + + // Date → Sabbat name + if (date && datePool.length >= 4) { + const t1 = makeTemplate( + `wheel-date-name:${month.id}`, + categoryId, + category, + `The Sabbat on ${date} is`, + name, + namePool + ); + if (t1) bank.push(t1); + } + + // Sabbat name → date + if (date && datePool.length >= 4) { + const t2 = makeTemplate( + `wheel-name-date:${month.id}`, + categoryId, + category, + `${name} falls on`, + date, + datePool + ); + if (t2) bank.push(t2); + } + + // Festival type (solar / cross-quarter) + if (month.type && typePool.length >= 2) { + const capType = month.type.charAt(0).toUpperCase() + month.type.slice(1); + const t3 = makeTemplate( + `wheel-type:${month.id}`, + categoryId, + category, + `${name} is a ___ festival`, + capType, + typePool + ); + if (t3) bank.push(t3); + } + + // Element association + if (element && elementPool.length >= 4) { + const t4 = makeTemplate( + `wheel-element:${month.id}`, + categoryId, + category, + `The primary element associated with ${name} is`, + element, + elementPool + ); + if (t4) bank.push(t4); + } + + // Direction + if (direction && directionPool.length >= 4) { + const t5 = makeTemplate( + `wheel-direction:${month.id}`, + categoryId, + category, + `The direction associated with ${name} is`, + direction, + directionPool + ); + if (t5) bank.push(t5); + } + + // Deities pool question + const deities = Array.isArray(month.associations?.deities) ? month.associations.deities : []; + const allDeities = toUniqueOptionList( + months.flatMap((m) => Array.isArray(m.associations?.deities) ? m.associations.deities : []) + ); + if (deities.length > 0 && allDeities.length >= 4) { + const mainDeity = deities[0]; + const t6 = makeTemplate( + `wheel-deity:${month.id}`, + categoryId, + category, + `${mainDeity} is primarily associated with which Sabbat`, + name, + namePool + ); + if (t6) bank.push(t6); + } + }); + + // Fixed knowledge questions + const wheelFacts = [ + { q: "The Celtic New Year Sabbat is", a: "Samhain" }, + { q: "Which Sabbat marks the longest night of the year", a: "Yule (Winter Solstice)" }, + { q: "The Spring Equinox Sabbat is called", a: "Ostara (Spring Equinox)" }, + { q: "The Summer Solstice Sabbat is called", a: "Litha (Summer Solstice)" }, + { q: "Which Sabbat is the first harvest festival", a: "Lughnasadh" }, + { q: "The Autumn Equinox Sabbat is called", a: "Mabon (Autumn Equinox)" }, + { q: "Which Sabbat is associated with the goddess Brigid", a: "Imbolc" }, + { q: "Beltane celebrates the beginning of which season", a: "Summer" } + ]; + + wheelFacts.forEach(({ q, a }, index) => { + const pool = index < 7 ? namePool : toUniqueOptionList(["Spring", "Summer", "Autumn / Fall", "Winter"]); + if (pool.some((p) => normalizeKey(p) === normalizeKey(a))) { + const t = makeTemplate( + `wheel-fact-${index}`, + categoryId, + category, + q, + a, + pool + ); + if (t) bank.push(t); + } + }); + + return bank; + } + + // ---- Registration ---------------------------------------------------------------- + + function registerCalendarQuizCategories() { + const { registerQuizCategory } = window.QuizSectionUi || {}; + if (typeof registerQuizCategory !== "function") { + return; + } + + registerQuizCategory( + "hebrew-calendar-months", + "Hebrew Calendar", + (referenceData) => buildHebrewCalendarQuiz(referenceData) + ); + + registerQuizCategory( + "islamic-calendar-months", + "Islamic Calendar", + (referenceData) => buildIslamicCalendarQuiz(referenceData) + ); + + registerQuizCategory( + "wheel-of-year", + "Wheel of the Year", + (referenceData) => buildWheelOfYearQuiz(referenceData) + ); + } + + // Register immediately — ui-quiz.js loads before this file + registerCalendarQuizCategories(); + + window.QuizCalendarsPlugin = { + registerCalendarQuizCategories + }; +})(); diff --git a/app/styles.css b/app/styles.css new file mode 100644 index 0000000..a09277d --- /dev/null +++ b/app/styles.css @@ -0,0 +1,3560 @@ + :root { + --font-script-main: "Noto Sans Hebrew", "Noto Serif", "Noto Sans Phoenician", "Segoe UI Symbol", "Arial Unicode MS", sans-serif; + --font-script-display: "Noto Sans Hebrew", "Noto Serif", "Noto Sans Phoenician", "Segoe UI Symbol", serif; + --font-script-arabic: "Amiri", "Noto Naskh Arabic", "Scheherazade New", "Arabic Typesetting", "Arial Unicode MS", serif; + --font-script-enochian: "Enochian", "Petrus Enochian", "Segoe UI Historic", "Segoe UI Symbol", serif; + } + + body { + margin: 0; + font-family: system-ui, -apple-system, "Segoe UI", Roboto, var(--font-script-main); + background: #0f0f14; + color: #f4f4f5; + } + .topbar { + padding: 12px 16px; + display: flex; + justify-content: space-between; + align-items: center; + gap: 10px; + border-bottom: 1px solid #27272a; + background: #18181b; + } + .topbar-actions { + display: flex; + align-items: center; + gap: 8px; + } + .topbar-dropdown { + position: relative; + display: inline-flex; + align-items: center; + } + .topbar-dropdown-menu { + position: absolute; + top: 100%; + left: 0; + min-width: 140px; + display: none; + grid-template-columns: 1fr; + gap: 4px; + padding: 6px; + border-radius: 10px; + border: 1px solid #3f3f46; + background: #18181b; + box-shadow: 0 12px 24px rgba(0, 0, 0, 0.3); + z-index: 40; + } + .topbar-dropdown.is-open .topbar-dropdown-menu { + display: grid; + } + .topbar-sub-trigger { + width: 100%; + text-align: left; + font-size: 13px; + padding: 6px 10px; + } + .topbar-sub-trigger.is-active { + background: #3f3f46; + border-color: #52525b; + } + .settings-trigger { + padding: 7px 12px; + border-radius: 8px; + border: 1px solid #3f3f46; + background: #27272a; + color: #f4f4f5; + cursor: pointer; + font-size: 14px; + } + .settings-trigger:hover { + background: #3f3f46; + } + .settings-trigger[aria-pressed="true"] { + background: #3f3f46; + } + #tarot-section { + height: calc(100vh - 61px); + background: #18181b; + box-sizing: border-box; + overflow: hidden; + display: grid; + grid-template-rows: auto minmax(0, 1fr); + } + #planet-section { + height: calc(100vh - 61px); + background: #18181b; + box-sizing: border-box; + overflow: hidden; + } + #cycles-section { + height: calc(100vh - 61px); + background: #18181b; + box-sizing: border-box; + overflow: hidden; + } + #iching-section { + height: calc(100vh - 61px); + background: #18181b; + box-sizing: border-box; + overflow: hidden; + } + #calendar-section { + height: calc(100vh - 61px); + background: #18181b; + box-sizing: border-box; + overflow: hidden; + } + #holiday-section { + height: calc(100vh - 61px); + background: #18181b; + box-sizing: border-box; + overflow: hidden; + } + #tarot-section[hidden] { + display: none; + } + #planet-section[hidden] { + display: none; + } + #cycles-section[hidden] { + display: none; + } + #iching-section[hidden] { + display: none; + } + #calendar-section[hidden] { + display: none; + } + #holiday-section[hidden] { + display: none; + } + .calendar-year-control { + display: flex; + justify-content: space-between; + align-items: center; + gap: 10px; + } + .calendar-holiday-filter { + display: flex; + justify-content: space-between; + align-items: center; + gap: 10px; + } + .calendar-year-control label { + font-size: 12px; + color: #a1a1aa; + } + .calendar-holiday-filter label { + font-size: 12px; + color: #a1a1aa; + } + .calendar-year-control input { + width: 110px; + padding: 7px 8px; + border-radius: 6px; + border: 1px solid #3f3f46; + background: #09090b; + color: #f4f4f5; + box-sizing: border-box; + font-size: 13px; + text-align: right; + } + .calendar-holiday-filter select { + min-width: 150px; + max-width: 210px; + width: 100%; + padding: 7px 8px; + border-radius: 6px; + border: 1px solid #3f3f46; + background: #09090b; + color: #f4f4f5; + box-sizing: border-box; + font-size: 13px; + } + .cal-item-stack { + display: grid; + gap: 10px; + margin-top: 8px; + } + .cal-item-row { + border: 1px solid #3f3f46; + border-radius: 8px; + padding: 8px; + background: #111118; + display: grid; + gap: 6px; + } + .cal-item-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + flex-wrap: wrap; + } + .cal-item-name { + font-weight: 600; + color: #f4f4f5; + } + .tarot-layout { + min-height: 0; + display: grid; + grid-template-columns: minmax(260px, 340px) minmax(0, 1fr); + position: relative; + z-index: 1; + } + + .tarot-section-house-top { + padding: 10px 12px 8px; + border-bottom: 1px solid #27272a; + max-height: min(44vh, 420px); + overflow: auto; + background: #151520; + position: relative; + z-index: 2; + } + .tarot-list-panel { + display: flex; + flex-direction: column; + min-width: 0; + min-height: 0; + border-right: 1px solid #27272a; + background: #111118; + } + .tarot-list-header { + padding: 14px 14px 10px; + border-bottom: 1px solid #27272a; + display: flex; + align-items: baseline; + justify-content: space-between; + gap: 8px; + } + .tarot-list-count { + color: #a1a1aa; + font-size: 12px; + } + .dataset-search-wrap { + padding: 8px 12px; + border-bottom: 1px solid #27272a; + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 6px; + align-items: center; + } + .dataset-search-input { + width: 100%; + padding: 7px 8px; + border-radius: 6px; + border: 1px solid #3f3f46; + background: #09090b; + color: #f4f4f5; + box-sizing: border-box; + font-size: 13px; + } + .dataset-search-input::placeholder { + color: #71717a; + } + .dataset-search-clear { + width: 30px; + height: 30px; + border-radius: 6px; + border: 1px solid #3f3f46; + background: #27272a; + color: #f4f4f5; + cursor: pointer; + line-height: 1; + font-size: 14px; + padding: 0; + } + .dataset-search-clear:hover { + background: #3f3f46; + } + .dataset-search-clear:disabled { + opacity: 0.45; + cursor: default; + } + .tarot-card-list { + overflow: auto; + flex: 1; + min-height: 0; + padding: 8px; + display: flex; + flex-direction: column; + gap: 6px; + } + .tarot-list-item { + width: 100%; + text-align: left; + display: flex; + flex-direction: column; + gap: 2px; + padding: 8px 10px; + border-radius: 8px; + border: 1px solid #3f3f46; + background: #18181b; + color: #f4f4f5; + cursor: pointer; + } + .tarot-list-item:hover, + .tarot-list-item.is-selected { + background: #27272a; + border-color: #52525b; + } + .tarot-list-name { + font-size: 14px; + font-weight: 600; + } + .tarot-list-meta { + font-size: 12px; + color: #a1a1aa; + } + .tarot-detail-panel { + min-width: 0; + overflow: auto; + padding: 18px; + display: grid; + grid-template-rows: auto auto auto; + gap: 16px; + background: #18181b; + } + .tarot-detail-top { + display: grid; + grid-template-columns: 150px minmax(0, 1fr); + gap: 16px; + align-items: start; + } + .tarot-detail-image { + width: 150px; + height: 225px; + object-fit: cover; + border-radius: 6px; + border: 1px solid #3f3f46; + background: #09090b; + } + .tarot-detail-heading h2 { + margin: 0; + font-size: 24px; + line-height: 1.2; + } + .tarot-detail-type { + margin-top: 4px; + color: #a1a1aa; + font-size: 13px; + } + .tarot-detail-summary { + margin-top: 10px; + font-size: 14px; + line-height: 1.45; + color: #e4e4e7; + } + .tarot-meanings { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 12px; + } + .tarot-meaning-card { + border: 1px solid #3f3f46; + border-radius: 10px; + padding: 10px; + background: #111118; + } + .tarot-meaning-card strong { + display: block; + font-size: 12px; + text-transform: uppercase; + letter-spacing: 0.04em; + color: #a1a1aa; + margin-bottom: 6px; + } + .tarot-meaning-card div { + font-size: 14px; + line-height: 1.45; + color: #e4e4e7; + } + .tarot-meta-grid { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); + gap: 12px; + } + .tarot-meta-card { + border: 1px solid #3f3f46; + border-radius: 10px; + padding: 10px; + background: #111118; + } + .tarot-meta-card strong { + display: block; + margin-bottom: 8px; + color: #a1a1aa; + font-size: 12px; + text-transform: uppercase; + letter-spacing: 0.04em; + } + .tarot-keywords { + display: flex; + flex-wrap: wrap; + gap: 6px; + } + .tarot-keyword-chip { + border: 1px solid #52525b; + border-radius: 999px; + padding: 4px 8px; + font-size: 12px; + color: #e4e4e7; + background: #27272a; + } + .tarot-relations { + margin: 0; + padding: 0; + list-style: none; + display: grid; + gap: 6px; + font-size: 13px; + line-height: 1.35; + } + .tarot-relation-btn { + width: 100%; + border: 1px solid #3f3f46; + border-radius: 8px; + background: #18181b; + color: #e4e4e7; + cursor: pointer; + text-align: left; + padding: 7px 8px; + font-size: 13px; + line-height: 1.35; + } + .tarot-relation-btn:hover, + .tarot-relation-btn.is-selected { + background: #27272a; + border-color: #52525b; + } + .tarot-relation-btn-static { + cursor: default; + } + .tarot-relation-btn-static:hover { + background: #18181b; + border-color: #3f3f46; + } + /* Navigatable relation list items */ + .tarot-rel-item { + display: flex; + gap: 4px; + align-items: stretch; + } + .tarot-rel-item .tarot-relation-btn { + flex: 1; + width: auto; + } + .tarot-rel-nav-btn { + flex-shrink: 0; + width: 32px; + border: 1px solid #3f3f46; + border-radius: 8px; + background: #18181b; + color: #7070a0; + cursor: pointer; + font-size: 13px; + display: flex; + align-items: center; + justify-content: center; + padding: 0; + transition: background 0.1s, color 0.1s, border-color 0.1s; + } + .tarot-rel-nav-btn:hover { + background: #27272a; + color: #c8b8f8; + border-color: #7060b0; + } + .tarot-relation-detail { + margin-top: 10px; + border: 1px solid #3f3f46; + border-radius: 8px; + background: #18181b; + padding: 8px; + display: grid; + gap: 6px; + } + .tarot-relation-detail-title { + font-size: 13px; + font-weight: 600; + color: #f4f4f5; + } + .tarot-relation-detail-meta { + font-size: 12px; + color: #a1a1aa; + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + } + .tarot-relation-detail-data { + font-size: 12px; + line-height: 1.45; + color: #d4d4d8; + white-space: pre-wrap; + margin: 0; + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + } + + /* Kabbalah path cross-reference panel in tarot detail */ + .tarot-kab-path-card { + border: 1px solid #4a4a6a; + border-radius: 10px; + padding: 12px 14px; + background: #0e0e1e; + margin-top: 4px; + } + .tarot-kab-path-card[hidden] { display: none; } + .tarot-kab-path-card > strong { + display: block; + margin-bottom: 8px; + color: #a1a1aa; + font-size: 12px; + text-transform: uppercase; + letter-spacing: 0.04em; + } + .tarot-kab-path-row { + display: flex; + align-items: center; + gap: 14px; + margin-bottom: 10px; + } + .tarot-kab-letter { + font-size: 36px; + line-height: 1; + color: #a8a8e0; + flex-shrink: 0; + font-family: var(--font-script-display); + } + .tarot-kab-meta { + display: flex; + flex-direction: column; + gap: 3px; + } + .tarot-kab-name { + font-size: 14px; + color: #e4e4e7; + } + .tarot-kab-connects { + font-size: 12px; + color: #a1a1aa; + } + + .tarot-misc-section { + display: grid; + gap: 12px; + } + + .tarot-misc-title { + display: block; + margin: 0; + color: #a1a1aa; + font-size: 12px; + text-transform: uppercase; + letter-spacing: 0.04em; + } + + .tarot-house-card { + display: grid; + gap: 10px; + } + + .tarot-house-layout { + display: grid; + gap: 12px; + } + + .tarot-house-trumps { + display: grid; + gap: 8px; + overflow-x: auto; + padding-bottom: 2px; + } + + .tarot-house-trump-row { + display: flex; + flex-wrap: nowrap; + gap: 6px; + width: max-content; + margin: 0 auto; + } + + .tarot-house-bottom-grid { + display: grid; + grid-template-columns: repeat(3, max-content); + column-gap: 6px; + row-gap: 0; + justify-content: center; + } + + .tarot-house-column { + display: grid; + gap: 8px; + align-content: start; + } + + .tarot-house-row { + display: flex; + flex-wrap: nowrap; + gap: 6px; + } + + .tarot-house-card-btn { + border: 1px solid #3f3f46; + border-radius: 6px; + padding: 0; + background: #18181b; + cursor: pointer; + overflow: hidden; + line-height: 0; + position: relative; + transform-origin: center; + transition: transform 120ms ease, border-color 120ms ease, box-shadow 120ms ease; + } + + .tarot-house-card-btn:hover { + border-color: #7060b0; + background: #27272a; + } + + .tarot-house-card-btn.is-selected { + border-color: #7060b0; + background: #27272a; + transform: scale(1.18); + z-index: 2; + box-shadow: 0 0 0 2px #7060b0; + } + + .tarot-house-card-image { + display: block; + width: 76.8px; + height: 115.2px; + object-fit: cover; + background: #09090b; + } + + .tarot-house-card-fallback { + width: 76.8px; + height: 115.2px; + display: flex; + align-items: center; + justify-content: center; + color: #a1a1aa; + font-size: 10px; + font-weight: 600; + letter-spacing: 0.03em; + text-transform: uppercase; + line-height: 1.2; + padding: 4px; + box-sizing: border-box; + } + + .planet-layout { + height: 100%; + display: grid; + grid-template-columns: minmax(240px, 320px) minmax(0, 1fr); + min-height: 0; + } + .planet-list-panel { + display: flex; + flex-direction: column; + min-width: 0; + min-height: 0; + overflow-x: hidden; + border-right: 1px solid #27272a; + background: #111118; + } + .planet-list-header { + padding: 14px 14px 10px; + border-bottom: 1px solid #27272a; + display: flex; + align-items: baseline; + justify-content: space-between; + gap: 8px; + } + .planet-list-count { + color: #a1a1aa; + font-size: 12px; + } + .planet-card-list { + overflow: auto; + flex: 1; + min-height: 0; + padding: 8px; + display: flex; + flex-direction: column; + gap: 6px; + } + .planet-list-item { + width: 100%; + text-align: left; + display: flex; + flex-direction: column; + gap: 2px; + padding: 8px 10px; + border-radius: 8px; + border: 1px solid #3f3f46; + background: #18181b; + color: #f4f4f5; + cursor: pointer; + } + .planet-list-item:hover, + .planet-list-item.is-selected { + background: #27272a; + border-color: #52525b; + } + .planet-list-name { + font-size: 14px; + font-weight: 600; + } + .planet-list-meta { + font-size: 12px; + color: #a1a1aa; + } + .planet-detail-panel { + min-width: 0; + overflow: auto; + padding: 18px; + display: grid; + grid-template-rows: auto auto; + gap: 16px; + background: #18181b; + } + .planet-detail-heading h2 { + margin: 0; + font-size: 24px; + line-height: 1.2; + } + .planet-detail-type { + margin-top: 4px; + color: #a1a1aa; + font-size: 13px; + } + .planet-detail-summary { + margin-top: 10px; + font-size: 14px; + line-height: 1.45; + color: #e4e4e7; + } + .planet-meta-grid { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); + gap: 12px; + align-items: start; + } + .planet-meta-card { + border: 1px solid #3f3f46; + border-radius: 10px; + padding: 10px; + background: #111118; + } + .planet-meta-card strong { + display: block; + margin-bottom: 8px; + color: #a1a1aa; + font-size: 12px; + text-transform: uppercase; + letter-spacing: 0.04em; + } + .planet-facts-list { + display: grid; + gap: 6px; + } + .planet-fact-row { + display: grid; + grid-template-columns: minmax(140px, 1fr) minmax(120px, 1fr); + gap: 10px; + align-items: baseline; + padding-bottom: 4px; + border-bottom: 1px dashed #27272a; + } + .planet-fact-label { + color: #a1a1aa; + font-size: 12px; + } + .planet-fact-value { + color: #f4f4f5; + font-size: 13px; + text-align: right; + } + .planet-notes { + margin: 0; + padding-left: 18px; + display: grid; + gap: 6px; + color: #e4e4e7; + font-size: 13px; + line-height: 1.4; + } + .planet-text { + margin: 0; + color: #e4e4e7; + font-size: 13px; + line-height: 1.4; + } + .iching-trigram-grid { + display: grid; + gap: 8px; + } + .iching-trigram-card { + border: 1px solid #3f3f46; + border-radius: 8px; + background: #18181b; + padding: 8px; + display: grid; + gap: 4px; + } + .iching-trigram-title { + font-size: 13px; + font-weight: 600; + color: #f4f4f5; + } + .iching-trigram-meta { + font-size: 12px; + color: #a1a1aa; + } + .iching-trigram-diagram { + display: grid; + gap: 6px; + align-items: start; + } + .iching-line-label { + font-size: 11px; + color: #a1a1aa; + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + letter-spacing: 0.05em; + } + .iching-lines { + width: min(100%, 140px); + display: grid; + gap: 6px; + } + .iching-lines-trigram { + width: min(100%, 110px); + gap: 5px; + } + .iching-lines-hexagram { + width: min(100%, 170px); + gap: 7px; + } + .iching-line { + height: 8px; + border-radius: 999px; + background: #e4e4e7; + position: relative; + } + .iching-line.is-yin { + background: transparent; + } + .iching-line.is-yin::before, + .iching-line.is-yin::after { + content: ""; + position: absolute; + top: 0; + height: 100%; + width: calc(50% - 8px); + border-radius: 999px; + background: #e4e4e7; + } + .iching-line.is-yin::before { + left: 0; + } + .iching-line.is-yin::after { + right: 0; + } + .iching-diagram { + margin: 6px 0 0; + padding: 8px; + border: 1px solid #3f3f46; + border-radius: 8px; + background: #111118; + color: #d4d4d8; + min-height: 86px; + display: grid; + align-content: center; + justify-items: start; + } + .iching-tarot-text { + white-space: pre-line; + line-height: 1.4; + font-size: 12px; + } + + /* ── Kabbalah sections ──────────────────────────────────────────────── */ + #quiz-section { + height: calc(100vh - 61px); + background: #18181b; + box-sizing: border-box; + overflow: auto; + padding: 20px; + } + #quiz-section[hidden] { display: none; } + + .quiz-layout { + max-width: 860px; + margin: 0 auto; + display: grid; + gap: 14px; + } + + .quiz-card { + border: 1px solid #3f3f46; + border-radius: 12px; + background: #111118; + padding: 16px; + color: #d4d4d8; + display: grid; + gap: 12px; + } + + .quiz-score-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 8px; + } + + .quiz-toolbar { + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; + } + + .quiz-category-label { + font-size: 12px; + color: #a1a1aa; + text-transform: uppercase; + letter-spacing: 0.04em; + } + + .quiz-category-select { + min-width: 220px; + padding: 7px 10px; + border-radius: 8px; + border: 1px solid #3f3f46; + background: #18181b; + color: #f4f4f5; + font-size: 14px; + } + + .quiz-difficulty-label { + font-size: 12px; + color: #a1a1aa; + text-transform: uppercase; + letter-spacing: 0.04em; + margin-left: 8px; + } + + .quiz-difficulty-select { + min-width: 140px; + padding: 7px 10px; + border-radius: 8px; + border: 1px solid #3f3f46; + background: #18181b; + color: #f4f4f5; + font-size: 14px; + } + + .quiz-score-item { + border: 1px solid #3f3f46; + border-radius: 8px; + background: #18181b; + padding: 8px; + display: grid; + gap: 2px; + } + + .quiz-score-label { + font-size: 11px; + letter-spacing: 0.04em; + text-transform: uppercase; + color: #a1a1aa; + } + + .quiz-score-value { + font-size: 18px; + color: #f4f4f5; + font-weight: 700; + line-height: 1.2; + } + + .quiz-question { + margin: 0; + font-size: 24px; + line-height: 1.35; + color: #f4f4f5; + } + + .quiz-options { + display: grid; + gap: 8px; + } + + .quiz-option { + width: 100%; + text-align: left; + padding: 10px 12px; + border-radius: 8px; + border: 1px solid #3f3f46; + background: #18181b; + color: #f4f4f5; + cursor: pointer; + font-size: 14px; + } + + .quiz-option:hover:enabled { + background: #27272a; + } + + .quiz-option:disabled { + cursor: default; + opacity: 0.92; + } + + .quiz-option.is-correct { + border-color: #16a34a; + background: #052e16; + color: #dcfce7; + } + + .quiz-option.is-wrong { + border-color: #dc2626; + background: #450a0a; + color: #fee2e2; + } + + .quiz-actions { + display: flex; + gap: 8px; + flex-wrap: wrap; + } + + #astronomy-section { + height: calc(100vh - 61px); + background: #18181b; + box-sizing: border-box; + overflow: hidden; + } + #astronomy-section[hidden] { display: none; } + + #natal-section { + height: calc(100vh - 61px); + background: #18181b; + box-sizing: border-box; + overflow: hidden; + } + #natal-section[hidden] { display: none; } + + #kabbalah-section { + height: calc(100vh - 61px); + background: #18181b; + box-sizing: border-box; + overflow: hidden; + } + #kabbalah-section[hidden] { display: none; } + + #kabbalah-tree-section { + height: calc(100vh - 61px); + background: #18181b; + box-sizing: border-box; + overflow: hidden; + } + #kabbalah-tree-section[hidden] { display: none; } + + .kabbalah-placeholder { + height: 100%; + display: grid; + place-items: center; + padding: 20px; + box-sizing: border-box; + } + .kabbalah-placeholder-card { + width: min(680px, 100%); + border: 1px solid #3f3f46; + border-radius: 12px; + background: #111118; + padding: 16px; + color: #d4d4d8; + text-align: left; + } + .kabbalah-placeholder-card strong { + display: block; + margin-bottom: 8px; + color: #a1a1aa; + font-size: 12px; + text-transform: uppercase; + letter-spacing: 0.04em; + } + + .natal-chart-summary { + margin-top: 10px; + margin-bottom: 0; + white-space: pre-wrap; + font-family: inherit; + } + + #cube-section { + height: calc(100vh - 61px); + background: #18181b; + box-sizing: border-box; + overflow: hidden; + } + #cube-section[hidden] { display: none; } + + .cube-svg { + display: block; + } + + .cube-rotation-controls { + display: grid; + grid-template-columns: repeat(5, minmax(0, 1fr)); + gap: 6px; + padding: 8px 12px 6px; + border-bottom: 1px solid #27272a; + } + + .cube-rotation-btn { + padding: 5px 0; + border: 1px solid #3f3f46; + border-radius: 6px; + background: #18181b; + color: #d4d4d8; + cursor: pointer; + font-size: 12px; + line-height: 1; + } + + .cube-rotation-btn:hover { + background: #27272a; + border-color: #52525b; + color: #f4f4f5; + } + + .cube-marker-mode-control { + grid-column: 1 / -1; + display: flex; + align-items: center; + gap: 8px; + margin-top: 2px; + } + + .cube-marker-mode-label { + font-size: 11px; + color: #a1a1aa; + letter-spacing: 0.02em; + white-space: nowrap; + } + + .cube-marker-mode-select { + flex: 1; + min-width: 0; + padding: 5px 8px; + border: 1px solid #3f3f46; + border-radius: 6px; + background: #18181b; + color: #d4d4d8; + font-size: 12px; + line-height: 1.2; + } + + .cube-connector-toggle-control, + .cube-primal-toggle-control { + grid-column: 1 / -1; + display: flex; + align-items: center; + gap: 8px; + margin-top: 2px; + font-size: 11px; + color: #a1a1aa; + } + + .cube-connector-toggle-control input, + .cube-primal-toggle-control input { + margin: 0; + accent-color: #a1a1aa; + } + + .cube-rotation-readout { + grid-column: 1 / -1; + margin-top: 2px; + text-align: center; + font-size: 11px; + color: #a1a1aa; + font-variant-numeric: tabular-nums; + letter-spacing: 0.02em; + } + + .cube-face { + cursor: pointer; + } + + .cube-face.is-active { + stroke-width: 2.5; + } + + .cube-face-symbol { + fill: currentColor; + fill-opacity: 0.62; + font-size: 27px; + font-weight: 700; + pointer-events: none; + -webkit-user-select: none; + user-select: none; + } + + .cube-face-symbol.is-active { + fill-opacity: 1; + font-size: 30px; + } + + .cube-face-symbol.is-missing { + fill: #ef4444; + fill-opacity: 1; + } + + .cube-face-label { + fill: currentColor; + fill-opacity: 0.72; + font-size: 8.5px; + pointer-events: none; + } + + .cube-face-label.is-active { + fill-opacity: 0.95; + font-size: 9px; + font-weight: 700; + } + + .cube-center-hit { + fill: transparent; + stroke: none; + } + + .cube-center-symbol { + fill: currentColor; + fill-opacity: 0.98; + font-size: 26px; + font-weight: 800; + pointer-events: none; + -webkit-user-select: none; + user-select: none; + } + + .cube-center-symbol.is-active { + fill-opacity: 1; + font-size: 28px; + } + + .cube-center-symbol.is-missing { + fill: #ef4444; + fill-opacity: 1; + } + + .cube-connector { + cursor: pointer; + } + + .cube-connector-line { + stroke: currentColor; + stroke-opacity: 0.42; + stroke-width: 1.6; + stroke-dasharray: 4 3; + transition: stroke-opacity 120ms ease, stroke-width 120ms ease; + } + + .cube-connector:hover .cube-connector-line, + .cube-connector.is-active .cube-connector-line { + stroke-opacity: 0.9; + stroke-width: 2.2; + stroke-dasharray: none; + } + + .cube-connector-hit { + stroke: transparent; + stroke-width: 12; + } + + .cube-connector-symbol { + fill: currentColor; + fill-opacity: 0.82; + font-size: 14px; + font-weight: 700; + pointer-events: none; + -webkit-user-select: none; + user-select: none; + transition: fill-opacity 120ms ease, font-size 120ms ease; + } + + .cube-connector:hover .cube-connector-symbol, + .cube-connector.is-active .cube-connector-symbol { + fill-opacity: 1; + font-size: 15px; + } + + .cube-direction { + cursor: pointer; + } + + .cube-direction-letter { + fill: currentColor; + fill-opacity: 0.56; + font-size: 14px; + font-weight: 600; + font-family: var(--font-script-display); + -webkit-user-select: none; + user-select: none; + transition: fill-opacity 120ms ease, font-size 120ms ease, font-weight 120ms ease; + } + + .cube-direction.is-wall-active .cube-direction-letter { + fill-opacity: 0.84; + } + + .cube-direction:hover .cube-direction-letter { + fill-opacity: 1; + } + + .cube-direction.is-active .cube-direction-letter { + fill-opacity: 1; + font-size: 15px; + font-weight: 700; + } + + .cube-direction-card { + transition: filter 120ms ease, opacity 120ms ease; + } + + .cube-direction:hover .cube-direction-card, + .cube-direction-card.is-active { + filter: drop-shadow(0 0 3px currentColor) drop-shadow(0 0 8px currentColor); + opacity: 1; + } + + .cube-direction-letter.is-missing { + fill: #ef4444; + fill-opacity: 1; + font-size: 16px; + font-weight: 800; + } + + .kab-layout { + height: 100%; + display: grid; + grid-template-rows: auto minmax(0, 1fr); + min-height: 0; + } + + .kab-tree-panel { + display: flex; + flex-direction: column; + border-bottom: 1px solid #27272a; + background: #0f0f17; + overflow: auto; + min-width: 0; + min-height: 240px; + max-height: min(44vh, 420px); + } + + .planet-layout, + .tarot-layout, + .kab-layout { + position: relative; + } + + .planet-list-panel, + .tarot-list-panel, + .kab-tree-panel { + transition: opacity 120ms ease; + } + + .sidebar-toggle-inline, + .sidebar-popout-open { + border: 1px solid #3f3f46; + border-radius: 6px; + background: #18181b; + color: #d4d4d8; + cursor: pointer; + font-size: 12px; + line-height: 1; + padding: 6px 8px; + } + + .sidebar-toggle-inline:hover, + .sidebar-popout-open:hover { + background: #27272a; + border-color: #52525b; + color: #f4f4f5; + } + + .detail-toggle-inline, + .detail-popout-open { + border: 1px solid #3f3f46; + border-radius: 6px; + background: #18181b; + color: #d4d4d8; + cursor: pointer; + font-size: 12px; + line-height: 1; + padding: 6px 8px; + } + + .detail-toggle-inline:hover, + .detail-popout-open:hover { + background: #27272a; + border-color: #52525b; + color: #f4f4f5; + } + + .sidebar-toggle-inline { + margin-left: auto; + align-self: center; + } + + .detail-toggle-inline { + margin-left: auto; + align-self: center; + } + + .sidebar-popout-open { + position: absolute; + top: 10px; + right: 10px; + z-index: 5; + } + + .detail-popout-open { + position: absolute; + top: 10px; + left: 10px; + z-index: 5; + } + + .sidebar-popout-open[hidden] { + display: none; + } + + .detail-popout-open[hidden] { + display: none; + } + + .planet-layout.layout-sidebar-collapsed { + grid-template-columns: 0 minmax(0, 1fr); + } + + .tarot-layout.layout-sidebar-collapsed { + grid-template-columns: 0 minmax(0, 1fr); + } + + .kab-layout.layout-sidebar-collapsed { + grid-template-rows: 0 minmax(0, 1fr); + } + + .planet-layout.layout-detail-collapsed { + grid-template-columns: minmax(0, 1fr) 0; + } + + .tarot-layout.layout-detail-collapsed { + grid-template-columns: minmax(0, 1fr) 0; + } + + .kab-layout.layout-detail-collapsed { + grid-template-rows: minmax(0, 1fr) 0; + } + + .layout-sidebar-collapsed > .planet-list-panel, + .layout-sidebar-collapsed > .tarot-list-panel { + width: 0; + max-width: 0; + min-width: 0; + opacity: 0; + pointer-events: none; + overflow: hidden; + border-right: none; + } + + .layout-sidebar-collapsed > .kab-tree-panel { + height: 0; + max-height: 0; + min-height: 0; + opacity: 0; + pointer-events: none; + overflow: hidden; + border-bottom: none; + border-right: none; + } + + .layout-detail-collapsed > .planet-detail-panel, + .layout-detail-collapsed > .tarot-detail-panel { + width: 0; + max-width: 0; + min-width: 0; + opacity: 0; + pointer-events: none; + overflow: hidden; + padding: 0; + border-left: none; + } + + .layout-detail-collapsed > .kab-detail-panel { + height: 0; + max-height: 0; + min-height: 0; + opacity: 0; + pointer-events: none; + overflow: hidden; + padding: 0; + border-top: none; + } + + .kab-layout.layout-detail-collapsed > .kab-tree-panel { + max-height: none; + min-height: 0; + border-bottom: none; + } + + .kab-tree-container { + flex: 1; + padding: 12px 14px 16px; + display: flex; + justify-content: center; + align-items: flex-start; + } + + .kab-tree-container > .kab-svg { + width: min(100%, 420px); + } + + .kab-tree-container > .cube-svg { + width: min(100%, 500px); + } + + .kab-path-toggle-wrap { + display: flex; + flex-wrap: wrap; + gap: 4px 10px; + padding: 7px 12px 6px; + border-bottom: 1px solid #27272a; + } + + .kab-path-toggle-control { + display: flex; + align-items: center; + gap: 8px; + padding: 0; + color: #a1a1aa; + font-size: 12px; + } + + .kab-path-toggle-control input { + margin: 0; + } + + .kab-detail-panel { + min-width: 0; + overflow: auto; + padding: 18px; + background: #18181b; + display: flex; + flex-direction: column; + gap: 16px; + } + + #kab-detail-body { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); + gap: 12px; + align-items: start; + } + + .kab-wide-card { + grid-column: 1 / -1; + } + + /* SVG tree interactivity */ + .kab-svg { display: block; } + + .kab-node { + transition: filter 0.12s; + } + .kab-node:hover { + filter: brightness(1.35); + } + .kab-node.kab-node-active { + filter: brightness(1.4); + stroke: rgba(255,255,255,0.5) !important; + stroke-width: 2 !important; + } + .kab-node-glow.kab-node-active { + opacity: 0.30 !important; + } + + .kab-path-line { + transition: stroke 0.12s, stroke-width 0.12s; + } + .kab-path-line.kab-path-active { + stroke: #9080e0 !important; + stroke-width: 2.5 !important; + } + + .kab-path-tarot.kab-path-active { + filter: drop-shadow(0 0 5px rgba(112, 96, 176, 0.78)); + } + + .kab-path-lbl.kab-path-active { + fill: #c8b8f8 !important; + } + .kab-path-hit:hover + .kab-path-lbl { + fill: #8878c8; + } + + /* Path/sephira chips in detail panel */ + .kab-chips { + display: flex; + flex-wrap: wrap; + gap: 6px; + margin-top: 4px; + } + .kab-chip { + display: inline-flex; + align-items: center; + gap: 3px; + padding: 3px 9px; + border-radius: 6px; + background: #27272a; + border: 1px solid #3f3f46; + color: #c4c4d4; + font-size: 14px; + cursor: pointer; + transition: background 0.1s; + line-height: 1; + font-family: var(--font-script-main); + } + .kab-chip:hover { background: #3f3f46; } + .kab-chip.is-active { + background: #3f3f46; + border-color: #6366f1; + color: #e0e7ff; + } + .kab-chip.is-missing { + border-color: #ef4444; + color: #ef4444; + } + .kab-chip-sub { + font-size: 10px; + color: #71717a; + } + + .cube-missing-value { + color: #ef4444; + font-weight: 700; + } + + .kab-tarot-link { + display: inline-block; + margin-top: 5px; + padding: 5px 10px; + border-radius: 6px; + border: 1px solid #4a4a6a; + background: #1e1e30; + color: #c8b8f8; + font-size: 13px; + cursor: pointer; + text-align: left; + transition: background 0.1s, border-color 0.1s; + width: 100%; + } + .kab-tarot-link:hover { + background: #2a2a44; + border-color: #7060b0; + color: #e0d0ff; + } + .kab-tarot-link::after { + content: " ↗"; + font-size: 10px; + opacity: 0.6; + } + + .kab-god-meta { + margin-top: 6px; + color: #a1a1aa; + font-size: 12px; + } + .kab-god-links { + margin-top: 7px; + display: flex; + flex-wrap: wrap; + gap: 6px; + } + .kab-god-link { + padding: 4px 9px; + border-radius: 999px; + border: 1px solid #4a4a6a; + background: #1e1e30; + color: #c8b8f8; + font-size: 12px; + cursor: pointer; + transition: background 0.1s, border-color 0.1s; + } + .kab-god-link:hover { + background: #2a2a44; + border-color: #7060b0; + color: #e0d0ff; + } + + /* ── Alphabet section ────────────────────────────────────────────────── */ + #alphabet-section { + height: calc(100vh - 61px); + background: #18181b; + box-sizing: border-box; + overflow: hidden; + display: grid; + grid-template-rows: auto minmax(0, 1fr); + } + #alphabet-section[hidden] { display: none; } + + .alpha-special-top { + padding: 10px 12px 8px; + border-bottom: 1px solid #27272a; + overflow: auto; + background: #151520; + } + + .alpha-gematria-card { + display: grid; + gap: 8px; + } + + .alpha-gematria-controls { + display: grid; + grid-template-columns: minmax(200px, 260px) minmax(0, 1fr); + gap: 8px; + align-items: start; + } + + .alpha-gematria-field { + display: grid; + gap: 4px; + min-width: 0; + } + + .alpha-gematria-field > span { + color: #a1a1aa; + font-size: 11px; + letter-spacing: 0.02em; + } + + .alpha-gematria-cipher, + .alpha-gematria-input { + width: 100%; + padding: 7px 8px; + border-radius: 6px; + border: 1px solid #3f3f46; + background: #09090b; + color: #f4f4f5; + box-sizing: border-box; + font-size: 13px; + } + + .alpha-gematria-input { + min-height: 54px; + resize: vertical; + font-family: var(--font-script-main); + } + + .alpha-gematria-result { + color: #e0d0ff; + font-weight: 700; + font-size: 16px; + line-height: 1.2; + } + + .alpha-gematria-breakdown { + color: #a1a1aa; + font-size: 12px; + line-height: 1.4; + word-break: break-word; + } + + .alpha-tabs { + display: flex; + flex-wrap: wrap; + gap: 4px; + padding: 8px 10px 0; + } + .alpha-tab-btn { + flex: 1 1 auto; + min-width: 0; + padding: 5px 4px; + border: 1px solid #3f3f46; + border-radius: 6px; + background: #18181b; + color: #a1a1aa; + font-size: 12px; + cursor: pointer; + white-space: nowrap; + } + .alpha-tab-btn.alpha-tab-active { + background: #312e81; + border-color: #6366f1; + color: #e0e7ff; + } + + .alpha-filter-search-wrap { + margin-top: 6px; + } + + .alpha-filter-type-wrap { + padding: 8px 12px; + border-bottom: 1px solid #27272a; + display: grid; + grid-template-columns: auto minmax(0, 1fr); + gap: 8px; + align-items: center; + } + + .alpha-filter-type-wrap label { + color: #a1a1aa; + font-size: 12px; + } + + .alpha-type-filter { + width: 100%; + padding: 7px 8px; + border-radius: 6px; + border: 1px solid #3f3f46; + background: #09090b; + color: #f4f4f5; + box-sizing: border-box; + font-size: 13px; + } + + .alpha-list-item { + flex-direction: row !important; + align-items: center; + gap: 8px; + } + .alpha-list-glyph { + font-size: 22px; + min-width: 34px; + text-align: center; + font-family: var(--font-script-display); + color: #e0d0ff; + line-height: 1; + } + .alpha-list-glyph--arabic { + font-family: var(--font-script-arabic); + font-size: 26px; + } + .alpha-list-glyph--enochian { + font-family: var(--font-script-enochian); + font-size: 24px; + } + .alpha-enochian-glyph-img { + display: inline-block; + object-fit: contain; + image-rendering: auto; + vertical-align: middle; + filter: + drop-shadow(1px 0 0 rgba(255, 255, 255, 0.98)) + drop-shadow(-1px 0 0 rgba(255, 255, 255, 0.98)) + drop-shadow(0 1px 0 rgba(255, 255, 255, 0.98)) + drop-shadow(0 -1px 0 rgba(255, 255, 255, 0.98)) + drop-shadow(1px 1px 0 rgba(255, 255, 255, 0.9)) + drop-shadow(-1px -1px 0 rgba(255, 255, 255, 0.9)); + } + .alpha-enochian-glyph-img--list { + width: 24px; + height: 24px; + margin: 0 auto; + } + .alpha-enochian-glyph-img--detail { + width: 72px; + height: 72px; + drop-shadow(0 0 0.7px rgba(255, 255, 255, 0.82)) + drop-shadow(0 0 1.6px rgba(255, 255, 255, 0.56)); + flex: 1; + min-width: 0; + overflow: hidden; + } + .alpha-list-meta strong { + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .alpha-list-sub { + font-size: 11px; + color: #71717a; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .alpha-detail-glyph { + font-size: 64px !important; + font-family: var(--font-script-display); + color: #e0d0ff; + line-height: 1; + letter-spacing: 0; + } + .alpha-detail-glyph--arabic { + font-family: var(--font-script-arabic); + } + .alpha-detail-glyph--enochian { + font-family: var(--font-script-enochian); + } + .alpha-dl { + display: grid; + grid-template-columns: auto 1fr; + gap: 3px 12px; + font-size: 13px; + color: #d4d4d8; + margin: 0; + } + .alpha-dl dt { color: #71717a; white-space: nowrap; } + + #tarot-browse-view { + min-height: 0; + overflow: hidden; + display: grid; + grid-template-rows: auto minmax(0, 1fr); + grid-row: 1 / -1; + } + + #tarot-browse-view[hidden] { + display: none !important; + } + + /* ── Tarot Spread View ─────────────────────────────── */ + #tarot-spread-view { + display: flex; + flex-direction: column; + gap: 1.25rem; + padding: 1rem 1.25rem; + min-height: 0; + overflow-y: auto; + grid-row: 1 / -1; + } + + #tarot-spread-view[hidden] { + display: none !important; + } + + .tarot-spread-toolbar { + display: flex; + align-items: center; + gap: 0.6rem; + flex-wrap: wrap; + padding-bottom: 0.75rem; + border-bottom: 1px solid #27272a; + } + + .tarot-spread-back-btn { + background: transparent; + border: 1px solid rgba(255,255,255,0.18); + color: #a5b4fc; + padding: 0.32rem 0.8rem; + border-radius: 6px; + cursor: pointer; + font-size: 0.82rem; + transition: background 120ms, border-color 120ms; + } + .tarot-spread-back-btn:hover { + background: #1e1b4b; + border-color: #6366f1; + } + + .tarot-spread-type-controls { + display: flex; + gap: 0.35rem; + } + + .tarot-spread-type-btn { + background: rgba(255,255,255,0.05); + border: 1px solid #3f3f46; + color: #c4b5fd; + padding: 0.32rem 0.85rem; + border-radius: 6px; + cursor: pointer; + font-size: 0.82rem; + transition: background 120ms, border-color 120ms, color 120ms; + } + .tarot-spread-type-btn:hover { + background: #27272a; + border-color: #6366f1; + color: #e0e7ff; + } + .tarot-spread-type-btn.is-active { + background: #312e81; + border-color: #6366f1; + color: #e0e7ff; + } + + .tarot-spread-redraw-btn { + background: #1e1b4b; + border: 1px solid #4f46e5; + color: #c7d2fe; + padding: 0.32rem 1rem; + border-radius: 6px; + cursor: pointer; + font-size: 0.82rem; + margin-left: auto; + transition: background 120ms, border-color 120ms; + } + .tarot-spread-redraw-btn:hover { + background: #312e81; + border-color: #6366f1; + } + + .tarot-spread-meanings { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 10px; + align-items: start; + } + + .tarot-spread-meaning-item { + border: 1px solid #3f3f46; + border-radius: 10px; + background: #111118; + padding: 10px; + display: grid; + gap: 6px; + } + + .tarot-spread-meaning-head { + font-size: 12px; + line-height: 1.3; + letter-spacing: 0.03em; + color: #a5b4fc; + font-weight: 700; + text-transform: uppercase; + } + + .tarot-spread-meaning-card { + color: #e4e4e7; + text-transform: none; + font-weight: 600; + } + + .tarot-spread-meaning-orientation { + color: #fb7185; + font-weight: 700; + text-transform: none; + } + + .tarot-spread-meaning-text { + font-size: 13px; + line-height: 1.45; + color: #e4e4e7; + } + + .tarot-spread-meaning-keywords { + font-size: 12px; + line-height: 1.35; + color: #a1a1aa; + } + + /* ── Spread Board ──────────────────────────────────── */ + .tarot-spread-board { + display: flex; + flex-wrap: wrap; + gap: 1.25rem; + justify-content: center; + padding: 0.5rem 0 1.5rem; + } + + .tarot-spread-board--three { + flex-wrap: nowrap; + justify-content: center; + gap: 2rem; + } + + .tarot-spread-board--celtic { + display: grid; + grid-template-areas: + ". crown . out" + "past present near-fut hope" + ". chall . env" + ". found . self"; + grid-template-columns: 1fr 1fr 1fr 1fr; + gap: 0.8rem 1rem; + justify-items: center; + } + + .spread-position { grid-area: unset; } + .spread-position[data-pos="crown"] { grid-area: crown; } + .spread-position[data-pos="out"] { grid-area: out; } + .spread-position[data-pos="past"] { grid-area: past; } + .spread-position[data-pos="present"] { grid-area: present; } + .spread-position[data-pos="near-fut"] { grid-area: near-fut; } + .spread-position[data-pos="hope"] { grid-area: hope; } + .spread-position[data-pos="chall"] { grid-area: chall; } + .spread-position[data-pos="env"] { grid-area: env; } + .spread-position[data-pos="found"] { grid-area: found; } + .spread-position[data-pos="self"] { grid-area: self; } + + .spread-position { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.4rem; + max-width: 130px; + } + + .spread-pos-label { + font-size: 0.68rem; + color: #a5b4fc; + text-transform: uppercase; + letter-spacing: 0.07em; + text-align: center; + line-height: 1.2; + } + + .spread-card-wrap { + border-radius: 8px; + overflow: hidden; + box-shadow: 0 4px 18px rgba(0,0,0,0.55); + border: 1px solid rgba(255,255,255,0.1); + background: #18181b; + } + + .spread-card-wrap.is-reversed .spread-card-img { + transform: rotate(180deg); + } + + .spread-card-img { + width: 90px; + height: auto; + display: block; + } + + .spread-card-name { + font-size: 0.74rem; + color: #d4d4d8; + text-align: center; + line-height: 1.3; + } + + .spread-reversed-tag { + display: block; + font-size: 0.66rem; + color: #fb7185; + margin-top: 0.1rem; + } + + .spread-empty { + color: #52525b; + padding: 2.5rem; + text-align: center; + font-size: 0.9rem; + } + .alpha-dl dd { margin: 0; } + .alpha-badge { + display: inline-block; + padding: 1px 7px; + border-radius: 9px; + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + } + .alpha-badge--mother { background: #7f1d1d; color: #fca5a5; } + .alpha-badge--double { background: #1e3a5f; color: #93c5fd; } + .alpha-badge--simple { background: #14532d; color: #86efac; } + .alpha-badge--archaic { background: #3b2f00; color: #fcd34d; } + .alpha-badge--solar { background: #4a1500; color: #fdba74; } + .alpha-badge--lunar { background: #0f1f3d; color: #93c5fd; } + /* Arabic letter forms display */ + .alpha-arabic-forms { + display: flex; + flex-wrap: wrap; + gap: 12px; + margin-top: 4px; + } + .alpha-arabic-form { + display: flex; + flex-direction: column; + align-items: center; + gap: 4px; + padding: 8px 12px; + background: #18181b; + border: 1px solid #3f3f46; + border-radius: 8px; + font-size: 11px; + color: #71717a; + min-width: 56px; + text-align: center; + } + .alpha-arabic-glyph { + font-family: var(--font-script-arabic); + font-size: 28px; + color: #e0d0ff; + line-height: 1.2; + direction: rtl; + } + .alpha-arabic-inline { + font-family: var(--font-script-arabic); + font-size: 16px; + direction: rtl; + } + .alpha-nav-btns { + display: flex; + flex-wrap: wrap; + gap: 6px; + margin-top: 8px; + } + .alpha-nav-btn { + padding: 5px 10px; + background: #1e1b4b; + border: 1px solid #4338ca; + border-radius: 6px; + color: #c7d2fe; + font-size: 12px; + cursor: pointer; + } + .alpha-nav-btn:hover { + background: #312e81; + border-color: #818cf8; + color: #e0e7ff; + } + .alpha-nav-btn.is-selected, + .alpha-nav-btn[aria-pressed="true"] { + background: #4338ca; + border-color: #a5b4fc; + color: #eef2ff; + } + .alpha-sister-wrap { + display: flex; + flex-direction: column; + gap: 6px; + } + .alpha-sister-btn { + display: flex; + align-items: center; + gap: 10px; + padding: 7px 10px; + background: #09090b; + border: 1px solid #27272a; + border-radius: 8px; + color: #d4d4d8; + font-size: 13px; + cursor: pointer; + text-align: left; + } + .alpha-sister-btn:hover { + background: #18181b; + border-color: #52525b; + } + .alpha-sister-glyph { + font-size: 28px; + min-width: 36px; + text-align: center; + font-family: "Segoe UI", "Noto Sans Hebrew", "Noto Serif", serif; + color: #e0d0ff; + line-height: 1; + } + .alpha-sister-glyph.alpha-list-glyph--arabic { + font-family: var(--font-script-arabic); + } + .alpha-sister-glyph.alpha-list-glyph--enochian { + font-family: var(--font-script-enochian); + } + .alpha-enochian-glyph-img--sister { + width: 28px; + height: 28px; + min-width: 36px; + flex: 0 0 36px; + margin: 0 4px; + } + .alpha-sister-name { + font-size: 12px; + color: #a1a1aa; + } + + /* ── Zodiac section ──────────────────────────────────────────────────── */ + #zodiac-section { + height: calc(100vh - 61px); + background: #18181b; + box-sizing: border-box; + overflow: hidden; + } + #zodiac-section[hidden] { display: none; } + + /* ── Numbers section ────────────────────────────────────────────────── */ + #numbers-section { + height: calc(100vh - 61px); + background: #18181b; + box-sizing: border-box; + overflow: hidden; + } + #numbers-section[hidden] { display: none; } + + .numbers-detail-body { + display: grid; + gap: 12px; + min-width: 0; + } + + .numbers-layout { + height: 100%; + display: grid; + grid-template-rows: auto minmax(0, 1fr); + min-height: 0; + } + + .numbers-special-panel { + border-bottom: 1px solid #27272a; + background: #0f0f17; + overflow: auto; + min-width: 0; + min-height: 220px; + max-height: min(44vh, 420px); + padding: 12px; + box-sizing: border-box; + } + + .numbers-main-layout { + min-height: 0; + } + + .numbers-main-layout .planet-detail-panel { + min-height: 0; + overflow: auto; + padding: 0 14px 14px; + box-sizing: border-box; + } + + .numbers-detail-card { + border: 1px solid #3f3f46; + border-radius: 10px; + background: #111118; + padding: 12px; + color: #d4d4d8; + display: grid; + gap: 8px; + } + + .numbers-detail-card > strong { + display: block; + margin-bottom: 2px; + color: #a1a1aa; + font-size: 12px; + text-transform: uppercase; + letter-spacing: 0.04em; + } + + .numbers-detail-text { + color: #d4d4d8; + font-size: 13px; + line-height: 1.4; + } + + .numbers-detail-text--muted { + color: #a1a1aa; + } + + .numbers-links-wrap { + display: flex; + flex-wrap: wrap; + gap: 6px; + } + + .numbers-nav-btn { + padding: 5px 10px; + background: #27272a; + border: 1px solid #3f3f46; + border-radius: 6px; + color: #c4b5fd; + font-size: 12px; + cursor: pointer; + transition: background 100ms; + text-align: left; + } + + .numbers-nav-btn:hover { + background: #3f3f46; + border-color: #7c3aed; + } + + .numbers-special-card-section { + padding: 14px; + border: 1px solid rgba(220, 182, 93, 0.2); + background: linear-gradient(180deg, rgba(27, 23, 48, 0.5), rgba(12, 10, 25, 0.75)); + border-radius: 14px; + display: grid; + gap: 8px; + } + + .numbers-special-board { + margin-top: 2px; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); + gap: 12px; + padding: 10px; + border-radius: 12px; + border: 1px solid rgba(220, 182, 93, 0.2); + background: radial-gradient(circle at top, rgba(70, 58, 107, 0.25), rgba(12, 10, 24, 0.78)); + } + + .numbers-special-card { + position: relative; + height: 164px; + border: 0; + padding: 0; + background: transparent; + cursor: pointer; + border-radius: 12px; + perspective: 900px; + } + + .numbers-special-card:hover .numbers-special-card-inner { + transform: translateY(-2px); + } + + .numbers-special-card.is-flipped .numbers-special-card-inner { + transform: rotateY(180deg); + } + + .numbers-special-card[data-suit="hearts"] .numbers-special-card-suit, + .numbers-special-card[data-suit="diamonds"] .numbers-special-card-suit { + color: #f28f8f; + } + + .numbers-special-card-inner { + position: relative; + width: 100%; + height: 100%; + border-radius: 12px; + transform-style: preserve-3d; + transition: transform .24s ease; + } + + .numbers-special-card-face { + position: absolute; + inset: 0; + border-radius: 12px; + border: 1px solid rgba(220, 182, 93, 0.35); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 5px; + color: #efe7d5; + box-shadow: 0 14px 24px rgba(0, 0, 0, 0.35); + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + } + + .numbers-special-card-face--front { + background: linear-gradient(180deg, rgba(32, 27, 58, 0.95), rgba(14, 11, 31, 0.98)); + } + + .numbers-special-card-face--back { + background: linear-gradient(180deg, rgba(48, 24, 24, 0.95), rgba(18, 11, 11, 0.98)); + border-color: rgba(255, 143, 122, 0.45); + transform: rotateY(180deg); + } + + .numbers-special-card-tag { + font-size: 0.7rem; + text-transform: uppercase; + letter-spacing: 0.08em; + color: #cbb9b4; + } + + .numbers-special-card-rank { + font-size: 1rem; + font-weight: 700; + letter-spacing: 0.02em; + text-align: center; + } + + .numbers-special-card-suit { + font-size: 1.5rem; + line-height: 1; + } + + .numbers-special-card-meta { + font-size: 0.72rem; + color: #bdb5cd; + text-align: center; + padding: 0 10px; + } + + /* ── Gods section ────────────────────────────────────────────────────── */ + #gods-section { + height: calc(100vh - 61px); + background: #18181b; + box-sizing: border-box; + overflow: hidden; + } + #gods-section[hidden] { display: none; } + + /* ── Enochian section ────────────────────────────────────────────────── */ + #enochian-section { + height: calc(100vh - 61px); + background: #18181b; + box-sizing: border-box; + overflow: hidden; + } + #enochian-section[hidden] { display: none; } + + /* ── Elements section ───────────────────────────────────────────────── */ + #elements-section { + height: calc(100vh - 61px); + background: #18181b; + box-sizing: border-box; + overflow: hidden; + } + #elements-section[hidden] { display: none; } + + .enoch-list-item { + width: 100%; + text-align: left; + display: grid; + gap: 3px; + padding: 9px 10px; + border-radius: 8px; + border: 1px solid #3f3f46; + background: #18181b; + color: #f4f4f5; + cursor: pointer; + } + .enoch-list-item:hover, + .enoch-list-item.is-selected { + background: #27272a; + border-color: #7060b0; + } + .enoch-list-name { + font-size: 13px; + font-weight: 600; + } + .enoch-list-meta { + font-size: 12px; + color: #a1a1aa; + } + + .enoch-grid { + display: grid; + gap: 4px; + overflow: auto; + max-width: 100%; + padding: 4px 2px 2px; + } + .enoch-grid-row { + display: flex; + gap: 4px; + width: max-content; + } + .enoch-grid-cell { + width: 26px; + height: 26px; + border-radius: 6px; + border: 1px solid #3f3f46; + background: #111118; + color: #f4f4f5; + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + font-size: 13px; + line-height: 1; + cursor: pointer; + } + .enoch-grid-cell:hover { + border-color: #7060b0; + background: #27272a; + } + .enoch-grid-cell.is-selected { + border-color: #7c3aed; + box-shadow: 0 0 0 1px #7c3aed; + background: #2a1f3d; + } + + .enoch-letter-meta { + display: grid; + gap: 6px; + font-size: 13px; + color: #d4d4d8; + } + .enoch-letter-row { + display: flex; + flex-wrap: wrap; + gap: 8px; + align-items: center; + } + .enoch-letter-chip { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 24px; + height: 24px; + border: 1px solid #52525b; + border-radius: 999px; + background: #111118; + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + font-size: 12px; + color: #f4f4f5; + padding: 0 8px; + box-sizing: border-box; + } + + .enoch-nav-btn { + padding: 5px 8px; + border: 1px solid #3f3f46; + border-radius: 7px; + background: #27272a; + color: #d4d4d8; + cursor: pointer; + font-size: 12px; + } + .enoch-nav-btn:hover { + background: #3f3f46; + border-color: #7060b0; + color: #f4f4f5; + } + + .gods-tabs { + display: flex; + flex-wrap: wrap; + gap: 4px; + padding: 8px 12px; + border-bottom: 1px solid #27272a; + } + .gods-tab-btn { + padding: 4px 9px; + border-radius: 6px; + border: 1px solid #3f3f46; + background: #27272a; + color: #a1a1aa; + font-size: 11px; + cursor: pointer; + white-space: nowrap; + transition: background 120ms, color 120ms; + } + .gods-tab-btn:hover { background: #3f3f46; color: #f4f4f5; } + .gods-tab-active { background: #4c1d95; border-color: #7c3aed; color: #e9d5ff !important; } + + .gods-list-item { + padding: 9px 12px; + border-bottom: 1px solid #27272a; + cursor: pointer; + transition: background 100ms; + } + .gods-list-item:hover { background: #27272a; } + .gods-list-active { background: #1e1b4b !important; border-left: 3px solid #6d28d9; } + .gods-list-main { + display: flex; + align-items: center; + justify-content: space-between; + gap: 6px; + } + .gods-list-label { + font-size: 13px; + font-weight: 600; + color: #f4f4f5; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + .gods-list-tag { + font-size: 10px; + padding: 2px 6px; + border-radius: 9px; + background: #27272a; + color: #71717a; + white-space: nowrap; + } + .gods-list-sub { + font-size: 12px; + color: #a1a1aa; + margin-top: 2px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .gods-detail-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); + gap: 12px; + padding: 14px; + } + .gods-card { + background: #1c1c24; + border: 1px solid #3f3f46; + border-radius: 10px; + padding: 12px 14px; + } + .gods-card--greek { border-color: #7c3aed; } + .gods-card--roman { border-color: #9a3412; } + .gods-card--egyptian { border-color: #92400e; } + .gods-card--elohim { border-color: #1d4ed8; } + .gods-card--angel { border-color: #065f46; } + .gods-card--wide { grid-column: 1 / -1; } + .gods-card-title { + font-size: 11px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.05em; + color: #71717a; + margin-bottom: 8px; + } + .gods-card-body { + font-size: 15px; + color: #f4f4f5; + line-height: 1.4; + } + .gods-equivalent-row { + display: flex; + flex-wrap: wrap; + gap: 6px; + } + .gods-equivalent-link, + .gods-equivalent-text { + display: inline-flex; + align-items: center; + gap: 4px; + font-size: 12px; + padding: 4px 8px; + border-radius: 999px; + border: 1px solid #3f3f46; + background: #27272a; + } + .gods-equivalent-link { + color: #c4b5fd; + cursor: pointer; + transition: background 100ms; + } + .gods-equivalent-link:hover { + background: #3f3f46; + border-color: #7c3aed; + } + .gods-equivalent-text { + color: #a1a1aa; + } + .gods-card-row { + font-size: 13px; + color: #d4d4d8; + margin-bottom: 4px; + } + .gods-field-label { + font-size: 11px; + color: #71717a; + text-transform: uppercase; + letter-spacing: 0.04em; + margin-right: 6px; + } + .gods-hebrew { + font-family: 'Segoe UI', 'Noto Sans Hebrew', serif; + font-size: 22px; + color: #e0d0ff; + margin-right: 8px; + vertical-align: middle; + } + .gods-transliteration { + font-size: 15px; + color: #f4f4f5; + font-weight: 600; + vertical-align: middle; + } + .gods-nav-row { + margin-top: 10px; + display: flex; + gap: 6px; + flex-wrap: wrap; + } + .gods-nav-btn { + padding: 5px 10px; + background: #27272a; + border: 1px solid #3f3f46; + border-radius: 6px; + color: #c4b5fd; + font-size: 12px; + cursor: pointer; + transition: background 100ms; + } + .gods-nav-btn:hover { background: #3f3f46; border-color: #7c3aed; } + + .gods-domain-row { + display: flex; + flex-wrap: wrap; + gap: 5px; + margin-top: 8px; + } + .gods-domain-tag { + font-size: 11px; + padding: 2px 8px; + border-radius: 12px; + background: #27272a; + border: 1px solid #3f3f46; + color: #a1a1aa; + } + .gods-card--kab { + border-color: #064e3b; + background: #022c22; + } + + .zod-list-row { + display: flex; + align-items: center; + gap: 6px; + } + .zod-list-symbol { + font-size: 22px; + min-width: 28px; + text-align: center; + line-height: 1; + } + .zod-list-elem { + margin-left: auto; + font-size: 14px; + } + .zod-badge { + display: inline-block; + padding: 2px 8px; + border-radius: 9px; + font-size: 11px; + font-weight: 600; + } + .zod-badge--fire { background: #7f1d1d; color: #fca5a5; } + .zod-badge--earth { background: #14532d; color: #86efac; } + .zod-badge--air { background: #1e3a5f; color: #93c5fd; } + .zod-badge--water { background: #1a1a5e; color: #a5b4fc; } + .zod-badge--quad { background: #27272a; color: #d4d4d8; } + .zod-hebrew-glyph { + font-size: 48px; + font-family: "Segoe UI", "Noto Sans Hebrew", "Noto Serif", serif; + color: #e0d0ff; + line-height: 1; + } + .zod-detail-name { + font-size: 52px; + font-family: "Segoe UI", "Noto Serif", serif; + } + .zod-decan-row { + display: flex; + align-items: center; + gap: 8px; + padding: 5px 0; + border-bottom: 1px solid #27272a; + font-size: 13px; + } + .zod-decan-row:last-child { border-bottom: none; } + .zod-decan-ord { font-weight: 600; min-width: 30px; color: #a1a1aa; } + .zod-decan-planet { min-width: 80px; color: #d4d4d8; } + .zod-decan-card-btn { + margin-left: auto; + padding: 3px 8px; + background: #1e1b4b; + border: 1px solid #4338ca; + border-radius: 6px; + color: #c7d2fe; + font-size: 11px; + cursor: pointer; + white-space: nowrap; + } + .zod-decan-card-btn:hover { background: #312e81; border-color: #818cf8; color: #e0e7ff; } + .zod-tetra { + font-family: "Segoe UI", "Noto Sans Hebrew", serif; + font-size: 15px; + letter-spacing: 2px; + color: #e0d0ff; + } + + .settings-popup { + position: fixed; + top: 62px; + right: 16px; + z-index: 30; + } + .settings-popup[hidden] { + display: none; + } + .settings-popup-card { + width: min(420px, calc(100vw - 32px)); + background: #18181b; + border: 1px solid #3f3f46; + border-radius: 12px; + padding: 14px; + box-shadow: 0 14px 34px rgba(0, 0, 0, 0.45); + box-sizing: border-box; + } + .settings-popup-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 10px; + } + .settings-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; + } + .settings-field { + display: flex; + flex-direction: column; + gap: 6px; + font-size: 13px; + color: #d4d4d8; + } + .settings-field-full { + grid-column: 1 / -1; + } + .settings-field input, + .settings-field select { + width: 100%; + padding: 7px 8px; + border-radius: 6px; + border: 1px solid #3f3f46; + background: #09090b; + color: #f4f4f5; + box-sizing: border-box; + } + .settings-actions { + margin-top: 12px; + display: flex; + gap: 10px; + justify-content: flex-end; + } + .settings-actions button, + .settings-popup-header button { + padding: 7px 12px; + border-radius: 6px; + border: 1px solid #3f3f46; + background: #27272a; + color: #f4f4f5; + cursor: pointer; + } + .settings-actions button:hover, + .settings-popup-header button:hover { + background: #3f3f46; + } + #month-strip { + height: 28px; + background: #18181b; + border-bottom: 1px solid #27272a; + box-sizing: border-box; + overflow: hidden; + padding-left: 0; + padding-right: 0; + } + #month-strip .month-strip-track { + height: 100%; + display: grid; + grid-template-columns: repeat(7, minmax(0, 1fr)); + } + #month-strip .month-strip-segment { + display: flex; + align-items: center; + justify-content: center; + font-size: 13px; + font-weight: 600; + color: #e5e7eb; + letter-spacing: 0.01em; + border-left: 1px solid rgba(255, 255, 255, 0.08); + box-sizing: border-box; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + text-transform: uppercase; + } + #month-strip .month-strip-segment:first-child { + border-left: 0; + } + #now-panel { + position: relative; + overflow: hidden; + padding: 16px 24px; + background: #1e1e24; + border-bottom: 1px solid #27272a; + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 0; + align-items: start; + } + + #now-panel[hidden] { + display: none !important; + } + + #now-sky-layer { + position: absolute; + width: calc(100% + 1360px); + height: calc(100% + 380px); + top: -200px; + left: -830px; + z-index: 0; + pointer-events: none; + opacity: 1; + border: none; + filter: none; + background-color: #000; + } + #now-panel::after { + content: ""; + position: absolute; + inset: 0; + z-index: 1; + background: transparent; + pointer-events: none; + } + .now-section { + position: relative; + z-index: 2; + min-width: 0; + box-sizing: border-box; + padding: 10px 12px 12px; + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + border-radius: 14px; + overflow: hidden; + isolation: isolate; + border: 0; + box-shadow: 0 8px 18px rgba(0, 0, 0, 0.24); + transition: color 180ms ease, box-shadow 180ms ease; + } + .now-section::before { + content: ""; + position: absolute; + width: clamp(210px, 56%, 340px); + height: 35%; + left: 50%; + bottom: 6px; + transform: translateX(-50%); + z-index: 0; + pointer-events: none; + opacity: 0.74; + border-radius: 999px; + background: + radial-gradient(56% 58% at 24% 44%, rgba(26, 32, 47, 0.64) 0%, rgba(26, 32, 47, 0.24) 62%, transparent 100%), + radial-gradient(52% 56% at 78% 40%, rgba(2, 6, 23, 0.58) 0%, rgba(2, 6, 23, 0.22) 62%, transparent 100%), + radial-gradient(64% 70% at 50% 72%, rgba(8, 12, 28, 0.54) 0%, rgba(8, 12, 28, 0.2) 64%, transparent 100%); + filter: saturate(0.92); + } + .now-section > * { + position: relative; + z-index: 1; + } + #now-panel.is-day .now-section { + color: #111827; + box-shadow: 0 7px 16px rgba(15, 23, 42, 0.14); + } + #now-panel.is-day .now-section::before { + background: + radial-gradient(56% 58% at 24% 44%, rgba(255, 255, 255, 0.86) 0%, rgba(255, 255, 255, 0.34) 62%, transparent 100%), + radial-gradient(52% 56% at 78% 40%, rgba(255, 255, 255, 0.82) 0%, rgba(255, 255, 255, 0.3) 62%, transparent 100%), + radial-gradient(64% 70% at 50% 72%, rgba(241, 245, 249, 0.74) 0%, rgba(226, 232, 240, 0.28) 64%, transparent 100%); + filter: saturate(1.02); + } + #now-panel.is-night .now-section { + color: #f8fafc; + box-shadow: 0 9px 20px rgba(0, 0, 0, 0.34); + } + #now-panel.is-day .now-title, + #now-panel.is-day .now-tarot, + #now-panel.is-day .now-countdown-row, + #now-panel.is-day .now-countdown-next, + #now-panel.is-day .now-countdown-value, + #now-panel.is-day .now-primary { + color: #111827; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.3); + } + #now-panel.is-day .now-primary-hour { + color: #581c87; + } + #now-panel.is-day .now-primary-moon { + color: #78350f; + } + #now-panel.is-day .now-primary-decan { + color: #9a3412; + } + #now-panel.is-night .now-title, + #now-panel.is-night .now-tarot, + #now-panel.is-night .now-countdown-row, + #now-panel.is-night .now-countdown-next, + #now-panel.is-night .now-countdown-value, + #now-panel.is-night .now-primary, + #now-panel.is-night .now-primary-hour, + #now-panel.is-night .now-primary-moon, + #now-panel.is-night .now-primary-decan { + color: #f8fafc; + text-shadow: 0 1px 3px rgba(2, 6, 23, 0.75); + } + .now-card { + margin: 6px 0 10px 0; + width: 150px; + height: 225px; + object-fit: cover; + border-radius: 6px; + border: 1px solid #3f3f46; + display: none; + } + #now-panel.is-day .now-card { + border-color: rgba(17, 24, 39, 0.32); + box-shadow: 0 8px 18px rgba(15, 23, 42, 0.22); + } + #now-panel.is-night .now-card { + border-color: rgba(226, 232, 240, 0.36); + box-shadow: 0 10px 22px rgba(2, 6, 23, 0.58); + } + .now-countdown-row { + margin-top: 4px; + width: min(100%, 340px); + display: grid; + grid-template-columns: 130px 1fr; + column-gap: 10px; + align-items: center; + color: #a1a1aa; + font-size: 12px; + text-align: left; + } + .now-countdown-value { + text-align: right; + white-space: nowrap; + font-variant-numeric: tabular-nums; + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + } + .now-countdown-next { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + .now-title { + font-size: 11px; + color: #a1a1aa; + text-transform: uppercase; + letter-spacing: 0.05em; + margin-bottom: 4px; + } + .now-stats-section { + position: relative; + z-index: 2; + grid-column: 1 / -1; + margin-top: 10px; + border-radius: 14px; + padding: 12px 14px; + background: rgba(15, 23, 42, 0.42); + border: 1px solid rgba(148, 163, 184, 0.35); + box-shadow: 0 8px 18px rgba(0, 0, 0, 0.24); + display: grid; + gap: 8px; + } + .now-stats-title { + font-size: 11px; + color: #cbd5e1; + text-transform: uppercase; + letter-spacing: 0.06em; + font-weight: 700; + } + .now-stats-sabian { + font-size: 14px; + font-weight: 600; + line-height: 1.4; + color: #f8fafc; + white-space: pre-line; + } + .now-stats-planets { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 6px 10px; + font-size: 13px; + line-height: 1.35; + color: #e2e8f0; + } + .now-stats-planet { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-variant-numeric: tabular-nums; + } + #now-panel.is-day .now-stats-section { + background: rgba(255, 255, 255, 0.58); + border-color: rgba(15, 23, 42, 0.2); + box-shadow: 0 7px 16px rgba(15, 23, 42, 0.12); + } + #now-panel.is-day .now-stats-title { + color: #475569; + text-shadow: none; + } + #now-panel.is-day .now-stats-sabian, + #now-panel.is-day .now-stats-planets { + color: #0f172a; + text-shadow: none; + } + #now-panel.is-night .now-stats-title, + #now-panel.is-night .now-stats-sabian, + #now-panel.is-night .now-stats-planets { + text-shadow: 0 1px 3px rgba(2, 6, 23, 0.75); + } + .now-primary { + font-size: 20px; + font-weight: 600; + } + .now-primary-hour { + color: #a855f7; + } + .now-primary-moon { + color: #fbbf24; + } + .now-primary-decan { + color: #f97316; + } + .now-tarot { + font-size: 13px; + color: #d4d4d8; + margin-top: 2px; + } + + .toastui-calendar-timegrid-time-column .toastui-calendar-timegrid-current-time { + font-size: 12px; + font-weight: 700; + color: #ffffff !important; + width: 52px; + height: 52px; + line-height: 1; + text-align: center; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + border: none; + padding: 0; + right: 0; + background: transparent; + box-shadow: none; + text-shadow: none; + z-index: 5; + } + + .toastui-calendar-timegrid-time-column .toastui-calendar-timegrid-current-time .now-celestial-chip { + width: auto; + height: auto; + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + gap: 0; + line-height: 1; + border: none; + background: transparent; + box-shadow: none; + text-shadow: none; + } + + .toastui-calendar-timegrid-time-column .toastui-calendar-timegrid-current-time .now-celestial-icon { + font-size: 34px; + line-height: 1; + } + + .toastui-calendar-timegrid-time-column .toastui-calendar-timegrid-current-time .now-celestial-value { + font-size: 10px; + font-weight: 800; + line-height: 1; + letter-spacing: 0.01em; + } + + .toastui-calendar-timegrid-time-column .toastui-calendar-timegrid-current-time.is-sun .now-celestial-chip { + color: #facc15; + filter: drop-shadow(0 0 var(--sun-glow-size, 10px) rgba(250, 204, 21, var(--sun-glow-alpha, 0.75))); + } + + .toastui-calendar-timegrid-time-column .toastui-calendar-timegrid-current-time.is-moon .now-celestial-chip { + color: #e2e8f0; + filter: drop-shadow(0 0 8px rgba(148, 163, 184, var(--moon-glow-alpha, 0.5))); + } + + .toastui-calendar-timegrid-time-column { + transition: background-image 400ms ease; + } + + .toastui-calendar-timegrid-time-column .toastui-calendar-timegrid-time .toastui-calendar-timegrid-time-label, + .toastui-calendar-timegrid-time-column .toastui-calendar-timegrid-time span { + color: #ffffff !important; + text-shadow: 0 0 2px rgba(0, 0, 0, 0.95), 0 0 6px rgba(0, 0, 0, 0.8); + } + + :root { + --week-col-1: #727781; + --week-col-2: #7f8490; + --week-col-3: #8d93a0; + --week-col-4: #9aa1ad; + --week-col-5: #aab1bd; + --week-col-6: #c0c7d2; + --week-col-7: #d7dee8; + } + + .toastui-calendar-week-view-day-names .toastui-calendar-day-name-item.toastui-calendar-week:nth-child(1), + .toastui-calendar-week-view .toastui-calendar-weekday-grid .toastui-calendar-daygrid-cell:nth-child(1), + .toastui-calendar-panel.toastui-calendar-allday .toastui-calendar-daygrid-cell:nth-child(1), + .toastui-calendar-week-view .toastui-calendar-timegrid .toastui-calendar-columns .toastui-calendar-column:nth-child(1) { + background-color: var(--week-col-1); + } + + .toastui-calendar-week-view-day-names .toastui-calendar-day-name-item.toastui-calendar-week:nth-child(2), + .toastui-calendar-week-view .toastui-calendar-weekday-grid .toastui-calendar-daygrid-cell:nth-child(2), + .toastui-calendar-panel.toastui-calendar-allday .toastui-calendar-daygrid-cell:nth-child(2), + .toastui-calendar-week-view .toastui-calendar-timegrid .toastui-calendar-columns .toastui-calendar-column:nth-child(2) { + background-color: var(--week-col-2); + } + + .toastui-calendar-week-view-day-names .toastui-calendar-day-name-item.toastui-calendar-week:nth-child(3), + .toastui-calendar-week-view .toastui-calendar-weekday-grid .toastui-calendar-daygrid-cell:nth-child(3), + .toastui-calendar-panel.toastui-calendar-allday .toastui-calendar-daygrid-cell:nth-child(3), + .toastui-calendar-week-view .toastui-calendar-timegrid .toastui-calendar-columns .toastui-calendar-column:nth-child(3) { + background-color: var(--week-col-3); + } + + .toastui-calendar-week-view-day-names .toastui-calendar-day-name-item.toastui-calendar-week:nth-child(4), + .toastui-calendar-week-view .toastui-calendar-weekday-grid .toastui-calendar-daygrid-cell:nth-child(4), + .toastui-calendar-panel.toastui-calendar-allday .toastui-calendar-daygrid-cell:nth-child(4), + .toastui-calendar-week-view .toastui-calendar-timegrid .toastui-calendar-columns .toastui-calendar-column:nth-child(4) { + background-color: var(--week-col-4); + } + + .toastui-calendar-week-view-day-names .toastui-calendar-day-name-item.toastui-calendar-week:nth-child(5), + .toastui-calendar-week-view .toastui-calendar-weekday-grid .toastui-calendar-daygrid-cell:nth-child(5), + .toastui-calendar-panel.toastui-calendar-allday .toastui-calendar-daygrid-cell:nth-child(5), + .toastui-calendar-week-view .toastui-calendar-timegrid .toastui-calendar-columns .toastui-calendar-column:nth-child(5) { + background-color: var(--week-col-5); + } + + .toastui-calendar-week-view-day-names .toastui-calendar-day-name-item.toastui-calendar-week:nth-child(6), + .toastui-calendar-week-view .toastui-calendar-weekday-grid .toastui-calendar-daygrid-cell:nth-child(6), + .toastui-calendar-panel.toastui-calendar-allday .toastui-calendar-daygrid-cell:nth-child(6), + .toastui-calendar-week-view .toastui-calendar-timegrid .toastui-calendar-columns .toastui-calendar-column:nth-child(6) { + background-color: var(--week-col-6); + } + + .toastui-calendar-week-view-day-names .toastui-calendar-day-name-item.toastui-calendar-week:nth-child(7), + .toastui-calendar-week-view .toastui-calendar-weekday-grid .toastui-calendar-daygrid-cell:nth-child(7), + .toastui-calendar-panel.toastui-calendar-allday .toastui-calendar-daygrid-cell:nth-child(7), + .toastui-calendar-week-view .toastui-calendar-timegrid .toastui-calendar-columns .toastui-calendar-column:nth-child(7) { + background-color: var(--week-col-7); + } + + .toastui-calendar-panel.toastui-calendar-allday, + .toastui-calendar-panel.toastui-calendar-allday .toastui-calendar-panel-grid-wrapper, + .toastui-calendar-panel.toastui-calendar-allday .toastui-calendar-allday-panel, + .toastui-calendar-panel.toastui-calendar-allday .toastui-calendar-panel-grid, + .toastui-calendar-panel.toastui-calendar-allday .toastui-calendar-panel-event-wrapper { + background: transparent !important; + } + + .toastui-calendar-week-view .toastui-calendar-panel.toastui-calendar-allday, + .toastui-calendar-panel.toastui-calendar-allday .toastui-calendar-panel-event-wrapper { + overflow-y: hidden !important; + } + + .toastui-calendar-panel.toastui-calendar-allday .toastui-calendar-panel-grid { + background: transparent !important; + background-image: none !important; + } + + .toastui-calendar-panel.toastui-calendar-allday .toastui-calendar-panel-grid:nth-child(1) { background-color: var(--week-col-1) !important; background-image: none !important; border-right: 0 !important; } + .toastui-calendar-panel.toastui-calendar-allday .toastui-calendar-panel-grid:nth-child(2) { background-color: var(--week-col-2) !important; background-image: none !important; border-right: 0 !important; } + .toastui-calendar-panel.toastui-calendar-allday .toastui-calendar-panel-grid:nth-child(3) { background-color: var(--week-col-3) !important; background-image: none !important; border-right: 0 !important; } + .toastui-calendar-panel.toastui-calendar-allday .toastui-calendar-panel-grid:nth-child(4) { background-color: var(--week-col-4) !important; background-image: none !important; border-right: 0 !important; } + .toastui-calendar-panel.toastui-calendar-allday .toastui-calendar-panel-grid:nth-child(5) { background-color: var(--week-col-5) !important; background-image: none !important; border-right: 0 !important; } + .toastui-calendar-panel.toastui-calendar-allday .toastui-calendar-panel-grid:nth-child(6) { background-color: var(--week-col-6) !important; background-image: none !important; border-right: 0 !important; } + .toastui-calendar-panel.toastui-calendar-allday .toastui-calendar-panel-grid:nth-child(7) { background-color: var(--week-col-7) !important; background-image: none !important; border-right: 0 !important; } + + .toastui-calendar-week-view-day-names .toastui-calendar-day-name-item.toastui-calendar-week:nth-child(-n+4) .toastui-calendar-day-name__date, + .toastui-calendar-week-view-day-names .toastui-calendar-day-name-item.toastui-calendar-week:nth-child(-n+4) .toastui-calendar-day-name__name { + color: #e5e7eb; + } + + .toastui-calendar-panel.toastui-calendar-week-view-day-names { + height: 52px !important; + overflow-y: hidden !important; + overflow-x: hidden !important; + } + + .toastui-calendar-day-names.toastui-calendar-week { + height: 52px; + } + + .toastui-calendar-day-name-item.toastui-calendar-week { + height: 52px; + line-height: 1; + padding: 0 6px; + } + + .weekday-header-template { + height: 100%; + width: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 0; + text-align: center; + } + + .weekday-header-number { + font-size: 16px; + font-weight: 700; + line-height: 1; + } + + .weekday-header-name { + font-size: 10px; + line-height: 1; + } + + .weekday-header-ruler { + font-size: 16px; + line-height: 1; + font-weight: 700; + } + + .toastui-calendar-week-view-day-names .toastui-calendar-day-name-item.toastui-calendar-week:nth-child(-n+5) .weekday-header-number, + .toastui-calendar-week-view-day-names .toastui-calendar-day-name-item.toastui-calendar-week:nth-child(-n+5) .weekday-header-name, + .toastui-calendar-week-view-day-names .toastui-calendar-day-name-item.toastui-calendar-week:nth-child(-n+5) .weekday-header-ruler { + color: #e5e7eb; + } + + .toastui-calendar-week-view-day-names .toastui-calendar-day-name-item.toastui-calendar-week:nth-child(n+6) .weekday-header-number, + .toastui-calendar-week-view-day-names .toastui-calendar-day-name-item.toastui-calendar-week:nth-child(n+6) .weekday-header-name, + .toastui-calendar-week-view-day-names .toastui-calendar-day-name-item.toastui-calendar-week:nth-child(n+6) .weekday-header-ruler { + color: #1f2937; + } + + .toastui-calendar-timegrid .toastui-calendar-timegrid-now-indicator .toastui-calendar-timegrid-now-indicator-today, + .toastui-calendar-timegrid .toastui-calendar-timegrid-now-indicator .toastui-calendar-timegrid-now-indicator-right { + height: 4px; + margin-top: -2px; + border-top: none !important; + background-image: linear-gradient( + 90deg, + #ef4444 0%, + #f97316 16.6%, + #facc15 33.2%, + #22c55e 49.8%, + #3b82f6 66.4%, + #a855f7 83%, + #ec4899 100% + ); + background-size: 320px 100%; + -webkit-mask-image: repeating-linear-gradient(90deg, #000 0 10px, transparent 10px 16px); + mask-image: repeating-linear-gradient(90deg, #000 0 10px, transparent 10px 16px); + opacity: 0.95; + box-shadow: 0 0 8px rgba(168, 85, 247, 0.35); + } + + .toastui-calendar-timegrid .toastui-calendar-timegrid-now-indicator .toastui-calendar-timegrid-now-indicator-left { + height: 4px; + margin-top: -2px; + border-top: none !important; + background: #ffffff; + -webkit-mask-image: repeating-linear-gradient(90deg, #000 0 10px, transparent 10px 16px); + mask-image: repeating-linear-gradient(90deg, #000 0 10px, transparent 10px 16px); + opacity: 0.98; + box-shadow: 0 0 6px rgba(255, 255, 255, 0.5); + } + + .toastui-calendar-timegrid .toastui-calendar-timegrid-now-indicator .toastui-calendar-timegrid-now-indicator-marker { + position: absolute; + width: 44px !important; + height: 44px !important; + margin: -22px 0 0 -22px !important; + border-radius: 50% !important; + background: radial-gradient(circle at 30% 30%, #fff7c2 0%, #facc15 55%, #f59e0b 100%) !important; + box-shadow: 0 0 0 2px rgba(245, 158, 11, 0.35), 0 0 var(--sun-marker-glow-size, 16px) rgba(250, 204, 21, 0.7); + z-index: 6; + overflow: visible; + } + + .toastui-calendar-timegrid .toastui-calendar-timegrid-now-indicator .toastui-calendar-timegrid-now-indicator-marker.is-moon { + background: radial-gradient(circle at 35% 30%, #64748b 0%, #1e293b 62%, #020617 100%) !important; + box-shadow: 0 0 0 2px rgba(148, 163, 184, 0.35), 0 0 12px rgba(148, 163, 184, var(--moon-glow-alpha, 0.45)); + } + + .toastui-calendar-timegrid .toastui-calendar-timegrid-now-indicator .toastui-calendar-timegrid-now-indicator-marker::before { + content: ""; + position: absolute; + inset: -10px; + border-radius: 50%; + background: repeating-conic-gradient( + from 0deg, + rgba(251, 191, 36, 0.9) 0deg 10deg, + transparent 10deg 30deg + ); + -webkit-mask-image: radial-gradient(circle, transparent 0 47%, #000 47% 62%, transparent 62% 100%); + mask-image: radial-gradient(circle, transparent 0 47%, #000 47% 62%, transparent 62% 100%); + pointer-events: none; + opacity: var(--sun-marker-ray-opacity, 0.9); + } + + .toastui-calendar-timegrid .toastui-calendar-timegrid-now-indicator .toastui-calendar-timegrid-now-indicator-marker.is-moon::before { + opacity: 0; + } + + .toastui-calendar-timegrid { + height: 240%; + min-height: 1200px; + } + + .toastui-calendar-event-time { + cursor: default !important; + min-height: 42px; + border-radius: 0 !important; + border-width: 0 !important; + } + + .toastui-calendar-event-time .toastui-calendar-event-time-content { + white-space: pre-line; + line-height: 1.2; + font-size: 11px; + padding: 0 !important; + margin: 0; + height: 100%; + width: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + text-align: center; + } + + .toastui-calendar-panel-event-wrapper .toastui-calendar-weekday-event { + cursor: default !important; + margin: 0 !important; + border-left-width: 0 !important; + border-radius: 0 !important; + } + + .toastui-calendar-weekday-event-title { + padding-left: 0 !important; + } + + .toastui-calendar-daygrid-cell + .toastui-calendar-daygrid-cell, + .toastui-calendar-timegrid .toastui-calendar-columns .toastui-calendar-column + .toastui-calendar-column, + .toastui-calendar-week-view-day-names .toastui-calendar-day-name-item.toastui-calendar-week + .toastui-calendar-day-name-item.toastui-calendar-week { + border-left: 0 !important; + } + + .toastui-calendar-column .toastui-calendar-events { + margin-right: 0 !important; + } + + .toastui-calendar-resize-handler-x, + .toastui-calendar-weekday-resize-handle { + display: none !important; + } + + @media (max-width: 900px) { + .planet-layout { + grid-template-columns: minmax(0, 1fr); + } + .planet-meta-grid { + grid-template-columns: minmax(0, 1fr); + } + .planet-fact-row { + grid-template-columns: minmax(0, 1fr); + gap: 4px; + } + .planet-fact-value { + text-align: left; + } + } + + #calendar { + height: calc(100vh - 96px); + } diff --git a/app/tarot-database.js b/app/tarot-database.js new file mode 100644 index 0000000..a6b3854 --- /dev/null +++ b/app/tarot-database.js @@ -0,0 +1,1341 @@ +(function () { + const MAJOR_CARDS = [ + { + number: 0, + name: "The Fool", + summary: "Open-hearted beginnings, leap of faith, and the sacred unknown.", + upright: "Fresh starts, trust in the path, and inspired spontaneity.", + reversed: "Recklessness, fear of risk, or resistance to a needed new beginning.", + keywords: ["beginnings", "faith", "innocence", "freedom", "journey"], + relations: ["Element: Air", "Hebrew Letter: Aleph"] + }, + { + number: 1, + name: "The Magus", + summary: "Focused will and conscious manifestation through aligned tools.", + upright: "Skill, initiative, concentration, and transforming intent into action.", + reversed: "Scattered power, manipulation, or blocked self-belief.", + keywords: ["will", "manifestation", "focus", "skill", "agency"], + relations: ["Planet: Mercury", "Hebrew Letter: Beth"] + }, + { + number: 2, + name: "The High Priestess", + summary: "Inner knowing, sacred silence, and intuitive perception.", + upright: "Intuition, subtle insight, patience, and hidden wisdom.", + reversed: "Disconnection from inner voice or confusion around truth.", + keywords: ["intuition", "mystery", "stillness", "inner-voice", "receptivity"], + relations: ["Planet: Luna", "Hebrew Letter: Gimel"] + }, + { + number: 3, + name: "The Empress", + summary: "Creative abundance, nurture, and embodied growth.", + upright: "Fertility, care, beauty, comfort, and creative flourishing.", + reversed: "Creative drought, overgiving, or neglecting self-nourishment.", + keywords: ["abundance", "creation", "nurture", "beauty", "growth"], + relations: ["Planet: Venus", "Hebrew Letter: Daleth"] + }, + { + number: 4, + name: "The Emperor", + summary: "Structure, authority, and stable leadership.", + upright: "Order, discipline, protection, and strategic direction.", + reversed: "Rigidity, domination, or weak boundaries.", + keywords: ["structure", "authority", "stability", "leadership", "boundaries"], + relations: ["Zodiac: Aries", "Hebrew Letter: He"] + }, + { + number: 5, + name: "The Hierophant", + summary: "Tradition, spiritual instruction, and shared values.", + upright: "Learning from lineage, ritual, ethics, and mentorship.", + reversed: "Dogma, rebellion without grounding, or spiritual stagnation.", + keywords: ["tradition", "teaching", "ritual", "ethics", "lineage"], + relations: ["Zodiac: Taurus", "Hebrew Letter: Vav"] + }, + { + number: 6, + name: "The Lovers", + summary: "Union, alignment, and value-centered choices.", + upright: "Harmony, connection, and conscious commitment.", + reversed: "Misalignment, indecision, or disconnection in relationship.", + keywords: ["union", "choice", "alignment", "connection", "values"], + relations: ["Zodiac: Gemini", "Hebrew Letter: Zayin"] + }, + { + number: 7, + name: "The Chariot", + summary: "Directed momentum and mastery through discipline.", + upright: "Determination, movement, and victory through control.", + reversed: "Loss of direction, conflict of will, or stalled progress.", + keywords: ["drive", "control", "momentum", "victory", "direction"], + relations: ["Zodiac: Cancer", "Hebrew Letter: Cheth"] + }, + { + number: 8, + name: "Lust", + summary: "Courageous life-force, passionate integrity, and heart-led power.", + upright: "Vitality, confidence, and wholehearted creative expression.", + reversed: "Self-doubt, depletion, or misdirected desire.", + keywords: ["vitality", "passion", "courage", "magnetism", "heart-power"], + relations: ["Zodiac: Leo", "Hebrew Letter: Teth"] + }, + { + number: 9, + name: "The Hermit", + summary: "Inner pilgrimage, discernment, and sacred solitude.", + upright: "Reflection, guidance, and deepening wisdom.", + reversed: "Isolation, avoidance, or overanalysis.", + keywords: ["solitude", "wisdom", "search", "reflection", "discernment"], + relations: ["Zodiac: Virgo", "Hebrew Letter: Yod"] + }, + { + number: 10, + name: "Fortune", + summary: "Cycles, timing, and turning points guided by greater rhythms.", + upright: "Opportunity, momentum, and fated change.", + reversed: "Resistance to cycles, delay, or repeating old patterns.", + keywords: ["cycles", "timing", "change", "destiny", "turning-point"], + relations: ["Planet: Jupiter", "Hebrew Letter: Kaph"] + }, + { + number: 11, + name: "Justice", + summary: "Balance, accountability, and clear consequence.", + upright: "Fairness, truth, and ethical alignment.", + reversed: "Bias, denial, or avoidance of responsibility.", + keywords: ["balance", "truth", "accountability", "law", "clarity"], + relations: ["Zodiac: Libra", "Hebrew Letter: Lamed"] + }, + { + number: 12, + name: "The Hanged Man", + summary: "Sacred pause, surrender, and transformed perspective.", + upright: "Release, contemplation, and spiritual reframing.", + reversed: "Stagnation, martyrdom, or refusing to let go.", + keywords: ["surrender", "pause", "perspective", "suspension", "release"], + relations: ["Element: Water", "Hebrew Letter: Mem"] + }, + { + number: 13, + name: "Death", + summary: "Endings that clear space for rebirth and renewal.", + upright: "Transformation, completion, and deep release.", + reversed: "Clinging, fear of change, or prolonged transition.", + keywords: ["transformation", "ending", "renewal", "release", "rebirth"], + relations: ["Zodiac: Scorpio", "Hebrew Letter: Nun"] + }, + { + number: 14, + name: "Art", + summary: "Alchemy, integration, and harmonizing opposites.", + upright: "Balance through blending, refinement, and patience.", + reversed: "Imbalance, excess, or fragmented energies.", + keywords: ["alchemy", "integration", "balance", "blending", "refinement"], + relations: ["Zodiac: Sagittarius", "Hebrew Letter: Samekh"] + }, + { + number: 15, + name: "The Devil", + summary: "Attachment, shadow desire, and material entanglement.", + upright: "Confronting bondage, ambition, and raw instinct.", + reversed: "Liberation, breaking chains, or denial of shadow.", + keywords: ["attachment", "shadow", "instinct", "temptation", "bondage"], + relations: ["Zodiac: Capricorn", "Hebrew Letter: Ayin"] + }, + { + number: 16, + name: "The Tower", + summary: "Sudden revelation that dismantles false structures.", + upright: "Shock, breakthrough, and necessary collapse.", + reversed: "Delayed upheaval, denial, or internal crisis.", + keywords: ["upheaval", "revelation", "breakthrough", "collapse", "truth"], + relations: ["Planet: Mars", "Hebrew Letter: Pe"] + }, + { + number: 17, + name: "The Star", + summary: "Healing hope, guidance, and spiritual renewal.", + upright: "Inspiration, serenity, and trust in the future.", + reversed: "Discouragement, doubt, or loss of faith.", + keywords: ["hope", "healing", "guidance", "renewal", "faith"], + relations: ["Zodiac: Aquarius", "Hebrew Letter: Tzaddi"] + }, + { + number: 18, + name: "The Moon", + summary: "Dream, uncertainty, and passage through the subconscious.", + upright: "Intuition, mystery, and emotional depth.", + reversed: "Confusion clearing, projection, or fear illusions.", + keywords: ["dream", "mystery", "intuition", "subconscious", "illusion"], + relations: ["Zodiac: Pisces", "Hebrew Letter: Qoph"] + }, + { + number: 19, + name: "The Sun", + summary: "Radiance, confidence, and vital life affirmation.", + upright: "Joy, clarity, success, and openness.", + reversed: "Temporary clouding, ego glare, or delayed confidence.", + keywords: ["joy", "clarity", "vitality", "success", "radiance"], + relations: ["Planet: Sol", "Hebrew Letter: Resh"] + }, + { + number: 20, + name: "Aeon", + summary: "Awakening call, reckoning, and soul-level renewal.", + upright: "Judgment, liberation, and answering purpose.", + reversed: "Self-judgment, hesitation, or resistance to calling.", + keywords: ["awakening", "reckoning", "calling", "renewal", "release"], + relations: ["Element: Fire", "Hebrew Letter: Shin"] + }, + { + number: 21, + name: "Universe", + summary: "Completion, integration, and embodied wholeness.", + upright: "Fulfillment, mastery, and successful closure.", + reversed: "Incomplete cycle, loose ends, or delayed completion.", + keywords: ["completion", "wholeness", "integration", "mastery", "closure"], + relations: ["Planet: Saturn", "Hebrew Letter: Tav"] + } + ]; + + const SUITS = ["Cups", "Wands", "Swords", "Disks"]; + const NUMBER_RANKS = ["Ace", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten"]; + const COURT_RANKS = ["Knight", "Queen", "Prince", "Princess"]; + + const SUIT_INFO = { + cups: { + element: "Water", + domain: "emotion, intuition, relationship", + keywords: ["emotion", "intuition", "connection"] + }, + wands: { + element: "Fire", + domain: "will, creativity, drive", + keywords: ["will", "drive", "inspiration"] + }, + swords: { + element: "Air", + domain: "mind, truth, communication", + keywords: ["mind", "truth", "clarity"] + }, + disks: { + element: "Earth", + domain: "body, craft, material life", + keywords: ["resources", "craft", "stability"] + } + }; + + const RANK_INFO = { + ace: { + number: 1, + upright: "Seed-force, pure potential, and concentrated essence.", + reversed: "Blocked potential, delay, or difficulty beginning.", + keywords: ["seed", "potential", "spark"] + }, + two: { + number: 2, + upright: "Polarity seeking balance, exchange, and response.", + reversed: "Imbalance, friction, or uncertain choices.", + keywords: ["duality", "balance", "exchange"] + }, + three: { + number: 3, + upright: "Initial growth, expansion, and expression.", + reversed: "Scattered growth or stalled development.", + keywords: ["growth", "expansion", "expression"] + }, + four: { + number: 4, + upright: "Structure, boundaries, and stabilizing form.", + reversed: "Rigidity, inertia, or unstable foundations.", + keywords: ["structure", "stability", "foundation"] + }, + five: { + number: 5, + upright: "Challenge, disruption, and catalytic conflict.", + reversed: "Lingering tension or fear of needed change.", + keywords: ["challenge", "conflict", "change"] + }, + six: { + number: 6, + upright: "Rebalancing, harmony, and restorative flow.", + reversed: "Partial recovery or unresolved imbalance.", + keywords: ["harmony", "repair", "flow"] + }, + seven: { + number: 7, + upright: "Testing, strategy, and spiritual/mental refinement.", + reversed: "Doubt, overdefense, or poor strategy.", + keywords: ["test", "strategy", "refinement"] + }, + eight: { + number: 8, + upright: "Adjustment, movement, and disciplined power.", + reversed: "Restriction, frustration, or scattered effort.", + keywords: ["movement", "discipline", "adjustment"] + }, + nine: { + number: 9, + upright: "Intensity, culmination, and mature force.", + reversed: "Excess, overload, or unresolved pressure.", + keywords: ["culmination", "intensity", "maturity"] + }, + ten: { + number: 10, + upright: "Completion, overflow, and transition into a new cycle.", + reversed: "Overextension, depletion, or difficulty releasing.", + keywords: ["completion", "threshold", "transition"] + } + }; + + const COURT_INFO = { + knight: { + role: "active catalyst and questing force", + elementalFace: "Fire of", + upright: "Bold pursuit, direct action, and forward momentum.", + reversed: "Impulsiveness, burnout, or unfocused drive.", + keywords: ["action", "drive", "initiative"] + }, + queen: { + role: "magnetic vessel and emotional intelligence", + elementalFace: "Water of", + upright: "Receptive mastery, mature feeling, and embodied wisdom.", + reversed: "Withholding, moodiness, or passive control.", + keywords: ["receptivity", "maturity", "depth"] + }, + prince: { + role: "strategic mediator and organizing mind", + elementalFace: "Air of", + upright: "Insight, communication, and adaptive coordination.", + reversed: "Overthinking, detachment, or mixed signals.", + keywords: ["strategy", "communication", "adaptability"] + }, + princess: { + role: "manifesting ground and practical seed-force", + elementalFace: "Earth of", + upright: "Practical growth, devotion, and tangible follow-through.", + reversed: "Stagnation, timidity, or blocked growth.", + keywords: ["manifestation", "grounding", "devotion"] + } + }; + + const MAJOR_ALIASES = { + magician: "magus", + strength: "lust", + "wheel of fortune": "fortune", + temperance: "art", + judgement: "aeon", + judgment: "aeon", + world: "universe", + adjustment: "justice" + }; + + const MINOR_SUIT_ALIASES = { + pentacles: "disks", + pentacle: "disks", + coins: "disks", + disks: "disks" + }; + + const MINOR_NUMERAL_ALIASES = { + 1: "ace", + 2: "two", + 3: "three", + 4: "four", + 5: "five", + 6: "six", + 7: "seven", + 8: "eight", + 9: "nine", + 10: "ten" + }; + + const MONTH_NAME_BY_NUMBER = { + 1: "January", + 2: "February", + 3: "March", + 4: "April", + 5: "May", + 6: "June", + 7: "July", + 8: "August", + 9: "September", + 10: "October", + 11: "November", + 12: "December" + }; + + const MONTH_ID_BY_NUMBER = { + 1: "january", + 2: "february", + 3: "march", + 4: "april", + 5: "may", + 6: "june", + 7: "july", + 8: "august", + 9: "september", + 10: "october", + 11: "november", + 12: "december" + }; + + const COURT_DECAN_WINDOWS = { + "Knight of Wands": ["scorpio-3", "sagittarius-1", "sagittarius-2"], + "Queen of Disks": ["sagittarius-3", "capricorn-1", "capricorn-2"], + "Prince of Swords": ["capricorn-3", "aquarius-1", "aquarius-2"], + "Knight of Cups": ["aquarius-3", "pisces-1", "pisces-2"], + "Queen of Wands": ["pisces-3", "aries-1", "aries-2"], + "Prince of Disks": ["aries-3", "taurus-1", "taurus-2"], + "Knight of Swords": ["taurus-3", "gemini-1", "gemini-2"], + "Queen of Cups": ["gemini-3", "cancer-1", "cancer-2"], + "Prince of Wands": ["cancer-3", "leo-1", "leo-2"], + "Knight of Disks": ["leo-3", "virgo-1", "virgo-2"], + "Queen of Swords": ["virgo-3", "libra-1", "libra-2"], + "Prince of Cups": ["libra-3", "scorpio-1", "scorpio-2"] + }; + + const MONTH_SHORT = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; + + const MAJOR_HEBREW_LETTER_ID_BY_CARD = { + fool: "alef", + magus: "bet", + "high priestess": "gimel", + empress: "dalet", + emperor: "he", + hierophant: "vav", + lovers: "zayin", + chariot: "het", + lust: "tet", + hermit: "yod", + fortune: "kaf", + justice: "lamed", + "hanged man": "mem", + death: "nun", + art: "samekh", + devil: "ayin", + tower: "pe", + star: "tsadi", + moon: "qof", + sun: "resh", + aeon: "shin", + universe: "tav" + }; + + const HEBREW_LETTER_ALIASES = { + aleph: "alef", + alef: "alef", + beth: "bet", + bet: "bet", + gimel: "gimel", + daleth: "dalet", + dalet: "dalet", + he: "he", + vav: "vav", + zayin: "zayin", + cheth: "het", + chet: "het", + het: "het", + teth: "tet", + tet: "tet", + yod: "yod", + kaph: "kaf", + kaf: "kaf", + lamed: "lamed", + mem: "mem", + nun: "nun", + samekh: "samekh", + ayin: "ayin", + pe: "pe", + tzaddi: "tsadi", + tzadi: "tsadi", + tsadi: "tsadi", + qoph: "qof", + qof: "qof", + resh: "resh", + shin: "shin", + tav: "tav" + }; + + function getTarotDbConfig(referenceData) { + const db = referenceData?.tarotDatabase; + const hasDb = db && typeof db === "object"; + + const majorCards = hasDb && Array.isArray(db.majorCards) && db.majorCards.length + ? db.majorCards + : MAJOR_CARDS; + + const suits = hasDb && Array.isArray(db.suits) && db.suits.length + ? db.suits + : SUITS; + + const numberRanks = hasDb && Array.isArray(db.numberRanks) && db.numberRanks.length + ? db.numberRanks + : NUMBER_RANKS; + + const courtRanks = hasDb && Array.isArray(db.courtRanks) && db.courtRanks.length + ? db.courtRanks + : COURT_RANKS; + + const suitInfo = hasDb && db.suitInfo && typeof db.suitInfo === "object" + ? db.suitInfo + : SUIT_INFO; + + const rankInfo = hasDb && db.rankInfo && typeof db.rankInfo === "object" + ? db.rankInfo + : RANK_INFO; + + const courtInfo = hasDb && db.courtInfo && typeof db.courtInfo === "object" + ? db.courtInfo + : COURT_INFO; + + const courtDecanWindows = hasDb && db.courtDecanWindows && typeof db.courtDecanWindows === "object" + ? db.courtDecanWindows + : COURT_DECAN_WINDOWS; + + return { + majorCards, + suits, + numberRanks, + courtRanks, + suitInfo, + rankInfo, + courtInfo, + courtDecanWindows + }; + } + + function normalizeCardName(value) { + return String(value || "") + .trim() + .toLowerCase() + .replace(/^the\s+/, "") + .replace(/\s+/g, " "); + } + + function canonicalCardName(value) { + const normalized = normalizeCardName(value); + const majorCanonical = MAJOR_ALIASES[normalized] || normalized; + const withSuitAliases = majorCanonical.replace(/\bof\s+(pentacles?|coins?)\b/i, "of disks"); + const numberMatch = withSuitAliases.match(/^(\d{1,2})\s+of\s+(.+)$/i); + + if (numberMatch) { + const number = Number(numberMatch[1]); + const suit = String(numberMatch[2] || "").trim(); + const numberWord = MINOR_NUMERAL_ALIASES[number]; + if (numberWord && suit) { + return `${numberWord} of ${suit}`; + } + } + + return withSuitAliases; + } + + function parseMonthDay(value) { + const [month, day] = String(value || "").split("-").map((part) => Number(part)); + if (!Number.isFinite(month) || !Number.isFinite(day)) { + return null; + } + return { month, day }; + } + + function monthDayToDate(monthDay, year) { + const parsed = parseMonthDay(monthDay); + if (!parsed) { + return null; + } + return new Date(year, parsed.month - 1, parsed.day); + } + + function addDays(date, days) { + const next = new Date(date); + next.setDate(next.getDate() + days); + return next; + } + + function formatMonthDayLabel(date) { + if (!(date instanceof Date)) { + return "--"; + } + return `${MONTH_SHORT[date.getMonth()]} ${date.getDate()}`; + } + + function formatMonthDayToken(date) { + if (!(date instanceof Date)) { + return ""; + } + const month = String(date.getMonth() + 1).padStart(2, "0"); + const day = String(date.getDate()).padStart(2, "0"); + return `${month}-${day}`; + } + + function normalizeMinorTarotCardName(value) { + const normalized = canonicalCardName(value); + return normalized + .split(" ") + .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) + .join(" "); + } + + function deriveSummaryFromMeaning(meaningText, fallbackSummary) { + const normalized = String(meaningText || "") + .replace(/\s+/g, " ") + .trim(); + + if (!normalized) { + return fallbackSummary; + } + + const sentenceMatch = normalized.match(/^(.+?[.!?])(?:\s|$)/); + if (sentenceMatch && sentenceMatch[1]) { + return sentenceMatch[1].trim(); + } + + if (normalized.length <= 220) { + return normalized; + } + + return `${normalized.slice(0, 217).trimEnd()}…`; + } + + function applyMeaningText(cards, referenceData) { + const majorByTrumpNumber = referenceData?.tarotDatabase?.meanings?.majorByTrumpNumber; + const byCardName = referenceData?.tarotDatabase?.meanings?.byCardName; + + if ((!majorByTrumpNumber || typeof majorByTrumpNumber !== "object") + && (!byCardName || typeof byCardName !== "object")) { + return cards; + } + + return cards.map((card) => { + const trumpNumber = Number(card?.number); + const isMajorTrumpCard = card?.arcana === "Major" && Number.isFinite(trumpNumber); + const canonicalName = canonicalCardName(card?.name); + + const majorMeaning = isMajorTrumpCard + ? String(majorByTrumpNumber?.[trumpNumber] || "").trim() + : ""; + const nameMeaning = String(byCardName?.[canonicalName] || "").trim(); + const selectedMeaning = majorMeaning || nameMeaning; + + if (!selectedMeaning) { + return card; + } + + return { + ...card, + meaning: selectedMeaning, + summary: deriveSummaryFromMeaning(selectedMeaning, card.summary), + meanings: { + upright: selectedMeaning, + reversed: card?.meanings?.reversed || "" + } + }; + }); + } + + function getSignDateBounds(sign) { + const start = monthDayToDate(sign?.start, 2025); + const endBase = monthDayToDate(sign?.end, 2025); + if (!start || !endBase) { + return null; + } + + const wraps = endBase.getTime() < start.getTime(); + const end = wraps ? monthDayToDate(sign?.end, 2026) : endBase; + if (!end) { + return null; + } + + return { start, end }; + } + + function buildDecanDateRange(sign, decanIndex) { + const bounds = getSignDateBounds(sign); + if (!bounds || !Number.isFinite(Number(decanIndex))) { + return null; + } + + const index = Number(decanIndex); + const start = addDays(bounds.start, (index - 1) * 10); + const nominalEnd = addDays(start, 9); + const end = nominalEnd.getTime() > bounds.end.getTime() ? bounds.end : nominalEnd; + + return { + start, + end, + startMonth: start.getMonth() + 1, + endMonth: end.getMonth() + 1, + startToken: formatMonthDayToken(start), + endToken: formatMonthDayToken(end), + label: `${formatMonthDayLabel(start)}–${formatMonthDayLabel(end)}` + }; + } + + function listMonthNumbersBetween(start, end) { + if (!(start instanceof Date) || !(end instanceof Date)) { + return []; + } + + const result = []; + const seen = new Set(); + const cursor = new Date(start.getFullYear(), start.getMonth(), 1); + const limit = new Date(end.getFullYear(), end.getMonth(), 1); + + while (cursor.getTime() <= limit.getTime()) { + const monthNo = cursor.getMonth() + 1; + if (!seen.has(monthNo)) { + seen.add(monthNo); + result.push(monthNo); + } + cursor.setMonth(cursor.getMonth() + 1); + } + + return result; + } + + function getSignName(sign, fallback) { + return sign?.name?.en || sign?.name || sign?.id || fallback || "Unknown"; + } + + function buildDecanMetadata(decan, sign) { + if (!decan || !sign) { + return null; + } + + const index = Number(decan.index); + if (!Number.isFinite(index)) { + return null; + } + + const startDegree = (index - 1) * 10; + const endDegree = startDegree + 10; + const dateRange = buildDecanDateRange(sign, index); + + return { + decan, + sign, + index, + signId: sign.id, + signName: getSignName(sign, decan.signId), + signSymbol: sign.symbol || "", + startDegree, + endDegree, + dateRange, + normalizedCardName: normalizeMinorTarotCardName(decan.tarotMinorArcana || "") + }; + } + + function collectCalendarMonthRelationsFromDecan(targetKey, relationMap, decanMeta) { + const dateRange = decanMeta?.dateRange; + if (!dateRange?.start || !dateRange?.end) { + return; + } + + const monthNumbers = listMonthNumbersBetween(dateRange.start, dateRange.end); + monthNumbers.forEach((monthNo) => { + const monthId = MONTH_ID_BY_NUMBER[monthNo]; + const monthName = MONTH_NAME_BY_NUMBER[monthNo] || `Month ${monthNo}`; + if (!monthId) { + return; + } + + pushMapValue( + relationMap, + targetKey, + createRelation( + "calendarMonth", + `${monthId}-${decanMeta.signId}-${decanMeta.index}`, + `Calendar month: ${monthName} (${decanMeta.signName} decan ${decanMeta.index})`, + { + monthId, + name: monthName, + monthOrder: monthNo, + signId: decanMeta.signId, + signName: decanMeta.signName, + decanIndex: decanMeta.index, + dateRange: dateRange.label + } + ) + ); + }); + } + + function normalizeHebrewKey(value) { + return String(value || "") + .trim() + .toLowerCase() + .replace(/[^a-z]/g, ""); + } + + function buildHebrewLetterLookup(magickDataset) { + const letters = magickDataset?.grouped?.hebrewLetters; + const lookup = new Map(); + + if (!letters || typeof letters !== "object") { + return lookup; + } + + Object.entries(letters).forEach(([letterId, entry]) => { + const idKey = normalizeHebrewKey(letterId); + const canonicalKey = HEBREW_LETTER_ALIASES[idKey] || idKey; + + if (canonicalKey && !lookup.has(canonicalKey)) { + lookup.set(canonicalKey, entry); + } + + const nameKey = normalizeHebrewKey(entry?.letter?.name); + const canonicalNameKey = HEBREW_LETTER_ALIASES[nameKey] || nameKey; + if (canonicalNameKey && !lookup.has(canonicalNameKey)) { + lookup.set(canonicalNameKey, entry); + } + + const entryIdKey = normalizeHebrewKey(entry?.id); + const canonicalEntryIdKey = HEBREW_LETTER_ALIASES[entryIdKey] || entryIdKey; + if (canonicalEntryIdKey && !lookup.has(canonicalEntryIdKey)) { + lookup.set(canonicalEntryIdKey, entry); + } + }); + + return lookup; + } + + function normalizeRelationId(value) { + return String(value || "") + .trim() + .toLowerCase() + .replace(/[^a-z0-9]+/g, "-") + .replace(/(^-|-$)/g, "") || "unknown"; + } + + function createRelation(type, id, label, data = null) { + return { + type, + id: normalizeRelationId(id), + label: String(label || "").trim(), + data + }; + } + + function relationSignature(value) { + if (!value || typeof value !== "object") { + return String(value || ""); + } + + return `${value.type || "text"}|${value.id || ""}|${value.label || ""}`; + } + + function parseLegacyRelation(text) { + const raw = String(text || "").trim(); + const match = raw.match(/^([^:]+):\s*(.+)$/); + if (!match) { + return createRelation("note", raw, raw, { value: raw }); + } + + const key = normalizeRelationId(match[1]); + const value = String(match[2] || "").trim(); + + if (key === "element") { + return createRelation("element", value, `Element: ${value}`, { name: value }); + } + + if (key === "planet") { + return createRelation("planet", value, `Planet: ${value}`, { name: value }); + } + + if (key === "zodiac") { + return createRelation("zodiac", value, `Zodiac: ${value}`, { name: value }); + } + + if (key === "suit-domain") { + return createRelation("suitDomain", value, `Suit domain: ${value}`, { value }); + } + + if (key === "numerology") { + const numeric = Number(value); + return createRelation("numerology", value, `Numerology: ${value}`, { + value: Number.isFinite(numeric) ? numeric : value + }); + } + + if (key === "court-role") { + return createRelation("courtRole", value, `Court role: ${value}`, { value }); + } + + if (key === "hebrew-letter") { + const normalized = normalizeHebrewKey(value); + const canonical = HEBREW_LETTER_ALIASES[normalized] || normalized; + return createRelation("hebrewLetter", canonical, `Hebrew Letter: ${value}`, { + requestedName: value + }); + } + + return createRelation(key || "relation", value, raw, { value }); + } + + function buildHebrewLetterRelation(hebrewLetterId, hebrewLookup) { + if (!hebrewLetterId || !hebrewLookup) { + return null; + } + + const normalizedId = normalizeHebrewKey(hebrewLetterId); + const canonicalId = HEBREW_LETTER_ALIASES[normalizedId] || normalizedId; + const entry = hebrewLookup.get(canonicalId); + if (!entry) { + return createRelation("hebrewLetter", canonicalId, `Hebrew Letter: ${hebrewLetterId}`, null); + } + + const glyph = entry?.letter?.he || ""; + const name = entry?.letter?.name || hebrewLetterId; + const latin = entry?.letter?.latin || ""; + const index = Number.isFinite(entry?.index) ? entry.index : null; + const value = Number.isFinite(entry?.value) ? entry.value : null; + const meaning = entry?.meaning?.en || ""; + + const indexText = index !== null ? index : "?"; + const valueText = value !== null ? value : "?"; + const meaningText = meaning ? ` · ${meaning}` : ""; + + return createRelation( + "hebrewLetter", + entry?.id || canonicalId, + `Hebrew Letter: ${glyph} ${name} (${latin}) (index ${indexText}, value ${valueText})${meaningText}`.trim(), + { + id: entry?.id || canonicalId, + glyph, + name, + latin, + index, + value, + meaning + } + ); + } + + function pushMapValue(map, key, value) { + if (!key || !value) { + return; + } + + if (!map.has(key)) { + map.set(key, []); + } + + const existing = map.get(key); + const signature = relationSignature(value); + const duplicate = existing.some((entry) => relationSignature(entry) === signature); + + if (!duplicate) { + existing.push(value); + } + } + + function buildMajorDynamicRelations(referenceData) { + const relationMap = new Map(); + + const planets = referenceData?.planets && typeof referenceData.planets === "object" + ? Object.values(referenceData.planets) + : []; + + planets.forEach((planet) => { + const cardName = planet?.tarot?.majorArcana; + if (!cardName) { + return; + } + + const relation = createRelation( + "planetCorrespondence", + planet?.id || planet?.name || cardName, + `Planet correspondence: ${planet.symbol || ""} ${planet.name || ""}`.trim(), + { + planetId: planet?.id || null, + symbol: planet?.symbol || "", + name: planet?.name || "" + } + ); + pushMapValue(relationMap, canonicalCardName(cardName), relation); + }); + + const signs = Array.isArray(referenceData?.signs) ? referenceData.signs : []; + signs.forEach((sign) => { + const cardName = sign?.tarot?.majorArcana; + if (!cardName) { + return; + } + + const relation = createRelation( + "zodiacCorrespondence", + sign?.id || sign?.name || cardName, + `Zodiac correspondence: ${sign.symbol || ""} ${sign.name || ""}`.trim(), + { + signId: sign?.id || null, + symbol: sign?.symbol || "", + name: sign?.name || "" + } + ); + pushMapValue(relationMap, canonicalCardName(cardName), relation); + }); + + return relationMap; + } + + function buildMinorDecanRelations(referenceData) { + const relationMap = new Map(); + const signs = Array.isArray(referenceData?.signs) ? referenceData.signs : []; + const signById = Object.fromEntries(signs.map((sign) => [sign.id, sign])); + const planets = referenceData?.planets || {}; + + if (!referenceData?.decansBySign || typeof referenceData.decansBySign !== "object") { + return relationMap; + } + + Object.entries(referenceData.decansBySign).forEach(([signId, decans]) => { + const sign = signById[signId]; + if (!Array.isArray(decans) || !sign) { + return; + } + + decans.forEach((decan) => { + const cardName = decan?.tarotMinorArcana; + if (!cardName) { + return; + } + + const decanMeta = buildDecanMetadata(decan, sign); + if (!decanMeta) { + return; + } + + const { startDegree, endDegree, dateRange, signId, signName, signSymbol, index } = decanMeta; + const ruler = planets[decan.rulerPlanetId] || null; + const cardKey = canonicalCardName(cardName); + + pushMapValue( + relationMap, + cardKey, + createRelation( + "zodiac", + signId, + `Zodiac: ${sign.symbol || ""} ${signName}`.trim(), + { + signId, + signName, + symbol: sign.symbol || "" + } + ) + ); + + pushMapValue( + relationMap, + cardKey, + createRelation( + "decan", + `${signId}-${index}`, + `Decan ${decan.index}: ${sign.symbol || ""} ${signName} (${startDegree}°–${endDegree}°)${dateRange ? ` · ${dateRange.label}` : ""}`.trim(), + { + signId, + signName, + signSymbol, + index, + startDegree, + endDegree, + dateStart: dateRange?.startToken || null, + dateEnd: dateRange?.endToken || null, + dateRange: dateRange?.label || null + } + ) + ); + + collectCalendarMonthRelationsFromDecan(cardKey, relationMap, decanMeta); + + if (ruler) { + pushMapValue( + relationMap, + cardKey, + createRelation( + "decanRuler", + `${signId}-${index}-${decan.rulerPlanetId}`, + `Decan ruler: ${ruler.symbol || ""} ${ruler.name || decan.rulerPlanetId}`.trim(), + { + signId, + decanIndex: index, + planetId: decan.rulerPlanetId, + symbol: ruler.symbol || "", + name: ruler.name || decan.rulerPlanetId + } + ) + ); + } + }); + }); + + return relationMap; + } + + function buildMajorCards(referenceData, magickDataset) { + const tarotDb = getTarotDbConfig(referenceData); + const dynamicRelations = buildMajorDynamicRelations(referenceData); + const hebrewLookup = buildHebrewLetterLookup(magickDataset); + + return tarotDb.majorCards.map((card) => { + const canonicalName = canonicalCardName(card.name); + const dynamic = dynamicRelations.get(canonicalName) || []; + const hebrewLetterId = MAJOR_HEBREW_LETTER_ID_BY_CARD[canonicalName] || null; + const hebrewLetterRelation = buildHebrewLetterRelation(hebrewLetterId, hebrewLookup); + const staticRelations = (card.relations || []) + .map((relation) => parseLegacyRelation(relation)) + .filter((relation) => relation.type !== "hebrewLetter" && relation.type !== "zodiac" && relation.type !== "planet"); + + return { + arcana: "Major", + name: card.name, + number: card.number, + suit: null, + rank: null, + hebrewLetterId, + hebrewLetter: hebrewLetterRelation?.data || null, + summary: card.summary, + meanings: { + upright: card.upright, + reversed: card.reversed + }, + keywords: [...card.keywords], + relations: [ + ...staticRelations, + ...(hebrewLetterRelation ? [hebrewLetterRelation] : []), + ...dynamic + ] + }; + }); + } + + function buildNumberMinorCards(referenceData) { + const tarotDb = getTarotDbConfig(referenceData); + const decanRelations = buildMinorDecanRelations(referenceData); + const cards = []; + + tarotDb.suits.forEach((suit) => { + const suitKey = suit.toLowerCase(); + const suitInfo = tarotDb.suitInfo[suitKey]; + if (!suitInfo) { + return; + } + + tarotDb.numberRanks.forEach((rank) => { + const rankKey = rank.toLowerCase(); + const rankInfo = tarotDb.rankInfo[rankKey]; + if (!rankInfo) { + return; + } + const cardName = `${rank} of ${suit}`; + const dynamicRelations = decanRelations.get(canonicalCardName(cardName)) || []; + + cards.push({ + arcana: "Minor", + name: cardName, + number: null, + suit, + rank, + summary: `${rank} energy expressed through ${suitInfo.domain}.`, + meanings: { + upright: `${rankInfo.upright} In ${suit}, this emphasizes ${suitInfo.domain}.`, + reversed: `${rankInfo.reversed} In ${suit}, this may distort ${suitInfo.domain}.` + }, + keywords: [...rankInfo.keywords, ...suitInfo.keywords], + relations: [ + createRelation("element", suitInfo.element, `Element: ${suitInfo.element}`, { + name: suitInfo.element + }), + createRelation("suitDomain", `${suitKey}-${rankKey}`, `Suit domain: ${suitInfo.domain}`, { + suit: suit, + rank, + domain: suitInfo.domain + }), + createRelation("numerology", rankInfo.number, `Numerology: ${rankInfo.number}`, { + value: rankInfo.number + }), + ...dynamicRelations + ] + }); + }); + }); + + return cards; + } + + function buildCourtMinorCards(referenceData) { + const tarotDb = getTarotDbConfig(referenceData); + const cards = []; + const signs = Array.isArray(referenceData?.signs) ? referenceData.signs : []; + const signById = Object.fromEntries(signs.map((sign) => [sign.id, sign])); + + const decanById = new Map(); + const decansBySign = referenceData?.decansBySign || {}; + Object.entries(decansBySign).forEach(([signId, decans]) => { + const sign = signById[signId]; + if (!sign || !Array.isArray(decans)) { + return; + } + decans.forEach((decan) => { + if (!decan?.id) { + return; + } + const meta = buildDecanMetadata(decan, sign); + if (meta) { + decanById.set(decan.id, meta); + } + }); + }); + + tarotDb.suits.forEach((suit) => { + const suitKey = suit.toLowerCase(); + const suitInfo = tarotDb.suitInfo[suitKey]; + if (!suitInfo) { + return; + } + + tarotDb.courtRanks.forEach((rank) => { + const rankKey = rank.toLowerCase(); + const courtInfo = tarotDb.courtInfo[rankKey]; + if (!courtInfo) { + return; + } + const cardName = `${rank} of ${suit}`; + const windowDecanIds = tarotDb.courtDecanWindows[cardName] || []; + const windowDecans = windowDecanIds + .map((decanId) => decanById.get(decanId) || null) + .filter(Boolean); + + const dynamicRelations = []; + const monthKeys = new Set(); + + windowDecans.forEach((meta) => { + dynamicRelations.push( + createRelation( + "decan", + `${meta.signId}-${meta.index}-${rankKey}-${suitKey}`, + `Decan ${meta.index}: ${meta.signSymbol} ${meta.signName} (${meta.startDegree}°–${meta.endDegree}°)${meta.dateRange ? ` · ${meta.dateRange.label}` : ""}`.trim(), + { + signId: meta.signId, + signName: meta.signName, + signSymbol: meta.signSymbol, + index: meta.index, + startDegree: meta.startDegree, + endDegree: meta.endDegree, + dateStart: meta.dateRange?.startToken || null, + dateEnd: meta.dateRange?.endToken || null, + dateRange: meta.dateRange?.label || null + } + ) + ); + + const dateRange = meta.dateRange; + if (dateRange?.start && dateRange?.end) { + const monthNumbers = listMonthNumbersBetween(dateRange.start, dateRange.end); + monthNumbers.forEach((monthNo) => { + const monthId = MONTH_ID_BY_NUMBER[monthNo]; + const monthName = MONTH_NAME_BY_NUMBER[monthNo] || `Month ${monthNo}`; + const monthKey = `${monthId}:${meta.signId}:${meta.index}`; + if (!monthId || monthKeys.has(monthKey)) { + return; + } + monthKeys.add(monthKey); + + dynamicRelations.push( + createRelation( + "calendarMonth", + `${monthId}-${meta.signId}-${meta.index}-${rankKey}-${suitKey}`, + `Calendar month: ${monthName} (${meta.signName} decan ${meta.index})`, + { + monthId, + name: monthName, + monthOrder: monthNo, + signId: meta.signId, + signName: meta.signName, + decanIndex: meta.index, + dateRange: meta.dateRange?.label || null + } + ) + ); + }); + } + }); + + if (windowDecans.length) { + const firstRange = windowDecans[0].dateRange; + const lastRange = windowDecans[windowDecans.length - 1].dateRange; + const windowLabel = firstRange && lastRange + ? `${formatMonthDayLabel(firstRange.start)}–${formatMonthDayLabel(lastRange.end)}` + : "--"; + + dynamicRelations.unshift( + createRelation( + "courtDateWindow", + `${rankKey}-${suitKey}`, + `Court date window: ${windowLabel}`, + { + dateStart: firstRange?.startToken || null, + dateEnd: lastRange?.endToken || null, + dateRange: windowLabel, + decanIds: windowDecanIds + } + ) + ); + } + + cards.push({ + arcana: "Minor", + name: cardName, + number: null, + suit, + rank, + summary: `${rank} as ${courtInfo.role} within ${suitInfo.domain}.`, + meanings: { + upright: `${courtInfo.upright} In ${suit}, this guides ${suitInfo.domain}.`, + reversed: `${courtInfo.reversed} In ${suit}, this complicates ${suitInfo.domain}.` + }, + keywords: [...courtInfo.keywords, ...suitInfo.keywords], + relations: [ + createRelation("element", suitInfo.element, `Element: ${suitInfo.element}`, { + name: suitInfo.element + }), + createRelation( + "elementalFace", + `${rankKey}-${suitKey}`, + `${courtInfo.elementalFace} ${suitInfo.element}`, + { + rank, + suit, + elementalFace: courtInfo.elementalFace, + element: suitInfo.element + } + ), + createRelation("courtRole", rankKey, `Court role: ${courtInfo.role}`, { + rank, + role: courtInfo.role + }), + ...dynamicRelations + ] + }); + }); + }); + + return cards; + } + + function buildTarotDatabase(referenceData, magickDataset = null) { + const cards = [ + ...buildMajorCards(referenceData, magickDataset), + ...buildNumberMinorCards(referenceData), + ...buildCourtMinorCards(referenceData) + ]; + + return applyMeaningText(cards, referenceData); + } + + window.TarotCardDatabase = { + buildTarotDatabase + }; +})(); diff --git a/app/ui-alphabet.js b/app/ui-alphabet.js new file mode 100644 index 0000000..f9a9253 --- /dev/null +++ b/app/ui-alphabet.js @@ -0,0 +1,2081 @@ +/* ui-alphabet.js — Multi-alphabet browser (English / Hebrew / Greek / Arabic / Enochian) */ +(function () { + "use strict"; + + const state = { + initialized: false, + alphabets: null, + activeAlphabet: "all", + selectedKey: null, + filters: { + query: "", + letterType: "" + }, + fourWorldLayers: [], + monthRefsByHebrewId: new Map(), + cubeRefs: { + hebrewPlacementById: new Map(), + signPlacementById: new Map(), + planetPlacementById: new Map(), + pathPlacementByNo: new Map() + }, + gematria: { + loadingPromise: null, + db: null, + listenersBound: false, + activeCipherId: "", + inputText: "", + scriptCharMap: new Map() + } + }; + + // ── Arabic display name table ───────────────────────────────────────── + const ARABIC_DISPLAY_NAMES = { + alif: "Alif", ba: "Ba", jeem: "Jeem", dal: "Dal", ha: "H\u0101", + waw: "W\u0101w", zayn: "Zayn", ha_khaa: "\u1e24\u0101", ta_tay: "\u1e6c\u0101", ya: "Y\u0101", + kaf: "K\u0101f", lam: "L\u0101m", meem: "M\u012bm", nun: "N\u016bn", seen: "S\u012bn", + ayn: "\u02bfAyn", fa: "F\u0101", sad: "\u1e62\u0101d", qaf: "Q\u0101f", ra: "R\u0101", + sheen: "Sh\u012bn", ta: "T\u0101", tha: "Th\u0101", kha: "Kh\u0101", + dhal: "Dh\u0101l", dad: "\u1e0c\u0101d", dha: "\u1e92\u0101", ghayn: "Ghayn" + }; + + function arabicDisplayName(letter) { + return ARABIC_DISPLAY_NAMES[letter && letter.name] || (String(letter && letter.name || "").charAt(0).toUpperCase() + String(letter && letter.name || "").slice(1)); + } + + // ── Element cache ──────────────────────────────────────────────────── + let listEl, countEl, detailNameEl, detailSubEl, detailBodyEl; + let tabAll, tabHebrew, tabGreek, tabEnglish, tabArabic, tabEnochian; + let searchInputEl, searchClearEl, typeFilterEl; + let gematriaCipherEl, gematriaInputEl, gematriaResultEl, gematriaBreakdownEl; + + function getElements() { + listEl = document.getElementById("alpha-letter-list"); + countEl = document.getElementById("alpha-letter-count"); + detailNameEl = document.getElementById("alpha-detail-name"); + detailSubEl = document.getElementById("alpha-detail-sub"); + detailBodyEl = document.getElementById("alpha-detail-body"); + tabAll = document.getElementById("alpha-tab-all"); + tabHebrew = document.getElementById("alpha-tab-hebrew"); + tabGreek = document.getElementById("alpha-tab-greek"); + tabEnglish = document.getElementById("alpha-tab-english"); + tabArabic = document.getElementById("alpha-tab-arabic"); + tabEnochian = document.getElementById("alpha-tab-enochian"); + searchInputEl = document.getElementById("alpha-search-input"); + searchClearEl = document.getElementById("alpha-search-clear"); + typeFilterEl = document.getElementById("alpha-type-filter"); + gematriaCipherEl = document.getElementById("alpha-gematria-cipher"); + gematriaInputEl = document.getElementById("alpha-gematria-input"); + gematriaResultEl = document.getElementById("alpha-gematria-result"); + gematriaBreakdownEl = document.getElementById("alpha-gematria-breakdown"); + } + + function getFallbackGematriaDb() { + return { + baseAlphabet: "abcdefghijklmnopqrstuvwxyz", + ciphers: [ + { + id: "simple-ordinal", + name: "Simple Ordinal", + description: "A=1 ... Z=26", + values: Array.from({ length: 26 }, (_, index) => index + 1) + } + ] + }; + } + + function normalizeGematriaText(value) { + return String(value || "") + .normalize("NFD") + .replace(/[\u0300-\u036f]/g, "") + .toLowerCase(); + } + + function transliterationToBaseLetters(transliteration, baseAlphabet) { + const normalized = normalizeGematriaText(transliteration); + if (!normalized) { + return ""; + } + + const primaryVariant = normalized.split(/[\/,;|]/)[0] || normalized; + const primaryLetters = [...primaryVariant].filter((char) => baseAlphabet.includes(char)); + if (primaryLetters.length) { + return primaryLetters[0]; + } + + const allLetters = [...normalized].filter((char) => baseAlphabet.includes(char)); + return allLetters[0] || ""; + } + + function addScriptCharMapEntry(map, scriptChar, mappedLetters) { + const key = String(scriptChar || "").trim(); + const value = String(mappedLetters || "").trim(); + if (!key || !value) { + return; + } + map.set(key, value); + } + + function buildGematriaScriptMap(baseAlphabet) { + const map = new Map(); + const hebrewLetters = Array.isArray(state.alphabets?.hebrew) ? state.alphabets.hebrew : []; + const greekLetters = Array.isArray(state.alphabets?.greek) ? state.alphabets.greek : []; + + hebrewLetters.forEach((entry) => { + const mapped = transliterationToBaseLetters(entry?.transliteration, baseAlphabet); + addScriptCharMapEntry(map, entry?.char, mapped); + }); + + greekLetters.forEach((entry) => { + const mapped = transliterationToBaseLetters(entry?.transliteration, baseAlphabet); + addScriptCharMapEntry(map, entry?.char, mapped); + addScriptCharMapEntry(map, entry?.charLower, mapped); + addScriptCharMapEntry(map, entry?.charFinal, mapped); + }); + + const hebrewFinalForms = { + ך: "k", + ם: "m", + ן: "n", + ף: "p", + ץ: "t" + }; + + Object.entries(hebrewFinalForms).forEach(([char, mapped]) => { + if (!map.has(char) && baseAlphabet.includes(mapped)) { + addScriptCharMapEntry(map, char, mapped); + } + }); + + if (!map.has("ς") && baseAlphabet.includes("s")) { + addScriptCharMapEntry(map, "ς", "s"); + } + + return map; + } + + function refreshGematriaScriptMap(baseAlphabet) { + state.gematria.scriptCharMap = buildGematriaScriptMap(baseAlphabet); + } + + function sanitizeGematriaDb(db) { + const baseAlphabet = String(db?.baseAlphabet || "abcdefghijklmnopqrstuvwxyz").toLowerCase(); + const ciphers = Array.isArray(db?.ciphers) + ? db.ciphers + .map((cipher) => { + const id = String(cipher?.id || "").trim(); + const name = String(cipher?.name || "").trim(); + const values = Array.isArray(cipher?.values) + ? cipher.values.map((value) => Number(value)) + : []; + + if (!id || !name || values.length !== baseAlphabet.length || values.some((value) => !Number.isFinite(value))) { + return null; + } + + return { + id, + name, + description: String(cipher?.description || "").trim(), + values + }; + }) + .filter(Boolean) + : []; + + if (!ciphers.length) { + return getFallbackGematriaDb(); + } + + return { + baseAlphabet, + ciphers + }; + } + + async function loadGematriaDb() { + if (state.gematria.db) { + return state.gematria.db; + } + + if (state.gematria.loadingPromise) { + return state.gematria.loadingPromise; + } + + state.gematria.loadingPromise = fetch("data/gematria-ciphers.json") + .then((response) => { + if (!response.ok) { + throw new Error(`Failed to load gematria ciphers (${response.status})`); + } + return response.json(); + }) + .then((db) => { + state.gematria.db = sanitizeGematriaDb(db); + return state.gematria.db; + }) + .catch(() => { + state.gematria.db = getFallbackGematriaDb(); + return state.gematria.db; + }) + .finally(() => { + state.gematria.loadingPromise = null; + }); + + return state.gematria.loadingPromise; + } + + function getActiveGematriaCipher() { + const db = state.gematria.db || getFallbackGematriaDb(); + const ciphers = Array.isArray(db.ciphers) ? db.ciphers : []; + if (!ciphers.length) { + return null; + } + + const selectedId = state.gematria.activeCipherId || ciphers[0].id; + return ciphers.find((cipher) => cipher.id === selectedId) || ciphers[0]; + } + + function renderGematriaCipherOptions() { + if (!gematriaCipherEl) { + return; + } + + const db = state.gematria.db || getFallbackGematriaDb(); + const ciphers = Array.isArray(db.ciphers) ? db.ciphers : []; + + gematriaCipherEl.innerHTML = ""; + ciphers.forEach((cipher) => { + const option = document.createElement("option"); + option.value = cipher.id; + option.textContent = cipher.name; + if (cipher.description) { + option.title = cipher.description; + } + gematriaCipherEl.appendChild(option); + }); + + const activeCipher = getActiveGematriaCipher(); + state.gematria.activeCipherId = activeCipher?.id || ""; + gematriaCipherEl.value = state.gematria.activeCipherId; + } + + function computeGematria(text, cipher, baseAlphabet) { + const normalizedInput = normalizeGematriaText(text); + const scriptMap = state.gematria.scriptCharMap instanceof Map + ? state.gematria.scriptCharMap + : new Map(); + + const letterParts = []; + let total = 0; + let count = 0; + + [...normalizedInput].forEach((char) => { + const mappedLetters = baseAlphabet.includes(char) + ? char + : (scriptMap.get(char) || ""); + + if (!mappedLetters) { + return; + } + + [...mappedLetters].forEach((mappedChar) => { + const index = baseAlphabet.indexOf(mappedChar); + if (index < 0) { + return; + } + + const value = Number(cipher.values[index]); + if (!Number.isFinite(value)) { + return; + } + + count += 1; + total += value; + letterParts.push(`${mappedChar.toUpperCase()}(${value})`); + }); + }); + + return { + total, + count, + breakdown: letterParts.join(" + ") + }; + } + + function renderGematriaResult() { + if (!gematriaResultEl || !gematriaBreakdownEl) { + return; + } + + const db = state.gematria.db || getFallbackGematriaDb(); + if (!(state.gematria.scriptCharMap instanceof Map) || !state.gematria.scriptCharMap.size) { + refreshGematriaScriptMap(db.baseAlphabet); + } + const cipher = getActiveGematriaCipher(); + if (!cipher) { + gematriaResultEl.textContent = "Total: --"; + gematriaBreakdownEl.textContent = "No ciphers available."; + return; + } + + const { total, count, breakdown } = computeGematria(state.gematria.inputText, cipher, db.baseAlphabet); + + gematriaResultEl.textContent = `Total: ${total}`; + if (!count) { + gematriaBreakdownEl.textContent = `Using ${cipher.name}. Enter English, Greek, or Hebrew letters to calculate.`; + return; + } + + gematriaBreakdownEl.textContent = `${cipher.name} · ${count} letters · ${breakdown} = ${total}`; + } + + function bindGematriaListeners() { + if (state.gematria.listenersBound || !gematriaCipherEl || !gematriaInputEl) { + return; + } + + gematriaCipherEl.addEventListener("change", () => { + state.gematria.activeCipherId = String(gematriaCipherEl.value || "").trim(); + renderGematriaResult(); + }); + + gematriaInputEl.addEventListener("input", () => { + state.gematria.inputText = gematriaInputEl.value || ""; + renderGematriaResult(); + }); + + state.gematria.listenersBound = true; + } + + function ensureGematriaCalculator() { + getElements(); + if (!gematriaCipherEl || !gematriaInputEl || !gematriaResultEl || !gematriaBreakdownEl) { + return; + } + + bindGematriaListeners(); + + if (gematriaInputEl.value !== state.gematria.inputText) { + gematriaInputEl.value = state.gematria.inputText; + } + + void loadGematriaDb().then(() => { + refreshGematriaScriptMap((state.gematria.db || getFallbackGematriaDb()).baseAlphabet); + renderGematriaCipherOptions(); + renderGematriaResult(); + }); + } + + // ── Data helpers ───────────────────────────────────────────────────── + function getLetters() { + if (!state.alphabets) return []; + if (state.activeAlphabet === "all") { + const alphabetOrder = ["hebrew", "greek", "english", "arabic", "enochian"]; + return alphabetOrder.flatMap((alphabet) => { + const rows = Array.isArray(state.alphabets?.[alphabet]) ? state.alphabets[alphabet] : []; + return rows.map((row) => ({ ...row, __alphabet: alphabet })); + }); + } + return state.alphabets[state.activeAlphabet] || []; + } + + function alphabetForLetter(letter) { + if (state.activeAlphabet === "all") { + return String(letter?.__alphabet || "").trim().toLowerCase(); + } + return state.activeAlphabet; + } + + function letterKeyByAlphabet(alphabet, letter) { + if (alphabet === "hebrew") return letter.hebrewLetterId; + if (alphabet === "greek") return letter.name; + if (alphabet === "english") return letter.letter; + if (alphabet === "arabic") return letter.name; + if (alphabet === "enochian") return letter.id; + return String(letter.index); + } + + function letterKey(letter) { + // Stable unique key per alphabet + entry + const alphabet = alphabetForLetter(letter); + const key = letterKeyByAlphabet(alphabet, letter); + if (state.activeAlphabet === "all") { + return `${alphabet}:${key}`; + } + return key; + } + + function displayGlyph(letter) { + const alphabet = alphabetForLetter(letter); + if (alphabet === "hebrew") return letter.char; + if (alphabet === "greek") return letter.char; + if (alphabet === "english") return letter.letter; + if (alphabet === "arabic") return letter.char; + if (alphabet === "enochian") return letter.char; + return "?"; + } + + function displayLabel(letter) { + const alphabet = alphabetForLetter(letter); + if (alphabet === "hebrew") return letter.name; + if (alphabet === "greek") return letter.displayName; + if (alphabet === "english") return letter.letter; + if (alphabet === "arabic") return arabicDisplayName(letter); + if (alphabet === "enochian") return letter.title; + return "?"; + } + + function displaySub(letter) { + const alphabet = alphabetForLetter(letter); + if (alphabet === "hebrew") return `${letter.transliteration} · ${letter.letterType} · ${letter.numerology}`; + if (alphabet === "greek") return `${letter.transliteration} · isopsephy ${letter.numerology}${letter.archaic ? " · archaic" : ""}`; + if (alphabet === "english") return `Pythagorean ${letter.pythagorean}`; + if (alphabet === "arabic") return `${letter.transliteration} · abjad ${letter.abjad} · ${letter.nameArabic}`; + if (alphabet === "enochian") return `${letter.transliteration} · ${Array.isArray(letter.englishLetters) ? letter.englishLetters.join("/") : ""} · value ${letter.numerology}`; + return ""; + } + + function normalizeLetterType(value) { + const key = String(value || "").trim().toLowerCase(); + if (["mother", "double", "simple"].includes(key)) { + return key; + } + return ""; + } + + function getHebrewLetterTypeMap() { + const map = new Map(); + const hebrewLetters = Array.isArray(state.alphabets?.hebrew) ? state.alphabets.hebrew : []; + hebrewLetters.forEach((entry) => { + const hebrewId = normalizeId(entry?.hebrewLetterId); + const letterType = normalizeLetterType(entry?.letterType); + if (hebrewId && letterType) { + map.set(hebrewId, letterType); + } + }); + return map; + } + + function resolveLetterType(letter) { + const direct = normalizeLetterType(letter?.letterType); + if (direct) { + return direct; + } + + const hebrewId = normalizeId(letter?.hebrewLetterId); + if (!hebrewId) { + return ""; + } + + return getHebrewLetterTypeMap().get(hebrewId) || ""; + } + + function buildLetterSearchText(letter) { + const chunks = []; + + chunks.push(String(displayLabel(letter) || "")); + chunks.push(String(displayGlyph(letter) || "")); + chunks.push(String(displaySub(letter) || "")); + chunks.push(String(letter?.transliteration || "")); + chunks.push(String(letter?.meaning || "")); + chunks.push(String(letter?.nameArabic || "")); + chunks.push(String(letter?.title || "")); + chunks.push(String(letter?.letter || "")); + chunks.push(String(letter?.displayName || "")); + chunks.push(String(letter?.name || "")); + chunks.push(String(letter?.index || "")); + chunks.push(String(resolveLetterType(letter) || "")); + + return chunks + .join(" ") + .toLowerCase(); + } + + function getFilteredLetters() { + const letters = getLetters(); + const query = String(state.filters.query || "").trim().toLowerCase(); + const letterTypeFilter = normalizeLetterType(state.filters.letterType); + const numericPosition = /^\d+$/.test(query) ? Number(query) : null; + + return letters.filter((letter) => { + if (letterTypeFilter) { + const entryType = resolveLetterType(letter); + if (entryType !== letterTypeFilter) { + return false; + } + } + + if (!query) { + return true; + } + + const index = Number(letter?.index); + if (Number.isFinite(numericPosition) && Number.isFinite(index) && index === numericPosition) { + return true; + } + + return buildLetterSearchText(letter).includes(query); + }); + } + + function syncFilterControls() { + if (searchInputEl && searchInputEl.value !== state.filters.query) { + searchInputEl.value = state.filters.query; + } + + if (typeFilterEl && typeFilterEl.value !== state.filters.letterType) { + typeFilterEl.value = state.filters.letterType; + } + + if (searchClearEl) { + const hasFilter = Boolean(String(state.filters.query || "").trim()) || Boolean(state.filters.letterType); + searchClearEl.disabled = !hasFilter; + } + } + + function applyFiltersAndRender() { + const filteredLetters = getFilteredLetters(); + const selectedInFiltered = filteredLetters.some((letter) => letterKey(letter) === state.selectedKey); + + if (!selectedInFiltered) { + state.selectedKey = filteredLetters[0] ? letterKey(filteredLetters[0]) : null; + } + + renderList(); + + const selected = filteredLetters.find((letter) => letterKey(letter) === state.selectedKey) + || filteredLetters[0] + || null; + + if (selected) { + renderDetail(selected); + return; + } + + resetDetail(); + if (detailSubEl) { + detailSubEl.textContent = "No letters match the current filter."; + } + } + + function bindFilterControls() { + if (searchInputEl) { + searchInputEl.addEventListener("input", () => { + state.filters.query = String(searchInputEl.value || ""); + syncFilterControls(); + applyFiltersAndRender(); + }); + } + + if (typeFilterEl) { + typeFilterEl.addEventListener("change", () => { + state.filters.letterType = normalizeLetterType(typeFilterEl.value); + syncFilterControls(); + applyFiltersAndRender(); + }); + } + + if (searchClearEl) { + searchClearEl.addEventListener("click", () => { + state.filters.query = ""; + state.filters.letterType = ""; + syncFilterControls(); + applyFiltersAndRender(); + }); + } + } + + function enochianGlyphKey(letter) { + return String(letter?.id || letter?.char || "").trim().toUpperCase(); + } + + function enochianGlyphCode(letter) { + const key = enochianGlyphKey(letter); + return key ? key.codePointAt(0) || 0 : 0; + } + + function enochianGlyphUrl(letter) { + const code = enochianGlyphCode(letter); + return code ? `asset/img/enochian/char(${code}).png` : ""; + } + + function enochianGlyphImageHtml(letter, className) { + const src = enochianGlyphUrl(letter); + const key = enochianGlyphKey(letter) || "?"; + if (!src) { + return `${key}`; + } + return `Enochian ${key}`; + } + + // ── List rendering ──────────────────────────────────────────────────── + function renderList() { + if (!listEl) return; + const allLetters = getLetters(); + const letters = getFilteredLetters(); + if (countEl) { + countEl.textContent = letters.length === allLetters.length + ? `${letters.length}` + : `${letters.length}/${allLetters.length}`; + } + + listEl.innerHTML = ""; + letters.forEach((letter) => { + const key = letterKey(letter); + const item = document.createElement("div"); + item.className = "planet-list-item alpha-list-item" + (key === state.selectedKey ? " is-selected" : ""); + item.setAttribute("role", "option"); + item.setAttribute("aria-selected", key === state.selectedKey ? "true" : "false"); + item.dataset.key = key; + + const glyph = document.createElement("span"); + const alphabet = alphabetForLetter(letter); + const glyphVariantClass = alphabet === "arabic" + ? " alpha-list-glyph--arabic" + : alphabet === "enochian" + ? " alpha-list-glyph--enochian" + : ""; + glyph.className = "alpha-list-glyph" + glyphVariantClass; + if (alphabet === "enochian") { + const image = document.createElement("img"); + image.className = "alpha-enochian-glyph-img alpha-enochian-glyph-img--list"; + image.src = enochianGlyphUrl(letter); + image.alt = `Enochian ${enochianGlyphKey(letter) || "?"}`; + image.loading = "lazy"; + image.decoding = "async"; + image.addEventListener("error", () => { + glyph.textContent = enochianGlyphKey(letter) || "?"; + }); + glyph.appendChild(image); + } else { + glyph.textContent = displayGlyph(letter); + } + + const meta = document.createElement("span"); + meta.className = "alpha-list-meta"; + const alphaLabel = alphabet ? `${cap(alphabet)} · ` : ""; + meta.innerHTML = `${alphaLabel}${displayLabel(letter)}
${displaySub(letter)}`; + + item.appendChild(glyph); + item.appendChild(meta); + item.addEventListener("click", () => selectLetter(key)); + listEl.appendChild(item); + }); + } + + // ── Detail rendering ────────────────────────────────────────────────── + function renderDetail(letter) { + if (!detailNameEl) return; + + const alphabet = alphabetForLetter(letter); + + detailNameEl.replaceChildren(); + if (alphabet === "enochian") { + const image = document.createElement("img"); + image.className = "alpha-enochian-glyph-img alpha-enochian-glyph-img--detail"; + image.src = enochianGlyphUrl(letter); + image.alt = `Enochian ${enochianGlyphKey(letter) || "?"}`; + image.loading = "lazy"; + image.decoding = "async"; + image.addEventListener("error", () => { + detailNameEl.textContent = enochianGlyphKey(letter) || "?"; + }); + detailNameEl.appendChild(image); + } else { + detailNameEl.textContent = displayGlyph(letter); + } + detailNameEl.classList.add("alpha-detail-glyph"); + detailNameEl.classList.toggle("alpha-detail-glyph--arabic", alphabet === "arabic"); + detailNameEl.classList.toggle("alpha-detail-glyph--enochian", alphabet === "enochian"); + + if (alphabet === "hebrew") renderHebrewDetail(letter); + else if (alphabet === "greek") renderGreekDetail(letter); + else if (alphabet === "english") renderEnglishDetail(letter); + else if (alphabet === "arabic") renderArabicDetail(letter); + else if (alphabet === "enochian") renderEnochianDetail(letter); + } + + function card(title, bodyHTML) { + return `
${title}
${bodyHTML}
`; + } + + const PLANET_SYMBOLS = { + mercury: "☿︎", luna: "☾︎", venus: "♀︎", sol: "☉︎", + jupiter: "♃︎", mars: "♂︎", saturn: "♄︎" + }; + + const ZODIAC_SYMBOLS = { + aries: "♈︎", taurus: "♉︎", gemini: "♊︎", cancer: "♋︎", + leo: "♌︎", virgo: "♍︎", libra: "♎︎", scorpio: "♏︎", + sagittarius: "♐︎", capricorn: "♑︎", aquarius: "♒︎", pisces: "♓︎" + }; + + const HEBREW_DOUBLE_DUALITY = { + bet: { left: "Life", right: "Death" }, + gimel: { left: "Peace", right: "War" }, + dalet: { left: "Wisdom", right: "Folly" }, + kaf: { left: "Wealth", right: "Poverty" }, + pe: { left: "Beauty", right: "Ugliness" }, + resh: { left: "Fruitfulness", right: "Sterility" }, + tav: { left: "Dominion", right: "Slavery" } + }; + + function normalizeId(value) { + return String(value || "").trim().toLowerCase(); + } + + function normalizeSoulId(value) { + return String(value || "") + .trim() + .toLowerCase() + .replace(/[^a-z]/g, ""); + } + + function buildFourWorldLayersFromDataset(magickDataset) { + const worlds = magickDataset?.grouped?.kabbalah?.fourWorlds; + const souls = magickDataset?.grouped?.kabbalah?.souls; + const paths = Array.isArray(magickDataset?.grouped?.kabbalah?.["kabbalah-tree"]?.paths) + ? magickDataset.grouped.kabbalah["kabbalah-tree"].paths + : []; + + if (!worlds || typeof worlds !== "object") { + return []; + } + + const soulAliases = { + chiah: "chaya", + chaya: "chaya", + neshamah: "neshama", + neshama: "neshama", + ruach: "ruach", + nephesh: "nephesh" + }; + + const pathByLetterId = new Map(); + paths.forEach((path) => { + const letterId = normalizeLetterId(path?.hebrewLetter?.transliteration || path?.hebrewLetter?.char); + const pathNo = Number(path?.pathNumber); + if (!letterId || !Number.isFinite(pathNo) || pathByLetterId.has(letterId)) { + return; + } + pathByLetterId.set(letterId, pathNo); + }); + + const worldOrder = ["atzilut", "briah", "yetzirah", "assiah"]; + + return worldOrder + .map((worldId) => { + const world = worlds?.[worldId]; + if (!world || typeof world !== "object") { + return null; + } + + const tetragrammaton = world?.tetragrammaton && typeof world.tetragrammaton === "object" + ? world.tetragrammaton + : {}; + + const letterId = normalizeLetterId(tetragrammaton?.hebrewLetterId); + const rawSoulId = normalizeSoulId(world?.soulId); + const soulId = soulAliases[rawSoulId] || rawSoulId; + const soul = souls?.[soulId] && typeof souls[soulId] === "object" + ? souls[soulId] + : null; + + const slot = tetragrammaton?.isFinal + ? `${String(tetragrammaton?.slot || "Heh")} (final)` + : String(tetragrammaton?.slot || ""); + + return { + slot, + letterChar: String(tetragrammaton?.letterChar || ""), + hebrewLetterId: letterId, + world: String(world?.name?.roman || titleCase(worldId)), + worldLayer: String(world?.worldLayer?.en || world?.desc?.en || ""), + worldDescription: String(world?.worldDescription?.en || ""), + soulLayer: String(soul?.name?.roman || titleCase(rawSoulId || soulId)), + soulTitle: String(soul?.title?.en || titleCase(soul?.name?.en || "")), + soulDescription: String(soul?.desc?.en || ""), + pathNumber: pathByLetterId.get(letterId) || null + }; + }) + .filter(Boolean); + } + + function buildMonthReferencesByHebrew(referenceData, alphabets) { + const map = new Map(); + const months = Array.isArray(referenceData?.calendarMonths) ? referenceData.calendarMonths : []; + const holidays = Array.isArray(referenceData?.celestialHolidays) ? referenceData.celestialHolidays : []; + const monthById = new Map(months.map((month) => [month.id, month])); + const hebrewLetters = Array.isArray(alphabets?.hebrew) ? alphabets.hebrew : []; + + const profiles = hebrewLetters + .filter((letter) => letter?.hebrewLetterId) + .map((letter) => { + const astrologyType = normalizeId(letter?.astrology?.type); + const astrologyName = normalizeId(letter?.astrology?.name); + return { + hebrewLetterId: normalizeId(letter.hebrewLetterId), + tarotTrumpNumber: Number.isFinite(Number(letter?.tarot?.trumpNumber)) + ? Number(letter.tarot.trumpNumber) + : null, + kabbalahPathNumber: Number.isFinite(Number(letter?.kabbalahPathNumber)) + ? Number(letter.kabbalahPathNumber) + : null, + planetId: astrologyType === "planet" ? astrologyName : "", + zodiacSignId: astrologyType === "zodiac" ? astrologyName : "" + }; + }); + + function parseMonthDayToken(value) { + const text = String(value || "").trim(); + const match = text.match(/^(\d{1,2})-(\d{1,2})$/); + if (!match) { + return null; + } + + const monthNo = Number(match[1]); + const dayNo = Number(match[2]); + if (!Number.isInteger(monthNo) || !Number.isInteger(dayNo) || monthNo < 1 || monthNo > 12 || dayNo < 1 || dayNo > 31) { + return null; + } + + return { month: monthNo, day: dayNo }; + } + + function parseMonthDayTokensFromText(value) { + const text = String(value || ""); + const matches = [...text.matchAll(/(\d{1,2})-(\d{1,2})/g)]; + return matches + .map((match) => ({ month: Number(match[1]), day: Number(match[2]) })) + .filter((token) => Number.isInteger(token.month) && Number.isInteger(token.day) && token.month >= 1 && token.month <= 12 && token.day >= 1 && token.day <= 31); + } + + function toDateToken(token, year) { + if (!token) { + return null; + } + return new Date(year, token.month - 1, token.day, 12, 0, 0, 0); + } + + function splitMonthDayRangeByMonth(startToken, endToken) { + const startDate = toDateToken(startToken, 2025); + const endBase = toDateToken(endToken, 2025); + if (!startDate || !endBase) { + return []; + } + + const wrapsYear = endBase.getTime() < startDate.getTime(); + const endDate = wrapsYear ? toDateToken(endToken, 2026) : endBase; + if (!endDate) { + return []; + } + + const segments = []; + let cursor = new Date(startDate); + while (cursor.getTime() <= endDate.getTime()) { + const monthEnd = new Date(cursor.getFullYear(), cursor.getMonth() + 1, 0, 12, 0, 0, 0); + const segmentEnd = monthEnd.getTime() < endDate.getTime() ? monthEnd : endDate; + + segments.push({ + monthNo: cursor.getMonth() + 1, + startDay: cursor.getDate(), + endDay: segmentEnd.getDate() + }); + + cursor = new Date(segmentEnd.getFullYear(), segmentEnd.getMonth(), segmentEnd.getDate() + 1, 12, 0, 0, 0); + } + + return segments; + } + + function tokenToString(monthNo, dayNo) { + return `${String(monthNo).padStart(2, "0")}-${String(dayNo).padStart(2, "0")}`; + } + + function formatRangeLabel(monthName, startDay, endDay) { + if (!Number.isFinite(startDay) || !Number.isFinite(endDay)) { + return monthName; + } + if (startDay === endDay) { + return `${monthName} ${startDay}`; + } + return `${monthName} ${startDay}-${endDay}`; + } + + function resolveRangeForMonth(month, options = {}) { + const monthOrder = Number(month?.order); + const monthStart = parseMonthDayToken(month?.start); + const monthEnd = parseMonthDayToken(month?.end); + if (!Number.isFinite(monthOrder) || !monthStart || !monthEnd) { + return { + startToken: String(month?.start || "").trim() || null, + endToken: String(month?.end || "").trim() || null, + label: month?.name || month?.id || "", + isFullMonth: true + }; + } + + let startToken = parseMonthDayToken(options.startToken); + let endToken = parseMonthDayToken(options.endToken); + + if (!startToken || !endToken) { + const tokens = parseMonthDayTokensFromText(options.rawDateText); + if (tokens.length >= 2) { + startToken = tokens[0]; + endToken = tokens[1]; + } else if (tokens.length === 1) { + startToken = tokens[0]; + endToken = tokens[0]; + } + } + + if (!startToken || !endToken) { + startToken = monthStart; + endToken = monthEnd; + } + + const segments = splitMonthDayRangeByMonth(startToken, endToken); + const segment = segments.find((entry) => entry.monthNo === monthOrder) || null; + + const useStart = segment ? { month: monthOrder, day: segment.startDay } : startToken; + const useEnd = segment ? { month: monthOrder, day: segment.endDay } : endToken; + const startText = tokenToString(useStart.month, useStart.day); + const endText = tokenToString(useEnd.month, useEnd.day); + const isFullMonth = startText === month.start && endText === month.end; + + return { + startToken: startText, + endToken: endText, + label: isFullMonth + ? (month.name || month.id) + : formatRangeLabel(month.name || month.id, useStart.day, useEnd.day), + isFullMonth + }; + } + + function pushRef(hebrewLetterId, month, options = {}) { + if (!hebrewLetterId || !month?.id) { + return; + } + + if (!map.has(hebrewLetterId)) { + map.set(hebrewLetterId, []); + } + + const rows = map.get(hebrewLetterId); + const range = resolveRangeForMonth(month, options); + const rowKey = `${month.id}|${range.startToken || ""}|${range.endToken || ""}`; + if (rows.some((entry) => entry.key === rowKey)) { + return; + } + + rows.push({ + id: month.id, + name: month.name || month.id, + order: Number.isFinite(Number(month.order)) ? Number(month.order) : 999, + label: range.label, + startToken: range.startToken, + endToken: range.endToken, + isFullMonth: range.isFullMonth, + key: rowKey + }); + } + + function collectRefs(associations, month, options = {}) { + if (!associations || typeof associations !== "object") { + return; + } + + const assocHebrewId = normalizeId(associations.hebrewLetterId); + const assocTarotTrump = Number.isFinite(Number(associations.tarotTrumpNumber)) + ? Number(associations.tarotTrumpNumber) + : null; + const assocPath = Number.isFinite(Number(associations.kabbalahPathNumber)) + ? Number(associations.kabbalahPathNumber) + : null; + const assocPlanetId = normalizeId(associations.planetId); + const assocSignId = normalizeId(associations.zodiacSignId); + + profiles.forEach((profile) => { + if (!profile.hebrewLetterId) { + return; + } + + const matchesDirect = assocHebrewId && assocHebrewId === profile.hebrewLetterId; + const matchesTarot = assocTarotTrump != null && profile.tarotTrumpNumber === assocTarotTrump; + const matchesPath = assocPath != null && profile.kabbalahPathNumber === assocPath; + const matchesPlanet = profile.planetId && assocPlanetId && profile.planetId === assocPlanetId; + const matchesZodiac = profile.zodiacSignId && assocSignId && profile.zodiacSignId === assocSignId; + + if (matchesDirect || matchesTarot || matchesPath || matchesPlanet || matchesZodiac) { + pushRef(profile.hebrewLetterId, month, options); + } + }); + } + + months.forEach((month) => { + collectRefs(month?.associations, month); + + const events = Array.isArray(month?.events) ? month.events : []; + events.forEach((event) => { + collectRefs(event?.associations, month, { + rawDateText: event?.dateRange || event?.date || "" + }); + }); + }); + + holidays.forEach((holiday) => { + const month = monthById.get(holiday?.monthId); + if (!month) { + return; + } + collectRefs(holiday?.associations, month, { + rawDateText: holiday?.dateRange || holiday?.date || "" + }); + }); + + map.forEach((rows, key) => { + const preciseMonthIds = new Set( + rows + .filter((entry) => !entry.isFullMonth) + .map((entry) => entry.id) + ); + + const filtered = rows.filter((entry) => { + if (!entry.isFullMonth) { + return true; + } + return !preciseMonthIds.has(entry.id); + }); + + filtered.sort((left, right) => { + if (left.order !== right.order) { + return left.order - right.order; + } + + const startLeft = parseMonthDayToken(left.startToken); + const startRight = parseMonthDayToken(right.startToken); + const dayLeft = startLeft ? startLeft.day : 999; + const dayRight = startRight ? startRight.day : 999; + if (dayLeft !== dayRight) { + return dayLeft - dayRight; + } + + return String(left.label || left.name || "").localeCompare(String(right.label || right.name || "")); + }); + + map.set(key, filtered); + }); + + return map; + } + + function createEmptyCubeRefs() { + return { + hebrewPlacementById: new Map(), + signPlacementById: new Map(), + planetPlacementById: new Map(), + pathPlacementByNo: new Map() + }; + } + + function normalizeLetterId(value) { + const key = normalizeId(value).replace(/[^a-z]/g, ""); + const aliases = { + aleph: "alef", + beth: "bet", + zain: "zayin", + cheth: "het", + chet: "het", + daleth: "dalet", + teth: "tet", + peh: "pe", + tzaddi: "tsadi", + tzadi: "tsadi", + tzade: "tsadi", + tsaddi: "tsadi", + qoph: "qof", + taw: "tav", + tau: "tav" + }; + return aliases[key] || key; + } + + function edgeWalls(edge) { + const explicitWalls = Array.isArray(edge?.walls) + ? edge.walls.map((wallId) => normalizeId(wallId)).filter(Boolean) + : []; + + if (explicitWalls.length >= 2) { + return explicitWalls.slice(0, 2); + } + + return normalizeId(edge?.id) + .split("-") + .map((wallId) => normalizeId(wallId)) + .filter(Boolean) + .slice(0, 2); + } + + function edgeLabel(edge) { + const explicitName = String(edge?.name || "").trim(); + if (explicitName) { + return explicitName; + } + + return edgeWalls(edge) + .map((part) => cap(part)) + .join(" "); + } + + function resolveCubeDirectionLabel(wallId, edge) { + const normalizedWallId = normalizeId(wallId); + const edgeId = normalizeId(edge?.id); + if (!normalizedWallId || !edgeId) { + return ""; + } + + const cubeUi = window.CubeSectionUi; + if (cubeUi && typeof cubeUi.getEdgeDirectionLabelForWall === "function") { + const directionLabel = String(cubeUi.getEdgeDirectionLabelForWall(normalizedWallId, edgeId) || "").trim(); + if (directionLabel) { + return directionLabel; + } + } + + return edgeLabel(edge); + } + + function makeCubePlacement(wall, edge = null) { + const wallId = normalizeId(wall?.id); + const edgeId = normalizeId(edge?.id); + return { + wallId, + edgeId, + wallName: wall?.name || cap(wallId), + edgeName: resolveCubeDirectionLabel(wallId, edge) + }; + } + + function setPlacementIfMissing(map, key, placement) { + if (!key || map.has(key) || !placement?.wallId) { + return; + } + map.set(key, placement); + } + + function buildCubeReferences(magickDataset) { + const refs = createEmptyCubeRefs(); + const cube = magickDataset?.grouped?.kabbalah?.cube || {}; + const walls = Array.isArray(cube?.walls) + ? cube.walls + : []; + const edges = Array.isArray(cube?.edges) + ? cube.edges + : []; + const paths = Array.isArray(magickDataset?.grouped?.kabbalah?.["kabbalah-tree"]?.paths) + ? magickDataset.grouped.kabbalah["kabbalah-tree"].paths + : []; + + const wallById = new Map( + walls.map((wall) => [normalizeId(wall?.id), wall]) + ); + const firstEdgeByWallId = new Map(); + + const pathByLetterId = new Map( + paths + .map((path) => [normalizeLetterId(path?.hebrewLetter?.transliteration), path]) + .filter(([letterId]) => Boolean(letterId)) + ); + + edges.forEach((edge) => { + edgeWalls(edge).forEach((wallId) => { + if (!firstEdgeByWallId.has(wallId)) { + firstEdgeByWallId.set(wallId, edge); + } + }); + }); + + walls.forEach((wall) => { + // each wall has a "face" letter; when we build a cube reference for that + // letter we want the label to read “Face” rather than arbitrarily using + // the first edge we encounter on that wall. previously we always + // computed `placementEdge` from the first edge, which produced labels + // like “East Wall – North” for the dalet face. instead we create a + // custom placement object for face letters with an empty edge id and a + // fixed edgeName of “Face”. + const wallHebrewLetterId = normalizeLetterId(wall?.hebrewLetterId || wall?.associations?.hebrewLetterId); + + let wallPlacement; + if (wallHebrewLetterId) { + // face letter; label should emphasise the face rather than a direction + wallPlacement = { + wallId: normalizeId(wall?.id), + edgeId: "", + wallName: wall?.name || cap(normalizeId(wall?.id)), + edgeName: "Face" + }; + } else { + // fall back to normal edge-based placement + const placementEdge = firstEdgeByWallId.get(normalizeId(wall?.id)) || null; + wallPlacement = makeCubePlacement(wall, placementEdge); + } + + setPlacementIfMissing(refs.hebrewPlacementById, wallHebrewLetterId, wallPlacement); + + const wallPath = pathByLetterId.get(wallHebrewLetterId) || null; + const wallSignId = normalizeId(wallPath?.astrology?.type) === "zodiac" + ? normalizeId(wallPath?.astrology?.name) + : ""; + setPlacementIfMissing(refs.signPlacementById, wallSignId, wallPlacement); + + const wallPathNo = Number(wallPath?.pathNumber); + if (Number.isFinite(wallPathNo)) { + setPlacementIfMissing(refs.pathPlacementByNo, wallPathNo, wallPlacement); + } + + const wallPlanet = normalizeId(wall?.associations?.planetId); + if (wallPlanet) { + setPlacementIfMissing(refs.planetPlacementById, wallPlanet, wallPlacement); + } + }); + + edges.forEach((edge) => { + const wallsForEdge = edgeWalls(edge); + const primaryWallId = wallsForEdge[0]; + const primaryWall = wallById.get(primaryWallId) || { + id: primaryWallId, + name: cap(primaryWallId) + }; + + const placement = makeCubePlacement(primaryWall, edge); + const hebrewLetterId = normalizeLetterId(edge?.hebrewLetterId || edge?.associations?.hebrewLetterId); + setPlacementIfMissing(refs.hebrewPlacementById, hebrewLetterId, placement); + + const path = pathByLetterId.get(hebrewLetterId) || null; + const signId = normalizeId(path?.astrology?.type) === "zodiac" + ? normalizeId(path?.astrology?.name) + : ""; + setPlacementIfMissing(refs.signPlacementById, signId, placement); + + const pathNo = Number(path?.pathNumber); + if (Number.isFinite(pathNo)) { + setPlacementIfMissing(refs.pathPlacementByNo, pathNo, placement); + } + }); + + return refs; + } + + function getCubePlacementForHebrewLetter(hebrewLetterId, pathNo = null) { + const normalizedLetterId = normalizeId(hebrewLetterId); + if (normalizedLetterId && state.cubeRefs.hebrewPlacementById.has(normalizedLetterId)) { + return state.cubeRefs.hebrewPlacementById.get(normalizedLetterId); + } + + const numericPath = Number(pathNo); + if (Number.isFinite(numericPath) && state.cubeRefs.pathPlacementByNo.has(numericPath)) { + return state.cubeRefs.pathPlacementByNo.get(numericPath); + } + + return null; + } + + function getCubePlacementForPlanet(planetId) { + const normalizedPlanetId = normalizeId(planetId); + return normalizedPlanetId ? state.cubeRefs.planetPlacementById.get(normalizedPlanetId) || null : null; + } + + function getCubePlacementForSign(signId) { + const normalizedSignId = normalizeId(signId); + return normalizedSignId ? state.cubeRefs.signPlacementById.get(normalizedSignId) || null : null; + } + + function cubePlacementLabel(placement) { + const wallName = placement?.wallName || "Wall"; + const edgeName = placement?.edgeName || "Direction"; + return `Cube: ${wallName} Wall - ${edgeName}`; + } + + function cubePlacementBtn(placement, fallbackDetail = null) { + if (!placement) { + return ""; + } + + const detail = { + "wall-id": placement.wallId, + "edge-id": placement.edgeId + }; + + if (fallbackDetail && typeof fallbackDetail === "object") { + Object.entries(fallbackDetail).forEach(([key, value]) => { + if (value !== undefined && value !== null && value !== "") { + detail[key] = value; + } + }); + } + + return navBtn(cubePlacementLabel(placement), "nav:cube", detail); + } + + function cap(s) { return s ? s.charAt(0).toUpperCase() + s.slice(1) : ""; } + + function renderAstrologyCard(astrology) { + if (!astrology) return ""; + const { type, name } = astrology; + const id = (name || "").toLowerCase(); + + if (type === "planet") { + const sym = PLANET_SYMBOLS[id] || ""; + const cubePlacement = getCubePlacementForPlanet(id); + const cubeBtn = cubePlacementBtn(cubePlacement, { "planet-id": id }); + return card("Astrology", ` +
+
Type
Planet
+
Ruler
${sym} ${cap(id)}
+
+
+ + ${cubeBtn} +
+ `); + } + if (type === "zodiac") { + const sym = ZODIAC_SYMBOLS[id] || ""; + const cubePlacement = getCubePlacementForSign(id); + const cubeBtn = cubePlacementBtn(cubePlacement, { "sign-id": id }); + return card("Astrology", ` +
+
Type
Zodiac Sign
+
Sign
${sym} ${cap(id)}
+
+
+ + ${cubeBtn} +
+ `); + } + if (type === "element") { + const elemEmoji = { air: "💨", water: "💧", fire: "🔥", earth: "🌍" }; + return card("Astrology", ` +
+
Type
Element
+
Element
${elemEmoji[id] || ""} ${cap(id)}
+
+ `); + } + return card("Astrology", ` +
+
Type
${cap(type)}
+
Name
${cap(name)}
+
+ `); + } + function navBtn(label, event, detail) { + const attrs = Object.entries(detail).map(([k, v]) => `data-${k}="${v}"`).join(" "); + return ``; + } + + function computeDigitalRoot(value) { + let current = Math.abs(Math.trunc(Number(value))); + if (!Number.isFinite(current)) { + return null; + } + + while (current >= 10) { + current = String(current) + .split("") + .reduce((sum, digit) => sum + Number(digit), 0); + } + + return current; + } + + function describeDigitalRootReduction(value, digitalRoot) { + const normalized = Math.abs(Math.trunc(Number(value))); + if (!Number.isFinite(normalized) || !Number.isFinite(digitalRoot)) { + return ""; + } + + if (normalized < 10) { + return String(normalized); + } + + return `${String(normalized).split("").join(" + ")} = ${digitalRoot}`; + } + + function renderPositionDigitalRootCard(letter, alphabet, orderLabel) { + const index = Number(letter?.index); + if (!Number.isFinite(index)) { + return ""; + } + + const position = Math.trunc(index); + if (position <= 0) { + return ""; + } + + const digitalRoot = computeDigitalRoot(position); + if (!Number.isFinite(digitalRoot)) { + return ""; + } + + const entries = Array.isArray(state.alphabets?.[alphabet]) ? state.alphabets[alphabet] : []; + const countText = entries.length ? ` of ${entries.length}` : ""; + const orderText = orderLabel ? ` (${orderLabel})` : ""; + const reductionText = describeDigitalRootReduction(position, digitalRoot); + const openNumberBtn = navBtn(`View Number ${digitalRoot}`, "nav:number", { value: digitalRoot }); + + return card("Position Digital Root", ` +
+
Position
#${position}${countText}${orderText}
+
Digital Root
${digitalRoot}${reductionText ? ` (${reductionText})` : ""}
+
+
${openNumberBtn}
+ `); + } + + function monthRefsForLetter(letter) { + const hebrewLetterId = normalizeId(letter?.hebrewLetterId); + if (!hebrewLetterId) { + return []; + } + return state.monthRefsByHebrewId.get(hebrewLetterId) || []; + } + + function calendarMonthsCard(monthRefs, titleLabel) { + if (!monthRefs.length) { + return ""; + } + + const monthButtons = monthRefs + .map((month) => navBtn(month.label || month.name, "nav:calendar-month", { "month-id": month.id })) + .join(""); + + return card("Calendar Months", ` +
${titleLabel}
+
${monthButtons}
+ `); + } + + function renderHebrewDualityCard(letter) { + const duality = HEBREW_DOUBLE_DUALITY[normalizeId(letter?.hebrewLetterId)]; + if (!duality) { + return ""; + } + + return card("Duality", ` +
+
Polarity
${duality.left} / ${duality.right}
+
+ `); + } + + function renderHebrewFourWorldsCard(letter) { + const letterId = normalizeLetterId(letter?.hebrewLetterId || letter?.transliteration || letter?.char); + if (!letterId) { + return ""; + } + + const rows = (Array.isArray(state.fourWorldLayers) ? state.fourWorldLayers : []) + .filter((entry) => entry?.hebrewLetterId === letterId); + + if (!rows.length) { + return ""; + } + + const body = rows.map((entry) => { + const pathBtn = Number.isFinite(Number(entry?.pathNumber)) + ? navBtn(`View Path ${entry.pathNumber}`, "nav:kabbalah-path", { "path-no": Number(entry.pathNumber) }) + : ""; + + return ` +
+
+ ${entry.slot}: ${entry.letterChar} — ${entry.world} + ${entry.soulLayer} +
+
${entry.worldLayer}${entry.worldDescription ? ` · ${entry.worldDescription}` : ""}
+
${entry.soulLayer}${entry.soulTitle ? ` — ${entry.soulTitle}` : ""}${entry.soulDescription ? `: ${entry.soulDescription}` : ""}
+
${pathBtn}
+
+ `; + }).join(""); + + return card("Qabalistic Worlds & Soul Layers", `
${body}
`); + } + + function normalizeLatinLetter(value) { + return String(value || "") + .trim() + .toUpperCase() + .replace(/[^A-Z]/g, ""); + } + + function extractEnglishLetterRefs(value) { + if (Array.isArray(value)) { + return [...new Set(value.map((entry) => normalizeLatinLetter(entry)).filter(Boolean))]; + } + + return [...new Set( + String(value || "") + .split(/[\s,;|\/]+/) + .map((entry) => normalizeLatinLetter(entry)) + .filter(Boolean) + )]; + } + + function renderAlphabetEquivalentCard(activeAlphabet, letter) { + const hebrewLetters = Array.isArray(state.alphabets?.hebrew) ? state.alphabets.hebrew : []; + const greekLetters = Array.isArray(state.alphabets?.greek) ? state.alphabets.greek : []; + const englishLetters = Array.isArray(state.alphabets?.english) ? state.alphabets.english : []; + const arabicLetters = Array.isArray(state.alphabets?.arabic) ? state.alphabets.arabic : []; + const enochianLetters = Array.isArray(state.alphabets?.enochian) ? state.alphabets.enochian : []; + const linkedHebrewIds = new Set(); + const linkedEnglishLetters = new Set(); + const buttons = []; + + function addHebrewId(value) { + const id = normalizeId(value); + if (id) { + linkedHebrewIds.add(id); + } + } + + function addEnglishLetter(value) { + const code = normalizeLatinLetter(value); + if (!code) { + return; + } + + linkedEnglishLetters.add(code); + englishLetters + .filter((entry) => normalizeLatinLetter(entry?.letter) === code) + .forEach((entry) => addHebrewId(entry?.hebrewLetterId)); + } + + if (activeAlphabet === "hebrew") { + addHebrewId(letter?.hebrewLetterId); + } else if (activeAlphabet === "greek") { + addHebrewId(letter?.hebrewLetterId); + englishLetters + .filter((entry) => normalizeId(entry?.greekEquivalent) === normalizeId(letter?.name)) + .forEach((entry) => addEnglishLetter(entry?.letter)); + } else if (activeAlphabet === "english") { + addEnglishLetter(letter?.letter); + addHebrewId(letter?.hebrewLetterId); + } else if (activeAlphabet === "arabic") { + addHebrewId(letter?.hebrewLetterId); + } else if (activeAlphabet === "enochian") { + extractEnglishLetterRefs(letter?.englishLetters).forEach((code) => addEnglishLetter(code)); + addHebrewId(letter?.hebrewLetterId); + } + + if (!linkedHebrewIds.size && !linkedEnglishLetters.size) { + return ""; + } + + const activeHebrewKey = normalizeId(letter?.hebrewLetterId); + const activeGreekKey = normalizeId(letter?.name); + const activeEnglishKey = normalizeLatinLetter(letter?.letter); + const activeArabicKey = normalizeId(letter?.name); + const activeEnochianKey = normalizeId(letter?.id || letter?.char || letter?.title); + + hebrewLetters.forEach((heb) => { + const key = normalizeId(heb?.hebrewLetterId); + if (!key || !linkedHebrewIds.has(key)) { + return; + } + if (activeAlphabet === "hebrew" && key === activeHebrewKey) { + return; + } + + buttons.push(``); + }); + + greekLetters.forEach((grk) => { + const key = normalizeId(grk?.name); + const viaHebrew = linkedHebrewIds.has(normalizeId(grk?.hebrewLetterId)); + const viaEnglish = englishLetters.some((eng) => ( + linkedEnglishLetters.has(normalizeLatinLetter(eng?.letter)) + && normalizeId(eng?.greekEquivalent) === key + )); + if (!(viaHebrew || viaEnglish)) { + return; + } + if (activeAlphabet === "greek" && key === activeGreekKey) { + return; + } + + buttons.push(``); + }); + + englishLetters.forEach((eng) => { + const key = normalizeLatinLetter(eng?.letter); + const viaLetter = linkedEnglishLetters.has(key); + const viaHebrew = linkedHebrewIds.has(normalizeId(eng?.hebrewLetterId)); + if (!(viaLetter || viaHebrew)) { + return; + } + if (activeAlphabet === "english" && key === activeEnglishKey) { + return; + } + + buttons.push(``); + }); + + arabicLetters.forEach((arb) => { + const key = normalizeId(arb?.name); + if (!linkedHebrewIds.has(normalizeId(arb?.hebrewLetterId))) { + return; + } + if (activeAlphabet === "arabic" && key === activeArabicKey) { + return; + } + + buttons.push(``); + }); + + enochianLetters.forEach((eno) => { + const key = normalizeId(eno?.id || eno?.char || eno?.title); + const englishRefs = extractEnglishLetterRefs(eno?.englishLetters); + const viaHebrew = linkedHebrewIds.has(normalizeId(eno?.hebrewLetterId)); + const viaEnglish = englishRefs.some((code) => linkedEnglishLetters.has(code)); + if (!(viaHebrew || viaEnglish)) { + return; + } + if (activeAlphabet === "enochian" && key === activeEnochianKey) { + return; + } + + buttons.push(``); + }); + + if (!buttons.length) { + return ""; + } + + return card("ALPHABET EQUIVALENT", `
${buttons.join("")}
`); + } + + function renderHebrewDetail(letter) { + detailSubEl.textContent = `${letter.name} — ${letter.transliteration}`; + detailBodyEl.innerHTML = ""; + + const sections = []; + + // Basics + sections.push(card("Letter Details", ` +
+
Character
${letter.char}
+
Name
${letter.name}
+
Transliteration
${letter.transliteration}
+
Meaning
${letter.meaning}
+
Gematria Value
${letter.numerology}
+
Letter Type
${letter.letterType}
+
Position
#${letter.index} of 22
+
+ `)); + + const positionRootCard = renderPositionDigitalRootCard(letter, "hebrew"); + if (positionRootCard) { + sections.push(positionRootCard); + } + + if (letter.letterType === "double") { + const dualityCard = renderHebrewDualityCard(letter); + if (dualityCard) { + sections.push(dualityCard); + } + } + + const fourWorldsCard = renderHebrewFourWorldsCard(letter); + if (fourWorldsCard) { + sections.push(fourWorldsCard); + } + + // Astrology + if (letter.astrology) { + sections.push(renderAstrologyCard(letter.astrology)); + } + + // Kabbalah Path + Tarot + if (letter.kabbalahPathNumber) { + const tarotPart = letter.tarot + ? `
Tarot Card
${letter.tarot.card} (Trump ${letter.tarot.trumpNumber})
` + : ""; + const kabBtn = navBtn("View Kabbalah Path", "tarot:view-kab-path", { "path-number": letter.kabbalahPathNumber }); + const tarotBtn = letter.tarot + ? navBtn("View Tarot Card", "kab:view-trump", { "trump-number": letter.tarot.trumpNumber }) + : ""; + const cubePlacement = getCubePlacementForHebrewLetter(letter.hebrewLetterId, letter.kabbalahPathNumber); + const cubeBtn = cubePlacementBtn(cubePlacement, { + "hebrew-letter-id": letter.hebrewLetterId, + "path-no": letter.kabbalahPathNumber + }); + sections.push(card("Kabbalah & Tarot", ` +
+
Path Number
${letter.kabbalahPathNumber}
+ ${tarotPart} +
+
${kabBtn}${tarotBtn}${cubeBtn}
+ `)); + } + + const monthRefs = monthRefsForLetter(letter); + const monthCard = calendarMonthsCard(monthRefs, `Calendar correspondences linked to ${letter.name}.`); + if (monthCard) { + sections.push(monthCard); + } + + const equivalentsCard = renderAlphabetEquivalentCard("hebrew", letter); + if (equivalentsCard) { + sections.push(equivalentsCard); + } + + detailBodyEl.innerHTML = sections.join(""); + attachDetailListeners(); + } + + function renderGreekDetail(letter) { + const archaicBadge = letter.archaic ? ' archaic' : ""; + detailSubEl.textContent = `${letter.displayName}${letter.archaic ? " (archaic)" : ""} — ${letter.transliteration}`; + detailBodyEl.innerHTML = ""; + + const sections = []; + + const charRow = letter.charFinal + ? `
Form (final)
${letter.charFinal}
` + : ""; + sections.push(card("Letter Details", ` +
+
Uppercase
${letter.char}
+
Lowercase
${letter.charLower || "—"}
+ ${charRow} +
Name
${letter.displayName}${archaicBadge}
+
Transliteration
${letter.transliteration}
+
IPA
${letter.ipa || "—"}
+
Isopsephy Value
${letter.numerology}
+
Meaning / Origin
${letter.meaning || "—"}
+
+ `)); + + const positionRootCard = renderPositionDigitalRootCard(letter, "greek"); + if (positionRootCard) { + sections.push(positionRootCard); + } + + const equivalentsCard = renderAlphabetEquivalentCard("greek", letter); + if (equivalentsCard) { + sections.push(equivalentsCard); + } + + const monthRefs = monthRefsForLetter(letter); + const monthCard = calendarMonthsCard(monthRefs, `Calendar correspondences inherited via ${letter.displayName}'s Hebrew origin.`); + if (monthCard) { + sections.push(monthCard); + } + + detailBodyEl.innerHTML = sections.join(""); + attachDetailListeners(); + } + + function renderEnglishDetail(letter) { + detailSubEl.textContent = `Letter ${letter.letter} · position #${letter.index}`; + detailBodyEl.innerHTML = ""; + + const sections = []; + + sections.push(card("Letter Details", ` +
+
Letter
${letter.letter}
+
Position
#${letter.index} of 26
+
IPA
${letter.ipa || "—"}
+
Pythagorean Value
${letter.pythagorean}
+
+ `)); + + const positionRootCard = renderPositionDigitalRootCard(letter, "english"); + if (positionRootCard) { + sections.push(positionRootCard); + } + + const equivalentsCard = renderAlphabetEquivalentCard("english", letter); + if (equivalentsCard) { + sections.push(equivalentsCard); + } + + const monthRefs = monthRefsForLetter(letter); + const monthCard = calendarMonthsCard(monthRefs, `Calendar correspondences linked through this letter's Hebrew correspondence.`); + if (monthCard) { + sections.push(monthCard); + } + + detailBodyEl.innerHTML = sections.join(""); + attachDetailListeners(); + } + + function renderArabicDetail(letter) { + detailSubEl.textContent = `${arabicDisplayName(letter)} — ${letter.transliteration}`; + detailBodyEl.innerHTML = ""; + + const sections = []; + + // Letter forms row + const f = letter.forms || {}; + const formParts = [ + f.isolated ? `${f.isolated}
isolated
` : "", + f.final ? `${f.final}
final
` : "", + f.medial ? `${f.medial}
medial
` : "", + f.initial ? `${f.initial}
initial
` : "" + ].filter(Boolean); + + sections.push(card("Letter Details", ` +
+
Arabic Name
${letter.nameArabic}
+
Transliteration
${letter.transliteration}
+
IPA
${letter.ipa || "—"}
+
Abjad Value
${letter.abjad}
+
Meaning
${letter.meaning || "—"}
+
Category
${letter.category}
+
Position
#${letter.index} of 28 (Abjad order)
+
+ `)); + + const positionRootCard = renderPositionDigitalRootCard(letter, "arabic", "Abjad order"); + if (positionRootCard) { + sections.push(positionRootCard); + } + + if (formParts.length) { + sections.push(card("Letter Forms", `
${formParts.join("")}
`)); + } + + const equivalentsCard = renderAlphabetEquivalentCard("arabic", letter); + if (equivalentsCard) { + sections.push(equivalentsCard); + } + + detailBodyEl.innerHTML = sections.join(""); + attachDetailListeners(); + } + + function renderEnochianDetail(letter) { + const englishRefs = extractEnglishLetterRefs(letter?.englishLetters); + detailSubEl.textContent = `${letter.title} — ${letter.transliteration}`; + detailBodyEl.innerHTML = ""; + + const sections = []; + + sections.push(card("Letter Details", ` +
+
Character
${enochianGlyphImageHtml(letter, "alpha-enochian-glyph-img alpha-enochian-glyph-img--detail-row")}
+
Name
${letter.title}
+
English Letters
${englishRefs.join(" / ") || "—"}
+
Transliteration
${letter.transliteration || "—"}
+
Element / Planet
${letter.elementOrPlanet || "—"}
+
Tarot
${letter.tarot || "—"}
+
Numerology
${letter.numerology || "—"}
+
Glyph Source
Local cache: asset/img/enochian (sourced from dCode set)
+
Position
#${letter.index} of 21
+
+ `)); + + const positionRootCard = renderPositionDigitalRootCard(letter, "enochian"); + if (positionRootCard) { + sections.push(positionRootCard); + } + + const equivalentsCard = renderAlphabetEquivalentCard("enochian", letter); + if (equivalentsCard) { + sections.push(equivalentsCard); + } + + const monthRefs = monthRefsForLetter(letter); + const monthCard = calendarMonthsCard(monthRefs, `Calendar correspondences linked through this letter's Hebrew correspondence.`); + if (monthCard) { + sections.push(monthCard); + } + + detailBodyEl.innerHTML = sections.join(""); + attachDetailListeners(); + } + + // ── Event delegation on detail body ────────────────────────────────── + function attachDetailListeners() { + if (!detailBodyEl) return; + + // Nav buttons — generic: forward all data-* (except data-event) as the event detail + detailBodyEl.querySelectorAll(".alpha-nav-btn[data-event]").forEach((btn) => { + btn.addEventListener("click", () => { + const evtName = btn.dataset.event; + const detail = {}; + Object.entries(btn.dataset).forEach(([key, val]) => { + if (key === "event") return; + // Convert kebab data keys to camelCase (e.g. planet-id → planetId) + const camel = key.replace(/-([a-z])/g, (_, c) => c.toUpperCase()); + detail[camel] = isNaN(Number(val)) || val === "" ? val : Number(val); + }); + document.dispatchEvent(new CustomEvent(evtName, { detail })); + }); + }); + + // Sister letter cross-navigation within this section + detailBodyEl.querySelectorAll(".alpha-sister-btn[data-alpha]").forEach((btn) => { + btn.addEventListener("click", () => { + const alpha = btn.dataset.alpha; + const key = btn.dataset.key; + switchAlphabet(alpha, key); + }); + }); + } + + // ── Selection ───────────────────────────────────────────────────────── + function selectLetter(key) { + state.selectedKey = key; + renderList(); + const letters = getFilteredLetters(); + const letter = letters.find((l) => letterKey(l) === key) || getLetters().find((l) => letterKey(l) === key); + if (letter) renderDetail(letter); + } + + // ── Alphabet switching ──────────────────────────────────────────────── + function switchAlphabet(alpha, selectKey) { + state.activeAlphabet = alpha; + state.selectedKey = selectKey || null; + updateTabs(); + syncFilterControls(); + renderList(); + if (selectKey) { + const letters = getFilteredLetters(); + const letter = letters.find((l) => letterKey(l) === selectKey) || getLetters().find((l) => letterKey(l) === selectKey); + if (letter) renderDetail(letter); + } else { + resetDetail(); + } + } + + function updateTabs() { + [tabAll, tabHebrew, tabGreek, tabEnglish, tabArabic, tabEnochian].forEach((btn) => { + if (!btn) return; + btn.classList.toggle("alpha-tab-active", btn.dataset.alpha === state.activeAlphabet); + }); + } + + function resetDetail() { + if (detailNameEl) { + detailNameEl.textContent = "--"; + detailNameEl.classList.remove("alpha-detail-glyph"); + } + if (detailSubEl) detailSubEl.textContent = "Select a letter to explore"; + if (detailBodyEl) detailBodyEl.innerHTML = ""; + } + + // ── Public init ─────────────────────────────────────────────────────── + function ensureAlphabetSection(magickDataset, referenceData = null) { + const grouped = magickDataset?.grouped || {}; + const alphabetData = (grouped["alphabets"] && grouped["alphabets"]["hebrew"]) + ? grouped["alphabets"] + : null; + + if (alphabetData) { + state.alphabets = alphabetData; + if (state.gematria.db?.baseAlphabet) { + refreshGematriaScriptMap(state.gematria.db.baseAlphabet); + } + } + + state.fourWorldLayers = buildFourWorldLayersFromDataset(magickDataset); + + state.cubeRefs = buildCubeReferences(magickDataset); + + if (referenceData && state.alphabets) { + state.monthRefsByHebrewId = buildMonthReferencesByHebrew(referenceData, state.alphabets); + } + + if (state.initialized) { + ensureGematriaCalculator(); + syncFilterControls(); + renderList(); + const letters = getFilteredLetters(); + const selected = letters.find((entry) => letterKey(entry) === state.selectedKey) || letters[0]; + if (selected) { + renderDetail(selected); + } else { + resetDetail(); + if (detailSubEl) { + detailSubEl.textContent = "No letters match the current filter."; + } + } + return; + } + state.initialized = true; + + // alphabets.json is a top-level file → grouped["alphabets"] = the data object + + getElements(); + ensureGematriaCalculator(); + bindFilterControls(); + syncFilterControls(); + + if (!state.alphabets) { + if (detailSubEl) detailSubEl.textContent = "Alphabet data not loaded."; + return; + } + + // Attach tab listeners + [tabAll, tabHebrew, tabGreek, tabEnglish, tabArabic, tabEnochian].forEach((btn) => { + if (!btn) return; + btn.addEventListener("click", () => { + switchAlphabet(btn.dataset.alpha, null); + }); + }); + + switchAlphabet("all", null); + } + + // ── Incoming navigation ─────────────────────────────────────────────── + function selectLetterByHebrewId(hebrewLetterId) { + switchAlphabet("hebrew", hebrewLetterId); + } + + function selectGreekLetterByName(name) { + switchAlphabet("greek", name); + } + + function selectEnglishLetter(letter) { + switchAlphabet("english", letter); + } + + function selectArabicLetter(name) { + switchAlphabet("arabic", name); + } + + function selectEnochianLetter(id) { + switchAlphabet("enochian", id); + } + + window.AlphabetSectionUi = { + ensureAlphabetSection, + selectLetterByHebrewId, + selectGreekLetterByName, + selectEnglishLetter, + selectArabicLetter, + selectEnochianLetter + }; +})(); diff --git a/app/ui-calendar.js b/app/ui-calendar.js new file mode 100644 index 0000000..3fc41d2 --- /dev/null +++ b/app/ui-calendar.js @@ -0,0 +1,2528 @@ +/* ui-calendar.js — Month and celestial holiday browser */ +(function () { + "use strict"; + + const { getTarotCardDisplayName, getTarotCardSearchAliases } = window.TarotCardImages || {}; + + const state = { + initialized: false, + referenceData: null, + magickDataset: null, + selectedCalendar: "gregorian", + calendarData: {}, + months: [], + filteredMonths: [], + holidays: [], + calendarHolidays: [], + selectedMonthId: null, + searchQuery: "", + selectedYear: new Date().getFullYear(), + selectedDayMonthId: null, + selectedDayCalendarId: null, + selectedDayEntries: [], + planetsById: new Map(), + signsById: new Map(), + godsById: new Map(), + hebrewById: new Map(), + dayLinksCache: new Map() + }; + + const TAROT_TRUMP_NUMBER_BY_NAME = { + "the fool": 0, + fool: 0, + "the magus": 1, + magus: 1, + magician: 1, + "the high priestess": 2, + "high priestess": 2, + "the empress": 3, + empress: 3, + "the emperor": 4, + emperor: 4, + "the hierophant": 5, + hierophant: 5, + "the lovers": 6, + lovers: 6, + "the chariot": 7, + chariot: 7, + strength: 8, + lust: 8, + "the hermit": 9, + hermit: 9, + fortune: 10, + "wheel of fortune": 10, + justice: 11, + "the hanged man": 12, + "hanged man": 12, + death: 13, + temperance: 14, + art: 14, + "the devil": 15, + devil: 15, + "the tower": 16, + tower: 16, + "the star": 17, + star: 17, + "the moon": 18, + moon: 18, + "the sun": 19, + sun: 19, + aeon: 20, + judgement: 20, + judgment: 20, + universe: 21, + world: 21, + "the world": 21 + }; + + const MINOR_NUMBER_WORD = { + 1: "Ace", + 2: "Two", + 3: "Three", + 4: "Four", + 5: "Five", + 6: "Six", + 7: "Seven", + 8: "Eight", + 9: "Nine", + 10: "Ten" + }; + + const HEBREW_MONTH_ALIAS_BY_ID = { + nisan: ["nisan"], + iyar: ["iyar"], + sivan: ["sivan"], + tammuz: ["tamuz", "tammuz"], + av: ["av"], + elul: ["elul"], + tishrei: ["tishri", "tishrei"], + cheshvan: ["heshvan", "cheshvan", "marcheshvan"], + kislev: ["kislev"], + tevet: ["tevet"], + shvat: ["shevat", "shvat"], + adar: ["adar", "adar i", "adar 1"], + "adar-ii": ["adar ii", "adar 2"] + }; + + const MONTH_NAME_TO_INDEX = { + january: 0, + february: 1, + march: 2, + april: 3, + may: 4, + june: 5, + july: 6, + august: 7, + september: 8, + october: 9, + november: 10, + december: 11 + }; + + const GREGORIAN_MONTH_ID_TO_ORDER = { + january: 1, + february: 2, + march: 3, + april: 4, + may: 5, + june: 6, + july: 7, + august: 8, + september: 9, + october: 10, + november: 11, + december: 12 + }; + + function getElements() { + return { + monthListEl: document.getElementById("calendar-month-list"), + listTitleEl: document.getElementById("calendar-list-title"), + monthCountEl: document.getElementById("calendar-month-count"), + yearInputEl: document.getElementById("calendar-year-input"), + calendarYearWrapEl: document.getElementById("calendar-year-wrap"), + calendarTypeEl: document.getElementById("calendar-type-select"), + searchInputEl: document.getElementById("calendar-search-input"), + searchClearEl: document.getElementById("calendar-search-clear"), + detailNameEl: document.getElementById("calendar-detail-name"), + detailSubEl: document.getElementById("calendar-detail-sub"), + detailBodyEl: document.getElementById("calendar-detail-body") + }; + } + + function normalizeText(value) { + return String(value || "").trim(); + } + + function normalizeSearchValue(value) { + return String(value || "").trim().toLowerCase(); + } + + function cap(value) { + const text = normalizeText(value); + return text ? text.charAt(0).toUpperCase() + text.slice(1) : text; + } + + function normalizeTarotName(value) { + return String(value || "") + .trim() + .toLowerCase() + .replace(/\s+/g, " "); + } + + function resolveTarotTrumpNumber(cardName) { + const key = normalizeTarotName(cardName); + if (!key) { + return null; + } + if (Object.prototype.hasOwnProperty.call(TAROT_TRUMP_NUMBER_BY_NAME, key)) { + return TAROT_TRUMP_NUMBER_BY_NAME[key]; + } + const withoutLeadingThe = key.replace(/^the\s+/, ""); + if (Object.prototype.hasOwnProperty.call(TAROT_TRUMP_NUMBER_BY_NAME, withoutLeadingThe)) { + return TAROT_TRUMP_NUMBER_BY_NAME[withoutLeadingThe]; + } + return null; + } + + function getDisplayTarotName(cardName, trumpNumber) { + if (!cardName) { + return ""; + } + + if (typeof getTarotCardDisplayName !== "function") { + return cardName; + } + + if (Number.isFinite(Number(trumpNumber))) { + return getTarotCardDisplayName(cardName, { trumpNumber: Number(trumpNumber) }) || cardName; + } + + return getTarotCardDisplayName(cardName) || cardName; + } + + function normalizeMinorTarotCardName(cardName) { + const text = String(cardName || "").trim(); + if (!text) { + return ""; + } + + const match = text.match(/^(\d{1,2})\s+of\s+(.+)$/i); + if (!match) { + return text.replace(/\b(pentacles?|coins?)\b/i, "Disks"); + } + + const numeric = Number(match[1]); + const suitRaw = String(match[2] || "").trim(); + const rank = MINOR_NUMBER_WORD[numeric] || String(numeric); + const suit = suitRaw.replace(/\b(pentacles?|coins?)\b/i, "Disks"); + return `${rank} of ${suit}`; + } + + function parseMonthDayToken(token) { + const [month, day] = String(token || "").split("-").map((part) => Number(part)); + if (!Number.isFinite(month) || !Number.isFinite(day)) { + return null; + } + return { month, day }; + } + + function monthDayDate(monthDay, year) { + const parsed = parseMonthDayToken(monthDay); + if (!parsed) { + return null; + } + return new Date(year, parsed.month - 1, parsed.day); + } + + function buildSignDateBounds(sign) { + const start = monthDayDate(sign?.start, 2025); + const endBase = monthDayDate(sign?.end, 2025); + if (!start || !endBase) { + return null; + } + + const wrapsYear = endBase.getTime() < start.getTime(); + const end = wrapsYear ? monthDayDate(sign?.end, 2026) : endBase; + if (!end) { + return null; + } + + return { start, end }; + } + + function addDays(date, days) { + const next = new Date(date); + next.setDate(next.getDate() + days); + return next; + } + + function formatDateLabel(date) { + return date.toLocaleDateString(undefined, { month: "short", day: "numeric" }); + } + + function monthDayOrdinal(month, day) { + if (!Number.isFinite(month) || !Number.isFinite(day)) { + return null; + } + const base = new Date(2025, Math.trunc(month) - 1, Math.trunc(day), 12, 0, 0, 0); + if (Number.isNaN(base.getTime())) { + return null; + } + const start = new Date(2025, 0, 1, 12, 0, 0, 0); + const diff = base.getTime() - start.getTime(); + return Math.floor(diff / (24 * 60 * 60 * 1000)) + 1; + } + + function isMonthDayInRange(targetMonth, targetDay, startMonth, startDay, endMonth, endDay) { + const target = monthDayOrdinal(targetMonth, targetDay); + const start = monthDayOrdinal(startMonth, startDay); + const end = monthDayOrdinal(endMonth, endDay); + if (!Number.isFinite(target) || !Number.isFinite(start) || !Number.isFinite(end)) { + return false; + } + + if (end >= start) { + return target >= start && target <= end; + } + return target >= start || target <= end; + } + + function parseMonthDayTokensFromText(value) { + const text = String(value || ""); + const matches = [...text.matchAll(/(\d{2})-(\d{2})/g)]; + return matches + .map((match) => ({ month: Number(match[1]), day: Number(match[2]) })) + .filter((token) => Number.isFinite(token.month) && Number.isFinite(token.day)); + } + + function parseDayRangeFromText(value) { + const text = String(value || ""); + const range = text.match(/\b(\d{1,2})\s*[–-]\s*(\d{1,2})\b/); + if (!range) { + return null; + } + + const startDay = Number(range[1]); + const endDay = Number(range[2]); + if (!Number.isFinite(startDay) || !Number.isFinite(endDay)) { + return null; + } + + return { startDay, endDay }; + } + + function isoToDateAtNoon(iso) { + const text = String(iso || "").trim(); + if (!text) { + return null; + } + const parsed = new Date(`${text}T12:00:00`); + return Number.isNaN(parsed.getTime()) ? null : parsed; + } + + function normalizeDayFilterEntry(dayNumber, gregorianIso) { + const day = Math.trunc(Number(dayNumber)); + if (!Number.isFinite(day) || day <= 0) { + return null; + } + + const iso = String(gregorianIso || "").trim(); + if (!iso) { + return null; + } + + return { + dayNumber: day, + gregorianIso: iso + }; + } + + function sortDayFilterEntries(entries) { + return [...entries].sort((left, right) => left.dayNumber - right.dayNumber || left.gregorianIso.localeCompare(right.gregorianIso)); + } + + function ensureDayFilterContext(month) { + if (!month) { + return; + } + + const sameContext = state.selectedDayMonthId === month.id + && state.selectedDayCalendarId === state.selectedCalendar; + + if (!sameContext) { + state.selectedDayMonthId = month.id; + state.selectedDayCalendarId = state.selectedCalendar; + state.selectedDayEntries = []; + } + } + + function clearSelectedDayFilter() { + state.selectedDayMonthId = null; + state.selectedDayCalendarId = null; + state.selectedDayEntries = []; + } + + function toggleDayFilterEntry(month, dayNumber, gregorianIso) { + ensureDayFilterContext(month); + + const next = normalizeDayFilterEntry(dayNumber, gregorianIso); + if (!next) { + return; + } + + const entries = state.selectedDayEntries; + const existingIndex = entries.findIndex((entry) => entry.dayNumber === next.dayNumber); + if (existingIndex >= 0) { + entries.splice(existingIndex, 1); + } else { + entries.push(next); + } + + state.selectedDayEntries = sortDayFilterEntries(entries); + } + + function toggleDayRangeFilter(month, startDay, endDay) { + ensureDayFilterContext(month); + + const start = Math.trunc(Number(startDay)); + const end = Math.trunc(Number(endDay)); + if (!Number.isFinite(start) || !Number.isFinite(end) || start <= 0 || end <= 0) { + return; + } + + const minDay = Math.min(start, end); + const maxDay = Math.max(start, end); + const rows = getMonthDayLinkRows(month) + .filter((row) => row.isResolved && row.day >= minDay && row.day <= maxDay) + .map((row) => normalizeDayFilterEntry(row.day, row.gregorianDate)) + .filter(Boolean); + + if (!rows.length) { + return; + } + + const existingSet = new Set(state.selectedDayEntries.map((entry) => entry.dayNumber)); + const allSelected = rows.every((row) => existingSet.has(row.dayNumber)); + + if (allSelected) { + const removeSet = new Set(rows.map((row) => row.dayNumber)); + state.selectedDayEntries = state.selectedDayEntries.filter((entry) => !removeSet.has(entry.dayNumber)); + return; + } + + rows.forEach((row) => { + if (!existingSet.has(row.dayNumber)) { + state.selectedDayEntries.push(row); + } + }); + + state.selectedDayEntries = sortDayFilterEntries(state.selectedDayEntries); + } + + function getSelectedDayFilterContext(month) { + if (!month) { + return null; + } + if (state.selectedDayMonthId !== month.id) { + return null; + } + if (state.selectedDayCalendarId !== state.selectedCalendar) { + return null; + } + + if (!Array.isArray(state.selectedDayEntries) || !state.selectedDayEntries.length) { + return null; + } + + const entries = state.selectedDayEntries + .map((entry) => { + const normalized = normalizeDayFilterEntry(entry.dayNumber, entry.gregorianIso); + if (!normalized) { + return null; + } + return { + ...normalized, + gregorianDate: isoToDateAtNoon(normalized.gregorianIso) + }; + }) + .filter(Boolean); + + if (!entries.length) { + return null; + } + + return { + entries, + dayNumbers: new Set(entries.map((entry) => entry.dayNumber)) + }; + } + + function buildDecanWindow(sign, decanIndex) { + const bounds = buildSignDateBounds(sign); + const index = Number(decanIndex); + if (!bounds || !Number.isFinite(index)) { + return null; + } + + const start = addDays(bounds.start, (index - 1) * 10); + const nominalEnd = addDays(start, 9); + const end = nominalEnd.getTime() > bounds.end.getTime() ? bounds.end : nominalEnd; + + return { + start, + end, + label: `${formatDateLabel(start)}–${formatDateLabel(end)}` + }; + } + + function listMonthNumbersBetween(start, end) { + const result = []; + const seen = new Set(); + const cursor = new Date(start.getFullYear(), start.getMonth(), 1); + const limit = new Date(end.getFullYear(), end.getMonth(), 1); + + while (cursor.getTime() <= limit.getTime()) { + const monthNo = cursor.getMonth() + 1; + if (!seen.has(monthNo)) { + seen.add(monthNo); + result.push(monthNo); + } + cursor.setMonth(cursor.getMonth() + 1); + } + + return result; + } + + function buildDecanTarotRowsForMonth(month) { + const monthOrder = Number(month?.order); + if (!Number.isFinite(monthOrder)) { + return []; + } + + const rows = []; + const seen = new Set(); + const decansBySign = state.referenceData?.decansBySign || {}; + + Object.entries(decansBySign).forEach(([signId, decans]) => { + const sign = state.signsById.get(signId); + if (!sign || !Array.isArray(decans)) { + return; + } + + decans.forEach((decan) => { + const window = buildDecanWindow(sign, decan?.index); + if (!window) { + return; + } + + const monthsTouched = listMonthNumbersBetween(window.start, window.end); + if (!monthsTouched.includes(monthOrder)) { + return; + } + + const cardName = normalizeMinorTarotCardName(decan?.tarotMinorArcana); + if (!cardName) { + return; + } + + const key = `${cardName}|${signId}|${decan.index}`; + if (seen.has(key)) { + return; + } + seen.add(key); + + const startDegree = (Number(decan.index) - 1) * 10; + const endDegree = startDegree + 10; + const signName = sign?.name?.en || sign?.name || signId; + + rows.push({ + cardName, + signId, + signName, + signSymbol: sign?.symbol || "", + decanIndex: Number(decan.index), + startDegree, + endDegree, + startTime: window.start.getTime(), + endTime: window.end.getTime(), + startMonth: window.start.getMonth() + 1, + startDay: window.start.getDate(), + endMonth: window.end.getMonth() + 1, + endDay: window.end.getDate(), + dateRange: window.label + }); + }); + }); + + rows.sort((left, right) => { + if (left.startTime !== right.startTime) { + return left.startTime - right.startTime; + } + if (left.decanIndex !== right.decanIndex) { + return left.decanIndex - right.decanIndex; + } + return left.cardName.localeCompare(right.cardName); + }); + + return rows; + } + + function buildPlanetMap(planetsObj) { + const map = new Map(); + if (!planetsObj || typeof planetsObj !== "object") { + return map; + } + + Object.values(planetsObj).forEach((planet) => { + if (!planet?.id) { + return; + } + map.set(planet.id, planet); + }); + + return map; + } + + function buildSignsMap(signs) { + const map = new Map(); + if (!Array.isArray(signs)) { + return map; + } + + signs.forEach((sign) => { + if (!sign?.id) { + return; + } + map.set(sign.id, sign); + }); + + return map; + } + + function buildGodsMap(magickDataset) { + const gods = magickDataset?.grouped?.gods?.gods; + const map = new Map(); + + if (!Array.isArray(gods)) { + return map; + } + + gods.forEach((god) => { + if (!god?.id) { + return; + } + map.set(god.id, god); + }); + + return map; + } + + function findGodIdByName(name) { + if (!name || !state.godsById) return null; + const normalized = String(name).trim().toLowerCase().replace(/^the\s+/, ""); + for (const [id, god] of state.godsById) { + const godName = String(god.name || "").trim().toLowerCase().replace(/^the\s+/, ""); + if (godName === normalized || id.toLowerCase() === normalized) return id; + } + return null; + } + + function buildHebrewMap(magickDataset) { + const map = new Map(); + const letters = magickDataset?.grouped?.alphabets?.hebrew; + if (!Array.isArray(letters)) { + return map; + } + + letters.forEach((letter) => { + if (!letter?.hebrewLetterId) { + return; + } + map.set(letter.hebrewLetterId, letter); + }); + + return map; + } + + function sortMonths(months) { + return [...months].sort((left, right) => Number(left?.order || 0) - Number(right?.order || 0)); + } + + function getSelectedMonth() { + return state.months.find((month) => month.id === state.selectedMonthId) || null; + } + + function getDaysInMonth(year, monthOrder) { + if (!Number.isFinite(year) || !Number.isFinite(monthOrder)) { + return null; + } + return new Date(year, monthOrder, 0).getDate(); + } + + function getMonthStartWeekday(year, monthOrder) { + const date = new Date(year, monthOrder - 1, 1); + return date.toLocaleDateString(undefined, { weekday: "long" }); + } + + function parseMonthRange(month) { + const startText = normalizeText(month?.start); + const endText = normalizeText(month?.end); + if (!startText || !endText) { + return "--"; + } + return `${startText} to ${endText}`; + } + + function getGregorianMonthOrderFromId(monthId) { + if (!monthId) { + return null; + } + const key = String(monthId).trim().toLowerCase(); + const value = GREGORIAN_MONTH_ID_TO_ORDER[key]; + return Number.isFinite(value) ? value : null; + } + + function normalizeCalendarText(value) { + return String(value || "") + .normalize("NFKD") + .replace(/[\u0300-\u036f]/g, "") + .replace(/['`´ʻ’]/g, "") + .toLowerCase() + .replace(/[^a-z0-9]+/g, " ") + .trim(); + } + + function readNumericPart(parts, partType) { + const raw = parts.find((part) => part.type === partType)?.value; + if (!raw) { + return null; + } + + const digits = String(raw).replace(/[^0-9]/g, ""); + if (!digits) { + return null; + } + + const parsed = Number(digits); + return Number.isFinite(parsed) ? parsed : null; + } + + function formatGregorianReferenceDate(date) { + if (!(date instanceof Date) || Number.isNaN(date.getTime())) { + return "--"; + } + + return date.toLocaleDateString(undefined, { + weekday: "long", + year: "numeric", + month: "long", + day: "numeric" + }); + } + + function formatCalendarDateFromGregorian(date, calendarId) { + if (!(date instanceof Date) || Number.isNaN(date.getTime())) { + return "--"; + } + + const locale = calendarId === "hebrew" + ? "en-u-ca-hebrew" + : (calendarId === "islamic" ? "en-u-ca-islamic" : "en"); + + return new Intl.DateTimeFormat(locale, { + weekday: "long", + year: "numeric", + month: "long", + day: "numeric" + }).format(date); + } + + function getGregorianMonthStartDate(monthOrder, year = state.selectedYear) { + if (!Number.isFinite(monthOrder) || !Number.isFinite(year)) { + return null; + } + + return new Date(Math.trunc(year), Math.trunc(monthOrder) - 1, 1, 12, 0, 0, 0); + } + + function getHebrewMonthAliases(month) { + const aliases = []; + const idAliases = HEBREW_MONTH_ALIAS_BY_ID[String(month?.id || "").toLowerCase()] || []; + aliases.push(...idAliases); + + const nameAlias = normalizeCalendarText(month?.name); + if (nameAlias) { + aliases.push(nameAlias); + } + + return Array.from(new Set(aliases.map((alias) => normalizeCalendarText(alias)).filter(Boolean))); + } + + function findHebrewMonthStartInGregorianYear(month, year) { + const aliases = getHebrewMonthAliases(month); + if (!aliases.length || !Number.isFinite(year)) { + return null; + } + + const formatter = new Intl.DateTimeFormat("en-u-ca-hebrew", { + day: "numeric", + month: "long", + year: "numeric" + }); + + const cursor = new Date(Math.trunc(year), 0, 1, 12, 0, 0, 0); + const end = new Date(Math.trunc(year), 11, 31, 12, 0, 0, 0); + + while (cursor.getTime() <= end.getTime()) { + const parts = formatter.formatToParts(cursor); + const day = readNumericPart(parts, "day"); + const monthName = normalizeCalendarText(parts.find((part) => part.type === "month")?.value); + if (day === 1 && aliases.includes(monthName)) { + return new Date(cursor); + } + cursor.setDate(cursor.getDate() + 1); + } + + return null; + } + + function findIslamicMonthStartInGregorianYear(month, year) { + const targetOrder = Number(month?.order); + if (!Number.isFinite(targetOrder) || !Number.isFinite(year)) { + return null; + } + + const formatter = new Intl.DateTimeFormat("en-u-ca-islamic", { + day: "numeric", + month: "numeric", + year: "numeric" + }); + + const cursor = new Date(Math.trunc(year), 0, 1, 12, 0, 0, 0); + const end = new Date(Math.trunc(year), 11, 31, 12, 0, 0, 0); + + while (cursor.getTime() <= end.getTime()) { + const parts = formatter.formatToParts(cursor); + const day = readNumericPart(parts, "day"); + const monthNo = readNumericPart(parts, "month"); + if (day === 1 && monthNo === Math.trunc(targetOrder)) { + return new Date(cursor); + } + cursor.setDate(cursor.getDate() + 1); + } + + return null; + } + + function parseFirstMonthDayFromText(dateText) { + const text = String(dateText || "").replace(/~/g, " "); + const firstSegment = text.split("/")[0] || text; + const match = firstSegment.match(/(January|February|March|April|May|June|July|August|September|October|November|December)\s+(\d{1,2})/i); + if (!match) { + return null; + } + + const monthIndex = MONTH_NAME_TO_INDEX[String(match[1]).toLowerCase()]; + const day = Number(match[2]); + if (!Number.isFinite(monthIndex) || !Number.isFinite(day)) { + return null; + } + + return { monthIndex, day }; + } + + function parseMonthDayStartToken(token) { + const match = String(token || "").match(/(\d{2})-(\d{2})/); + if (!match) { + return null; + } + + const month = Number(match[1]); + const day = Number(match[2]); + if (!Number.isFinite(month) || !Number.isFinite(day)) { + return null; + } + + return { month, day }; + } + + function createDateAtNoon(year, monthIndex, dayOfMonth) { + return new Date(Math.trunc(year), monthIndex, Math.trunc(dayOfMonth), 12, 0, 0, 0); + } + + function computeWesternEasterDate(year) { + const y = Math.trunc(Number(year)); + if (!Number.isFinite(y)) { + return null; + } + + // Meeus/Jones/Butcher Gregorian algorithm. + const a = y % 19; + const b = Math.floor(y / 100); + const c = y % 100; + const d = Math.floor(b / 4); + const e = b % 4; + const f = Math.floor((b + 8) / 25); + const g = Math.floor((b - f + 1) / 3); + const h = (19 * a + b - d - g + 15) % 30; + const i = Math.floor(c / 4); + const k = c % 4; + const l = (32 + 2 * e + 2 * i - h - k) % 7; + const m = Math.floor((a + 11 * h + 22 * l) / 451); + const month = Math.floor((h + l - 7 * m + 114) / 31); + const day = ((h + l - 7 * m + 114) % 31) + 1; + return createDateAtNoon(y, month - 1, day); + } + + function computeNthWeekdayOfMonth(year, monthIndex, weekday, ordinal) { + const y = Math.trunc(Number(year)); + if (!Number.isFinite(y)) { + return null; + } + + const first = createDateAtNoon(y, monthIndex, 1); + const firstWeekday = first.getDay(); + const offset = (weekday - firstWeekday + 7) % 7; + const dayOfMonth = 1 + offset + (Math.trunc(ordinal) - 1) * 7; + const daysInMonth = new Date(y, monthIndex + 1, 0).getDate(); + if (dayOfMonth > daysInMonth) { + return null; + } + return createDateAtNoon(y, monthIndex, dayOfMonth); + } + + function resolveGregorianDateRule(rule) { + const key = String(rule || "").trim().toLowerCase(); + if (!key) { + return null; + } + + if (key === "gregorian-easter-sunday") { + return computeWesternEasterDate(state.selectedYear); + } + + if (key === "gregorian-good-friday") { + const easter = computeWesternEasterDate(state.selectedYear); + if (!(easter instanceof Date) || Number.isNaN(easter.getTime())) { + return null; + } + return createDateAtNoon(easter.getFullYear(), easter.getMonth(), easter.getDate() - 2); + } + + if (key === "gregorian-thanksgiving-us") { + // US Thanksgiving: 4th Thursday of November. + return computeNthWeekdayOfMonth(state.selectedYear, 10, 4, 4); + } + + return null; + } + + function findHebrewMonthDayInGregorianYear(monthId, day, year) { + const aliases = HEBREW_MONTH_ALIAS_BY_ID[String(monthId || "").toLowerCase()] || []; + const targetDay = Number(day); + if (!aliases.length || !Number.isFinite(targetDay) || !Number.isFinite(year)) { + return null; + } + + const normalizedAliases = aliases.map((alias) => normalizeCalendarText(alias)).filter(Boolean); + const formatter = new Intl.DateTimeFormat("en-u-ca-hebrew", { + day: "numeric", + month: "long", + year: "numeric" + }); + + const cursor = new Date(Math.trunc(year), 0, 1, 12, 0, 0, 0); + const end = new Date(Math.trunc(year), 11, 31, 12, 0, 0, 0); + + while (cursor.getTime() <= end.getTime()) { + const parts = formatter.formatToParts(cursor); + const currentDay = readNumericPart(parts, "day"); + const monthName = normalizeCalendarText(parts.find((part) => part.type === "month")?.value); + if (currentDay === Math.trunc(targetDay) && normalizedAliases.includes(monthName)) { + return new Date(cursor); + } + cursor.setDate(cursor.getDate() + 1); + } + + return null; + } + + function getIslamicMonthOrderById(monthId) { + const month = (state.calendarData?.islamic || []).find((item) => item?.id === monthId); + const order = Number(month?.order); + return Number.isFinite(order) ? Math.trunc(order) : null; + } + + function findIslamicMonthDayInGregorianYear(monthId, day, year) { + const monthOrder = getIslamicMonthOrderById(monthId); + const targetDay = Number(day); + if (!Number.isFinite(monthOrder) || !Number.isFinite(targetDay) || !Number.isFinite(year)) { + return null; + } + + const formatter = new Intl.DateTimeFormat("en-u-ca-islamic", { + day: "numeric", + month: "numeric", + year: "numeric" + }); + + const cursor = new Date(Math.trunc(year), 0, 1, 12, 0, 0, 0); + const end = new Date(Math.trunc(year), 11, 31, 12, 0, 0, 0); + + while (cursor.getTime() <= end.getTime()) { + const parts = formatter.formatToParts(cursor); + const currentDay = readNumericPart(parts, "day"); + const currentMonth = readNumericPart(parts, "month"); + if (currentDay === Math.trunc(targetDay) && currentMonth === monthOrder) { + return new Date(cursor); + } + cursor.setDate(cursor.getDate() + 1); + } + + return null; + } + + function resolveHolidayGregorianDate(holiday) { + if (!holiday || typeof holiday !== "object") { + return null; + } + + const calendarId = String(holiday.calendarId || "").trim().toLowerCase(); + const monthId = String(holiday.monthId || "").trim().toLowerCase(); + const day = Number(holiday.day); + + if (calendarId === "gregorian") { + if (holiday?.dateRule) { + const ruledDate = resolveGregorianDateRule(holiday.dateRule); + if (ruledDate) { + return ruledDate; + } + } + + const monthDay = parseMonthDayStartToken(holiday.monthDayStart) || parseMonthDayStartToken(holiday.dateText); + if (monthDay) { + return new Date(state.selectedYear, monthDay.month - 1, monthDay.day, 12, 0, 0, 0); + } + const order = getGregorianMonthOrderFromId(monthId); + if (Number.isFinite(order) && Number.isFinite(day)) { + return new Date(state.selectedYear, order - 1, Math.trunc(day), 12, 0, 0, 0); + } + return null; + } + + if (calendarId === "hebrew") { + return findHebrewMonthDayInGregorianYear(monthId, day, state.selectedYear); + } + + if (calendarId === "islamic") { + return findIslamicMonthDayInGregorianYear(monthId, day, state.selectedYear); + } + + if (calendarId === "wheel-of-year") { + const monthDay = parseMonthDayStartToken(holiday.monthDayStart) || parseFirstMonthDayFromText(holiday.dateText); + if (monthDay?.month && monthDay?.day) { + return new Date(state.selectedYear, monthDay.month - 1, monthDay.day, 12, 0, 0, 0); + } + if (monthDay?.monthIndex != null && monthDay?.day) { + return new Date(state.selectedYear, monthDay.monthIndex, monthDay.day, 12, 0, 0, 0); + } + } + + return null; + } + + function findWheelMonthStartInGregorianYear(month, year) { + const parsed = parseFirstMonthDayFromText(month?.date); + if (!parsed || !Number.isFinite(year)) { + return null; + } + + return new Date(Math.trunc(year), parsed.monthIndex, parsed.day, 12, 0, 0, 0); + } + + function getGregorianReferenceDateForCalendarMonth(month) { + const calId = state.selectedCalendar; + if (calId === "gregorian") { + return getGregorianMonthStartDate(Number(month?.order)); + } + if (calId === "hebrew") { + return findHebrewMonthStartInGregorianYear(month, state.selectedYear); + } + if (calId === "islamic") { + return findIslamicMonthStartInGregorianYear(month, state.selectedYear); + } + if (calId === "wheel-of-year") { + return findWheelMonthStartInGregorianYear(month, state.selectedYear); + } + return null; + } + + function getMonthSubtitle(month) { + const calId = state.selectedCalendar; + if (calId === "hebrew" || calId === "islamic") { + const native = month.nativeName ? ` · ${month.nativeName}` : ""; + const days = month.days ? ` · ${month.days} days` : ""; + return `${month.season || ""}${native}${days}`; + } + if (calId === "wheel-of-year") { + return [month.date, month.type, month.season].filter(Boolean).join(" · "); + } + return parseMonthRange(month); + } + + function formatIsoDate(date) { + if (!(date instanceof Date) || Number.isNaN(date.getTime())) { + return ""; + } + + const year = date.getFullYear(); + const month = `${date.getMonth() + 1}`.padStart(2, "0"); + const day = `${date.getDate()}`.padStart(2, "0"); + return `${year}-${month}-${day}`; + } + + function resolveCalendarDayToGregorian(month, dayNumber) { + const calId = state.selectedCalendar; + const day = Math.trunc(Number(dayNumber)); + if (!Number.isFinite(day) || day <= 0) { + return null; + } + + if (calId === "gregorian") { + const monthOrder = Number(month?.order); + if (!Number.isFinite(monthOrder)) { + return null; + } + return new Date(state.selectedYear, monthOrder - 1, day, 12, 0, 0, 0); + } + + if (calId === "hebrew") { + return findHebrewMonthDayInGregorianYear(month?.id, day, state.selectedYear); + } + + if (calId === "islamic") { + return findIslamicMonthDayInGregorianYear(month?.id, day, state.selectedYear); + } + + return null; + } + + function getMonthDayLinkRows(month) { + const cacheKey = `${state.selectedCalendar}|${state.selectedYear}|${month?.id || ""}`; + if (state.dayLinksCache.has(cacheKey)) { + return state.dayLinksCache.get(cacheKey); + } + + let dayCount = null; + if (state.selectedCalendar === "gregorian") { + dayCount = getDaysInMonth(state.selectedYear, Number(month?.order)); + } else if (state.selectedCalendar === "hebrew" || state.selectedCalendar === "islamic") { + const baseDays = Number(month?.days); + const variantDays = Number(month?.daysVariant); + if (Number.isFinite(baseDays) && Number.isFinite(variantDays)) { + dayCount = Math.max(Math.trunc(baseDays), Math.trunc(variantDays)); + } else if (Number.isFinite(baseDays)) { + dayCount = Math.trunc(baseDays); + } else if (Number.isFinite(variantDays)) { + dayCount = Math.trunc(variantDays); + } + } + + if (!Number.isFinite(dayCount) || dayCount <= 0) { + state.dayLinksCache.set(cacheKey, []); + return []; + } + + const rows = []; + for (let day = 1; day <= dayCount; day += 1) { + const gregorianDate = resolveCalendarDayToGregorian(month, day); + rows.push({ + day, + gregorianDate: formatIsoDate(gregorianDate), + isResolved: Boolean(gregorianDate && !Number.isNaN(gregorianDate.getTime())) + }); + } + + state.dayLinksCache.set(cacheKey, rows); + return rows; + } + + function renderDayLinksCard(month) { + const rows = getMonthDayLinkRows(month); + if (!rows.length) { + return ""; + } + + const selectedContext = getSelectedDayFilterContext(month); + const selectedDaySet = selectedContext?.dayNumbers || new Set(); + const selectedDays = selectedContext?.entries?.map((entry) => entry.dayNumber) || []; + const selectedSummary = selectedDays.length + ? selectedDays.join(", ") + : ""; + + const links = rows.map((row) => { + if (!row.isResolved) { + return `${row.day}`; + } + + const isSelected = selectedDaySet.has(Number(row.day)); + return ``; + }).join(""); + + const clearButton = selectedContext + ? `` + : ""; + + const helperText = selectedContext + ? `
Filtered to days: ${selectedSummary}
` + : ""; + + return ` +
+ Day Links +
Filter this month to events, holidays, and data connected to a specific day.
+ ${helperText} +
${links}
+ ${clearButton ? `
${clearButton}
` : ""} +
+ `; + } + + function renderList(elements) { + const { monthListEl, monthCountEl, listTitleEl } = elements; + if (!monthListEl) { + return; + } + + monthListEl.innerHTML = ""; + + state.filteredMonths.forEach((month) => { + const isSelected = month.id === state.selectedMonthId; + const itemEl = document.createElement("div"); + itemEl.className = `planet-list-item${isSelected ? " is-selected" : ""}`; + itemEl.setAttribute("role", "option"); + itemEl.setAttribute("aria-selected", isSelected ? "true" : "false"); + itemEl.dataset.monthId = month.id; + + itemEl.innerHTML = ` +
${month.name || month.id}
+
${getMonthSubtitle(month)}
+ `; + + itemEl.addEventListener("click", () => { + selectByMonthId(month.id, elements); + }); + + monthListEl.appendChild(itemEl); + }); + + if (monthCountEl) { + monthCountEl.textContent = state.searchQuery + ? `${state.filteredMonths.length} of ${state.months.length} months` + : `${state.months.length} months`; + } + + if (listTitleEl) { + listTitleEl.textContent = "Calendar > Months"; + } + } + + function planetLabel(planetId) { + if (!planetId) { + return "Planet"; + } + + const planet = state.planetsById.get(planetId); + if (!planet) { + return cap(planetId); + } + + return `${planet.symbol || ""} ${planet.name || cap(planetId)}`.trim(); + } + + function zodiacLabel(signId) { + if (!signId) { + return "Zodiac"; + } + + const sign = state.signsById.get(signId); + if (!sign) { + return cap(signId); + } + + return `${sign.symbol || ""} ${sign.name || cap(signId)}`.trim(); + } + + function godLabel(godId, godName) { + if (godName) { + return godName; + } + + if (!godId) { + return "Deity"; + } + + const god = state.godsById.get(godId); + return god?.name || cap(godId); + } + + function hebrewLabel(hebrewLetterId) { + if (!hebrewLetterId) { + return "Hebrew Letter"; + } + + const letter = state.hebrewById.get(hebrewLetterId); + if (!letter) { + return cap(hebrewLetterId); + } + + return `${letter.char || ""} ${letter.name || cap(hebrewLetterId)}`.trim(); + } + + function computeDigitalRoot(value) { + let current = Math.abs(Math.trunc(Number(value))); + if (!Number.isFinite(current)) { + return null; + } + + while (current >= 10) { + current = String(current) + .split("") + .reduce((sum, digit) => sum + Number(digit), 0); + } + + return current; + } + + function buildAssociationButtons(associations) { + if (!associations || typeof associations !== "object") { + return "
--
"; + } + + const buttons = []; + + if (associations.planetId) { + buttons.push( + `` + ); + } + + if (associations.zodiacSignId) { + buttons.push( + `` + ); + } + + if (Number.isFinite(Number(associations.numberValue))) { + const rawNumber = Math.trunc(Number(associations.numberValue)); + if (rawNumber >= 0) { + const numberValue = computeDigitalRoot(rawNumber); + if (numberValue != null) { + const label = rawNumber === numberValue + ? `Number ${numberValue}` + : `Number ${numberValue} (from ${rawNumber})`; + buttons.push( + `` + ); + } + } + } + + if (associations.tarotCard) { + const trumpNumber = resolveTarotTrumpNumber(associations.tarotCard); + const explicitTrumpNumber = Number(associations.tarotTrumpNumber); + const tarotTrumpNumber = Number.isFinite(explicitTrumpNumber) ? explicitTrumpNumber : trumpNumber; + const tarotLabel = getDisplayTarotName(associations.tarotCard, tarotTrumpNumber); + buttons.push( + `` + ); + } + + if (associations.godId || associations.godName) { + const label = godLabel(associations.godId, associations.godName); + buttons.push( + `` + ); + } + + if (associations.hebrewLetterId) { + buttons.push( + `` + ); + } + + if (associations.kabbalahPathNumber != null) { + buttons.push( + `` + ); + } + + if (associations.iChingPlanetaryInfluence) { + buttons.push( + `` + ); + } + + if (!buttons.length) { + return "
--
"; + } + + return `
${buttons.join("")}
`; + } + + function associationSearchText(associations) { + if (!associations || typeof associations !== "object") { + return ""; + } + + const tarotAliases = associations.tarotCard && typeof getTarotCardSearchAliases === "function" + ? getTarotCardSearchAliases(associations.tarotCard, { trumpNumber: associations.tarotTrumpNumber }) + : []; + + return [ + associations.planetId, + associations.zodiacSignId, + associations.numberValue, + associations.tarotCard, + associations.tarotTrumpNumber, + ...tarotAliases, + associations.godId, + associations.godName, + associations.hebrewLetterId, + associations.kabbalahPathNumber, + associations.iChingPlanetaryInfluence + ].filter(Boolean).join(" "); + } + + function eventSearchText(event) { + return normalizeSearchValue([ + event?.name, + event?.date, + event?.dateRange, + event?.description, + associationSearchText(event?.associations) + ].filter(Boolean).join(" ")); + } + + function holidaySearchText(holiday) { + return normalizeSearchValue([ + holiday?.name, + holiday?.kind, + holiday?.date, + holiday?.dateRange, + holiday?.dateText, + holiday?.monthDayStart, + holiday?.calendarId, + holiday?.description, + associationSearchText(holiday?.associations) + ].filter(Boolean).join(" ")); + } + + function buildHolidayList(month) { + const calendarId = state.selectedCalendar; + const monthOrder = Number(month?.order); + + const fromRepo = state.calendarHolidays.filter((holiday) => { + const holidayCalendarId = String(holiday?.calendarId || "").trim().toLowerCase(); + if (holidayCalendarId !== calendarId) { + return false; + } + + const isDirectMonthMatch = String(holiday?.monthId || "").trim().toLowerCase() === String(month?.id || "").trim().toLowerCase(); + if (isDirectMonthMatch) { + return true; + } + + // For movable Gregorian holidays, place the holiday under the computed month for the selected year. + if (calendarId === "gregorian" && holiday?.dateRule && Number.isFinite(monthOrder)) { + const computedDate = resolveHolidayGregorianDate(holiday); + return computedDate instanceof Date + && !Number.isNaN(computedDate.getTime()) + && (computedDate.getMonth() + 1) === Math.trunc(monthOrder); + } + + return false; + }); + + if (fromRepo.length) { + return [...fromRepo].sort((left, right) => { + const leftDate = resolveHolidayGregorianDate(left); + const rightDate = resolveHolidayGregorianDate(right); + const leftDay = Number.isFinite(Number(left?.day)) + ? Number(left.day) + : ((leftDate instanceof Date && !Number.isNaN(leftDate.getTime())) ? leftDate.getDate() : NaN); + const rightDay = Number.isFinite(Number(right?.day)) + ? Number(right.day) + : ((rightDate instanceof Date && !Number.isNaN(rightDate.getTime())) ? rightDate.getDate() : NaN); + if (Number.isFinite(leftDay) && Number.isFinite(rightDay) && leftDay !== rightDay) { + return leftDay - rightDay; + } + return String(left?.name || "").localeCompare(String(right?.name || "")); + }); + } + + // Legacy fallback for old Gregorian-only holiday structure. + const seen = new Set(); + const ordered = []; + + (month.holidayIds || []).forEach((holidayId) => { + const holiday = state.holidays.find((item) => item.id === holidayId); + if (!holiday || seen.has(holiday.id)) { + return; + } + seen.add(holiday.id); + ordered.push(holiday); + }); + + state.holidays.forEach((holiday) => { + if (holiday?.monthId !== month.id || seen.has(holiday.id)) { + return; + } + seen.add(holiday.id); + ordered.push(holiday); + }); + + return ordered; + } + + function buildMonthSearchText(month) { + const calId = state.selectedCalendar; + const monthHolidays = buildHolidayList(month); + const holidayText = monthHolidays.map((holiday) => holidaySearchText(holiday)).join(" "); + + if (calId === "gregorian") { + const events = Array.isArray(month?.events) ? month.events : []; + + const searchable = [ + month?.name, + month?.id, + month?.start, + month?.end, + month?.coreTheme, + month?.seasonNorth, + month?.seasonSouth, + associationSearchText(month?.associations), + events.map((event) => eventSearchText(event)).join(" "), + holidayText + ]; + + return normalizeSearchValue(searchable.filter(Boolean).join(" ")); + } + + const wheelAssocText = month?.associations + ? [ + Array.isArray(month.associations.themes) ? month.associations.themes.join(" ") : "", + Array.isArray(month.associations.deities) ? month.associations.deities.join(" ") : "", + month.associations.element, + month.associations.direction + ].filter(Boolean).join(" ") + : ""; + + const searchable = [ + month?.name, + month?.id, + month?.nativeName, + month?.meaning, + month?.season, + month?.description, + month?.zodiacSign, + month?.tribe, + month?.element, + month?.type, + month?.date, + month?.hebrewLetter, + holidayText, + wheelAssocText + ]; + + return normalizeSearchValue(searchable.filter(Boolean).join(" ")); + } + + function matchesSearch(searchText) { + if (!state.searchQuery) { + return true; + } + return searchText.includes(state.searchQuery); + } + + function syncSearchControls(elements) { + if (elements.searchInputEl) { + elements.searchInputEl.value = state.searchQuery; + } + if (elements.searchClearEl) { + elements.searchClearEl.disabled = !state.searchQuery; + } + } + + function applySearchFilter(elements) { + state.filteredMonths = state.searchQuery + ? state.months.filter((month) => matchesSearch(buildMonthSearchText(month))) + : [...state.months]; + + if (!state.filteredMonths.some((month) => month.id === state.selectedMonthId)) { + state.selectedMonthId = state.filteredMonths[0]?.id || null; + } + + syncSearchControls(elements); + renderList(elements); + renderDetail(elements); + } + + function renderFactsCard(month) { + const monthOrder = Number(month?.order); + const daysInMonth = getDaysInMonth(state.selectedYear, monthOrder); + const hoursInMonth = Number.isFinite(daysInMonth) ? daysInMonth * 24 : null; + const firstWeekday = Number.isFinite(monthOrder) + ? getMonthStartWeekday(state.selectedYear, monthOrder) + : "--"; + const gregorianStartDate = getGregorianMonthStartDate(monthOrder); + const hebrewStartReference = formatCalendarDateFromGregorian(gregorianStartDate, "hebrew"); + const islamicStartReference = formatCalendarDateFromGregorian(gregorianStartDate, "islamic"); + + return ` +
+ Month Facts +
+
+
Year
${state.selectedYear}
+
Start Date (Gregorian)
${formatGregorianReferenceDate(gregorianStartDate)}
+
Days
${daysInMonth ?? "--"}
+
Hours
${hoursInMonth ?? "--"}
+
Starts On
${firstWeekday}
+
Hebrew On 1st
${hebrewStartReference}
+
Islamic On 1st
${islamicStartReference}
+
North Season
${month.seasonNorth || "--"}
+
South Season
${month.seasonSouth || "--"}
+
+
+
+ `; + } + + function renderAssociationsCard(month) { + const monthOrder = Number(month?.order); + const associations = { + ...(month?.associations || {}), + ...(Number.isFinite(monthOrder) ? { numberValue: Math.trunc(monthOrder) } : {}) + }; + + return ` +
+ Associations +
${month.coreTheme || "--"}
+ ${buildAssociationButtons(associations)} +
+ `; + } + + function renderEventsCard(month) { + const allEvents = Array.isArray(month?.events) ? month.events : []; + if (!allEvents.length) { + return ` +
+ Monthly Events +
No monthly events listed.
+
+ `; + } + + const selectedDay = getSelectedDayFilterContext(month); + + function eventMatchesDay(event) { + if (!selectedDay) { + return true; + } + + return selectedDay.entries.some((entry) => { + const targetDate = entry.gregorianDate; + const targetMonth = targetDate?.getMonth() + 1; + const targetDayNo = targetDate?.getDate(); + + const explicitDate = parseMonthDayToken(event?.date); + if (explicitDate && Number.isFinite(targetMonth) && Number.isFinite(targetDayNo)) { + return explicitDate.month === targetMonth && explicitDate.day === targetDayNo; + } + + const rangeTokens = parseMonthDayTokensFromText(event?.dateRange || event?.dateText || ""); + if (rangeTokens.length >= 2 && Number.isFinite(targetMonth) && Number.isFinite(targetDayNo)) { + const start = rangeTokens[0]; + const end = rangeTokens[1]; + return isMonthDayInRange(targetMonth, targetDayNo, start.month, start.day, end.month, end.day); + } + + const dayRange = parseDayRangeFromText(event?.date || event?.dateRange || event?.dateText || ""); + if (dayRange) { + return entry.dayNumber >= dayRange.startDay && entry.dayNumber <= dayRange.endDay; + } + + return false; + }); + } + + const dayFiltered = allEvents.filter((event) => eventMatchesDay(event)); + const events = state.searchQuery + ? dayFiltered.filter((event) => matchesSearch(eventSearchText(event))) + : dayFiltered; + + if (!events.length) { + return ` +
+ Monthly Events +
No monthly events match current search.
+
+ `; + } + + const rows = events.map((event) => { + const dateText = event?.date || event?.dateRange || "--"; + return ` +
+
+ ${event?.name || "Untitled"} + ${dateText} +
+
${event?.description || ""}
+ ${buildAssociationButtons(event?.associations)} +
+ `; + }).join(""); + + return ` +
+ Monthly Events +
${rows}
+
+ `; + } + + function renderHolidaysCard(month, title = "Holiday Repository") { + const allHolidays = buildHolidayList(month); + if (!allHolidays.length) { + return ` +
+ ${title} +
No holidays listed in the repository for this month.
+
+ `; + } + + const selectedDay = getSelectedDayFilterContext(month); + + function holidayMatchesDay(holiday) { + if (!selectedDay) { + return true; + } + + return selectedDay.entries.some((entry) => { + const targetDate = entry.gregorianDate; + const targetMonth = targetDate?.getMonth() + 1; + const targetDayNo = targetDate?.getDate(); + + const exactResolved = resolveHolidayGregorianDate(holiday); + if (exactResolved instanceof Date && !Number.isNaN(exactResolved.getTime()) && targetDate instanceof Date) { + return formatIsoDate(exactResolved) === formatIsoDate(targetDate); + } + + if (state.selectedCalendar === "gregorian" && Number.isFinite(targetMonth) && Number.isFinite(targetDayNo)) { + const tokens = parseMonthDayTokensFromText(holiday?.dateText || holiday?.dateRange || ""); + if (tokens.length >= 2) { + const start = tokens[0]; + const end = tokens[1]; + return isMonthDayInRange(targetMonth, targetDayNo, start.month, start.day, end.month, end.day); + } + + if (tokens.length === 1) { + const single = tokens[0]; + return single.month === targetMonth && single.day === targetDayNo; + } + + const direct = parseMonthDayStartToken(holiday?.monthDayStart || holiday?.dateText || ""); + if (direct) { + return direct.month === targetMonth && direct.day === targetDayNo; + } + + if (Number.isFinite(Number(holiday?.day))) { + return Number(holiday.day) === entry.dayNumber; + } + } + + const localRange = parseDayRangeFromText(holiday?.dateText || holiday?.dateRange || ""); + if (localRange) { + return entry.dayNumber >= localRange.startDay && entry.dayNumber <= localRange.endDay; + } + + if (Number.isFinite(Number(holiday?.day))) { + return Number(holiday.day) === entry.dayNumber; + } + + return false; + }); + } + + const dayFiltered = allHolidays.filter((holiday) => holidayMatchesDay(holiday)); + const holidays = state.searchQuery + ? dayFiltered.filter((holiday) => matchesSearch(holidaySearchText(holiday))) + : dayFiltered; + + if (!holidays.length) { + return ` +
+ ${title} +
No holidays match current search.
+
+ `; + } + + const rows = holidays.map((holiday) => { + const dateText = holiday?.dateText || holiday?.date || holiday?.dateRange || "--"; + const gregorianDate = resolveHolidayGregorianDate(holiday); + const gregorianRef = formatGregorianReferenceDate(gregorianDate); + const hebrewRef = formatCalendarDateFromGregorian(gregorianDate, "hebrew"); + const islamicRef = formatCalendarDateFromGregorian(gregorianDate, "islamic"); + const conversionConfidence = String(holiday?.conversionConfidence || holiday?.datePrecision || "approximate").toLowerCase(); + const conversionLabel = (!(gregorianDate instanceof Date) || Number.isNaN(gregorianDate.getTime())) + ? "Conversion: unresolved" + : (conversionConfidence === "exact" ? "Conversion: exact" : "Conversion: approximate"); + return ` +
+
+ ${holiday?.name || "Untitled"} + ${dateText} +
+
${cap(holiday?.kind || holiday?.calendarId || "observance")}
+
${conversionLabel}
+
Gregorian: ${gregorianRef}
+
Hebrew: ${hebrewRef}
+
Islamic: ${islamicRef}
+
${holiday?.description || ""}
+ ${buildAssociationButtons(holiday?.associations)} +
+ `; + }).join(""); + + return ` +
+ ${title} +
${rows}
+
+ `; + } + + function findSignIdByAstrologyName(name) { + const token = normalizeCalendarText(name); + if (!token) { + return null; + } + + for (const [signId, sign] of state.signsById) { + const idToken = normalizeCalendarText(signId); + const nameToken = normalizeCalendarText(sign?.name?.en || sign?.name || ""); + if (token === idToken || token === nameToken) { + return signId; + } + } + + return null; + } + + function intersectDateRanges(startA, endA, startB, endB) { + const start = startA.getTime() > startB.getTime() ? startA : startB; + const end = endA.getTime() < endB.getTime() ? endA : endB; + return start.getTime() <= end.getTime() ? { start, end } : null; + } + + function buildMajorArcanaRowsForMonth(month) { + if (state.selectedCalendar !== "gregorian") { + return []; + } + + const monthOrder = Number(month?.order); + if (!Number.isFinite(monthOrder)) { + return []; + } + + const monthStart = new Date(state.selectedYear, monthOrder - 1, 1, 12, 0, 0, 0); + const monthEnd = new Date(state.selectedYear, monthOrder, 0, 12, 0, 0, 0); + const rows = []; + + state.hebrewById.forEach((letter) => { + const astrologyType = normalizeCalendarText(letter?.astrology?.type); + if (astrologyType !== "zodiac") { + return; + } + + const signId = findSignIdByAstrologyName(letter?.astrology?.name); + const sign = signId ? state.signsById.get(signId) : null; + if (!sign) { + return; + } + + const startToken = parseMonthDayToken(sign?.start); + const endToken = parseMonthDayToken(sign?.end); + if (!startToken || !endToken) { + return; + } + + const spanStart = new Date(state.selectedYear, startToken.month - 1, startToken.day, 12, 0, 0, 0); + const spanEnd = new Date(state.selectedYear, endToken.month - 1, endToken.day, 12, 0, 0, 0); + const wraps = spanEnd.getTime() < spanStart.getTime(); + + const segments = wraps + ? [ + { + start: spanStart, + end: new Date(state.selectedYear, 11, 31, 12, 0, 0, 0) + }, + { + start: new Date(state.selectedYear, 0, 1, 12, 0, 0, 0), + end: spanEnd + } + ] + : [{ start: spanStart, end: spanEnd }]; + + segments.forEach((segment) => { + const overlap = intersectDateRanges(segment.start, segment.end, monthStart, monthEnd); + if (!overlap) { + return; + } + + const rangeStartDay = overlap.start.getDate(); + const rangeEndDay = overlap.end.getDate(); + const cardName = String(letter?.tarot?.card || "").trim(); + const trumpNumber = Number(letter?.tarot?.trumpNumber); + if (!cardName) { + return; + } + + rows.push({ + id: `${signId}-${rangeStartDay}-${rangeEndDay}`, + signId, + signName: sign?.name?.en || sign?.name || signId, + signSymbol: sign?.symbol || "", + cardName, + trumpNumber: Number.isFinite(trumpNumber) ? Math.trunc(trumpNumber) : null, + hebrewLetterId: String(letter?.hebrewLetterId || "").trim(), + hebrewLetterName: String(letter?.name || "").trim(), + hebrewLetterChar: String(letter?.char || "").trim(), + dayStart: rangeStartDay, + dayEnd: rangeEndDay, + rangeLabel: `${month?.name || "Month"} ${rangeStartDay}-${rangeEndDay}` + }); + }); + }); + + rows.sort((left, right) => { + if (left.dayStart !== right.dayStart) { + return left.dayStart - right.dayStart; + } + return left.cardName.localeCompare(right.cardName); + }); + + return rows; + } + + function renderMajorArcanaCard(month) { + const selectedDay = getSelectedDayFilterContext(month); + const allRows = buildMajorArcanaRowsForMonth(month); + + const rows = selectedDay + ? allRows.filter((row) => selectedDay.entries.some((entry) => entry.dayNumber >= row.dayStart && entry.dayNumber <= row.dayEnd)) + : allRows; + + if (!rows.length) { + return ` +
+ Major Arcana Windows +
No major arcana windows for this month.
+
+ `; + } + + const list = rows.map((row) => { + const hebrewLabel = row.hebrewLetterId + ? `${row.hebrewLetterChar ? `${row.hebrewLetterChar} ` : ""}${row.hebrewLetterName || row.hebrewLetterId}` + : "--"; + const displayCardName = getDisplayTarotName(row.cardName, row.trumpNumber); + + return ` +
+
+ ${displayCardName}${row.trumpNumber != null ? ` · Trump ${row.trumpNumber}` : ""} + ${row.rangeLabel} +
+
${row.signSymbol} ${row.signName} · Hebrew: ${hebrewLabel}
+
+ + + ${row.hebrewLetterId ? `` : ""} +
+
+ `; + }).join(""); + + return ` +
+ Major Arcana Windows +
${list}
+
+ `; + } + + function renderDecanTarotCard(month) { + const selectedDay = getSelectedDayFilterContext(month); + const allRows = buildDecanTarotRowsForMonth(month); + const rows = selectedDay + ? allRows.filter((row) => selectedDay.entries.some((entry) => { + const targetDate = entry.gregorianDate; + if (!(targetDate instanceof Date) || Number.isNaN(targetDate.getTime())) { + return false; + } + + const targetMonth = targetDate.getMonth() + 1; + const targetDayNo = targetDate.getDate(); + return isMonthDayInRange( + targetMonth, + targetDayNo, + row.startMonth, + row.startDay, + row.endMonth, + row.endDay + ); + })) + : allRows; + + if (!rows.length) { + return ` +
+ Decan Tarot Windows +
No decan tarot windows for this month.
+
+ `; + } + + const list = rows.map((row) => { + const displayCardName = getDisplayTarotName(row.cardName); + return ` +
+
+ ${row.signSymbol} ${row.signName} · Decan ${row.decanIndex} + ${row.startDegree}°–${row.endDegree}° · ${row.dateRange} +
+
+ +
+
+ `; + }).join(""); + + return ` +
+ Decan Tarot Windows +
${list}
+
+ `; + } + + function attachNavHandlers(detailBodyEl) { + if (!detailBodyEl) { + return; + } + + detailBodyEl.querySelectorAll("[data-nav]").forEach((button) => { + button.addEventListener("click", () => { + const navType = button.dataset.nav; + + if (navType === "planet" && button.dataset.planetId) { + document.dispatchEvent(new CustomEvent("nav:planet", { + detail: { planetId: button.dataset.planetId } + })); + return; + } + + if (navType === "zodiac" && button.dataset.signId) { + document.dispatchEvent(new CustomEvent("nav:zodiac", { + detail: { signId: button.dataset.signId } + })); + return; + } + + if (navType === "number" && button.dataset.numberValue) { + document.dispatchEvent(new CustomEvent("nav:number", { + detail: { value: Number(button.dataset.numberValue) } + })); + return; + } + + if (navType === "tarot-card" && button.dataset.cardName) { + const trumpNumber = Number(button.dataset.trumpNumber); + document.dispatchEvent(new CustomEvent("nav:tarot-trump", { + detail: { + cardName: button.dataset.cardName, + trumpNumber: Number.isFinite(trumpNumber) ? trumpNumber : undefined + } + })); + return; + } + + if (navType === "god") { + document.dispatchEvent(new CustomEvent("nav:gods", { + detail: { + godId: button.dataset.godId || undefined, + godName: button.dataset.godName || undefined + } + })); + return; + } + + if (navType === "alphabet" && button.dataset.hebrewLetterId) { + document.dispatchEvent(new CustomEvent("nav:alphabet", { + detail: { + alphabet: "hebrew", + hebrewLetterId: button.dataset.hebrewLetterId + } + })); + return; + } + + if (navType === "kabbalah" && button.dataset.pathNo) { + document.dispatchEvent(new CustomEvent("nav:kabbalah-path", { + detail: { pathNo: Number(button.dataset.pathNo) } + })); + return; + } + + if (navType === "iching" && button.dataset.planetaryInfluence) { + document.dispatchEvent(new CustomEvent("nav:iching", { + detail: { + planetaryInfluence: button.dataset.planetaryInfluence + } + })); + return; + } + + if (navType === "calendar-month" && button.dataset.monthId) { + document.dispatchEvent(new CustomEvent("nav:calendar-month", { + detail: { + calendarId: button.dataset.calendarId || undefined, + monthId: button.dataset.monthId + } + })); + return; + } + + if (navType === "calendar-day" && button.dataset.dayNumber) { + const month = getSelectedMonth(); + const dayNumber = Number(button.dataset.dayNumber); + if (!month || !Number.isFinite(dayNumber)) { + return; + } + + toggleDayFilterEntry(month, dayNumber, button.dataset.gregorianDate); + renderDetail(getElements()); + return; + } + + if (navType === "calendar-day-range" && button.dataset.rangeStart && button.dataset.rangeEnd) { + const month = getSelectedMonth(); + if (!month) { + return; + } + + toggleDayRangeFilter(month, Number(button.dataset.rangeStart), Number(button.dataset.rangeEnd)); + renderDetail(getElements()); + return; + } + + if (navType === "calendar-day-clear") { + clearSelectedDayFilter(); + renderDetail(getElements()); + } + }); + }); + } + + function renderHebrewMonthDetail(month) { + const gregorianStartDate = getGregorianReferenceDateForCalendarMonth(month); + const factsRows = [ + ["Hebrew Name", month.nativeName || "--"], + ["Month Order", month.leapYearOnly ? `${month.order} (leap year only)` : String(month.order)], + ["Gregorian Reference Year", String(state.selectedYear)], + ["Month Start (Gregorian)", formatGregorianReferenceDate(gregorianStartDate)], + ["Days", month.daysVariant ? `${month.days}–${month.daysVariant} (varies)` : String(month.days || "--")], + ["Season", month.season || "--"], + ["Zodiac Sign", cap(month.zodiacSign) || "--"], + ["Tribe of Israel", month.tribe || "--"], + ["Sense", month.sense || "--"], + ["Hebrew Letter", month.hebrewLetter || "--"] + ].map(([dt, dd]) => `
${dt}
${dd}
`).join(""); + + const monthOrder = Number(month?.order); + const navButtons = buildAssociationButtons({ + ...(month?.associations || {}), + ...(Number.isFinite(monthOrder) ? { numberValue: Math.trunc(monthOrder) } : {}) + }); + const connectionsCard = navButtons + ? `
Connections${navButtons}
` + : ""; + + return ` +
+
+ Month Facts +
+
${factsRows}
+
+
+ ${connectionsCard} +
+ About ${month.name} +
${month.description || "--"}
+
+ ${renderDayLinksCard(month)} + ${renderHolidaysCard(month, "Holiday Repository")} +
+ `; + } + + function renderIslamicMonthDetail(month) { + const gregorianStartDate = getGregorianReferenceDateForCalendarMonth(month); + const factsRows = [ + ["Arabic Name", month.nativeName || "--"], + ["Month Order", String(month.order)], + ["Gregorian Reference Year", String(state.selectedYear)], + ["Month Start (Gregorian)", formatGregorianReferenceDate(gregorianStartDate)], + ["Meaning", month.meaning || "--"], + ["Days", month.daysVariant ? `${month.days}–${month.daysVariant} (varies)` : String(month.days || "--")], + ["Sacred Month", month.sacred ? "Yes — warfare prohibited" : "No"] + ].map(([dt, dd]) => `
${dt}
${dd}
`).join(""); + + const monthOrder = Number(month?.order); + const hasNumberLink = Number.isFinite(monthOrder) && monthOrder >= 0; + const navButtons = hasNumberLink + ? buildAssociationButtons({ numberValue: Math.trunc(monthOrder) }) + : ""; + const connectionsCard = hasNumberLink + ? `
Connections${navButtons}
` + : ""; + + return ` +
+
+ Month Facts +
+
${factsRows}
+
+
+ ${connectionsCard} +
+ About ${month.name} +
${month.description || "--"}
+
+ ${renderDayLinksCard(month)} + ${renderHolidaysCard(month, "Holiday Repository")} +
+ `; + } + + function buildWheelDeityButtons(deities) { + const buttons = []; + (Array.isArray(deities) ? deities : []).forEach((rawName) => { + // Strip qualifiers like "(early)" or "/ Father Christmas" before matching + const cleanName = String(rawName || "").replace(/\s*\/.*$/, "").replace(/\s*\(.*\)$/, "").trim(); + const godId = findGodIdByName(cleanName) || findGodIdByName(rawName); + if (!godId) return; + const god = state.godsById.get(godId); + const label = god?.name || cleanName; + buttons.push(``); + }); + return buttons; + } + + function renderWheelMonthDetail(month) { + const gregorianStartDate = getGregorianReferenceDateForCalendarMonth(month); + const assoc = month?.associations; + const themes = Array.isArray(assoc?.themes) ? assoc.themes.join(", ") : "--"; + const deities = Array.isArray(assoc?.deities) ? assoc.deities.join(", ") : "--"; + const colors = Array.isArray(assoc?.colors) ? assoc.colors.join(", ") : "--"; + const herbs = Array.isArray(assoc?.herbs) ? assoc.herbs.join(", ") : "--"; + + const factsRows = [ + ["Date", month.date || "--"], + ["Type", cap(month.type) || "--"], + ["Gregorian Reference Year", String(state.selectedYear)], + ["Start (Gregorian)", formatGregorianReferenceDate(gregorianStartDate)], + ["Season", month.season || "--"], + ["Element", cap(month.element) || "--"], + ["Direction", assoc?.direction || "--"] + ].map(([dt, dd]) => `
${dt}
${dd}
`).join(""); + + const assocRows = [ + ["Themes", themes], + ["Deities", deities], + ["Colors", colors], + ["Herbs", herbs] + ].map(([dt, dd]) => `
${dt}
${dd}
`).join(""); + + const deityButtons = buildWheelDeityButtons(assoc?.deities); + const deityLinksCard = deityButtons.length + ? `
Linked Deities
${deityButtons.join("")}
` + : ""; + + const monthOrder = Number(month?.order); + const hasNumberLink = Number.isFinite(monthOrder) && monthOrder >= 0; + const numberButtons = hasNumberLink + ? buildAssociationButtons({ numberValue: Math.trunc(monthOrder) }) + : ""; + const numberLinksCard = hasNumberLink + ? `
Connections${numberButtons}
` + : ""; + + return ` +
+
+ Sabbat Facts +
+
${factsRows}
+
+
+
+ About ${month.name} +
${month.description || "--"}
+
+
+ Associations +
+
${assocRows}
+
+
+ ${renderDayLinksCard(month)} + ${numberLinksCard} + ${deityLinksCard} + ${renderHolidaysCard(month, "Holiday Repository")} +
+ `; + } + + function renderDetail(elements) { + const { detailNameEl, detailSubEl, detailBodyEl } = elements; + if (!detailBodyEl || !detailNameEl || !detailSubEl) { + return; + } + + const month = getSelectedMonth(); + if (!month) { + detailNameEl.textContent = "--"; + detailSubEl.textContent = "Select a month to explore"; + detailBodyEl.innerHTML = ""; + return; + } + + detailNameEl.textContent = month.name || month.id; + + const calId = state.selectedCalendar; + + if (calId === "gregorian") { + detailSubEl.textContent = `${parseMonthRange(month)} · ${month.coreTheme || "Month correspondences"}`; + detailBodyEl.innerHTML = ` +
+ ${renderFactsCard(month)} + ${renderDayLinksCard(month)} + ${renderAssociationsCard(month)} + ${renderMajorArcanaCard(month)} + ${renderDecanTarotCard(month)} + ${renderEventsCard(month)} + ${renderHolidaysCard(month, "Holiday Repository")} +
+ `; + } else if (calId === "hebrew") { + detailSubEl.textContent = getMonthSubtitle(month); + detailBodyEl.innerHTML = renderHebrewMonthDetail(month); + } else if (calId === "islamic") { + detailSubEl.textContent = getMonthSubtitle(month); + detailBodyEl.innerHTML = renderIslamicMonthDetail(month); + } else { + detailSubEl.textContent = getMonthSubtitle(month); + detailBodyEl.innerHTML = renderWheelMonthDetail(month); + } + + attachNavHandlers(detailBodyEl); + } + + function selectByMonthId(monthId, elements = getElements()) { + const target = state.months.find((month) => month.id === monthId); + if (!target) { + return false; + } + + if (state.searchQuery && !state.filteredMonths.some((month) => month.id === target.id)) { + state.searchQuery = ""; + state.filteredMonths = [...state.months]; + } + + if (state.selectedMonthId !== target.id) { + clearSelectedDayFilter(); + } + + state.selectedMonthId = target.id; + syncSearchControls(elements); + renderList(elements); + renderDetail(elements); + return true; + } + + function selectCalendarType(calendarId, elements = getElements()) { + if (!state.calendarData || !Array.isArray(state.calendarData[calendarId])) { + return false; + } + + if (elements.calendarTypeEl) { + elements.calendarTypeEl.value = calendarId; + } + + loadCalendarType(calendarId, elements); + return true; + } + + function bindYearInput(elements) { + if (!elements.yearInputEl) { + return; + } + + elements.yearInputEl.value = String(state.selectedYear); + elements.yearInputEl.addEventListener("change", () => { + const nextYear = Number(elements.yearInputEl.value); + if (!Number.isFinite(nextYear)) { + elements.yearInputEl.value = String(state.selectedYear); + return; + } + + state.selectedYear = Math.min(2500, Math.max(1900, Math.round(nextYear))); + elements.yearInputEl.value = String(state.selectedYear); + state.dayLinksCache = new Map(); + clearSelectedDayFilter(); + renderDetail(elements); + }); + } + + function bindSearchInput(elements) { + if (elements.searchInputEl) { + elements.searchInputEl.addEventListener("input", () => { + state.searchQuery = normalizeSearchValue(elements.searchInputEl.value); + applySearchFilter(elements); + }); + } + + if (elements.searchClearEl && elements.searchInputEl) { + elements.searchClearEl.addEventListener("click", () => { + state.searchQuery = ""; + elements.searchInputEl.value = ""; + applySearchFilter(elements); + elements.searchInputEl.focus(); + }); + } + } + + function loadCalendarType(calendarId, elements) { + const months = state.calendarData[calendarId]; + if (!Array.isArray(months)) { + return; + } + + state.selectedCalendar = calendarId; + state.dayLinksCache = new Map(); + clearSelectedDayFilter(); + state.months = sortMonths(months); + state.filteredMonths = [...state.months]; + state.selectedMonthId = state.months[0]?.id || null; + state.searchQuery = ""; + + if (elements.calendarYearWrapEl) { + elements.calendarYearWrapEl.hidden = false; + } + + syncSearchControls(elements); + applySearchFilter(elements); + } + + function bindCalendarTypeSelect(elements) { + if (!elements.calendarTypeEl) { + return; + } + + elements.calendarTypeEl.value = state.selectedCalendar; + elements.calendarTypeEl.addEventListener("change", () => { + const calId = String(elements.calendarTypeEl.value || "gregorian"); + loadCalendarType(calId, elements); + }); + } + + function ensureCalendarSection(referenceData, magickDataset) { + if (!referenceData) { + return; + } + + state.referenceData = referenceData; + state.magickDataset = magickDataset || null; + state.dayLinksCache = new Map(); + clearSelectedDayFilter(); + state.holidays = Array.isArray(referenceData.celestialHolidays) ? referenceData.celestialHolidays : []; + state.calendarHolidays = Array.isArray(referenceData.calendarHolidays) ? referenceData.calendarHolidays : []; + state.planetsById = buildPlanetMap(referenceData.planets); + state.signsById = buildSignsMap(referenceData.signs); + state.godsById = buildGodsMap(state.magickDataset); + state.hebrewById = buildHebrewMap(state.magickDataset); + + state.calendarData = { + gregorian: Array.isArray(referenceData.calendarMonths) ? referenceData.calendarMonths : [], + hebrew: Array.isArray(referenceData.hebrewCalendar?.months) ? referenceData.hebrewCalendar.months : [], + islamic: Array.isArray(referenceData.islamicCalendar?.months) ? referenceData.islamicCalendar.months : [], + "wheel-of-year": Array.isArray(referenceData.wheelOfYear?.months) ? referenceData.wheelOfYear.months : [] + }; + + const currentCalMonths = state.calendarData[state.selectedCalendar] || state.calendarData.gregorian || []; + state.months = sortMonths(currentCalMonths); + state.filteredMonths = [...state.months]; + + const elements = getElements(); + + if (elements.calendarYearWrapEl) { + elements.calendarYearWrapEl.hidden = false; + } + + if (!state.months.length) { + if (elements.detailNameEl) { + elements.detailNameEl.textContent = "Calendar"; + } + if (elements.detailSubEl) { + elements.detailSubEl.textContent = "No month data available."; + } + if (elements.detailBodyEl) { + elements.detailBodyEl.innerHTML = ""; + } + if (elements.monthListEl) { + elements.monthListEl.innerHTML = ""; + } + return; + } + + if (!state.selectedMonthId || !state.months.some((month) => month.id === state.selectedMonthId)) { + state.selectedMonthId = state.months[0].id; + } + + if (!state.initialized) { + state.initialized = true; + bindYearInput(elements); + bindSearchInput(elements); + bindCalendarTypeSelect(elements); + } + + applySearchFilter(elements); + } + + window.CalendarSectionUi = { + ensureCalendarSection, + selectByMonthId, + selectCalendarType + }; +})(); diff --git a/app/ui-cube.js b/app/ui-cube.js new file mode 100644 index 0000000..c17457b --- /dev/null +++ b/app/ui-cube.js @@ -0,0 +1,1901 @@ +(function () { + "use strict"; + + const state = { + initialized: false, + controlsBound: false, + cube: null, + hebrewLetters: null, + kabbalahPathsByLetterId: new Map(), + markerDisplayMode: "both", + rotationX: 18, + rotationY: -28, + selectedNodeType: "wall", + showConnectorLines: true, + showPrimalPoint: true, + selectedConnectorId: null, + selectedWallId: null, + selectedEdgeId: null + }; + + const CUBE_VERTICES = [ + [-1, -1, -1], + [1, -1, -1], + [1, 1, -1], + [-1, 1, -1], + [-1, -1, 1], + [1, -1, 1], + [1, 1, 1], + [-1, 1, 1] + ]; + + const FACE_GEOMETRY = { + north: [4, 5, 6, 7], + south: [1, 0, 3, 2], + east: [5, 1, 2, 6], + west: [0, 4, 7, 3], + above: [0, 1, 5, 4], + below: [7, 6, 2, 3] + }; + + const EDGE_GEOMETRY = [ + [0, 1], [1, 2], [2, 3], [3, 0], + [4, 5], [5, 6], [6, 7], [7, 4], + [0, 4], [1, 5], [2, 6], [3, 7] + ]; + + const WALL_ORDER = ["north", "south", "east", "west", "above", "below"]; + const EDGE_ORDER = [ + "north-east", + "south-east", + "east-above", + "east-below", + "north-above", + "north-below", + "north-west", + "south-west", + "west-above", + "west-below", + "south-above", + "south-below" + ]; + const EDGE_GEOMETRY_KEYS = [ + "south-above", + "south-east", + "south-below", + "south-west", + "north-above", + "north-east", + "north-below", + "north-west", + "west-above", + "east-above", + "east-below", + "west-below" + ]; + const CUBE_VIEW_CENTER = { x: 110, y: 108 }; + const LOCAL_DIRECTION_ORDER = ["east", "south", "west", "north"]; + const LOCAL_DIRECTION_RANK = { + east: 0, + south: 1, + west: 2, + north: 3 + }; + const LOCAL_DIRECTION_VIEW_MAP = { + north: "east", + east: "south", + south: "west", + west: "north" + }; + const MOTHER_CONNECTORS = [ + { + id: "above-below", + fromWallId: "above", + toWallId: "below", + hebrewLetterId: "alef", + name: "Above ↔ Below" + }, + { + id: "east-west", + fromWallId: "east", + toWallId: "west", + hebrewLetterId: "mem", + name: "East ↔ West" + }, + { + id: "south-north", + fromWallId: "south", + toWallId: "north", + hebrewLetterId: "shin", + name: "South ↔ North" + } + ]; + + const WALL_FRONT_ROTATIONS = { + north: { x: 0, y: 0 }, + south: { x: 0, y: 180 }, + east: { x: 0, y: -90 }, + west: { x: 0, y: 90 }, + above: { x: -90, y: 0 }, + below: { x: 90, y: 0 } + }; + + function getElements() { + return { + viewContainerEl: document.getElementById("cube-view-container"), + rotateLeftEl: document.getElementById("cube-rotate-left"), + rotateRightEl: document.getElementById("cube-rotate-right"), + rotateUpEl: document.getElementById("cube-rotate-up"), + rotateDownEl: document.getElementById("cube-rotate-down"), + rotateResetEl: document.getElementById("cube-rotate-reset"), + markerModeEl: document.getElementById("cube-marker-mode"), + connectorToggleEl: document.getElementById("cube-connector-toggle"), + primalToggleEl: document.getElementById("cube-primal-toggle"), + rotationReadoutEl: document.getElementById("cube-rotation-readout"), + detailNameEl: document.getElementById("cube-detail-name"), + detailSubEl: document.getElementById("cube-detail-sub"), + detailBodyEl: document.getElementById("cube-detail-body") + }; + } + + function normalizeId(value) { + return String(value || "").trim().toLowerCase(); + } + + function normalizeLetterKey(value) { + const key = normalizeId(value).replace(/[^a-z]/g, ""); + const aliases = { + aleph: "alef", + beth: "bet", + zain: "zayin", + cheth: "het", + chet: "het", + daleth: "dalet", + kaf: "kaf", + kaph: "kaf", + teth: "tet", + peh: "pe", + tzaddi: "tsadi", + tzadi: "tsadi", + tzade: "tsadi", + tsaddi: "tsadi", + qoph: "qof", + taw: "tav", + tau: "tav" + }; + return aliases[key] || key; + } + + function asRecord(value) { + return value && typeof value === "object" && !Array.isArray(value) ? value : null; + } + + function getWalls() { + const walls = Array.isArray(state.cube?.walls) ? state.cube.walls : []; + return walls + .slice() + .sort((left, right) => WALL_ORDER.indexOf(normalizeId(left?.id)) - WALL_ORDER.indexOf(normalizeId(right?.id))); + } + + function getWallById(wallId) { + const target = normalizeId(wallId); + return getWalls().find((wall) => normalizeId(wall?.id) === target) || null; + } + + function normalizeEdgeId(value) { + return normalizeId(value).replace(/[\s_]+/g, "-"); + } + + function formatEdgeName(edgeId) { + return normalizeEdgeId(edgeId) + .split("-") + .filter(Boolean) + .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) + .join(" "); + } + + function formatDirectionName(direction) { + const key = normalizeId(direction); + return key ? `${key.charAt(0).toUpperCase()}${key.slice(1)}` : ""; + } + + function getEdges() { + const configuredEdges = Array.isArray(state.cube?.edges) ? state.cube.edges : []; + const byId = new Map( + configuredEdges.map((edge) => [normalizeEdgeId(edge?.id), edge]) + ); + + return EDGE_ORDER.map((edgeId) => { + const configured = byId.get(edgeId); + if (configured) { + return configured; + } + return { + id: edgeId, + name: formatEdgeName(edgeId), + walls: edgeId.split("-") + }; + }); + } + + function getEdgeById(edgeId) { + const target = normalizeEdgeId(edgeId); + return getEdges().find((edge) => normalizeEdgeId(edge?.id) === target) || null; + } + + function getEdgeWalls(edge) { + const explicitWalls = Array.isArray(edge?.walls) + ? edge.walls.map((wallId) => normalizeId(wallId)).filter(Boolean) + : []; + + if (explicitWalls.length >= 2) { + return explicitWalls.slice(0, 2); + } + + return normalizeEdgeId(edge?.id) + .split("-") + .map((wallId) => normalizeId(wallId)) + .filter(Boolean) + .slice(0, 2); + } + + function getEdgesForWall(wallOrWallId) { + const wallId = normalizeId(typeof wallOrWallId === "string" ? wallOrWallId : wallOrWallId?.id); + return getEdges().filter((edge) => getEdgeWalls(edge).includes(wallId)); + } + + function toFiniteNumber(value) { + const numeric = Number(value); + return Number.isFinite(numeric) ? numeric : null; + } + + function normalizeAngle(angle) { + let next = angle; + while (next > 180) { + next -= 360; + } + while (next <= -180) { + next += 360; + } + return next; + } + + function setRotation(nextX, nextY) { + state.rotationX = normalizeAngle(nextX); + state.rotationY = normalizeAngle(nextY); + } + + function snapRotationToWall(wallId) { + const target = WALL_FRONT_ROTATIONS[normalizeId(wallId)]; + if (!target) { + return; + } + setRotation(target.x, target.y); + } + + function facePoint(quad, u, v) { + const weight0 = ((1 - u) * (1 - v)) / 4; + const weight1 = ((1 + u) * (1 - v)) / 4; + const weight2 = ((1 + u) * (1 + v)) / 4; + const weight3 = ((1 - u) * (1 + v)) / 4; + + return { + x: quad[0].x * weight0 + quad[1].x * weight1 + quad[2].x * weight2 + quad[3].x * weight3, + y: quad[0].y * weight0 + quad[1].y * weight1 + quad[2].y * weight2 + quad[3].y * weight3 + }; + } + + function projectVerticesForRotation(rotationX, rotationY) { + const yaw = (rotationY * Math.PI) / 180; + const pitch = (rotationX * Math.PI) / 180; + + const cosY = Math.cos(yaw); + const sinY = Math.sin(yaw); + const cosX = Math.cos(pitch); + const sinX = Math.sin(pitch); + + const centerX = CUBE_VIEW_CENTER.x; + const centerY = CUBE_VIEW_CENTER.y; + const scale = 54; + const camera = 4.6; + + return CUBE_VERTICES.map(([x, y, z]) => { + const x1 = x * cosY + z * sinY; + const z1 = -x * sinY + z * cosY; + + const y2 = y * cosX - z1 * sinX; + const z2 = y * sinX + z1 * cosX; + + const perspective = camera / (camera - z2); + + return { + x: centerX + x1 * scale * perspective, + y: centerY + y2 * scale * perspective, + z: z2 + }; + }); + } + + function projectVertices() { + return projectVerticesForRotation(state.rotationX, state.rotationY); + } + + function getEdgeGeometryById(edgeId) { + const canonicalId = normalizeEdgeId(edgeId); + const geometryIndex = EDGE_GEOMETRY_KEYS.indexOf(canonicalId); + if (geometryIndex < 0) { + return null; + } + return EDGE_GEOMETRY[geometryIndex] || null; + } + + function getWallEdgeDirections(wallOrWallId) { + const wallId = normalizeId(typeof wallOrWallId === "string" ? wallOrWallId : wallOrWallId?.id); + const faceIndices = FACE_GEOMETRY[wallId]; + if (!Array.isArray(faceIndices) || faceIndices.length !== 4) { + return new Map(); + } + + const frontRotation = WALL_FRONT_ROTATIONS[wallId] || { + x: state.rotationX, + y: state.rotationY + }; + const projectedVertices = projectVerticesForRotation(frontRotation.x, frontRotation.y); + const quad = faceIndices.map((index) => projectedVertices[index]); + const center = facePoint(quad, 0, 0); + const directionsByEdgeId = new Map(); + + getEdgesForWall(wallId).forEach((edge) => { + const geometry = getEdgeGeometryById(edge?.id); + if (!geometry) { + return; + } + + const [fromIndex, toIndex] = geometry; + const from = projectedVertices[fromIndex]; + const to = projectedVertices[toIndex]; + if (!from || !to) { + return; + } + + const midpointX = (from.x + to.x) / 2; + const midpointY = (from.y + to.y) / 2; + const dx = midpointX - center.x; + const dy = midpointY - center.y; + + const directionByPosition = Math.abs(dx) >= Math.abs(dy) + ? (dx >= 0 ? "east" : "west") + : (dy >= 0 ? "south" : "north"); + const direction = LOCAL_DIRECTION_VIEW_MAP[directionByPosition] || directionByPosition; + + directionsByEdgeId.set(normalizeEdgeId(edge?.id), direction); + }); + + return directionsByEdgeId; + } + + function getEdgeDirectionForWall(wallId, edgeId) { + const wallKey = normalizeId(wallId); + const edgeKey = normalizeEdgeId(edgeId); + if (!wallKey || !edgeKey) { + return ""; + } + + const directions = getWallEdgeDirections(wallKey); + return directions.get(edgeKey) || ""; + } + + function getEdgeDirectionLabelForWall(wallId, edgeId) { + return formatDirectionName(getEdgeDirectionForWall(wallId, edgeId)); + } + + function bindRotationControls(elements) { + if (state.controlsBound) { + return; + } + + const rotateAndRender = (deltaX, deltaY) => { + setRotation(state.rotationX + deltaX, state.rotationY + deltaY); + render(getElements()); + }; + + elements.rotateLeftEl?.addEventListener("click", () => rotateAndRender(0, -9)); + elements.rotateRightEl?.addEventListener("click", () => rotateAndRender(0, 9)); + elements.rotateUpEl?.addEventListener("click", () => rotateAndRender(-9, 0)); + elements.rotateDownEl?.addEventListener("click", () => rotateAndRender(9, 0)); + elements.rotateResetEl?.addEventListener("click", () => { + setRotation(18, -28); + render(getElements()); + }); + + elements.markerModeEl?.addEventListener("change", (event) => { + const nextMode = normalizeId(event?.target?.value); + state.markerDisplayMode = ["both", "letter", "astro", "tarot"].includes(nextMode) + ? nextMode + : "both"; + render(getElements()); + }); + + if (elements.connectorToggleEl) { + elements.connectorToggleEl.checked = state.showConnectorLines; + elements.connectorToggleEl.addEventListener("change", () => { + state.showConnectorLines = Boolean(elements.connectorToggleEl.checked); + if (!state.showConnectorLines && state.selectedNodeType === "connector") { + state.selectedNodeType = "wall"; + state.selectedConnectorId = null; + } + render(getElements()); + }); + } + + if (elements.primalToggleEl) { + elements.primalToggleEl.checked = state.showPrimalPoint; + elements.primalToggleEl.addEventListener("change", () => { + state.showPrimalPoint = Boolean(elements.primalToggleEl.checked); + if (!state.showPrimalPoint && state.selectedNodeType === "center") { + state.selectedNodeType = "wall"; + } + render(getElements()); + }); + } + + state.controlsBound = true; + } + + function getHebrewLetterSymbol(hebrewLetterId) { + const id = normalizeLetterKey(hebrewLetterId); + if (!id || !state.hebrewLetters) { + return ""; + } + + const entry = state.hebrewLetters[id]; + if (!entry || typeof entry !== "object") { + return ""; + } + + const symbol = String( + entry?.letter?.he || entry?.he || entry?.glyph || entry?.symbol || "" + ).trim(); + + return symbol; + } + + function getHebrewLetterName(hebrewLetterId) { + const id = normalizeLetterKey(hebrewLetterId); + if (!id || !state.hebrewLetters) { + return ""; + } + + const entry = state.hebrewLetters[id]; + if (!entry || typeof entry !== "object") { + return ""; + } + + const name = String(entry?.letter?.name || entry?.name || "").trim(); + return name; + } + + function getAstrologySymbol(type, name) { + const normalizedType = normalizeId(type); + const normalizedName = normalizeId(name); + + const planetSymbols = { + mercury: "☿︎", + venus: "♀︎", + mars: "♂︎", + jupiter: "♃︎", + saturn: "♄︎", + sol: "☉︎", + sun: "☉︎", + luna: "☾︎", + moon: "☾︎", + earth: "⊕", + uranus: "♅︎", + neptune: "♆︎", + pluto: "♇︎" + }; + + const zodiacSymbols = { + aries: "♈︎", + taurus: "♉︎", + gemini: "♊︎", + cancer: "♋︎", + leo: "♌︎", + virgo: "♍︎", + libra: "♎︎", + scorpio: "♏︎", + sagittarius: "♐︎", + capricorn: "♑︎", + aquarius: "♒︎", + pisces: "♓︎" + }; + + const elementSymbols = { + fire: "🜂", + water: "🜄", + air: "🜁", + earth: "🜃", + spirit: "🜀" + }; + + if (normalizedType === "planet") { + return planetSymbols[normalizedName] || ""; + } + + if (normalizedType === "zodiac") { + return zodiacSymbols[normalizedName] || ""; + } + + if (normalizedType === "element") { + return elementSymbols[normalizedName] || ""; + } + + return ""; + } + + function getEdgeLetterId(edge) { + return normalizeLetterKey(edge?.hebrewLetterId || edge?.associations?.hebrewLetterId); + } + + function getWallFaceLetterId(wall) { + return normalizeLetterKey(wall?.hebrewLetterId || wall?.associations?.hebrewLetterId); + } + + function getWallFaceLetter(wall) { + const hebrewLetterId = getWallFaceLetterId(wall); + if (!hebrewLetterId) { + return ""; + } + return getHebrewLetterSymbol(hebrewLetterId); + } + + function getCubeCenterData() { + const center = state.cube?.center; + return center && typeof center === "object" ? center : null; + } + + function getCenterLetterId(center = null) { + const entry = center || getCubeCenterData(); + return normalizeLetterKey(entry?.hebrewLetterId || entry?.associations?.hebrewLetterId || entry?.letter); + } + + function getCenterLetterSymbol(center = null) { + const centerLetterId = getCenterLetterId(center); + if (!centerLetterId) { + return ""; + } + return getHebrewLetterSymbol(centerLetterId); + } + + function getConnectorById(connectorId) { + const target = normalizeId(connectorId); + return MOTHER_CONNECTORS.find((entry) => normalizeId(entry?.id) === target) || null; + } + + function getConnectorPathEntry(connector) { + const letterId = normalizeLetterKey(connector?.hebrewLetterId); + if (!letterId) { + return null; + } + return state.kabbalahPathsByLetterId.get(letterId) || null; + } + + function getEdgePathEntry(edge) { + const hebrewLetterId = getEdgeLetterId(edge); + + if (!hebrewLetterId) { + return null; + } + + return state.kabbalahPathsByLetterId.get(hebrewLetterId) || null; + } + + function getEdgeAstrologySymbol(edge) { + const pathEntry = getEdgePathEntry(edge); + const astrology = pathEntry?.astrology || {}; + return getAstrologySymbol(astrology.type, astrology.name); + } + + function getEdgeMarkerDisplay(edge) { + const letter = getEdgeLetter(edge); + const astro = getEdgeAstrologySymbol(edge); + + if (state.markerDisplayMode === "letter") { + return letter + ? { text: letter, isMissing: false } + : { text: "!", isMissing: true }; + } + + if (state.markerDisplayMode === "astro") { + return astro + ? { text: astro, isMissing: false } + : { text: "!", isMissing: true }; + } + + if (letter && astro) { + return { text: `${letter} ${astro}`, isMissing: false }; + } + + return { text: "!", isMissing: true }; + } + + function getEdgeLetter(edge) { + const hebrewLetterId = getEdgeLetterId(edge); + if (!hebrewLetterId) { + return ""; + } + + return getHebrewLetterSymbol(hebrewLetterId); + } + + function getWallTarotCard(wall) { + return toDisplayText(wall?.associations?.tarotCard || wall?.tarotCard); + } + + function getEdgeTarotCard(edge) { + const pathEntry = getEdgePathEntry(edge); + return toDisplayText(pathEntry?.tarot?.card); + } + + function getConnectorTarotCard(connector) { + const pathEntry = getConnectorPathEntry(connector); + return toDisplayText(pathEntry?.tarot?.card); + } + + function getCenterTarotCard(center = null) { + const entry = center || getCubeCenterData(); + return toDisplayText(entry?.associations?.tarotCard || entry?.tarotCard); + } + + function resolveCardImageUrl(cardName) { + const name = toDisplayText(cardName); + if (!name || typeof window.TarotCardImages?.resolveTarotCardImage !== "function") { + return null; + } + return window.TarotCardImages.resolveTarotCardImage(name) || null; + } + + function applyPlacement(placement) { + const fallbackWallId = normalizeId(getWalls()[0]?.id); + const nextWallId = normalizeId(placement?.wallId || placement?.wall?.id || state.selectedWallId || fallbackWallId); + const wall = getWallById(nextWallId); + if (!wall) { + return false; + } + + state.selectedWallId = normalizeId(wall.id); + + const candidateEdgeId = normalizeEdgeId(placement?.edgeId || placement?.edge?.id); + const wallEdges = getEdgesForWall(state.selectedWallId); + const resolvedEdgeId = candidateEdgeId && getEdgeById(candidateEdgeId) + ? candidateEdgeId + : normalizeEdgeId(wallEdges[0]?.id || getEdges()[0]?.id); + + state.selectedEdgeId = resolvedEdgeId; + state.selectedNodeType = "wall"; + state.selectedConnectorId = null; + render(getElements()); + return true; + } + + function createMetaCard(title, bodyContent) { + const card = document.createElement("div"); + card.className = "planet-meta-card"; + + const titleEl = document.createElement("strong"); + titleEl.textContent = title; + card.appendChild(titleEl); + + if (typeof bodyContent === "string") { + const bodyEl = document.createElement("p"); + bodyEl.className = "planet-text"; + bodyEl.textContent = bodyContent; + card.appendChild(bodyEl); + } else if (bodyContent instanceof Node) { + card.appendChild(bodyContent); + } + + return card; + } + + function createNavButton(label, eventName, detail) { + const button = document.createElement("button"); + button.type = "button"; + button.className = "kab-god-link"; + button.textContent = `${label} ↗`; + button.addEventListener("click", () => { + document.dispatchEvent(new CustomEvent(eventName, { detail })); + }); + return button; + } + + function toDisplayText(value) { + return String(value ?? "").trim(); + } + + function escapeHtml(value) { + return String(value) + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/\"/g, """) + .replace(/'/g, "'"); + } + + function toDetailValueMarkup(value) { + const text = toDisplayText(value); + return text ? escapeHtml(text) : '!'; + } + + function renderFaceSvg(containerEl, walls) { + if (!containerEl) { + return; + } + + const svgNS = "http://www.w3.org/2000/svg"; + const svg = document.createElementNS(svgNS, "svg"); + svg.setAttribute("viewBox", "0 0 240 220"); + svg.setAttribute("width", "100%"); + svg.setAttribute("class", "cube-svg"); + svg.setAttribute("role", "img"); + svg.setAttribute("aria-label", "Cube of Space interactive chassis"); + + const wallById = new Map(walls.map((wall) => [normalizeId(wall?.id), wall])); + const projectedVertices = projectVertices(); + const faces = Object.entries(FACE_GEOMETRY) + .map(([wallId, indices]) => { + const wall = wallById.get(wallId); + if (!wall) { + return null; + } + + const quad = indices.map((index) => projectedVertices[index]); + const avgDepth = quad.reduce((sum, point) => sum + point.z, 0) / quad.length; + + return { + wallId, + wall, + quad, + depth: avgDepth, + pointsText: quad.map((point) => `${point.x.toFixed(2)},${point.y.toFixed(2)}`).join(" ") + }; + }) + .filter(Boolean) + .sort((left, right) => left.depth - right.depth); + + faces.forEach((faceData) => { + const { wallId, wall, quad, pointsText } = faceData; + + const isActive = wallId === normalizeId(state.selectedWallId); + const polygon = document.createElementNS(svgNS, "polygon"); + polygon.setAttribute("points", pointsText); + polygon.setAttribute("class", `cube-face${isActive ? " is-active" : ""}`); + polygon.setAttribute("fill", "#000"); + polygon.setAttribute("fill-opacity", isActive ? "0.78" : "0.62"); + polygon.setAttribute("stroke", "currentColor"); + polygon.setAttribute("stroke-opacity", isActive ? "0.92" : "0.68"); + polygon.setAttribute("stroke-width", isActive ? "2.5" : "1"); + polygon.setAttribute("data-wall-id", wallId); + polygon.setAttribute("role", "button"); + polygon.setAttribute("tabindex", "0"); + polygon.setAttribute("aria-label", `Cube wall ${wall?.name || wallId}`); + + const selectWall = () => { + state.selectedWallId = wallId; + state.selectedEdgeId = normalizeEdgeId(getEdgesForWall(wallId)[0]?.id || getEdges()[0]?.id); + state.selectedNodeType = "wall"; + state.selectedConnectorId = null; + snapRotationToWall(wallId); + render(getElements()); + }; + + polygon.addEventListener("click", selectWall); + polygon.addEventListener("keydown", (event) => { + if (event.key === "Enter" || event.key === " ") { + event.preventDefault(); + selectWall(); + } + }); + + svg.appendChild(polygon); + + const wallFaceLetter = getWallFaceLetter(wall); + const faceGlyphAnchor = facePoint(quad, 0, 0); + + if (state.markerDisplayMode === "tarot") { + const cardUrl = resolveCardImageUrl(getWallTarotCard(wall)); + if (cardUrl) { + let defs = svg.querySelector("defs"); + if (!defs) { + defs = document.createElementNS(svgNS, "defs"); + svg.insertBefore(defs, svg.firstChild); + } + const clipId = `face-clip-${wallId}`; + const clipPath = document.createElementNS(svgNS, "clipPath"); + clipPath.setAttribute("id", clipId); + const clipPoly = document.createElementNS(svgNS, "polygon"); + clipPoly.setAttribute("points", pointsText); + clipPath.appendChild(clipPoly); + defs.appendChild(clipPath); + + const cardW = 40, cardH = 60; + const cardImg = document.createElementNS(svgNS, "image"); + cardImg.setAttribute("href", cardUrl); + cardImg.setAttribute("x", String((faceGlyphAnchor.x - cardW / 2).toFixed(2))); + cardImg.setAttribute("y", String((faceGlyphAnchor.y - cardH / 2).toFixed(2))); + cardImg.setAttribute("width", String(cardW)); + cardImg.setAttribute("height", String(cardH)); + cardImg.setAttribute("clip-path", `url(#${clipId})`); + cardImg.setAttribute("role", "button"); + cardImg.setAttribute("tabindex", "0"); + cardImg.setAttribute("aria-label", `Cube wall ${wall?.name || wallId}`); + cardImg.setAttribute("preserveAspectRatio", "xMidYMid meet"); + cardImg.addEventListener("click", selectWall); + cardImg.addEventListener("keydown", (event) => { + if (event.key === "Enter" || event.key === " ") { + event.preventDefault(); + selectWall(); + } + }); + svg.appendChild(cardImg); + } + } else { + const faceGlyph = document.createElementNS(svgNS, "text"); + faceGlyph.setAttribute( + "class", + `cube-face-symbol${isActive ? " is-active" : ""}${wallFaceLetter ? "" : " is-missing"}` + ); + faceGlyph.setAttribute("x", String(faceGlyphAnchor.x)); + faceGlyph.setAttribute("y", String(faceGlyphAnchor.y)); + faceGlyph.setAttribute("text-anchor", "middle"); + faceGlyph.setAttribute("dominant-baseline", "middle"); + faceGlyph.setAttribute("pointer-events", "none"); + faceGlyph.textContent = wallFaceLetter || "!"; + svg.appendChild(faceGlyph); + } + + const labelAnchor = facePoint(quad, 0, 0.9); + const label = document.createElementNS(svgNS, "text"); + label.setAttribute("class", `cube-face-label${isActive ? " is-active" : ""}`); + label.setAttribute("x", String(labelAnchor.x)); + label.setAttribute("y", String(labelAnchor.y)); + label.setAttribute("text-anchor", "middle"); + label.setAttribute("dominant-baseline", "middle"); + label.setAttribute("pointer-events", "none"); + label.textContent = wall?.name || wallId; + svg.appendChild(label); + }); + + const faceCenterByWallId = new Map( + faces.map((faceData) => [faceData.wallId, facePoint(faceData.quad, 0, 0)]) + ); + + if (state.showConnectorLines) { + MOTHER_CONNECTORS.forEach((connector, connectorIndex) => { + const fromWallId = normalizeId(connector?.fromWallId); + const toWallId = normalizeId(connector?.toWallId); + const from = faceCenterByWallId.get(fromWallId); + const to = faceCenterByWallId.get(toWallId); + if (!from || !to) { + return; + } + + const connectorId = normalizeId(connector?.id); + const isActive = state.selectedNodeType === "connector" + && normalizeId(state.selectedConnectorId) === connectorId; + const connectorLetter = getHebrewLetterSymbol(connector?.hebrewLetterId); + const connectorCardUrl = state.markerDisplayMode === "tarot" + ? resolveCardImageUrl(getConnectorTarotCard(connector)) + : null; + + const group = document.createElementNS(svgNS, "g"); + group.setAttribute("class", `cube-connector${isActive ? " is-active" : ""}`); + group.setAttribute("role", "button"); + group.setAttribute("tabindex", "0"); + group.setAttribute( + "aria-label", + `Mother connector ${formatDirectionName(fromWallId)} to ${formatDirectionName(toWallId)}` + ); + + const connectorLine = document.createElementNS(svgNS, "line"); + connectorLine.setAttribute("class", `cube-connector-line${isActive ? " is-active" : ""}`); + connectorLine.setAttribute("x1", from.x.toFixed(2)); + connectorLine.setAttribute("y1", from.y.toFixed(2)); + connectorLine.setAttribute("x2", to.x.toFixed(2)); + connectorLine.setAttribute("y2", to.y.toFixed(2)); + group.appendChild(connectorLine); + + const connectorHit = document.createElementNS(svgNS, "line"); + connectorHit.setAttribute("class", "cube-connector-hit"); + connectorHit.setAttribute("x1", from.x.toFixed(2)); + connectorHit.setAttribute("y1", from.y.toFixed(2)); + connectorHit.setAttribute("x2", to.x.toFixed(2)); + connectorHit.setAttribute("y2", to.y.toFixed(2)); + group.appendChild(connectorHit); + + const dx = to.x - from.x; + const dy = to.y - from.y; + const length = Math.hypot(dx, dy) || 1; + const perpX = -dy / length; + const perpY = dx / length; + const shift = (connectorIndex - 1) * 12; + const labelX = ((from.x + to.x) / 2) + (perpX * shift); + const labelY = ((from.y + to.y) / 2) + (perpY * shift); + + if (state.markerDisplayMode === "tarot" && connectorCardUrl) { + const cardW = 18; + const cardH = 27; + const connectorImg = document.createElementNS(svgNS, "image"); + connectorImg.setAttribute("href", connectorCardUrl); + connectorImg.setAttribute("x", String((labelX - cardW / 2).toFixed(2))); + connectorImg.setAttribute("y", String((labelY - cardH / 2).toFixed(2))); + connectorImg.setAttribute("width", String(cardW)); + connectorImg.setAttribute("height", String(cardH)); + connectorImg.setAttribute("preserveAspectRatio", "xMidYMid meet"); + group.appendChild(connectorImg); + } else { + const connectorText = document.createElementNS(svgNS, "text"); + connectorText.setAttribute( + "class", + `cube-connector-symbol${isActive ? " is-active" : ""}${connectorLetter ? "" : " is-missing"}` + ); + connectorText.setAttribute("x", String(labelX)); + connectorText.setAttribute("y", String(labelY)); + connectorText.setAttribute("text-anchor", "middle"); + connectorText.setAttribute("dominant-baseline", "middle"); + connectorText.setAttribute("pointer-events", "none"); + connectorText.textContent = connectorLetter || "!"; + group.appendChild(connectorText); + } + + const selectConnector = () => { + state.selectedNodeType = "connector"; + state.selectedConnectorId = connectorId; + render(getElements()); + }; + + group.addEventListener("click", selectConnector); + group.addEventListener("keydown", (event) => { + if (event.key === "Enter" || event.key === " ") { + event.preventDefault(); + selectConnector(); + } + }); + + svg.appendChild(group); + }); + } + + const edgeById = new Map( + getEdges().map((edge) => [normalizeEdgeId(edge?.id), edge]) + ); + + EDGE_GEOMETRY.forEach(([fromIndex, toIndex], edgeIndex) => { + const edgeId = EDGE_GEOMETRY_KEYS[edgeIndex]; + const edge = edgeById.get(edgeId) || { + id: edgeId, + name: formatEdgeName(edgeId), + walls: edgeId.split("-") + }; + const markerDisplay = getEdgeMarkerDisplay(edge); + const edgeWalls = getEdgeWalls(edge); + + const wallIsActive = edgeWalls.includes(normalizeId(state.selectedWallId)); + const edgeIsActive = normalizeEdgeId(state.selectedEdgeId) === edgeId; + + const from = projectedVertices[fromIndex]; + const to = projectedVertices[toIndex]; + + const line = document.createElementNS(svgNS, "line"); + line.setAttribute("x1", from.x.toFixed(2)); + line.setAttribute("y1", from.y.toFixed(2)); + line.setAttribute("x2", to.x.toFixed(2)); + line.setAttribute("y2", to.y.toFixed(2)); + line.setAttribute("stroke", "currentColor"); + line.setAttribute("stroke-opacity", edgeIsActive ? "0.94" : (wallIsActive ? "0.70" : "0.32")); + line.setAttribute("stroke-width", edgeIsActive ? "2.4" : (wallIsActive ? "1.9" : "1.4")); + line.setAttribute("class", `cube-edge-line${edgeIsActive ? " is-active" : ""}`); + line.setAttribute("role", "button"); + line.setAttribute("tabindex", "0"); + line.setAttribute("aria-label", `Cube edge ${toDisplayText(edge?.name) || formatEdgeName(edgeId)}`); + + const selectEdge = () => { + state.selectedEdgeId = edgeId; + state.selectedNodeType = "wall"; + state.selectedConnectorId = null; + if (!edgeWalls.includes(normalizeId(state.selectedWallId)) && edgeWalls[0]) { + state.selectedWallId = edgeWalls[0]; + snapRotationToWall(state.selectedWallId); + } + render(getElements()); + }; + + line.addEventListener("click", selectEdge); + line.addEventListener("keydown", (event) => { + if (event.key === "Enter" || event.key === " ") { + event.preventDefault(); + selectEdge(); + } + }); + svg.appendChild(line); + + const dx = to.x - from.x; + const dy = to.y - from.y; + const length = Math.hypot(dx, dy) || 1; + const normalX = -dy / length; + const normalY = dx / length; + const midpointX = (from.x + to.x) / 2; + const midpointY = (from.y + to.y) / 2; + + const centerVectorX = midpointX - CUBE_VIEW_CENTER.x; + const centerVectorY = midpointY - CUBE_VIEW_CENTER.y; + const normalSign = (centerVectorX * normalX + centerVectorY * normalY) >= 0 ? 1 : -1; + + const markerOffset = edgeIsActive ? 17 : (wallIsActive ? 13 : 12); + const labelX = midpointX + (normalX * markerOffset * normalSign); + const labelY = midpointY + (normalY * markerOffset * normalSign); + + const marker = document.createElementNS(svgNS, "g"); + marker.setAttribute( + "class", + `cube-direction${wallIsActive ? " is-wall-active" : ""}${edgeIsActive ? " is-active" : ""}` + ); + marker.setAttribute("role", "button"); + marker.setAttribute("tabindex", "0"); + marker.setAttribute("aria-label", `Cube edge ${toDisplayText(edge?.name) || formatEdgeName(edgeId)}`); + + if (state.markerDisplayMode === "tarot") { + const edgeCardUrl = resolveCardImageUrl(getEdgeTarotCard(edge)); + if (edgeCardUrl) { + const cardW = edgeIsActive ? 28 : 20; + const cardH = edgeIsActive ? 42 : 30; + const cardImg = document.createElementNS(svgNS, "image"); + cardImg.setAttribute("class", `cube-direction-card${edgeIsActive ? " is-active" : ""}`); + cardImg.setAttribute("href", edgeCardUrl); + cardImg.setAttribute("x", String((labelX - cardW / 2).toFixed(2))); + cardImg.setAttribute("y", String((labelY - cardH / 2).toFixed(2))); + cardImg.setAttribute("width", String(cardW)); + cardImg.setAttribute("height", String(cardH)); + cardImg.setAttribute("preserveAspectRatio", "xMidYMid meet"); + marker.appendChild(cardImg); + } else { + const markerText = document.createElementNS(svgNS, "text"); + markerText.setAttribute("class", "cube-direction-letter is-missing"); + markerText.setAttribute("x", String(labelX)); + markerText.setAttribute("y", String(labelY)); + markerText.setAttribute("text-anchor", "middle"); + markerText.setAttribute("dominant-baseline", "middle"); + markerText.textContent = "!"; + marker.appendChild(markerText); + } + } else { + const markerText = document.createElementNS(svgNS, "text"); + markerText.setAttribute( + "class", + `cube-direction-letter${markerDisplay.isMissing ? " is-missing" : ""}` + ); + markerText.setAttribute("x", String(labelX)); + markerText.setAttribute("y", String(labelY)); + markerText.setAttribute("text-anchor", "middle"); + markerText.setAttribute("dominant-baseline", "middle"); + markerText.textContent = markerDisplay.text; + marker.appendChild(markerText); + } + + marker.addEventListener("click", selectEdge); + marker.addEventListener("keydown", (event) => { + if (event.key === "Enter" || event.key === " ") { + event.preventDefault(); + selectEdge(); + } + }); + + svg.appendChild(marker); + }); + + const center = getCubeCenterData(); + if (center && state.showPrimalPoint) { + const centerLetter = getCenterLetterSymbol(center); + const centerCardUrl = state.markerDisplayMode === "tarot" + ? resolveCardImageUrl(getCenterTarotCard(center)) + : null; + const centerActive = state.selectedNodeType === "center"; + + const centerMarker = document.createElementNS(svgNS, "g"); + centerMarker.setAttribute("class", `cube-center${centerActive ? " is-active" : ""}`); + centerMarker.setAttribute("role", "button"); + centerMarker.setAttribute("tabindex", "0"); + centerMarker.setAttribute("aria-label", "Cube primal point"); + + const centerHit = document.createElementNS(svgNS, "circle"); + centerHit.setAttribute("class", "cube-center-hit"); + centerHit.setAttribute("cx", String(CUBE_VIEW_CENTER.x)); + centerHit.setAttribute("cy", String(CUBE_VIEW_CENTER.y)); + centerHit.setAttribute("r", "18"); + centerMarker.appendChild(centerHit); + + if (state.markerDisplayMode === "tarot" && centerCardUrl) { + const cardW = 24; + const cardH = 36; + const centerImg = document.createElementNS(svgNS, "image"); + centerImg.setAttribute("href", centerCardUrl); + centerImg.setAttribute("x", String((CUBE_VIEW_CENTER.x - cardW / 2).toFixed(2))); + centerImg.setAttribute("y", String((CUBE_VIEW_CENTER.y - cardH / 2).toFixed(2))); + centerImg.setAttribute("width", String(cardW)); + centerImg.setAttribute("height", String(cardH)); + centerImg.setAttribute("preserveAspectRatio", "xMidYMid meet"); + centerMarker.appendChild(centerImg); + } else { + const centerText = document.createElementNS(svgNS, "text"); + centerText.setAttribute( + "class", + `cube-center-symbol${centerActive ? " is-active" : ""}${centerLetter ? "" : " is-missing"}` + ); + centerText.setAttribute("x", String(CUBE_VIEW_CENTER.x)); + centerText.setAttribute("y", String(CUBE_VIEW_CENTER.y)); + centerText.setAttribute("text-anchor", "middle"); + centerText.setAttribute("dominant-baseline", "middle"); + centerText.setAttribute("pointer-events", "none"); + centerText.textContent = centerLetter || "!"; + centerMarker.appendChild(centerText); + } + + const selectCenter = () => { + state.selectedNodeType = "center"; + state.selectedConnectorId = null; + render(getElements()); + }; + + centerMarker.addEventListener("click", selectCenter); + centerMarker.addEventListener("keydown", (event) => { + if (event.key === "Enter" || event.key === " ") { + event.preventDefault(); + selectCenter(); + } + }); + + svg.appendChild(centerMarker); + } + + if (state.markerDisplayMode === "tarot") { + Array.from(svg.querySelectorAll("g.cube-direction")).forEach((group) => { + svg.appendChild(group); + }); + + if (state.showConnectorLines) { + Array.from(svg.querySelectorAll("g.cube-connector")).forEach((group) => { + svg.appendChild(group); + }); + } + } + + containerEl.replaceChildren(svg); + } + + function renderCenterDetail(elements) { + if (!state.showPrimalPoint) { + return false; + } + + const center = getCubeCenterData(); + if (!center || !elements?.detailNameEl || !elements?.detailSubEl || !elements?.detailBodyEl) { + return false; + } + + const centerLetterId = getCenterLetterId(center); + const centerLetter = getCenterLetterSymbol(center); + const centerLetterText = centerLetterId + ? `${centerLetter ? `${centerLetter} ` : ""}${toDisplayText(centerLetterId)}` + : ""; + const centerElement = toDisplayText(center?.element); + + elements.detailNameEl.textContent = "Primal Point"; + elements.detailSubEl.textContent = [centerLetterText, centerElement].filter(Boolean).join(" · ") || "Center of the Cube"; + + const bodyEl = elements.detailBodyEl; + bodyEl.innerHTML = ""; + + const summary = document.createElement("div"); + summary.className = "planet-text"; + summary.innerHTML = ` +
+
Name
${toDetailValueMarkup(center?.name)}
+
Letter
${toDetailValueMarkup(centerLetterText)}
+
Element
${toDetailValueMarkup(center?.element)}
+
+ `; + bodyEl.appendChild(createMetaCard("Center Details", summary)); + + if (Array.isArray(center?.keywords) && center.keywords.length) { + bodyEl.appendChild(createMetaCard("Keywords", center.keywords.join(", "))); + } + + if (center?.description) { + bodyEl.appendChild(createMetaCard("Description", center.description)); + } + + const associations = center?.associations || {}; + const links = document.createElement("div"); + links.className = "kab-god-links"; + + if (centerLetterId) { + links.appendChild(createNavButton(centerLetter || "!", "nav:alphabet", { + alphabet: "hebrew", + hebrewLetterId: centerLetterId + })); + } + + const centerTrumpNo = toFiniteNumber(associations?.tarotTrumpNumber); + const centerTarotCard = toDisplayText(associations?.tarotCard); + if (centerTarotCard || centerTrumpNo != null) { + links.appendChild(createNavButton(centerTarotCard || `Trump ${centerTrumpNo}`, "nav:tarot-trump", { + cardName: centerTarotCard, + trumpNumber: centerTrumpNo + })); + } + + const centerPathNo = toFiniteNumber(associations?.kabbalahPathNumber); + if (centerPathNo != null) { + links.appendChild(createNavButton(`Path ${centerPathNo}`, "nav:kabbalah-path", { + pathNo: centerPathNo + })); + } + + if (links.childElementCount) { + const linksCard = document.createElement("div"); + linksCard.className = "planet-meta-card"; + linksCard.innerHTML = "Correspondence Links"; + linksCard.appendChild(links); + bodyEl.appendChild(linksCard); + } + + return true; + } + + function renderConnectorDetail(elements, walls) { + const connector = getConnectorById(state.selectedConnectorId); + if (!connector || !elements?.detailNameEl || !elements?.detailSubEl || !elements?.detailBodyEl) { + return false; + } + + const fromWallId = normalizeId(connector?.fromWallId); + const toWallId = normalizeId(connector?.toWallId); + const fromWall = getWallById(fromWallId) || walls.find((entry) => normalizeId(entry?.id) === fromWallId) || null; + const toWall = getWallById(toWallId) || walls.find((entry) => normalizeId(entry?.id) === toWallId) || null; + const connectorPath = getConnectorPathEntry(connector); + + const letterId = normalizeLetterKey(connector?.hebrewLetterId); + const letterSymbol = getHebrewLetterSymbol(letterId); + const letterText = letterId + ? `${letterSymbol ? `${letterSymbol} ` : ""}${toDisplayText(letterId)}` + : ""; + + const pathNo = toFiniteNumber(connectorPath?.pathNumber); + const tarotCard = toDisplayText(connectorPath?.tarot?.card); + const tarotTrumpNumber = toFiniteNumber(connectorPath?.tarot?.trumpNumber); + const astrologyType = toDisplayText(connectorPath?.astrology?.type); + const astrologyName = toDisplayText(connectorPath?.astrology?.name); + const astrologySummary = [astrologyType, astrologyName].filter(Boolean).join(": "); + + elements.detailNameEl.textContent = connector?.name || "Mother Connector"; + elements.detailSubEl.textContent = ["Mother Letter", letterText].filter(Boolean).join(" · ") || "Mother Letter"; + + const bodyEl = elements.detailBodyEl; + bodyEl.innerHTML = ""; + + const summary = document.createElement("div"); + summary.className = "planet-text"; + summary.innerHTML = ` +
+
Letter
${toDetailValueMarkup(letterText)}
+
From
${toDetailValueMarkup(fromWall?.name || formatDirectionName(fromWallId))}
+
To
${toDetailValueMarkup(toWall?.name || formatDirectionName(toWallId))}
+
Tarot
${toDetailValueMarkup(tarotCard || (tarotTrumpNumber != null ? `Trump ${tarotTrumpNumber}` : ""))}
+
+ `; + bodyEl.appendChild(createMetaCard("Connector Details", summary)); + + if (astrologySummary) { + bodyEl.appendChild(createMetaCard("Astrology", astrologySummary)); + } + + const links = document.createElement("div"); + links.className = "kab-god-links"; + + if (letterId) { + links.appendChild(createNavButton(letterSymbol || "!", "nav:alphabet", { + alphabet: "hebrew", + hebrewLetterId: letterId + })); + } + + if (pathNo != null) { + links.appendChild(createNavButton(`Path ${pathNo}`, "nav:kabbalah-path", { + pathNo + })); + } + + if (tarotCard || tarotTrumpNumber != null) { + links.appendChild(createNavButton(tarotCard || `Trump ${tarotTrumpNumber}`, "nav:tarot-trump", { + cardName: tarotCard, + trumpNumber: tarotTrumpNumber + })); + } + + if (links.childElementCount) { + const linksCard = document.createElement("div"); + linksCard.className = "planet-meta-card"; + linksCard.innerHTML = "Correspondence Links"; + linksCard.appendChild(links); + bodyEl.appendChild(linksCard); + } + + return true; + } + + function renderEdgeCard(wall, detailBodyEl, wallEdgeDirections = new Map()) { + const wallId = normalizeId(wall?.id); + const selectedEdge = getEdgeById(state.selectedEdgeId) + || getEdgesForWall(wallId)[0] + || getEdges()[0] + || null; + if (!selectedEdge) { + return; + } + + state.selectedEdgeId = normalizeEdgeId(selectedEdge.id); + + const edgeDirection = wallEdgeDirections.get(normalizeEdgeId(selectedEdge.id)); + const edgeName = edgeDirection + ? formatDirectionName(edgeDirection) + : (toDisplayText(selectedEdge.name) || formatEdgeName(selectedEdge.id)); + const edgeWalls = getEdgeWalls(selectedEdge) + .map((entry) => entry.charAt(0).toUpperCase() + entry.slice(1)) + .join(" · "); + + const edgeLetterId = getEdgeLetterId(selectedEdge); + const edgeLetter = getEdgeLetter(selectedEdge); + const edgePath = getEdgePathEntry(selectedEdge); + const astrologyType = toDisplayText(edgePath?.astrology?.type); + const astrologyName = toDisplayText(edgePath?.astrology?.name); + const astrologySymbol = getEdgeAstrologySymbol(selectedEdge); + const astrologyText = astrologySymbol && astrologyName + ? `${astrologySymbol} ${astrologyName}` + : astrologySymbol || astrologyName; + + const pathNo = toFiniteNumber(edgePath?.pathNumber); + const tarotCard = toDisplayText(edgePath?.tarot?.card); + const tarotTrumpNumber = toFiniteNumber(edgePath?.tarot?.trumpNumber); + + const edgeCard = document.createElement("div"); + edgeCard.className = "planet-meta-card"; + + const title = document.createElement("strong"); + title.textContent = `Edge · ${edgeName}`; + edgeCard.appendChild(title); + + const dlWrap = document.createElement("div"); + dlWrap.className = "planet-text"; + dlWrap.innerHTML = ` +
+
Direction
${toDetailValueMarkup(edgeName)}
+
Edge
${toDetailValueMarkup(edgeWalls)}
+
Letter
${toDetailValueMarkup(edgeLetter)}
+
Astrology
${toDetailValueMarkup(astrologyText)}
+
Tarot
${toDetailValueMarkup(tarotCard)}
+
+ `; + edgeCard.appendChild(dlWrap); + + if (Array.isArray(selectedEdge.keywords) && selectedEdge.keywords.length) { + const keywords = document.createElement("p"); + keywords.className = "planet-text"; + keywords.textContent = selectedEdge.keywords.join(", "); + edgeCard.appendChild(keywords); + } + + if (selectedEdge.description) { + const description = document.createElement("p"); + description.className = "planet-text"; + description.textContent = selectedEdge.description; + edgeCard.appendChild(description); + } + + const links = document.createElement("div"); + links.className = "kab-god-links"; + + if (edgeLetterId) { + links.appendChild(createNavButton(edgeLetter || "!", "nav:alphabet", { + alphabet: "hebrew", + hebrewLetterId: edgeLetterId + })); + } + + if (astrologyType === "zodiac" && astrologyName) { + links.appendChild(createNavButton(astrologyName, "nav:zodiac", { + signId: normalizeId(astrologyName) + })); + } + + if (tarotCard) { + links.appendChild(createNavButton(tarotCard, "nav:tarot-trump", { + cardName: tarotCard, + trumpNumber: tarotTrumpNumber + })); + } + + if (pathNo != null) { + links.appendChild(createNavButton(`Path ${pathNo}`, "nav:kabbalah-path", { + pathNo + })); + } + + if (links.childElementCount) { + edgeCard.appendChild(links); + } + + detailBodyEl.appendChild(edgeCard); + } + + function renderDetail(elements, walls) { + if (state.selectedNodeType === "connector" && renderConnectorDetail(elements, walls)) { + return; + } + + if (state.selectedNodeType === "center" && renderCenterDetail(elements)) { + return; + } + + const wall = getWallById(state.selectedWallId) || walls[0] || null; + if (!wall || !elements?.detailNameEl || !elements?.detailSubEl || !elements?.detailBodyEl) { + if (elements?.detailNameEl) { + elements.detailNameEl.textContent = "Cube data unavailable"; + } + if (elements?.detailSubEl) { + elements.detailSubEl.textContent = "Could not load cube dataset."; + } + if (elements?.detailBodyEl) { + elements.detailBodyEl.innerHTML = ""; + } + return; + } + + state.selectedWallId = normalizeId(wall.id); + + const wallPlanet = toDisplayText(wall?.planet) || "!"; + const wallElement = toDisplayText(wall?.element) || "!"; + const wallFaceLetterId = getWallFaceLetterId(wall); + const wallFaceLetter = getWallFaceLetter(wall); + const wallFaceLetterText = wallFaceLetterId + ? `${wallFaceLetter ? `${wallFaceLetter} ` : ""}${toDisplayText(wallFaceLetterId)}` + : ""; + elements.detailNameEl.textContent = `${wall.name} Wall`; + elements.detailSubEl.textContent = `${wallElement} · ${wallPlanet}`; + + const bodyEl = elements.detailBodyEl; + bodyEl.innerHTML = ""; + + const summary = document.createElement("div"); + summary.className = "planet-text"; + summary.innerHTML = ` +
+
Opposite
${toDetailValueMarkup(wall.opposite)}
+
Face Letter
${toDetailValueMarkup(wallFaceLetterText)}
+
Element
${toDetailValueMarkup(wall.element)}
+
Planet
${toDetailValueMarkup(wall.planet)}
+
Archangel
${toDetailValueMarkup(wall.archangel)}
+
+ `; + bodyEl.appendChild(createMetaCard("Wall Details", summary)); + + if (Array.isArray(wall.keywords) && wall.keywords.length) { + bodyEl.appendChild(createMetaCard("Keywords", wall.keywords.join(", "))); + } + + if (wall.description) { + bodyEl.appendChild(createMetaCard("Description", wall.description)); + } + + const wallLinksCard = document.createElement("div"); + wallLinksCard.className = "planet-meta-card"; + wallLinksCard.innerHTML = "Correspondence Links"; + const wallLinks = document.createElement("div"); + wallLinks.className = "kab-god-links"; + + if (wallFaceLetterId) { + const wallFaceLetterName = getHebrewLetterName(wallFaceLetterId) || toDisplayText(wallFaceLetterId); + const faceLetterText = [wallFaceLetter, wallFaceLetterName].filter(Boolean).join(" "); + const faceLetterLabel = faceLetterText + ? `Face ${faceLetterText}` + : "Face !"; + wallLinks.appendChild(createNavButton(faceLetterLabel, "nav:alphabet", { + alphabet: "hebrew", + hebrewLetterId: wallFaceLetterId + })); + } + + const wallAssociations = wall.associations || {}; + if (wallAssociations.planetId) { + wallLinks.appendChild(createNavButton(toDisplayText(wall.planet) || "!", "nav:planet", { + planetId: wallAssociations.planetId + })); + } + + if (wallAssociations.godName) { + wallLinks.appendChild(createNavButton(wallAssociations.godName, "nav:gods", { + godName: wallAssociations.godName + })); + } + + if (wall.oppositeWallId) { + const oppositeWall = getWallById(wall.oppositeWallId); + const internal = document.createElement("button"); + internal.type = "button"; + internal.className = "kab-god-link"; + internal.textContent = `Opposite: ${oppositeWall?.name || wall.oppositeWallId}`; + internal.addEventListener("click", () => { + state.selectedWallId = normalizeId(wall.oppositeWallId); + state.selectedEdgeId = normalizeEdgeId(getEdgesForWall(state.selectedWallId)[0]?.id || getEdges()[0]?.id); + state.selectedNodeType = "wall"; + state.selectedConnectorId = null; + snapRotationToWall(state.selectedWallId); + render(getElements()); + }); + wallLinks.appendChild(internal); + } + + if (wallLinks.childElementCount) { + wallLinksCard.appendChild(wallLinks); + bodyEl.appendChild(wallLinksCard); + } + + const edgesCard = document.createElement("div"); + edgesCard.className = "planet-meta-card"; + edgesCard.innerHTML = "Wall Edges"; + + const chips = document.createElement("div"); + chips.className = "kab-chips"; + + const wallEdgeDirections = getWallEdgeDirections(wall); + const wallEdges = getEdgesForWall(wall) + .slice() + .sort((left, right) => { + const leftDirection = wallEdgeDirections.get(normalizeEdgeId(left?.id)); + const rightDirection = wallEdgeDirections.get(normalizeEdgeId(right?.id)); + const leftRank = LOCAL_DIRECTION_RANK[leftDirection] ?? LOCAL_DIRECTION_ORDER.length; + const rightRank = LOCAL_DIRECTION_RANK[rightDirection] ?? LOCAL_DIRECTION_ORDER.length; + if (leftRank !== rightRank) { + return leftRank - rightRank; + } + return normalizeEdgeId(left?.id).localeCompare(normalizeEdgeId(right?.id)); + }); + + wallEdges.forEach((edge) => { + const id = normalizeEdgeId(edge.id); + const chipLetter = getEdgeLetter(edge); + const chipIsMissing = !chipLetter; + const direction = wallEdgeDirections.get(id); + const directionLabel = direction + ? formatDirectionName(direction) + : (toDisplayText(edge.name) || formatEdgeName(edge.id)); + const chip = document.createElement("span"); + chip.className = `kab-chip${id === normalizeEdgeId(state.selectedEdgeId) ? " is-active" : ""}${chipIsMissing ? " is-missing" : ""}`; + chip.setAttribute("role", "button"); + chip.setAttribute("tabindex", "0"); + chip.textContent = `${directionLabel} · ${chipLetter || "!"}`; + + const selectEdge = () => { + state.selectedEdgeId = id; + state.selectedNodeType = "wall"; + state.selectedConnectorId = null; + render(getElements()); + }; + + chip.addEventListener("click", selectEdge); + chip.addEventListener("keydown", (event) => { + if (event.key === "Enter" || event.key === " ") { + event.preventDefault(); + selectEdge(); + } + }); + + chips.appendChild(chip); + }); + + edgesCard.appendChild(chips); + bodyEl.appendChild(edgesCard); + + renderEdgeCard(wall, bodyEl, wallEdgeDirections); + } + + function render(elements) { + if (elements?.markerModeEl) { + elements.markerModeEl.value = state.markerDisplayMode; + } + + if (elements?.connectorToggleEl) { + elements.connectorToggleEl.checked = state.showConnectorLines; + } + + if (elements?.primalToggleEl) { + elements.primalToggleEl.checked = state.showPrimalPoint; + } + + if (elements?.rotationReadoutEl) { + elements.rotationReadoutEl.textContent = `X ${Math.round(state.rotationX)}° · Y ${Math.round(state.rotationY)}°`; + } + + const walls = getWalls(); + renderFaceSvg(elements.viewContainerEl, walls); + renderDetail(elements, walls); + } + + function ensureCubeSection(magickDataset) { + const cubeData = magickDataset?.grouped?.kabbalah?.cube; + const elements = getElements(); + + state.cube = cubeData || null; + state.hebrewLetters = + asRecord(magickDataset?.grouped?.hebrewLetters) + || asRecord(magickDataset?.grouped?.alphabets?.hebrew) + || null; + + const pathList = Array.isArray(magickDataset?.grouped?.kabbalah?.["kabbalah-tree"]?.paths) + ? magickDataset.grouped.kabbalah["kabbalah-tree"].paths + : []; + + const letterEntries = state.hebrewLetters && typeof state.hebrewLetters === "object" + ? Object.values(state.hebrewLetters) + : []; + const letterIdsByChar = new Map( + letterEntries + .map((letterEntry) => [String(letterEntry?.letter?.he || "").trim(), normalizeLetterKey(letterEntry?.id)]) + .filter(([character, letterId]) => Boolean(character) && Boolean(letterId)) + ); + + state.kabbalahPathsByLetterId = new Map( + pathList + .map((pathEntry) => { + const transliterationId = normalizeLetterKey(pathEntry?.hebrewLetter?.transliteration); + const char = String(pathEntry?.hebrewLetter?.char || "").trim(); + const charId = letterIdsByChar.get(char) || ""; + return [charId || transliterationId, pathEntry]; + }) + .filter(([letterId]) => Boolean(letterId)) + ); + + if (!state.selectedWallId) { + state.selectedWallId = normalizeId(getWalls()[0]?.id); + } + + const initialEdge = getEdgesForWall(state.selectedWallId)[0] || getEdges()[0] || null; + if (!state.selectedEdgeId || !getEdgeById(state.selectedEdgeId)) { + state.selectedEdgeId = normalizeEdgeId(initialEdge?.id); + } + + bindRotationControls(elements); + + render(elements); + state.initialized = true; + } + + function selectWallById(wallId) { + if (!state.initialized) { + return false; + } + + const wall = getWallById(wallId); + if (!wall) { + return false; + } + + state.selectedWallId = normalizeId(wall.id); + state.selectedEdgeId = normalizeEdgeId(getEdgesForWall(state.selectedWallId)[0]?.id || getEdges()[0]?.id); + state.selectedNodeType = "wall"; + state.selectedConnectorId = null; + snapRotationToWall(state.selectedWallId); + render(getElements()); + return true; + } + + function selectConnectorById(connectorId) { + if (!state.initialized) { + return false; + } + + const connector = getConnectorById(connectorId); + if (!connector) { + return false; + } + + const fromWallId = normalizeId(connector.fromWallId); + if (fromWallId && getWallById(fromWallId)) { + state.selectedWallId = fromWallId; + state.selectedEdgeId = normalizeEdgeId(getEdgesForWall(fromWallId)[0]?.id || getEdges()[0]?.id); + snapRotationToWall(fromWallId); + } + + state.showConnectorLines = true; + state.selectedNodeType = "connector"; + state.selectedConnectorId = normalizeId(connector.id); + render(getElements()); + return true; + } + + function selectCenterNode() { + if (!state.initialized) { + return false; + } + + state.showPrimalPoint = true; + state.selectedNodeType = "center"; + state.selectedConnectorId = null; + render(getElements()); + return true; + } + + function selectPlacement(criteria = {}) { + if (!state.initialized) { + return false; + } + + const wallId = normalizeId(criteria.wallId); + const connectorId = normalizeId(criteria.connectorId); + const edgeId = normalizeEdgeId(criteria.edgeId || criteria.directionId); + const hebrewLetterId = normalizeLetterKey(criteria.hebrewLetterId); + const signId = normalizeId(criteria.signId || criteria.zodiacSignId); + const planetId = normalizeId(criteria.planetId); + const pathNo = toFiniteNumber(criteria.pathNo || criteria.kabbalahPathNumber); + const trumpNo = toFiniteNumber(criteria.trumpNumber || criteria.tarotTrumpNumber); + const nodeType = normalizeId(criteria.nodeType); + const centerRequested = nodeType === "center" + || Boolean(criteria.center) + || Boolean(criteria.primalPoint) + || normalizeId(criteria.centerId) === "primal-point"; + const edges = getEdges(); + + const findEdgeBy = (predicate) => edges.find((edge) => predicate(edge)) || null; + + const findWallForEdge = (edge, preferredWallId) => { + const edgeWalls = getEdgeWalls(edge); + if (preferredWallId && edgeWalls.includes(preferredWallId)) { + return preferredWallId; + } + return edgeWalls[0] || normalizeId(getWalls()[0]?.id); + }; + + if (connectorId) { + return selectConnectorById(connectorId); + } + + if (centerRequested) { + return selectCenterNode(); + } + + if (edgeId) { + const edge = getEdgeById(edgeId); + if (!edge) { + return false; + } + return applyPlacement({ + wallId: findWallForEdge(edge, wallId), + edgeId + }); + } + + if (wallId) { + const wall = getWallById(wallId); + if (!wall) { + return false; + } + + // if an explicit edge id was not provided (or was empty) we treat this + // as a request to show the wall/face itself rather than any particular + // edge direction. `applyPlacement` only knows how to highlight edges, + // so we fall back to selecting the wall directly in that case. this + // is the behaviour we want when navigating from a "face" letter like + // dalet, where the placement computed by ui-alphabet leaves edgeId + // blank. + if (!edgeId) { + return selectWallById(wallId); + } + + const firstEdge = getEdgesForWall(wallId)[0] || null; + return applyPlacement({ wallId, edgeId: firstEdge?.id }); + } + + if (hebrewLetterId) { + const byHebrew = findEdgeBy((edge) => getEdgeLetterId(edge) === hebrewLetterId); + if (byHebrew) { + return applyPlacement({ + wallId: findWallForEdge(byHebrew), + edgeId: byHebrew.id + }); + } + + const byWallFace = getWalls().find((wall) => getWallFaceLetterId(wall) === hebrewLetterId) || null; + if (byWallFace) { + const byWallFaceId = normalizeId(byWallFace.id); + const firstEdge = getEdgesForWall(byWallFaceId)[0] || null; + return applyPlacement({ wallId: byWallFaceId, edgeId: firstEdge?.id }); + } + } + + if (signId) { + const bySign = findEdgeBy((edge) => { + const astrology = getEdgePathEntry(edge)?.astrology || {}; + return normalizeId(astrology.type) === "zodiac" && normalizeId(astrology.name) === signId; + }); + if (bySign) { + return applyPlacement({ + wallId: findWallForEdge(bySign), + edgeId: bySign.id + }); + } + } + + if (pathNo != null) { + const byPath = findEdgeBy((edge) => toFiniteNumber(getEdgePathEntry(edge)?.pathNumber) === pathNo); + if (byPath) { + return applyPlacement({ + wallId: findWallForEdge(byPath), + edgeId: byPath.id + }); + } + } + + if (trumpNo != null) { + const byTrump = findEdgeBy((edge) => { + const tarot = getEdgePathEntry(edge)?.tarot || {}; + return toFiniteNumber(tarot.trumpNumber) === trumpNo; + }); + if (byTrump) { + return applyPlacement({ + wallId: findWallForEdge(byTrump), + edgeId: byTrump.id + }); + } + } + + if (planetId) { + const wall = getWalls().find((entry) => normalizeId(entry?.associations?.planetId) === planetId); + if (wall) { + const wallIdByPlanet = normalizeId(wall.id); + return applyPlacement({ + wallId: wallIdByPlanet, + edgeId: getEdgesForWall(wallIdByPlanet)[0]?.id + }); + } + } + + return false; + } + + function selectByHebrewLetterId(hebrewLetterId) { + return selectPlacement({ hebrewLetterId }); + } + + function selectBySignId(signId) { + return selectPlacement({ signId }); + } + + function selectByPlanetId(planetId) { + return selectPlacement({ planetId }); + } + + function selectByPathNo(pathNo) { + return selectPlacement({ pathNo }); + } + + window.CubeSectionUi = { + ensureCubeSection, + selectWallById, + selectPlacement, + selectByHebrewLetterId, + selectBySignId, + selectByPlanetId, + selectByPathNo, + getEdgeDirectionForWall, + getEdgeDirectionLabelForWall + }; +})(); diff --git a/app/ui-cycles.js b/app/ui-cycles.js new file mode 100644 index 0000000..7f25c49 --- /dev/null +++ b/app/ui-cycles.js @@ -0,0 +1,350 @@ +// app/ui-cycles.js +(function () { + "use strict"; + + const state = { + initialized: false, + referenceData: null, + entries: [], + filteredEntries: [], + searchQuery: "", + selectedId: "" + }; + + function getElements() { + return { + searchInputEl: document.getElementById("cycles-search-input"), + searchClearEl: document.getElementById("cycles-search-clear"), + countEl: document.getElementById("cycles-count"), + listEl: document.getElementById("cycles-list"), + detailNameEl: document.getElementById("cycles-detail-name"), + detailTypeEl: document.getElementById("cycles-detail-type"), + detailSummaryEl: document.getElementById("cycles-detail-summary"), + detailBodyEl: document.getElementById("cycles-detail-body") + }; + } + + function normalizeSearchValue(value) { + return String(value || "").trim().toLowerCase(); + } + + function normalizeLookupToken(value) { + return String(value || "") + .trim() + .toLowerCase() + .replace(/[^a-z0-9]+/g, " ") + .trim(); + } + + function normalizeCycle(raw, index) { + const name = String(raw?.name || "").trim(); + if (!name) { + return null; + } + + const id = String(raw?.id || `cycle-${index + 1}`).trim(); + const category = String(raw?.category || "Uncategorized").trim(); + const period = String(raw?.period || "").trim(); + const description = String(raw?.description || "").trim(); + const significance = String(raw?.significance || "").trim(); + const related = Array.isArray(raw?.related) + ? raw.related.map((item) => String(item || "").trim()).filter(Boolean) + : []; + const periodDaysRaw = Number(raw?.periodDays); + const periodDays = Number.isFinite(periodDaysRaw) ? periodDaysRaw : null; + + const searchText = normalizeSearchValue([ + name, + category, + period, + description, + significance, + related.join(" ") + ].join(" ")); + + return { + id, + name, + category, + period, + periodDays, + description, + significance, + related, + searchText + }; + } + + function buildEntries(referenceData) { + const rows = Array.isArray(referenceData?.astronomyCycles?.cycles) + ? referenceData.astronomyCycles.cycles + : []; + + return rows + .map((row, index) => normalizeCycle(row, index)) + .filter(Boolean) + .sort((left, right) => left.name.localeCompare(right.name)); + } + + function formatDays(value) { + if (!Number.isFinite(value)) { + return ""; + } + return value >= 1000 + ? Math.round(value).toLocaleString() + : value.toFixed(3).replace(/\.0+$/, ""); + } + + function escapeHtml(value) { + return String(value || "") + .replaceAll("&", "&") + .replaceAll("<", "<") + .replaceAll(">", ">") + .replaceAll('"', """) + .replaceAll("'", "'"); + } + + function escapeAttr(value) { + return escapeHtml(value).replaceAll("`", "`"); + } + + function cssEscape(value) { + if (typeof CSS !== "undefined" && typeof CSS.escape === "function") { + return CSS.escape(value); + } + return String(value || "").replace(/[^a-zA-Z0-9_\-]/g, "\\$&"); + } + + function setSelectedCycle(nextId) { + const normalized = String(nextId || "").trim(); + if (normalized && state.entries.some((entry) => entry.id === normalized)) { + state.selectedId = normalized; + return; + } + state.selectedId = state.filteredEntries[0]?.id || state.entries[0]?.id || ""; + } + + function selectedEntry() { + return state.filteredEntries.find((entry) => entry.id === state.selectedId) + || state.entries.find((entry) => entry.id === state.selectedId) + || state.filteredEntries[0] + || state.entries[0] + || null; + } + + function findEntryByReference(reference) { + const token = normalizeLookupToken(reference); + if (!token) { + return null; + } + + return state.entries.find((entry) => { + const idToken = normalizeLookupToken(entry.id); + const nameToken = normalizeLookupToken(entry.name); + return token === idToken || token === nameToken; + }) || null; + } + + function applyFilter() { + const query = state.searchQuery; + if (!query) { + state.filteredEntries = state.entries.slice(); + } else { + state.filteredEntries = state.entries.filter((entry) => entry.searchText.includes(query)); + } + + if (!state.filteredEntries.some((entry) => entry.id === state.selectedId)) { + state.selectedId = state.filteredEntries[0]?.id || ""; + } + } + + function syncControls(elements) { + const { searchInputEl, searchClearEl } = elements; + if (searchInputEl) { + searchInputEl.value = state.searchQuery; + } + if (searchClearEl) { + searchClearEl.disabled = !state.searchQuery; + } + } + + function renderList(elements) { + const { listEl, countEl } = elements; + if (!(listEl instanceof HTMLElement)) { + return; + } + + if (countEl) { + countEl.textContent = state.searchQuery + ? `${state.filteredEntries.length} of ${state.entries.length}` + : `${state.entries.length}`; + } + + if (!state.filteredEntries.length) { + listEl.innerHTML = '
No cycles match your search.
'; + return; + } + + listEl.innerHTML = ""; + + state.filteredEntries.forEach((entry) => { + const itemEl = document.createElement("div"); + const isSelected = entry.id === state.selectedId; + itemEl.className = `planet-list-item${isSelected ? " is-selected" : ""}`; + itemEl.setAttribute("role", "option"); + itemEl.setAttribute("aria-selected", isSelected ? "true" : "false"); + itemEl.dataset.cycleId = entry.id; + + const periodMeta = entry.period ? ` · ${escapeHtml(entry.period)}` : ""; + itemEl.innerHTML = [ + `
${escapeHtml(entry.name)}
`, + `
${escapeHtml(entry.category)}${periodMeta}
` + ].join("\n"); + + itemEl.addEventListener("click", () => { + setSelectedCycle(entry.id); + renderAll(); + }); + + listEl.appendChild(itemEl); + }); + } + + function renderDetail(elements) { + const { + detailNameEl, + detailTypeEl, + detailSummaryEl, + detailBodyEl + } = elements; + + const entry = selectedEntry(); + + if (!entry) { + if (detailNameEl) detailNameEl.textContent = "No cycle selected"; + if (detailTypeEl) detailTypeEl.textContent = ""; + if (detailSummaryEl) detailSummaryEl.textContent = "Select a cycle from the list."; + if (detailBodyEl) detailBodyEl.innerHTML = ""; + return; + } + + if (detailNameEl) detailNameEl.textContent = entry.name; + if (detailTypeEl) detailTypeEl.textContent = entry.category; + if (detailSummaryEl) detailSummaryEl.textContent = entry.description || "No description available."; + + const body = []; + + if (entry.period) { + body.push(`

Period: ${escapeHtml(entry.period)}

`); + } + + if (Number.isFinite(entry.periodDays)) { + body.push(`

Approx days: ${escapeHtml(formatDays(entry.periodDays))}

`); + } + + if (entry.significance) { + body.push(`

Significance: ${escapeHtml(entry.significance)}

`); + } + + if (entry.related.length) { + const relatedButtons = entry.related + .map((label) => { + const relatedEntry = findEntryByReference(label); + if (!relatedEntry) { + return `${escapeHtml(label)}`; + } + return ``; + }) + .join(""); + + body.push([ + "
", + " Related:", + `
${relatedButtons}
`, + "
" + ].join("\n")); + } + + if (detailBodyEl) { + detailBodyEl.innerHTML = body.join("\n"); + } + } + + function renderAll() { + const elements = getElements(); + syncControls(elements); + renderList(elements); + renderDetail(elements); + } + + function handleSearchInput() { + const { searchInputEl } = getElements(); + state.searchQuery = normalizeSearchValue(searchInputEl?.value); + applyFilter(); + renderAll(); + } + + function handleSearchClear() { + const { searchInputEl } = getElements(); + if (searchInputEl) { + searchInputEl.value = ""; + searchInputEl.focus(); + } + state.searchQuery = ""; + applyFilter(); + renderAll(); + } + + function handleRelatedClick(event) { + const target = event.target instanceof Element + ? event.target.closest("[data-related-cycle-id]") + : null; + if (!(target instanceof HTMLElement)) { + return; + } + + const nextId = target.getAttribute("data-related-cycle-id"); + setSelectedCycle(nextId); + renderAll(); + } + + function bindEvents() { + const { searchInputEl, searchClearEl, detailBodyEl } = getElements(); + + if (searchInputEl) { + searchInputEl.addEventListener("input", handleSearchInput); + } + + if (searchClearEl) { + searchClearEl.addEventListener("click", handleSearchClear); + } + + if (detailBodyEl) { + detailBodyEl.addEventListener("click", handleRelatedClick); + } + } + + function ensureCyclesSection(referenceData) { + state.referenceData = referenceData || {}; + state.entries = buildEntries(state.referenceData); + applyFilter(); + + if (!state.initialized) { + bindEvents(); + state.initialized = true; + } + + setSelectedCycle(state.selectedId); + renderAll(); + } + + function selectCycleById(cycleId) { + setSelectedCycle(cycleId); + renderAll(); + } + + window.CyclesSectionUi = { + ensureCyclesSection, + selectCycleById + }; +})(); diff --git a/app/ui-elements.js b/app/ui-elements.js new file mode 100644 index 0000000..96b3efe --- /dev/null +++ b/app/ui-elements.js @@ -0,0 +1,454 @@ +(function () { + "use strict"; + + const { getTarotCardSearchAliases } = window.TarotCardImages || {}; + + const CLASSICAL_ELEMENT_IDS = ["fire", "water", "air", "earth"]; + + const ACE_BY_ELEMENT_ID = { + water: "Ace of Cups", + fire: "Ace of Wands", + air: "Ace of Swords", + earth: "Ace of Disks" + }; + + const HEBREW_LETTER_NAME_BY_ELEMENT_ID = { + fire: "Yod", + water: "Heh", + air: "Vav", + earth: "Heh" + }; + + const HEBREW_LETTER_CHAR_BY_ELEMENT_ID = { + fire: "י", + water: "ה", + air: "ו", + earth: "ה" + }; + + const COURT_RANK_BY_ELEMENT_ID = { + fire: "Knight", + water: "Queen", + air: "Prince", + earth: "Princess" + }; + + const COURT_SUITS = ["Wands", "Cups", "Swords", "Disks"]; + + const SUIT_BY_ELEMENT_ID = { + fire: "Wands", + water: "Cups", + air: "Swords", + earth: "Disks" + }; + + const SMALL_CARD_GROUPS = [ + { label: "2–4", modality: "Cardinal", numbers: [2, 3, 4] }, + { label: "5–7", modality: "Fixed", numbers: [5, 6, 7] }, + { label: "8–10", modality: "Mutable", numbers: [8, 9, 10] } + ]; + + const SIGN_BY_ELEMENT_AND_MODALITY = { + fire: { cardinal: "aries", fixed: "leo", mutable: "sagittarius" }, + water: { cardinal: "cancer", fixed: "scorpio", mutable: "pisces" }, + air: { cardinal: "libra", fixed: "aquarius", mutable: "gemini" }, + earth: { cardinal: "capricorn", fixed: "taurus", mutable: "virgo" } + }; + + const state = { + initialized: false, + entries: [], + filteredEntries: [], + selectedId: "", + searchQuery: "" + }; + + function getElements() { + return { + listEl: document.getElementById("elements-list"), + countEl: document.getElementById("elements-count"), + searchEl: document.getElementById("elements-search-input"), + searchClearEl: document.getElementById("elements-search-clear"), + detailNameEl: document.getElementById("elements-detail-name"), + detailSubEl: document.getElementById("elements-detail-sub"), + detailBodyEl: document.getElementById("elements-detail-body") + }; + } + + function normalize(value) { + return String(value || "") + .trim() + .toLowerCase() + .replace(/\s+/g, " "); + } + + function titleCase(value) { + return String(value || "") + .split(" ") + .filter(Boolean) + .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) + .join(" "); + } + + function buildTarotAliasText(cardNames) { + if (typeof getTarotCardSearchAliases !== "function") { + return Array.isArray(cardNames) ? cardNames.join(" ") : ""; + } + + const aliases = new Set(); + (Array.isArray(cardNames) ? cardNames : []).forEach((cardName) => { + getTarotCardSearchAliases(cardName).forEach((alias) => aliases.add(alias)); + }); + return Array.from(aliases).join(" "); + } + + function buildSmallCardGroupsForElement(elementId) { + const suit = SUIT_BY_ELEMENT_ID[elementId] || ""; + const signByModality = SIGN_BY_ELEMENT_AND_MODALITY[elementId] || {}; + + return SMALL_CARD_GROUPS.map((group) => { + const signId = String(signByModality[String(group.modality || "").toLowerCase()] || "").trim(); + const signName = titleCase(signId); + const cardNames = group.numbers.map((number) => `${number} of ${suit}`); + + return { + rangeLabel: group.label, + modality: group.modality, + signId, + signName, + cardNames + }; + }); + } + + function buildEntries(magickDataset) { + const source = magickDataset?.grouped?.alchemy?.elements; + if (!source || typeof source !== "object") { + return []; + } + + return CLASSICAL_ELEMENT_IDS + .map((id) => { + const item = source[id]; + if (!item || typeof item !== "object") { + return null; + } + + const name = String(item?.name?.en || item?.name || titleCase(id)).trim() || titleCase(id); + const symbol = String(item?.symbol || "").trim(); + const aceCardName = ACE_BY_ELEMENT_ID[id] || ""; + const hebrewLetter = HEBREW_LETTER_CHAR_BY_ELEMENT_ID[id] || ""; + const hebrewLetterName = HEBREW_LETTER_NAME_BY_ELEMENT_ID[id] || ""; + const courtRank = COURT_RANK_BY_ELEMENT_ID[id] || ""; + const courtCardNames = courtRank + ? COURT_SUITS.map((suit) => `${courtRank} of ${suit}`) + : []; + const smallCardGroups = buildSmallCardGroupsForElement(id); + const smallCardNames = smallCardGroups.flatMap((group) => group.cardNames || []); + const tarotAliasText = buildTarotAliasText([aceCardName, ...courtCardNames, ...smallCardNames]); + + return { + id, + name, + symbol, + elementalId: String(item?.elementalId || "").trim(), + aceCardName, + hebrewLetter, + hebrewLetterName, + courtRank, + courtCardNames, + smallCardGroups, + searchText: normalize(`${id} ${name} ${symbol} ${aceCardName} ${hebrewLetter} ${hebrewLetterName} ${courtRank} ${courtCardNames.join(" ")} ${smallCardGroups.map((group) => `${group.modality} ${group.signName} ${group.cardNames.join(" ")}`).join(" ")} ${tarotAliasText}`) + }; + }) + .filter(Boolean); + } + + function findEntryById(id) { + const normalizedId = normalize(id); + return state.entries.find((entry) => entry.id === normalizedId) || null; + } + + function renderList(elements) { + if (!elements?.listEl) { + return; + } + + elements.listEl.replaceChildren(); + + state.filteredEntries.forEach((entry) => { + const button = document.createElement("button"); + button.type = "button"; + button.className = "planet-list-item"; + button.dataset.elementId = entry.id; + button.setAttribute("role", "option"); + + const isSelected = entry.id === state.selectedId; + button.classList.toggle("is-selected", isSelected); + button.setAttribute("aria-selected", isSelected ? "true" : "false"); + + const name = document.createElement("span"); + name.className = "planet-list-name"; + name.textContent = `${entry.symbol} ${entry.name}`.trim(); + + const meta = document.createElement("span"); + meta.className = "planet-list-meta"; + meta.textContent = `Letter: ${entry.hebrewLetter || "--"} · Ace: ${entry.aceCardName || "--"} · Court: ${entry.courtRank || "--"}`; + + button.append(name, meta); + elements.listEl.appendChild(button); + }); + + if (elements.countEl) { + elements.countEl.textContent = `${state.filteredEntries.length} elements`; + } + + if (!state.filteredEntries.length) { + const empty = document.createElement("div"); + empty.className = "planet-text"; + empty.style.padding = "16px"; + empty.style.color = "#71717a"; + empty.textContent = "No elements match your search."; + elements.listEl.appendChild(empty); + } + } + + function renderDetail(elements) { + if (!elements?.detailNameEl || !elements.detailSubEl || !elements.detailBodyEl) { + return; + } + + const entry = findEntryById(state.selectedId); + elements.detailBodyEl.replaceChildren(); + + if (!entry) { + elements.detailNameEl.textContent = "--"; + elements.detailSubEl.textContent = "Select an element to explore"; + return; + } + + elements.detailNameEl.textContent = `${entry.symbol} ${entry.name}`.trim(); + elements.detailSubEl.textContent = "Classical Element"; + + const grid = document.createElement("div"); + grid.className = "planet-meta-grid"; + + const detailsCard = document.createElement("div"); + detailsCard.className = "planet-meta-card"; + detailsCard.innerHTML = ` + Element Details +
+
Name
${entry.name}
+
Symbol
${entry.symbol || "--"}
+
Hebrew Letter
${entry.hebrewLetter || "--"}
+
Court Rank
${entry.courtRank || "--"}
+
ID
${entry.id}
+
+ `; + + const tarotCard = document.createElement("div"); + tarotCard.className = "planet-meta-card"; + + const tarotTitle = document.createElement("strong"); + tarotTitle.textContent = "Tarot Correspondence"; + + const tarotText = document.createElement("div"); + tarotText.className = "planet-text"; + tarotText.textContent = [ + entry.aceCardName ? `Ace: ${entry.aceCardName}` : "", + entry.courtRank ? `Court Rank: ${entry.courtRank} (all suits)` : "" + ].filter(Boolean).join(" · ") || "--"; + + tarotCard.append(tarotTitle, tarotText); + + if (entry.aceCardName || entry.courtCardNames.length) { + const navWrap = document.createElement("div"); + navWrap.className = "alpha-nav-btns"; + + if (entry.aceCardName) { + const tarotBtn = document.createElement("button"); + tarotBtn.type = "button"; + tarotBtn.className = "alpha-nav-btn"; + tarotBtn.textContent = `Open ${entry.aceCardName} ↗`; + tarotBtn.addEventListener("click", () => { + document.dispatchEvent(new CustomEvent("nav:tarot-trump", { + detail: { cardName: entry.aceCardName } + })); + }); + + navWrap.appendChild(tarotBtn); + } + + entry.courtCardNames.forEach((cardName) => { + const courtBtn = document.createElement("button"); + courtBtn.type = "button"; + courtBtn.className = "alpha-nav-btn"; + courtBtn.textContent = `Open ${cardName} ↗`; + courtBtn.addEventListener("click", () => { + document.dispatchEvent(new CustomEvent("nav:tarot-trump", { + detail: { cardName } + })); + }); + navWrap.appendChild(courtBtn); + }); + + tarotCard.appendChild(navWrap); + } + + const smallCardCard = document.createElement("div"); + smallCardCard.className = "planet-meta-card"; + + const smallCardTitle = document.createElement("strong"); + smallCardTitle.textContent = "Small Card Sign Types"; + smallCardCard.appendChild(smallCardTitle); + + const smallCardStack = document.createElement("div"); + smallCardStack.className = "cal-item-stack"; + + (entry.smallCardGroups || []).forEach((group) => { + const row = document.createElement("div"); + row.className = "cal-item-row"; + + const head = document.createElement("div"); + head.className = "cal-item-head"; + head.innerHTML = ` + ${group.rangeLabel} · ${group.modality} + ${group.signName || "--"} + `; + row.appendChild(head); + + const navWrap = document.createElement("div"); + navWrap.className = "alpha-nav-btns"; + + if (group.signId) { + const signBtn = document.createElement("button"); + signBtn.type = "button"; + signBtn.className = "alpha-nav-btn"; + signBtn.textContent = `Open ${group.signName} ↗`; + signBtn.addEventListener("click", () => { + document.dispatchEvent(new CustomEvent("nav:zodiac", { + detail: { signId: group.signId } + })); + }); + navWrap.appendChild(signBtn); + } + + (group.cardNames || []).forEach((cardName) => { + const cardBtn = document.createElement("button"); + cardBtn.type = "button"; + cardBtn.className = "alpha-nav-btn"; + cardBtn.textContent = `Open ${cardName} ↗`; + cardBtn.addEventListener("click", () => { + document.dispatchEvent(new CustomEvent("nav:tarot-trump", { + detail: { cardName } + })); + }); + navWrap.appendChild(cardBtn); + }); + + row.appendChild(navWrap); + smallCardStack.appendChild(row); + }); + + smallCardCard.appendChild(smallCardStack); + + grid.append(detailsCard, tarotCard, smallCardCard); + elements.detailBodyEl.appendChild(grid); + } + + function applyFilter(elements) { + const query = normalize(state.searchQuery); + state.filteredEntries = query + ? state.entries.filter((entry) => entry.searchText.includes(query)) + : [...state.entries]; + + if (elements?.searchClearEl) { + elements.searchClearEl.disabled = !query; + } + + if (!state.filteredEntries.some((entry) => entry.id === state.selectedId)) { + state.selectedId = state.filteredEntries[0]?.id || ""; + } + + renderList(elements); + renderDetail(elements); + } + + function selectByElementId(elementId) { + const target = findEntryById(elementId); + if (!target) { + return false; + } + + const elements = getElements(); + state.selectedId = target.id; + renderList(elements); + renderDetail(elements); + + const listItem = elements.listEl?.querySelector(`[data-element-id="${target.id}"]`); + listItem?.scrollIntoView({ block: "nearest" }); + + return true; + } + + function ensureElementsSection(magickDataset) { + const elements = getElements(); + if (!elements.listEl || !elements.detailBodyEl) { + return; + } + + state.entries = buildEntries(magickDataset); + + if (!state.selectedId && state.entries.length) { + state.selectedId = state.entries[0].id; + } + + applyFilter(elements); + + if (state.initialized) { + return; + } + + elements.listEl.addEventListener("click", (event) => { + const target = event.target instanceof Element + ? event.target.closest(".planet-list-item") + : null; + + if (!(target instanceof HTMLButtonElement)) { + return; + } + + const elementId = target.dataset.elementId; + if (!elementId) { + return; + } + + state.selectedId = elementId; + renderList(elements); + renderDetail(elements); + }); + + if (elements.searchEl) { + elements.searchEl.addEventListener("input", () => { + state.searchQuery = elements.searchEl.value || ""; + applyFilter(elements); + }); + } + + if (elements.searchClearEl && elements.searchEl) { + elements.searchClearEl.addEventListener("click", () => { + state.searchQuery = ""; + elements.searchEl.value = ""; + applyFilter(elements); + elements.searchEl.focus(); + }); + } + + state.initialized = true; + } + + window.ElementsSectionUi = { + ensureElementsSection, + selectByElementId + }; +})(); diff --git a/app/ui-enochian.js b/app/ui-enochian.js new file mode 100644 index 0000000..d02c01a --- /dev/null +++ b/app/ui-enochian.js @@ -0,0 +1,459 @@ +(function () { + "use strict"; + + const TABLET_META = { + union: { label: "Enochian Tablet of Union", element: "Spirit", order: 0 }, + spirit: { label: "Enochian Tablet of Union", element: "Spirit", order: 0 }, + earth: { label: "Enochian Tablet of Earth", element: "Earth", order: 1 }, + air: { label: "Enochian Tablet of Air", element: "Air", order: 2 }, + water: { label: "Enochian Tablet of Water", element: "Water", order: 3 }, + fire: { label: "Enochian Tablet of Fire", element: "Fire", order: 4 } + }; + + const TAROT_NAME_ALIASES = { + juggler: "magus", + magician: "magus", + strength: "lust", + temperance: "art", + judgement: "aeon", + judgment: "aeon", + charit: "chariot" + }; + + const state = { + initialized: false, + entries: [], + filteredEntries: [], + selectedId: "", + selectedCell: null, + searchQuery: "", + lettersById: new Map() + }; + + function getElements() { + return { + listEl: document.getElementById("enochian-list"), + countEl: document.getElementById("enochian-count"), + searchEl: document.getElementById("enochian-search-input"), + searchClearEl: document.getElementById("enochian-search-clear"), + detailNameEl: document.getElementById("enochian-detail-name"), + detailSubEl: document.getElementById("enochian-detail-sub"), + detailBodyEl: document.getElementById("enochian-detail-body") + }; + } + + function normalize(value) { + return String(value || "") + .trim() + .toLowerCase() + .replace(/\s+/g, " "); + } + + function titleCase(value) { + return String(value || "") + .split(" ") + .filter(Boolean) + .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) + .join(" "); + } + + function getTabletMeta(id) { + const normalizedId = normalize(id); + return TABLET_META[normalizedId] || { + label: `Enochian Tablet of ${titleCase(normalizedId || "Unknown")}`, + element: titleCase(normalizedId || "Unknown"), + order: 99 + }; + } + + function buildSearchText(entry) { + return normalize([ + entry.id, + entry.label, + entry.element, + entry.rowCount, + entry.colCount, + ...entry.uniqueLetters + ].join(" ")); + } + + function buildEntries(magickDataset) { + const tablets = magickDataset?.grouped?.enochian?.tablets; + if (!tablets || typeof tablets !== "object") { + return []; + } + + return Object.entries(tablets) + .map(([key, value]) => { + const id = normalize(value?.id || key); + const grid = Array.isArray(value?.grid) + ? value.grid.map((row) => (Array.isArray(row) + ? row.map((cell) => String(cell || "").trim()) + : [])) + : []; + + const rowCount = grid.length; + const colCount = grid.reduce((max, row) => Math.max(max, row.length), 0); + const uniqueLetters = [...new Set( + grid + .flat() + .map((cell) => String(cell || "").trim().toUpperCase()) + .filter(Boolean) + )].sort((left, right) => left.localeCompare(right)); + + const meta = getTabletMeta(id); + + return { + id, + grid, + rowCount, + colCount, + uniqueLetters, + element: meta.element, + label: meta.label, + order: Number(meta.order) + }; + }) + .sort((left, right) => left.order - right.order || left.label.localeCompare(right.label)); + } + + function buildLetterMap(magickDataset) { + const letters = magickDataset?.grouped?.enochian?.letters; + if (!letters || typeof letters !== "object") { + return new Map(); + } + + return new Map( + Object.entries(letters) + .map(([key, value]) => [String(key || "").trim().toUpperCase(), value]) + .filter(([key]) => Boolean(key)) + ); + } + + function findEntryById(id) { + return state.entries.find((entry) => entry.id === id) || null; + } + + function getDefaultCell(entry) { + if (!entry || !Array.isArray(entry.grid)) { + return null; + } + + for (let rowIndex = 0; rowIndex < entry.grid.length; rowIndex += 1) { + const row = entry.grid[rowIndex]; + for (let colIndex = 0; colIndex < row.length; colIndex += 1) { + const value = String(row[colIndex] || "").trim(); + if (value) { + return { rowIndex, colIndex, value }; + } + } + } + + return null; + } + + function renderList(elements) { + if (!elements?.listEl) { + return; + } + + elements.listEl.replaceChildren(); + + state.filteredEntries.forEach((entry) => { + const button = document.createElement("button"); + button.type = "button"; + button.className = "enoch-list-item"; + button.dataset.tabletId = entry.id; + button.setAttribute("role", "option"); + + const isSelected = entry.id === state.selectedId; + button.classList.toggle("is-selected", isSelected); + button.setAttribute("aria-selected", isSelected ? "true" : "false"); + + const name = document.createElement("span"); + name.className = "enoch-list-name"; + name.textContent = entry.label; + + const meta = document.createElement("span"); + meta.className = "enoch-list-meta"; + meta.textContent = `${entry.element} · ${entry.rowCount}×${entry.colCount} · ${entry.uniqueLetters.length} letters`; + + button.append(name, meta); + elements.listEl.appendChild(button); + }); + + if (elements.countEl) { + elements.countEl.textContent = `${state.filteredEntries.length} tablets`; + } + + if (!state.filteredEntries.length) { + const empty = document.createElement("div"); + empty.className = "planet-text"; + empty.style.padding = "16px"; + empty.style.color = "#71717a"; + empty.textContent = "No Enochian tablets match your search."; + elements.listEl.appendChild(empty); + } + } + + function resolveTarotCardName(value) { + const normalized = normalize(value); + if (!normalized) { + return ""; + } + + return TAROT_NAME_ALIASES[normalized] || normalized; + } + + function renderDetail(elements) { + if (!elements?.detailBodyEl || !elements.detailNameEl || !elements.detailSubEl) { + return; + } + + const entry = findEntryById(state.selectedId); + elements.detailBodyEl.replaceChildren(); + + if (!entry) { + elements.detailNameEl.textContent = "--"; + elements.detailSubEl.textContent = "Select a tablet to explore"; + return; + } + + elements.detailNameEl.textContent = entry.label; + elements.detailSubEl.textContent = `${entry.element} Tablet · ${entry.rowCount} rows × ${entry.colCount} columns`; + + if (!state.selectedCell) { + state.selectedCell = getDefaultCell(entry); + } + + const detailGrid = document.createElement("div"); + detailGrid.className = "planet-meta-grid"; + + const summaryCard = document.createElement("div"); + summaryCard.className = "planet-meta-card"; + summaryCard.innerHTML = ` + Tablet Overview +
+
${entry.label}
+
${entry.rowCount} rows × ${entry.colCount} columns
+
Unique letters: ${entry.uniqueLetters.length}
+
+ `; + + const gridCard = document.createElement("div"); + gridCard.className = "planet-meta-card"; + const gridTitle = document.createElement("strong"); + gridTitle.textContent = "Tablet Grid"; + const gridEl = document.createElement("div"); + gridEl.className = "enoch-grid"; + + entry.grid.forEach((row, rowIndex) => { + const rowEl = document.createElement("div"); + rowEl.className = "enoch-grid-row"; + row.forEach((cell, colIndex) => { + const value = String(cell || "").trim(); + const cellBtn = document.createElement("button"); + cellBtn.type = "button"; + cellBtn.className = "enoch-grid-cell"; + cellBtn.textContent = value || "·"; + + const isSelectedCell = state.selectedCell + && state.selectedCell.rowIndex === rowIndex + && state.selectedCell.colIndex === colIndex; + cellBtn.classList.toggle("is-selected", Boolean(isSelectedCell)); + + cellBtn.addEventListener("click", () => { + state.selectedCell = { rowIndex, colIndex, value }; + renderDetail(elements); + }); + + rowEl.appendChild(cellBtn); + }); + gridEl.appendChild(rowEl); + }); + + gridCard.append(gridTitle, gridEl); + + const letterCard = document.createElement("div"); + letterCard.className = "planet-meta-card"; + const letterTitle = document.createElement("strong"); + letterTitle.textContent = "Selected Letter"; + + const selectedLetter = String(state.selectedCell?.value || "").trim().toUpperCase(); + const letterData = selectedLetter ? state.lettersById.get(selectedLetter) : null; + + const letterContent = document.createElement("div"); + letterContent.className = "enoch-letter-meta"; + + if (!selectedLetter) { + letterContent.textContent = "Select any grid cell to inspect its correspondence data."; + } else { + const firstRow = document.createElement("div"); + firstRow.className = "enoch-letter-row"; + const chip = document.createElement("span"); + chip.className = "enoch-letter-chip"; + chip.textContent = selectedLetter; + firstRow.appendChild(chip); + + const title = document.createElement("span"); + title.textContent = letterData?.title + ? `${letterData.title}${letterData.english ? ` · ${letterData.english}` : ""}` + : "No letter metadata yet"; + firstRow.appendChild(title); + letterContent.appendChild(firstRow); + + if (letterData) { + const detailRows = [ + ["Pronunciation", letterData.pronounciation], + ["Planet / Element", letterData["planet/element"]], + ["Tarot", letterData.tarot], + ["Gematria", letterData.gematria] + ]; + + detailRows.forEach(([label, value]) => { + if (value === undefined || value === null || String(value).trim() === "") { + return; + } + const row = document.createElement("div"); + row.className = "enoch-letter-row"; + row.innerHTML = `${label}:${value}`; + letterContent.appendChild(row); + }); + + const navRow = document.createElement("div"); + navRow.className = "enoch-letter-row"; + + const tarotCardName = resolveTarotCardName(letterData.tarot); + if (tarotCardName) { + const tarotBtn = document.createElement("button"); + tarotBtn.type = "button"; + tarotBtn.className = "enoch-nav-btn"; + tarotBtn.textContent = `Open Tarot (${titleCase(tarotCardName)}) ↗`; + tarotBtn.addEventListener("click", () => { + document.dispatchEvent(new CustomEvent("nav:tarot-trump", { + detail: { cardName: tarotCardName } + })); + }); + navRow.appendChild(tarotBtn); + } + + const alphabetBtn = document.createElement("button"); + alphabetBtn.type = "button"; + alphabetBtn.className = "enoch-nav-btn"; + alphabetBtn.textContent = "Open Alphabet ↗"; + alphabetBtn.addEventListener("click", () => { + document.dispatchEvent(new CustomEvent("nav:alphabet", { + detail: { + alphabet: "english", + englishLetter: selectedLetter + } + })); + }); + navRow.appendChild(alphabetBtn); + + letterContent.appendChild(navRow); + } + } + + letterCard.append(letterTitle, letterContent); + + detailGrid.append(summaryCard, letterCard, gridCard); + elements.detailBodyEl.appendChild(detailGrid); + } + + function applyFilter(elements) { + const query = normalize(state.searchQuery); + state.filteredEntries = query + ? state.entries.filter((entry) => buildSearchText(entry).includes(query)) + : [...state.entries]; + + if (elements?.searchClearEl) { + elements.searchClearEl.disabled = !query; + } + + if (!state.filteredEntries.some((entry) => entry.id === state.selectedId)) { + state.selectedId = state.filteredEntries[0]?.id || ""; + state.selectedCell = state.selectedId ? getDefaultCell(findEntryById(state.selectedId)) : null; + } + + renderList(elements); + renderDetail(elements); + } + + function selectByTabletId(tabletId) { + const elements = getElements(); + const target = findEntryById(normalize(tabletId)); + if (!target) { + return false; + } + + state.selectedId = target.id; + state.selectedCell = getDefaultCell(target); + renderList(elements); + renderDetail(elements); + return true; + } + + function ensureEnochianSection(magickDataset) { + const elements = getElements(); + if (!elements.listEl || !elements.detailBodyEl) { + return; + } + + state.entries = buildEntries(magickDataset); + state.lettersById = buildLetterMap(magickDataset); + + if (!state.selectedId && state.entries.length) { + state.selectedId = state.entries[0].id; + state.selectedCell = getDefaultCell(state.entries[0]); + } + + applyFilter(elements); + + if (state.initialized) { + return; + } + + elements.listEl.addEventListener("click", (event) => { + const target = event.target instanceof Element + ? event.target.closest(".enoch-list-item") + : null; + if (!(target instanceof HTMLButtonElement)) { + return; + } + + const tabletId = target.dataset.tabletId; + if (!tabletId) { + return; + } + + state.selectedId = tabletId; + state.selectedCell = getDefaultCell(findEntryById(tabletId)); + renderList(elements); + renderDetail(elements); + }); + + if (elements.searchEl) { + elements.searchEl.addEventListener("input", () => { + state.searchQuery = elements.searchEl.value || ""; + applyFilter(elements); + }); + } + + if (elements.searchClearEl && elements.searchEl) { + elements.searchClearEl.addEventListener("click", () => { + state.searchQuery = ""; + elements.searchEl.value = ""; + applyFilter(elements); + elements.searchEl.focus(); + }); + } + + state.initialized = true; + } + + window.EnochianSectionUi = { + ensureEnochianSection, + selectByTabletId + }; +})(); diff --git a/app/ui-gods.js b/app/ui-gods.js new file mode 100644 index 0000000..ad68bee --- /dev/null +++ b/app/ui-gods.js @@ -0,0 +1,618 @@ +/* ui-gods.js — Divine Pantheons section + * Individual deity browser: Greek, Roman, Egyptian, Hebrew divine names, Archangels. + * Kabbalah paths are shown only as a reference at the bottom of each detail view. + */ +(() => { + // ── State ────────────────────────────────────────────────────────────────── + const state = { + initialized: false, + gods: [], + godsByName: new Map(), + monthRefsByGodId: new Map(), + filteredGods: [], + selectedId: null, + activeTab: "greek", + searchQuery: "" + }; + + let listEl, detailNameEl, detailSubEl, detailBodyEl, countEl, + searchInputEl, searchClearEl; + + // ── Tab definitions ──────────────────────────────────────────────────────── + const TABS = [ + { id: "greek", label: "Greek", emoji: "🏛️" }, + { id: "roman", label: "Roman", emoji: "🦅" }, + { id: "egyptian", label: "Egyptian", emoji: "𓂀" }, + { id: "hebrew", label: "God Names", emoji: "✡️" }, + { id: "archangel", label: "Archangels", emoji: "☀️" }, + { id: "all", label: "All", emoji: "∞" }, + ]; + + const PANTHEON_LABEL = { + greek: "Greek", roman: "Roman", egyptian: "Egyptian", + hebrew: "God Names", archangel: "Archangel" + }; + + function normalizeName(value) { + return String(value || "") + .normalize("NFD") + .replace(/[\u0300-\u036f]/g, "") + .toLowerCase() + .replace(/[^a-z0-9]+/g, " ") + .trim(); + } + + function tokenizeEquivalent(value) { + return String(value || "") + .replace(/\([^)]*\)/g, " ") + .split(/\/|,|;|\bor\b|\band\b|·|—|–/i) + .map((token) => token.trim()) + .filter(Boolean); + } + + function findEquivalentTarget(equivalent, currentGodId) { + const tokens = tokenizeEquivalent(equivalent); + for (const token of tokens) { + const matches = state.godsByName.get(normalizeName(token)); + if (!matches?.length) continue; + const target = matches.find((x) => x.id !== currentGodId); + if (target) return target; + } + return null; + } + + function buildMonthReferencesByGod(referenceData) { + const map = new Map(); + const months = Array.isArray(referenceData?.calendarMonths) ? referenceData.calendarMonths : []; + const holidays = Array.isArray(referenceData?.celestialHolidays) ? referenceData.celestialHolidays : []; + const monthById = new Map(months.map((month) => [month.id, month])); + + function parseMonthDayToken(value) { + const text = String(value || "").trim(); + const match = text.match(/^(\d{1,2})-(\d{1,2})$/); + if (!match) { + return null; + } + + const monthNo = Number(match[1]); + const dayNo = Number(match[2]); + if (!Number.isInteger(monthNo) || !Number.isInteger(dayNo) || monthNo < 1 || monthNo > 12 || dayNo < 1 || dayNo > 31) { + return null; + } + + return { month: monthNo, day: dayNo }; + } + + function parseMonthDayTokensFromText(value) { + const text = String(value || ""); + const matches = [...text.matchAll(/(\d{1,2})-(\d{1,2})/g)]; + return matches + .map((match) => ({ month: Number(match[1]), day: Number(match[2]) })) + .filter((token) => Number.isInteger(token.month) && Number.isInteger(token.day) && token.month >= 1 && token.month <= 12 && token.day >= 1 && token.day <= 31); + } + + function toDateToken(token, year) { + if (!token) { + return null; + } + return new Date(year, token.month - 1, token.day, 12, 0, 0, 0); + } + + function splitMonthDayRangeByMonth(startToken, endToken) { + const startDate = toDateToken(startToken, 2025); + const endBase = toDateToken(endToken, 2025); + if (!startDate || !endBase) { + return []; + } + + const wrapsYear = endBase.getTime() < startDate.getTime(); + const endDate = wrapsYear ? toDateToken(endToken, 2026) : endBase; + if (!endDate) { + return []; + } + + const segments = []; + let cursor = new Date(startDate); + while (cursor.getTime() <= endDate.getTime()) { + const monthEnd = new Date(cursor.getFullYear(), cursor.getMonth() + 1, 0, 12, 0, 0, 0); + const segmentEnd = monthEnd.getTime() < endDate.getTime() ? monthEnd : endDate; + + segments.push({ + monthNo: cursor.getMonth() + 1, + startDay: cursor.getDate(), + endDay: segmentEnd.getDate() + }); + + cursor = new Date(segmentEnd.getFullYear(), segmentEnd.getMonth(), segmentEnd.getDate() + 1, 12, 0, 0, 0); + } + + return segments; + } + + function tokenToString(monthNo, dayNo) { + return `${String(monthNo).padStart(2, "0")}-${String(dayNo).padStart(2, "0")}`; + } + + function formatRangeLabel(monthName, startDay, endDay) { + if (!Number.isFinite(startDay) || !Number.isFinite(endDay)) { + return monthName; + } + if (startDay === endDay) { + return `${monthName} ${startDay}`; + } + return `${monthName} ${startDay}-${endDay}`; + } + + function resolveRangeForMonth(month, options = {}) { + const monthOrder = Number(month?.order); + const monthStart = parseMonthDayToken(month?.start); + const monthEnd = parseMonthDayToken(month?.end); + if (!Number.isFinite(monthOrder) || !monthStart || !monthEnd) { + return { + startToken: String(month?.start || "").trim() || null, + endToken: String(month?.end || "").trim() || null, + label: month?.name || month?.id || "", + isFullMonth: true + }; + } + + let startToken = parseMonthDayToken(options.startToken); + let endToken = parseMonthDayToken(options.endToken); + + if (!startToken || !endToken) { + const tokens = parseMonthDayTokensFromText(options.rawDateText); + if (tokens.length >= 2) { + startToken = tokens[0]; + endToken = tokens[1]; + } else if (tokens.length === 1) { + startToken = tokens[0]; + endToken = tokens[0]; + } + } + + if (!startToken || !endToken) { + startToken = monthStart; + endToken = monthEnd; + } + + const segments = splitMonthDayRangeByMonth(startToken, endToken); + const segment = segments.find((entry) => entry.monthNo === monthOrder) || null; + + const useStart = segment ? { month: monthOrder, day: segment.startDay } : startToken; + const useEnd = segment ? { month: monthOrder, day: segment.endDay } : endToken; + const startText = tokenToString(useStart.month, useStart.day); + const endText = tokenToString(useEnd.month, useEnd.day); + const isFullMonth = startText === month.start && endText === month.end; + + return { + startToken: startText, + endToken: endText, + label: isFullMonth + ? (month.name || month.id) + : formatRangeLabel(month.name || month.id, useStart.day, useEnd.day), + isFullMonth + }; + } + + function pushRef(godId, month, options = {}) { + if (!godId || !month?.id) return; + const key = String(godId).trim().toLowerCase(); + if (!key) return; + + if (!map.has(key)) { + map.set(key, []); + } + + const rows = map.get(key); + const range = resolveRangeForMonth(month, options); + const rowKey = `${month.id}|${range.startToken || ""}|${range.endToken || ""}`; + if (rows.some((entry) => entry.key === rowKey)) { + return; + } + + rows.push({ + id: month.id, + name: month.name || month.id, + order: Number.isFinite(Number(month.order)) ? Number(month.order) : 999, + label: range.label, + startToken: range.startToken, + endToken: range.endToken, + isFullMonth: range.isFullMonth, + key: rowKey + }); + } + + months.forEach((month) => { + pushRef(month?.associations?.godId, month); + + const events = Array.isArray(month?.events) ? month.events : []; + events.forEach((event) => { + pushRef(event?.associations?.godId, month, { + rawDateText: event?.dateRange || event?.date || "" + }); + }); + }); + + holidays.forEach((holiday) => { + const month = monthById.get(holiday?.monthId); + if (month) { + pushRef(holiday?.associations?.godId, month, { + rawDateText: holiday?.dateRange || holiday?.date || "" + }); + } + }); + + map.forEach((rows, key) => { + const preciseMonthIds = new Set( + rows + .filter((entry) => !entry.isFullMonth) + .map((entry) => entry.id) + ); + + const filtered = rows.filter((entry) => { + if (!entry.isFullMonth) { + return true; + } + return !preciseMonthIds.has(entry.id); + }); + + filtered.sort((left, right) => { + if (left.order !== right.order) { + return left.order - right.order; + } + + const startLeft = parseMonthDayToken(left.startToken); + const startRight = parseMonthDayToken(right.startToken); + const dayLeft = startLeft ? startLeft.day : 999; + const dayRight = startRight ? startRight.day : 999; + if (dayLeft !== dayRight) { + return dayLeft - dayRight; + } + + return String(left.label || left.name || "").localeCompare(String(right.label || right.name || "")); + }); + + map.set(key, filtered); + }); + + return map; + } + + // ── Filter ───────────────────────────────────────────────────────────────── + function applyFilter() { + const q = state.searchQuery.toLowerCase(); + const tab = state.activeTab; + + state.filteredGods = state.gods.filter((g) => { + if (tab !== "all" && g.pantheon !== tab) return false; + if (!q) return true; + const hay = [ + g.name, g.epithet, g.role, + ...(g.domains || []), + ...(g.parents || []), + ...(g.siblings || []), + ...(g.children || []), + ...(g.symbols || []), + ...(g.equivalents || []), + g.meaning, g.description, g.myth, + ].filter(Boolean).join(" ").toLowerCase(); + return hay.includes(q); + }); + + const hasSelected = state.filteredGods.some((g) => g.id === state.selectedId); + if (!hasSelected) { + state.selectedId = state.filteredGods[0]?.id || null; + } + + renderList(); + renderDetail(state.selectedId); + } + + // ── List ─────────────────────────────────────────────────────────────────── + function renderList() { + if (!listEl) return; + if (countEl) countEl.textContent = `${state.filteredGods.length} deities`; + + if (!state.filteredGods.length) { + listEl.innerHTML = `
No matches
`; + return; + } + + listEl.innerHTML = state.filteredGods.map((g) => { + const isActive = state.selectedId === g.id; + const tag = PANTHEON_LABEL[g.pantheon] || g.pantheon; + return `
+
+ ${g.name} + ${tag} +
+
${g.role || g.epithet || "—"}
+
`; + }).join(""); + + listEl.querySelectorAll(".gods-list-item").forEach((el) => { + el.addEventListener("click", () => selectGod(el.dataset.id)); + el.addEventListener("keydown", (e) => { + if (e.key === "Enter" || e.key === " ") selectGod(el.dataset.id); + }); + }); + } + + // ── Detail ───────────────────────────────────────────────────────────────── + function renderDetail(id) { + if (!detailNameEl) return; + const g = state.gods.find((x) => x.id === id); + if (!g) { + detailNameEl.textContent = "—"; + detailSubEl.textContent = "Select a deity to explore"; + detailBodyEl.innerHTML = ""; + return; + } + + detailNameEl.textContent = g.name; + detailSubEl.textContent = g.epithet || g.role || ""; + + const cards = []; + + // ── Role & Domains ── + if (g.role || g.domains?.length) { + const domHtml = g.domains?.length + ? `
${g.domains.map(d => `${d}`).join("")}
` + : ""; + cards.push(`
+
⚡ Role & Domains
+ ${g.role ? `
${g.role}
` : ""} + ${domHtml} +
`); + } + + // ── Family ── + const hasFamily = [g.parents, g.siblings, g.consorts, g.children].some(a => a?.length); + if (hasFamily) { + const rows = [ + g.parents?.length ? `
Parents${g.parents.join(", ")}
` : "", + g.siblings?.length ? `
Siblings${g.siblings.join(", ")}
` : "", + g.consorts?.length ? `
Consort(s)${g.consorts.join(", ")}
` : "", + g.children?.length ? `
Children${g.children.join(", ")}
` : "", + ].filter(Boolean).join(""); + cards.push(`
+
👨‍👩‍👧 Family
+ ${rows} +
`); + } + + // ── Symbols & Sacred ── + if (g.symbols?.length || g.sacredAnimals?.length || g.sacredPlaces?.length) { + const rows = [ + g.symbols?.length ? `
Symbols${g.symbols.join(", ")}
` : "", + g.sacredAnimals?.length ? `
Sacred animals${g.sacredAnimals.join(", ")}
` : "", + g.sacredPlaces?.length ? `
Sacred places${g.sacredPlaces.join(", ")}
` : "", + ].filter(Boolean).join(""); + cards.push(`
+
🔱 Sacred & Symbols
+ ${rows} +
`); + } + + // ── Hebrew Name (divine names / archangels) ── + if (g.hebrew) { + const title = g.pantheon === "archangel" ? "☀️ Angelic Name" : "✡️ Hebrew Name"; + cards.push(`
+
${title}
+
+ ${g.hebrew} + ${g.name} +
+ ${g.meaning ? `
${g.meaning}
` : ""} + ${g.sephirah ? `
Governs Sephirah ${g.sephirah}
` : ""} +
`); + } + + // ── Equivalents ── + if (g.equivalents?.length) { + const equivalentHtml = g.equivalents.map((equivalent) => { + const target = findEquivalentTarget(equivalent, g.id); + if (target) { + return ``; + } + return `${equivalent}`; + }).join(""); + + cards.push(`
+
⟺ Equivalents
+
${equivalentHtml}
+
`); + } + + const monthRefs = state.monthRefsByGodId.get(String(g.id || "").toLowerCase()) || []; + if (monthRefs.length) { + const monthButtons = monthRefs.map((month) => + `` + ).join(""); + + cards.push(`
+
📅 Calendar Months
+
Linked month correspondences for ${g.name}
+
${monthButtons}
+
`); + } + + // ── Description ── + if (g.description) { + cards.push(`
+
📖 Description
+
${g.description}
+
`); + } + + // ── Myth ── + if (g.myth) { + cards.push(`
+
📜 Myth
+
${g.myth}
+
`); + } + + // ── Poem ── + if (g.poem) { + cards.push(`
+
✍️ Poem
+
${g.poem}
+
`); + } + + // ── Kabbalah reference (small, at bottom) ── + if (g.kabbalahPaths?.length) { + const btnHtml = g.kabbalahPaths.map(p => + `` + ).join(""); + cards.push(`
+
🌳 Kabbalah Reference
+
Paths: ${g.kabbalahPaths.join(", ")}
+
${btnHtml}
+
`); + } + + detailBodyEl.innerHTML = `
${cards.join("")}
`; + + // Attach nav button listeners + detailBodyEl.querySelectorAll(".gods-nav-btn[data-event]").forEach((btn) => { + btn.addEventListener("click", () => { + const evtName = btn.dataset.event; + const detail = {}; + Object.entries(btn.dataset).forEach(([key, val]) => { + if (key === "event") return; + const camel = key.replace(/-([a-z])/g, (_, c) => c.toUpperCase()); + detail[camel] = isNaN(Number(val)) || val === "" ? val : Number(val); + }); + document.dispatchEvent(new CustomEvent(evtName, { detail })); + }); + }); + + detailBodyEl.querySelectorAll(".gods-equivalent-link[data-god-id]").forEach((btn) => { + btn.addEventListener("click", () => { + const godId = btn.dataset.godId; + if (godId) selectGod(godId); + }); + }); + } + + // ── Tabs ─────────────────────────────────────────────────────────────────── + function renderTabs() { + const tabsEl = document.getElementById("gods-tabs"); + if (!tabsEl) return; + tabsEl.innerHTML = TABS.map((t) => + `` + ).join(""); + tabsEl.querySelectorAll(".gods-tab-btn").forEach((btn) => { + btn.addEventListener("click", () => { + state.activeTab = btn.dataset.tab; + renderTabs(); + applyFilter(); + }); + }); + } + + // ── Public: select god by id ─────────────────────────────────────────────── + function selectGod(id) { + const g = state.gods.find((x) => x.id === id); + if (!g) return false; + if (g && state.activeTab !== "all" && g.pantheon !== state.activeTab) { + state.activeTab = "all"; + renderTabs(); + } + state.selectedId = id; + applyFilter(); + requestAnimationFrame(() => { + const item = listEl?.querySelector(`[data-id="${id}"]`); + if (item) item.scrollIntoView({ block: "nearest", behavior: "smooth" }); + }); + return true; + } + + function selectById(godId) { + return selectGod(godId); + } + + function selectByName(name) { + const tokens = tokenizeEquivalent(name); + for (const token of tokens) { + const matches = state.godsByName.get(normalizeName(token)); + const target = matches?.[0]; + if (target?.id) { + return selectGod(target.id); + } + } + return false; + } + + // ── Public: navigate here from kabbalah (find first god on that path) ────── + function selectByPathNo(pathNo) { + const no = Number(pathNo); + const g = state.gods.find((x) => x.kabbalahPaths?.includes(no)); + if (g) return selectGod(g.id); + return false; + } + + // ── Init ─────────────────────────────────────────────────────────────────── + function ensureGodsSection(magickDataset, referenceData = null) { + if (referenceData) { + state.monthRefsByGodId = buildMonthReferencesByGod(referenceData); + } + + if (state.initialized) { + if (state.selectedId) { + renderDetail(state.selectedId); + } + return; + } + state.initialized = true; + + listEl = document.getElementById("gods-list"); + detailNameEl = document.getElementById("gods-detail-name"); + detailSubEl = document.getElementById("gods-detail-sub"); + detailBodyEl = document.getElementById("gods-detail-body"); + countEl = document.getElementById("gods-count"); + searchInputEl = document.getElementById("gods-search-input"); + searchClearEl = document.getElementById("gods-search-clear"); + + const godsData = magickDataset?.grouped?.["gods"]; + if (!godsData?.gods?.length) { + if (listEl) listEl.innerHTML = `
Failed to load gods data.
`; + return; + } + + state.gods = godsData.gods; + state.godsByName = state.gods.reduce((map, god) => { + const key = normalizeName(god.name); + const row = map.get(key) || []; + row.push(god); + map.set(key, row); + return map; + }, new Map()); + + if (searchInputEl) { + searchInputEl.addEventListener("input", () => { + state.searchQuery = searchInputEl.value; + if (searchClearEl) searchClearEl.disabled = !state.searchQuery; + applyFilter(); + }); + } + if (searchClearEl) { + searchClearEl.disabled = true; + searchClearEl.addEventListener("click", () => { + state.searchQuery = ""; + if (searchInputEl) searchInputEl.value = ""; + searchClearEl.disabled = true; + applyFilter(); + }); + } + + renderTabs(); + applyFilter(); + } + + // ── Expose public API ────────────────────────────────────────────────────── + window.GodsSectionUi = { ensureGodsSection, selectByPathNo, selectById, selectByName }; +})(); + diff --git a/app/ui-holidays.js b/app/ui-holidays.js new file mode 100644 index 0000000..6a5c999 --- /dev/null +++ b/app/ui-holidays.js @@ -0,0 +1,1107 @@ +/* ui-holidays.js - Standalone holiday repository browser */ +(function () { + "use strict"; + + const { getTarotCardDisplayName, getTarotCardSearchAliases } = window.TarotCardImages || {}; + + const state = { + initialized: false, + referenceData: null, + magickDataset: null, + selectedYear: new Date().getFullYear(), + selectedSource: "all", + searchQuery: "", + holidays: [], + filteredHolidays: [], + selectedHolidayId: null, + planetsById: new Map(), + signsById: new Map(), + godsById: new Map(), + hebrewById: new Map(), + calendarData: {} + }; + + const TAROT_TRUMP_NUMBER_BY_NAME = { + "the fool": 0, + fool: 0, + "the magus": 1, + magus: 1, + magician: 1, + "the high priestess": 2, + "high priestess": 2, + "the empress": 3, + empress: 3, + "the emperor": 4, + emperor: 4, + "the hierophant": 5, + hierophant: 5, + "the lovers": 6, + lovers: 6, + "the chariot": 7, + chariot: 7, + strength: 8, + lust: 8, + "the hermit": 9, + hermit: 9, + fortune: 10, + "wheel of fortune": 10, + justice: 11, + "the hanged man": 12, + "hanged man": 12, + death: 13, + temperance: 14, + art: 14, + "the devil": 15, + devil: 15, + "the tower": 16, + tower: 16, + "the star": 17, + star: 17, + "the moon": 18, + moon: 18, + "the sun": 19, + sun: 19, + aeon: 20, + judgement: 20, + judgment: 20, + universe: 21, + world: 21, + "the world": 21 + }; + + const HEBREW_MONTH_ALIAS_BY_ID = { + nisan: ["nisan"], + iyar: ["iyar"], + sivan: ["sivan"], + tammuz: ["tamuz", "tammuz"], + av: ["av"], + elul: ["elul"], + tishrei: ["tishri", "tishrei"], + cheshvan: ["heshvan", "cheshvan", "marcheshvan"], + kislev: ["kislev"], + tevet: ["tevet"], + shvat: ["shevat", "shvat"], + adar: ["adar", "adar i", "adar 1"], + "adar-ii": ["adar ii", "adar 2"] + }; + + const MONTH_NAME_TO_INDEX = { + january: 0, + february: 1, + march: 2, + april: 3, + may: 4, + june: 5, + july: 6, + august: 7, + september: 8, + october: 9, + november: 10, + december: 11 + }; + + const GREGORIAN_MONTH_ID_TO_ORDER = { + january: 1, + february: 2, + march: 3, + april: 4, + may: 5, + june: 6, + july: 7, + august: 8, + september: 9, + october: 10, + november: 11, + december: 12 + }; + + function getElements() { + return { + sourceSelectEl: document.getElementById("holiday-source-select"), + yearInputEl: document.getElementById("holiday-year-input"), + searchInputEl: document.getElementById("holiday-search-input"), + searchClearEl: document.getElementById("holiday-search-clear"), + countEl: document.getElementById("holiday-count"), + listEl: document.getElementById("holiday-list"), + detailNameEl: document.getElementById("holiday-detail-name"), + detailSubEl: document.getElementById("holiday-detail-sub"), + detailBodyEl: document.getElementById("holiday-detail-body") + }; + } + + function normalizeText(value) { + return String(value || "").trim(); + } + + function normalizeSearchValue(value) { + return String(value || "").trim().toLowerCase(); + } + + function cap(value) { + const text = normalizeText(value); + return text ? text.charAt(0).toUpperCase() + text.slice(1) : text; + } + + function normalizeTarotName(value) { + return String(value || "") + .trim() + .toLowerCase() + .replace(/\s+/g, " "); + } + + function resolveTarotTrumpNumber(cardName) { + const key = normalizeTarotName(cardName); + if (!key) { + return null; + } + if (Object.prototype.hasOwnProperty.call(TAROT_TRUMP_NUMBER_BY_NAME, key)) { + return TAROT_TRUMP_NUMBER_BY_NAME[key]; + } + const withoutLeadingThe = key.replace(/^the\s+/, ""); + if (Object.prototype.hasOwnProperty.call(TAROT_TRUMP_NUMBER_BY_NAME, withoutLeadingThe)) { + return TAROT_TRUMP_NUMBER_BY_NAME[withoutLeadingThe]; + } + return null; + } + + function getDisplayTarotName(cardName, trumpNumber) { + if (!cardName) { + return ""; + } + + if (typeof getTarotCardDisplayName !== "function") { + return cardName; + } + + if (Number.isFinite(Number(trumpNumber))) { + return getTarotCardDisplayName(cardName, { trumpNumber: Number(trumpNumber) }) || cardName; + } + + return getTarotCardDisplayName(cardName) || cardName; + } + + function normalizeCalendarText(value) { + return String(value || "") + .normalize("NFKD") + .replace(/[\u0300-\u036f]/g, "") + .replace(/['`]/g, "") + .toLowerCase() + .replace(/[^a-z0-9]+/g, " ") + .trim(); + } + + function readNumericPart(parts, partType) { + const raw = parts.find((part) => part.type === partType)?.value; + if (!raw) { + return null; + } + + const digits = String(raw).replace(/[^0-9]/g, ""); + if (!digits) { + return null; + } + + const parsed = Number(digits); + return Number.isFinite(parsed) ? parsed : null; + } + + function getGregorianMonthOrderFromId(monthId) { + if (!monthId) { + return null; + } + const key = String(monthId).trim().toLowerCase(); + const value = GREGORIAN_MONTH_ID_TO_ORDER[key]; + return Number.isFinite(value) ? value : null; + } + + function parseMonthDayStartToken(token) { + const match = String(token || "").match(/(\d{2})-(\d{2})/); + if (!match) { + return null; + } + + const month = Number(match[1]); + const day = Number(match[2]); + if (!Number.isFinite(month) || !Number.isFinite(day)) { + return null; + } + + return { month, day }; + } + + function createDateAtNoon(year, monthIndex, dayOfMonth) { + return new Date(Math.trunc(year), monthIndex, Math.trunc(dayOfMonth), 12, 0, 0, 0); + } + + function computeWesternEasterDate(year) { + const y = Math.trunc(Number(year)); + if (!Number.isFinite(y)) { + return null; + } + + // Meeus/Jones/Butcher Gregorian algorithm. + const a = y % 19; + const b = Math.floor(y / 100); + const c = y % 100; + const d = Math.floor(b / 4); + const e = b % 4; + const f = Math.floor((b + 8) / 25); + const g = Math.floor((b - f + 1) / 3); + const h = (19 * a + b - d - g + 15) % 30; + const i = Math.floor(c / 4); + const k = c % 4; + const l = (32 + 2 * e + 2 * i - h - k) % 7; + const m = Math.floor((a + 11 * h + 22 * l) / 451); + const month = Math.floor((h + l - 7 * m + 114) / 31); + const day = ((h + l - 7 * m + 114) % 31) + 1; + return createDateAtNoon(y, month - 1, day); + } + + function computeNthWeekdayOfMonth(year, monthIndex, weekday, ordinal) { + const y = Math.trunc(Number(year)); + if (!Number.isFinite(y)) { + return null; + } + + const first = createDateAtNoon(y, monthIndex, 1); + const firstWeekday = first.getDay(); + const offset = (weekday - firstWeekday + 7) % 7; + const dayOfMonth = 1 + offset + (Math.trunc(ordinal) - 1) * 7; + const daysInMonth = new Date(y, monthIndex + 1, 0).getDate(); + if (dayOfMonth > daysInMonth) { + return null; + } + return createDateAtNoon(y, monthIndex, dayOfMonth); + } + + function resolveGregorianDateRule(rule) { + const key = String(rule || "").trim().toLowerCase(); + if (!key) { + return null; + } + + if (key === "gregorian-easter-sunday") { + return computeWesternEasterDate(state.selectedYear); + } + + if (key === "gregorian-good-friday") { + const easter = computeWesternEasterDate(state.selectedYear); + if (!(easter instanceof Date) || Number.isNaN(easter.getTime())) { + return null; + } + return createDateAtNoon(easter.getFullYear(), easter.getMonth(), easter.getDate() - 2); + } + + if (key === "gregorian-thanksgiving-us") { + // US Thanksgiving: 4th Thursday of November. + return computeNthWeekdayOfMonth(state.selectedYear, 10, 4, 4); + } + + return null; + } + + function parseFirstMonthDayFromText(dateText) { + const text = String(dateText || "").replace(/~/g, " "); + const firstSegment = text.split("/")[0] || text; + const match = firstSegment.match(/(January|February|March|April|May|June|July|August|September|October|November|December)\s+(\d{1,2})/i); + if (!match) { + return null; + } + + const monthIndex = MONTH_NAME_TO_INDEX[String(match[1]).toLowerCase()]; + const day = Number(match[2]); + if (!Number.isFinite(monthIndex) || !Number.isFinite(day)) { + return null; + } + + return { monthIndex, day }; + } + + function findHebrewMonthDayInGregorianYear(monthId, day, year) { + const aliases = HEBREW_MONTH_ALIAS_BY_ID[String(monthId || "").toLowerCase()] || []; + const targetDay = Number(day); + if (!aliases.length || !Number.isFinite(targetDay) || !Number.isFinite(year)) { + return null; + } + + const normalizedAliases = aliases.map((alias) => normalizeCalendarText(alias)).filter(Boolean); + const formatter = new Intl.DateTimeFormat("en-u-ca-hebrew", { + day: "numeric", + month: "long", + year: "numeric" + }); + + const cursor = new Date(Math.trunc(year), 0, 1, 12, 0, 0, 0); + const end = new Date(Math.trunc(year), 11, 31, 12, 0, 0, 0); + + while (cursor.getTime() <= end.getTime()) { + const parts = formatter.formatToParts(cursor); + const currentDay = readNumericPart(parts, "day"); + const monthName = normalizeCalendarText(parts.find((part) => part.type === "month")?.value); + if (currentDay === Math.trunc(targetDay) && normalizedAliases.includes(monthName)) { + return new Date(cursor); + } + cursor.setDate(cursor.getDate() + 1); + } + + return null; + } + + function getIslamicMonthOrderById(monthId) { + const month = (state.calendarData?.islamic || []).find((item) => item?.id === monthId); + const order = Number(month?.order); + return Number.isFinite(order) ? Math.trunc(order) : null; + } + + function findIslamicMonthDayInGregorianYear(monthId, day, year) { + const monthOrder = getIslamicMonthOrderById(monthId); + const targetDay = Number(day); + if (!Number.isFinite(monthOrder) || !Number.isFinite(targetDay) || !Number.isFinite(year)) { + return null; + } + + const formatter = new Intl.DateTimeFormat("en-u-ca-islamic", { + day: "numeric", + month: "numeric", + year: "numeric" + }); + + const cursor = new Date(Math.trunc(year), 0, 1, 12, 0, 0, 0); + const end = new Date(Math.trunc(year), 11, 31, 12, 0, 0, 0); + + while (cursor.getTime() <= end.getTime()) { + const parts = formatter.formatToParts(cursor); + const currentDay = readNumericPart(parts, "day"); + const currentMonth = readNumericPart(parts, "month"); + if (currentDay === Math.trunc(targetDay) && currentMonth === monthOrder) { + return new Date(cursor); + } + cursor.setDate(cursor.getDate() + 1); + } + + return null; + } + + function resolveHolidayGregorianDate(holiday) { + if (!holiday || typeof holiday !== "object") { + return null; + } + + const calendarId = String(holiday.calendarId || "").trim().toLowerCase(); + const monthId = String(holiday.monthId || "").trim().toLowerCase(); + const day = Number(holiday.day); + + if (calendarId === "gregorian") { + if (holiday?.dateRule) { + const ruledDate = resolveGregorianDateRule(holiday.dateRule); + if (ruledDate) { + return ruledDate; + } + } + + const monthDay = parseMonthDayStartToken(holiday.monthDayStart) || parseMonthDayStartToken(holiday.dateText); + if (monthDay) { + return new Date(state.selectedYear, monthDay.month - 1, monthDay.day, 12, 0, 0, 0); + } + const order = getGregorianMonthOrderFromId(monthId); + if (Number.isFinite(order) && Number.isFinite(day)) { + return new Date(state.selectedYear, order - 1, Math.trunc(day), 12, 0, 0, 0); + } + return null; + } + + if (calendarId === "hebrew") { + return findHebrewMonthDayInGregorianYear(monthId, day, state.selectedYear); + } + + if (calendarId === "islamic") { + return findIslamicMonthDayInGregorianYear(monthId, day, state.selectedYear); + } + + if (calendarId === "wheel-of-year") { + const monthDay = parseMonthDayStartToken(holiday.monthDayStart) || parseFirstMonthDayFromText(holiday.dateText); + if (monthDay?.month && monthDay?.day) { + return new Date(state.selectedYear, monthDay.month - 1, monthDay.day, 12, 0, 0, 0); + } + if (monthDay?.monthIndex != null && monthDay?.day) { + return new Date(state.selectedYear, monthDay.monthIndex, monthDay.day, 12, 0, 0, 0); + } + } + + return null; + } + + function formatGregorianReferenceDate(date) { + if (!(date instanceof Date) || Number.isNaN(date.getTime())) { + return "--"; + } + + return date.toLocaleDateString(undefined, { + weekday: "long", + year: "numeric", + month: "long", + day: "numeric" + }); + } + + function formatCalendarDateFromGregorian(date, calendarId) { + if (!(date instanceof Date) || Number.isNaN(date.getTime())) { + return "--"; + } + + const locale = calendarId === "hebrew" + ? "en-u-ca-hebrew" + : (calendarId === "islamic" ? "en-u-ca-islamic" : "en"); + + return new Intl.DateTimeFormat(locale, { + weekday: "long", + year: "numeric", + month: "long", + day: "numeric" + }).format(date); + } + + function buildPlanetMap(planetsObj) { + const map = new Map(); + if (!planetsObj || typeof planetsObj !== "object") { + return map; + } + + Object.values(planetsObj).forEach((planet) => { + if (!planet?.id) { + return; + } + map.set(planet.id, planet); + }); + + return map; + } + + function buildSignsMap(signs) { + const map = new Map(); + if (!Array.isArray(signs)) { + return map; + } + + signs.forEach((sign) => { + if (!sign?.id) { + return; + } + map.set(sign.id, sign); + }); + + return map; + } + + function buildGodsMap(magickDataset) { + const gods = magickDataset?.grouped?.gods?.gods; + const map = new Map(); + + if (!Array.isArray(gods)) { + return map; + } + + gods.forEach((god) => { + if (!god?.id) { + return; + } + map.set(god.id, god); + }); + + return map; + } + + function buildHebrewMap(magickDataset) { + const map = new Map(); + const letters = magickDataset?.grouped?.alphabets?.hebrew; + if (!Array.isArray(letters)) { + return map; + } + + letters.forEach((letter) => { + if (!letter?.hebrewLetterId) { + return; + } + map.set(letter.hebrewLetterId, letter); + }); + + return map; + } + + function calendarLabel(calendarId) { + const key = String(calendarId || "").trim().toLowerCase(); + if (key === "hebrew") return "Hebrew"; + if (key === "islamic") return "Islamic"; + if (key === "wheel-of-year") return "Wheel of the Year"; + return "Gregorian"; + } + + function monthLabelForCalendar(calendarId, monthId) { + const months = state.calendarData?.[calendarId]; + if (!Array.isArray(months)) { + return monthId || "--"; + } + + const month = months.find((entry) => String(entry?.id || "").toLowerCase() === String(monthId || "").toLowerCase()); + return month?.name || monthId || "--"; + } + + function normalizeSourceFilter(value) { + const key = String(value || "").trim().toLowerCase(); + if (key === "gregorian" || key === "hebrew" || key === "islamic" || key === "wheel-of-year") { + return key; + } + return "all"; + } + + function buildAllHolidays() { + if (Array.isArray(state.referenceData?.calendarHolidays) && state.referenceData.calendarHolidays.length) { + return [...state.referenceData.calendarHolidays].sort((left, right) => { + const calCmp = calendarLabel(left?.calendarId).localeCompare(calendarLabel(right?.calendarId)); + if (calCmp !== 0) return calCmp; + + const leftDay = Number(left?.day); + const rightDay = Number(right?.day); + if (Number.isFinite(leftDay) && Number.isFinite(rightDay) && leftDay !== rightDay) { + return leftDay - rightDay; + } + + return String(left?.name || "").localeCompare(String(right?.name || "")); + }); + } + + const legacy = Array.isArray(state.referenceData?.celestialHolidays) ? state.referenceData.celestialHolidays : []; + return legacy.map((holiday) => ({ + ...holiday, + calendarId: "gregorian", + dateText: holiday?.date || holiday?.dateRange || "" + })); + } + + function planetLabel(planetId) { + if (!planetId) { + return "Planet"; + } + + const planet = state.planetsById.get(planetId); + if (!planet) { + return cap(planetId); + } + + return `${planet.symbol || ""} ${planet.name || cap(planetId)}`.trim(); + } + + function zodiacLabel(signId) { + if (!signId) { + return "Zodiac"; + } + + const sign = state.signsById.get(signId); + if (!sign) { + return cap(signId); + } + + return `${sign.symbol || ""} ${sign.name || cap(signId)}`.trim(); + } + + function godLabel(godId, godName) { + if (godName) { + return godName; + } + + if (!godId) { + return "Deity"; + } + + const god = state.godsById.get(godId); + return god?.name || cap(godId); + } + + function hebrewLabel(hebrewLetterId) { + if (!hebrewLetterId) { + return "Hebrew Letter"; + } + + const letter = state.hebrewById.get(hebrewLetterId); + if (!letter) { + return cap(hebrewLetterId); + } + + return `${letter.char || ""} ${letter.name || cap(hebrewLetterId)}`.trim(); + } + + function computeDigitalRoot(value) { + let current = Math.abs(Math.trunc(Number(value))); + if (!Number.isFinite(current)) { + return null; + } + + while (current >= 10) { + current = String(current) + .split("") + .reduce((sum, digit) => sum + Number(digit), 0); + } + + return current; + } + + function buildAssociationButtons(associations) { + if (!associations || typeof associations !== "object") { + return "
--
"; + } + + const buttons = []; + + if (associations.planetId) { + buttons.push( + `` + ); + } + + if (associations.zodiacSignId) { + buttons.push( + `` + ); + } + + if (Number.isFinite(Number(associations.numberValue))) { + const rawNumber = Math.trunc(Number(associations.numberValue)); + if (rawNumber >= 0) { + const numberValue = computeDigitalRoot(rawNumber); + if (numberValue != null) { + const label = rawNumber === numberValue + ? `Number ${numberValue}` + : `Number ${numberValue} (from ${rawNumber})`; + buttons.push( + `` + ); + } + } + } + + if (associations.tarotCard) { + const trumpNumber = resolveTarotTrumpNumber(associations.tarotCard); + const explicitTrumpNumber = Number(associations.tarotTrumpNumber); + const tarotTrumpNumber = Number.isFinite(explicitTrumpNumber) ? explicitTrumpNumber : trumpNumber; + const tarotLabel = getDisplayTarotName(associations.tarotCard, tarotTrumpNumber); + buttons.push( + `` + ); + } + + if (associations.godId || associations.godName) { + const label = godLabel(associations.godId, associations.godName); + buttons.push( + `` + ); + } + + if (associations.hebrewLetterId) { + buttons.push( + `` + ); + } + + if (associations.kabbalahPathNumber != null) { + buttons.push( + `` + ); + } + + if (associations.iChingPlanetaryInfluence) { + buttons.push( + `` + ); + } + + if (!buttons.length) { + return "
--
"; + } + + return `
${buttons.join("")}
`; + } + + function associationSearchText(associations) { + if (!associations || typeof associations !== "object") { + return ""; + } + + const tarotAliases = associations.tarotCard && typeof getTarotCardSearchAliases === "function" + ? getTarotCardSearchAliases(associations.tarotCard, { trumpNumber: associations.tarotTrumpNumber }) + : []; + + return [ + associations.planetId, + associations.zodiacSignId, + associations.numberValue, + associations.tarotCard, + associations.tarotTrumpNumber, + ...tarotAliases, + associations.godId, + associations.godName, + associations.hebrewLetterId, + associations.kabbalahPathNumber, + associations.iChingPlanetaryInfluence + ].filter(Boolean).join(" "); + } + + function holidaySearchText(holiday) { + return normalizeSearchValue([ + holiday?.name, + holiday?.kind, + holiday?.date, + holiday?.dateRange, + holiday?.dateText, + holiday?.monthDayStart, + holiday?.calendarId, + holiday?.description, + associationSearchText(holiday?.associations) + ].filter(Boolean).join(" ")); + } + + function getSelectedHoliday() { + return state.holidays.find((holiday) => holiday.id === state.selectedHolidayId) || null; + } + + function filterBySource(holidays) { + const source = normalizeSourceFilter(state.selectedSource); + if (source === "all") { + return [...holidays]; + } + return holidays.filter((holiday) => String(holiday?.calendarId || "").trim().toLowerCase() === source); + } + + function syncControls(elements) { + if (elements.sourceSelectEl) { + elements.sourceSelectEl.value = normalizeSourceFilter(state.selectedSource); + } + if (elements.yearInputEl) { + elements.yearInputEl.value = String(state.selectedYear); + } + if (elements.searchInputEl) { + elements.searchInputEl.value = state.searchQuery; + } + if (elements.searchClearEl) { + elements.searchClearEl.disabled = !state.searchQuery; + } + } + + function renderList(elements) { + const { listEl, countEl } = elements; + if (!listEl) { + return; + } + + listEl.innerHTML = ""; + + state.filteredHolidays.forEach((holiday) => { + const isSelected = holiday.id === state.selectedHolidayId; + const itemEl = document.createElement("div"); + itemEl.className = `planet-list-item${isSelected ? " is-selected" : ""}`; + itemEl.setAttribute("role", "option"); + itemEl.setAttribute("aria-selected", isSelected ? "true" : "false"); + itemEl.dataset.holidayId = holiday.id; + + const sourceCalendar = calendarLabel(holiday.calendarId); + const sourceMonth = monthLabelForCalendar(holiday.calendarId, holiday.monthId); + const sourceDate = holiday?.dateText || holiday?.date || holiday?.dateRange || "--"; + + itemEl.innerHTML = ` +
${holiday?.name || holiday?.id || "Holiday"}
+
${sourceCalendar} - ${sourceMonth} - ${sourceDate}
+ `; + + itemEl.addEventListener("click", () => { + selectByHolidayId(holiday.id, elements); + }); + + listEl.appendChild(itemEl); + }); + + if (countEl) { + const sourceFiltered = filterBySource(state.holidays); + const activeFilter = normalizeSourceFilter(state.selectedSource); + const sourceLabel = activeFilter === "all" + ? "" + : ` (${calendarLabel(activeFilter)})`; + countEl.textContent = state.searchQuery + ? `${state.filteredHolidays.length} of ${sourceFiltered.length} holidays${sourceLabel}` + : `${sourceFiltered.length} holidays${sourceLabel}`; + } + } + + function renderHolidayDetail(holiday) { + const gregorianDate = resolveHolidayGregorianDate(holiday); + const gregorianRef = formatGregorianReferenceDate(gregorianDate); + const hebrewRef = formatCalendarDateFromGregorian(gregorianDate, "hebrew"); + const islamicRef = formatCalendarDateFromGregorian(gregorianDate, "islamic"); + const confidence = String(holiday?.conversionConfidence || holiday?.datePrecision || "approximate").toLowerCase(); + const confidenceLabel = (!(gregorianDate instanceof Date) || Number.isNaN(gregorianDate.getTime())) + ? "unresolved" + : (confidence === "exact" ? "exact" : "approximate"); + const monthName = monthLabelForCalendar(holiday?.calendarId, holiday?.monthId); + const holidayDate = holiday?.dateText || holiday?.date || holiday?.dateRange || "--"; + const sourceMonthLink = holiday?.monthId + ? `
` + : ""; + + return ` +
+
+ Holiday Facts +
+
+
Source Calendar
${calendarLabel(holiday?.calendarId)}
+
Source Month
${monthName}
+
Source Date
${holidayDate}
+
Reference Year
${state.selectedYear}
+
Conversion
${confidenceLabel}
+
+
+
+
+ Cross-Calendar Dates +
+
+
Gregorian
${gregorianRef}
+
Hebrew
${hebrewRef}
+
Islamic
${islamicRef}
+
+
+
+
+ Description +
${holiday?.description || "--"}
+ ${sourceMonthLink} +
+
+ Associations + ${buildAssociationButtons(holiday?.associations)} +
+
+ `; + } + + function renderDetail(elements) { + const { detailNameEl, detailSubEl, detailBodyEl } = elements; + if (!detailNameEl || !detailSubEl || !detailBodyEl) { + return; + } + + const holiday = getSelectedHoliday(); + if (!holiday) { + detailNameEl.textContent = "--"; + detailSubEl.textContent = "Select a holiday to explore"; + detailBodyEl.innerHTML = ""; + return; + } + + detailNameEl.textContent = holiday?.name || holiday?.id || "Holiday"; + detailSubEl.textContent = `${calendarLabel(holiday?.calendarId)} - ${monthLabelForCalendar(holiday?.calendarId, holiday?.monthId)}`; + detailBodyEl.innerHTML = renderHolidayDetail(holiday); + attachNavHandlers(detailBodyEl); + } + + function applyFilters(elements) { + const sourceFiltered = filterBySource(state.holidays); + state.filteredHolidays = state.searchQuery + ? sourceFiltered.filter((holiday) => holidaySearchText(holiday).includes(state.searchQuery)) + : sourceFiltered; + + if (!state.filteredHolidays.some((holiday) => holiday.id === state.selectedHolidayId)) { + state.selectedHolidayId = state.filteredHolidays[0]?.id || null; + } + + syncControls(elements); + renderList(elements); + renderDetail(elements); + } + + function selectByHolidayId(holidayId, elements = getElements()) { + const target = state.holidays.find((holiday) => holiday.id === holidayId); + if (!target) { + return false; + } + + const targetCalendar = String(target.calendarId || "").trim().toLowerCase(); + const activeFilter = normalizeSourceFilter(state.selectedSource); + if (activeFilter !== "all" && activeFilter !== targetCalendar) { + state.selectedSource = targetCalendar || "all"; + } + + if (state.searchQuery && !holidaySearchText(target).includes(state.searchQuery)) { + state.searchQuery = ""; + } + + state.selectedHolidayId = target.id; + applyFilters(elements); + return true; + } + + function bindControls(elements) { + if (elements.sourceSelectEl) { + elements.sourceSelectEl.addEventListener("change", () => { + state.selectedSource = normalizeSourceFilter(elements.sourceSelectEl.value); + applyFilters(elements); + }); + } + + if (elements.yearInputEl) { + elements.yearInputEl.addEventListener("change", () => { + const nextYear = Number(elements.yearInputEl.value); + if (!Number.isFinite(nextYear)) { + elements.yearInputEl.value = String(state.selectedYear); + return; + } + + state.selectedYear = Math.min(2500, Math.max(1900, Math.round(nextYear))); + elements.yearInputEl.value = String(state.selectedYear); + renderDetail(elements); + }); + } + + if (elements.searchInputEl) { + elements.searchInputEl.addEventListener("input", () => { + state.searchQuery = normalizeSearchValue(elements.searchInputEl.value); + applyFilters(elements); + }); + } + + if (elements.searchClearEl && elements.searchInputEl) { + elements.searchClearEl.addEventListener("click", () => { + state.searchQuery = ""; + elements.searchInputEl.value = ""; + applyFilters(elements); + elements.searchInputEl.focus(); + }); + } + } + + function attachNavHandlers(detailBodyEl) { + if (!detailBodyEl) { + return; + } + + detailBodyEl.querySelectorAll("[data-nav]").forEach((button) => { + button.addEventListener("click", () => { + const navType = button.dataset.nav; + + if (navType === "planet" && button.dataset.planetId) { + document.dispatchEvent(new CustomEvent("nav:planet", { + detail: { planetId: button.dataset.planetId } + })); + return; + } + + if (navType === "zodiac" && button.dataset.signId) { + document.dispatchEvent(new CustomEvent("nav:zodiac", { + detail: { signId: button.dataset.signId } + })); + return; + } + + if (navType === "number" && button.dataset.numberValue) { + document.dispatchEvent(new CustomEvent("nav:number", { + detail: { value: Number(button.dataset.numberValue) } + })); + return; + } + + if (navType === "tarot-card" && button.dataset.cardName) { + const trumpNumber = Number(button.dataset.trumpNumber); + document.dispatchEvent(new CustomEvent("nav:tarot-trump", { + detail: { + cardName: button.dataset.cardName, + trumpNumber: Number.isFinite(trumpNumber) ? trumpNumber : undefined + } + })); + return; + } + + if (navType === "god") { + document.dispatchEvent(new CustomEvent("nav:gods", { + detail: { + godId: button.dataset.godId || undefined, + godName: button.dataset.godName || undefined + } + })); + return; + } + + if (navType === "alphabet" && button.dataset.hebrewLetterId) { + document.dispatchEvent(new CustomEvent("nav:alphabet", { + detail: { + alphabet: "hebrew", + hebrewLetterId: button.dataset.hebrewLetterId + } + })); + return; + } + + if (navType === "kabbalah" && button.dataset.pathNo) { + document.dispatchEvent(new CustomEvent("nav:kabbalah-path", { + detail: { pathNo: Number(button.dataset.pathNo) } + })); + return; + } + + if (navType === "iching" && button.dataset.planetaryInfluence) { + document.dispatchEvent(new CustomEvent("nav:iching", { + detail: { + planetaryInfluence: button.dataset.planetaryInfluence + } + })); + return; + } + + if (navType === "calendar-month" && button.dataset.monthId) { + document.dispatchEvent(new CustomEvent("nav:calendar-month", { + detail: { + calendarId: button.dataset.calendarId || undefined, + monthId: button.dataset.monthId + } + })); + } + }); + }); + } + + function ensureHolidaySection(referenceData, magickDataset) { + if (!referenceData) { + return; + } + + state.referenceData = referenceData; + state.magickDataset = magickDataset || null; + state.planetsById = buildPlanetMap(referenceData.planets); + state.signsById = buildSignsMap(referenceData.signs); + state.godsById = buildGodsMap(state.magickDataset); + state.hebrewById = buildHebrewMap(state.magickDataset); + + state.calendarData = { + gregorian: Array.isArray(referenceData.calendarMonths) ? referenceData.calendarMonths : [], + hebrew: Array.isArray(referenceData.hebrewCalendar?.months) ? referenceData.hebrewCalendar.months : [], + islamic: Array.isArray(referenceData.islamicCalendar?.months) ? referenceData.islamicCalendar.months : [], + "wheel-of-year": Array.isArray(referenceData.wheelOfYear?.months) ? referenceData.wheelOfYear.months : [] + }; + + state.holidays = buildAllHolidays(); + if (!state.selectedHolidayId || !state.holidays.some((holiday) => holiday.id === state.selectedHolidayId)) { + state.selectedHolidayId = state.holidays[0]?.id || null; + } + + const elements = getElements(); + + if (!state.initialized) { + state.initialized = true; + bindControls(elements); + } + + applyFilters(elements); + } + + window.HolidaySectionUi = { + ensureHolidaySection, + selectByHolidayId + }; +})(); diff --git a/app/ui-iching.js b/app/ui-iching.js new file mode 100644 index 0000000..dc20472 --- /dev/null +++ b/app/ui-iching.js @@ -0,0 +1,882 @@ +(function () { + const { getTarotCardSearchAliases } = window.TarotCardImages || {}; + + const state = { + initialized: false, + hexagrams: [], + filteredHexagrams: [], + trigramsByName: {}, + tarotByTrigramName: {}, + monthRefsByHexagramNumber: new Map(), + searchQuery: "", + selectedNumber: null + }; + + const ICHING_PLANET_BY_PLANET_ID = { + sol: "Sun", + luna: "Moon", + mercury: "Mercury", + venus: "Venus", + mars: "Mars", + jupiter: "Jupiter", + saturn: "Saturn", + earth: "Earth", + uranus: "Uranus", + neptune: "Neptune", + pluto: "Pluto" + }; + + function getElements() { + return { + ichingCardListEl: document.getElementById("iching-card-list"), + ichingSearchInputEl: document.getElementById("iching-search-input"), + ichingSearchClearEl: document.getElementById("iching-search-clear"), + ichingCountEl: document.getElementById("iching-card-count"), + ichingDetailNameEl: document.getElementById("iching-detail-name"), + ichingDetailTypeEl: document.getElementById("iching-detail-type"), + ichingDetailSummaryEl: document.getElementById("iching-detail-summary"), + ichingDetailJudgementEl: document.getElementById("iching-detail-judgement"), + ichingDetailImageEl: document.getElementById("iching-detail-image"), + ichingDetailBinaryEl: document.getElementById("iching-detail-binary"), + ichingDetailLineEl: document.getElementById("iching-detail-line"), + ichingDetailKeywordsEl: document.getElementById("iching-detail-keywords"), + ichingDetailTrigramsEl: document.getElementById("iching-detail-trigrams"), + ichingDetailPlanetEl: document.getElementById("iching-detail-planet"), + ichingDetailTarotEl: document.getElementById("iching-detail-tarot"), + ichingDetailCalendarEl: document.getElementById("iching-detail-calendar") + }; + } + + function normalizeSearchValue(value) { + return String(value || "").trim().toLowerCase(); + } + + function clearChildren(element) { + if (element) { + element.replaceChildren(); + } + } + + function getTrigramByName(name) { + const key = normalizeSearchValue(name); + return key ? state.trigramsByName[key] || null : null; + } + + function normalizePlanetInfluence(value) { + return String(value || "") + .trim() + .toLowerCase() + .replace(/[^a-z]/g, ""); + } + + function resolveAssociationPlanetInfluence(associations) { + if (!associations || typeof associations !== "object") { + return ""; + } + + const explicit = normalizePlanetInfluence(associations.iChingPlanetaryInfluence || associations.planetaryInfluence); + if (explicit) { + return explicit; + } + + const planetId = normalizePlanetInfluence(associations.planetId); + if (!planetId) { + return ""; + } + + return normalizePlanetInfluence(ICHING_PLANET_BY_PLANET_ID[planetId]); + } + + function buildMonthReferencesByHexagram(referenceData, hexagrams) { + const map = new Map(); + const months = Array.isArray(referenceData?.calendarMonths) ? referenceData.calendarMonths : []; + const holidays = Array.isArray(referenceData?.celestialHolidays) ? referenceData.celestialHolidays : []; + const monthById = new Map(months.map((month) => [month.id, month])); + + function parseMonthDayToken(value) { + const text = String(value || "").trim(); + const match = text.match(/^(\d{1,2})-(\d{1,2})$/); + if (!match) { + return null; + } + + const monthNo = Number(match[1]); + const dayNo = Number(match[2]); + if (!Number.isInteger(monthNo) || !Number.isInteger(dayNo) || monthNo < 1 || monthNo > 12 || dayNo < 1 || dayNo > 31) { + return null; + } + + return { month: monthNo, day: dayNo }; + } + + function parseMonthDayTokensFromText(value) { + const text = String(value || ""); + const matches = [...text.matchAll(/(\d{1,2})-(\d{1,2})/g)]; + return matches + .map((match) => ({ month: Number(match[1]), day: Number(match[2]) })) + .filter((token) => Number.isInteger(token.month) && Number.isInteger(token.day) && token.month >= 1 && token.month <= 12 && token.day >= 1 && token.day <= 31); + } + + function toDateToken(token, year) { + if (!token) { + return null; + } + return new Date(year, token.month - 1, token.day, 12, 0, 0, 0); + } + + function splitMonthDayRangeByMonth(startToken, endToken) { + const startDate = toDateToken(startToken, 2025); + const endBase = toDateToken(endToken, 2025); + if (!startDate || !endBase) { + return []; + } + + const wrapsYear = endBase.getTime() < startDate.getTime(); + const endDate = wrapsYear ? toDateToken(endToken, 2026) : endBase; + if (!endDate) { + return []; + } + + const segments = []; + let cursor = new Date(startDate); + while (cursor.getTime() <= endDate.getTime()) { + const monthEnd = new Date(cursor.getFullYear(), cursor.getMonth() + 1, 0, 12, 0, 0, 0); + const segmentEnd = monthEnd.getTime() < endDate.getTime() ? monthEnd : endDate; + + segments.push({ + monthNo: cursor.getMonth() + 1, + startDay: cursor.getDate(), + endDay: segmentEnd.getDate() + }); + + cursor = new Date(segmentEnd.getFullYear(), segmentEnd.getMonth(), segmentEnd.getDate() + 1, 12, 0, 0, 0); + } + + return segments; + } + + function tokenToString(monthNo, dayNo) { + return `${String(monthNo).padStart(2, "0")}-${String(dayNo).padStart(2, "0")}`; + } + + function formatRangeLabel(monthName, startDay, endDay) { + if (!Number.isFinite(startDay) || !Number.isFinite(endDay)) { + return monthName; + } + if (startDay === endDay) { + return `${monthName} ${startDay}`; + } + return `${monthName} ${startDay}-${endDay}`; + } + + function resolveRangeForMonth(month, options = {}) { + const monthOrder = Number(month?.order); + const monthStart = parseMonthDayToken(month?.start); + const monthEnd = parseMonthDayToken(month?.end); + if (!Number.isFinite(monthOrder) || !monthStart || !monthEnd) { + return { + startToken: String(month?.start || "").trim() || null, + endToken: String(month?.end || "").trim() || null, + label: month?.name || month?.id || "", + isFullMonth: true + }; + } + + let startToken = parseMonthDayToken(options.startToken); + let endToken = parseMonthDayToken(options.endToken); + + if (!startToken || !endToken) { + const tokens = parseMonthDayTokensFromText(options.rawDateText); + if (tokens.length >= 2) { + startToken = tokens[0]; + endToken = tokens[1]; + } else if (tokens.length === 1) { + startToken = tokens[0]; + endToken = tokens[0]; + } + } + + if (!startToken || !endToken) { + startToken = monthStart; + endToken = monthEnd; + } + + const segments = splitMonthDayRangeByMonth(startToken, endToken); + const segment = segments.find((entry) => entry.monthNo === monthOrder) || null; + + const useStart = segment ? { month: monthOrder, day: segment.startDay } : startToken; + const useEnd = segment ? { month: monthOrder, day: segment.endDay } : endToken; + const startText = tokenToString(useStart.month, useStart.day); + const endText = tokenToString(useEnd.month, useEnd.day); + const isFullMonth = startText === month.start && endText === month.end; + + return { + startToken: startText, + endToken: endText, + label: isFullMonth + ? (month.name || month.id) + : formatRangeLabel(month.name || month.id, useStart.day, useEnd.day), + isFullMonth + }; + } + + function pushRef(hexagramNumber, month, options = {}) { + if (!Number.isFinite(hexagramNumber) || !month?.id) { + return; + } + + if (!map.has(hexagramNumber)) { + map.set(hexagramNumber, []); + } + + const rows = map.get(hexagramNumber); + const range = resolveRangeForMonth(month, options); + const rowKey = `${month.id}|${range.startToken || ""}|${range.endToken || ""}`; + if (rows.some((entry) => entry.key === rowKey)) { + return; + } + + rows.push({ + id: month.id, + name: month.name || month.id, + order: Number.isFinite(Number(month.order)) ? Number(month.order) : 999, + label: range.label, + startToken: range.startToken, + endToken: range.endToken, + isFullMonth: range.isFullMonth, + key: rowKey + }); + } + + function collectRefs(associations, month, options = {}) { + const associationInfluence = resolveAssociationPlanetInfluence(associations); + if (!associationInfluence) { + return; + } + + hexagrams.forEach((hexagram) => { + const hexagramInfluence = normalizePlanetInfluence(hexagram?.planetaryInfluence); + if (hexagramInfluence && hexagramInfluence === associationInfluence) { + pushRef(hexagram.number, month, options); + } + }); + } + + months.forEach((month) => { + collectRefs(month?.associations, month); + const events = Array.isArray(month?.events) ? month.events : []; + events.forEach((event) => { + collectRefs(event?.associations, month, { + rawDateText: event?.dateRange || event?.date || "" + }); + }); + }); + + holidays.forEach((holiday) => { + const month = monthById.get(holiday?.monthId); + if (!month) { + return; + } + collectRefs(holiday?.associations, month, { + rawDateText: holiday?.dateRange || holiday?.date || "" + }); + }); + + map.forEach((rows, key) => { + const preciseMonthIds = new Set( + rows + .filter((entry) => !entry.isFullMonth) + .map((entry) => entry.id) + ); + + const filtered = rows.filter((entry) => { + if (!entry.isFullMonth) { + return true; + } + return !preciseMonthIds.has(entry.id); + }); + + filtered.sort((left, right) => { + if (left.order !== right.order) { + return left.order - right.order; + } + + const startLeft = parseMonthDayToken(left.startToken); + const startRight = parseMonthDayToken(right.startToken); + const dayLeft = startLeft ? startLeft.day : 999; + const dayRight = startRight ? startRight.day : 999; + if (dayLeft !== dayRight) { + return dayLeft - dayRight; + } + + return String(left.label || left.name || "").localeCompare(String(right.label || right.name || "")); + }); + + map.set(key, filtered); + }); + + return map; + } + + function getBinaryPattern(value, expectedLength = 0) { + const raw = String(value || "").trim(); + if (!raw) { + return ""; + } + + if (/^[01]+$/.test(raw)) { + if (!expectedLength || raw.length === expectedLength) { + return raw; + } + return ""; + } + + if (/^[|:]+$/.test(raw)) { + const mapped = raw + .split("") + .map((char) => (char === "|" ? "1" : "0")) + .join(""); + if (!expectedLength || mapped.length === expectedLength) { + return mapped; + } + } + + return ""; + } + + function createLineStack(binaryPattern, variant = "trigram") { + const container = document.createElement("div"); + container.className = `iching-lines iching-lines-${variant}`; + + binaryPattern.split("").forEach((bit) => { + const line = document.createElement("div"); + line.className = bit === "1" ? "iching-line is-yang" : "iching-line is-yin"; + container.appendChild(line); + }); + + return container; + } + + function buildHexagramSearchText(entry) { + const upper = getTrigramByName(entry?.upperTrigram); + const lower = getTrigramByName(entry?.lowerTrigram); + const upperCards = upper ? state.tarotByTrigramName[normalizeSearchValue(upper.name)] || [] : []; + const lowerCards = lower ? state.tarotByTrigramName[normalizeSearchValue(lower.name)] || [] : []; + const tarotAliasText = typeof getTarotCardSearchAliases === "function" + ? [...upperCards, ...lowerCards] + .flatMap((cardName) => getTarotCardSearchAliases(cardName)) + .join(" ") + : [...upperCards, ...lowerCards].join(" "); + const parts = [ + entry?.number, + entry?.name, + entry?.chineseName, + entry?.pinyin, + entry?.judgement, + entry?.image, + entry?.upperTrigram, + entry?.lowerTrigram, + entry?.planetaryInfluence, + entry?.binary, + entry?.lineDiagram, + ...(Array.isArray(entry?.keywords) ? entry.keywords : []), + upper?.name, + upper?.chineseName, + upper?.pinyin, + upper?.element, + upper?.attribute, + upper?.binary, + upper?.description, + lower?.name, + lower?.chineseName, + lower?.pinyin, + lower?.element, + lower?.attribute, + lower?.binary, + lower?.description, + upperCards.join(" "), + lowerCards.join(" "), + tarotAliasText + ]; + + return normalizeSearchValue(parts.filter((part) => part !== null && part !== undefined).join(" ")); + } + + function updateSelection(elements) { + if (!elements?.ichingCardListEl) { + return; + } + + const buttons = elements.ichingCardListEl.querySelectorAll(".planet-list-item"); + buttons.forEach((button) => { + const isSelected = Number(button.dataset.hexagramNumber) === state.selectedNumber; + button.classList.toggle("is-selected", isSelected); + button.setAttribute("aria-selected", isSelected ? "true" : "false"); + }); + } + + function renderEmptyDetail(elements, detailName = "No hexagrams found") { + if (!elements) { + return; + } + + if (elements.ichingDetailNameEl) { + elements.ichingDetailNameEl.textContent = detailName; + } + if (elements.ichingDetailTypeEl) { + elements.ichingDetailTypeEl.textContent = "--"; + } + if (elements.ichingDetailSummaryEl) { + elements.ichingDetailSummaryEl.textContent = "--"; + } + if (elements.ichingDetailJudgementEl) { + elements.ichingDetailJudgementEl.textContent = "--"; + } + if (elements.ichingDetailImageEl) { + elements.ichingDetailImageEl.textContent = "--"; + } + if (elements.ichingDetailBinaryEl) { + elements.ichingDetailBinaryEl.textContent = "--"; + } + if (elements.ichingDetailLineEl) { + clearChildren(elements.ichingDetailLineEl); + elements.ichingDetailLineEl.textContent = "--"; + } + if (elements.ichingDetailPlanetEl) { + elements.ichingDetailPlanetEl.textContent = "--"; + } + if (elements.ichingDetailTarotEl) { + elements.ichingDetailTarotEl.textContent = "--"; + } + if (elements.ichingDetailCalendarEl) { + clearChildren(elements.ichingDetailCalendarEl); + elements.ichingDetailCalendarEl.textContent = "--"; + } + + clearChildren(elements.ichingDetailKeywordsEl); + clearChildren(elements.ichingDetailTrigramsEl); + } + + function renderKeywords(entry, elements) { + clearChildren(elements.ichingDetailKeywordsEl); + const keywords = Array.isArray(entry?.keywords) ? entry.keywords : []; + + if (!keywords.length) { + if (elements.ichingDetailKeywordsEl) { + elements.ichingDetailKeywordsEl.textContent = "--"; + } + return; + } + + keywords.forEach((keyword) => { + const chip = document.createElement("span"); + chip.className = "tarot-keyword-chip"; + chip.textContent = keyword; + elements.ichingDetailKeywordsEl?.appendChild(chip); + }); + } + + function createTrigramCard(label, trigram) { + const card = document.createElement("div"); + card.className = "iching-trigram-card"; + + if (!trigram) { + card.textContent = `${label}: --`; + return card; + } + + const title = document.createElement("div"); + title.className = "iching-trigram-title"; + const chinese = trigram.chineseName ? ` (${trigram.chineseName})` : ""; + title.textContent = `${label}: ${trigram.name || "--"}${chinese}`; + + const meta = document.createElement("div"); + meta.className = "iching-trigram-meta"; + const attribute = trigram.attribute || "--"; + const element = trigram.element || "--"; + meta.textContent = `${attribute} · ${element}`; + + const diagram = document.createElement("div"); + diagram.className = "iching-trigram-diagram"; + const binaryPattern = getBinaryPattern(trigram.binary || trigram.lineDiagram, 3); + + if (binaryPattern) { + const binaryLabel = document.createElement("div"); + binaryLabel.className = "iching-line-label"; + binaryLabel.textContent = binaryPattern; + diagram.append(binaryLabel, createLineStack(binaryPattern, "trigram")); + } else { + diagram.textContent = "--"; + } + + card.append(title, meta, diagram); + + if (trigram.description) { + const description = document.createElement("div"); + description.className = "planet-text"; + description.textContent = trigram.description; + card.appendChild(description); + } + + return card; + } + + function renderTrigrams(entry, elements) { + clearChildren(elements.ichingDetailTrigramsEl); + + const upper = getTrigramByName(entry?.upperTrigram); + const lower = getTrigramByName(entry?.lowerTrigram); + + elements.ichingDetailTrigramsEl?.append( + createTrigramCard("Upper", upper), + createTrigramCard("Lower", lower) + ); + } + + function renderTarotCorrespondences(entry, elements) { + if (!elements?.ichingDetailTarotEl) { + return; + } + + const upperKey = normalizeSearchValue(entry?.upperTrigram); + const lowerKey = normalizeSearchValue(entry?.lowerTrigram); + const upperCards = upperKey ? state.tarotByTrigramName[upperKey] || [] : []; + const lowerCards = lowerKey ? state.tarotByTrigramName[lowerKey] || [] : []; + + const upperTrigram = upperKey ? state.trigramsByName[upperKey] : null; + const lowerTrigram = lowerKey ? state.trigramsByName[lowerKey] : null; + const upperLabel = upperTrigram?.element || entry?.upperTrigram || "--"; + const lowerLabel = lowerTrigram?.element || entry?.lowerTrigram || "--"; + + const lines = []; + if (upperKey) { + lines.push(`Upper (${upperLabel}): ${upperCards.length ? upperCards.join(", ") : "--"}`); + } + if (lowerKey) { + lines.push(`Lower (${lowerLabel}): ${lowerCards.length ? lowerCards.join(", ") : "--"}`); + } + + elements.ichingDetailTarotEl.textContent = lines.length ? lines.join("\n\n") : "--"; + } + + function renderCalendarMonths(entry, elements) { + if (!elements?.ichingDetailCalendarEl) { + return; + } + + clearChildren(elements.ichingDetailCalendarEl); + const rows = state.monthRefsByHexagramNumber.get(entry?.number) || []; + + if (!rows.length) { + elements.ichingDetailCalendarEl.textContent = "--"; + return; + } + + rows.forEach((month) => { + const button = document.createElement("button"); + button.type = "button"; + button.className = "alpha-nav-btn"; + button.textContent = `${month.label || month.name} ↗`; + button.addEventListener("click", () => { + document.dispatchEvent(new CustomEvent("nav:calendar-month", { + detail: { monthId: month.id } + })); + }); + elements.ichingDetailCalendarEl.appendChild(button); + }); + } + + function renderDetail(entry, elements) { + if (!entry || !elements) { + return; + } + + const number = Number.isFinite(entry.number) ? entry.number : "--"; + if (elements.ichingDetailNameEl) { + elements.ichingDetailNameEl.textContent = `${number} · ${entry.name || "--"}`; + } + + if (elements.ichingDetailTypeEl) { + const chinese = entry.chineseName || "--"; + const pinyin = entry.pinyin || "--"; + const upper = entry.upperTrigram || "--"; + const lower = entry.lowerTrigram || "--"; + elements.ichingDetailTypeEl.textContent = `Hexagram · ${chinese} · ${pinyin} · ${upper}/${lower}`; + } + + if (elements.ichingDetailSummaryEl) { + elements.ichingDetailSummaryEl.textContent = entry.judgement || "--"; + } + + if (elements.ichingDetailJudgementEl) { + elements.ichingDetailJudgementEl.textContent = entry.judgement || "--"; + } + + if (elements.ichingDetailImageEl) { + elements.ichingDetailImageEl.textContent = entry.image || "--"; + } + + if (elements.ichingDetailBinaryEl) { + const binary = entry.binary || "--"; + elements.ichingDetailBinaryEl.textContent = `Binary: ${binary}`; + } + + if (elements.ichingDetailLineEl) { + clearChildren(elements.ichingDetailLineEl); + const linePattern = getBinaryPattern(entry.binary, 6) || getBinaryPattern(entry.lineDiagram, 6); + if (linePattern) { + elements.ichingDetailLineEl.appendChild(createLineStack(linePattern, "hexagram")); + } else { + elements.ichingDetailLineEl.textContent = "--"; + } + } + + if (elements.ichingDetailPlanetEl) { + elements.ichingDetailPlanetEl.textContent = entry.planetaryInfluence || "--"; + } + + renderKeywords(entry, elements); + renderTrigrams(entry, elements); + renderTarotCorrespondences(entry, elements); + renderCalendarMonths(entry, elements); + } + + function selectByNumber(number, elements) { + const numeric = Number(number); + if (!Number.isFinite(numeric)) { + return; + } + + const entry = state.hexagrams.find((hexagram) => hexagram.number === numeric); + if (!entry) { + return; + } + + state.selectedNumber = entry.number; + updateSelection(elements); + renderDetail(entry, elements); + } + + function renderList(elements) { + if (!elements?.ichingCardListEl) { + return; + } + + clearChildren(elements.ichingCardListEl); + + state.filteredHexagrams.forEach((entry) => { + const button = document.createElement("button"); + button.type = "button"; + button.className = "planet-list-item"; + button.dataset.hexagramNumber = String(entry.number); + button.setAttribute("role", "option"); + + const nameEl = document.createElement("span"); + nameEl.className = "planet-list-name"; + const number = Number.isFinite(entry.number) ? `#${entry.number} ` : ""; + nameEl.textContent = `${number}${entry.name || "--"}`; + + const metaEl = document.createElement("span"); + metaEl.className = "planet-list-meta"; + const upper = entry.upperTrigram || "--"; + const lower = entry.lowerTrigram || "--"; + metaEl.textContent = `${upper}/${lower} · ${entry.planetaryInfluence || "--"}`; + + button.append(nameEl, metaEl); + elements.ichingCardListEl.appendChild(button); + }); + + if (elements.ichingCountEl) { + elements.ichingCountEl.textContent = state.searchQuery + ? `${state.filteredHexagrams.length} of ${state.hexagrams.length} hexagrams` + : `${state.hexagrams.length} hexagrams`; + } + } + + function applySearchFilter(elements) { + const query = normalizeSearchValue(state.searchQuery); + state.filteredHexagrams = query + ? state.hexagrams.filter((entry) => buildHexagramSearchText(entry).includes(query)) + : [...state.hexagrams]; + + if (elements?.ichingSearchClearEl) { + elements.ichingSearchClearEl.disabled = !query; + } + + renderList(elements); + + if (!state.filteredHexagrams.some((entry) => entry.number === state.selectedNumber)) { + if (state.filteredHexagrams.length > 0) { + selectByNumber(state.filteredHexagrams[0].number, elements); + } else { + state.selectedNumber = null; + updateSelection(elements); + renderEmptyDetail(elements); + } + return; + } + + updateSelection(elements); + } + + function ensureIChingSection(referenceData) { + const elements = getElements(); + + if (state.initialized) { + state.monthRefsByHexagramNumber = buildMonthReferencesByHexagram(referenceData, state.hexagrams); + const selected = state.hexagrams.find((hexagram) => hexagram.number === state.selectedNumber); + if (selected) { + renderDetail(selected, elements); + } + return; + } + + if (!elements.ichingCardListEl || !elements.ichingDetailNameEl) { + return; + } + + const iChing = referenceData?.iChing; + const trigrams = Array.isArray(iChing?.trigrams) ? iChing.trigrams : []; + const hexagrams = Array.isArray(iChing?.hexagrams) ? iChing.hexagrams : []; + const correspondences = iChing?.correspondences; + + if (!hexagrams.length) { + renderEmptyDetail(elements, "I Ching data unavailable"); + return; + } + + state.trigramsByName = trigrams.reduce((acc, trigram) => { + const key = normalizeSearchValue(trigram?.name); + if (key) { + acc[key] = trigram; + } + return acc; + }, {}); + + const tarotToTrigram = Array.isArray(correspondences?.tarotToTrigram) + ? correspondences.tarotToTrigram + : []; + + state.tarotByTrigramName = tarotToTrigram.reduce((acc, row) => { + const key = normalizeSearchValue(row?.trigram); + const tarotCard = String(row?.tarot || "").trim(); + if (!key || !tarotCard) { + return acc; + } + if (!Array.isArray(acc[key])) { + acc[key] = []; + } + if (!acc[key].includes(tarotCard)) { + acc[key].push(tarotCard); + } + return acc; + }, {}); + + state.hexagrams = [...hexagrams] + .map((entry) => ({ + ...entry, + number: Number(entry?.number) + })) + .filter((entry) => Number.isFinite(entry.number)) + .sort((a, b) => a.number - b.number); + + state.monthRefsByHexagramNumber = buildMonthReferencesByHexagram(referenceData, state.hexagrams); + + state.filteredHexagrams = [...state.hexagrams]; + renderList(elements); + + if (state.hexagrams.length > 0) { + selectByNumber(state.hexagrams[0].number, elements); + } + + elements.ichingCardListEl.addEventListener("click", (event) => { + const target = event.target; + if (!(target instanceof Node)) { + return; + } + + const button = target instanceof Element + ? target.closest(".planet-list-item") + : null; + + if (!(button instanceof HTMLButtonElement)) { + return; + } + + const selectedNumber = button.dataset.hexagramNumber; + if (!selectedNumber) { + return; + } + + selectByNumber(selectedNumber, elements); + }); + + if (elements.ichingSearchInputEl) { + elements.ichingSearchInputEl.addEventListener("input", () => { + state.searchQuery = elements.ichingSearchInputEl.value || ""; + applySearchFilter(elements); + }); + } + + if (elements.ichingSearchClearEl && elements.ichingSearchInputEl) { + elements.ichingSearchClearEl.addEventListener("click", () => { + elements.ichingSearchInputEl.value = ""; + state.searchQuery = ""; + applySearchFilter(elements); + elements.ichingSearchInputEl.focus(); + }); + } + + state.initialized = true; + } + + function selectByHexagramNumber(number) { + if (!state.initialized) { + return false; + } + + const elements = getElements(); + const numeric = Number(number); + if (!Number.isFinite(numeric)) { + return false; + } + + const entry = state.hexagrams.find((hexagram) => hexagram.number === numeric); + if (!entry) { + return false; + } + + selectByNumber(entry.number, elements); + elements.ichingCardListEl + ?.querySelector(`[data-hexagram-number="${entry.number}"]`) + ?.scrollIntoView({ block: "nearest" }); + return true; + } + + function selectByPlanetaryInfluence(planetaryInfluence) { + if (!state.initialized) { + return false; + } + + const targetInfluence = normalizePlanetInfluence(planetaryInfluence); + if (!targetInfluence) { + return false; + } + + const entry = state.hexagrams.find((hexagram) => + normalizePlanetInfluence(hexagram?.planetaryInfluence) === targetInfluence + ); + + if (!entry) { + return false; + } + + return selectByHexagramNumber(entry.number); + } + + window.IChingSectionUi = { + ensureIChingSection, + selectByHexagramNumber, + selectByPlanetaryInfluence + }; +})(); diff --git a/app/ui-kabbalah.js b/app/ui-kabbalah.js new file mode 100644 index 0000000..6ade11d --- /dev/null +++ b/app/ui-kabbalah.js @@ -0,0 +1,1153 @@ +(function () { + "use strict"; + + // ─── SVG tree layout constants ────────────────────────────────────────────── + const NS = "http://www.w3.org/2000/svg"; + const R = 11; // sephira circle radius + + // Standard Hermetic GD Tree of Life positions in a 240×470 viewBox + const NODE_POS = { + 1: [120, 30], // Kether — crown of middle pillar + 2: [200, 88], // Chokmah — right pillar + 3: [40, 88], // Binah — left pillar + 4: [200, 213], // Chesed — right pillar + 5: [40, 213], // Geburah — left pillar + 6: [120, 273], // Tiphareth — middle pillar + 7: [200, 343], // Netzach — right pillar + 8: [40, 343], // Hod — left pillar + 9: [120, 398], // Yesod — middle pillar + 10: [120, 448], // Malkuth — bottom of middle pillar + }; + + // King-scale fill colours + const SEPH_FILL = { + 1: "#e8e8e8", // Kether — white brilliance + 2: "#87ceeb", // Chokmah — soft sky blue + 3: "#7d2b5a", // Binah — dark crimson + 4: "#1a56e0", // Chesed — deep blue + 5: "#d44014", // Geburah — scarlet + 6: "#d4a017", // Tiphareth — gold + 7: "#22aa60", // Netzach — emerald + 8: "#cc5500", // Hod — orange + 9: "#6030c0", // Yesod — violet + 10: "#c8b000", // Malkuth — citrine / amber + }; + + // Nodes with light-ish fills get dark number text; others get white + const DARK_TEXT = new Set([1, 2, 6, 10]); + + // Da'at – phantom sephira drawn as a dashed circle, not clickable + const DAAT = [120, 148]; + const PATH_MARKER_SCALE = 1.33; + const PATH_LABEL_RADIUS = 9 * PATH_MARKER_SCALE; + const PATH_LABEL_FONT_SIZE = 8.8 * PATH_MARKER_SCALE; + const PATH_TAROT_WIDTH = 16 * PATH_MARKER_SCALE; + const PATH_TAROT_HEIGHT = 24 * PATH_MARKER_SCALE; + const PATH_LABEL_OFFSET_WITH_TAROT = 11 * PATH_MARKER_SCALE; + const PATH_TAROT_OFFSET_WITH_LABEL = 1 * PATH_MARKER_SCALE; + const PATH_TAROT_OFFSET_NO_LABEL = 12 * PATH_MARKER_SCALE; + + // ─── state ────────────────────────────────────────────────────────────────── + const state = { + initialized: false, + tree: null, + godsData: {}, + hebrewLetterIdByToken: {}, + fourWorldLayers: [], + showPathLetters: true, + showPathNumbers: true, + showPathTarotCards: false, + selectedSephiraNumber: null, + selectedPathNumber: null + }; + + const PLANET_NAME_TO_ID = { + saturn: "saturn", + jupiter: "jupiter", + mars: "mars", + sol: "sol", + sun: "sol", + venus: "venus", + mercury: "mercury", + luna: "luna", + moon: "luna" + }; + + const ZODIAC_NAME_TO_ID = { + aries: "aries", + taurus: "taurus", + gemini: "gemini", + cancer: "cancer", + leo: "leo", + virgo: "virgo", + libra: "libra", + scorpio: "scorpio", + sagittarius: "sagittarius", + capricorn: "capricorn", + aquarius: "aquarius", + pisces: "pisces" + }; + + const PLANET_ID_TO_LABEL = { + saturn: "Saturn", + jupiter: "Jupiter", + mars: "Mars", + sol: "Sol", + venus: "Venus", + mercury: "Mercury", + luna: "Luna" + }; + + const MINOR_RANK_BY_PLURAL = { + aces: "Ace", + twos: "Two", + threes: "Three", + fours: "Four", + fives: "Five", + sixes: "Six", + sevens: "Seven", + eights: "Eight", + nines: "Nine", + tens: "Ten" + }; + + const MINOR_SUITS = ["Wands", "Cups", "Swords", "Disks"]; + + const HEBREW_LETTER_ALIASES = { + aleph: "alef", + alef: "alef", + beth: "bet", + bet: "bet", + gimel: "gimel", + daleth: "dalet", + dalet: "dalet", + he: "he", + vav: "vav", + zayin: "zayin", + cheth: "het", + chet: "het", + het: "het", + teth: "tet", + tet: "tet", + yod: "yod", + kaph: "kaf", + kaf: "kaf", + lamed: "lamed", + mem: "mem", + nun: "nun", + samekh: "samekh", + ayin: "ayin", + pe: "pe", + tzaddi: "tsadi", + tzadi: "tsadi", + tsadi: "tsadi", + qoph: "qof", + qof: "qof", + resh: "resh", + shin: "shin", + tav: "tav" + }; + + const DEFAULT_FOUR_QABALISTIC_WORLD_LAYERS = [ + { + slot: "Yod", + letterChar: "י", + hebrewToken: "yod", + world: "Atziluth", + worldLayer: "Archetypal World (God’s Will)", + worldDescription: "World of gods or specific facets or divine qualities.", + soulLayer: "Chiah", + soulTitle: "Life Force", + soulDescription: "The Chiah is the Life Force itself and our true identity as reflection of Supreme Consciousness." + }, + { + slot: "Heh", + letterChar: "ה", + hebrewToken: "he", + world: "Briah", + worldLayer: "Creative World (God’s Love)", + worldDescription: "World of archangels, executors of divine qualities.", + soulLayer: "Neshamah", + soulTitle: "Soul-Intuition", + soulDescription: "The Neshamah is the part of our soul that transcends the thinking process." + }, + { + slot: "Vav", + letterChar: "ו", + hebrewToken: "vav", + world: "Yetzirah", + worldLayer: "Formative World (God’s Mind)", + worldDescription: "World of angels who work under archangelic direction.", + soulLayer: "Ruach", + soulTitle: "Intellect", + soulDescription: "The Ruach is the thinking mind that often dominates attention and identity." + }, + { + slot: "Heh (final)", + letterChar: "ה", + hebrewToken: "he", + world: "Assiah", + worldLayer: "Material World (God’s Creation)", + worldDescription: "World of spirits that infuse matter and energy through specialized duties.", + soulLayer: "Nephesh", + soulTitle: "Animal Soul", + soulDescription: "The Nephesh is instinctive consciousness expressed through appetite, emotion, sex drive, and survival." + } + ]; + + function titleCase(value) { + return String(value || "") + .split(/[\s-_]+/) + .filter(Boolean) + .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) + .join(" "); + } + + function normalizeSoulId(value) { + return String(value || "") + .trim() + .toLowerCase() + .replace(/[^a-z]/g, ""); + } + + function buildFourWorldLayersFromDataset(magickDataset) { + const worlds = magickDataset?.grouped?.kabbalah?.fourWorlds; + const souls = magickDataset?.grouped?.kabbalah?.souls; + if (!worlds || typeof worlds !== "object") { + return [...DEFAULT_FOUR_QABALISTIC_WORLD_LAYERS]; + } + + const worldOrder = ["atzilut", "briah", "yetzirah", "assiah"]; + const soulAliases = { + chiah: "chaya", + chaya: "chaya", + neshamah: "neshama", + neshama: "neshama", + ruach: "ruach", + nephesh: "nephesh" + }; + + return worldOrder.map((worldId, index) => { + const fallback = DEFAULT_FOUR_QABALISTIC_WORLD_LAYERS[index] || {}; + const worldEntry = worlds?.[worldId] || null; + if (!worldEntry || typeof worldEntry !== "object") { + return fallback; + } + + const tetragrammaton = worldEntry?.tetragrammaton && typeof worldEntry.tetragrammaton === "object" + ? worldEntry.tetragrammaton + : {}; + + const rawSoulId = normalizeSoulId(worldEntry?.soulId); + const soulId = soulAliases[rawSoulId] || rawSoulId; + const soulEntry = souls?.[soulId] && typeof souls[soulId] === "object" + ? souls[soulId] + : null; + + const soulLayer = soulEntry?.name?.roman || fallback.soulLayer || titleCase(rawSoulId || soulId); + const soulTitle = soulEntry?.title?.en || fallback.soulTitle || titleCase(soulEntry?.name?.en || ""); + const soulDescription = soulEntry?.desc?.en || fallback.soulDescription || ""; + + return { + slot: tetragrammaton?.isFinal + ? `${String(tetragrammaton?.slot || fallback.slot || "Heh")} (final)` + : String(tetragrammaton?.slot || fallback.slot || ""), + letterChar: String(tetragrammaton?.letterChar || fallback.letterChar || ""), + hebrewToken: String(tetragrammaton?.hebrewLetterId || fallback.hebrewToken || "").toLowerCase(), + world: String(worldEntry?.name?.roman || fallback.world || titleCase(worldEntry?.id || worldId)), + worldLayer: String(worldEntry?.worldLayer?.en || fallback.worldLayer || worldEntry?.desc?.en || ""), + worldDescription: String(worldEntry?.worldDescription?.en || fallback.worldDescription || ""), + soulLayer: String(soulLayer || ""), + soulTitle: String(soulTitle || ""), + soulDescription: String(soulDescription || "") + }; + }).filter(Boolean); + } + + // ─── element references ───────────────────────────────────────────────────── + function getElements() { + return { + treeContainerEl: document.getElementById("kab-tree-container"), + detailNameEl: document.getElementById("kab-detail-name"), + detailSubEl: document.getElementById("kab-detail-sub"), + detailBodyEl: document.getElementById("kab-detail-body"), + pathLetterToggleEl: document.getElementById("kab-path-letter-toggle"), + pathNumberToggleEl: document.getElementById("kab-path-number-toggle"), + pathTarotToggleEl: document.getElementById("kab-path-tarot-toggle"), + }; + } + + function resolvePathTarotImage(path) { + const cardName = String(path?.tarot?.card || "").trim(); + if (!cardName || typeof window.TarotCardImages?.resolveTarotCardImage !== "function") { + return null; + } + + return window.TarotCardImages.resolveTarotCardImage(cardName); + } + + function getPathLabel(path) { + const glyph = String(path?.hebrewLetter?.char || "").trim(); + const pathNumber = Number(path?.pathNumber); + const parts = []; + + if (state.showPathLetters && glyph) { + parts.push(glyph); + } + + if (state.showPathNumbers && Number.isFinite(pathNumber)) { + parts.push(String(pathNumber)); + } + + return parts.join(" "); + } + + // ─── SVG element factory ──────────────────────────────────────────────────── + function svgEl(tag, attrs, text) { + const el = document.createElementNS(NS, tag); + for (const [k, v] of Object.entries(attrs || {})) { + el.setAttribute(k, String(v)); + } + if (text != null) el.textContent = text; + return el; + } + + // ─── build the full SVG tree ───────────────────────────────────────────────── + function buildTreeSVG(tree) { + const svg = svgEl("svg", { + viewBox: "0 0 240 470", + width: "100%", + role: "img", + "aria-label": "Kabbalah Tree of Life diagram", + class: "kab-svg", + }); + + // Subtle pillar background tracks + svg.appendChild(svgEl("rect", { + x: 113, y: 30, width: 14, height: 420, + rx: 7, fill: "#ffffff07", "pointer-events": "none", + })); + svg.appendChild(svgEl("rect", { + x: 33, y: 88, width: 14, height: 255, + rx: 7, fill: "#ff220010", "pointer-events": "none", + })); + svg.appendChild(svgEl("rect", { + x: 193, y: 88, width: 14, height: 255, + rx: 7, fill: "#2244ff10", "pointer-events": "none", + })); + + // Pillar labels + [ + { x: 198, y: 73, text: "Mercy", anchor: "middle" }, + { x: 120, y: 17, text: "Balance", anchor: "middle" }, + { x: 42, y: 73, text: "Severity", anchor: "middle" }, + ].forEach(({ x, y, text, anchor }) => { + svg.appendChild(svgEl("text", { + x, y, "text-anchor": anchor, "dominant-baseline": "auto", + fill: "#42425a", "font-size": "6", "pointer-events": "none", + }, text)); + }); + + // ── path lines (drawn before sephiroth so nodes sit on top) ────────────── + tree.paths.forEach(path => { + const [x1, y1] = NODE_POS[path.connects.from]; + const [x2, y2] = NODE_POS[path.connects.to]; + const mx = (x1 + x2) / 2; + const my = (y1 + y2) / 2; + const tarotImage = state.showPathTarotCards ? resolvePathTarotImage(path) : null; + const hasTarotImage = Boolean(tarotImage); + const pathLabel = getPathLabel(path); + const hasLabel = Boolean(pathLabel); + const labelY = hasTarotImage && hasLabel ? my - PATH_LABEL_OFFSET_WITH_TAROT : my; + + // Visual line (thin) + svg.appendChild(svgEl("line", { + x1, y1, x2, y2, + class: "kab-path-line", + "data-path": path.pathNumber, + stroke: "#3c3c5c", + "stroke-width": "1.5", + "pointer-events": "none", + })); + + // Invisible wide hit area for easy clicking + svg.appendChild(svgEl("line", { + x1, y1, x2, y2, + class: "kab-path-hit", + "data-path": path.pathNumber, + stroke: "transparent", + "stroke-width": String(12 * PATH_MARKER_SCALE), + role: "button", + tabindex: "0", + "aria-label": `Path ${path.pathNumber}: ${path.hebrewLetter?.transliteration || ""} — ${path.tarot?.card || ""}`, + style: "cursor:pointer", + })); + + if (hasLabel) { + // Background disc for legibility behind path label + svg.appendChild(svgEl("circle", { + cx: mx, cy: labelY, r: PATH_LABEL_RADIUS.toFixed(2), + fill: "#0d0d1c", opacity: "0.82", + "pointer-events": "none", + })); + + // Path label at path midpoint + svg.appendChild(svgEl("text", { + x: mx, y: labelY + 1, + "text-anchor": "middle", + "dominant-baseline": "middle", + class: "kab-path-lbl", + "data-path": path.pathNumber, + fill: "#a8a8e0", + "font-size": PATH_LABEL_FONT_SIZE.toFixed(2), + "pointer-events": "none", + }, pathLabel)); + } + + if (hasTarotImage) { + const tarotY = hasLabel + ? my + PATH_TAROT_OFFSET_WITH_LABEL + : my - PATH_TAROT_OFFSET_NO_LABEL; + svg.appendChild(svgEl("image", { + href: tarotImage, + x: (mx - (PATH_TAROT_WIDTH / 2)).toFixed(2), + y: tarotY.toFixed(2), + width: PATH_TAROT_WIDTH.toFixed(2), + height: PATH_TAROT_HEIGHT.toFixed(2), + preserveAspectRatio: "xMidYMid meet", + class: "kab-path-tarot", + "data-path": path.pathNumber, + role: "button", + tabindex: "0", + "aria-label": `Path ${path.pathNumber} Tarot card ${path.tarot?.card || ""}`, + style: "cursor:pointer" + })); + } + }); + + // ── Da'at — phantom sephira (dashed, informational only) ──────────────── + svg.appendChild(svgEl("circle", { + cx: DAAT[0], cy: DAAT[1], r: "9", + fill: "none", stroke: "#3c3c5c", + "stroke-dasharray": "3 2", "stroke-width": "1", + "pointer-events": "none", + })); + svg.appendChild(svgEl("text", { + x: DAAT[0] + 13, y: DAAT[1] + 1, + "text-anchor": "start", "dominant-baseline": "middle", + fill: "#3c3c5c", "font-size": "6.5", "pointer-events": "none", + }, "Da'at")); + + // ── sephiroth circles (drawn last, on top of paths) ────────────────────── + tree.sephiroth.forEach(seph => { + const [cx, cy] = NODE_POS[seph.number]; + const fill = SEPH_FILL[seph.number] || "#555"; + const isLeft = cx < 80; + const isMid = cx === 120; + + // Glow halo (subtle, pointer-events:none) + svg.appendChild(svgEl("circle", { + cx, cy, r: "16", + fill, opacity: "0.12", + class: "kab-node-glow", + "data-sephira": seph.number, + "pointer-events": "none", + })); + + // Main clickable circle + svg.appendChild(svgEl("circle", { + cx, cy, r: R, + fill, stroke: "#00000040", "stroke-width": "1", + class: "kab-node", + "data-sephira": seph.number, + role: "button", + tabindex: "0", + "aria-label": `Sephira ${seph.number}: ${seph.name}`, + style: "cursor:pointer", + })); + + // Sephira number inside the circle + svg.appendChild(svgEl("text", { + x: cx, y: cy + 0.5, + "text-anchor": "middle", "dominant-baseline": "middle", + fill: DARK_TEXT.has(seph.number) ? "#111" : "#fff", + "font-size": "8", "font-weight": "bold", + "pointer-events": "none", + }, String(seph.number))); + + // Name label beside the circle + const lx = isLeft ? cx - R - 4 : cx + R + 4; + svg.appendChild(svgEl("text", { + x: isMid ? cx : lx, + y: isMid ? cy + R + 8 : cy, + "text-anchor": isMid ? "middle" : (isLeft ? "end" : "start"), + "dominant-baseline": isMid ? "auto" : "middle", + fill: "#c0c0d4", + "font-size": "7.5", "pointer-events": "none", + class: "kab-node-lbl", + }, seph.name)); + }); + + return svg; + } + + // ─── detail panel helpers ─────────────────────────────────────────────────── + function metaCard(label, value, wide) { + const card = document.createElement("div"); + card.className = wide ? "planet-meta-card kab-wide-card" : "planet-meta-card"; + card.innerHTML = `${label}

${value || "—"}

`; + return card; + } + + function normalizeText(value) { + return String(value || "").trim().toLowerCase(); + } + + function normalizeLetterToken(value) { + return String(value || "") + .trim() + .toLowerCase() + .replace(/[^a-z]/g, ""); + } + + function buildHebrewLetterLookup(magickDataset) { + const letters = magickDataset?.grouped?.hebrewLetters; + const lookup = {}; + + if (!letters || typeof letters !== "object") { + return lookup; + } + + Object.entries(letters).forEach(([letterId, entry]) => { + const entryId = String(entry?.id || letterId || ""); + + const idToken = normalizeLetterToken(letterId); + const canonicalIdToken = HEBREW_LETTER_ALIASES[idToken] || idToken; + if (canonicalIdToken && !lookup[canonicalIdToken]) { + lookup[canonicalIdToken] = entryId; + } + + const nameToken = normalizeLetterToken(entry?.letter?.name); + const canonicalNameToken = HEBREW_LETTER_ALIASES[nameToken] || nameToken; + if (canonicalNameToken && !lookup[canonicalNameToken]) { + lookup[canonicalNameToken] = entryId; + } + }); + + return lookup; + } + + function resolveHebrewLetterId(value) { + const token = normalizeLetterToken(value); + if (!token) return null; + + const canonical = HEBREW_LETTER_ALIASES[token] || token; + return state.hebrewLetterIdByToken[canonical] || state.hebrewLetterIdByToken[token] || null; + } + + function resolvePlanetId(value) { + const text = normalizeText(value); + if (!text) return null; + + for (const [key, planetId] of Object.entries(PLANET_NAME_TO_ID)) { + if (text === key || text.includes(key)) { + return planetId; + } + } + + return null; + } + + function resolveZodiacId(value) { + const text = normalizeText(value); + if (!text) return null; + for (const [name, zodiacId] of Object.entries(ZODIAC_NAME_TO_ID)) { + if (text === name || text.includes(name)) { + return zodiacId; + } + } + return null; + } + + function createNavButton(label, eventName, detail) { + const btn = document.createElement("button"); + btn.type = "button"; + btn.className = "kab-god-link"; + btn.textContent = `${label} ↗`; + btn.addEventListener("click", () => { + document.dispatchEvent(new CustomEvent(eventName, { detail })); + }); + return btn; + } + + function appendLinkRow(card, buttons) { + if (!buttons?.length) return; + const row = document.createElement("div"); + row.className = "kab-god-links"; + buttons.forEach((button) => row.appendChild(button)); + card.appendChild(row); + } + + function buildPlanetLuminaryCard(planetValue) { + const card = metaCard("Planet / Luminary", planetValue); + const planetId = resolvePlanetId(planetValue); + if (planetId) { + appendLinkRow(card, [ + createNavButton(`View ${PLANET_ID_TO_LABEL[planetId] || planetValue} in Planets`, "nav:planet", { planetId }) + ]); + return card; + } + + const zodiacId = resolveZodiacId(planetValue); + if (zodiacId) { + appendLinkRow(card, [ + createNavButton(`View ${zodiacId.charAt(0).toUpperCase() + zodiacId.slice(1)} in Zodiac`, "nav:zodiac", { signId: zodiacId }) + ]); + } + return card; + } + + function extractMinorRank(attribution) { + const match = String(attribution || "").match(/\bthe\s+4\s+(aces|twos|threes|fours|fives|sixes|sevens|eights|nines|tens)\b/i); + if (!match) return null; + return MINOR_RANK_BY_PLURAL[(match[1] || "").toLowerCase()] || null; + } + + function buildMinorTarotNames(attribution) { + const rank = extractMinorRank(attribution); + if (!rank) return []; + return MINOR_SUITS.map((suit) => `${rank} of ${suit}`); + } + + function buildTarotAttributionCard(attribution) { + const card = metaCard("Tarot Attribution", attribution); + const minorCards = buildMinorTarotNames(attribution); + if (minorCards.length) { + appendLinkRow(card, minorCards.map((cardName) => + createNavButton(cardName, "nav:tarot-trump", { cardName }) + )); + } + return card; + } + + function buildAstrologyCard(astrology) { + const astroText = astrology ? `${astrology.name} (${astrology.type})` : "—"; + const card = metaCard("Astrology", astroText); + if (astrology?.type === "planet") { + const planetId = resolvePlanetId(astrology.name); + if (planetId) { + appendLinkRow(card, [ + createNavButton(`View ${PLANET_ID_TO_LABEL[planetId] || astrology.name} in Planets`, "nav:planet", { planetId }) + ]); + } + } else if (astrology?.type === "zodiac") { + const signId = resolveZodiacId(astrology.name); + if (signId) { + appendLinkRow(card, [ + createNavButton(`View ${signId.charAt(0).toUpperCase() + signId.slice(1)} in Zodiac`, "nav:zodiac", { signId }) + ]); + } + } + return card; + } + + function buildConnectsCard(path, fromName, toName) { + const card = metaCard("Connects", `${fromName} → ${toName}`); + appendLinkRow(card, [ + createNavButton(`View ${fromName}`, "nav:kabbalah-path", { pathNo: Number(path.connects.from) }), + createNavButton(`View ${toName}`, "nav:kabbalah-path", { pathNo: Number(path.connects.to) }) + ]); + return card; + } + + function buildHebrewLetterCard(letter) { + const label = `${letter.char || ""} ${letter.transliteration || ""} — "${letter.meaning || ""}" (${letter.letterType || ""})`; + const card = metaCard("Hebrew Letter", label); + const hebrewLetterId = resolveHebrewLetterId(letter.transliteration || letter.char || ""); + + if (hebrewLetterId) { + appendLinkRow(card, [ + createNavButton(`View ${letter.transliteration || letter.char || "Letter"} in Alphabet`, "nav:alphabet", { + alphabet: "hebrew", + hebrewLetterId + }) + ]); + } + + return card; + } + + function findPathByHebrewToken(tree, hebrewToken) { + const canonicalToken = HEBREW_LETTER_ALIASES[normalizeLetterToken(hebrewToken)] || normalizeLetterToken(hebrewToken); + if (!canonicalToken) { + return null; + } + + const paths = Array.isArray(tree?.paths) ? tree.paths : []; + return paths.find((path) => { + const letterToken = normalizeLetterToken(path?.hebrewLetter?.transliteration || path?.hebrewLetter?.char); + const canonicalLetterToken = HEBREW_LETTER_ALIASES[letterToken] || letterToken; + return canonicalLetterToken === canonicalToken; + }) || null; + } + + function buildFourWorldsCard(tree, activeLetterToken = "") { + const activeToken = HEBREW_LETTER_ALIASES[normalizeLetterToken(activeLetterToken)] || normalizeLetterToken(activeLetterToken); + const worldLayers = Array.isArray(state.fourWorldLayers) && state.fourWorldLayers.length + ? state.fourWorldLayers + : DEFAULT_FOUR_QABALISTIC_WORLD_LAYERS; + + const card = document.createElement("div"); + card.className = "planet-meta-card kab-wide-card"; + + const title = document.createElement("strong"); + title.textContent = "Four Qabalistic Worlds & Soul Layers"; + card.appendChild(title); + + const stack = document.createElement("div"); + stack.className = "cal-item-stack"; + + worldLayers.forEach((layer) => { + const row = document.createElement("div"); + row.className = "cal-item-row"; + + const isActive = Boolean(activeToken) && activeToken === layer.hebrewToken; + + const head = document.createElement("div"); + head.className = "cal-item-head"; + head.innerHTML = ` + ${layer.slot}: ${layer.letterChar} — ${layer.world} + ${layer.soulLayer} + `; + row.appendChild(head); + + const worldLine = document.createElement("div"); + worldLine.className = "planet-text"; + worldLine.textContent = `${layer.worldLayer} · ${layer.worldDescription}`; + row.appendChild(worldLine); + + const soulLine = document.createElement("div"); + soulLine.className = "planet-text"; + soulLine.textContent = `${layer.soulLayer} — ${layer.soulTitle}: ${layer.soulDescription}`; + row.appendChild(soulLine); + + const buttonRow = []; + const hebrewLetterId = resolveHebrewLetterId(layer.hebrewToken); + if (hebrewLetterId) { + buttonRow.push( + createNavButton(`View ${layer.letterChar} in Alphabet`, "nav:alphabet", { + alphabet: "hebrew", + hebrewLetterId + }) + ); + } + + const linkedPath = findPathByHebrewToken(tree, layer.hebrewToken); + if (linkedPath?.pathNumber != null) { + buttonRow.push( + createNavButton(`View Path ${linkedPath.pathNumber}`, "nav:kabbalah-path", { pathNo: Number(linkedPath.pathNumber) }) + ); + } + + appendLinkRow(row, buttonRow); + + if (isActive) { + row.style.borderColor = "#818cf8"; + } + + stack.appendChild(row); + }); + + card.appendChild(stack); + return card; + } + + function splitCorrespondenceNames(value) { + return String(value || "") + .split(/,|;|·|\/|\bor\b|\band\b|\+/i) + .map((item) => item.trim()) + .filter(Boolean); + } + + function uniqueNames(values) { + const seen = new Set(); + const output = []; + values.forEach((name) => { + const key = String(name || "").toLowerCase(); + if (seen.has(key)) return; + seen.add(key); + output.push(name); + }); + return output; + } + + function godLinksCard(label, names, pathNo, metaText) { + const card = document.createElement("div"); + card.className = "planet-meta-card"; + + const title = document.createElement("strong"); + title.textContent = label; + card.appendChild(title); + + if (metaText) { + const meta = document.createElement("p"); + meta.className = "planet-text kab-god-meta"; + meta.textContent = metaText; + card.appendChild(meta); + } + + const row = document.createElement("div"); + row.className = "kab-god-links"; + + names.forEach((name) => { + const btn = document.createElement("button"); + btn.type = "button"; + btn.className = "kab-god-link"; + btn.textContent = name; + btn.addEventListener("click", () => { + document.dispatchEvent(new CustomEvent("nav:gods", { + detail: { godName: name, pathNo: Number(pathNo) } + })); + }); + row.appendChild(btn); + }); + + card.appendChild(row); + return card; + } + + function clearHighlights() { + document.querySelectorAll(".kab-node, .kab-node-glow") + .forEach(el => el.classList.remove("kab-node-active")); + document.querySelectorAll(".kab-path-hit, .kab-path-line, .kab-path-lbl, .kab-path-tarot") + .forEach(el => el.classList.remove("kab-path-active")); + } + + // ─── helper: append divine correspondences from gods.json ───────────────────── + function appendGodsCards(pathNo, elements) { + const gd = state.godsData[String(pathNo)]; + if (!gd) return; + + const hasAny = gd.greek || gd.roman || gd.egyptian || gd.egyptianPractical + || gd.elohim || gd.archangel || gd.angelicOrder; + if (!hasAny) return; + + const sep = document.createElement("div"); + sep.className = "planet-meta-card kab-wide-card"; + sep.innerHTML = `Divine Correspondences`; + elements.detailBodyEl.appendChild(sep); + + const greekNames = uniqueNames(splitCorrespondenceNames(gd.greek)); + const romanNames = uniqueNames(splitCorrespondenceNames(gd.roman)); + const egyptNames = uniqueNames([ + ...splitCorrespondenceNames(gd.egyptianPractical), + ...splitCorrespondenceNames(gd.egyptian) + ]); + + if (greekNames.length) { + elements.detailBodyEl.appendChild(godLinksCard("Greek", greekNames, pathNo)); + } + if (romanNames.length) { + elements.detailBodyEl.appendChild(godLinksCard("Roman", romanNames, pathNo)); + } + if (egyptNames.length) { + elements.detailBodyEl.appendChild(godLinksCard("Egyptian", egyptNames, pathNo)); + } + + if (gd.elohim) { + const g = gd.elohim; + const meta = `${g.hebrew}${g.meaning ? " — " + g.meaning : ""}`; + elements.detailBodyEl.appendChild(godLinksCard( + "God Name", + uniqueNames(splitCorrespondenceNames(g.transliteration)), + pathNo, + meta + )); + } + if (gd.archangel) { + const a = gd.archangel; + const meta = `${a.hebrew}`; + elements.detailBodyEl.appendChild(godLinksCard( + "Archangel", + uniqueNames(splitCorrespondenceNames(a.transliteration)), + pathNo, + meta + )); + } + if (gd.angelicOrder) { + const o = gd.angelicOrder; + elements.detailBodyEl.appendChild(metaCard( + "Angelic Order", + `${o.hebrew} ${o.transliteration}${o.meaning ? " — " + o.meaning : ""}` + )); + } + + } + + // ─── render sephira detail ─────────────────────────────────────────────────── + function renderSephiraDetail(seph, tree, elements) { + state.selectedSephiraNumber = Number(seph?.number); + state.selectedPathNumber = null; + + clearHighlights(); + document.querySelectorAll(`.kab-node[data-sephira="${seph.number}"], .kab-node-glow[data-sephira="${seph.number}"]`) + .forEach(el => el.classList.add("kab-node-active")); + + elements.detailNameEl.textContent = `${seph.number} · ${seph.name}`; + elements.detailSubEl.textContent = + [seph.nameHebrew, seph.translation, seph.planet].filter(Boolean).join(" · "); + + elements.detailBodyEl.innerHTML = ""; + elements.detailBodyEl.appendChild(buildFourWorldsCard(tree)); + elements.detailBodyEl.appendChild(buildPlanetLuminaryCard(seph.planet)); + elements.detailBodyEl.appendChild(metaCard("Intelligence", seph.intelligence)); + elements.detailBodyEl.appendChild(buildTarotAttributionCard(seph.tarot)); + + if (seph.description) { + elements.detailBodyEl.appendChild( + metaCard(seph.name, seph.description, true) + ); + } + + // Quick-access chips for connected paths + const connected = tree.paths.filter( + p => p.connects.from === seph.number || p.connects.to === seph.number + ); + if (connected.length) { + const card = document.createElement("div"); + card.className = "planet-meta-card kab-wide-card"; + const chips = connected.map(p => + `` + + `${p.hebrewLetter?.char || ""} ${p.pathNumber}` + + `` + ).join(""); + card.innerHTML = `Connected Paths
${chips}
`; + elements.detailBodyEl.appendChild(card); + + card.querySelectorAll(".kab-chip[data-path]").forEach(chip => { + const handler = () => { + const path = tree.paths.find(p => p.pathNumber === Number(chip.dataset.path)); + if (path) renderPathDetail(path, tree, elements); + }; + chip.addEventListener("click", handler); + chip.addEventListener("keydown", e => { + if (e.key === "Enter" || e.key === " ") { e.preventDefault(); handler(); } + }); + }); + } + + appendGodsCards(seph.number, elements); + } + + // ─── render path detail ────────────────────────────────────────────────────── + function renderPathDetail(path, tree, elements) { + state.selectedPathNumber = Number(path?.pathNumber); + state.selectedSephiraNumber = null; + + clearHighlights(); + document.querySelectorAll(`[data-path="${path.pathNumber}"]`) + .forEach(el => el.classList.add("kab-path-active")); + + const letter = path.hebrewLetter || {}; + const fromName = tree.sephiroth.find(s => s.number === path.connects.from)?.name || path.connects.from; + const toName = tree.sephiroth.find(s => s.number === path.connects.to)?.name || path.connects.to; + const astro = path.astrology ? `${path.astrology.name} (${path.astrology.type})` : "—"; + const tarotStr = path.tarot?.card + ? `${path.tarot.card}${path.tarot.trumpNumber != null ? " · Trump " + path.tarot.trumpNumber : ""}` + : "—"; + + elements.detailNameEl.textContent = + `Path ${path.pathNumber} · ${letter.char || ""} ${letter.transliteration || ""}`; + elements.detailSubEl.textContent = [path.tarot?.card, astro].filter(Boolean).join(" · "); + + elements.detailBodyEl.innerHTML = ""; + elements.detailBodyEl.appendChild(buildFourWorldsCard(tree, letter.transliteration || letter.char || "")); + elements.detailBodyEl.appendChild(buildConnectsCard(path, fromName, toName)); + elements.detailBodyEl.appendChild(buildHebrewLetterCard(letter)); + elements.detailBodyEl.appendChild(buildAstrologyCard(path.astrology)); + + // Tarot card — clickable if a trump card is associated + const tarotMetaCard = document.createElement("div"); + tarotMetaCard.className = "planet-meta-card"; + const tarotLabel = document.createElement("strong"); + tarotLabel.textContent = "Tarot"; + tarotMetaCard.appendChild(tarotLabel); + if (path.tarot?.card && path.tarot.trumpNumber != null) { + const tarotBtn = document.createElement("button"); + tarotBtn.type = "button"; + tarotBtn.className = "kab-tarot-link"; + tarotBtn.textContent = `${path.tarot.card} · Trump ${path.tarot.trumpNumber}`; + tarotBtn.title = "Open in Tarot section"; + tarotBtn.addEventListener("click", () => { + document.dispatchEvent(new CustomEvent("kab:view-trump", { + detail: { trumpNumber: path.tarot.trumpNumber } + })); + }); + tarotMetaCard.appendChild(tarotBtn); + } else { + const tarotP = document.createElement("p"); + tarotP.className = "planet-text"; + tarotP.textContent = tarotStr || "—"; + tarotMetaCard.appendChild(tarotP); + } + elements.detailBodyEl.appendChild(tarotMetaCard); + + elements.detailBodyEl.appendChild(metaCard("Intelligence", path.intelligence)); + elements.detailBodyEl.appendChild(metaCard("Pillar", path.pillar)); + + if (path.description) { + const desc = document.createElement("div"); + desc.className = "planet-meta-card kab-wide-card"; + desc.innerHTML = + `Path ${path.pathNumber} — Sefer Yetzirah` + + `

${path.description.replace(/\n/g, "

")}

`; + elements.detailBodyEl.appendChild(desc); + } + + appendGodsCards(path.pathNumber, elements); + } + + function bindTreeInteractions(svg, tree, elements) { + // Delegate clicks via element's own data attributes + svg.addEventListener("click", e => { + const sephNum = e.target.dataset?.sephira; + const pathNum = e.target.dataset?.path; + if (sephNum != null) { + const s = tree.sephiroth.find(x => x.number === Number(sephNum)); + if (s) renderSephiraDetail(s, tree, elements); + } else if (pathNum != null) { + const p = tree.paths.find(x => x.pathNumber === Number(pathNum)); + if (p) renderPathDetail(p, tree, elements); + } + }); + + // Keyboard access for path hit-areas and tarot images + svg.querySelectorAll(".kab-path-hit, .kab-path-tarot").forEach(el => { + el.addEventListener("keydown", e => { + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + const p = tree.paths.find(x => x.pathNumber === Number(el.dataset.path)); + if (p) renderPathDetail(p, tree, elements); + } + }); + }); + + // Keyboard access for sephira circles + svg.querySelectorAll(".kab-node").forEach(el => { + el.addEventListener("keydown", e => { + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + const s = tree.sephiroth.find(x => x.number === Number(el.dataset.sephira)); + if (s) renderSephiraDetail(s, tree, elements); + } + }); + }); + } + + function renderTree(elements) { + if (!state.tree || !elements?.treeContainerEl) { + return; + } + + const svg = buildTreeSVG(state.tree); + elements.treeContainerEl.innerHTML = ""; + elements.treeContainerEl.appendChild(svg); + bindTreeInteractions(svg, state.tree, elements); + } + + function renderCurrentSelection(elements) { + if (!state.tree) { + return; + } + + if (Number.isFinite(Number(state.selectedPathNumber))) { + const selectedPath = state.tree.paths.find((entry) => entry.pathNumber === Number(state.selectedPathNumber)); + if (selectedPath) { + renderPathDetail(selectedPath, state.tree, elements); + return; + } + } + + if (Number.isFinite(Number(state.selectedSephiraNumber))) { + const selectedSephira = state.tree.sephiroth.find((entry) => entry.number === Number(state.selectedSephiraNumber)); + if (selectedSephira) { + renderSephiraDetail(selectedSephira, state.tree, elements); + return; + } + } + + renderSephiraDetail(state.tree.sephiroth[0], state.tree, elements); + } + + // ─── initialise section ────────────────────────────────────────────────────── + function init(magickDataset, elements) { + const tree = magickDataset?.grouped?.kabbalah?.["kabbalah-tree"]; + if (!tree) { + if (elements.detailNameEl) { + elements.detailNameEl.textContent = "Kabbalah data unavailable"; + elements.detailSubEl.textContent = "Could not load kabbalah-tree.json"; + } + return; + } + state.tree = tree; + state.godsData = magickDataset?.grouped?.["gods"]?.byPath || {}; + state.hebrewLetterIdByToken = buildHebrewLetterLookup(magickDataset); + state.fourWorldLayers = buildFourWorldLayersFromDataset(magickDataset); + + const bindPathDisplayToggle = (toggleEl, stateKey) => { + if (!toggleEl) { + return; + } + + toggleEl.checked = Boolean(state[stateKey]); + if (toggleEl.dataset.bound) { + return; + } + + toggleEl.addEventListener("change", () => { + state[stateKey] = Boolean(toggleEl.checked); + renderTree(elements); + renderCurrentSelection(elements); + }); + + toggleEl.dataset.bound = "true"; + }; + + bindPathDisplayToggle(elements.pathLetterToggleEl, "showPathLetters"); + bindPathDisplayToggle(elements.pathNumberToggleEl, "showPathNumbers"); + bindPathDisplayToggle(elements.pathTarotToggleEl, "showPathTarotCards"); + + renderTree(elements); + renderCurrentSelection(elements); + } + + function selectPathByNumber(pathNumber) { + if (!state.initialized || !state.tree) return; + const el = getElements(); + const path = state.tree.paths.find(p => p.pathNumber === pathNumber); + if (path) renderPathDetail(path, state.tree, el); + } + + function selectSephiraByNumber(n) { + if (!state.initialized || !state.tree) return; + const el = getElements(); + const seph = state.tree.sephiroth.find(s => s.number === n); + if (seph) renderSephiraDetail(seph, state.tree, el); + } + + // select sephirah (1-10) or path (11+) by a single number + function selectNode(n) { + if (n >= 1 && n <= 10) selectSephiraByNumber(n); + else selectPathByNumber(n); + } + + // ─── public API ──────────────────────────────────────────────────────── + function ensureKabbalahSection(magickDataset) { + if (state.initialized) return; + state.initialized = true; + const elements = getElements(); + init(magickDataset, elements); + } + + window.KabbalahSectionUi = { ensureKabbalahSection, selectPathByNumber, selectSephiraByNumber, selectNode }; +})(); diff --git a/app/ui-natal.js b/app/ui-natal.js new file mode 100644 index 0000000..6033ce6 --- /dev/null +++ b/app/ui-natal.js @@ -0,0 +1,185 @@ +(function () { + const DAY_IN_MS = 24 * 60 * 60 * 1000; + + let referenceDataCache = null; + let natalSummaryEl = null; + + function getNatalSummaryEl() { + if (!natalSummaryEl) { + natalSummaryEl = document.getElementById("natal-chart-summary") + || document.getElementById("now-natal-summary"); + } + return natalSummaryEl; + } + + function parseMonthDay(monthDay) { + const [month, day] = String(monthDay || "").split("-").map(Number); + if (!Number.isFinite(month) || !Number.isFinite(day)) { + return null; + } + return { month, day }; + } + + function parseBirthDateAsLocalNoon(isoDate) { + const [year, month, day] = String(isoDate || "").split("-").map(Number); + if (!year || !month || !day) { + return null; + } + return new Date(year, month - 1, day, 12, 0, 0, 0); + } + + function isDateInSign(date, sign) { + const start = parseMonthDay(sign?.start); + const end = parseMonthDay(sign?.end); + if (!start || !end) { + return false; + } + + const month = date.getMonth() + 1; + const day = date.getDate(); + const wrapsYear = start.month > end.month; + + if (!wrapsYear) { + const afterStart = month > start.month || (month === start.month && day >= start.day); + const beforeEnd = month < end.month || (month === end.month && day <= end.day); + return afterStart && beforeEnd; + } + + const afterStart = month > start.month || (month === start.month && day >= start.day); + const beforeEnd = month < end.month || (month === end.month && day <= end.day); + return afterStart || beforeEnd; + } + + function getSignStartDate(date, sign) { + const start = parseMonthDay(sign?.start); + const end = parseMonthDay(sign?.end); + if (!start || !end) { + return null; + } + + const wrapsYear = start.month > end.month; + const month = date.getMonth() + 1; + const day = date.getDate(); + let year = date.getFullYear(); + + if (wrapsYear && (month < start.month || (month === start.month && day < start.day))) { + year -= 1; + } + + return new Date(year, start.month - 1, start.day, 12, 0, 0, 0); + } + + function getSunSignAnchor(referenceData, birthDate) { + const signs = Array.isArray(referenceData?.signs) ? referenceData.signs : []; + if (!signs.length || !birthDate) { + return null; + } + + const sign = signs.find((candidate) => isDateInSign(birthDate, candidate)) || null; + if (!sign) { + return null; + } + + return { + id: sign.id, + name: sign.name, + symbol: sign.symbol || "", + tarotMajorArcana: sign?.tarot?.majorArcana || "" + }; + } + + function getSunDecanAnchor(referenceData, signId, birthDate) { + if (!signId || !birthDate) { + return null; + } + + const sign = (referenceData?.signs || []).find((entry) => entry.id === signId) || null; + if (!sign) { + return null; + } + + const signStartDate = getSignStartDate(birthDate, sign); + if (!signStartDate) { + return null; + } + + const daysSinceSignStart = Math.max(0, Math.floor((birthDate.getTime() - signStartDate.getTime()) / DAY_IN_MS)); + const decanIndex = Math.max(1, Math.min(3, Math.floor(daysSinceSignStart / 10) + 1)); + + const decans = referenceData?.decansBySign?.[signId] || []; + const decan = decans.find((entry) => Number(entry.index) === decanIndex) || null; + + return { + index: decanIndex, + tarotMinorArcana: decan?.tarotMinorArcana || "" + }; + } + + function buildNatalScaffoldSummary() { + const context = window.TarotNatal?.getContext?.() || null; + if (!context) { + return "Natal Chart Scaffold: unavailable"; + } + + const birthDate = context.birthDateParts?.isoDate + ? parseBirthDateAsLocalNoon(context.birthDateParts.isoDate) + : null; + + if (!birthDate) { + return [ + "Natal Chart Scaffold", + "Birth Date: --", + `Geo Anchor: ${context.latitude.toFixed(4)}, ${context.longitude.toFixed(4)}`, + "Sun Sign Anchor: --", + "Sun Decan Anchor: --", + "House Scaffold: 12 houses ready (Equal House placeholder), Ascendant/cusps pending birth time" + ].join("\n"); + } + + const sunSign = getSunSignAnchor(referenceDataCache, birthDate); + const sunDecan = getSunDecanAnchor(referenceDataCache, sunSign?.id, birthDate); + + const signLabel = sunSign + ? `${sunSign.symbol} ${sunSign.name}${sunSign.tarotMajorArcana ? ` · ${sunSign.tarotMajorArcana}` : ""}` + : "--"; + + const decanLabel = sunDecan + ? `Decan ${sunDecan.index}${sunDecan.tarotMinorArcana ? ` · ${sunDecan.tarotMinorArcana}` : ""}` + : "--"; + + return [ + "Natal Chart Scaffold", + `Birth Date: ${context.birthDateParts.isoDate} (${context.timeZone})`, + `Geo Anchor: ${context.latitude.toFixed(4)}, ${context.longitude.toFixed(4)}`, + `Sun Sign Anchor: ${signLabel}`, + `Sun Decan Anchor: ${decanLabel}`, + "House Scaffold: 12 houses ready (Equal House placeholder), Ascendant/cusps pending birth time" + ].join("\n"); + } + + function renderNatalSummary() { + const outputEl = getNatalSummaryEl(); + if (!outputEl) { + return; + } + + outputEl.textContent = buildNatalScaffoldSummary(); + } + + function ensureNatalPanel(referenceData) { + if (referenceData && typeof referenceData === "object") { + referenceDataCache = referenceData; + } + + renderNatalSummary(); + } + + document.addEventListener("settings:updated", () => { + renderNatalSummary(); + }); + + window.TarotNatalUi = { + ensureNatalPanel, + renderNatalSummary + }; +})(); diff --git a/app/ui-now.js b/app/ui-now.js new file mode 100644 index 0000000..0b47dd5 --- /dev/null +++ b/app/ui-now.js @@ -0,0 +1,686 @@ +(function () { + const { + DAY_IN_MS, + getDateKey, + getMoonPhaseName, + getDecanForDate, + calcPlanetaryHoursForDayAndLocation + } = window.TarotCalc; + const { resolveTarotCardImage, getTarotCardDisplayName } = window.TarotCardImages || {}; + + let moonCountdownCache = null; + let decanCountdownCache = null; + let nowLightboxOverlayEl = null; + let nowLightboxImageEl = null; + let nowLightboxZoomed = false; + + const LIGHTBOX_ZOOM_SCALE = 6.66; + + const PLANETARY_BODIES = [ + { id: "sol", astronomyBody: "Sun", fallbackName: "Sun", fallbackSymbol: "☉︎" }, + { id: "luna", astronomyBody: "Moon", fallbackName: "Moon", fallbackSymbol: "☾︎" }, + { id: "mercury", astronomyBody: "Mercury", fallbackName: "Mercury", fallbackSymbol: "☿︎" }, + { id: "venus", astronomyBody: "Venus", fallbackName: "Venus", fallbackSymbol: "♀︎" }, + { id: "mars", astronomyBody: "Mars", fallbackName: "Mars", fallbackSymbol: "♂︎" }, + { id: "jupiter", astronomyBody: "Jupiter", fallbackName: "Jupiter", fallbackSymbol: "♃︎" }, + { id: "saturn", astronomyBody: "Saturn", fallbackName: "Saturn", fallbackSymbol: "♄︎" }, + { id: "uranus", astronomyBody: "Uranus", fallbackName: "Uranus", fallbackSymbol: "♅︎" }, + { id: "neptune", astronomyBody: "Neptune", fallbackName: "Neptune", fallbackSymbol: "♆︎" }, + { id: "pluto", astronomyBody: "Pluto", fallbackName: "Pluto", fallbackSymbol: "♇︎" } + ]; + + function resetNowLightboxZoom() { + if (!nowLightboxImageEl) { + return; + } + + nowLightboxZoomed = false; + nowLightboxImageEl.style.transform = "scale(1)"; + nowLightboxImageEl.style.transformOrigin = "center center"; + nowLightboxImageEl.style.cursor = "zoom-in"; + } + + function updateNowLightboxZoomOrigin(clientX, clientY) { + if (!nowLightboxZoomed || !nowLightboxImageEl) { + return; + } + + const rect = nowLightboxImageEl.getBoundingClientRect(); + if (!rect.width || !rect.height) { + return; + } + + const x = Math.min(100, Math.max(0, ((clientX - rect.left) / rect.width) * 100)); + const y = Math.min(100, Math.max(0, ((clientY - rect.top) / rect.height) * 100)); + nowLightboxImageEl.style.transformOrigin = `${x}% ${y}%`; + } + + function isNowLightboxPointOnCard(clientX, clientY) { + if (!nowLightboxImageEl) { + return false; + } + + const rect = nowLightboxImageEl.getBoundingClientRect(); + const naturalWidth = nowLightboxImageEl.naturalWidth; + const naturalHeight = nowLightboxImageEl.naturalHeight; + + if (!rect.width || !rect.height || !naturalWidth || !naturalHeight) { + return true; + } + + const frameAspect = rect.width / rect.height; + const imageAspect = naturalWidth / naturalHeight; + + let renderWidth = rect.width; + let renderHeight = rect.height; + if (imageAspect > frameAspect) { + renderHeight = rect.width / imageAspect; + } else { + renderWidth = rect.height * imageAspect; + } + + const left = rect.left + (rect.width - renderWidth) / 2; + const top = rect.top + (rect.height - renderHeight) / 2; + const right = left + renderWidth; + const bottom = top + renderHeight; + + return clientX >= left && clientX <= right && clientY >= top && clientY <= bottom; + } + + function ensureNowImageLightbox() { + if (nowLightboxOverlayEl && nowLightboxImageEl) { + return; + } + + nowLightboxOverlayEl = document.createElement("div"); + nowLightboxOverlayEl.setAttribute("aria-hidden", "true"); + nowLightboxOverlayEl.style.position = "fixed"; + nowLightboxOverlayEl.style.inset = "0"; + nowLightboxOverlayEl.style.background = "rgba(0, 0, 0, 0.82)"; + nowLightboxOverlayEl.style.display = "none"; + nowLightboxOverlayEl.style.alignItems = "center"; + nowLightboxOverlayEl.style.justifyContent = "center"; + nowLightboxOverlayEl.style.zIndex = "9999"; + nowLightboxOverlayEl.style.padding = "0"; + + const image = document.createElement("img"); + image.alt = "Now card enlarged image"; + image.style.maxWidth = "100vw"; + image.style.maxHeight = "100vh"; + image.style.width = "100vw"; + image.style.height = "100vh"; + image.style.objectFit = "contain"; + image.style.borderRadius = "0"; + image.style.boxShadow = "none"; + image.style.border = "none"; + image.style.cursor = "zoom-in"; + image.style.transform = "scale(1)"; + image.style.transformOrigin = "center center"; + image.style.transition = "transform 120ms ease-out"; + image.style.userSelect = "none"; + + nowLightboxImageEl = image; + nowLightboxOverlayEl.appendChild(image); + + const closeLightbox = () => { + if (!nowLightboxOverlayEl || !nowLightboxImageEl) { + return; + } + nowLightboxOverlayEl.style.display = "none"; + nowLightboxOverlayEl.setAttribute("aria-hidden", "true"); + nowLightboxImageEl.removeAttribute("src"); + resetNowLightboxZoom(); + }; + + nowLightboxOverlayEl.addEventListener("click", (event) => { + if (event.target === nowLightboxOverlayEl) { + closeLightbox(); + } + }); + + nowLightboxImageEl.addEventListener("click", (event) => { + event.stopPropagation(); + if (!isNowLightboxPointOnCard(event.clientX, event.clientY)) { + closeLightbox(); + return; + } + if (!nowLightboxZoomed) { + nowLightboxZoomed = true; + nowLightboxImageEl.style.transform = `scale(${LIGHTBOX_ZOOM_SCALE})`; + nowLightboxImageEl.style.cursor = "zoom-out"; + updateNowLightboxZoomOrigin(event.clientX, event.clientY); + return; + } + resetNowLightboxZoom(); + }); + + nowLightboxImageEl.addEventListener("mousemove", (event) => { + updateNowLightboxZoomOrigin(event.clientX, event.clientY); + }); + + nowLightboxImageEl.addEventListener("mouseleave", () => { + if (nowLightboxZoomed) { + nowLightboxImageEl.style.transformOrigin = "center center"; + } + }); + + document.addEventListener("keydown", (event) => { + if (event.key === "Escape") { + closeLightbox(); + } + }); + + document.body.appendChild(nowLightboxOverlayEl); + } + + function openNowImageLightbox(src, altText) { + if (!src) { + return; + } + + ensureNowImageLightbox(); + if (!nowLightboxOverlayEl || !nowLightboxImageEl) { + return; + } + + nowLightboxImageEl.src = src; + nowLightboxImageEl.alt = altText || "Now card enlarged image"; + resetNowLightboxZoom(); + nowLightboxOverlayEl.style.display = "flex"; + nowLightboxOverlayEl.setAttribute("aria-hidden", "false"); + } + + function getDisplayTarotName(cardName, trumpNumber) { + if (!cardName) { + return ""; + } + if (typeof getTarotCardDisplayName !== "function") { + return cardName; + } + if (Number.isFinite(Number(trumpNumber))) { + return getTarotCardDisplayName(cardName, { trumpNumber: Number(trumpNumber) }) || cardName; + } + return getTarotCardDisplayName(cardName) || cardName; + } + + function bindNowCardLightbox(imageEl) { + if (!(imageEl instanceof HTMLImageElement) || imageEl.dataset.lightboxBound === "true") { + return; + } + + imageEl.dataset.lightboxBound = "true"; + imageEl.style.cursor = "zoom-in"; + imageEl.title = "Click to enlarge"; + imageEl.addEventListener("click", () => { + const src = imageEl.getAttribute("src"); + if (!src || imageEl.style.display === "none") { + return; + } + openNowImageLightbox(src, imageEl.alt || "Now card enlarged image"); + }); + } + + function normalizeLongitude(value) { + const numeric = Number(value); + if (!Number.isFinite(numeric)) { + return null; + } + + return ((numeric % 360) + 360) % 360; + } + + function getSortedSigns(signs) { + if (!Array.isArray(signs)) { + return []; + } + + return [...signs].sort((a, b) => (a.order || 0) - (b.order || 0)); + } + + function getSignForLongitude(longitude, signs) { + const normalized = normalizeLongitude(longitude); + if (normalized === null) { + return null; + } + + const sortedSigns = getSortedSigns(signs); + if (!sortedSigns.length) { + return null; + } + + const signIndex = Math.min(sortedSigns.length - 1, Math.floor(normalized / 30)); + const sign = sortedSigns[signIndex] || null; + if (!sign) { + return null; + } + + return { + sign, + degreeInSign: normalized - signIndex * 30, + absoluteLongitude: normalized + }; + } + + function getSabianSymbolForLongitude(longitude, sabianSymbols) { + const normalized = normalizeLongitude(longitude); + if (normalized === null || !Array.isArray(sabianSymbols) || !sabianSymbols.length) { + return null; + } + + const absoluteDegree = Math.floor(normalized) + 1; + return sabianSymbols.find((entry) => Number(entry?.absoluteDegree) === absoluteDegree) || null; + } + + function calculatePlanetPositions(referenceData, now) { + if (!window.Astronomy || !referenceData) { + return []; + } + + const positions = []; + + PLANETARY_BODIES.forEach((body) => { + try { + const geoVector = window.Astronomy.GeoVector(body.astronomyBody, now, true); + const ecliptic = window.Astronomy.Ecliptic(geoVector); + const signInfo = getSignForLongitude(ecliptic?.elon, referenceData.signs); + if (!signInfo?.sign) { + return; + } + + const planetInfo = referenceData.planets?.[body.id] || null; + const symbol = planetInfo?.symbol || body.fallbackSymbol; + const name = planetInfo?.name || body.fallbackName; + + positions.push({ + id: body.id, + symbol, + name, + longitude: signInfo.absoluteLongitude, + sign: signInfo.sign, + degreeInSign: signInfo.degreeInSign, + label: `${symbol} ${name}: ${signInfo.sign.symbol} ${signInfo.sign.name} ${signInfo.degreeInSign.toFixed(1)}°` + }); + } catch { + } + }); + + return positions; + } + + function updateNowStats(referenceData, elements, now) { + const planetPositions = calculatePlanetPositions(referenceData, now); + + if (elements.nowStatsPlanetsEl) { + elements.nowStatsPlanetsEl.replaceChildren(); + + if (!planetPositions.length) { + elements.nowStatsPlanetsEl.textContent = "--"; + } else { + planetPositions.forEach((position) => { + const item = document.createElement("div"); + item.className = "now-stats-planet"; + item.textContent = position.label; + elements.nowStatsPlanetsEl.appendChild(item); + }); + } + } + + if (elements.nowStatsSabianEl) { + const sunPosition = planetPositions.find((entry) => entry.id === "sol") || null; + const moonPosition = planetPositions.find((entry) => entry.id === "luna") || null; + const sunSabianSymbol = sunPosition + ? getSabianSymbolForLongitude(sunPosition.longitude, referenceData.sabianSymbols) + : null; + const moonSabianSymbol = moonPosition + ? getSabianSymbolForLongitude(moonPosition.longitude, referenceData.sabianSymbols) + : null; + + const sunLine = sunSabianSymbol?.phrase + ? `Sun Sabian ${sunSabianSymbol.absoluteDegree}: ${sunSabianSymbol.phrase}` + : "Sun Sabian: --"; + const moonLine = moonSabianSymbol?.phrase + ? `Moon Sabian ${moonSabianSymbol.absoluteDegree}: ${moonSabianSymbol.phrase}` + : "Moon Sabian: --"; + + elements.nowStatsSabianEl.textContent = `${sunLine}\n${moonLine}`; + } + } + + function formatCountdown(ms, mode) { + if (!Number.isFinite(ms) || ms <= 0) { + if (mode === "hours") { + return "0.0 hours"; + } + if (mode === "seconds") { + return "0s"; + } + return "0m"; + } + + if (mode === "hours") { + return `${(ms / 3600000).toFixed(1)} hours`; + } + + if (mode === "seconds") { + return `${Math.floor(ms / 1000)}s`; + } + + return `${Math.floor(ms / 60000)}m`; + } + + function parseMonthDay(monthDay) { + const [month, day] = String(monthDay || "").split("-").map(Number); + return { month, day }; + } + + function getCurrentPhaseName(date) { + return getMoonPhaseName(window.SunCalc.getMoonIllumination(date).phase); + } + + function findNextMoonPhaseTransition(now) { + const currentPhase = getCurrentPhaseName(now); + const stepMs = 15 * 60 * 1000; + const maxMs = 40 * DAY_IN_MS; + + let previousTime = now.getTime(); + let previousPhase = currentPhase; + + for (let t = previousTime + stepMs; t <= previousTime + maxMs; t += stepMs) { + const phaseName = getCurrentPhaseName(new Date(t)); + if (phaseName !== previousPhase) { + let low = previousTime; + let high = t; + while (high - low > 1000) { + const mid = Math.floor((low + high) / 2); + const midPhase = getCurrentPhaseName(new Date(mid)); + if (midPhase === currentPhase) { + low = mid; + } else { + high = mid; + } + } + + const transitionAt = new Date(high); + const nextPhase = getCurrentPhaseName(new Date(high + 1000)); + return { + fromPhase: currentPhase, + nextPhase, + changeAt: transitionAt + }; + } + previousTime = t; + previousPhase = phaseName; + } + + return null; + } + + function getSignStartDate(now, sign) { + const { month: startMonth, day: startDay } = parseMonthDay(sign.start); + const { month: endMonth } = parseMonthDay(sign.end); + const wrapsYear = startMonth > endMonth; + + let year = now.getFullYear(); + const nowMonth = now.getMonth() + 1; + const nowDay = now.getDate(); + + if (wrapsYear && (nowMonth < startMonth || (nowMonth === startMonth && nowDay < startDay))) { + year -= 1; + } + + return new Date(year, startMonth - 1, startDay); + } + + function getNextSign(signs, currentSign) { + const sorted = [...signs].sort((a, b) => (a.order || 0) - (b.order || 0)); + const index = sorted.findIndex((entry) => entry.id === currentSign.id); + if (index < 0) { + return null; + } + return sorted[(index + 1) % sorted.length] || null; + } + + function getDecanByIndex(decansBySign, signId, index) { + const signDecans = decansBySign[signId] || []; + return signDecans.find((entry) => entry.index === index) || null; + } + + function findNextDecanTransition(now, signs, decansBySign) { + const currentInfo = getDecanForDate(now, signs, decansBySign); + if (!currentInfo?.sign) { + return null; + } + + const currentIndex = currentInfo.decan?.index || 1; + const signStart = getSignStartDate(now, currentInfo.sign); + + if (currentIndex < 3) { + const changeAt = new Date(signStart.getTime() + currentIndex * 10 * DAY_IN_MS); + const nextDecan = getDecanByIndex(decansBySign, currentInfo.sign.id, currentIndex + 1); + const nextLabel = nextDecan?.tarotMinorArcana || `${currentInfo.sign.name} Decan ${currentIndex + 1}`; + + return { + key: `${currentInfo.sign.id}-${currentIndex}`, + changeAt, + nextLabel + }; + } + + const nextSign = getNextSign(signs, currentInfo.sign); + if (!nextSign) { + return null; + } + + const { month: nextMonth, day: nextDay } = parseMonthDay(nextSign.start); + let year = now.getFullYear(); + let changeAt = new Date(year, nextMonth - 1, nextDay); + if (changeAt.getTime() <= now.getTime()) { + changeAt = new Date(year + 1, nextMonth - 1, nextDay); + } + + const nextDecan = getDecanByIndex(decansBySign, nextSign.id, 1); + + return { + key: `${currentInfo.sign.id}-${currentIndex}`, + changeAt, + nextLabel: nextDecan?.tarotMinorArcana || `${nextSign.name} Decan 1` + }; + } + + function setNowCardImage(imageEl, cardName, fallbackLabel, trumpNumber) { + if (!imageEl) { + return; + } + + bindNowCardLightbox(imageEl); + + if (!cardName || typeof resolveTarotCardImage !== "function") { + imageEl.style.display = "none"; + imageEl.removeAttribute("src"); + return; + } + + const src = resolveTarotCardImage(cardName); + if (!src) { + imageEl.style.display = "none"; + imageEl.removeAttribute("src"); + return; + } + + imageEl.src = src; + const displayName = getDisplayTarotName(cardName, trumpNumber); + imageEl.alt = `${fallbackLabel}: ${displayName}`; + imageEl.style.display = "block"; + } + + function updateNowPanel(referenceData, geo, elements, timeFormat = "minutes") { + if (!referenceData || !geo || !elements) { + return { dayKey: getDateKey(new Date()), skyRefreshKey: "" }; + } + + const now = new Date(); + const dayKey = getDateKey(now); + + const todayHours = calcPlanetaryHoursForDayAndLocation(now, geo); + const yesterday = new Date(now.getTime() - DAY_IN_MS); + const yesterdayHours = calcPlanetaryHoursForDayAndLocation(yesterday, geo); + const tomorrow = new Date(now.getTime() + DAY_IN_MS); + const tomorrowHours = calcPlanetaryHoursForDayAndLocation(tomorrow, geo); + const allHours = [...yesterdayHours, ...todayHours, ...tomorrowHours].sort( + (a, b) => a.start.getTime() - b.start.getTime() + ); + const currentHour = allHours.find((entry) => now >= entry.start && now < entry.end); + const currentHourSkyKey = currentHour + ? `${currentHour.planetId}-${currentHour.start.toISOString()}` + : "no-hour"; + + if (currentHour) { + const planet = referenceData.planets[currentHour.planetId]; + elements.nowHourEl.textContent = planet + ? `${planet.symbol} ${planet.name}` + : currentHour.planetId; + if (elements.nowHourTarotEl) { + const hourCardName = planet?.tarot?.majorArcana || ""; + const hourTrumpNumber = planet?.tarot?.number; + elements.nowHourTarotEl.textContent = hourCardName + ? getDisplayTarotName(hourCardName, hourTrumpNumber) + : "--"; + } + + const msLeft = Math.max(0, currentHour.end.getTime() - now.getTime()); + elements.nowCountdownEl.textContent = formatCountdown(msLeft, timeFormat); + + if (elements.nowHourNextEl) { + const nextHour = allHours.find( + (entry) => entry.start.getTime() >= currentHour.end.getTime() - 1000 + ); + if (nextHour) { + const nextPlanet = referenceData.planets[nextHour.planetId]; + elements.nowHourNextEl.textContent = nextPlanet + ? `> ${nextPlanet.name}` + : `> ${nextHour.planetId}`; + } else { + elements.nowHourNextEl.textContent = "> --"; + } + } + + setNowCardImage( + elements.nowHourCardEl, + planet?.tarot?.majorArcana, + "Current planetary hour card", + planet?.tarot?.number + ); + } else { + elements.nowHourEl.textContent = "--"; + elements.nowCountdownEl.textContent = "--"; + if (elements.nowHourTarotEl) { + elements.nowHourTarotEl.textContent = "--"; + } + if (elements.nowHourNextEl) { + elements.nowHourNextEl.textContent = "> --"; + } + setNowCardImage(elements.nowHourCardEl, null, "Current planetary hour card"); + } + + const moonIllum = window.SunCalc.getMoonIllumination(now); + const moonPhase = getMoonPhaseName(moonIllum.phase); + const moonTarot = referenceData.planets.luna?.tarot?.majorArcana || "The High Priestess"; + elements.nowMoonEl.textContent = `${moonPhase} (${Math.round(moonIllum.fraction * 100)}%)`; + elements.nowMoonTarotEl.textContent = getDisplayTarotName(moonTarot, referenceData.planets.luna?.tarot?.number); + setNowCardImage( + elements.nowMoonCardEl, + moonTarot, + "Current moon phase card", + referenceData.planets.luna?.tarot?.number + ); + + if (!moonCountdownCache || moonCountdownCache.fromPhase !== moonPhase || now >= moonCountdownCache.changeAt) { + moonCountdownCache = findNextMoonPhaseTransition(now); + } + + if (elements.nowMoonCountdownEl) { + if (moonCountdownCache?.changeAt) { + const remaining = moonCountdownCache.changeAt.getTime() - now.getTime(); + elements.nowMoonCountdownEl.textContent = formatCountdown(remaining, timeFormat); + if (elements.nowMoonNextEl) { + elements.nowMoonNextEl.textContent = `> ${moonCountdownCache.nextPhase}`; + } + } else { + elements.nowMoonCountdownEl.textContent = "--"; + if (elements.nowMoonNextEl) { + elements.nowMoonNextEl.textContent = "> --"; + } + } + } + + const sunInfo = getDecanForDate(now, referenceData.signs, referenceData.decansBySign); + const decanSkyKey = sunInfo?.sign + ? `${sunInfo.sign.id}-${sunInfo.decan?.index || 1}` + : "no-decan"; + if (sunInfo?.sign) { + const signStartDate = getSignStartDate(now, sunInfo.sign); + const daysSinceSignStart = (now.getTime() - signStartDate.getTime()) / DAY_IN_MS; + const signDegree = Math.min(29.9, Math.max(0, daysSinceSignStart)); + const signMajorName = getDisplayTarotName(sunInfo.sign.tarot.majorArcana, sunInfo.sign.tarot.trumpNumber); + elements.nowDecanEl.textContent = `${sunInfo.sign.symbol} ${sunInfo.sign.name} · ${signMajorName} (${signDegree.toFixed(1)}°)`; + + const currentDecanKey = `${sunInfo.sign.id}-${sunInfo.decan?.index || 1}`; + if (!decanCountdownCache || decanCountdownCache.key !== currentDecanKey || now >= decanCountdownCache.changeAt) { + decanCountdownCache = findNextDecanTransition(now, referenceData.signs, referenceData.decansBySign); + } + + if (sunInfo.decan) { + const decanCardName = sunInfo.decan.tarotMinorArcana; + elements.nowDecanTarotEl.textContent = getDisplayTarotName(decanCardName); + setNowCardImage(elements.nowDecanCardEl, sunInfo.decan.tarotMinorArcana, "Current decan card"); + } else { + const signTarotName = sunInfo.sign.tarot?.majorArcana || "--"; + elements.nowDecanTarotEl.textContent = signTarotName === "--" + ? "--" + : getDisplayTarotName(signTarotName, sunInfo.sign.tarot?.trumpNumber); + setNowCardImage( + elements.nowDecanCardEl, + sunInfo.sign.tarot?.majorArcana, + "Current decan card", + sunInfo.sign.tarot?.trumpNumber + ); + } + + if (elements.nowDecanCountdownEl) { + if (decanCountdownCache?.changeAt) { + const remaining = decanCountdownCache.changeAt.getTime() - now.getTime(); + elements.nowDecanCountdownEl.textContent = formatCountdown(remaining, timeFormat); + if (elements.nowDecanNextEl) { + elements.nowDecanNextEl.textContent = `> ${getDisplayTarotName(decanCountdownCache.nextLabel)}`; + } + } else { + elements.nowDecanCountdownEl.textContent = "--"; + if (elements.nowDecanNextEl) { + elements.nowDecanNextEl.textContent = "> --"; + } + } + } + } else { + elements.nowDecanEl.textContent = "--"; + elements.nowDecanTarotEl.textContent = "--"; + setNowCardImage(elements.nowDecanCardEl, null, "Current decan card"); + if (elements.nowDecanCountdownEl) { + elements.nowDecanCountdownEl.textContent = "--"; + } + if (elements.nowDecanNextEl) { + elements.nowDecanNextEl.textContent = "> --"; + } + } + + updateNowStats(referenceData, elements, now); + + return { + dayKey, + skyRefreshKey: `${currentHourSkyKey}|${decanSkyKey}|${moonPhase}` + }; + } + + window.TarotNowUi = { + updateNowPanel + }; +})(); diff --git a/app/ui-planets.js b/app/ui-planets.js new file mode 100644 index 0000000..87d411f --- /dev/null +++ b/app/ui-planets.js @@ -0,0 +1,898 @@ +(function () { + const { getTarotCardDisplayName, getTarotCardSearchAliases } = window.TarotCardImages || {}; + + const state = { + initialized: false, + entries: [], + filteredEntries: [], + searchQuery: "", + selectedId: "", + kabbalahTargetsByPlanetId: {}, + monthRefsByPlanetId: new Map(), + cubePlacementsByPlanetId: new Map() + }; + + function normalizePlanetToken(value) { + return String(value || "") + .trim() + .toLowerCase() + .replace(/[^a-z]/g, ""); + } + + function toPlanetId(value) { + const token = normalizePlanetToken(value); + if (!token) return null; + + if (token === "sun") return "sol"; + if (token === "moon") return "luna"; + if (["saturn", "jupiter", "mars", "sol", "venus", "mercury", "luna"].includes(token)) { + return token; + } + return null; + } + + function appendKabbalahTarget(map, planetId, target) { + if (!planetId || !target) return; + if (!map[planetId]) map[planetId] = []; + const key = `${target.kind}:${target.number}`; + if (map[planetId].some((entry) => `${entry.kind}:${entry.number}` === key)) return; + map[planetId].push(target); + } + + function buildKabbalahTargetsByPlanet(magickDataset) { + const tree = magickDataset?.grouped?.kabbalah?.["kabbalah-tree"]; + const map = {}; + if (!tree) return map; + + (tree.sephiroth || []).forEach((seph) => { + const planetId = toPlanetId(seph?.planet); + if (!planetId) return; + appendKabbalahTarget(map, planetId, { + kind: "sephirah", + number: seph.number, + label: `Sephirah ${seph.number} · ${seph.name}` + }); + }); + + (tree.paths || []).forEach((path) => { + if (path?.astrology?.type !== "planet") return; + const planetId = toPlanetId(path?.astrology?.name); + if (!planetId) return; + appendKabbalahTarget(map, planetId, { + kind: "path", + number: path.pathNumber, + label: `Path ${path.pathNumber} · ${path?.tarot?.card || path?.hebrewLetter?.transliteration || ""}`.trim() + }); + }); + + return map; + } + + function buildMonthReferencesByPlanet(referenceData) { + const map = new Map(); + const months = Array.isArray(referenceData?.calendarMonths) ? referenceData.calendarMonths : []; + const holidays = Array.isArray(referenceData?.celestialHolidays) ? referenceData.celestialHolidays : []; + const monthById = new Map(months.map((month) => [month.id, month])); + + function parseMonthDayToken(value) { + const text = String(value || "").trim(); + const match = text.match(/^(\d{1,2})-(\d{1,2})$/); + if (!match) { + return null; + } + + const monthNo = Number(match[1]); + const dayNo = Number(match[2]); + if (!Number.isInteger(monthNo) || !Number.isInteger(dayNo) || monthNo < 1 || monthNo > 12 || dayNo < 1 || dayNo > 31) { + return null; + } + + return { month: monthNo, day: dayNo }; + } + + function parseMonthDayTokensFromText(value) { + const text = String(value || ""); + const matches = [...text.matchAll(/(\d{1,2})-(\d{1,2})/g)]; + return matches + .map((match) => ({ month: Number(match[1]), day: Number(match[2]) })) + .filter((token) => Number.isInteger(token.month) && Number.isInteger(token.day) && token.month >= 1 && token.month <= 12 && token.day >= 1 && token.day <= 31); + } + + function toDateToken(token, year) { + if (!token) { + return null; + } + return new Date(year, token.month - 1, token.day, 12, 0, 0, 0); + } + + function splitMonthDayRangeByMonth(startToken, endToken) { + const startDate = toDateToken(startToken, 2025); + const endBase = toDateToken(endToken, 2025); + if (!startDate || !endBase) { + return []; + } + + const wrapsYear = endBase.getTime() < startDate.getTime(); + const endDate = wrapsYear ? toDateToken(endToken, 2026) : endBase; + if (!endDate) { + return []; + } + + const segments = []; + let cursor = new Date(startDate); + while (cursor.getTime() <= endDate.getTime()) { + const monthEnd = new Date(cursor.getFullYear(), cursor.getMonth() + 1, 0, 12, 0, 0, 0); + const segmentEnd = monthEnd.getTime() < endDate.getTime() ? monthEnd : endDate; + + segments.push({ + monthNo: cursor.getMonth() + 1, + startDay: cursor.getDate(), + endDay: segmentEnd.getDate() + }); + + cursor = new Date(segmentEnd.getFullYear(), segmentEnd.getMonth(), segmentEnd.getDate() + 1, 12, 0, 0, 0); + } + + return segments; + } + + function tokenToString(monthNo, dayNo) { + return `${String(monthNo).padStart(2, "0")}-${String(dayNo).padStart(2, "0")}`; + } + + function formatRangeLabel(monthName, startDay, endDay) { + if (!Number.isFinite(startDay) || !Number.isFinite(endDay)) { + return monthName; + } + if (startDay === endDay) { + return `${monthName} ${startDay}`; + } + return `${monthName} ${startDay}-${endDay}`; + } + + function resolveRangeForMonth(month, options = {}) { + const monthOrder = Number(month?.order); + const monthStart = parseMonthDayToken(month?.start); + const monthEnd = parseMonthDayToken(month?.end); + if (!Number.isFinite(monthOrder) || !monthStart || !monthEnd) { + return { + startToken: String(month?.start || "").trim() || null, + endToken: String(month?.end || "").trim() || null, + label: month?.name || month?.id || "", + isFullMonth: true + }; + } + + let startToken = parseMonthDayToken(options.startToken); + let endToken = parseMonthDayToken(options.endToken); + + if (!startToken || !endToken) { + const tokens = parseMonthDayTokensFromText(options.rawDateText); + if (tokens.length >= 2) { + startToken = tokens[0]; + endToken = tokens[1]; + } else if (tokens.length === 1) { + startToken = tokens[0]; + endToken = tokens[0]; + } + } + + if (!startToken || !endToken) { + startToken = monthStart; + endToken = monthEnd; + } + + const segments = splitMonthDayRangeByMonth(startToken, endToken); + const segment = segments.find((entry) => entry.monthNo === monthOrder) || null; + + const useStart = segment ? { month: monthOrder, day: segment.startDay } : startToken; + const useEnd = segment ? { month: monthOrder, day: segment.endDay } : endToken; + const startText = tokenToString(useStart.month, useStart.day); + const endText = tokenToString(useEnd.month, useEnd.day); + const isFullMonth = startText === month.start && endText === month.end; + + return { + startToken: startText, + endToken: endText, + label: isFullMonth + ? (month.name || month.id) + : formatRangeLabel(month.name || month.id, useStart.day, useEnd.day), + isFullMonth + }; + } + + function pushRef(planetToken, month, options = {}) { + const planetId = toPlanetId(planetToken) || normalizePlanetToken(planetToken); + if (!planetId || !month?.id) { + return; + } + + if (!map.has(planetId)) { + map.set(planetId, []); + } + + const rows = map.get(planetId); + const range = resolveRangeForMonth(month, options); + const key = `${month.id}|${range.startToken || ""}|${range.endToken || ""}`; + if (rows.some((entry) => entry.key === key)) { + return; + } + + rows.push({ + id: month.id, + name: month.name || month.id, + order: Number.isFinite(Number(month.order)) ? Number(month.order) : 999, + label: range.label, + startToken: range.startToken, + endToken: range.endToken, + isFullMonth: range.isFullMonth, + key + }); + } + + months.forEach((month) => { + pushRef(month?.associations?.planetId, month); + const events = Array.isArray(month?.events) ? month.events : []; + events.forEach((event) => { + pushRef(event?.associations?.planetId, month, { + rawDateText: event?.dateRange || event?.date || "" + }); + }); + }); + + holidays.forEach((holiday) => { + const month = monthById.get(holiday?.monthId); + if (!month) { + return; + } + pushRef(holiday?.associations?.planetId, month, { + rawDateText: holiday?.dateRange || holiday?.date || "" + }); + }); + + map.forEach((rows, key) => { + const preciseMonthIds = new Set( + rows + .filter((entry) => !entry.isFullMonth) + .map((entry) => entry.id) + ); + + const filtered = rows.filter((entry) => { + if (!entry.isFullMonth) { + return true; + } + return !preciseMonthIds.has(entry.id); + }); + + filtered.sort((left, right) => { + if (left.order !== right.order) { + return left.order - right.order; + } + + const startLeft = parseMonthDayToken(left.startToken); + const startRight = parseMonthDayToken(right.startToken); + const dayLeft = startLeft ? startLeft.day : 999; + const dayRight = startRight ? startRight.day : 999; + if (dayLeft !== dayRight) { + return dayLeft - dayRight; + } + + return String(left.label || left.name || "").localeCompare(String(right.label || right.name || "")); + }); + + map.set(key, filtered); + }); + + return map; + } + + function buildCubePlacementsByPlanet(magickDataset) { + const map = new Map(); + const cube = magickDataset?.grouped?.kabbalah?.cube || {}; + const walls = Array.isArray(cube?.walls) + ? cube.walls + : []; + const edges = Array.isArray(cube?.edges) + ? cube.edges + : []; + + function edgeWalls(edge) { + const explicitWalls = Array.isArray(edge?.walls) + ? edge.walls.map((wallId) => String(wallId || "").trim().toLowerCase()).filter(Boolean) + : []; + + if (explicitWalls.length >= 2) { + return explicitWalls.slice(0, 2); + } + + return String(edge?.id || "") + .trim() + .toLowerCase() + .split("-") + .map((wallId) => wallId.trim()) + .filter(Boolean) + .slice(0, 2); + } + + function edgeLabel(edge) { + const explicitName = String(edge?.name || "").trim(); + if (explicitName) { + return explicitName; + } + return edgeWalls(edge) + .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) + .join(" "); + } + + function resolveCubeDirectionLabel(wallId, edge) { + const normalizedWallId = String(wallId || "").trim().toLowerCase(); + const edgeId = String(edge?.id || "").trim().toLowerCase(); + if (!normalizedWallId || !edgeId) { + return ""; + } + + const cubeUi = window.CubeSectionUi; + if (cubeUi && typeof cubeUi.getEdgeDirectionLabelForWall === "function") { + const directionLabel = String(cubeUi.getEdgeDirectionLabelForWall(normalizedWallId, edgeId) || "").trim(); + if (directionLabel) { + return directionLabel; + } + } + + return edgeLabel(edge); + } + + const firstEdgeByWallId = new Map(); + edges.forEach((edge) => { + edgeWalls(edge).forEach((wallId) => { + if (!firstEdgeByWallId.has(wallId)) { + firstEdgeByWallId.set(wallId, edge); + } + }); + }); + + function pushPlacement(planetId, placement) { + if (!planetId || !placement?.wallId || !placement?.edgeId) { + return; + } + + if (!map.has(planetId)) { + map.set(planetId, []); + } + + const rows = map.get(planetId); + const key = `${placement.wallId}:${placement.edgeId}`; + if (rows.some((row) => `${row.wallId}:${row.edgeId}` === key)) { + return; + } + + rows.push(placement); + } + + walls.forEach((wall) => { + const planetId = toPlanetId(wall?.associations?.planetId || wall?.planet); + if (!planetId) { + return; + } + + const wallId = String(wall?.id || "").trim().toLowerCase(); + const edge = firstEdgeByWallId.get(wallId) || null; + + pushPlacement(planetId, { + wallId, + edgeId: String(edge?.id || "").trim().toLowerCase(), + label: `Cube: ${wall?.name || "Wall"} Wall - ${resolveCubeDirectionLabel(wallId, edge) || "Direction"}` + }); + }); + + return map; + } + + function getElements() { + return { + planetCardListEl: document.getElementById("planet-card-list"), + planetSearchInputEl: document.getElementById("planet-search-input"), + planetSearchClearEl: document.getElementById("planet-search-clear"), + planetCountEl: document.getElementById("planet-card-count"), + planetDetailNameEl: document.getElementById("planet-detail-name"), + planetDetailTypeEl: document.getElementById("planet-detail-type"), + planetDetailSummaryEl: document.getElementById("planet-detail-summary"), + planetDetailFactsEl: document.getElementById("planet-detail-facts"), + planetDetailAtmosphereEl: document.getElementById("planet-detail-atmosphere"), + planetDetailNotableEl: document.getElementById("planet-detail-notable"), + planetDetailCorrespondenceEl: document.getElementById("planet-detail-correspondence") + }; + } + + function normalizeSearchValue(value) { + return String(value || "").trim().toLowerCase(); + } + + function buildPlanetSearchText(entry) { + const correspondence = entry?.correspondence || {}; + const factValues = buildFactRows(entry).map(([, value]) => String(value || "")); + const tarotAliases = correspondence?.tarot?.majorArcana && typeof getTarotCardSearchAliases === "function" + ? getTarotCardSearchAliases(correspondence.tarot.majorArcana) + : []; + const rawNumbers = [ + entry?.meanDistanceFromSun?.kmMillions, + entry?.meanDistanceFromSun?.au, + entry?.orbitalPeriod?.days, + entry?.orbitalPeriod?.years, + entry?.rotationPeriodHours, + entry?.radiusKm, + entry?.diameterKm, + entry?.massKg, + entry?.gravityMs2, + entry?.escapeVelocityKms, + entry?.axialTiltDeg, + entry?.averageTempC, + entry?.moons + ] + .filter((value) => Number.isFinite(value)) + .map((value) => String(value)); + + const parts = [ + entry?.name, + entry?.symbol, + entry?.classification, + entry?.summary, + entry?.atmosphere, + ...(Array.isArray(entry?.notableFacts) ? entry.notableFacts : []), + ...factValues, + ...rawNumbers, + correspondence?.name, + correspondence?.symbol, + correspondence?.weekday, + correspondence?.tarot?.majorArcana, + ...tarotAliases + ]; + + return normalizeSearchValue(parts.filter(Boolean).join(" ")); + } + + function applySearchFilter(elements) { + const query = normalizeSearchValue(state.searchQuery); + state.filteredEntries = query + ? state.entries.filter((entry) => buildPlanetSearchText(entry).includes(query)) + : [...state.entries]; + + if (elements?.planetSearchClearEl) { + elements.planetSearchClearEl.disabled = !query; + } + + renderList(elements); + + if (!state.filteredEntries.some((entry) => entry.id === state.selectedId)) { + if (state.filteredEntries.length > 0) { + selectById(state.filteredEntries[0].id, elements); + } + return; + } + + updateSelection(elements); + } + + function clearChildren(element) { + if (element) { + element.replaceChildren(); + } + } + + function toNumber(value) { + return Number.isFinite(value) ? value : null; + } + + function formatNumber(value, maximumFractionDigits = 2) { + if (!Number.isFinite(value)) { + return "--"; + } + + return new Intl.NumberFormat(undefined, { + maximumFractionDigits, + minimumFractionDigits: 0 + }).format(value); + } + + function formatSignedHours(value) { + if (!Number.isFinite(value)) { + return "--"; + } + + const absValue = Math.abs(value); + const formatted = `${formatNumber(absValue, 2)} h`; + return value < 0 ? `${formatted} (retrograde)` : formatted; + } + + function formatMass(value) { + if (!Number.isFinite(value)) { + return "--"; + } + + return value.toExponential(3).replace("e+", " × 10^") + " kg"; + } + + function buildFactRows(entry) { + return [ + ["Mean distance from Sun", Number.isFinite(entry?.meanDistanceFromSun?.kmMillions) && Number.isFinite(entry?.meanDistanceFromSun?.au) + ? `${formatNumber(entry.meanDistanceFromSun.kmMillions, 1)} million km (${formatNumber(entry.meanDistanceFromSun.au, 3)} AU)` + : "--"], + ["Orbital period", Number.isFinite(entry?.orbitalPeriod?.days) && Number.isFinite(entry?.orbitalPeriod?.years) + ? `${formatNumber(entry.orbitalPeriod.days, 2)} days (${formatNumber(entry.orbitalPeriod.years, 3)} years)` + : "--"], + ["Rotation period", formatSignedHours(toNumber(entry?.rotationPeriodHours))], + ["Radius", Number.isFinite(entry?.radiusKm) ? `${formatNumber(entry.radiusKm, 1)} km` : "--"], + ["Diameter", Number.isFinite(entry?.diameterKm) ? `${formatNumber(entry.diameterKm, 1)} km` : "--"], + ["Mass", formatMass(toNumber(entry?.massKg))], + ["Surface gravity", Number.isFinite(entry?.gravityMs2) ? `${formatNumber(entry.gravityMs2, 3)} m/s²` : "--"], + ["Escape velocity", Number.isFinite(entry?.escapeVelocityKms) ? `${formatNumber(entry.escapeVelocityKms, 2)} km/s` : "--"], + ["Axial tilt", Number.isFinite(entry?.axialTiltDeg) ? `${formatNumber(entry.axialTiltDeg, 2)}°` : "--"], + ["Average temperature", Number.isFinite(entry?.averageTempC) ? `${formatNumber(entry.averageTempC, 0)} °C` : "--"], + ["Moons", Number.isFinite(entry?.moons) ? formatNumber(entry.moons, 0) : "--"] + ]; + } + + function getDisplayTarotName(cardName, trumpNumber) { + if (!cardName) { + return ""; + } + + if (typeof getTarotCardDisplayName !== "function") { + return cardName; + } + + if (Number.isFinite(Number(trumpNumber))) { + return getTarotCardDisplayName(cardName, { trumpNumber: Number(trumpNumber) }) || cardName; + } + + return getTarotCardDisplayName(cardName) || cardName; + } + + function renderCorrespondence(entry, containerEl) { + if (!containerEl) return; + containerEl.innerHTML = ""; + + const correspondence = entry?.correspondence; + + if (!correspondence || typeof correspondence !== "object") { + const fallback = document.createElement("span"); + fallback.className = "planet-text"; + fallback.textContent = "No tarot/day correspondence in current local dataset."; + containerEl.appendChild(fallback); + } + + const symbol = correspondence?.symbol || ""; + const weekday = correspondence?.weekday || ""; + const arcana = correspondence?.tarot?.majorArcana || ""; + const trumpNo = correspondence?.tarot?.number; + const arcanaLabel = getDisplayTarotName(arcana, trumpNo); + + // Symbol + weekday line + if (symbol || weekday) { + const line = document.createElement("span"); + line.className = "planet-text"; + line.textContent = [symbol, weekday].filter(Boolean).join(" \u00b7 "); + containerEl.appendChild(line); + } + + // Tarot card link + if (arcana) { + const btn = document.createElement("button"); + btn.type = "button"; + btn.className = "kab-tarot-link"; + btn.style.marginTop = "8px"; + btn.textContent = trumpNo != null ? `${arcanaLabel} \u00b7 Trump ${trumpNo}` : arcanaLabel; + btn.title = "Open in Tarot section"; + btn.addEventListener("click", () => { + document.dispatchEvent(new CustomEvent("nav:tarot-trump", { + detail: { trumpNumber: trumpNo ?? null, cardName: arcana } + })); + }); + containerEl.appendChild(btn); + } + + const planetId = toPlanetId(correspondence?.id || entry?.id || entry?.name) || + normalizePlanetToken(correspondence?.id || entry?.id || entry?.name); + const kabbalahTargets = state.kabbalahTargetsByPlanetId[planetId] || []; + if (kabbalahTargets.length) { + const row = document.createElement("div"); + row.className = "kab-god-links"; + row.style.marginTop = "8px"; + + kabbalahTargets.forEach((target) => { + const btn = document.createElement("button"); + btn.type = "button"; + btn.className = "kab-god-link"; + btn.textContent = `View ${target.label} ↗`; + btn.addEventListener("click", () => { + document.dispatchEvent(new CustomEvent("nav:kabbalah-path", { + detail: { pathNo: Number(target.number) } + })); + }); + row.appendChild(btn); + }); + + containerEl.appendChild(row); + } + + const monthRefs = state.monthRefsByPlanetId.get(planetId) || []; + if (monthRefs.length) { + const meta = document.createElement("div"); + meta.className = "kab-god-meta"; + meta.textContent = "Calendar month correspondences"; + containerEl.appendChild(meta); + + const row = document.createElement("div"); + row.className = "kab-god-links"; + monthRefs.forEach((month) => { + const btn = document.createElement("button"); + btn.type = "button"; + btn.className = "kab-god-link"; + btn.textContent = `${month.label || month.name} ↗`; + btn.addEventListener("click", () => { + document.dispatchEvent(new CustomEvent("nav:calendar-month", { + detail: { monthId: month.id } + })); + }); + row.appendChild(btn); + }); + containerEl.appendChild(row); + } + + const cubePlacements = state.cubePlacementsByPlanetId.get(planetId) || []; + if (cubePlacements.length) { + const meta = document.createElement("div"); + meta.className = "kab-god-meta"; + meta.textContent = "Cube placements"; + containerEl.appendChild(meta); + + const row = document.createElement("div"); + row.className = "kab-god-links"; + cubePlacements.forEach((placement) => { + const btn = document.createElement("button"); + btn.type = "button"; + btn.className = "kab-god-link"; + btn.textContent = `${placement.label} ↗`; + btn.addEventListener("click", () => { + document.dispatchEvent(new CustomEvent("nav:cube", { + detail: { + planetId, + wallId: placement.wallId, + edgeId: placement.edgeId + } + })); + }); + row.appendChild(btn); + }); + containerEl.appendChild(row); + } + } + + function renderDetail(entry, elements) { + if (!entry || !elements) { + return; + } + + if (elements.planetDetailNameEl) { + const symbol = entry.symbol ? `${entry.symbol} ` : ""; + elements.planetDetailNameEl.textContent = `${symbol}${entry.name || "--"}`; + } + + if (elements.planetDetailTypeEl) { + elements.planetDetailTypeEl.textContent = entry.classification || "--"; + } + + if (elements.planetDetailSummaryEl) { + elements.planetDetailSummaryEl.textContent = entry.summary || "--"; + } + + clearChildren(elements.planetDetailFactsEl); + buildFactRows(entry).forEach(([label, value]) => { + const row = document.createElement("div"); + row.className = "planet-fact-row"; + + const labelEl = document.createElement("span"); + labelEl.className = "planet-fact-label"; + labelEl.textContent = label; + + const valueEl = document.createElement("span"); + valueEl.className = "planet-fact-value"; + valueEl.textContent = value; + + row.append(labelEl, valueEl); + elements.planetDetailFactsEl?.appendChild(row); + }); + + if (elements.planetDetailAtmosphereEl) { + elements.planetDetailAtmosphereEl.textContent = entry.atmosphere || "--"; + } + + clearChildren(elements.planetDetailNotableEl); + const notableFacts = Array.isArray(entry.notableFacts) ? entry.notableFacts : []; + notableFacts.forEach((fact) => { + const item = document.createElement("li"); + item.textContent = fact; + elements.planetDetailNotableEl?.appendChild(item); + }); + + if (elements.planetDetailCorrespondenceEl) { + renderCorrespondence(entry, elements.planetDetailCorrespondenceEl); + } + } + + function updateSelection(elements) { + if (!elements?.planetCardListEl) { + return; + } + + const buttons = elements.planetCardListEl.querySelectorAll(".planet-list-item"); + buttons.forEach((button) => { + const isSelected = button.dataset.planetId === state.selectedId; + button.classList.toggle("is-selected", isSelected); + button.setAttribute("aria-selected", isSelected ? "true" : "false"); + }); + } + + function selectById(id, elements) { + const entry = state.entries.find((planet) => planet.id === id); + if (!entry) { + return; + } + + state.selectedId = entry.id; + updateSelection(elements); + renderDetail(entry, elements); + } + + function renderList(elements) { + if (!elements?.planetCardListEl) { + return; + } + + clearChildren(elements.planetCardListEl); + + state.filteredEntries.forEach((entry) => { + const button = document.createElement("button"); + button.type = "button"; + button.className = "planet-list-item"; + button.dataset.planetId = entry.id; + button.setAttribute("role", "option"); + + const nameEl = document.createElement("span"); + nameEl.className = "planet-list-name"; + const symbol = entry.symbol ? `${entry.symbol} ` : ""; + nameEl.textContent = `${symbol}${entry.name || "--"}`; + + const metaEl = document.createElement("span"); + metaEl.className = "planet-list-meta"; + metaEl.textContent = entry.classification || "--"; + + button.append(nameEl, metaEl); + elements.planetCardListEl.appendChild(button); + }); + + if (elements.planetCountEl) { + elements.planetCountEl.textContent = state.searchQuery + ? `${state.filteredEntries.length} of ${state.entries.length} bodies` + : `${state.entries.length} bodies`; + } + } + + function ensurePlanetSection(referenceData, magickDataset = null) { + if (state.initialized) { + return; + } + + const elements = getElements(); + if (!elements.planetCardListEl || !elements.planetDetailNameEl) { + return; + } + + const baseList = Array.isArray(referenceData?.planetScience) + ? referenceData.planetScience + : []; + + if (baseList.length === 0) { + if (elements.planetDetailNameEl) { + elements.planetDetailNameEl.textContent = "Planet data unavailable"; + } + if (elements.planetDetailSummaryEl) { + elements.planetDetailSummaryEl.textContent = "Could not load local science facts dataset."; + } + return; + } + + const correspondences = referenceData?.planets && typeof referenceData.planets === "object" + ? referenceData.planets + : {}; + + const correspondenceByName = Object.values(correspondences).reduce((acc, value) => { + const key = String(value?.name || "").trim().toLowerCase(); + if (key) { + acc[key] = value; + } + return acc; + }, {}); + + state.kabbalahTargetsByPlanetId = buildKabbalahTargetsByPlanet(magickDataset); + state.monthRefsByPlanetId = buildMonthReferencesByPlanet(referenceData); + state.cubePlacementsByPlanetId = buildCubePlacementsByPlanet(magickDataset); + + state.entries = baseList.map((entry) => { + const byId = correspondences[entry.id] || null; + const byName = correspondenceByName[String(entry?.name || "").trim().toLowerCase()] || null; + return { + ...entry, + correspondence: byId || byName || null + }; + }); + state.filteredEntries = [...state.entries]; + + renderList(elements); + + if (state.entries.length > 0) { + selectById(state.entries[0].id, elements); + } + + elements.planetCardListEl.addEventListener("click", (event) => { + const target = event.target; + if (!(target instanceof Node)) { + return; + } + + const button = target instanceof Element + ? target.closest(".planet-list-item") + : null; + + if (!(button instanceof HTMLButtonElement)) { + return; + } + + const selectedId = button.dataset.planetId; + if (!selectedId) { + return; + } + + selectById(selectedId, elements); + }); + + if (elements.planetSearchInputEl) { + elements.planetSearchInputEl.addEventListener("input", () => { + state.searchQuery = elements.planetSearchInputEl.value || ""; + applySearchFilter(elements); + }); + } + + if (elements.planetSearchClearEl && elements.planetSearchInputEl) { + elements.planetSearchClearEl.addEventListener("click", () => { + elements.planetSearchInputEl.value = ""; + state.searchQuery = ""; + applySearchFilter(elements); + elements.planetSearchInputEl.focus(); + }); + } + + state.initialized = true; + } + + function selectByPlanetId(planetId) { + if (!state.initialized) return; + const el = getElements(); + const needle = String(planetId || "").toLowerCase(); + const entry = state.entries.find(e => + String(e.id || "").toLowerCase() === needle || + String(e.correspondence?.id || "").toLowerCase() === needle || + String(e.name || "").toLowerCase() === needle + ); + if (!entry) return; + selectById(entry.id, el); + el.planetCardListEl + ?.querySelector(`[data-planet-id="${entry.id}"]`) + ?.scrollIntoView({ block: "nearest" }); + } + + window.PlanetSectionUi = { + ensurePlanetSection, + selectByPlanetId + }; +})(); diff --git a/app/ui-quiz.js b/app/ui-quiz.js new file mode 100644 index 0000000..dce68fb --- /dev/null +++ b/app/ui-quiz.js @@ -0,0 +1,1484 @@ +/* ui-quiz.js — Correspondences quiz */ +(function () { + "use strict"; + + const state = { + initialized: false, + scoreCorrect: 0, + scoreAnswered: 0, + selectedCategory: "random", + selectedDifficulty: "normal", + questionBank: [], + templateByKey: new Map(), + runUnseenKeys: [], + runRetryKeys: [], + runRetrySet: new Set(), + currentQuestion: null, + answeredCurrent: false, + autoAdvanceTimer: null, + autoAdvanceDelayMs: 1500 + }; + + const FIXED_CATEGORY_OPTIONS = [ + { value: "random", label: "Random" }, + { value: "all", label: "All" } + ]; + + const CATEGORY_META = [ + { id: "english-gematria", label: "English Gematria" }, + { id: "hebrew-numerology", label: "Hebrew Gematria" }, + { id: "english-hebrew-mapping", label: "Alphabet Mapping" }, + { id: "zodiac-rulers", label: "Zodiac Rulers" }, + { id: "zodiac-elements", label: "Zodiac Elements" }, + { id: "planetary-weekdays", label: "Planetary Weekdays" }, + { id: "zodiac-tarot", label: "Zodiac ↔ Tarot" }, + { id: "kabbalah-path-between-sephirot", label: "Kabbalah: Path Between Sephirot" }, + { id: "kabbalah-path-letter", label: "Kabbalah: Path Number ↔ Letter" }, + { id: "kabbalah-path-tarot", label: "Kabbalah: Path ↔ Tarot Trump" }, + { id: "sephirot-planets", label: "Sephirot ↔ Planet" }, + { id: "tarot-decan-sign", label: "Tarot Decans: Card ↔ Decan" }, + { id: "tarot-decan-ruler", label: "Tarot Decans: Card ↔ Ruler" }, + { id: "tarot-cube-location", label: "Tarot ↔ Cube Location" }, + { id: "cube-hebrew-letter", label: "Cube ↔ Hebrew Letter" }, + { id: "playing-card-tarot", label: "Playing Card ↔ Tarot" } + ]; + + // Dynamic category plugin registry — populated by registerQuizCategory() + const DYNAMIC_CATEGORY_REGISTRY = []; + + function registerQuizCategory(id, label, builder) { + if (typeof id !== "string" || !id || typeof builder !== "function") { + return; + } + + const existingIndex = DYNAMIC_CATEGORY_REGISTRY.findIndex((entry) => entry.id === id); + if (existingIndex >= 0) { + DYNAMIC_CATEGORY_REGISTRY[existingIndex] = { id, label: String(label || id), builder }; + } else { + DYNAMIC_CATEGORY_REGISTRY.push({ id, label: String(label || id), builder }); + } + + if (!CATEGORY_META.some((item) => item.id === id)) { + CATEGORY_META.push({ id, label: String(label || id) }); + } + } + + let categoryEl; + let difficultyEl; + let questionTypeEl; + let questionEl; + let optionsEl; + let feedbackEl; + let resetEl; + let scoreCorrectEl; + let scoreAnsweredEl; + let scoreAccuracyEl; + + function getElements() { + categoryEl = document.getElementById("quiz-category"); + difficultyEl = document.getElementById("quiz-difficulty"); + questionTypeEl = document.getElementById("quiz-question-type"); + questionEl = document.getElementById("quiz-question"); + optionsEl = document.getElementById("quiz-options"); + feedbackEl = document.getElementById("quiz-feedback"); + resetEl = document.getElementById("quiz-reset"); + scoreCorrectEl = document.getElementById("quiz-score-correct"); + scoreAnsweredEl = document.getElementById("quiz-score-answered"); + scoreAccuracyEl = document.getElementById("quiz-score-accuracy"); + } + + function isReady() { + return Boolean( + categoryEl + && difficultyEl + && questionTypeEl + && questionEl + && optionsEl + && feedbackEl + && resetEl + && scoreCorrectEl + && scoreAnsweredEl + && scoreAccuracyEl + ); + } + + function clearAutoAdvanceTimer() { + if (!state.autoAdvanceTimer) { + return; + } + clearTimeout(state.autoAdvanceTimer); + state.autoAdvanceTimer = null; + } + + function queueAutoAdvance() { + clearAutoAdvanceTimer(); + state.autoAdvanceTimer = setTimeout(() => { + state.autoAdvanceTimer = null; + showNextQuestion(); + }, state.autoAdvanceDelayMs); + } + + function toTitleCase(value) { + const text = String(value || "").trim().toLowerCase(); + if (!text) { + return ""; + } + return text.charAt(0).toUpperCase() + text.slice(1); + } + + function normalizeOption(value) { + return String(value || "").trim(); + } + + function normalizeKey(value) { + return normalizeOption(value).toLowerCase(); + } + + function toUniqueOptionList(values) { + const seen = new Set(); + const unique = []; + + (values || []).forEach((value) => { + const formatted = normalizeOption(value); + if (!formatted) { + return; + } + + const key = normalizeKey(formatted); + if (seen.has(key)) { + return; + } + + seen.add(key); + unique.push(formatted); + }); + + return unique; + } + + function shuffle(list) { + const clone = list.slice(); + for (let index = clone.length - 1; index > 0; index -= 1) { + const nextIndex = Math.floor(Math.random() * (index + 1)); + [clone[index], clone[nextIndex]] = [clone[nextIndex], clone[index]]; + } + return clone; + } + + function pickMany(list, count) { + if (!Array.isArray(list) || !list.length || count <= 0) { + return []; + } + return shuffle(list).slice(0, Math.min(count, list.length)); + } + + function getActiveDifficulty() { + const value = String(state.selectedDifficulty || "normal").toLowerCase(); + if (value === "easy" || value === "hard") { + return value; + } + return "normal"; + } + + function resolveDifficultyValue(valueByDifficulty, difficulty = getActiveDifficulty()) { + if (valueByDifficulty == null) { + return ""; + } + + if (typeof valueByDifficulty !== "object" || Array.isArray(valueByDifficulty)) { + return valueByDifficulty; + } + + if (Object.prototype.hasOwnProperty.call(valueByDifficulty, difficulty)) { + return valueByDifficulty[difficulty]; + } + + if (Object.prototype.hasOwnProperty.call(valueByDifficulty, "normal")) { + return valueByDifficulty.normal; + } + + if (Object.prototype.hasOwnProperty.call(valueByDifficulty, "easy")) { + return valueByDifficulty.easy; + } + + if (Object.prototype.hasOwnProperty.call(valueByDifficulty, "hard")) { + return valueByDifficulty.hard; + } + + return ""; + } + + function buildOptions(correctValue, poolValues) { + const correct = normalizeOption(correctValue); + if (!correct) { + return null; + } + + const uniquePool = toUniqueOptionList(poolValues || []); + if (!uniquePool.some((value) => normalizeKey(value) === normalizeKey(correct))) { + uniquePool.push(correct); + } + + const distractors = uniquePool.filter((value) => normalizeKey(value) !== normalizeKey(correct)); + if (distractors.length < 3) { + return null; + } + + const selectedDistractors = pickMany(distractors, 3); + const options = shuffle([correct, ...selectedDistractors]); + const correctIndex = options.findIndex((value) => normalizeKey(value) === normalizeKey(correct)); + + if (correctIndex < 0 || options.length < 4) { + return null; + } + + return { + options, + correctIndex + }; + } + + function createQuestionTemplate(payload, poolValues) { + const key = String(payload?.key || "").trim(); + const promptByDifficulty = payload?.promptByDifficulty ?? payload?.prompt; + const answerByDifficulty = payload?.answerByDifficulty ?? payload?.answer; + const poolByDifficulty = poolValues; + const categoryId = String(payload?.categoryId || "").trim(); + const category = String(payload?.category || "Correspondence").trim(); + + const defaultPrompt = String(resolveDifficultyValue(promptByDifficulty, "normal") || "").trim(); + const defaultAnswer = normalizeOption(resolveDifficultyValue(answerByDifficulty, "normal")); + const defaultPool = toUniqueOptionList(resolveDifficultyValue(poolByDifficulty, "normal") || []); + + if (!key || !defaultPrompt || !defaultAnswer || !categoryId || !category) { + return null; + } + + if (!defaultPool.some((value) => normalizeKey(value) === normalizeKey(defaultAnswer))) { + defaultPool.push(defaultAnswer); + } + + const distractorCount = defaultPool.filter((value) => normalizeKey(value) !== normalizeKey(defaultAnswer)).length; + if (distractorCount < 3) { + return null; + } + + return { + key, + categoryId, + category, + promptByDifficulty, + answerByDifficulty, + poolByDifficulty + }; + } + + function instantiateQuestion(template) { + if (!template) { + return null; + } + + const prompt = String(resolveDifficultyValue(template.promptByDifficulty) || "").trim(); + const answer = normalizeOption(resolveDifficultyValue(template.answerByDifficulty)); + const pool = toUniqueOptionList(resolveDifficultyValue(template.poolByDifficulty) || []); + + if (!prompt || !answer) { + return null; + } + + const built = buildOptions(answer, pool); + if (!built) { + return null; + } + + return { + key: template.key, + categoryId: template.categoryId, + category: template.category, + prompt, + answer, + options: built.options, + correctIndex: built.correctIndex + }; + } + + function buildQuestionBank(referenceData, magickDataset) { + const grouped = magickDataset?.grouped || {}; + const alphabets = grouped.alphabets || {}; + const englishLetters = Array.isArray(alphabets?.english) ? alphabets.english : []; + const hebrewLetters = Array.isArray(alphabets?.hebrew) ? alphabets.hebrew : []; + const kabbalahTree = grouped?.kabbalah?.["kabbalah-tree"] || {}; + const treePaths = Array.isArray(kabbalahTree?.paths) ? kabbalahTree.paths : []; + const treeSephiroth = Array.isArray(kabbalahTree?.sephiroth) ? kabbalahTree.sephiroth : []; + const sephirotById = grouped?.kabbalah?.sephirot && typeof grouped.kabbalah.sephirot === "object" + ? grouped.kabbalah.sephirot + : {}; + const cube = grouped?.kabbalah?.cube && typeof grouped.kabbalah.cube === "object" + ? grouped.kabbalah.cube + : {}; + const cubeWalls = Array.isArray(cube?.walls) ? cube.walls : []; + const cubeEdges = Array.isArray(cube?.edges) ? cube.edges : []; + const cubeCenter = cube?.center && typeof cube.center === "object" ? cube.center : null; + const playingCardsData = grouped?.["playing-cards-52"]; + const playingCards = Array.isArray(playingCardsData) + ? playingCardsData + : (Array.isArray(playingCardsData?.entries) ? playingCardsData.entries : []); + const signs = Array.isArray(referenceData?.signs) ? referenceData.signs : []; + const planetsById = referenceData?.planets && typeof referenceData.planets === "object" + ? referenceData.planets + : {}; + const planets = Object.values(planetsById); + const decansBySign = referenceData?.decansBySign && typeof referenceData.decansBySign === "object" + ? referenceData.decansBySign + : {}; + + const normalizeId = (value) => String(value || "").trim().toLowerCase(); + + const toRomanNumeral = (value) => { + const numeric = Number(value); + if (!Number.isFinite(numeric) || numeric <= 0) { + return String(value || ""); + } + + const intValue = Math.trunc(numeric); + const lookup = [ + [1000, "M"], + [900, "CM"], + [500, "D"], + [400, "CD"], + [100, "C"], + [90, "XC"], + [50, "L"], + [40, "XL"], + [10, "X"], + [9, "IX"], + [5, "V"], + [4, "IV"], + [1, "I"] + ]; + + let current = intValue; + let result = ""; + lookup.forEach(([size, symbol]) => { + while (current >= size) { + result += symbol; + current -= size; + } + }); + + return result || String(intValue); + }; + + const labelFromId = (value) => { + const id = String(value || "").trim(); + if (!id) { + return ""; + } + return id + .replace(/[_-]+/g, " ") + .replace(/\s+/g, " ") + .trim() + .split(" ") + .map((part) => part ? part.charAt(0).toUpperCase() + part.slice(1) : "") + .join(" "); + }; + + const getPlanetLabelById = (planetId) => { + const normalized = normalizeId(planetId); + if (!normalized) { + return ""; + } + + const directPlanet = planetsById[normalized]; + if (directPlanet?.name) { + return directPlanet.name; + } + + if (normalized === "primum-mobile") { + return "Primum Mobile"; + } + if (normalized === "olam-yesodot") { + return "Earth / Elements"; + } + + return labelFromId(normalized); + }; + + const hebrewById = new Map( + hebrewLetters + .filter((entry) => entry?.hebrewLetterId) + .map((entry) => [normalizeId(entry.hebrewLetterId), entry]) + ); + + const formatHebrewLetterLabel = (entry, fallbackId = "") => { + if (entry?.name && entry?.char) { + return `${entry.name} (${entry.char})`; + } + if (entry?.name) { + return entry.name; + } + if (entry?.char) { + return entry.char; + } + return labelFromId(fallbackId); + }; + + const sephiraNameByNumber = new Map( + treeSephiroth + .filter((entry) => Number.isFinite(Number(entry?.number)) && entry?.name) + .map((entry) => [Math.trunc(Number(entry.number)), String(entry.name)]) + ); + + const sephiraNameById = new Map( + treeSephiroth + .filter((entry) => entry?.sephiraId && entry?.name) + .map((entry) => [normalizeId(entry.sephiraId), String(entry.name)]) + ); + + const getSephiraName = (numberValue, idValue) => { + const numberKey = Number(numberValue); + if (Number.isFinite(numberKey)) { + const byNumber = sephiraNameByNumber.get(Math.trunc(numberKey)); + if (byNumber) { + return byNumber; + } + } + + const byId = sephiraNameById.get(normalizeId(idValue)); + if (byId) { + return byId; + } + + if (Number.isFinite(numberKey)) { + return `Sephira ${Math.trunc(numberKey)}`; + } + + return labelFromId(idValue); + }; + + const formatPathLetter = (path) => { + const transliteration = String(path?.hebrewLetter?.transliteration || "").trim(); + const glyph = String(path?.hebrewLetter?.char || "").trim(); + + if (transliteration && glyph) { + return `${transliteration} (${glyph})`; + } + if (transliteration) { + return transliteration; + } + if (glyph) { + return glyph; + } + return ""; + }; + + const flattenDecans = Object.values(decansBySign) + .flatMap((entries) => (Array.isArray(entries) ? entries : [])); + + const signNameById = new Map( + signs + .filter((entry) => entry?.id && entry?.name) + .map((entry) => [normalizeId(entry.id), String(entry.name)]) + ); + + const formatDecanLabel = (decan) => { + const signName = signNameById.get(normalizeId(decan?.signId)) || labelFromId(decan?.signId); + const index = Number(decan?.index); + if (!signName || !Number.isFinite(index)) { + return ""; + } + return `${signName} Decan ${toRomanNumeral(index)}`; + }; + + const bank = []; + + const englishGematriaPool = englishLetters + .map((item) => (Number.isFinite(Number(item?.pythagorean)) ? String(item.pythagorean) : "")) + .filter(Boolean); + + const hebrewNumerologyPool = hebrewLetters + .map((item) => (Number.isFinite(Number(item?.numerology)) ? String(item.numerology) : "")) + .filter(Boolean); + + const hebrewNameAndCharPool = hebrewLetters + .filter((item) => item?.name && item?.char) + .map((item) => `${item.name} (${item.char})`); + + const hebrewCharPool = hebrewLetters + .map((item) => item?.char) + .filter(Boolean); + + const planetNamePool = planets + .map((planet) => planet?.name) + .filter(Boolean); + + const planetWeekdayPool = planets + .map((planet) => planet?.weekday) + .filter(Boolean); + + const zodiacElementPool = signs + .map((sign) => toTitleCase(sign?.element)) + .filter(Boolean); + + const zodiacTarotPool = signs + .map((sign) => sign?.tarot?.majorArcana) + .filter(Boolean); + + const pathNumberPool = toUniqueOptionList( + treePaths + .map((path) => { + const pathNo = Number(path?.pathNumber); + return Number.isFinite(pathNo) ? String(Math.trunc(pathNo)) : ""; + }) + ); + + const pathLetterPool = toUniqueOptionList(treePaths.map((path) => formatPathLetter(path))); + const pathTarotPool = toUniqueOptionList(treePaths.map((path) => normalizeOption(path?.tarot?.card))); + + const decanLabelPool = toUniqueOptionList(flattenDecans.map((decan) => formatDecanLabel(decan))); + const decanRulerPool = toUniqueOptionList( + flattenDecans.map((decan) => getPlanetLabelById(decan?.rulerPlanetId)) + ); + + const cubeWallLabelPool = toUniqueOptionList( + cubeWalls.map((wall) => `${String(wall?.name || labelFromId(wall?.id)).trim()} Wall`) + ); + + const cubeEdgeLabelPool = toUniqueOptionList( + cubeEdges.map((edge) => `${String(edge?.name || labelFromId(edge?.id)).trim()} Edge`) + ); + + const cubeLocationPool = toUniqueOptionList([ + ...cubeWallLabelPool, + ...cubeEdgeLabelPool, + "Center" + ]); + + const cubeHebrewLetterPool = toUniqueOptionList([ + ...cubeWalls.map((wall) => { + const hebrew = hebrewById.get(normalizeId(wall?.hebrewLetterId)); + return formatHebrewLetterLabel(hebrew, wall?.hebrewLetterId); + }), + ...cubeEdges.map((edge) => { + const hebrew = hebrewById.get(normalizeId(edge?.hebrewLetterId)); + return formatHebrewLetterLabel(hebrew, edge?.hebrewLetterId); + }), + formatHebrewLetterLabel(hebrewById.get(normalizeId(cubeCenter?.hebrewLetterId)), cubeCenter?.hebrewLetterId) + ]); + + const playingTarotPool = toUniqueOptionList( + playingCards.map((entry) => normalizeOption(entry?.tarotCard)) + ); + + englishLetters.forEach((entry) => { + if (!entry?.letter || !Number.isFinite(Number(entry?.pythagorean))) { + return; + } + + const template = createQuestionTemplate( + { + key: `english-gematria:${entry.letter}`, + categoryId: "english-gematria", + category: "English Gematria", + promptByDifficulty: `${entry.letter} has a simple gematria value of`, + answerByDifficulty: String(entry.pythagorean) + }, + englishGematriaPool + ); + + if (template) { + bank.push(template); + } + }); + + hebrewLetters.forEach((entry) => { + if (!entry?.name || !entry?.char || !Number.isFinite(Number(entry?.numerology))) { + return; + } + + const template = createQuestionTemplate( + { + key: `hebrew-number:${entry.hebrewLetterId || entry.name}`, + categoryId: "hebrew-numerology", + category: "Hebrew Gematria", + promptByDifficulty: { + easy: `${entry.name} (${entry.char}) has a gematria value of`, + normal: `${entry.name} (${entry.char}) has a gematria value of`, + hard: `${entry.char} has a gematria value of` + }, + answerByDifficulty: String(entry.numerology) + }, + hebrewNumerologyPool + ); + + if (template) { + bank.push(template); + } + }); + + englishLetters.forEach((entry) => { + if (!entry?.letter || !entry?.hebrewLetterId) { + return; + } + + const mappedHebrew = hebrewById.get(String(entry.hebrewLetterId)); + if (!mappedHebrew?.name || !mappedHebrew?.char) { + return; + } + + const template = createQuestionTemplate( + { + key: `english-hebrew:${entry.letter}`, + categoryId: "english-hebrew-mapping", + category: "Alphabet Mapping", + promptByDifficulty: { + easy: `${entry.letter} maps to which Hebrew letter`, + normal: `${entry.letter} maps to which Hebrew letter`, + hard: `${entry.letter} maps to which Hebrew glyph` + }, + answerByDifficulty: { + easy: `${mappedHebrew.name} (${mappedHebrew.char})`, + normal: `${mappedHebrew.name} (${mappedHebrew.char})`, + hard: mappedHebrew.char + } + }, + { + easy: hebrewNameAndCharPool, + normal: hebrewNameAndCharPool, + hard: hebrewCharPool + } + ); + + if (template) { + bank.push(template); + } + }); + + signs.forEach((entry) => { + if (!entry?.name || !entry?.rulingPlanetId) { + return; + } + + const rulerName = planetsById[String(entry.rulingPlanetId)]?.name; + if (!rulerName) { + return; + } + + const template = createQuestionTemplate( + { + key: `zodiac-ruler:${entry.id || entry.name}`, + categoryId: "zodiac-rulers", + category: "Zodiac Rulers", + promptByDifficulty: `${entry.name} is ruled by`, + answerByDifficulty: rulerName + }, + planetNamePool + ); + + if (template) { + bank.push(template); + } + }); + + signs.forEach((entry) => { + if (!entry?.name || !entry?.element) { + return; + } + + const template = createQuestionTemplate( + { + key: `zodiac-element:${entry.id || entry.name}`, + categoryId: "zodiac-elements", + category: "Zodiac Elements", + promptByDifficulty: `${entry.name} is`, + answerByDifficulty: toTitleCase(entry.element) + }, + zodiacElementPool + ); + + if (template) { + bank.push(template); + } + }); + + planets.forEach((entry) => { + if (!entry?.name || !entry?.weekday) { + return; + } + + const template = createQuestionTemplate( + { + key: `planet-weekday:${entry.id || entry.name}`, + categoryId: "planetary-weekdays", + category: "Planetary Weekdays", + promptByDifficulty: `${entry.name} corresponds to`, + answerByDifficulty: entry.weekday + }, + planetWeekdayPool + ); + + if (template) { + bank.push(template); + } + }); + + signs.forEach((entry) => { + if (!entry?.name || !entry?.tarot?.majorArcana) { + return; + } + + const template = createQuestionTemplate( + { + key: `zodiac-tarot:${entry.id || entry.name}`, + categoryId: "zodiac-tarot", + category: "Zodiac ↔ Tarot", + promptByDifficulty: `${entry.name} corresponds to`, + answerByDifficulty: entry.tarot.majorArcana + }, + zodiacTarotPool + ); + + if (template) { + bank.push(template); + } + }); + + treePaths.forEach((path) => { + const pathNo = Number(path?.pathNumber); + if (!Number.isFinite(pathNo)) { + return; + } + + const pathNumberLabel = String(Math.trunc(pathNo)); + const fromNo = Number(path?.connects?.from); + const toNo = Number(path?.connects?.to); + const fromName = getSephiraName(fromNo, path?.connectIds?.from); + const toName = getSephiraName(toNo, path?.connectIds?.to); + const pathLetter = formatPathLetter(path); + const tarotCard = normalizeOption(path?.tarot?.card); + + if (fromName && toName) { + const template = createQuestionTemplate( + { + key: `kabbalah-path-between:${pathNumberLabel}`, + categoryId: "kabbalah-path-between-sephirot", + category: "Kabbalah Paths", + promptByDifficulty: { + easy: `Which path is between ${fromName} and ${toName}`, + normal: `What path connects ${fromName} and ${toName}`, + hard: `${fromName} ↔ ${toName} is which path` + }, + answerByDifficulty: pathNumberLabel + }, + pathNumberPool + ); + + if (template) { + bank.push(template); + } + } + + if (pathLetter) { + const numberToLetterTemplate = createQuestionTemplate( + { + key: `kabbalah-path-letter:${pathNumberLabel}`, + categoryId: "kabbalah-path-letter", + category: "Kabbalah Paths", + promptByDifficulty: { + easy: `Which letter is on Path ${pathNumberLabel}`, + normal: `Path ${pathNumberLabel} carries which Hebrew letter`, + hard: `Letter on Path ${pathNumberLabel}` + }, + answerByDifficulty: pathLetter + }, + pathLetterPool + ); + + if (numberToLetterTemplate) { + bank.push(numberToLetterTemplate); + } + + const letterToNumberTemplate = createQuestionTemplate( + { + key: `kabbalah-letter-path-number:${pathNumberLabel}`, + categoryId: "kabbalah-path-letter", + category: "Kabbalah Paths", + promptByDifficulty: { + easy: `${pathLetter} belongs to which path`, + normal: `${pathLetter} corresponds to Path`, + hard: `${pathLetter} is on Path` + }, + answerByDifficulty: pathNumberLabel + }, + pathNumberPool + ); + + if (letterToNumberTemplate) { + bank.push(letterToNumberTemplate); + } + } + + if (tarotCard) { + const pathToTarotTemplate = createQuestionTemplate( + { + key: `kabbalah-path-tarot:${pathNumberLabel}`, + categoryId: "kabbalah-path-tarot", + category: "Kabbalah ↔ Tarot", + promptByDifficulty: { + easy: `Path ${pathNumberLabel} corresponds to which Tarot trump`, + normal: `Which Tarot trump is on Path ${pathNumberLabel}`, + hard: `Tarot trump on Path ${pathNumberLabel}` + }, + answerByDifficulty: tarotCard + }, + pathTarotPool + ); + + if (pathToTarotTemplate) { + bank.push(pathToTarotTemplate); + } + + const tarotToPathTemplate = createQuestionTemplate( + { + key: `tarot-trump-path:${pathNumberLabel}`, + categoryId: "kabbalah-path-tarot", + category: "Tarot ↔ Kabbalah", + promptByDifficulty: { + easy: `${tarotCard} is on which path`, + normal: `Which path corresponds to ${tarotCard}`, + hard: `${tarotCard} corresponds to Path` + }, + answerByDifficulty: pathNumberLabel + }, + pathNumberPool + ); + + if (tarotToPathTemplate) { + bank.push(tarotToPathTemplate); + } + } + }); + + Object.values(sephirotById).forEach((sephira) => { + const sephiraName = String(sephira?.name?.roman || sephira?.name?.en || "").trim(); + const planetLabel = getPlanetLabelById(sephira?.planetId); + if (!sephiraName || !planetLabel) { + return; + } + + const template = createQuestionTemplate( + { + key: `sephirot-planet:${normalizeId(sephira?.id || sephiraName)}`, + categoryId: "sephirot-planets", + category: "Sephirot ↔ Planet", + promptByDifficulty: { + easy: `${sephiraName} corresponds to which planet`, + normal: `Planetary correspondence of ${sephiraName}`, + hard: `${sephiraName} corresponds to` + }, + answerByDifficulty: planetLabel + }, + toUniqueOptionList(Object.values(sephirotById).map((entry) => getPlanetLabelById(entry?.planetId))) + ); + + if (template) { + bank.push(template); + } + }); + + flattenDecans.forEach((decan) => { + const decanId = String(decan?.id || "").trim(); + const card = normalizeOption(decan?.tarotMinorArcana); + const decanLabel = formatDecanLabel(decan); + const rulerLabel = getPlanetLabelById(decan?.rulerPlanetId); + + if (!decanId || !card) { + return; + } + + if (decanLabel) { + const template = createQuestionTemplate( + { + key: `tarot-decan-sign:${decanId}`, + categoryId: "tarot-decan-sign", + category: "Tarot Decans", + promptByDifficulty: { + easy: `${card} belongs to which decan`, + normal: `Which decan contains ${card}`, + hard: `${card} is in` + }, + answerByDifficulty: decanLabel + }, + decanLabelPool + ); + + if (template) { + bank.push(template); + } + } + + if (rulerLabel) { + const template = createQuestionTemplate( + { + key: `tarot-decan-ruler:${decanId}`, + categoryId: "tarot-decan-ruler", + category: "Tarot Decans", + promptByDifficulty: { + easy: `The decan of ${card} is ruled by`, + normal: `Who rules the decan for ${card}`, + hard: `${card} decan ruler` + }, + answerByDifficulty: rulerLabel + }, + decanRulerPool + ); + + if (template) { + bank.push(template); + } + } + }); + + cubeWalls.forEach((wall) => { + const wallName = String(wall?.name || labelFromId(wall?.id)).trim(); + const wallLabel = wallName ? `${wallName} Wall` : ""; + const tarotCard = normalizeOption(wall?.associations?.tarotCard); + const hebrew = hebrewById.get(normalizeId(wall?.hebrewLetterId)); + const hebrewLabel = formatHebrewLetterLabel(hebrew, wall?.hebrewLetterId); + + if (tarotCard && wallLabel) { + const template = createQuestionTemplate( + { + key: `tarot-cube-wall:${normalizeId(wall?.id || wallName)}`, + categoryId: "tarot-cube-location", + category: "Tarot ↔ Cube", + promptByDifficulty: { + easy: `${tarotCard} is on which Cube wall`, + normal: `Where is ${tarotCard} on the Cube`, + hard: `${tarotCard} location on Cube` + }, + answerByDifficulty: wallLabel + }, + cubeLocationPool + ); + + if (template) { + bank.push(template); + } + } + + if (wallLabel && hebrewLabel) { + const template = createQuestionTemplate( + { + key: `cube-wall-letter:${normalizeId(wall?.id || wallName)}`, + categoryId: "cube-hebrew-letter", + category: "Cube ↔ Hebrew", + promptByDifficulty: { + easy: `${wallLabel} corresponds to which Hebrew letter`, + normal: `Which Hebrew letter is on ${wallLabel}`, + hard: `${wallLabel} letter` + }, + answerByDifficulty: hebrewLabel + }, + cubeHebrewLetterPool + ); + + if (template) { + bank.push(template); + } + } + }); + + cubeEdges.forEach((edge) => { + const edgeName = String(edge?.name || labelFromId(edge?.id)).trim(); + const edgeLabel = edgeName ? `${edgeName} Edge` : ""; + const hebrew = hebrewById.get(normalizeId(edge?.hebrewLetterId)); + const hebrewLabel = formatHebrewLetterLabel(hebrew, edge?.hebrewLetterId); + const tarotCard = normalizeOption(hebrew?.tarot?.card); + + if (tarotCard && edgeLabel) { + const template = createQuestionTemplate( + { + key: `tarot-cube-edge:${normalizeId(edge?.id || edgeName)}`, + categoryId: "tarot-cube-location", + category: "Tarot ↔ Cube", + promptByDifficulty: { + easy: `${tarotCard} is on which Cube edge`, + normal: `Where is ${tarotCard} on the Cube edges`, + hard: `${tarotCard} edge location` + }, + answerByDifficulty: edgeLabel + }, + cubeLocationPool + ); + + if (template) { + bank.push(template); + } + } + + if (edgeLabel && hebrewLabel) { + const template = createQuestionTemplate( + { + key: `cube-edge-letter:${normalizeId(edge?.id || edgeName)}`, + categoryId: "cube-hebrew-letter", + category: "Cube ↔ Hebrew", + promptByDifficulty: { + easy: `${edgeLabel} corresponds to which Hebrew letter`, + normal: `Which Hebrew letter is on ${edgeLabel}`, + hard: `${edgeLabel} letter` + }, + answerByDifficulty: hebrewLabel + }, + cubeHebrewLetterPool + ); + + if (template) { + bank.push(template); + } + } + }); + + if (cubeCenter) { + const centerTarot = normalizeOption(cubeCenter?.associations?.tarotCard || cubeCenter?.tarotCard); + const centerHebrew = hebrewById.get(normalizeId(cubeCenter?.hebrewLetterId)); + const centerHebrewLabel = formatHebrewLetterLabel(centerHebrew, cubeCenter?.hebrewLetterId); + + if (centerTarot) { + const template = createQuestionTemplate( + { + key: "tarot-cube-center", + categoryId: "tarot-cube-location", + category: "Tarot ↔ Cube", + promptByDifficulty: { + easy: `${centerTarot} is located at which Cube position`, + normal: `Where is ${centerTarot} on the Cube`, + hard: `${centerTarot} Cube location` + }, + answerByDifficulty: "Center" + }, + cubeLocationPool + ); + + if (template) { + bank.push(template); + } + } + + if (centerHebrewLabel) { + const template = createQuestionTemplate( + { + key: "cube-center-letter", + categoryId: "cube-hebrew-letter", + category: "Cube ↔ Hebrew", + promptByDifficulty: { + easy: "The Cube center corresponds to which Hebrew letter", + normal: "Which Hebrew letter is at the Cube center", + hard: "Cube center letter" + }, + answerByDifficulty: centerHebrewLabel + }, + cubeHebrewLetterPool + ); + + if (template) { + bank.push(template); + } + } + } + + playingCards.forEach((entry) => { + const cardId = String(entry?.id || "").trim(); + const rankLabel = normalizeOption(entry?.rankLabel || entry?.rank); + const suitLabel = normalizeOption(entry?.suitLabel || labelFromId(entry?.suit)); + const tarotCard = normalizeOption(entry?.tarotCard); + + if (!cardId || !rankLabel || !suitLabel || !tarotCard) { + return; + } + + const template = createQuestionTemplate( + { + key: `playing-card-tarot:${cardId}`, + categoryId: "playing-card-tarot", + category: "Playing Card ↔ Tarot", + promptByDifficulty: { + easy: `${rankLabel} of ${suitLabel} maps to which Tarot card`, + normal: `${rankLabel} of ${suitLabel} corresponds to`, + hard: `${rankLabel} of ${suitLabel} maps to` + }, + answerByDifficulty: tarotCard + }, + playingTarotPool + ); + + if (template) { + bank.push(template); + } + }); + + // Dynamic plugin categories + DYNAMIC_CATEGORY_REGISTRY.forEach(({ builder }) => { + try { + const dynamicTemplates = builder(referenceData, magickDataset); + if (Array.isArray(dynamicTemplates)) { + dynamicTemplates.forEach((t) => { + if (t) { + bank.push(t); + } + }); + } + } catch (_e) { + // skip broken plugins silently + } + }); + + return bank; + } + + function refreshQuestionBank(referenceData, magickDataset) { + state.questionBank = buildQuestionBank(referenceData, magickDataset); + state.templateByKey = new Map(state.questionBank.map((template) => [template.key, template])); + + const hasTemplate = (key) => state.templateByKey.has(key); + state.runUnseenKeys = state.runUnseenKeys.filter(hasTemplate); + state.runRetryKeys = state.runRetryKeys.filter(hasTemplate); + state.runRetrySet = new Set(state.runRetryKeys); + + if (state.currentQuestion && !state.templateByKey.has(state.currentQuestion.key)) { + state.currentQuestion = null; + state.answeredCurrent = true; + } + } + + function getScopedTemplates() { + if (!state.questionBank.length) { + return []; + } + + const mode = state.selectedCategory || "random"; + if (mode === "random" || mode === "all") { + return state.questionBank.slice(); + } + + return state.questionBank.filter((template) => template.categoryId === mode); + } + + function getCategoryOptions() { + const availableCategoryIds = new Set(state.questionBank.map((template) => template.categoryId)); + const dynamic = CATEGORY_META + .filter((item) => availableCategoryIds.has(item.id)) + .map((item) => ({ value: item.id, label: item.label })); + + return [...FIXED_CATEGORY_OPTIONS, ...dynamic]; + } + + function renderCategoryOptions() { + if (!categoryEl) { + return false; + } + + const options = getCategoryOptions(); + const preservedValue = state.selectedCategory; + categoryEl.innerHTML = ""; + + options.forEach((optionDef) => { + const option = document.createElement("option"); + option.value = optionDef.value; + option.textContent = optionDef.label; + categoryEl.appendChild(option); + }); + + const validValues = new Set(options.map((optionDef) => optionDef.value)); + const nextSelected = validValues.has(preservedValue) ? preservedValue : "random"; + state.selectedCategory = nextSelected; + categoryEl.value = nextSelected; + return nextSelected !== preservedValue; + } + + function syncDifficultyControl() { + if (!difficultyEl) { + return; + } + + const difficulty = getActiveDifficulty(); + state.selectedDifficulty = difficulty; + difficultyEl.value = difficulty; + } + + function getRunLabel() { + const mode = state.selectedCategory || "random"; + if (mode === "random") { + return "Random"; + } + if (mode === "all") { + return "All"; + } + return CATEGORY_META.find((item) => item.id === mode)?.label || "Category"; + } + + function startRun(resetScore = false) { + clearAutoAdvanceTimer(); + + if (resetScore) { + state.scoreCorrect = 0; + state.scoreAnswered = 0; + } + + const templates = getScopedTemplates(); + const mode = state.selectedCategory || "random"; + + if (!templates.length) { + state.runUnseenKeys = []; + state.runRetryKeys = []; + state.runRetrySet = new Set(); + state.currentQuestion = null; + state.answeredCurrent = true; + updateScoreboard(); + renderNoQuestionState(); + return; + } + + let orderedTemplates; + if (mode === "all") { + orderedTemplates = templates + .slice() + .sort((left, right) => { + const leftCategory = String(left.category || ""); + const rightCategory = String(right.category || ""); + if (leftCategory === rightCategory) { + return String(left.key || "").localeCompare(String(right.key || "")); + } + return leftCategory.localeCompare(rightCategory); + }); + } else { + orderedTemplates = shuffle(templates); + } + + state.runUnseenKeys = orderedTemplates.map((template) => template.key); + state.runRetryKeys = []; + state.runRetrySet = new Set(); + state.currentQuestion = null; + state.answeredCurrent = true; + + updateScoreboard(); + showNextQuestion(); + } + + function popNextTemplateFromRun() { + while (state.runUnseenKeys.length) { + const key = state.runUnseenKeys.shift(); + const template = state.templateByKey.get(key); + if (template) { + return template; + } + } + + while (state.runRetryKeys.length) { + const key = state.runRetryKeys.shift(); + state.runRetrySet.delete(key); + const template = state.templateByKey.get(key); + if (template) { + return template; + } + } + + return null; + } + + function renderRunCompleteState() { + state.currentQuestion = null; + state.answeredCurrent = true; + questionTypeEl.textContent = getRunLabel(); + questionEl.textContent = "Run complete. All previously missed questions are now correct."; + optionsEl.innerHTML = ""; + feedbackEl.textContent = "Change category/difficulty or press Reset Score to start a new run."; + } + + function updateScoreboard() { + if (!isReady()) { + return; + } + + const answered = state.scoreAnswered; + const correct = state.scoreCorrect; + const accuracy = answered > 0 + ? `${Math.round((correct / answered) * 100)}%` + : "0%"; + + scoreCorrectEl.textContent = String(correct); + scoreAnsweredEl.textContent = String(answered); + scoreAccuracyEl.textContent = accuracy; + } + + function renderQuestion(question) { + if (!isReady()) { + return; + } + + state.currentQuestion = question; + state.answeredCurrent = false; + + questionTypeEl.textContent = question.category; + questionEl.textContent = question.prompt; + feedbackEl.textContent = "Choose the best answer."; + + optionsEl.innerHTML = ""; + question.options.forEach((optionText, index) => { + const button = document.createElement("button"); + button.type = "button"; + button.className = "quiz-option"; + button.textContent = `${String.fromCharCode(65 + index)}. ${optionText}`; + button.dataset.optionIndex = String(index); + button.addEventListener("click", () => { + submitAnswer(index); + }); + optionsEl.appendChild(button); + }); + } + + function renderNoQuestionState() { + if (!isReady()) { + return; + } + + state.currentQuestion = null; + state.answeredCurrent = true; + + questionTypeEl.textContent = getRunLabel(); + questionEl.textContent = "Not enough variation for this category yet."; + optionsEl.innerHTML = ""; + feedbackEl.textContent = "Try Random/All or switch to another category."; + } + + function showNextQuestion() { + clearAutoAdvanceTimer(); + + const totalPending = state.runUnseenKeys.length + state.runRetryKeys.length; + if (totalPending <= 0) { + if (state.questionBank.length) { + renderRunCompleteState(); + } else { + renderNoQuestionState(); + } + return; + } + + const maxAttempts = totalPending + 1; + for (let index = 0; index < maxAttempts; index += 1) { + const template = popNextTemplateFromRun(); + if (!template) { + continue; + } + + const question = instantiateQuestion(template); + if (question) { + renderQuestion(question); + return; + } + } + + renderNoQuestionState(); + } + + function submitAnswer(optionIndex) { + if (!state.currentQuestion || state.answeredCurrent) { + return; + } + + state.answeredCurrent = true; + state.scoreAnswered += 1; + + const isCorrect = optionIndex === state.currentQuestion.correctIndex; + if (isCorrect) { + state.scoreCorrect += 1; + } + + const optionButtons = optionsEl.querySelectorAll(".quiz-option"); + optionButtons.forEach((button, index) => { + button.disabled = true; + if (index === state.currentQuestion.correctIndex) { + button.classList.add("is-correct"); + } else if (index === optionIndex) { + button.classList.add("is-wrong"); + } + }); + + if (isCorrect) { + feedbackEl.textContent = "Correct. Next question incoming…"; + } else { + feedbackEl.textContent = `Not quite. Correct answer: ${state.currentQuestion.answer}. Next question incoming…`; + const failedKey = state.currentQuestion.key; + if (!state.runRetrySet.has(failedKey)) { + state.runRetrySet.add(failedKey); + state.runRetryKeys.push(failedKey); + } + } + + updateScoreboard(); + queueAutoAdvance(); + } + + function bindEvents() { + if (!isReady()) { + return; + } + + categoryEl.addEventListener("change", () => { + state.selectedCategory = String(categoryEl.value || "random"); + startRun(true); + }); + + difficultyEl.addEventListener("change", () => { + state.selectedDifficulty = String(difficultyEl.value || "normal").toLowerCase(); + syncDifficultyControl(); + startRun(true); + }); + + resetEl.addEventListener("click", () => { + startRun(true); + }); + } + + function ensureQuizSection(referenceData, magickDataset) { + ensureQuizSection._referenceData = referenceData; + ensureQuizSection._magickDataset = magickDataset; + + if (!state.initialized) { + getElements(); + if (!isReady()) { + return; + } + + bindEvents(); + state.initialized = true; + updateScoreboard(); + } + + refreshQuestionBank(referenceData, magickDataset); + const categoryAdjusted = renderCategoryOptions(); + syncDifficultyControl(); + + if (categoryAdjusted) { + startRun(false); + return; + } + + const hasRunPending = state.runUnseenKeys.length > 0 || state.runRetryKeys.length > 0; + if (!state.currentQuestion && !hasRunPending) { + startRun(false); + return; + } + + if (!state.currentQuestion && hasRunPending) { + showNextQuestion(); + } + + updateScoreboard(); + } + + window.QuizSectionUi = { + ensureQuizSection, + registerQuizCategory + }; +})(); diff --git a/app/ui-tarot.js b/app/ui-tarot.js new file mode 100644 index 0000000..24f454a --- /dev/null +++ b/app/ui-tarot.js @@ -0,0 +1,2314 @@ +(function () { + const { resolveTarotCardImage, getTarotCardDisplayName, getTarotCardSearchAliases } = window.TarotCardImages || {}; + + const state = { + initialized: false, + cards: [], + filteredCards: [], + searchQuery: "", + selectedCardId: "", + magickDataset: null, + referenceData: null, + monthRefsByCardId: new Map(), + courtCardByDecanId: new Map() + }; + + let tarotLightboxOverlayEl = null; + let tarotLightboxImageEl = null; + let tarotLightboxZoomed = false; + + const LIGHTBOX_ZOOM_SCALE = 6.66; + + function resetTarotLightboxZoom() { + if (!tarotLightboxImageEl) { + return; + } + + tarotLightboxZoomed = false; + tarotLightboxImageEl.style.transform = "scale(1)"; + tarotLightboxImageEl.style.transformOrigin = "center center"; + tarotLightboxImageEl.style.cursor = "zoom-in"; + } + + function updateTarotLightboxZoomOrigin(clientX, clientY) { + if (!tarotLightboxZoomed || !tarotLightboxImageEl) { + return; + } + + const rect = tarotLightboxImageEl.getBoundingClientRect(); + if (!rect.width || !rect.height) { + return; + } + + const x = Math.min(100, Math.max(0, ((clientX - rect.left) / rect.width) * 100)); + const y = Math.min(100, Math.max(0, ((clientY - rect.top) / rect.height) * 100)); + tarotLightboxImageEl.style.transformOrigin = `${x}% ${y}%`; + } + + function isTarotLightboxPointOnCard(clientX, clientY) { + if (!tarotLightboxImageEl) { + return false; + } + + const rect = tarotLightboxImageEl.getBoundingClientRect(); + const naturalWidth = tarotLightboxImageEl.naturalWidth; + const naturalHeight = tarotLightboxImageEl.naturalHeight; + + if (!rect.width || !rect.height || !naturalWidth || !naturalHeight) { + return true; + } + + const frameAspect = rect.width / rect.height; + const imageAspect = naturalWidth / naturalHeight; + + let renderWidth = rect.width; + let renderHeight = rect.height; + if (imageAspect > frameAspect) { + renderHeight = rect.width / imageAspect; + } else { + renderWidth = rect.height * imageAspect; + } + + const left = rect.left + (rect.width - renderWidth) / 2; + const top = rect.top + (rect.height - renderHeight) / 2; + const right = left + renderWidth; + const bottom = top + renderHeight; + + return clientX >= left && clientX <= right && clientY >= top && clientY <= bottom; + } + + function ensureTarotImageLightbox() { + if (tarotLightboxOverlayEl && tarotLightboxImageEl) { + return; + } + + tarotLightboxOverlayEl = document.createElement("div"); + tarotLightboxOverlayEl.setAttribute("aria-hidden", "true"); + tarotLightboxOverlayEl.style.position = "fixed"; + tarotLightboxOverlayEl.style.inset = "0"; + tarotLightboxOverlayEl.style.background = "rgba(0, 0, 0, 0.82)"; + tarotLightboxOverlayEl.style.display = "none"; + tarotLightboxOverlayEl.style.alignItems = "center"; + tarotLightboxOverlayEl.style.justifyContent = "center"; + tarotLightboxOverlayEl.style.zIndex = "9999"; + tarotLightboxOverlayEl.style.padding = "0"; + + const image = document.createElement("img"); + image.alt = "Tarot card enlarged image"; + image.style.maxWidth = "100vw"; + image.style.maxHeight = "100vh"; + image.style.width = "100vw"; + image.style.height = "100vh"; + image.style.objectFit = "contain"; + image.style.borderRadius = "0"; + image.style.boxShadow = "none"; + image.style.border = "none"; + image.style.cursor = "zoom-in"; + image.style.transform = "scale(1)"; + image.style.transformOrigin = "center center"; + image.style.transition = "transform 120ms ease-out"; + image.style.userSelect = "none"; + + tarotLightboxImageEl = image; + tarotLightboxOverlayEl.appendChild(image); + + const closeLightbox = () => { + if (!tarotLightboxOverlayEl || !tarotLightboxImageEl) { + return; + } + tarotLightboxOverlayEl.style.display = "none"; + tarotLightboxOverlayEl.setAttribute("aria-hidden", "true"); + tarotLightboxImageEl.removeAttribute("src"); + resetTarotLightboxZoom(); + }; + + tarotLightboxOverlayEl.addEventListener("click", (event) => { + if (event.target === tarotLightboxOverlayEl) { + closeLightbox(); + } + }); + + tarotLightboxImageEl.addEventListener("click", (event) => { + event.stopPropagation(); + if (!isTarotLightboxPointOnCard(event.clientX, event.clientY)) { + closeLightbox(); + return; + } + if (!tarotLightboxZoomed) { + tarotLightboxZoomed = true; + tarotLightboxImageEl.style.transform = `scale(${LIGHTBOX_ZOOM_SCALE})`; + tarotLightboxImageEl.style.cursor = "zoom-out"; + updateTarotLightboxZoomOrigin(event.clientX, event.clientY); + return; + } + resetTarotLightboxZoom(); + }); + + tarotLightboxImageEl.addEventListener("mousemove", (event) => { + updateTarotLightboxZoomOrigin(event.clientX, event.clientY); + }); + + tarotLightboxImageEl.addEventListener("mouseleave", () => { + if (tarotLightboxZoomed) { + tarotLightboxImageEl.style.transformOrigin = "center center"; + } + }); + + document.addEventListener("keydown", (event) => { + if (event.key === "Escape") { + closeLightbox(); + } + }); + + document.body.appendChild(tarotLightboxOverlayEl); + } + + function openTarotImageLightbox(src, altText) { + if (!src) { + return; + } + + ensureTarotImageLightbox(); + if (!tarotLightboxOverlayEl || !tarotLightboxImageEl) { + return; + } + + tarotLightboxImageEl.src = src; + tarotLightboxImageEl.alt = altText || "Tarot card enlarged image"; + resetTarotLightboxZoom(); + tarotLightboxOverlayEl.style.display = "flex"; + tarotLightboxOverlayEl.setAttribute("aria-hidden", "false"); + } + + const TAROT_TRUMP_NUMBER_BY_NAME = { + "the fool": 0, + fool: 0, + "the magus": 1, + magus: 1, + magician: 1, + "the high priestess": 2, + "high priestess": 2, + "the empress": 3, + empress: 3, + "the emperor": 4, + emperor: 4, + "the hierophant": 5, + hierophant: 5, + "the lovers": 6, + lovers: 6, + "the chariot": 7, + chariot: 7, + strength: 8, + lust: 8, + "the hermit": 9, + hermit: 9, + fortune: 10, + "wheel of fortune": 10, + justice: 11, + "the hanged man": 12, + "hanged man": 12, + death: 13, + temperance: 14, + art: 14, + "the devil": 15, + devil: 15, + "the tower": 16, + tower: 16, + "the star": 17, + star: 17, + "the moon": 18, + moon: 18, + "the sun": 19, + sun: 19, + aeon: 20, + judgement: 20, + judgment: 20, + universe: 21, + world: 21, + "the world": 21 + }; + + const MINOR_NUMBER_WORD_BY_VALUE = { + 1: "ace", + 2: "two", + 3: "three", + 4: "four", + 5: "five", + 6: "six", + 7: "seven", + 8: "eight", + 9: "nine", + 10: "ten" + }; + + const MINOR_TITLE_WORD_BY_VALUE = { + 1: "Ace", + 2: "Two", + 3: "Three", + 4: "Four", + 5: "Five", + 6: "Six", + 7: "Seven", + 8: "Eight", + 9: "Nine", + 10: "Ten" + }; + + const HOUSE_MINOR_NUMBER_BANDS = [ + [2, 3, 4], + [5, 6, 7], + [8, 9, 10], + [2, 3, 4], + [5, 6, 7], + [8, 9, 10] + ]; + const HOUSE_LEFT_SUITS = ["Wands", "Disks", "Swords", "Cups", "Wands", "Disks"]; + const HOUSE_RIGHT_SUITS = ["Swords", "Cups", "Wands", "Disks", "Swords", "Cups"]; + const HOUSE_MIDDLE_SUITS = ["Wands", "Cups", "Swords", "Disks"]; + const HOUSE_MIDDLE_RANKS = ["Ace", "Knight", "Queen", "Prince", "Princess"]; + const HOUSE_TRUMP_ROWS = [ + [0], + [20, 21, 12], + [19, 10, 2, 1, 3, 16], + [18, 17, 15, 14, 13, 9, 8, 7, 6, 5, 4], + [11] + ]; + + const HEBREW_LETTER_ALIASES = { + aleph: "alef", + alef: "alef", + heh: "he", + he: "he", + beth: "bet", + bet: "bet", + cheth: "het", + chet: "het", + kaph: "kaf", + kaf: "kaf", + peh: "pe", + tzaddi: "tsadi", + tzadi: "tsadi", + tsadi: "tsadi", + qoph: "qof", + qof: "qof", + taw: "tav", + tau: "tav" + }; + + const CUBE_MOTHER_CONNECTOR_BY_LETTER = { + alef: { connectorId: "above-below", connectorName: "Above ↔ Below" }, + mem: { connectorId: "east-west", connectorName: "East ↔ West" }, + shin: { connectorId: "south-north", connectorName: "South ↔ North" } + }; + + const ELEMENT_NAME_BY_ID = { + water: "Water", + fire: "Fire", + air: "Air", + earth: "Earth" + }; + + const ELEMENT_HEBREW_LETTER_BY_ID = { + fire: "Yod", + water: "Heh", + air: "Vav", + earth: "Heh" + }; + + const ELEMENT_HEBREW_CHAR_BY_ID = { + fire: "י", + water: "ה", + air: "ו", + earth: "ה" + }; + + const HEBREW_LETTER_ID_BY_TETRAGRAMMATON_LETTER = { + yod: "yod", + heh: "he", + vav: "vav" + }; + + const ACE_ELEMENT_BY_CARD_NAME = { + "ace of cups": "water", + "ace of wands": "fire", + "ace of swords": "air", + "ace of disks": "earth" + }; + + const COURT_ELEMENT_BY_RANK = { + knight: "fire", + queen: "water", + prince: "air", + princess: "earth" + }; + + const SUIT_ELEMENT_BY_SUIT = { + wands: "fire", + cups: "water", + swords: "air", + disks: "earth" + }; + + const MINOR_RANK_NUMBER_BY_NAME = { + ace: 1, + two: 2, + three: 3, + four: 4, + five: 5, + six: 6, + seven: 7, + eight: 8, + nine: 9, + ten: 10 + }; + + const SMALL_CARD_SIGN_BY_MODALITY_AND_SUIT = { + cardinal: { + wands: "aries", + cups: "cancer", + swords: "libra", + disks: "capricorn" + }, + fixed: { + wands: "leo", + cups: "scorpio", + swords: "aquarius", + disks: "taurus" + }, + mutable: { + wands: "sagittarius", + cups: "pisces", + swords: "gemini", + disks: "virgo" + } + }; + + function slugify(value) { + return String(value || "") + .trim() + .toLowerCase() + .replace(/[^a-z0-9]+/g, "-") + .replace(/(^-|-$)/g, ""); + } + + function cardId(card) { + const suitPart = card.suit ? `-${slugify(card.suit)}` : ""; + return `${slugify(card.arcana)}${suitPart}-${slugify(card.name)}`; + } + + function getElements() { + return { + tarotCardListEl: document.getElementById("tarot-card-list"), + tarotSearchInputEl: document.getElementById("tarot-search-input"), + tarotSearchClearEl: document.getElementById("tarot-search-clear"), + tarotCountEl: document.getElementById("tarot-card-count"), + tarotDetailImageEl: document.getElementById("tarot-detail-image"), + tarotDetailNameEl: document.getElementById("tarot-detail-name"), + tarotDetailTypeEl: document.getElementById("tarot-detail-type"), + tarotDetailSummaryEl: document.getElementById("tarot-detail-summary"), + tarotDetailUprightEl: document.getElementById("tarot-detail-upright"), + tarotDetailReversedEl: document.getElementById("tarot-detail-reversed"), + tarotMetaMeaningCardEl: document.getElementById("tarot-meta-meaning-card"), + tarotDetailMeaningEl: document.getElementById("tarot-detail-meaning"), + tarotDetailKeywordsEl: document.getElementById("tarot-detail-keywords"), + tarotMetaPlanetCardEl: document.getElementById("tarot-meta-planet-card"), + tarotMetaElementCardEl: document.getElementById("tarot-meta-element-card"), + tarotMetaTetragrammatonCardEl: document.getElementById("tarot-meta-tetragrammaton-card"), + tarotMetaZodiacCardEl: document.getElementById("tarot-meta-zodiac-card"), + tarotMetaCourtDateCardEl: document.getElementById("tarot-meta-courtdate-card"), + tarotMetaHebrewCardEl: document.getElementById("tarot-meta-hebrew-card"), + tarotMetaCubeCardEl: document.getElementById("tarot-meta-cube-card"), + tarotMetaCalendarCardEl: document.getElementById("tarot-meta-calendar-card"), + tarotDetailPlanetEl: document.getElementById("tarot-detail-planet"), + tarotDetailElementEl: document.getElementById("tarot-detail-element"), + tarotDetailTetragrammatonEl: document.getElementById("tarot-detail-tetragrammaton"), + tarotDetailZodiacEl: document.getElementById("tarot-detail-zodiac"), + tarotDetailCourtDateEl: document.getElementById("tarot-detail-courtdate"), + tarotDetailHebrewEl: document.getElementById("tarot-detail-hebrew"), + tarotDetailCubeEl: document.getElementById("tarot-detail-cube"), + tarotDetailCalendarEl: document.getElementById("tarot-detail-calendar"), + tarotKabPathEl: document.getElementById("tarot-kab-path"), + tarotHouseOfCardsEl: document.getElementById("tarot-house-of-cards") + }; + } + + function normalizeRelationId(value) { + return String(value || "") + .trim() + .toLowerCase() + .replace(/[^a-z0-9]+/g, "-") + .replace(/(^-|-$)/g, ""); + } + + function normalizeSearchValue(value) { + return String(value || "").trim().toLowerCase(); + } + + function normalizeTarotName(value) { + return String(value || "") + .trim() + .toLowerCase() + .replace(/\s+/g, " "); + } + + function normalizeTarotCardLookupName(value) { + const text = normalizeTarotName(value) + .replace(/\b(pentacles?|coins?)\b/g, "disks"); + + const match = text.match(/^(\d{1,2})\s+of\s+(.+)$/i); + if (!match) { + return text; + } + + const numeric = Number(match[1]); + const suit = String(match[2] || "").trim(); + const rankWord = MINOR_NUMBER_WORD_BY_VALUE[numeric]; + if (!rankWord || !suit) { + return text; + } + + return `${rankWord} of ${suit}`; + } + + function getDisplayCardName(cardOrName, trumpNumber) { + const cardName = typeof cardOrName === "object" + ? String(cardOrName?.name || "") + : String(cardOrName || ""); + + const resolvedTrumpNumber = typeof cardOrName === "object" + ? cardOrName?.number + : trumpNumber; + + if (typeof getTarotCardDisplayName === "function") { + const display = String(getTarotCardDisplayName(cardName, { trumpNumber: resolvedTrumpNumber }) || "").trim(); + if (display) { + return display; + } + } + + return cardName.trim(); + } + + function toTitleCase(value) { + return String(value || "") + .split(" ") + .filter(Boolean) + .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) + .join(" "); + } + + function resolveElementIdForCard(card) { + if (!card) { + return ""; + } + + const cardLookupName = normalizeTarotCardLookupName(card.name); + const rankKey = String(card.rank || "").trim().toLowerCase(); + + return ACE_ELEMENT_BY_CARD_NAME[cardLookupName] || COURT_ELEMENT_BY_RANK[rankKey] || ""; + } + + function createElementRelation(card, elementId, sourceKind, sourceLabel) { + if (!card || !elementId) { + return null; + } + + const elementName = ELEMENT_NAME_BY_ID[elementId] || toTitleCase(elementId); + const hebrewLetter = ELEMENT_HEBREW_LETTER_BY_ID[elementId] || ""; + const hebrewChar = ELEMENT_HEBREW_CHAR_BY_ID[elementId] || ""; + const relationLabel = `${elementName}${hebrewChar ? ` (${hebrewChar})` : (hebrewLetter ? ` (${hebrewLetter})` : "")} · ${sourceLabel}`; + + return { + type: "element", + id: elementId, + label: relationLabel, + data: { + elementId, + name: elementName, + tarotCard: card.name, + hebrewLetter, + hebrewChar, + sourceKind, + sourceLabel, + rank: card.rank || "", + suit: card.suit || "" + }, + __key: `element|${elementId}|${sourceKind}|${normalizeRelationId(sourceLabel)}|${card.id || normalizeTarotCardLookupName(card.name)}` + }; + } + + function buildElementRelationsForCard(card, baseElementRelations = []) { + if (!card) { + return []; + } + + if (card.arcana === "Major") { + return Array.isArray(baseElementRelations) ? [...baseElementRelations] : []; + } + + const relations = []; + + const suitKey = String(card.suit || "").trim().toLowerCase(); + const suitElementId = SUIT_ELEMENT_BY_SUIT[suitKey] || ""; + if (suitElementId) { + const suitRelation = createElementRelation(card, suitElementId, "suit", `Suit: ${card.suit}`); + if (suitRelation) { + relations.push(suitRelation); + } + } + + const rankKey = String(card.rank || "").trim().toLowerCase(); + const courtElementId = COURT_ELEMENT_BY_RANK[rankKey] || ""; + if (courtElementId) { + const courtRelation = createElementRelation(card, courtElementId, "court", `Court: ${card.rank}`); + if (courtRelation) { + relations.push(courtRelation); + } + } + + return relations; + } + + function buildTetragrammatonRelationsForCard(card) { + if (!card) { + return []; + } + + const elementId = resolveElementIdForCard(card); + if (!elementId) { + return []; + } + + const letter = ELEMENT_HEBREW_LETTER_BY_ID[elementId] || ""; + if (!letter) { + return []; + } + + const elementName = ELEMENT_NAME_BY_ID[elementId] || elementId; + const letterKey = String(letter || "").trim().toLowerCase(); + const hebrewLetterId = HEBREW_LETTER_ID_BY_TETRAGRAMMATON_LETTER[letterKey] || ""; + + return [{ + type: "tetragrammaton", + id: `${letterKey}-${elementId}`, + label: `${letter} · ${elementName}`, + data: { + letter, + elementId, + elementName, + hebrewLetterId + }, + __key: `tetragrammaton|${letterKey}|${elementId}|${card.id || normalizeTarotCardLookupName(card.name)}` + }]; + } + + function getSmallCardModality(rankNumber) { + const numeric = Number(rankNumber); + if (!Number.isFinite(numeric) || numeric < 2 || numeric > 10) { + return ""; + } + + if (numeric <= 4) { + return "cardinal"; + } + if (numeric <= 7) { + return "fixed"; + } + return "mutable"; + } + + function buildSmallCardRulershipRelation(card) { + if (!card || card.arcana !== "Minor") { + return null; + } + + const rankKey = String(card.rank || "").trim().toLowerCase(); + const rankNumber = MINOR_RANK_NUMBER_BY_NAME[rankKey]; + const modality = getSmallCardModality(rankNumber); + if (!modality) { + return null; + } + + const suitKey = String(card.suit || "").trim().toLowerCase(); + const signId = SMALL_CARD_SIGN_BY_MODALITY_AND_SUIT[modality]?.[suitKey] || ""; + if (!signId) { + return null; + } + + const sign = (Array.isArray(state.referenceData?.signs) ? state.referenceData.signs : []) + .find((entry) => String(entry?.id || "").trim().toLowerCase() === signId); + + const signName = String(sign?.name || toTitleCase(signId)); + const signSymbol = String(sign?.symbol || "").trim(); + const modalityName = toTitleCase(modality); + + return { + type: "zodiacRulership", + id: `${signId}-${rankKey}-${suitKey}`, + label: `Sign type: ${modalityName} · ${signSymbol} ${signName}`.trim(), + data: { + signId, + signName, + symbol: signSymbol, + modality, + rank: card.rank, + suit: card.suit + }, + __key: `zodiacRulership|${signId}|${rankKey}|${suitKey}` + }; + } + + function buildCourtCardByDecanId(cards) { + const map = new Map(); + + (cards || []).forEach((card) => { + if (!card || card.arcana !== "Minor") { + return; + } + + const rankKey = String(card.rank || "").trim().toLowerCase(); + if (rankKey !== "knight" && rankKey !== "queen" && rankKey !== "prince") { + return; + } + + const windowRelation = (Array.isArray(card.relations) ? card.relations : []) + .find((relation) => relation && typeof relation === "object" && relation.type === "courtDateWindow"); + + const decanIds = Array.isArray(windowRelation?.data?.decanIds) + ? windowRelation.data.decanIds + : []; + + decanIds.forEach((decanId) => { + const decanKey = normalizeRelationId(decanId); + if (!decanKey || map.has(decanKey)) { + return; + } + + map.set(decanKey, { + cardName: card.name, + rank: card.rank, + suit: card.suit, + dateRange: String(windowRelation?.data?.dateRange || "").trim() + }); + }); + }); + + return map; + } + + function buildSmallCardCourtLinkRelations(card, relations) { + if (!card || card.arcana !== "Minor") { + return []; + } + + const rankKey = String(card.rank || "").trim().toLowerCase(); + const rankNumber = MINOR_RANK_NUMBER_BY_NAME[rankKey]; + if (!Number.isFinite(rankNumber) || rankNumber < 2 || rankNumber > 10) { + return []; + } + + const decans = (relations || []).filter((relation) => relation?.type === "decan"); + if (!decans.length) { + return []; + } + + const results = []; + const seenCourtCardNames = new Set(); + + decans.forEach((decan) => { + const signId = String(decan?.data?.signId || "").trim().toLowerCase(); + const decanIndex = Number(decan?.data?.index); + if (!signId || !Number.isFinite(decanIndex)) { + return; + } + + const decanId = normalizeRelationId(`${signId}-${decanIndex}`); + const linkedCourt = state.courtCardByDecanId.get(decanId); + if (!linkedCourt?.cardName || seenCourtCardNames.has(linkedCourt.cardName)) { + return; + } + + seenCourtCardNames.add(linkedCourt.cardName); + + results.push({ + type: "tarotCard", + id: `${decanId}-${normalizeRelationId(linkedCourt.cardName)}`, + label: `Shared court date window: ${linkedCourt.cardName}${linkedCourt.dateRange ? ` · ${linkedCourt.dateRange}` : ""}`, + data: { + cardName: linkedCourt.cardName, + dateRange: linkedCourt.dateRange || "", + decanId + }, + __key: `tarotCard|${decanId}|${normalizeRelationId(linkedCourt.cardName)}` + }); + }); + + return results; + } + + function normalizeHebrewLetterId(value) { + const key = String(value || "") + .trim() + .toLowerCase() + .replace(/[^a-z]/g, ""); + return HEBREW_LETTER_ALIASES[key] || key; + } + + function resolveTarotTrumpNumber(cardName) { + const key = normalizeTarotName(cardName); + if (!key) { + return null; + } + if (Object.prototype.hasOwnProperty.call(TAROT_TRUMP_NUMBER_BY_NAME, key)) { + return TAROT_TRUMP_NUMBER_BY_NAME[key]; + } + const withoutLeadingThe = key.replace(/^the\s+/, ""); + if (Object.prototype.hasOwnProperty.call(TAROT_TRUMP_NUMBER_BY_NAME, withoutLeadingThe)) { + return TAROT_TRUMP_NUMBER_BY_NAME[withoutLeadingThe]; + } + return null; + } + + function cardMatchesTarotAssociation(card, tarotCardName) { + const associationName = normalizeTarotName(tarotCardName); + if (!associationName || !card) { + return false; + } + + const cardName = normalizeTarotName(card.name); + const cardBare = cardName.replace(/^the\s+/, ""); + const assocBare = associationName.replace(/^the\s+/, ""); + + if ( + associationName === cardName || + associationName === cardBare || + assocBare === cardName || + assocBare === cardBare + ) { + return true; + } + + if (card.arcana === "Major" && Number.isFinite(Number(card.number))) { + const trumpNumber = resolveTarotTrumpNumber(associationName); + if (trumpNumber != null) { + return trumpNumber === Number(card.number); + } + } + + return false; + } + + function parseMonthDayToken(value) { + const text = String(value || "").trim(); + const match = text.match(/^(\d{1,2})-(\d{1,2})$/); + if (!match) { + return null; + } + + const month = Number(match[1]); + const day = Number(match[2]); + if (!Number.isInteger(month) || !Number.isInteger(day) || month < 1 || month > 12 || day < 1 || day > 31) { + return null; + } + + return { month, day }; + } + + function toReferenceDate(token, year) { + if (!token) { + return null; + } + return new Date(year, token.month - 1, token.day, 12, 0, 0, 0); + } + + function splitMonthDayRangeByMonth(startToken, endToken) { + const startDate = toReferenceDate(startToken, 2025); + const endBase = toReferenceDate(endToken, 2025); + if (!startDate || !endBase) { + return []; + } + + const wrapsYear = endBase.getTime() < startDate.getTime(); + const endDate = wrapsYear ? toReferenceDate(endToken, 2026) : endBase; + if (!endDate) { + return []; + } + + const segments = []; + let cursor = new Date(startDate); + + while (cursor.getTime() <= endDate.getTime()) { + const monthEnd = new Date(cursor.getFullYear(), cursor.getMonth() + 1, 0, 12, 0, 0, 0); + const segmentEnd = monthEnd.getTime() < endDate.getTime() ? monthEnd : endDate; + + segments.push({ + monthNo: cursor.getMonth() + 1, + startDay: cursor.getDate(), + endDay: segmentEnd.getDate() + }); + + cursor = new Date(segmentEnd.getFullYear(), segmentEnd.getMonth(), segmentEnd.getDate() + 1, 12, 0, 0, 0); + } + + return segments; + } + + function formatMonthDayRangeLabel(monthName, startDay, endDay) { + const start = Number(startDay); + const end = Number(endDay); + if (!Number.isFinite(start) || !Number.isFinite(end)) { + return monthName; + } + if (start === end) { + return `${monthName} ${start}`; + } + return `${monthName} ${start}-${end}`; + } + + function buildMonthReferencesByCard(referenceData, cards) { + const map = new Map(); + const months = Array.isArray(referenceData?.calendarMonths) ? referenceData.calendarMonths : []; + const holidays = Array.isArray(referenceData?.celestialHolidays) ? referenceData.celestialHolidays : []; + const signs = Array.isArray(referenceData?.signs) ? referenceData.signs : []; + const monthById = new Map(months.map((month) => [month.id, month])); + + function parseMonthFromDateToken(value) { + const token = parseMonthDayToken(value); + return token ? token.month : null; + } + + function findMonthByNumber(monthNo) { + if (!Number.isInteger(monthNo) || monthNo < 1 || monthNo > 12) { + return null; + } + + const byOrder = months.find((month) => Number(month?.order) === monthNo); + if (byOrder) { + return byOrder; + } + + return months.find((month) => parseMonthFromDateToken(month?.start) === monthNo) || null; + } + + function pushRef(card, month, options = {}) { + if (!card?.id || !month?.id) { + return; + } + + if (!map.has(card.id)) { + map.set(card.id, []); + } + + const rows = map.get(card.id); + const monthOrder = Number.isFinite(Number(month.order)) ? Number(month.order) : 999; + const startToken = parseMonthDayToken(options.startToken || month.start); + const endToken = parseMonthDayToken(options.endToken || month.end); + const dateRange = String(options.dateRange || "").trim() || ( + startToken && endToken + ? formatMonthDayRangeLabel(month.name || month.id, startToken.day, endToken.day) + : "" + ); + + const uniqueKey = [ + month.id, + dateRange.toLowerCase(), + String(options.context || "").trim().toLowerCase(), + String(options.source || "").trim().toLowerCase() + ].join("|"); + + if (rows.some((entry) => entry.uniqueKey === uniqueKey)) { + return; + } + + rows.push({ + id: month.id, + name: month.name || month.id, + order: monthOrder, + startToken: startToken ? `${String(startToken.month).padStart(2, "0")}-${String(startToken.day).padStart(2, "0")}` : null, + endToken: endToken ? `${String(endToken.month).padStart(2, "0")}-${String(endToken.day).padStart(2, "0")}` : null, + dateRange, + context: String(options.context || "").trim(), + source: String(options.source || "").trim(), + uniqueKey + }); + } + + function captureRefs(associations, month) { + const tarotCardName = associations?.tarotCard; + if (!tarotCardName) { + return; + } + + cards.forEach((card) => { + if (cardMatchesTarotAssociation(card, tarotCardName)) { + pushRef(card, month); + } + }); + } + + months.forEach((month) => { + captureRefs(month?.associations, month); + + const events = Array.isArray(month?.events) ? month.events : []; + events.forEach((event) => { + const tarotCardName = event?.associations?.tarotCard; + if (!tarotCardName) { + return; + } + cards.forEach((card) => { + if (!cardMatchesTarotAssociation(card, tarotCardName)) { + return; + } + pushRef(card, month, { + source: "month-event", + context: String(event?.name || "").trim() + }); + }); + }); + }); + + holidays.forEach((holiday) => { + const month = monthById.get(holiday?.monthId); + if (!month) { + return; + } + const tarotCardName = holiday?.associations?.tarotCard; + if (!tarotCardName) { + return; + } + cards.forEach((card) => { + if (!cardMatchesTarotAssociation(card, tarotCardName)) { + return; + } + pushRef(card, month, { + source: "holiday", + context: String(holiday?.name || "").trim() + }); + }); + }); + + signs.forEach((sign) => { + const signTrumpNumber = Number(sign?.tarot?.number); + const signTarotName = sign?.tarot?.majorArcana || sign?.tarot?.card || sign?.tarotCard; + if (!Number.isFinite(signTrumpNumber) && !signTarotName) { + return; + } + + const signName = String(sign?.name || sign?.id || "").trim(); + const startToken = parseMonthDayToken(sign?.start); + const endToken = parseMonthDayToken(sign?.end); + const monthSegments = splitMonthDayRangeByMonth(startToken, endToken); + const fallbackStartMonthNo = parseMonthFromDateToken(sign?.start); + const fallbackEndMonthNo = parseMonthFromDateToken(sign?.end); + const fallbackStartMonth = findMonthByNumber(fallbackStartMonthNo); + const fallbackEndMonth = findMonthByNumber(fallbackEndMonthNo); + + cards.forEach((card) => { + const cardTrumpNumber = Number(card?.number); + const matchesByTrump = card?.arcana === "Major" + && Number.isFinite(cardTrumpNumber) + && Number.isFinite(signTrumpNumber) + && cardTrumpNumber === signTrumpNumber; + const matchesByName = signTarotName ? cardMatchesTarotAssociation(card, signTarotName) : false; + + if (!matchesByTrump && !matchesByName) { + return; + } + + if (monthSegments.length) { + monthSegments.forEach((segment) => { + const month = findMonthByNumber(segment.monthNo); + if (!month) { + return; + } + + pushRef(card, month, { + source: "zodiac-window", + context: signName ? `${signName} window` : "", + startToken: `${String(segment.monthNo).padStart(2, "0")}-${String(segment.startDay).padStart(2, "0")}`, + endToken: `${String(segment.monthNo).padStart(2, "0")}-${String(segment.endDay).padStart(2, "0")}`, + dateRange: formatMonthDayRangeLabel(month.name || month.id, segment.startDay, segment.endDay) + }); + }); + return; + } + + if (fallbackStartMonth) { + pushRef(card, fallbackStartMonth, { + source: "zodiac-window", + context: signName ? `${signName} window` : "" + }); + } + if (fallbackEndMonth && (!fallbackStartMonth || fallbackEndMonth.id !== fallbackStartMonth.id)) { + pushRef(card, fallbackEndMonth, { + source: "zodiac-window", + context: signName ? `${signName} window` : "" + }); + } + }); + }); + + map.forEach((rows, key) => { + const monthIdsWithZodiacWindows = new Set( + rows + .filter((entry) => entry?.source === "zodiac-window" && entry?.id) + .map((entry) => entry.id) + ); + + const filteredRows = rows.filter((entry) => { + if (!entry?.id || entry?.source === "zodiac-window") { + return true; + } + + if (!monthIdsWithZodiacWindows.has(entry.id)) { + return true; + } + + const month = monthById.get(entry.id); + if (!month) { + return true; + } + + const isFullMonth = String(entry.startToken || "") === String(month.start || "") + && String(entry.endToken || "") === String(month.end || ""); + + return !isFullMonth; + }); + + filteredRows.sort((left, right) => { + if (left.order !== right.order) { + return left.order - right.order; + } + + const startLeft = parseMonthDayToken(left.startToken); + const startRight = parseMonthDayToken(right.startToken); + const dayLeft = startLeft ? startLeft.day : 999; + const dayRight = startRight ? startRight.day : 999; + if (dayLeft !== dayRight) { + return dayLeft - dayRight; + } + + return String(left.dateRange || left.name || "").localeCompare(String(right.dateRange || right.name || "")); + }); + map.set(key, filteredRows); + }); + + return map; + } + + function relationToSearchText(relation) { + if (!relation) { + return ""; + } + + if (typeof relation === "string") { + return relation; + } + + const relationParts = [ + relation.label, + relation.type, + relation.id, + relation.data && typeof relation.data === "object" + ? Object.values(relation.data).join(" ") + : "" + ]; + + return relationParts.filter(Boolean).join(" "); + } + + function buildCardSearchText(card) { + const displayName = getDisplayCardName(card); + const tarotAliases = typeof getTarotCardSearchAliases === "function" + ? getTarotCardSearchAliases(card?.name, { trumpNumber: card?.number }) + : []; + const parts = [ + card.name, + displayName, + card.arcana, + card.rank, + card.suit, + card.summary, + ...tarotAliases, + ...(Array.isArray(card.keywords) ? card.keywords : []), + ...(Array.isArray(card.relations) ? card.relations.map(relationToSearchText) : []) + ]; + + return normalizeSearchValue(parts.join(" ")); + } + + function applySearchFilter(elements) { + const query = normalizeSearchValue(state.searchQuery); + state.filteredCards = query + ? state.cards.filter((card) => buildCardSearchText(card).includes(query)) + : [...state.cards]; + + if (elements?.tarotSearchClearEl) { + elements.tarotSearchClearEl.disabled = !query; + } + + renderList(elements); + + if (!state.filteredCards.some((card) => card.id === state.selectedCardId)) { + if (state.filteredCards.length > 0) { + selectCardById(state.filteredCards[0].id, elements); + } + return; + } + + updateListSelection(elements); + } + + function clearChildren(element) { + if (element) { + element.replaceChildren(); + } + } + + function getCardLookupMap(cards) { + const map = new Map(); + (cards || []).forEach((card) => { + const key = normalizeTarotCardLookupName(card?.name); + if (key) { + map.set(key, card); + } + }); + return map; + } + + function buildMinorCardName(rankNumber, suit) { + const rank = MINOR_TITLE_WORD_BY_VALUE[Number(rankNumber)]; + const suitName = String(suit || "").trim(); + if (!rank || !suitName) { + return ""; + } + return `${rank} of ${suitName}`; + } + + function buildCourtCardName(rank, suit) { + const rankName = String(rank || "").trim(); + const suitName = String(suit || "").trim(); + if (!rankName || !suitName) { + return ""; + } + return `${rankName} of ${suitName}`; + } + + function findCardByLookupName(cardLookupMap, cardName) { + const key = normalizeTarotCardLookupName(cardName); + if (!key) { + return null; + } + return cardLookupMap.get(key) || null; + } + + function findMajorCardByTrumpNumber(cards, trumpNumber) { + const target = Number(trumpNumber); + if (!Number.isFinite(target)) { + return null; + } + return (cards || []).find((card) => card?.arcana === "Major" && Number(card?.number) === target) || null; + } + + function createHouseCardButton(card, elements) { + const button = document.createElement("button"); + button.type = "button"; + button.className = "tarot-house-card-btn"; + + if (!card) { + button.disabled = true; + const fallback = document.createElement("span"); + fallback.className = "tarot-house-card-fallback"; + fallback.textContent = "Missing"; + button.appendChild(fallback); + return button; + } + + const cardDisplayName = getDisplayCardName(card); + button.title = cardDisplayName || card.name; + button.setAttribute("aria-label", cardDisplayName || card.name); + button.dataset.houseCardId = card.id; + const imageUrl = typeof resolveTarotCardImage === "function" + ? resolveTarotCardImage(card.name) + : null; + + if (imageUrl) { + const image = document.createElement("img"); + image.className = "tarot-house-card-image"; + image.src = imageUrl; + image.alt = cardDisplayName || card.name; + button.appendChild(image); + } else { + const fallback = document.createElement("span"); + fallback.className = "tarot-house-card-fallback"; + fallback.textContent = cardDisplayName || card.name; + button.appendChild(fallback); + } + + button.addEventListener("click", () => { + selectCardById(card.id, elements); + elements?.tarotCardListEl + ?.querySelector(`[data-card-id="${card.id}"]`) + ?.scrollIntoView({ block: "nearest" }); + }); + + return button; + } + + function updateHouseSelection(elements) { + if (!elements?.tarotHouseOfCardsEl) { + return; + } + + const buttons = elements.tarotHouseOfCardsEl.querySelectorAll(".tarot-house-card-btn[data-house-card-id]"); + buttons.forEach((button) => { + const isSelected = button.dataset.houseCardId === state.selectedCardId; + button.classList.toggle("is-selected", isSelected); + button.setAttribute("aria-current", isSelected ? "true" : "false"); + }); + } + + function appendHouseMinorRow(columnEl, cardLookupMap, numbers, suit, elements) { + const rowEl = document.createElement("div"); + rowEl.className = "tarot-house-row"; + + numbers.forEach((rankNumber) => { + const cardName = buildMinorCardName(rankNumber, suit); + const card = findCardByLookupName(cardLookupMap, cardName); + rowEl.appendChild(createHouseCardButton(card, elements)); + }); + + columnEl.appendChild(rowEl); + } + + function appendHouseCourtRow(columnEl, cardLookupMap, rank, elements) { + const rowEl = document.createElement("div"); + rowEl.className = "tarot-house-row"; + + HOUSE_MIDDLE_SUITS.forEach((suit) => { + const cardName = buildCourtCardName(rank, suit); + const card = findCardByLookupName(cardLookupMap, cardName); + rowEl.appendChild(createHouseCardButton(card, elements)); + }); + + columnEl.appendChild(rowEl); + } + + function appendHouseTrumpRow(containerEl, trumpNumbers, elements) { + const rowEl = document.createElement("div"); + rowEl.className = "tarot-house-trump-row"; + + (trumpNumbers || []).forEach((trumpNumber) => { + const card = findMajorCardByTrumpNumber(state.cards, trumpNumber); + rowEl.appendChild(createHouseCardButton(card, elements)); + }); + + containerEl.appendChild(rowEl); + } + + function renderHouseOfCards(elements) { + if (!elements?.tarotHouseOfCardsEl) { + return; + } + + clearChildren(elements.tarotHouseOfCardsEl); + const cardLookupMap = getCardLookupMap(state.cards); + + const trumpSectionEl = document.createElement("div"); + trumpSectionEl.className = "tarot-house-trumps"; + HOUSE_TRUMP_ROWS.forEach((trumpRow) => { + appendHouseTrumpRow(trumpSectionEl, trumpRow, elements); + }); + + const bottomGridEl = document.createElement("div"); + bottomGridEl.className = "tarot-house-bottom-grid"; + + const leftColumnEl = document.createElement("div"); + leftColumnEl.className = "tarot-house-column"; + HOUSE_MINOR_NUMBER_BANDS.forEach((numbers, rowIndex) => { + appendHouseMinorRow(leftColumnEl, cardLookupMap, numbers, HOUSE_LEFT_SUITS[rowIndex], elements); + }); + + const middleColumnEl = document.createElement("div"); + middleColumnEl.className = "tarot-house-column"; + HOUSE_MIDDLE_RANKS.forEach((rank) => { + appendHouseCourtRow(middleColumnEl, cardLookupMap, rank, elements); + }); + + const rightColumnEl = document.createElement("div"); + rightColumnEl.className = "tarot-house-column"; + HOUSE_MINOR_NUMBER_BANDS.forEach((numbers, rowIndex) => { + appendHouseMinorRow(rightColumnEl, cardLookupMap, numbers, HOUSE_RIGHT_SUITS[rowIndex], elements); + }); + + bottomGridEl.append(leftColumnEl, middleColumnEl, rightColumnEl); + elements.tarotHouseOfCardsEl.append(trumpSectionEl, bottomGridEl); + updateHouseSelection(elements); + } + + function buildTypeLabel(card) { + if (card.arcana === "Major") { + return typeof card.number === "number" + ? `Major Arcana · ${card.number}` + : "Major Arcana"; + } + + const parts = ["Minor Arcana"]; + if (card.rank) { + parts.push(card.rank); + } + if (card.suit) { + parts.push(card.suit); + } + + return parts.join(" · "); + } + + const MINOR_PLURAL_BY_RANK = { + ace: "aces", + two: "twos", + three: "threes", + four: "fours", + five: "fives", + six: "sixes", + seven: "sevens", + eight: "eights", + nine: "nines", + ten: "tens" + }; + + function findSephirahForMinorCard(card, kabTree) { + if (!card || card.arcana !== "Minor" || !kabTree) { + return null; + } + + const rankKey = String(card.rank || "").trim().toLowerCase(); + const plural = MINOR_PLURAL_BY_RANK[rankKey]; + if (!plural) { + return null; + } + + const matcher = new RegExp(`\\b4\\s+${plural}\\b`, "i"); + return (kabTree.sephiroth || []).find((seph) => matcher.test(String(seph?.tarot || ""))) || null; + } + + function formatRelation(relation) { + if (typeof relation === "string") { + return relation; + } + + if (!relation || typeof relation !== "object") { + return ""; + } + + if (typeof relation.label === "string" && relation.label.trim()) { + return relation.label; + } + + if (relation.type === "hebrewLetter" && relation.data) { + const glyph = relation.data.glyph || ""; + const name = relation.data.name || relation.id || "Unknown"; + const latin = relation.data.latin ? ` (${relation.data.latin})` : ""; + const index = Number.isFinite(relation.data.index) ? relation.data.index : "?"; + const value = Number.isFinite(relation.data.value) ? relation.data.value : "?"; + const meaning = relation.data.meaning ? ` · ${relation.data.meaning}` : ""; + return `Hebrew Letter: ${glyph} ${name}${latin} (index ${index}, value ${value})${meaning}`.trim(); + } + + if (typeof relation.type === "string" && typeof relation.id === "string") { + return `${relation.type}: ${relation.id}`; + } + + return ""; + } + + function relationKey(relation, index) { + const safeType = String(relation?.type || "relation"); + const safeId = String(relation?.id || index || "0"); + const safeLabel = String(relation?.label || relation?.text || ""); + return `${safeType}|${safeId}|${safeLabel}`; + } + + function normalizeRelationObject(relation, index) { + if (relation && typeof relation === "object") { + const label = formatRelation(relation); + if (!label) { + return null; + } + + return { + ...relation, + label, + __key: relationKey(relation, index) + }; + } + + const text = formatRelation(relation); + if (!text) { + return null; + } + + return { + type: "text", + id: `text-${index}`, + label: text, + data: { value: text }, + __key: relationKey({ type: "text", id: `text-${index}`, label: text }, index) + }; + } + + function formatRelationDataLines(relation) { + if (!relation || typeof relation !== "object") { + return "--"; + } + + const data = relation.data; + if (!data || typeof data !== "object") { + return "(no additional relation data)"; + } + + const lines = Object.entries(data) + .filter(([, value]) => value !== null && value !== undefined && String(value).trim() !== "") + .map(([key, value]) => `${key}: ${value}`); + + return lines.length ? lines.join("\n") : "(no additional relation data)"; + } + + function buildCubeFaceRelationsForCard(card) { + const cube = state.magickDataset?.grouped?.kabbalah?.cube; + const walls = Array.isArray(cube?.walls) ? cube.walls : []; + if (!card || !walls.length) { + return []; + } + + return walls + .map((wall, index) => { + const wallTarot = wall?.associations?.tarotCard || wall?.tarotCard; + if (!wallTarot || !cardMatchesTarotAssociation(card, wallTarot)) { + return null; + } + + const wallId = String(wall?.id || "").trim(); + const wallName = String(wall?.name || wallId || "").trim(); + if (!wallId) { + return null; + } + + return { + type: "cubeFace", + id: wallId, + label: `Cube: ${wallName} Wall - Face`, + data: { + wallId, + wallName, + edgeId: "" + }, + __key: `cubeFace|${wallId}|${index}` + }; + }) + .filter(Boolean); + } + + function cardMatchesPathTarot(card, path) { + if (!card || !path) { + return false; + } + + const trumpNumber = Number(path?.tarot?.trumpNumber); + if (card?.arcana === "Major" && Number.isFinite(Number(card?.number)) && Number.isFinite(trumpNumber)) { + if (Number(card.number) === trumpNumber) { + return true; + } + } + + return cardMatchesTarotAssociation(card, path?.tarot?.card); + } + + function buildCubeEdgeRelationsForCard(card) { + const cube = state.magickDataset?.grouped?.kabbalah?.cube; + const tree = state.magickDataset?.grouped?.kabbalah?.["kabbalah-tree"]; + const edges = Array.isArray(cube?.edges) ? cube.edges : []; + const paths = Array.isArray(tree?.paths) ? tree.paths : []; + + if (!card || !edges.length || !paths.length) { + return []; + } + + const pathByLetterId = new Map( + paths + .map((path) => [normalizeHebrewLetterId(path?.hebrewLetter?.transliteration), path]) + .filter(([letterId]) => Boolean(letterId)) + ); + + return edges + .map((edge, index) => { + const edgeLetterId = normalizeHebrewLetterId(edge?.hebrewLetterId || edge?.associations?.hebrewLetterId); + if (!edgeLetterId) { + return null; + } + + const pathMatch = pathByLetterId.get(edgeLetterId); + if (!pathMatch || !cardMatchesPathTarot(card, pathMatch)) { + return null; + } + + const edgeId = String(edge?.id || "").trim(); + if (!edgeId) { + return null; + } + + const edgeName = String(edge?.name || edgeId).trim(); + const wallId = String(Array.isArray(edge?.walls) ? (edge.walls[0] || "") : "").trim(); + + return { + type: "cubeEdge", + id: edgeId, + label: `Cube: ${edgeName} Edge`, + data: { + edgeId, + edgeName, + wallId: wallId || undefined, + hebrewLetterId: edgeLetterId + }, + __key: `cubeEdge|${edgeId}|${index}` + }; + }) + .filter(Boolean); + } + + function buildCubeMotherConnectorRelationsForCard(card) { + const tree = state.magickDataset?.grouped?.kabbalah?.["kabbalah-tree"]; + const paths = Array.isArray(tree?.paths) ? tree.paths : []; + const relations = Array.isArray(card?.relations) ? card.relations : []; + + return Object.entries(CUBE_MOTHER_CONNECTOR_BY_LETTER) + .map(([letterId, connector]) => { + const pathMatch = paths.find((path) => normalizeHebrewLetterId(path?.hebrewLetter?.transliteration) === letterId) || null; + + const matchesByPath = cardMatchesPathTarot(card, pathMatch); + + const matchesByHebrewRelation = relations.some((relation) => { + if (relation?.type !== "hebrewLetter") { + return false; + } + const relationLetterId = normalizeHebrewLetterId( + relation?.data?.id || relation?.id || relation?.data?.latin || relation?.data?.name + ); + return relationLetterId === letterId; + }); + + if (!matchesByPath && !matchesByHebrewRelation) { + return null; + } + + return { + type: "cubeConnector", + id: connector.connectorId, + label: `Cube: ${connector.connectorName}`, + data: { + connectorId: connector.connectorId, + connectorName: connector.connectorName, + hebrewLetterId: letterId + }, + __key: `cubeConnector|${connector.connectorId}|${letterId}` + }; + }) + .filter(Boolean); + } + + function buildCubePrimalPointRelationsForCard(card) { + const center = state.magickDataset?.grouped?.kabbalah?.cube?.center; + if (!center || !card) { + return []; + } + + const centerTarot = center?.associations?.tarotCard || center?.tarotCard; + const centerTrump = Number(center?.associations?.tarotTrumpNumber); + const matchesByName = cardMatchesTarotAssociation(card, centerTarot); + const matchesByTrump = card?.arcana === "Major" + && Number.isFinite(Number(card?.number)) + && Number.isFinite(centerTrump) + && Number(card.number) === centerTrump; + + if (!matchesByName && !matchesByTrump) { + return []; + } + + return [{ + type: "cubeCenter", + id: "primal-point", + label: "Cube: Primal Point", + data: { + nodeType: "center", + primalPoint: true + }, + __key: "cubeCenter|primal-point" + }]; + } + + function buildCubeRelationsForCard(card) { + return [ + ...buildCubeFaceRelationsForCard(card), + ...buildCubeEdgeRelationsForCard(card), + ...buildCubePrimalPointRelationsForCard(card), + ...buildCubeMotherConnectorRelationsForCard(card) + ]; + } + + // Returns nav dispatch config for relations that have a corresponding section, + // null for informational-only relations. + function getRelationNavTarget(relation) { + const t = relation?.type; + const d = relation?.data || {}; + if ((t === "planetCorrespondence" || t === "decanRuler") && d.planetId) { + return { + event: "nav:planet", + detail: { planetId: d.planetId }, + label: `Open ${d.name || d.planetId} in Planets` + }; + } + if (t === "planet") { + const planetId = normalizeRelationId(d.name || relation?.id || ""); + if (!planetId) return null; + return { + event: "nav:planet", + detail: { planetId }, + label: `Open ${d.name || planetId} in Planets` + }; + } + if (t === "element") { + const elementId = d.elementId || relation?.id; + if (!elementId) { + return null; + } + return { + event: "nav:elements", + detail: { elementId }, + label: `Open ${d.name || elementId} in Elements` + }; + } + if (t === "tetragrammaton") { + if (!d.hebrewLetterId) { + return null; + } + return { + event: "nav:alphabet", + detail: { alphabet: "hebrew", hebrewLetterId: d.hebrewLetterId }, + label: `Open ${d.letter || d.hebrewLetterId} in Alphabet` + }; + } + if (t === "tarotCard") { + const cardName = d.cardName || relation?.id; + if (!cardName) { + return null; + } + return { + event: "nav:tarot-trump", + detail: { cardName }, + label: `Open ${cardName} in Tarot` + }; + } + if (t === "zodiacRulership") { + const signId = d.signId || relation?.id; + if (!signId) { + return null; + } + return { + event: "nav:zodiac", + detail: { signId }, + label: `Open ${d.signName || signId} in Zodiac` + }; + } + if (t === "zodiacCorrespondence" || t === "zodiac") { + const signId = d.signId || relation?.id || normalizeRelationId(d.name || ""); + if (!signId) { + return null; + } + return { + event: "nav:zodiac", + detail: { signId }, + label: `Open ${d.name || signId} in Zodiac` + }; + } + if (t === "decan") { + const signId = d.signId || normalizeRelationId(d.signName || relation?.id || ""); + if (!signId) { + return null; + } + return { + event: "nav:zodiac", + detail: { signId }, + label: `Open ${d.signName || signId} in Zodiac` + }; + } + if (t === "hebrewLetter") { + const hebrewLetterId = d.id || relation?.id; + if (!hebrewLetterId) { + return null; + } + return { + event: "nav:alphabet", + detail: { alphabet: "hebrew", hebrewLetterId }, + label: `Open ${d.name || hebrewLetterId} in Alphabet` + }; + } + if (t === "calendarMonth") { + const monthId = d.monthId || relation?.id; + if (!monthId) { + return null; + } + return { + event: "nav:calendar-month", + detail: { monthId }, + label: `Open ${d.name || monthId} in Calendar` + }; + } + if (t === "cubeFace") { + const wallId = d.wallId || relation?.id; + if (!wallId) { + return null; + } + return { + event: "nav:cube", + detail: { wallId, edgeId: "" }, + label: `Open ${d.wallName || wallId} face in Cube` + }; + } + if (t === "cubeEdge") { + const edgeId = d.edgeId || relation?.id; + if (!edgeId) { + return null; + } + return { + event: "nav:cube", + detail: { edgeId, wallId: d.wallId || undefined }, + label: `Open ${d.edgeName || edgeId} edge in Cube` + }; + } + if (t === "cubeConnector") { + const connectorId = d.connectorId || relation?.id; + if (!connectorId) { + return null; + } + return { + event: "nav:cube", + detail: { connectorId }, + label: `Open ${d.connectorName || connectorId} connector in Cube` + }; + } + if (t === "cubeCenter") { + return { + event: "nav:cube", + detail: { nodeType: "center", primalPoint: true }, + label: "Open Primal Point in Cube" + }; + } + return null; + } + + function createRelationListItem(relation) { + const item = document.createElement("li"); + const navTarget = getRelationNavTarget(relation); + + const button = document.createElement("button"); + button.type = "button"; + button.className = "tarot-relation-btn"; + button.dataset.relationKey = relation.__key; + button.textContent = relation.label; + item.appendChild(button); + + if (!navTarget) { + button.classList.add("tarot-relation-btn-static"); + } + button.addEventListener("click", () => { + if (navTarget) { + document.dispatchEvent(new CustomEvent(navTarget.event, { detail: navTarget.detail })); + } + }); + + if (navTarget) { + item.className = "tarot-rel-item"; + const navBtn = document.createElement("button"); + navBtn.type = "button"; + navBtn.className = "tarot-rel-nav-btn"; + navBtn.title = navTarget.label; + navBtn.textContent = "\u2197"; + navBtn.addEventListener("click", (e) => { + e.stopPropagation(); + document.dispatchEvent(new CustomEvent(navTarget.event, { detail: navTarget.detail })); + }); + item.appendChild(navBtn); + } + + return item; + } + + function renderStaticRelationGroup(targetEl, cardEl, relations) { + clearChildren(targetEl); + if (!targetEl || !cardEl) return; + if (!relations.length) { + cardEl.hidden = true; + return; + } + + cardEl.hidden = false; + + relations.forEach((relation) => { + targetEl.appendChild(createRelationListItem(relation)); + }); + } + + function renderDetail(card, elements) { + if (!card || !elements) { + return; + } + + const cardDisplayName = getDisplayCardName(card); + const imageUrl = typeof resolveTarotCardImage === "function" + ? resolveTarotCardImage(card.name) + : null; + + if (elements.tarotDetailImageEl) { + if (imageUrl) { + elements.tarotDetailImageEl.src = imageUrl; + elements.tarotDetailImageEl.alt = cardDisplayName || card.name; + elements.tarotDetailImageEl.style.display = "block"; + elements.tarotDetailImageEl.style.cursor = "zoom-in"; + elements.tarotDetailImageEl.title = "Click to enlarge"; + } else { + elements.tarotDetailImageEl.removeAttribute("src"); + elements.tarotDetailImageEl.alt = ""; + elements.tarotDetailImageEl.style.display = "none"; + elements.tarotDetailImageEl.style.cursor = "default"; + elements.tarotDetailImageEl.removeAttribute("title"); + } + } + + if (elements.tarotDetailNameEl) { + elements.tarotDetailNameEl.textContent = cardDisplayName || card.name; + } + + if (elements.tarotDetailTypeEl) { + elements.tarotDetailTypeEl.textContent = buildTypeLabel(card); + } + + if (elements.tarotDetailSummaryEl) { + elements.tarotDetailSummaryEl.textContent = card.summary || "--"; + } + + if (elements.tarotDetailUprightEl) { + elements.tarotDetailUprightEl.textContent = card.meanings?.upright || "--"; + } + + if (elements.tarotDetailReversedEl) { + elements.tarotDetailReversedEl.textContent = card.meanings?.reversed || "--"; + } + + const meaningText = String(card.meaning || card.meanings?.upright || "").trim(); + if (elements.tarotMetaMeaningCardEl && elements.tarotDetailMeaningEl) { + if (meaningText) { + elements.tarotMetaMeaningCardEl.hidden = false; + elements.tarotDetailMeaningEl.textContent = meaningText; + } else { + elements.tarotMetaMeaningCardEl.hidden = true; + elements.tarotDetailMeaningEl.textContent = "--"; + } + } + + clearChildren(elements.tarotDetailKeywordsEl); + (card.keywords || []).forEach((keyword) => { + const chip = document.createElement("span"); + chip.className = "tarot-keyword-chip"; + chip.textContent = keyword; + elements.tarotDetailKeywordsEl?.appendChild(chip); + }); + + const allRelations = (card.relations || []) + .map((relation, index) => normalizeRelationObject(relation, index)) + .filter(Boolean); + + const uniqueByKey = new Set(); + const dedupedRelations = allRelations.filter((relation) => { + const key = `${relation.type || "relation"}|${relation.id || ""}|${relation.label || ""}`; + if (uniqueByKey.has(key)) return false; + uniqueByKey.add(key); + return true; + }); + + const planetRelations = dedupedRelations.filter((relation) => + relation.type === "planetCorrespondence" || relation.type === "decanRuler" || relation.type === "planet" + ); + + const zodiacRelations = dedupedRelations.filter((relation) => + relation.type === "zodiacCorrespondence" || relation.type === "zodiac" || relation.type === "decan" + ); + + const courtDateRelations = dedupedRelations.filter((relation) => relation.type === "courtDateWindow"); + + const hebrewRelations = dedupedRelations.filter((relation) => relation.type === "hebrewLetter"); + const baseElementRelations = dedupedRelations.filter((relation) => relation.type === "element"); + const elementRelations = buildElementRelationsForCard(card, baseElementRelations); + const tetragrammatonRelations = buildTetragrammatonRelationsForCard(card); + const smallCardRulershipRelation = buildSmallCardRulershipRelation(card); + const zodiacRelationsWithRulership = smallCardRulershipRelation + ? [...zodiacRelations, smallCardRulershipRelation] + : zodiacRelations; + const smallCardCourtLinkRelations = buildSmallCardCourtLinkRelations(card, dedupedRelations); + const mergedCourtDateRelations = [...courtDateRelations, ...smallCardCourtLinkRelations]; + const cubeRelations = buildCubeRelationsForCard(card); + const monthRelations = (state.monthRefsByCardId.get(card.id) || []).map((month, index) => { + const dateRange = String(month?.dateRange || "").trim(); + const context = String(month?.context || "").trim(); + const labelBase = dateRange || month.name; + const label = context ? `${labelBase} · ${context}` : labelBase; + + return { + type: "calendarMonth", + id: month.id, + label, + data: { + monthId: month.id, + name: month.name, + monthOrder: Number.isFinite(Number(month.order)) ? Number(month.order) : null, + dateRange: dateRange || null, + dateStart: month.startToken || null, + dateEnd: month.endToken || null, + context: context || null, + source: month.source || null + }, + __key: `calendarMonth|${month.id}|${month.uniqueKey || index}` + }; + }); + + const relationMonthRows = dedupedRelations + .filter((relation) => relation.type === "calendarMonth") + .map((relation) => { + const dateRange = String(relation?.data?.dateRange || "").trim(); + const baseName = relation?.data?.name || relation.label; + const label = dateRange && baseName + ? `${baseName} · ${dateRange}` + : baseName; + + return { + type: "calendarMonth", + id: relation?.data?.monthId || relation.id, + label, + data: { + monthId: relation?.data?.monthId || relation.id, + name: relation?.data?.name || relation.label, + monthOrder: Number.isFinite(Number(relation?.data?.monthOrder)) + ? Number(relation.data.monthOrder) + : null, + dateRange: dateRange || null, + dateStart: relation?.data?.dateStart || null, + dateEnd: relation?.data?.dateEnd || null, + context: relation?.data?.signName || null + }, + __key: relation.__key + }; + }) + .filter((entry) => entry.data.monthId); + + const mergedMonthMap = new Map(); + [...monthRelations, ...relationMonthRows].forEach((entry) => { + const monthId = entry?.data?.monthId; + if (!monthId) { + return; + } + + const key = [ + monthId, + String(entry?.data?.dateRange || "").trim().toLowerCase(), + String(entry?.data?.context || "").trim().toLowerCase(), + String(entry?.label || "").trim().toLowerCase() + ].join("|"); + + if (!mergedMonthMap.has(key)) { + mergedMonthMap.set(key, entry); + } + }); + + const mergedMonthRelations = [...mergedMonthMap.values()].sort((left, right) => { + const orderLeft = Number.isFinite(Number(left?.data?.monthOrder)) ? Number(left.data.monthOrder) : 999; + const orderRight = Number.isFinite(Number(right?.data?.monthOrder)) ? Number(right.data.monthOrder) : 999; + + if (orderLeft !== orderRight) { + return orderLeft - orderRight; + } + + const startLeft = parseMonthDayToken(left?.data?.dateStart); + const startRight = parseMonthDayToken(right?.data?.dateStart); + const dayLeft = startLeft ? startLeft.day : 999; + const dayRight = startRight ? startRight.day : 999; + if (dayLeft !== dayRight) { + return dayLeft - dayRight; + } + + return String(left.label || "").localeCompare(String(right.label || "")); + }); + + renderStaticRelationGroup(elements.tarotDetailPlanetEl, elements.tarotMetaPlanetCardEl, planetRelations); + renderStaticRelationGroup(elements.tarotDetailElementEl, elements.tarotMetaElementCardEl, elementRelations); + renderStaticRelationGroup(elements.tarotDetailTetragrammatonEl, elements.tarotMetaTetragrammatonCardEl, tetragrammatonRelations); + renderStaticRelationGroup(elements.tarotDetailZodiacEl, elements.tarotMetaZodiacCardEl, zodiacRelationsWithRulership); + renderStaticRelationGroup(elements.tarotDetailCourtDateEl, elements.tarotMetaCourtDateCardEl, mergedCourtDateRelations); + renderStaticRelationGroup(elements.tarotDetailHebrewEl, elements.tarotMetaHebrewCardEl, hebrewRelations); + renderStaticRelationGroup(elements.tarotDetailCubeEl, elements.tarotMetaCubeCardEl, cubeRelations); + renderStaticRelationGroup(elements.tarotDetailCalendarEl, elements.tarotMetaCalendarCardEl, mergedMonthRelations); + + // ── Kabbalah Tree path cross-reference ───────────────────────────────── + const kabPathEl = elements.tarotKabPathEl; + if (kabPathEl) { + const kabTree = state.magickDataset?.grouped?.kabbalah?.["kabbalah-tree"]; + const kabPath = (card.arcana === "Major" && typeof card.number === "number" && kabTree) + ? kabTree.paths.find(p => p.tarot?.trumpNumber === card.number) + : null; + const kabSeph = !kabPath ? findSephirahForMinorCard(card, kabTree) : null; + + if (kabPath) { + const letter = kabPath.hebrewLetter || {}; + const fromName = kabTree.sephiroth.find(s => s.number === kabPath.connects.from)?.name || kabPath.connects.from; + const toName = kabTree.sephiroth.find(s => s.number === kabPath.connects.to)?.name || kabPath.connects.to; + const astro = kabPath.astrology ? `${kabPath.astrology.name} (${kabPath.astrology.type})` : ""; + + kabPathEl.innerHTML = ` + Kabbalah Tree — Path ${kabPath.pathNumber} +
+ ${letter.char || ""} + + ${letter.transliteration || ""} — “${letter.meaning || ""}” · ${letter.letterType || ""} + ${fromName} → ${toName}${astro ? " · " + astro : ""} + +
`; + + const btn = document.createElement("button"); + btn.type = "button"; + btn.className = "kab-tarot-link"; + btn.textContent = `View Path ${kabPath.pathNumber} in Kabbalah Tree`; + btn.addEventListener("click", () => { + document.dispatchEvent(new CustomEvent("tarot:view-kab-path", { + detail: { pathNumber: kabPath.pathNumber } + })); + }); + kabPathEl.appendChild(btn); + kabPathEl.hidden = false; + } else if (kabSeph) { + const hebrewName = kabSeph.nameHebrew ? ` (${kabSeph.nameHebrew})` : ""; + const translation = kabSeph.translation ? ` — ${kabSeph.translation}` : ""; + const planetInfo = kabSeph.planet || ""; + const tarotInfo = kabSeph.tarot ? ` · ${kabSeph.tarot}` : ""; + + kabPathEl.innerHTML = ` + Kabbalah Tree — Sephirah ${kabSeph.number} +
+ ${kabSeph.number} + + ${kabSeph.name || ""}${hebrewName}${translation} + ${planetInfo}${tarotInfo} + +
`; + + const btn = document.createElement("button"); + btn.type = "button"; + btn.className = "kab-tarot-link"; + btn.textContent = `View Sephirah ${kabSeph.number} in Kabbalah Tree`; + btn.addEventListener("click", () => { + document.dispatchEvent(new CustomEvent("tarot:view-kab-path", { + detail: { pathNumber: kabSeph.number } + })); + }); + kabPathEl.appendChild(btn); + kabPathEl.hidden = false; + } else { + kabPathEl.hidden = true; + kabPathEl.innerHTML = ""; + } + } + } + + function updateListSelection(elements) { + if (!elements?.tarotCardListEl) { + return; + } + + const buttons = elements.tarotCardListEl.querySelectorAll(".tarot-list-item"); + buttons.forEach((button) => { + const isSelected = button.dataset.cardId === state.selectedCardId; + button.classList.toggle("is-selected", isSelected); + button.setAttribute("aria-selected", isSelected ? "true" : "false"); + }); + } + + function selectCardById(cardIdToSelect, elements) { + const card = state.cards.find((entry) => entry.id === cardIdToSelect); + if (!card) { + return; + } + + state.selectedCardId = card.id; + updateListSelection(elements); + updateHouseSelection(elements); + renderDetail(card, elements); + } + + function renderList(elements) { + if (!elements?.tarotCardListEl) { + return; + } + + clearChildren(elements.tarotCardListEl); + + state.filteredCards.forEach((card) => { + const cardDisplayName = getDisplayCardName(card); + const button = document.createElement("button"); + button.type = "button"; + button.className = "tarot-list-item"; + button.dataset.cardId = card.id; + button.setAttribute("role", "option"); + + const nameEl = document.createElement("span"); + nameEl.className = "tarot-list-name"; + nameEl.textContent = cardDisplayName || card.name; + + const metaEl = document.createElement("span"); + metaEl.className = "tarot-list-meta"; + metaEl.textContent = buildTypeLabel(card); + + button.append(nameEl, metaEl); + elements.tarotCardListEl.appendChild(button); + }); + + if (elements.tarotCountEl) { + elements.tarotCountEl.textContent = state.searchQuery + ? `${state.filteredCards.length} of ${state.cards.length} cards` + : `${state.cards.length} cards`; + } + } + + function ensureTarotSection(referenceData, magickDataset = null) { + state.referenceData = referenceData || state.referenceData; + + if (magickDataset) { + state.magickDataset = magickDataset; + } + + const elements = getElements(); + + if (state.initialized) { + state.monthRefsByCardId = buildMonthReferencesByCard(referenceData, state.cards); + state.courtCardByDecanId = buildCourtCardByDecanId(state.cards); + renderHouseOfCards(elements); + if (state.selectedCardId) { + const selected = state.cards.find((card) => card.id === state.selectedCardId); + if (selected) { + renderDetail(selected, elements); + } + } + return; + } + + if (!elements.tarotCardListEl || !elements.tarotDetailNameEl) { + return; + } + + const databaseBuilder = window.TarotCardDatabase?.buildTarotDatabase; + if (typeof databaseBuilder !== "function") { + return; + } + + const cards = databaseBuilder(referenceData, magickDataset).map((card) => ({ + ...card, + id: cardId(card) + })); + + state.cards = cards; + state.monthRefsByCardId = buildMonthReferencesByCard(referenceData, cards); + state.courtCardByDecanId = buildCourtCardByDecanId(cards); + state.filteredCards = [...cards]; + renderList(elements); + renderHouseOfCards(elements); + + if (cards.length > 0) { + selectCardById(cards[0].id, elements); + } + + elements.tarotCardListEl.addEventListener("click", (event) => { + const target = event.target; + if (!(target instanceof Node)) { + return; + } + + const button = target instanceof Element + ? target.closest(".tarot-list-item") + : null; + + if (!(button instanceof HTMLButtonElement)) { + return; + } + + const selectedId = button.dataset.cardId; + if (!selectedId) { + return; + } + + selectCardById(selectedId, elements); + }); + + if (elements.tarotSearchInputEl) { + elements.tarotSearchInputEl.addEventListener("input", () => { + state.searchQuery = elements.tarotSearchInputEl.value || ""; + applySearchFilter(elements); + }); + } + + if (elements.tarotSearchClearEl && elements.tarotSearchInputEl) { + elements.tarotSearchClearEl.addEventListener("click", () => { + elements.tarotSearchInputEl.value = ""; + state.searchQuery = ""; + applySearchFilter(elements); + elements.tarotSearchInputEl.focus(); + }); + } + + if (elements.tarotDetailImageEl) { + elements.tarotDetailImageEl.addEventListener("click", () => { + const src = elements.tarotDetailImageEl.getAttribute("src") || ""; + if (!src || elements.tarotDetailImageEl.style.display === "none") { + return; + } + openTarotImageLightbox(src, elements.tarotDetailImageEl.alt || "Tarot card enlarged image"); + }); + } + + state.initialized = true; + } + + function selectCardByTrump(trumpNumber) { + if (!state.initialized) return; + const el = getElements(); + const card = state.cards.find(c => c.arcana === "Major" && c.number === trumpNumber); + if (!card) return; + selectCardById(card.id, el); + const listItem = el.tarotCardListEl?.querySelector(`[data-card-id="${card.id}"]`); + listItem?.scrollIntoView({ block: "nearest" }); + } + + function selectCardByName(name) { + if (!state.initialized) return; + const el = getElements(); + const needle = normalizeTarotCardLookupName(name); + const card = state.cards.find((entry) => normalizeTarotCardLookupName(entry.name) === needle); + if (!card) return; + selectCardById(card.id, el); + el.tarotCardListEl + ?.querySelector(`[data-card-id="${card.id}"]`) + ?.scrollIntoView({ block: "nearest" }); + } + + window.TarotSectionUi = { + ensureTarotSection, + selectCardByTrump, + selectCardByName, + getCards: () => state.cards + }; +})(); diff --git a/app/ui-zodiac.js b/app/ui-zodiac.js new file mode 100644 index 0000000..311f967 --- /dev/null +++ b/app/ui-zodiac.js @@ -0,0 +1,590 @@ +/* ui-zodiac.js — Zodiac sign browser section */ +(function () { + "use strict"; + + const ELEMENT_STYLE = { + fire: { emoji: "🔥", badge: "zod-badge--fire", label: "Fire" }, + earth: { emoji: "🌍", badge: "zod-badge--earth", label: "Earth" }, + air: { emoji: "💨", badge: "zod-badge--air", label: "Air" }, + water: { emoji: "💧", badge: "zod-badge--water", label: "Water" } + }; + + const PLANET_SYMBOLS = { + saturn: "♄︎", jupiter: "♃︎", mars: "♂︎", sol: "☉︎", + venus: "♀︎", mercury: "☿︎", luna: "☾︎" + }; + + const MONTH_NAMES = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]; + + const state = { + initialized: false, + entries: [], + filteredEntries: [], + selectedId: null, + searchQuery: "", + kabPaths: [], + decansBySign: {}, + monthRefsBySignId: new Map(), + cubePlacementBySignId: new Map() + }; + + // ── Elements ────────────────────────────────────────────────────────── + function getElements() { + return { + listEl: document.getElementById("zodiac-sign-list"), + countEl: document.getElementById("zodiac-sign-count"), + searchEl: document.getElementById("zodiac-search-input"), + searchClearEl: document.getElementById("zodiac-search-clear"), + detailNameEl: document.getElementById("zodiac-detail-name"), + detailSubEl: document.getElementById("zodiac-detail-sub"), + detailBodyEl: document.getElementById("zodiac-detail-body") + }; + } + + // ── Normalise ───────────────────────────────────────────────────────── + function norm(s) { + return String(s || "").toLowerCase().replace(/[^a-z0-9 ]/g, "").trim(); + } + + function cap(s) { + return String(s || "").charAt(0).toUpperCase() + String(s || "").slice(1); + } + + function buildSearchText(sign) { + return norm([ + sign.name?.en, sign.meaning?.en, sign.elementId, sign.quadruplicity, + sign.planetId, sign.id + ].join(" ")); + } + + function formatDateRange(rulesFrom) { + if (!Array.isArray(rulesFrom) || rulesFrom.length < 2) return "—"; + const [from, to] = rulesFrom; + const fMonth = MONTH_NAMES[(from[0] || 1) - 1]; + const tMonth = MONTH_NAMES[(to[0] || 1) - 1]; + return `${fMonth} ${from[1]} – ${tMonth} ${to[1]}`; + } + + function buildMonthReferencesBySign(referenceData) { + const map = new Map(); + const months = Array.isArray(referenceData?.calendarMonths) ? referenceData.calendarMonths : []; + const holidays = Array.isArray(referenceData?.celestialHolidays) ? referenceData.celestialHolidays : []; + const signs = Array.isArray(referenceData?.signs) ? referenceData.signs : []; + const monthById = new Map(months.map((month) => [month.id, month])); + const monthByOrder = new Map( + months + .filter((month) => Number.isFinite(Number(month?.order))) + .map((month) => [Number(month.order), month]) + ); + + function parseMonthDay(value) { + const [month, day] = String(value || "").split("-").map((part) => Number(part)); + if (!Number.isFinite(month) || !Number.isFinite(day)) { + return null; + } + return { month, day }; + } + + function monthOrdersInRange(startMonth, endMonth) { + const orders = []; + let cursor = startMonth; + let guard = 0; + + while (guard < 13) { + orders.push(cursor); + if (cursor === endMonth) { + break; + } + cursor = cursor === 12 ? 1 : cursor + 1; + guard += 1; + } + + return orders; + } + + function pushRef(signId, month) { + const key = String(signId || "").trim().toLowerCase(); + if (!key || !month?.id) { + return; + } + + if (!map.has(key)) { + map.set(key, []); + } + + const rows = map.get(key); + if (rows.some((entry) => entry.id === month.id)) { + return; + } + + rows.push({ + id: month.id, + name: month.name || month.id, + order: Number.isFinite(Number(month.order)) ? Number(month.order) : 999 + }); + } + + months.forEach((month) => { + pushRef(month?.associations?.zodiacSignId, month); + const events = Array.isArray(month?.events) ? month.events : []; + events.forEach((event) => { + pushRef(event?.associations?.zodiacSignId, month); + }); + }); + + holidays.forEach((holiday) => { + const month = monthById.get(holiday?.monthId); + if (!month) { + return; + } + pushRef(holiday?.associations?.zodiacSignId, month); + }); + + // Structural month coverage from sign date ranges (e.g., Scorpio spans Oct+Nov). + signs.forEach((sign) => { + const start = parseMonthDay(sign?.start); + const end = parseMonthDay(sign?.end); + if (!start || !end || !sign?.id) { + return; + } + + monthOrdersInRange(start.month, end.month).forEach((monthOrder) => { + const month = monthByOrder.get(monthOrder); + if (month) { + pushRef(sign.id, month); + } + }); + }); + + map.forEach((rows, key) => { + rows.sort((left, right) => left.order - right.order || left.name.localeCompare(right.name)); + map.set(key, rows); + }); + + return map; + } + + function buildCubeSignPlacements(magickDataset) { + const placements = new Map(); + const cube = magickDataset?.grouped?.kabbalah?.cube || {}; + const walls = Array.isArray(cube?.walls) + ? cube.walls + : []; + const edges = Array.isArray(cube?.edges) + ? cube.edges + : []; + const paths = Array.isArray(magickDataset?.grouped?.kabbalah?.["kabbalah-tree"]?.paths) + ? magickDataset.grouped.kabbalah["kabbalah-tree"].paths + : []; + + function normalizeLetterId(value) { + const key = String(value || "").toLowerCase().replace(/[^a-z]/g, "").trim(); + const aliases = { + aleph: "alef", + beth: "bet", + zain: "zayin", + cheth: "het", + chet: "het", + daleth: "dalet", + teth: "tet", + peh: "pe", + tzaddi: "tsadi", + tzadi: "tsadi", + tzade: "tsadi", + tsaddi: "tsadi", + qoph: "qof", + taw: "tav", + tau: "tav" + }; + return aliases[key] || key; + } + + function edgeWalls(edge) { + const explicitWalls = Array.isArray(edge?.walls) + ? edge.walls.map((wallId) => String(wallId || "").trim().toLowerCase()).filter(Boolean) + : []; + + if (explicitWalls.length >= 2) { + return explicitWalls.slice(0, 2); + } + + return String(edge?.id || "") + .trim() + .toLowerCase() + .split("-") + .map((wallId) => wallId.trim()) + .filter(Boolean) + .slice(0, 2); + } + + function edgeLabel(edge) { + const explicitName = String(edge?.name || "").trim(); + if (explicitName) { + return explicitName; + } + return edgeWalls(edge) + .map((part) => cap(part)) + .join(" "); + } + + function resolveCubeDirectionLabel(wallId, edge) { + const normalizedWallId = String(wallId || "").trim().toLowerCase(); + const edgeId = String(edge?.id || "").trim().toLowerCase(); + if (!normalizedWallId || !edgeId) { + return ""; + } + + const cubeUi = window.CubeSectionUi; + if (cubeUi && typeof cubeUi.getEdgeDirectionLabelForWall === "function") { + const directionLabel = String(cubeUi.getEdgeDirectionLabelForWall(normalizedWallId, edgeId) || "").trim(); + if (directionLabel) { + return directionLabel; + } + } + + return edgeLabel(edge); + } + + const wallById = new Map( + walls.map((wall) => [String(wall?.id || "").trim().toLowerCase(), wall]) + ); + + const pathByLetterId = new Map( + paths + .map((path) => [normalizeLetterId(path?.hebrewLetter?.transliteration), path]) + .filter(([letterId]) => Boolean(letterId)) + ); + + edges.forEach((edge) => { + const letterId = normalizeLetterId(edge?.hebrewLetterId || edge?.associations?.hebrewLetterId); + const path = pathByLetterId.get(letterId) || null; + const signId = path?.astrology?.type === "zodiac" + ? String(path?.astrology?.name || "").trim().toLowerCase() + : ""; + + if (!signId || placements.has(signId)) { + return; + } + + const wallsForEdge = edgeWalls(edge); + const primaryWallId = wallsForEdge[0] || ""; + const primaryWall = wallById.get(primaryWallId); + + placements.set(signId, { + wallId: primaryWallId, + edgeId: String(edge?.id || "").trim().toLowerCase(), + wallName: primaryWall?.name || cap(primaryWallId || "wall"), + edgeName: resolveCubeDirectionLabel(primaryWallId, edge) + }); + }); + + return placements; + } + + function cubePlacementLabel(placement) { + const wallName = placement?.wallName || "Wall"; + const edgeName = placement?.edgeName || "Direction"; + return `Cube: ${wallName} Wall - ${edgeName}`; + } + + // ── List ────────────────────────────────────────────────────────────── + function applyFilter() { + const q = norm(state.searchQuery); + state.filteredEntries = q + ? state.entries.filter((s) => buildSearchText(s).includes(q)) + : [...state.entries]; + } + + function renderList(els) { + if (!els.listEl) return; + els.listEl.innerHTML = ""; + state.filteredEntries.forEach((sign) => { + const active = sign.id === state.selectedId; + const el = document.createElement("div"); + el.className = "planet-list-item" + (active ? " is-selected" : ""); + el.setAttribute("role", "option"); + el.setAttribute("aria-selected", active ? "true" : "false"); + el.dataset.id = sign.id; + + const elemStyle = ELEMENT_STYLE[sign.elementId] || {}; + el.innerHTML = ` +
+ ${sign.symbol || "?"} + ${sign.name?.en || sign.id} + ${elemStyle.emoji || ""} +
+
${cap(sign.elementId)} · ${cap(sign.quadruplicity)} · ${cap(sign.planetId)}
+ `; + el.addEventListener("click", () => { selectById(sign.id, els); }); + els.listEl.appendChild(el); + }); + + if (els.countEl) { + els.countEl.textContent = state.searchQuery + ? `${state.filteredEntries.length} of ${state.entries.length} signs` + : `${state.entries.length} signs`; + } + } + + // ── Detail ──────────────────────────────────────────────────────────── + function renderDetail(sign, els) { + if (!els.detailNameEl) return; + + const elemStyle = ELEMENT_STYLE[sign.elementId] || {}; + const polarity = ["fire", "air"].includes(sign.elementId) ? "Masculine / Positive" : "Feminine / Negative"; + const kabPath = state.kabPaths.find( + (p) => p.astrology?.type === "zodiac" && + p.astrology?.name?.toLowerCase() === sign.id + ); + const decans = state.decansBySign[sign.id] || []; + const monthRefs = state.monthRefsBySignId.get(String(sign.id || "").toLowerCase()) || []; + const cubePlacement = state.cubePlacementBySignId.get(String(sign.id || "").toLowerCase()) || null; + + // Heading + els.detailNameEl.textContent = sign.symbol || sign.id; + els.detailSubEl.textContent = `${sign.name?.en || ""} — ${sign.meaning?.en || ""}`; + + const sections = []; + + // ── Sign Details ────────────────────────────────────────────────── + const elemBadge = `${elemStyle.emoji || ""} ${cap(sign.elementId)}`; + const quadBadge = `${cap(sign.quadruplicity)}`; + sections.push(`
+ Sign Details +
+
+
Symbol
${sign.symbol || "—"}
+
Meaning
${sign.meaning?.en || "—"}
+
Element
${elemBadge}
+
Modality
${quadBadge}
+
Polarity
${polarity}
+
Dates
${formatDateRange(sign.rulesFrom)}
+
Position
#${sign.no} of 12
+
+
+
`); + + // ── Ruling Planet ───────────────────────────────────────────────── + const planetSym = PLANET_SYMBOLS[sign.planetId] || ""; + sections.push(`
+ Ruling Planet +
+

${planetSym} ${cap(sign.planetId)}

+ +
+
`); + + if (cubePlacement) { + sections.push(`
+ Cube of Space +
This sign appears in Cube edge correspondences.
+
+ +
+
`); + } + + // ── Kabbalah Path + Trump ───────────────────────────────────────── + if (kabPath) { + const hl = kabPath.hebrewLetter || {}; + sections.push(`
+ Kabbalah & Major Arcana +
+
+ ${hl.char || ""} +
+
${hl.transliteration || ""} (${hl.meaning || ""})
+
${cap(hl.letterType || "")} letter · Path ${kabPath.pathNumber}
+
+
+
+
Trump Card
${kabPath.tarot?.card || "—"}
+
Intelligence
${kabPath.intelligence || "—"}
+
+
+ + +
+
+
`); + } + + // ── Decans & Minor Arcana ───────────────────────────────────────── + if (decans.length) { + const decanRows = decans.map((d) => { + const ord = ["1st","2nd","3rd"][d.index - 1] || d.index; + const sym = PLANET_SYMBOLS[d.rulerPlanetId] || ""; + return `
+ ${ord} + ${sym} ${cap(d.rulerPlanetId)} + +
`; + }).join(""); + sections.push(`
+ Decans & Minor Arcana +
${decanRows}
+
`); + } + + if (monthRefs.length) { + const monthButtons = monthRefs.map((month) => + `` + ).join(""); + + sections.push(`
+ Calendar Months +
Month correspondences linked to ${sign.name?.en || sign.id}.
+
${monthButtons}
+
`); + } + + // ── Kabbalah extras ─────────────────────────────────────────────── + if (sign.tribeOfIsraelId || sign.tetragrammatonPermutation) { + sections.push(`
+ Kabbalah Correspondences +
+
+ ${sign.tribeOfIsraelId ? `
Tribe of Israel
${cap(sign.tribeOfIsraelId)}
` : ""} + ${sign.tetragrammatonPermutation ? `
Tetragrammaton
${sign.tetragrammatonPermutation}
` : ""} +
+
+
`); + } + + els.detailBodyEl.innerHTML = `
${sections.join("")}
`; + + // Attach button listeners + els.detailBodyEl.querySelectorAll("[data-nav]").forEach((btn) => { + btn.addEventListener("click", () => { + const nav = btn.dataset.nav; + if (nav === "planet") { + document.dispatchEvent(new CustomEvent("nav:planet", { + detail: { planetId: btn.dataset.planetId } + })); + } else if (nav === "kab-path") { + document.dispatchEvent(new CustomEvent("tarot:view-kab-path", { + detail: { pathNumber: Number(btn.dataset.pathNumber) } + })); + } else if (nav === "trump") { + document.dispatchEvent(new CustomEvent("kab:view-trump", { + detail: { trumpNumber: Number(btn.dataset.trumpNumber) } + })); + } else if (nav === "tarot-card") { + document.dispatchEvent(new CustomEvent("nav:tarot-trump", { + detail: { cardName: btn.dataset.cardName } + })); + } else if (nav === "calendar-month") { + document.dispatchEvent(new CustomEvent("nav:calendar-month", { + detail: { monthId: btn.dataset.monthId } + })); + } else if (nav === "cube-sign") { + document.dispatchEvent(new CustomEvent("nav:cube", { + detail: { + signId: btn.dataset.signId, + wallId: btn.dataset.wallId, + edgeId: btn.dataset.edgeId + } + })); + } + }); + }); + } + + function resetDetail(els) { + if (els.detailNameEl) els.detailNameEl.textContent = "--"; + if (els.detailSubEl) els.detailSubEl.textContent = "Select a sign to explore"; + if (els.detailBodyEl) els.detailBodyEl.innerHTML = ""; + } + + // ── Selection ───────────────────────────────────────────────────────── + function selectById(id, els) { + const sign = state.entries.find((s) => s.id === id); + if (!sign) return; + state.selectedId = id; + renderList(els); + renderDetail(sign, els); + } + + // ── Public select (for incoming navigation) ─────────────────────────── + function selectBySignId(signId) { + const els = getElements(); + if (!state.initialized) return; + const sign = state.entries.find((s) => s.id === signId); + if (sign) selectById(signId, els); + } + + // ── Init ─────────────────────────────────────────────────────────────── + function ensureZodiacSection(referenceData, magickDataset) { + state.monthRefsBySignId = buildMonthReferencesBySign(referenceData); + state.cubePlacementBySignId = buildCubeSignPlacements(magickDataset); + + if (state.initialized) { + const els = getElements(); + const current = state.entries.find((entry) => entry.id === state.selectedId); + if (current) { + renderDetail(current, els); + } + return; + } + state.initialized = true; + + const zodiacObj = magickDataset?.grouped?.astrology?.zodiac || {}; + state.entries = Object.values(zodiacObj).sort((a, b) => (a.no || 0) - (b.no || 0)); + + const kabTree = magickDataset?.grouped?.kabbalah?.["kabbalah-tree"]; + state.kabPaths = Array.isArray(kabTree?.paths) ? kabTree.paths : []; + + state.decansBySign = referenceData?.decansBySign || {}; + + const els = getElements(); + applyFilter(); + renderList(els); + + if (state.entries.length > 0) { + selectById(state.entries[0].id, els); + } + + // Search + if (els.searchEl) { + els.searchEl.addEventListener("input", () => { + state.searchQuery = els.searchEl.value; + if (els.searchClearEl) els.searchClearEl.disabled = !state.searchQuery; + applyFilter(); + renderList(els); + if (!state.filteredEntries.some((s) => s.id === state.selectedId)) { + if (state.filteredEntries.length > 0) { + selectById(state.filteredEntries[0].id, els); + } else { + state.selectedId = null; + resetDetail(els); + } + } + }); + } + if (els.searchClearEl) { + els.searchClearEl.addEventListener("click", () => { + state.searchQuery = ""; + if (els.searchEl) els.searchEl.value = ""; + els.searchClearEl.disabled = true; + applyFilter(); + renderList(els); + if (state.entries.length > 0) selectById(state.entries[0].id, els); + }); + } + } + + window.ZodiacSectionUi = { + ensureZodiacSection, + selectBySignId + }; +})(); diff --git a/asset/img/enochian/char(65).png b/asset/img/enochian/char(65).png new file mode 100644 index 0000000000000000000000000000000000000000..de534e4649193f502cb6428602db016083cafa13 GIT binary patch literal 562 zcmeAS@N?(olHy`uVBq!ia0y~yV31>AU{K&-V_;xlbvRMaz`*#^)5S5wqx0-F`wZbk znPVUM4O;??I;L!YJObl{y zagkB!nB39qJW)$YNX4V0u;TrR*mnW9_Jr3wh<)e#?)Sd^_UGTvh})Wy`@{comf&&a zJ?YL?9sU)a*TNjrH+BiK?P#h>s}I)vFK*Bxko?Q|7yVwh55e~?}hT(H?IHm@E!MC^I+BueX3v77T6cA)XE4p=>NyE zrM>vrGNDl89Xjumln*9Mdy;E1D{9Be8;5NZlU%=N-Dqs@s{PIDp2YBqQ)TttibWNd z&qs7H-qri@=IEKtd~s78>>mX1ywFpelKJ`i2Kjui1^I;vY+IDy?{=PZ=r`vBf%O~p z*|zkp?)qG|DYmUbZxgRsfkb)h%xJS$VJ%9lUAQCk8SRDAJ}%q(R=?W)BZIZ~w`(7z z<6EY<1PU(~RX;PU#+1L+f6lor2Q24^R+(gfV^+NWc}H?gO6q}CH0%r7c`d$?~^w;a+V#)jagi2jL-Tm)Z&#-d# W&Rm|o?sW_d3=E#GelF{r5}E+Ad;zln literal 0 HcmV?d00001 diff --git a/asset/img/enochian/char(66).png b/asset/img/enochian/char(66).png new file mode 100644 index 0000000000000000000000000000000000000000..aff0adc45cb3f29902df6f1c80f19f7c64484bd5 GIT binary patch literal 770 zcmeAS@N?(olHy`uVBq!ia0y~yV31>AU{K&-V_;xlbvRMaz`%6U)5S5wqx0=`+m29o z5%v%BPj5OQG)=cD;)QZYM2qes4=zE$)dE6+0y!+M0!^(8%x2wl@<`59RBBo#Y?!dJ zwWCXI2Y1>>+GX{M0uw7ceV)E}$T>HTC|cc{YcdY;LpbF39w`$OF)7rqSJf8g4YA6m~ITigr% z^46RE>jO95k4Jev=B}CBy2ZhsFZ9dZOGQ})tJ;3Vwf>m$px~_sw@&!Iv*8g_{QhlC zd(C&8ZSegWp;oxxD`xwwou+>`FAV?iYrXd0=O4|!uJhzc_H^@%&uQP_=h6AN(66xW z@5KX-IwDRbu}0xC)k^74`Hc7e>J6)t6S|bKJ#+H}>5CgA6%BPN61$d7yBvFcZ?Wi( zp5Cl3S7(k7S5EDp`uS_(-1Q%1*KxUK)Om%RQgGtkyLit0*{3T1@R+4OGi#0c;M4Ir z`VikekypjHcifNP_-kpH%wa7>|L3RTm6HR+c5W$O`pIr>jb7qKEGv-52v`pWwS7=KpM(HPWK?$HjlBOuk1vbnvLbP0l+XkKe}8bl literal 0 HcmV?d00001 diff --git a/asset/img/enochian/char(67).png b/asset/img/enochian/char(67).png new file mode 100644 index 0000000000000000000000000000000000000000..58dd1e66935321fbfe9a1db720e23a09a88d26c5 GIT binary patch literal 717 zcmeAS@N?(olHy`uVBq!ia0y~yV31>AU{K&-V_;xlbvRMaz`!)c)5S5wqx0!?|BkbP zBJ3afGsR}9`9}0)ICY#kw0}u}(WVJ1nv*ovsND3JG_`dptETG`6>im2ewve7HR`)O zp4?yZXZ{6~e>(4KFVDTdd;j-)AJ){o{#N(>{?B>U=WL(PJN~~-@Z)yg$rmS`sXey2 z-q%Jd{KCvLvB$3c*lYV!FNxv)$H0Iue0dKO=dA9Y{A22t&nNF3JQTC{8=Jbp-2iPq zCD}bK|J+P+80Ov(N|4Rp_yK4#n^W@f7?SQhj6UwxCS z=l``2`a~^M-25i~@a^0%Ws9hN^67<(FUV_06!E!n{#^KK)%U3!-iG|`t-(u|ia&?FBlm){SCwkHdlJv)Z|D);)L9*TYx>sm{B@ckC%hTF9B{4Y`)et_@IsgHkj3-!MxFPyOR8JC^!)At8g zRW*g3PG2W-`h#o7PtjFV=j+(?W`3(u*%MOl`$s0*c!_Aq%DdhH{Rathh};!Ro#FUW`N^0=ef zA2@UC*9%G_j7K->6#92guyop&E6?`PtpPvTy(#}M fGGmu&9rK!i-EL1mn?y1&Ffe$!`njxgN@xNA0&`87 literal 0 HcmV?d00001 diff --git a/asset/img/enochian/char(68).png b/asset/img/enochian/char(68).png new file mode 100644 index 0000000000000000000000000000000000000000..f3f7ec3f39cebb0aff818a5a62260b94607f957c GIT binary patch literal 642 zcmeAS@N?(olHy`uVBq!ia0y~yV31>AU{K&-V_;xlbvRMaz`*3`>EamT(fRh;?(E5l zB5WV#FZ9q{C{m~q)a5L=d{<|)g=L3@wMgN{j*gE14U3yC#0n>z<6ETEr{vn@+}^Zc z>(-`>qug;B*?UdCJuA3(;<`!vtQkTwH4l{YCw;d)|MT4MIo$s*D&=_37vI3YJHTgu z?J<#K2EE}wwq4~rldStZ@?dq(qnM&~aUz%h_t_6;0I^ z7kNHP?Nd>1?dfIW>pG$HbWZhcosUvt1!~S!BAaK=`RP1Ie4VS}GRrTk>ejo)9MoU) zXx5zSHLaCX3dE1MN_3qJQ2+LLl|bav=s!OGTRWzFy0@dpP{!Gv_xY_0?TUrRD7Tqa^hmJM)CbAR?6Xzp9lpLLZtd2>)t%E_85`%WRmnU2ZOgse zUXSzwZ+1joxUTi+Ur%pY%$11!|FpMseu{`RQZEP-nbG$oLUHYGRoyA>^Y6|G+;7Ca z$cwkLz&m}j;Gc%$71Q^XFW4-SAg-8{do~1_lNOPgg&ebxsLQ E06gp|NB{r; literal 0 HcmV?d00001 diff --git a/asset/img/enochian/char(69).png b/asset/img/enochian/char(69).png new file mode 100644 index 0000000000000000000000000000000000000000..1ef376f6207d13c3c300dc64042daa8db452e692 GIT binary patch literal 395 zcmeAS@N?(olHy`uVBq!ia0y~yV31>AU{K&-V_;xlbvRMaz`z*f>EamT(fRh;elKQ6 zf#V3_y3#fk=*)IiO(>6@_*%+<2o^iW$!7!5#2jyZ`!o@ z&9Q8=RtWs{6|1%8e#d$K#o|U!pJd_PH|MQPJdnTR5a+o9dC3H6>zVIQu6j@{^C0BJ zsxGTT;VXFZlI6vnu7!S`aQnJObV$ub(Y!|+0xEhK#f{%_Nb+;1e#lsK@NPVV{Z(yvdug}1ZqJ~Qt^ zz3SC@?{7aSURcMUu}AHNO<(Kh8qEmrjCQ@v4*$aByT1EG>)iSGmVtqR!PC{xWt~$( F69A~*v>yNf literal 0 HcmV?d00001 diff --git a/asset/img/enochian/char(70).png b/asset/img/enochian/char(70).png new file mode 100644 index 0000000000000000000000000000000000000000..cc4cd3dfc62ab852c32ac9313c399805163fdc82 GIT binary patch literal 547 zcmeAS@N?(olHy`uVBq!ia0y~yV31>AU{K&-V_;xlbvRMaz`*#*)5S5wqx0?cz1hKm z5^Nvj1Dz%aba_lw;*(Xe=oI=X+sWQ!l>78WZOd$?2YyU-}kLzSGgnzh^@ANz3!U=Y8HIKmGp??;C-i>UI}OmR@f&Y0rM^?9O#YHj<{7ZLnr z-)^qxWM=hzy`yjClUnKL1?wN!Ngdj|;fD- zR)+uHAW*;kBb)pO=emEU8|^>;5e@&4DNxuYyrbLOLP~b!$-t!w%i4EM%;Q>aD)j9| z*c?ZT=J%41cx8_1_&qwY!Y`-Y=IQniJ^^3cWoEH_jNE&1*|sc}P<|)oexXIB4_8>b z+Xb6x&bcm~%YD2xC*$F)8pFQUT>`OYx1=Yo5ITAJQY6=}bbv zt22vK7Y3DHI94npvMSs(|Kg4nk6Qn|{2cVr%t}O0#YMWx`o>w$31+P(C-1#owBW(@ zi=CHFKi;x-X`!#jOs>9}4)^>XZ(I9)=cGUDLjun}>ie^@@WsSs99lCr|2?zyLiyJT zE|F(A=PvEk|D@g@B;{~x*SrT?K4@OrsF^5qQ`)nekL$nMKZaWl2d0#Mp61WMz`)?? L>gTe~DWM4fsbvI^ literal 0 HcmV?d00001 diff --git a/asset/img/enochian/char(71).png b/asset/img/enochian/char(71).png new file mode 100644 index 0000000000000000000000000000000000000000..2f43a22cde4b4cf9884bb77aad6bee6e13f2ab47 GIT binary patch literal 652 zcmeAS@N?(olHy`uVBq!ia0y~yV31>AU{K&-V_;xlbvRMaz`zvj>EamT(fRh8bw-Gz z$gz*{(^OjooCGE+h&eiWC@Lw5EfQ*S<=QC1y-^@gFh{4>aYDkv1jVF@PDc(oUGZ|! z32~`WSoZO+gTVZZD=f((zFT(eezSM;?(grOB|c?8d~fDle%cXdcTbieBKcEg)uX%XqoWykKUJFPrfQPOJ8f^(N1@x1DRQe zzcSR!KDF>)Oor=Lf95};-3#Y`lLIWz#qY0fd|3HrXYjYPOJ+rF)R3s2 z%2p>-opX>$_D*+SpzQC#fWU3^)9D*Qlga5)XQ63ULVUmdEKN(w&}Kq^UO?7TMOl% zer~R*ABE()ys{_1II;GS*GcUUvTqKC{_#sxopM<18X049bk?0-k_er1Le?8tQ_K5Vc$r5}F P3=9mOu6{1-oD!MAU{K&-V_;xlbvRMaz`*p_)5S5wqx0=G-;Qaa zBJ3aL^B+EKz@vCrK}ku-nR_CKV5ZR7_;Ac=$PIqbD$OXrl#5&Ldu_kHjC&mX<6t+Ky!wD|n-|2EI(RiArW|HnPz z>IVD1@{NM~AKBF%PPNJSxLqzdqTTx9JG;E2@56i^nuu+jv}U)3c(Z3>U$c+?l+zMksu70jK=+ zl$3O#h<##)vB}D8|4SEHPv-ir=69YW^Gsld-G6z5OAb8ix!kuk$p;_Ixl_|r9woD9 z{eK_t*|xf7sTZEc>nHd&&U=-!?!k01_WO^{=e+0J_gX>kB&p#%C5zBX4@jrUYb~CKzf`atH_RjnBy$*`Vafz14YWx*xEm*%h>XGxC zRf=`aR}W6xxL%}(Q+8>|hTX?1VqTn&&9>S6`eDkW-~5r~yJv7bZ~6GmXoLUMv!V+b zzsf$H-dPYS(VhK|F;Q_&rIkvS^x1zNGqmOkUotZAwK927q~;aAEx&1CuN>q1|`Z>4`N&JmAm z+u3|sme1^Ez1-CF-MJD$PN&LK{cim;+iE>cQ2s4P8H24%*j}@9e3{$hN_C%`+NAF` zP>Y$!>MY{kbiSpby!zv_&oZl9^}Z<0IKugr|5&~3+~>c{@7QX8d^SP(4FdxMgQu&X J%Q~loCIFy6Z+8Fy literal 0 HcmV?d00001 diff --git a/asset/img/enochian/char(73).png b/asset/img/enochian/char(73).png new file mode 100644 index 0000000000000000000000000000000000000000..50ce227ab28f26c35063f5bcd5344865b6109064 GIT binary patch literal 361 zcmeAS@N?(olHy`uVBq!ia0y~yV31>AU{K&-V_;xlbvRMaz`$tW>EamT(fRh;MlWYW zkz*g{Pw(~+6)X(hrFF@1ZVgMiV9963J2It3Uszex%hTsO)^M{1&gq(>6twDa?nALt zZ_nmtYy5CL;aN1P-p}LE!}$6oJLl9ktma(vv&FciYtE%Pd)ww3HpHrAe0$}zlr>ql zX3^cvyZ8QDef|Cs_In?DN@J$neQHqPUKUecafCa{O-9!5lv8_S^o}FFI-3O6hOurh z?E3AH5Pv%KApehb9IZ!JJ@_UO5W!I>SNWDXyDc1b*({C* z@f0Y1I6dcQM{uOl$L9~CbP8gZJl`T~l_>OpWw+r0?TjfYrk{EXLyuqMm|rRLaNB*s z62&%+qZ<#tFJgYqw)v8;%&AL{Iy2<+e`rR=eYRe=$~NY7(b}|=mZ^W2EswwJVp4xV TpW!eA0|SGntDnm{r-UW|R5qRT literal 0 HcmV?d00001 diff --git a/asset/img/enochian/char(76).png b/asset/img/enochian/char(76).png new file mode 100644 index 0000000000000000000000000000000000000000..cf7f85ed669221694f21e3976d42cb9361750cc2 GIT binary patch literal 594 zcmeAS@N?(olHy`uVBq!ia0y~yV31>AU{K&-V_;xlbvRMaz`!Kw>EamT(fRiJ{_NmD z5%v%M^%6}Unu;AFqJr!%6}Xg?9zA&Q>{Wok92XgxkU#t~GO||>dOFT&>ads+B;q1) z&~w9r2mY%bENDJDOJqJKyitt9!_^b5HfY=Xa{#eV_9|zMfN=_4{>$UgjIk zns*LdZL0e%H1C*l%~7v&QWElPFVpV08Y|}9R#H2qcDH4xTubHcaJ82z&)5}W5B{{b zl>VZ3tWdgr(pUM4F7+QhTHBJjwSL$w_&?90aoW|MSv#7WBl<7i**;aL;n0e%SGkk7 z6#)8r%@sjQlbr56?YWvY|Hx*UkF|k5 ztrO3`l{yf^2(f~3Pqz`EvFxed}`e?g*9AaWrNO>YyL_3pL|uhw;5E< z`gnTI<-)tOcKdohn(*zhregQohE*2oReCidwcl$cpZqoG(zd$)(I>L+cVeW?Mw`B5 z=fFo>c9?Z7UaOj2DI@0ed4oo!T&d0VyYbN{-$$xy-_Vd#Y5DFlP4lO;VfokBL7^9| zma{4DpJ!P&-7?R9e%rgr8ILAUxN2$GZMrEzo;&w{8+Wt7{Ih27FGbc|RyytIHvh`A z)*oM7W8XivZVJy`$a0hK3%skcfY|lwny8O?nRyWw*B^#8TWP>G?ng{ z$r|)SpQq5&`e@0{P@fs43t3GS%|s1bzopr E01wm=00000 literal 0 HcmV?d00001 diff --git a/asset/img/enochian/char(77).png b/asset/img/enochian/char(77).png new file mode 100644 index 0000000000000000000000000000000000000000..150db4a9c13fe208a77cb89ebd4f67c354eca2e5 GIT binary patch literal 554 zcmeAS@N?(olHy`uVBq!ia0y~yV31>AU{K&-V_;xlbvRMaz`*#?)5S5wqx0=G`)mwP9)ycW8qUy^n#0Y_l$K?HD-GA znC3s0+2*!ft$?L{Rm-)H3%IL-EiP8B3gw)4Y&XZ>rshz+d7;~%^QwF-R%$8N-7g;@ zT#;yc<@1eC?LQY=$j)DCn*Oz*v?XTVmED`A^b#f0S26T+F5S4hV9zmE#V?%OIIaIm z92eiXo2x=;$yN5h-5%HSI3ok2OcyrAey@)EzImI#uS2h{=Z1aYS$;uimg_1pxyP#a zxF-JWS^DCGmt?`J^*^Tkd}rt9lA-7^kL~1P^VEf|2XD&uuYRB)%4mOT&-=hNtEOI? za*z99s;GIF&DD()7|jcJod3hJ*~wb>?VUCE?&Lnw>PuXIVe#{p{mW#Jf1l2J{ptOK zm%JZry5O}rncJyTcWL3mKJk?)cf*%y7i~&-EOp?1K{&_LWf@=hui5CBp|Jjgfwe)0 z+XiDjtLC>CJH_rDf6n)&`}4Gd5RI)%54f6Y$?SRj@vqH3{+TxyZ_Icge*e^>XTFcW ScQY_BFnGH9xvXAU{K&-V_;xlbvRMaz`&&J>EamT(fRheb+$;L z2>XZm-VrJ+ktQq`6_msrRa!kJgouT4acR0rbK5%#IPPz3Y7OPm<`(px*r8-+5OHG# zqi{$_jiQ#Cok!T>&Z9F+)eom_-khh@RBv(g&d%b0&+mNRb+}$fI4otJ`9fy-&gZHPINS=qZdV(*qZ zT>a*}_m!~Zx4e3f_nmn6Rq1y12G*lz{`v7F%KAMsUC<=V>3D5{nc(@g5=SZ@X5RX& zZxQ31u6Qw0{4K9kgv$}jBoLLZtmHru^9e9rY- z{%Pl!BY6`gRM#AgDp6meI?lFacBT=FjFI_4*)a?h0r{aO;Sbz1f!QRA%*jDnN6<~^$B zF1hZyz9ZRc8*}B!;9hl-8wNVe7Y7zU!8ToT}ts(+SeJc zQrGJkeNw!qmL0oj*>0he=bmZbP<~dSXHv|(^2CZAEM~1~9m@+t&+QK1ytgV$=6~)S z(eiiay@(MjCgFg0?xAu3(JN&;f)t4#d00RR9 NgQu&X%Q~loCIGS%2u1(^ literal 0 HcmV?d00001 diff --git a/asset/img/enochian/char(79).png b/asset/img/enochian/char(79).png new file mode 100644 index 0000000000000000000000000000000000000000..44ceb8e0ddcc1e8efce83f313d6fcdfa6bacec5d GIT binary patch literal 552 zcmeAS@N?(olHy`uVBq!ia0y~yV31>AU{K&-V_;xlbvRMaz`*$4)5S5wqx0>x-QB^C zBF8`WE3yc3I_8MPs&sU8bWGI|6XPy>6rZ4YtK)=HQs(ZCh>oeP9jzQ)5gqwDDWdXo zTe-}{4o($4DsFe}?whDH8A;b>rR2gpAme}|D$g58{c^kIBU*JR8QCxp!GvJ?caxb zw(Fl*rW@y|6`1-Lm>#-q+Wq;E*IE2tPa~^E;1#+^iKDM=@FAN;)S$gT{Gs^UG2?_4m%Xb!#y>V zf8O^Q@yB=lh*R6*WUABkL3zi`)3*f|_Rl)@ugB8)hHA>6=$n6UoKH{Je<2{}{vvSy z`!i882Q2K4Snh48ZCvdn=KpZVwVOPT*A&d1Rk$$IFP%khsq^MenH_8j#%wn$jI zFzUuBwYUdDZi|gebp*M*M73M0_xWeEOXc>ZHJQ7tcVGO`aL*y@J6&z7zt;C_AG-KP zY+cfIqur9~izi{|5^WATuKDF2G3gYSMyEXkM`suRT+wg=1_lNO MPgg&ebxsLQ04qxTTL1t6 literal 0 HcmV?d00001 diff --git a/asset/img/enochian/char(80).png b/asset/img/enochian/char(80).png new file mode 100644 index 0000000000000000000000000000000000000000..237b2c0992ac9dbb44cc4c442663d771fec32783 GIT binary patch literal 662 zcmeAS@N?(olHy`uVBq!ia0y~yV31>AU{K&-V_;xlbvRMaz`zvi>EamT(fRhebw-Gz z$gz*}g_=58I2AoO0(Dd)Iy6`|@+e8X*&=Y!PlrV`dn1=pn3z(M?jvRvXO_1*TLcA7 zA2loN>Qr*{HF=UKr28zc{9BrQd3o@!g9Xpu%{Bf%(>VRvnNRUQx9*aB99p_1w)^di z!+s^nGWj2+A;amiv(BbL~ZrU~*Vp_e!E|xslE9pTeS8_K;%dUJ8oRIZ_@zqOi{FV~ANDwK+4_Y!%@NXze>H7b zyG)8fpVK{k%_QBatmW=s*H_$NFu5wcv{{eyj7~*ui2j3Q;Rk{~F7Lgs9{Bh>V_V_e zUurydp(iJ$9^Aj^SIe`nOPy*ZE&f^TW_qxr|NGSg`HQMfb2U6}s0!tqW9M!#|BAHN zBgs;=^O~Y*dweZ4xPDChbM2x^N$&&?j@^%!J~;YKVvn;2)8ej)HAU{K&-V_;xlbvRMaz`$7Q>EamT(fRhet+z~| z$gz*}H+6J$bg%?=hKV@_MYK%WXg+motAdBuZIQ-=$Ke4R1A;Py|F9`Bayw6mSfjtR zHHu}C(3(kCpYg?+?>_y;>P^G11Fg^JS%3fYoF`q+*k-k7R_C8|pOBsFI#y1xv`^Y} z>Xv+s!$twtc0;im&b6A3Po=s(O?k~VS4%}dQG9bmzJSd0sKZQr*|9siY`?Vs)W|#Z zBapqo@XgP@c>>kF^LHGP{#NoZ&W5d>e}3*_F!99cke!<%qwsIEw20XfT`*;Lvgv9iR_)};uQ+| z1$m<1ch!5C^4@9fvpI0LvT=Ug(Yd`1+IcI2*RGlqCmL=0BJ_#-hPgs!L1yyD(@yQZ zwc+8r*{>BJGw5G#y8p1;?Lqwy6<2%SN6~H#K; o&#bsx(z~ife+IXm{9UH#g1V5$yJi(KFfcH9y85}Sb4q9e0N!@L#sB~S literal 0 HcmV?d00001 diff --git a/asset/img/enochian/char(82).png b/asset/img/enochian/char(82).png new file mode 100644 index 0000000000000000000000000000000000000000..fa592935ff14999163aae1aebd22c4d31b6fd7d7 GIT binary patch literal 679 zcmeAS@N?(olHy`uVBq!ia0y~yV31>AU{K&-V_;xlbvRMaz`&H}>EamT(fRh8b%scw z2-}16rJPEENdba_-3oRWoVI*u6-!#EsWP==$tHn=_YV1~bVPKtcC_C3bwDeE)9!+s zi>u4VzzHrM3NwCmtUq$((U}=H6U}DN=$vXVQ}(>*{NML`pU?Rp^Y5{I;<@-AI!Co~ z4poI1wzXPxFJJnWPc-MKtIhHBp659%?Wa_74+ZjwO?djYB67*+0;f641YcU{O>w-{ z68t07;X*U}i(}nqeyla>D1R8;_d#%5_l)H$7Rj1VK6{2kH0IRAIJeU(cD@<&G(RTU zoSSe@^rIf{RIA5pBWC`r>v;daX~)eYTl>0h>56{suwSTeoIcxQ(ItcJ=Y9yZZPcH+ znU(tkSA46>IjLt0z1LRM95H<(9%-Y!B4KgBVx^~2ZuavY_(V$IR0+w5*jV)RExT2^ zQ^Y~WJ+ql2@8$WoYuxVFw&=O++1U!Ih{FjyGI9APB;NksZX+}p3E zsxGb>M>n7Ovwz)JzpWql)p%xD&t|4e(|+%3)90;<}MZgc$S^83Y*lbewd+*OLzV9l+^`t)vZ$79y)tX zSfu`Z%VNgDT{o+i$Syi^%U5Q}+KfkXTh|{q&x(E=ve75R`QqabArqauH9AW!>Bo4A zX}wAko%mJTMf2S;&pjdWZ+8Vh|I_@hqr-Dn^68U!&J} zUR!&xW{&V9d(qx`XWU$B`PE*3?Rb9W(;{Kv@Eu`$)XbB&dUQQYS-$eGNa;V-ot^$l s#lKyT@v9~?D^0$AU{K&-V_;xlbvRMaz`(fI)5S5wqx0>x^WD9H zGRHqo|ESCx>5;ZcRr_wMi>H`L#0tSwZB@-v=Pq5lH9tW&RXN2pD@{@Jrbn8FUXo9p z$z@slG@WymMbG%Y&wF0=Y-X?jTr1n>HT&PcuiSV4bMyaIp4yZC<>stA@!q`VkjlLW zhQ~f^nz2D^=A8r6c$ODuJoGNHYwhI7cvQ+FmRx(ILw@CCkvp2dJZ%m$+jLI&ULijB z{MMUmj`PhqmS=PPs>Br~Q^WaN&hO}ZEOCZoLjZHRVXvUfrPM~&AL@BWI!yHT;c5DGxiPT)4ooQWr#JI@WuQJr{Co%TX|PAZJ+cnQ8F}Ev_k9U`{Uns z9nim36BkmX|9w@g*u2k|EtVZV6c)yk_pqhl@4+mY13Gpb?jM_^*Jypv*_P!0DOa}5 zpr^U8#%)KC_SLwDYv#SbuugRHYAwBrmiC>KSF>y{NVPmrAY49;ndiCc`jb`%g)N@y z^Y32Da`-Pt{Vtb(yH_<<|5%r6eRZL))7JVm(moz9?dGhM-s7>KA!B*w=JhAn*fTIN OFnGH9xvXAU{K&-V_;xlbvRMaz`$7M>EamT(fRhOt#@>y z2>XZmLMkZ}0%W>G3JWH)-E!=Dpmb@Wg+=ENMynmOHgeRp^sSXYcro(UrGu^|Z!T?J zx{r6Qr((xFKL1N``n;99zt6NOXuIS6>G8bkPxTcm+D|QC_Q9ey(T2&Y@#`8<=D1_Q zHT?;H*ScFAa{K0X(SMKbp4*d~=2m}Y61Q;B6pK2jD3o*5X)oz7$6^lk2z+DT@s3H&;&6$*{g3_GQ_JiRDoy(!v$@Lt zWc8mHto^BW*FUb|J;$kjT0wdD5u0=DKlpwu`pr@Ol{b!`tB>n`Qhr29#E#bTLw+vJ z`#GQIR*IAe?C7jbyj=Wq|F@>uha`8W9l7|8`98UUTHS p%>lnu=9Hj_DQtDmIa6Qr@0D4|l=9wgDgy%pgQu&X%Q~loCIE%xz`y_i literal 0 HcmV?d00001 diff --git a/asset/img/enochian/char(85).png b/asset/img/enochian/char(85).png new file mode 100644 index 0000000000000000000000000000000000000000..fabfc2d57ce1dc96ff497beeeb1ef26f1f013ed9 GIT binary patch literal 639 zcmeAS@N?(olHy`uVBq!ia0y~yV31>AU{K&-V_;xlbvRMaz`*3@>EamT(fRh;{%nyz z0rn65uLYZ07C5SObWDw4;cRaa5nGhBG%PDwT`W*(MMOvI+>ZLjh{%rCj*bj%p{9r< z3jbc1++g96>UHewQrlQjosz$~diQMQu)2o_@4Y=!bAQjwpZlKL{}a5H!2YY~p8f;% zXCGwaKN!~KNsDic*ie_uCS1|=+M=U(weOtmGYxsxmoRIq=B=shdCahA(-PM%5GVka) zts{#I7eq-eXW8~BvFi_?Rkw`)#q-*j&^rWRjggg zmMMPuM#J5Y?>WA=R>qfp$+mlEqq**mN2W_t)>n?$f9fCCakqb5=F6wkd%@I3cvIHW zEaew@?HjnAD>S1`K83yW5tl9Ht!^!R?SE6BgV(fk<&VlJi>V85Y?E$ox-!kpT#x&D zqEYwx9jaIIcLzLP5>fKV`Ov?0T#s)xOCQou@r>74o~O(?|N0AE=Usj~UOW$8l0P+I zvBY-M>y-;mC>~<{vuTg#D^<^jX{YK}oJb7V{Ki!8m$#?~XQ}V89oJ9o5boEN@07f% zKjC9Y&-WYC(raA4_4I|Q#OhB``&(P4lzAoNvy|V-ea`noRqjm{T;BHhtUg2M yU9UUKc};r#N~TL3?$JjwAIQjwrf$gk!R;^eIy|#vRtp0I1B0ilpUXO@geCy7W+CGM literal 0 HcmV?d00001 diff --git a/asset/img/enochian/char(86).png b/asset/img/enochian/char(86).png new file mode 100644 index 0000000000000000000000000000000000000000..fabfc2d57ce1dc96ff497beeeb1ef26f1f013ed9 GIT binary patch literal 639 zcmeAS@N?(olHy`uVBq!ia0y~yV31>AU{K&-V_;xlbvRMaz`*3@>EamT(fRh;{%nyz z0rn65uLYZ07C5SObWDw4;cRaa5nGhBG%PDwT`W*(MMOvI+>ZLjh{%rCj*bj%p{9r< z3jbc1++g96>UHewQrlQjosz$~diQMQu)2o_@4Y=!bAQjwpZlKL{}a5H!2YY~p8f;% zXCGwaKN!~KNsDic*ie_uCS1|=+M=U(weOtmGYxsxmoRIq=B=shdCahA(-PM%5GVka) zts{#I7eq-eXW8~BvFi_?Rkw`)#q-*j&^rWRjggg zmMMPuM#J5Y?>WA=R>qfp$+mlEqq**mN2W_t)>n?$f9fCCakqb5=F6wkd%@I3cvIHW zEaew@?HjnAD>S1`K83yW5tl9Ht!^!R?SE6BgV(fk<&VlJi>V85Y?E$ox-!kpT#x&D zqEYwx9jaIIcLzLP5>fKV`Ov?0T#s)xOCQou@r>74o~O(?|N0AE=Usj~UOW$8l0P+I zvBY-M>y-;mC>~<{vuTg#D^<^jX{YK}oJb7V{Ki!8m$#?~XQ}V89oJ9o5boEN@07f% zKjC9Y&-WYC(raA4_4I|Q#OhB``&(P4lzAoNvy|V-ea`noRqjm{T;BHhtUg2M yU9UUKc};r#N~TL3?$JjwAIQjwrf$gk!R;^eIy|#vRtp0I1B0ilpUXO@geCy7W+CGM literal 0 HcmV?d00001 diff --git a/asset/img/enochian/char(88).png b/asset/img/enochian/char(88).png new file mode 100644 index 0000000000000000000000000000000000000000..caeb854ed5e28cbdcc9f87df8925ceefa68f9181 GIT binary patch literal 381 zcmeAS@N?(olHy`uVBq!ia0y~yV31>AU{K&-V_;xlbvRMaz`*F@>EamT(fRh;UN7cA zfwqUo1)Kypg>+q-A|h_w>@YJcyVtPQT=AP&Skv}JX7@U_P3!8l|Isb9_-NW$HMQ4~ zcXT>_CUo7Ed^Y!{@$)x{OD480*(h?~EUfs^odogeiU-?&NF==1+*eoDcr@ixkprWy z%tIdkitPQX7j$Ww7+m*@<9?~-TXVh`t8`1-=ib>gggsx_)-L^%8Sr*C>OJ)C35n$5kZA~d*S zj=DUmT&eM4M~3v#C{3=I%eE&!7sWkyd=TmL{Yb6|W8C}4w@sE5TZb_(-xnVkztK!5 oL-h9|?pwL?*4J#`*89&7c5t_xlPOfhz`(%Z>FVdQ&MBb@0A7%!cmMzZ literal 0 HcmV?d00001 diff --git a/asset/img/enochian/char(90).png b/asset/img/enochian/char(90).png new file mode 100644 index 0000000000000000000000000000000000000000..b3348dfbca03b41140130a453096e0b45b7dfaa8 GIT binary patch literal 587 zcmeAS@N?(olHy`uVBq!ia0y~yV31>AU{K&-V_;xlbvRMaz`!Kr>EamT(fRi3!S3Eb ziQ^yp3n!OMZp(9Xp0z|%@Q{<(rU|@}H#Ag(PYG%NZ_r86OgruNlUZkiO5mgj53$Kt zuCyFml5HS){n4J1`{n0$e&1XE&PQyXRq4IYcISW2`#h(3p8WsZi8-DrZ-n@!b<90z zz2|868v8c=yLxFNy?nQI>>hEROYk*+&>h>*{Z_DS;duCvwtQpTHV^|cIycLoBzedX#cVAyB=Au z*6g3k?R99D?$*aA*DS43`>={fAvN`jk(yNV#EtWsmn$bni!<8@OWszFbE)F$W8B6m zv}3`883|vwuVyM{L-P@nKpV6c1u+Hvh%RewMFfe$!`njxgN@xNARKF3t literal 0 HcmV?d00001 diff --git a/data/MANIFEST.json b/data/MANIFEST.json new file mode 100644 index 0000000..e2b0e4c --- /dev/null +++ b/data/MANIFEST.json @@ -0,0 +1,50 @@ +{ + "generatedAt": "2026-02-22T03:40:25.496Z", + "source": "data", + "output": "data", + "totals": { + "json": 37, + "assets": 1, + "files": 38 + }, + "files": [ + "alphabets.json", + "gods.json", + "numbers.json", + "playing-cards-52.json", + "alchemy/elementals.json", + "alchemy/elements.json", + "alchemy/symbols.json", + "alchemy/terms.json", + "astrology/houses.json", + "astrology/planets.json", + "astrology/retrograde.json", + "astrology/zodiac.json", + "chakras.json", + "enochian/dictionary.json", + "enochian/letters.json", + "enochian/tablets.json", + "gd/degrees.json", + "gd/grades.json", + "geomancy/houses.json", + "geomancy/tetragrams.json", + "hebrewLetters.json", + "kabbalah/angelicOrders.json", + "kabbalah/archangels.json", + "kabbalah/cube.json", + "kabbalah/fourWorlds.json", + "kabbalah/godNames.json", + "kabbalah/kerubim.json", + "kabbalah/kabbalah-tree.json", + "kabbalah/paths.json", + "kabbalah/sephirot.json", + "kabbalah/seventyTwoAngels.json", + "kabbalah/souls.json", + "kabbalah/tribesOfIsrael.json", + "hebrew-calendar.json", + "islamic-calendar.json", + "calendar-holidays.json", + "wheel-of-year.json", + "pentagram.svg" + ] +} diff --git a/data/alchemy/elementals.json b/data/alchemy/elementals.json new file mode 100644 index 0000000..09bc813 --- /dev/null +++ b/data/alchemy/elementals.json @@ -0,0 +1,54 @@ +{ + "gnome": { + "id": "gnome", + "name": { + "en": "Gnome" + }, + "namePlural": { + "en": "Gnomes" + }, + "title": { + "en": "Spirits of Earth" + }, + "elementId": "earth" + }, + "sylph": { + "id": "sylph", + "name": { + "en": "Sylph" + }, + "namePlural": { + "en": "Sylphs" + }, + "title": { + "en": "Spirits of Air" + }, + "elementId": "air" + }, + "undine": { + "id": "undine", + "name": { + "en": "Undine" + }, + "namePlural": { + "en": "Undines" + }, + "title": { + "en": "Spirits of Water" + }, + "elementId": "water" + }, + "salamander": { + "id": "salamander", + "name": { + "en": "Salamander" + }, + "namePlural": { + "en": "Salamanders" + }, + "title": { + "en": "Spirits of Fire" + }, + "elementId": "fire" + } +} diff --git a/data/alchemy/elements.json b/data/alchemy/elements.json new file mode 100644 index 0000000..5e08ac9 --- /dev/null +++ b/data/alchemy/elements.json @@ -0,0 +1,42 @@ +{ + "earth": { + "id": "earth", + "symbol": "🜃", + "name": { + "en": "Earth" + }, + "elementalId": "gnome" + }, + "air": { + "id": "air", + "symbol": "🜁", + "name": { + "en": "Air" + }, + "elementalId": "sylph" + }, + "water": { + "id": "water", + "symbol": "🜄", + "name": { + "en": "Water" + }, + "elementalId": "undine" + }, + "fire": { + "id": "fire", + "symbol": "🜂", + "name": { + "en": "Fire" + }, + "elementalId": "salamander" + }, + "spirit": { + "id": "spirit", + "symbol": "☸", + "name": { + "en": "Spirit" + }, + "elementalId": "" + } +} diff --git a/data/alchemy/symbols.json b/data/alchemy/symbols.json new file mode 100644 index 0000000..28ffefd --- /dev/null +++ b/data/alchemy/symbols.json @@ -0,0 +1,109 @@ +{ + "sulphur": { + "id": "sulphur", + "symbol": "🜍", + "altSymbol": "🜍", + "name": { + "en": "Sulphur" + }, + "category": "principles", + "gdGrade": 1 + }, + "mercury": { + "id": "mercury", + "symbol": "☿︎", + "altSymbol": "☿︎", + "name": { + "en": "Mercury" + }, + "category": "principles", + "gdGrade": 1 + }, + "salt": { + "id": "salt", + "symbol": "🜔", + "altSymbol": "🜔", + "name": { + "en": "Salt" + }, + "category": "principles", + "gdGrade": 1 + }, + "lead": { + "id": "lead", + "symbol": "🜪", + "altSymbol": "♄︎", + "name": { + "en": "Lead" + }, + "category": "planets", + "planetId": "saturn", + "gdGrade": 1 + }, + "tin": { + "id": "tin", + "symbol": "🜩", + "altSymbol": "♃︎", + "name": { + "en": "Tin" + }, + "category": "planets", + "planetId": "jupiter", + "gdGrade": 1 + }, + "iron": { + "id": "iron", + "symbol": "🜜", + "altSymbol": "♂︎", + "name": { + "en": "Iron" + }, + "category": "planets", + "planetId": "mars", + "gdGrade": 1 + }, + "gold": { + "id": "gold", + "symbol": "🜚", + "altSymbol": "☉︎", + "name": { + "en": "Gold" + }, + "category": "planets", + "planetId": "sol", + "gdGrade": 1 + }, + "copper": { + "id": "copper", + "symbol": "🜠", + "altSymbol": "♀︎", + "name": { + "en": "Copper / Brass" + }, + "category": "planets", + "planetId": "venus", + "gdGrade": 1 + }, + "quicksilver": { + "id": "quicksilver", + "symbol": "☿︎", + "altSymbol": "☿︎", + "name": { + "en": "Quicksilver" + }, + "category": "planets", + "planetId": "mercury", + "gdGrade": 1 + }, + "silver": { + "id": "silver", + "symbol": "🜛", + "altSymbol": "☽︎", + "name": { + "en": "Silver" + }, + "category": "planets", + "planetId": "luna", + "gdGrade": 1 + } +} diff --git a/data/alchemy/terms.json b/data/alchemy/terms.json new file mode 100644 index 0000000..3aa3cc6 --- /dev/null +++ b/data/alchemy/terms.json @@ -0,0 +1,78 @@ +{ + "sol-philosophorum": { + "id": "sol-philosophorum", + "name": { + "en": "Sol Philosophorum" + }, + "terms": { + "en": [ + "Pure living Alchemical Spirit of Gold", + "Refined Essence of Heat & Dryness" + ] + }, + "gdGrade": 1 + }, + "luna-philosophorum": { + "id": "luna-philosophorum", + "name": { + "en": "Luna Philosophorum" + }, + "terms": { + "en": [ + "Pure living Alchemical Spirit of Silver", + "Refined Essence of Heat & Moisture" + ] + }, + "gdGrade": 1 + }, + "green-lion": { + "id": "green-lion", + "name": { + "en": "Green Lion" + }, + "terms": { + "en": [ + "Stem and Root of Radical Essence of Metals" + ] + }, + "gdGrade": 1 + }, + "black-dragon": { + "id": "black-dragon", + "name": { + "en": "Black Dragon" + }, + "terms": { + "en": [ + "Death - Putrafaction - Decay" + ] + }, + "gdGrade": 1 + }, + "king": { + "id": "king", + "name": { + "en": "The King" + }, + "terms": { + "en": [ + "Red - Qabalistic Microprosopus", + "Tiphareth - Analogous to Gold and the Sun" + ] + }, + "gdGrade": 1 + }, + "queen": { + "id": "queen", + "name": { + "en": "The Queen" + }, + "terms": { + "en": [ + "White - Qabalistic Bride of Microprosopus", + "Malkah - Analogous to Silver and the Moon" + ] + }, + "gdGrade": 1 + } +} diff --git a/data/alphabets.json b/data/alphabets.json new file mode 100644 index 0000000..c8f64ca --- /dev/null +++ b/data/alphabets.json @@ -0,0 +1,755 @@ +{ + "meta": { + "description": "Cross-alphabet reference: English (26), Hebrew (22 kabbalah letters), and Greek (24 modern + 3 archaic numerals). Hebrew entries cross-reference hebrewLetters.json and kabbalah-tree.json; Greek entries trace Phoenician/Hebrew origins via hebrewLetterId FK.", + "sources": [ + "Sefer Yetzirah", + "Liber 777 — Aleister Crowley", + "Ancient Greek Alphabet & Isopsephy tradition", + "ISO 15924", + "Pythagorean numerology" + ], + "notes": "Hebrew kabbalahPathNumber is a FK to kabbalah-tree.json paths[].pathNumber. hebrewLetterId is a FK to hebrewLetters.json. Greek archaic letters (Digamma, Qoppa, Sampi) are marked archaic:true and used only in isopsephy; they are not part of the modern 24-letter Greek alphabet." + }, + "hebrew": [ + { + "index": 1, + "hebrewLetterId": "alef", + "char": "א", + "name": "Aleph", + "transliteration": "A", + "numerology": 1, + "meaning": "ox", + "letterType": "mother", + "astrology": { "type": "element", "name": "Air" }, + "kabbalahPathNumber": 11, + "tarot": { "card": "The Fool", "trumpNumber": 0 }, + "greekEquivalent": "alpha" + }, + { + "index": 2, + "hebrewLetterId": "bet", + "char": "ב", + "name": "Beth", + "transliteration": "B", + "numerology": 2, + "meaning": "house", + "letterType": "double", + "astrology": { "type": "planet", "name": "Mercury" }, + "kabbalahPathNumber": 12, + "tarot": { "card": "The Magician", "trumpNumber": 1 }, + "greekEquivalent": "beta" + }, + { + "index": 3, + "hebrewLetterId": "gimel", + "char": "ג", + "name": "Gimel", + "transliteration": "G", + "numerology": 3, + "meaning": "camel", + "letterType": "double", + "astrology": { "type": "planet", "name": "Luna" }, + "kabbalahPathNumber": 13, + "tarot": { "card": "The High Priestess", "trumpNumber": 2 }, + "greekEquivalent": "gamma" + }, + { + "index": 4, + "hebrewLetterId": "dalet", + "char": "ד", + "name": "Daleth", + "transliteration": "D", + "numerology": 4, + "meaning": "door", + "letterType": "double", + "astrology": { "type": "planet", "name": "Venus" }, + "kabbalahPathNumber": 14, + "tarot": { "card": "The Empress", "trumpNumber": 3 }, + "greekEquivalent": "delta" + }, + { + "index": 5, + "hebrewLetterId": "he", + "char": "ה", + "name": "Heh", + "transliteration": "H", + "numerology": 5, + "meaning": "window", + "letterType": "simple", + "astrology": { "type": "zodiac", "name": "Aries" }, + "kabbalahPathNumber": 15, + "tarot": { "card": "The Emperor", "trumpNumber": 4 }, + "greekEquivalent": "epsilon" + }, + { + "index": 6, + "hebrewLetterId": "vav", + "char": "ו", + "name": "Vav", + "transliteration": "V", + "numerology": 6, + "meaning": "peg, nail, hook", + "letterType": "simple", + "astrology": { "type": "zodiac", "name": "Taurus" }, + "kabbalahPathNumber": 16, + "tarot": { "card": "The Hierophant", "trumpNumber": 5 }, + "greekEquivalent": "digamma" + }, + { + "index": 7, + "hebrewLetterId": "zayin", + "char": "ז", + "name": "Zayin", + "transliteration": "Z", + "numerology": 7, + "meaning": "sword, weapon", + "letterType": "simple", + "astrology": { "type": "zodiac", "name": "Gemini" }, + "kabbalahPathNumber": 17, + "tarot": { "card": "The Lovers", "trumpNumber": 6 }, + "greekEquivalent": "zeta" + }, + { + "index": 8, + "hebrewLetterId": "het", + "char": "ח", + "name": "Cheth", + "transliteration": "Ch", + "numerology": 8, + "meaning": "enclosure, fence", + "letterType": "simple", + "astrology": { "type": "zodiac", "name": "Cancer" }, + "kabbalahPathNumber": 18, + "tarot": { "card": "The Chariot", "trumpNumber": 7 }, + "greekEquivalent": "eta" + }, + { + "index": 9, + "hebrewLetterId": "tet", + "char": "ט", + "name": "Teth", + "transliteration": "T", + "numerology": 9, + "meaning": "serpent", + "letterType": "simple", + "astrology": { "type": "zodiac", "name": "Leo" }, + "kabbalahPathNumber": 19, + "tarot": { "card": "Strength", "trumpNumber": 8 }, + "greekEquivalent": "theta" + }, + { + "index": 10, + "hebrewLetterId": "yod", + "char": "י", + "name": "Yod", + "transliteration": "I", + "numerology": 10, + "meaning": "hand", + "letterType": "simple", + "astrology": { "type": "zodiac", "name": "Virgo" }, + "kabbalahPathNumber": 20, + "tarot": { "card": "The Hermit", "trumpNumber": 9 }, + "greekEquivalent": "iota" + }, + { + "index": 11, + "hebrewLetterId": "kaf", + "char": "כ", + "name": "Kaph", + "transliteration": "K", + "numerology": 20, + "meaning": "palm of hand", + "letterType": "double", + "astrology": { "type": "planet", "name": "Jupiter" }, + "kabbalahPathNumber": 21, + "tarot": { "card": "Wheel of Fortune", "trumpNumber": 10 }, + "greekEquivalent": "kappa" + }, + { + "index": 12, + "hebrewLetterId": "lamed", + "char": "ל", + "name": "Lamed", + "transliteration": "L", + "numerology": 30, + "meaning": "ox-goad", + "letterType": "simple", + "astrology": { "type": "zodiac", "name": "Libra" }, + "kabbalahPathNumber": 22, + "tarot": { "card": "Justice", "trumpNumber": 11 }, + "greekEquivalent": "lambda" + }, + { + "index": 13, + "hebrewLetterId": "mem", + "char": "מ", + "name": "Mem", + "transliteration": "M", + "numerology": 40, + "meaning": "water", + "letterType": "mother", + "astrology": { "type": "element", "name": "Water" }, + "kabbalahPathNumber": 23, + "tarot": { "card": "The Hanged Man", "trumpNumber": 12 }, + "greekEquivalent": "mu" + }, + { + "index": 14, + "hebrewLetterId": "nun", + "char": "נ", + "name": "Nun", + "transliteration": "N", + "numerology": 50, + "meaning": "fish", + "letterType": "simple", + "astrology": { "type": "zodiac", "name": "Scorpio" }, + "kabbalahPathNumber": 24, + "tarot": { "card": "Death", "trumpNumber": 13 }, + "greekEquivalent": "nu" + }, + { + "index": 15, + "hebrewLetterId": "samekh", + "char": "ס", + "name": "Samekh", + "transliteration": "S", + "numerology": 60, + "meaning": "prop, support", + "letterType": "simple", + "astrology": { "type": "zodiac", "name": "Sagittarius" }, + "kabbalahPathNumber": 25, + "tarot": { "card": "Temperance", "trumpNumber": 14 }, + "greekEquivalent": "xi" + }, + { + "index": 16, + "hebrewLetterId": "ayin", + "char": "ע", + "name": "Ayin", + "transliteration": "O", + "numerology": 70, + "meaning": "eye", + "letterType": "simple", + "astrology": { "type": "zodiac", "name": "Capricorn" }, + "kabbalahPathNumber": 26, + "tarot": { "card": "The Devil", "trumpNumber": 15 }, + "greekEquivalent": "omicron" + }, + { + "index": 17, + "hebrewLetterId": "pe", + "char": "פ", + "name": "Pe", + "transliteration": "P", + "numerology": 80, + "meaning": "mouth", + "letterType": "double", + "astrology": { "type": "planet", "name": "Mars" }, + "kabbalahPathNumber": 27, + "tarot": { "card": "The Tower", "trumpNumber": 16 }, + "greekEquivalent": "pi" + }, + { + "index": 18, + "hebrewLetterId": "tsadi", + "char": "צ", + "name": "Tzaddi", + "transliteration": "Tz", + "numerology": 90, + "meaning": "fishhook", + "letterType": "simple", + "astrology": { "type": "zodiac", "name": "Aquarius" }, + "kabbalahPathNumber": 28, + "tarot": { "card": "The Star", "trumpNumber": 17 }, + "greekEquivalent": "qoppa" + }, + { + "index": 19, + "hebrewLetterId": "qof", + "char": "ק", + "name": "Qoph", + "transliteration": "Q", + "numerology": 100, + "meaning": "back of the head", + "letterType": "simple", + "astrology": { "type": "zodiac", "name": "Pisces" }, + "kabbalahPathNumber": 29, + "tarot": { "card": "The Moon", "trumpNumber": 18 } + }, + { + "index": 20, + "hebrewLetterId": "resh", + "char": "ר", + "name": "Resh", + "transliteration": "R", + "numerology": 200, + "meaning": "head", + "letterType": "double", + "astrology": { "type": "planet", "name": "Sol" }, + "kabbalahPathNumber": 30, + "tarot": { "card": "The Sun", "trumpNumber": 19 }, + "greekEquivalent": "rho" + }, + { + "index": 21, + "hebrewLetterId": "shin", + "char": "ש", + "name": "Shin", + "transliteration": "Sh", + "numerology": 300, + "meaning": "tooth", + "letterType": "mother", + "astrology": { "type": "element", "name": "Fire" }, + "kabbalahPathNumber": 31, + "tarot": { "card": "Judgement", "trumpNumber": 20 }, + "greekEquivalent": "sigma" + }, + { + "index": 22, + "hebrewLetterId": "tav", + "char": "ת", + "name": "Tav", + "transliteration": "Th", + "numerology": 400, + "meaning": "cross, mark", + "letterType": "double", + "astrology": { "type": "planet", "name": "Saturn" }, + "kabbalahPathNumber": 32, + "tarot": { "card": "The Universe", "trumpNumber": 21 }, + "greekEquivalent": "tau" + } + ], + "greek": [ + { + "name": "alpha", + "index": 1, + "char": "Α", + "charLower": "α", + "displayName": "Alpha", + "transliteration": "A", + "ipa": "/a/, /aː/", + "numerology": 1, + "meaning": "ox (from Phoenician aleph)", + "hebrewLetterId": "alef", + "archaic": false + }, + { + "name": "beta", + "index": 2, + "char": "Β", + "charLower": "β", + "displayName": "Beta", + "transliteration": "B", + "ipa": "/b/", + "numerology": 2, + "meaning": "house (from Phoenician beth)", + "hebrewLetterId": "bet", + "archaic": false + }, + { + "name": "gamma", + "index": 3, + "char": "Γ", + "charLower": "γ", + "displayName": "Gamma", + "transliteration": "G", + "ipa": "/ɡ/", + "numerology": 3, + "meaning": "camel (from Phoenician gimel)", + "hebrewLetterId": "gimel", + "archaic": false + }, + { + "name": "delta", + "index": 4, + "char": "Δ", + "charLower": "δ", + "displayName": "Delta", + "transliteration": "D", + "ipa": "/d/", + "numerology": 4, + "meaning": "door (from Phoenician daleth)", + "hebrewLetterId": "dalet", + "archaic": false + }, + { + "name": "epsilon", + "index": 5, + "char": "Ε", + "charLower": "ε", + "displayName": "Epsilon", + "transliteration": "E", + "ipa": "/e/", + "numerology": 5, + "meaning": "bare / simple e (adapted from Phoenician he)", + "hebrewLetterId": "he", + "archaic": false + }, + { + "name": "digamma", + "index": 6, + "char": "Ϝ", + "charLower": "ϝ", + "displayName": "Digamma", + "transliteration": "W/V", + "ipa": "/w/", + "numerology": 6, + "meaning": "double-gamma; from Phoenician waw (same root as Hebrew Vav)", + "hebrewLetterId": "vav", + "archaic": true + }, + { + "name": "zeta", + "index": 7, + "char": "Ζ", + "charLower": "ζ", + "displayName": "Zeta", + "transliteration": "Z", + "ipa": "/dz/ or /z/", + "numerology": 7, + "meaning": "weapon (from Phoenician zayin)", + "hebrewLetterId": "zayin", + "archaic": false + }, + { + "name": "eta", + "index": 8, + "char": "Η", + "charLower": "η", + "displayName": "Eta", + "transliteration": "E / H", + "ipa": "/eː/", + "numerology": 8, + "meaning": "fence, enclosure (from Phoenician heth)", + "hebrewLetterId": "het", + "archaic": false + }, + { + "name": "theta", + "index": 9, + "char": "Θ", + "charLower": "θ", + "displayName": "Theta", + "transliteration": "Th", + "ipa": "/tʰ/, /θ/", + "numerology": 9, + "meaning": "wheel; from Phoenician teth (serpent)", + "hebrewLetterId": "tet", + "archaic": false + }, + { + "name": "iota", + "index": 10, + "char": "Ι", + "charLower": "ι", + "displayName": "Iota", + "transliteration": "I", + "ipa": "/i/, /iː/", + "numerology": 10, + "meaning": "hand (from Phoenician yod)", + "hebrewLetterId": "yod", + "archaic": false + }, + { + "name": "kappa", + "index": 11, + "char": "Κ", + "charLower": "κ", + "displayName": "Kappa", + "transliteration": "K", + "ipa": "/k/", + "numerology": 20, + "meaning": "palm of hand (from Phoenician kaph)", + "hebrewLetterId": "kaf", + "archaic": false + }, + { + "name": "lambda", + "index": 12, + "char": "Λ", + "charLower": "λ", + "displayName": "Lambda", + "transliteration": "L", + "ipa": "/l/", + "numerology": 30, + "meaning": "ox-goad (from Phoenician lamed)", + "hebrewLetterId": "lamed", + "archaic": false + }, + { + "name": "mu", + "index": 13, + "char": "Μ", + "charLower": "μ", + "displayName": "Mu", + "transliteration": "M", + "ipa": "/m/", + "numerology": 40, + "meaning": "water (from Phoenician mem)", + "hebrewLetterId": "mem", + "archaic": false + }, + { + "name": "nu", + "index": 14, + "char": "Ν", + "charLower": "ν", + "displayName": "Nu", + "transliteration": "N", + "ipa": "/n/", + "numerology": 50, + "meaning": "fish (from Phoenician nun)", + "hebrewLetterId": "nun", + "archaic": false + }, + { + "name": "xi", + "index": 15, + "char": "Ξ", + "charLower": "ξ", + "displayName": "Xi", + "transliteration": "X", + "ipa": "/ks/", + "numerology": 60, + "meaning": "prop/support (from Phoenician samekh)", + "hebrewLetterId": "samekh", + "archaic": false + }, + { + "name": "omicron", + "index": 16, + "char": "Ο", + "charLower": "ο", + "displayName": "Omicron", + "transliteration": "O", + "ipa": "/o/", + "numerology": 70, + "meaning": "small o; eye (from Phoenician ayin)", + "hebrewLetterId": "ayin", + "archaic": false + }, + { + "name": "pi", + "index": 17, + "char": "Π", + "charLower": "π", + "displayName": "Pi", + "transliteration": "P", + "ipa": "/p/", + "numerology": 80, + "meaning": "mouth (from Phoenician pe)", + "hebrewLetterId": "pe", + "archaic": false + }, + { + "name": "qoppa", + "index": 18, + "char": "Ϟ", + "charLower": "ϟ", + "displayName": "Qoppa", + "transliteration": "Q", + "ipa": "/kʷ/", + "numerology": 90, + "meaning": "back of head (from Phoenician qoph)", + "hebrewLetterId": "qof", + "archaic": true + }, + { + "name": "rho", + "index": 19, + "char": "Ρ", + "charLower": "ρ", + "displayName": "Rho", + "transliteration": "R", + "ipa": "/r/", + "numerology": 100, + "meaning": "head (from Phoenician resh)", + "hebrewLetterId": "resh", + "archaic": false + }, + { + "name": "sigma", + "index": 20, + "char": "Σ", + "charLower": "σ", + "charFinal": "ς", + "displayName": "Sigma", + "transliteration": "S", + "ipa": "/s/", + "numerology": 200, + "meaning": "tooth (from Phoenician shin)", + "hebrewLetterId": "shin", + "archaic": false + }, + { + "name": "tau", + "index": 21, + "char": "Τ", + "charLower": "τ", + "displayName": "Tau", + "transliteration": "T", + "ipa": "/t/", + "numerology": 300, + "meaning": "cross, mark (from Phoenician tav)", + "hebrewLetterId": "tav", + "archaic": false + }, + { + "name": "upsilon", + "index": 22, + "char": "Υ", + "charLower": "υ", + "displayName": "Upsilon", + "transliteration": "U/Y", + "ipa": "/y/, /u/", + "numerology": 400, + "meaning": "simple upsilon; from Phoenician waw (also root of Vav, W, U, V, F)", + "hebrewLetterId": "vav", + "archaic": false + }, + { + "name": "phi", + "index": 23, + "char": "Φ", + "charLower": "φ", + "displayName": "Phi", + "transliteration": "Ph/F", + "ipa": "/pʰ/, /f/", + "numerology": 500, + "meaning": "light; late Phoenician pe secondary derivative", + "archaic": false + }, + { + "name": "chi", + "index": 24, + "char": "Χ", + "charLower": "χ", + "displayName": "Chi", + "transliteration": "Ch/Kh", + "ipa": "/kʰ/, /x/", + "numerology": 600, + "meaning": "cross-variant; possible relation to Phoenician heth or teth", + "archaic": false + }, + { + "name": "psi", + "index": 25, + "char": "Ψ", + "charLower": "ψ", + "displayName": "Psi", + "transliteration": "Ps", + "ipa": "/ps/", + "numerology": 700, + "meaning": "soul, trident; some scholars link to Phoenician tsade", + "hebrewLetterId": "tsadi", + "archaic": false + }, + { + "name": "omega", + "index": 26, + "char": "Ω", + "charLower": "ω", + "displayName": "Omega", + "transliteration": "O", + "ipa": "/oː/", + "numerology": 800, + "meaning": "great o; from ayin (eye) family, contrasted with omicron", + "hebrewLetterId": "ayin", + "archaic": false + }, + { + "name": "sampi", + "index": 27, + "char": "Ϡ", + "charLower": "ϡ", + "displayName": "Sampi", + "transliteration": "Ss/Ts", + "ipa": "/ss/ or /ts/", + "numerology": 900, + "meaning": "like pi (variant); Phoenician tsade origin", + "hebrewLetterId": "tsadi", + "archaic": true + } + ], + "english": [ + { "index": 1, "letter": "A", "ipa": "/eɪ/", "pythagorean": 1, "hebrewLetterId": "alef", "greekEquivalent": "alpha" }, + { "index": 2, "letter": "B", "ipa": "/biː/", "pythagorean": 2, "hebrewLetterId": "bet", "greekEquivalent": "beta" }, + { "index": 3, "letter": "C", "ipa": "/siː/", "pythagorean": 3, "hebrewLetterId": "gimel", "greekEquivalent": "gamma" }, + { "index": 4, "letter": "D", "ipa": "/diː/", "pythagorean": 4, "hebrewLetterId": "dalet", "greekEquivalent": "delta" }, + { "index": 5, "letter": "E", "ipa": "/iː/", "pythagorean": 5, "hebrewLetterId": "he", "greekEquivalent": "epsilon" }, + { "index": 6, "letter": "F", "ipa": "/ɛf/", "pythagorean": 6, "hebrewLetterId": "vav", "greekEquivalent": "digamma" }, + { "index": 7, "letter": "G", "ipa": "/dʒiː/","pythagorean": 7, "hebrewLetterId": "gimel", "greekEquivalent": "gamma" }, + { "index": 8, "letter": "H", "ipa": "/eɪtʃ/","pythagorean": 8, "hebrewLetterId": "he", "greekEquivalent": "eta" }, + { "index": 9, "letter": "I", "ipa": "/aɪ/", "pythagorean": 9, "hebrewLetterId": "yod", "greekEquivalent": "iota" }, + { "index": 10, "letter": "J", "ipa": "/dʒeɪ/","pythagorean": 1, "hebrewLetterId": "yod", "greekEquivalent": "iota" }, + { "index": 11, "letter": "K", "ipa": "/keɪ/", "pythagorean": 2, "hebrewLetterId": "kaf", "greekEquivalent": "kappa" }, + { "index": 12, "letter": "L", "ipa": "/ɛl/", "pythagorean": 3, "hebrewLetterId": "lamed", "greekEquivalent": "lambda" }, + { "index": 13, "letter": "M", "ipa": "/ɛm/", "pythagorean": 4, "hebrewLetterId": "mem", "greekEquivalent": "mu" }, + { "index": 14, "letter": "N", "ipa": "/ɛn/", "pythagorean": 5, "hebrewLetterId": "nun", "greekEquivalent": "nu" }, + { "index": 15, "letter": "O", "ipa": "/oʊ/", "pythagorean": 6, "hebrewLetterId": "ayin", "greekEquivalent": "omicron" }, + { "index": 16, "letter": "P", "ipa": "/piː/", "pythagorean": 7, "hebrewLetterId": "pe", "greekEquivalent": "pi" }, + { "index": 17, "letter": "Q", "ipa": "/kjuː/","pythagorean": 8, "hebrewLetterId": "qof", "greekEquivalent": "qoppa" }, + { "index": 18, "letter": "R", "ipa": "/ɑːr/", "pythagorean": 9, "hebrewLetterId": "resh", "greekEquivalent": "rho" }, + { "index": 19, "letter": "S", "ipa": "/ɛs/", "pythagorean": 1, "hebrewLetterId": "samekh", "greekEquivalent": "sigma" }, + { "index": 20, "letter": "T", "ipa": "/tiː/", "pythagorean": 2, "hebrewLetterId": "tav", "greekEquivalent": "tau" }, + { "index": 21, "letter": "U", "ipa": "/juː/", "pythagorean": 3, "hebrewLetterId": "vav", "greekEquivalent": "upsilon" }, + { "index": 22, "letter": "V", "ipa": "/viː/", "pythagorean": 4, "hebrewLetterId": "vav", "greekEquivalent": "upsilon" }, + { "index": 23, "letter": "W", "ipa": "/ˈdʌbljuː/", "pythagorean": 5, "hebrewLetterId": "vav", "greekEquivalent": "digamma" }, + { "index": 24, "letter": "X", "ipa": "/ɛks/", "pythagorean": 6, "hebrewLetterId": "samekh", "greekEquivalent": "xi" }, + { "index": 25, "letter": "Y", "ipa": "/waɪ/", "pythagorean": 7, "hebrewLetterId": "yod", "greekEquivalent": "upsilon" }, + { "index": 26, "letter": "Z", "ipa": "/zɛd/", "pythagorean": 8, "hebrewLetterId": "zayin", "greekEquivalent": "zeta" } + ], + "arabic": [ + { "index": 1, "name": "alif", "char": "ا", "nameArabic": "أَلِف", "transliteration": "ā / ʾ", "ipa": "/ʔ/, /aː/", "abjad": 1, "meaning": "ox; breath; onset", "category": "solar", "hebrewLetterId": "alef", "forms": { "isolated": "ا", "final": "ـا" } }, + { "index": 2, "name": "ba", "char": "ب", "nameArabic": "بَاء", "transliteration": "b", "ipa": "/b/", "abjad": 2, "meaning": "house", "category": "lunar", "hebrewLetterId": "bet", "forms": { "isolated": "ب", "initial": "بـ", "medial": "ـبـ", "final": "ـب" } }, + { "index": 3, "name": "jeem", "char": "ج", "nameArabic": "جِيم", "transliteration": "j", "ipa": "/d͡ʒ/", "abjad": 3, "meaning": "camel; beauty", "category": "lunar", "hebrewLetterId": "gimel", "forms": { "isolated": "ج", "initial": "جـ", "medial": "ـجـ", "final": "ـج" } }, + { "index": 4, "name": "dal", "char": "د", "nameArabic": "دَال", "transliteration": "d", "ipa": "/d/", "abjad": 4, "meaning": "door", "category": "solar", "hebrewLetterId": "dalet", "forms": { "isolated": "د", "final": "ـد" } }, + { "index": 5, "name": "ha", "char": "ه", "nameArabic": "هَاء", "transliteration": "h", "ipa": "/h/", "abjad": 5, "meaning": "window; breath; exclamation", "category": "lunar", "hebrewLetterId": "he", "forms": { "isolated": "ه", "initial": "هـ", "medial": "ـهـ", "final": "ـه" } }, + { "index": 6, "name": "waw", "char": "و", "nameArabic": "وَاو", "transliteration": "w / ū", "ipa": "/w/, /uː/", "abjad": 6, "meaning": "hook; peg; and", "category": "solar", "hebrewLetterId": "vav", "forms": { "isolated": "و", "final": "ـو" } }, + { "index": 7, "name": "zayn", "char": "ز", "nameArabic": "زَاي", "transliteration": "z", "ipa": "/z/", "abjad": 7, "meaning": "adornment; weapon", "category": "solar", "hebrewLetterId": "zayin", "forms": { "isolated": "ز", "final": "ـز" } }, + { "index": 8, "name": "ha_khaa", "char": "ح", "nameArabic": "حَاء", "transliteration": "ḥ", "ipa": "/ħ/", "abjad": 8, "meaning": "fence; enclosed courtyard; breath","category": "lunar", "hebrewLetterId": "het", "forms": { "isolated": "ح", "initial": "حـ", "medial": "ـحـ", "final": "ـح" } }, + { "index": 9, "name": "ta_tay", "char": "ط", "nameArabic": "طَاء", "transliteration": "ṭ", "ipa": "/tˤ/", "abjad": 9, "meaning": "serpent; goodness", "category": "solar", "hebrewLetterId": "tet", "forms": { "isolated": "ط", "initial": "طـ", "medial": "ـطـ", "final": "ـط" } }, + { "index": 10, "name": "ya", "char": "ي", "nameArabic": "يَاء", "transliteration": "y / ī", "ipa": "/j/, /iː/", "abjad": 10, "meaning": "hand", "category": "solar", "hebrewLetterId": "yod", "forms": { "isolated": "ي", "initial": "يـ", "medial": "ـيـ", "final": "ـي" } }, + { "index": 11, "name": "kaf", "char": "ك", "nameArabic": "كَاف", "transliteration": "k", "ipa": "/k/", "abjad": 20, "meaning": "palm of hand; like", "category": "solar", "hebrewLetterId": "kaf", "forms": { "isolated": "ك", "initial": "كـ", "medial": "ـكـ", "final": "ـك" } }, + { "index": 12, "name": "lam", "char": "ل", "nameArabic": "لَام", "transliteration": "l", "ipa": "/l/", "abjad": 30, "meaning": "ox-goad; for/to", "category": "solar", "hebrewLetterId": "lamed", "forms": { "isolated": "ل", "initial": "لـ", "medial": "ـلـ", "final": "ـل" } }, + { "index": 13, "name": "meem", "char": "م", "nameArabic": "مِيم", "transliteration": "m", "ipa": "/m/", "abjad": 40, "meaning": "water", "category": "lunar", "hebrewLetterId": "mem", "forms": { "isolated": "م", "initial": "مـ", "medial": "ـمـ", "final": "ـم" } }, + { "index": 14, "name": "nun", "char": "ن", "nameArabic": "نُون", "transliteration": "n", "ipa": "/n/", "abjad": 50, "meaning": "fish; inkwell", "category": "solar", "hebrewLetterId": "nun", "forms": { "isolated": "ن", "initial": "نـ", "medial": "ـنـ", "final": "ـن" } }, + { "index": 15, "name": "seen", "char": "س", "nameArabic": "سِين", "transliteration": "s", "ipa": "/s/", "abjad": 60, "meaning": "tooth; prop; support", "category": "solar", "hebrewLetterId": "samekh", "forms": { "isolated": "س", "initial": "سـ", "medial": "ـسـ", "final": "ـس" } }, + { "index": 16, "name": "ayn", "char": "ع", "nameArabic": "عَيْن", "transliteration": "ʿ", "ipa": "/ʕ/", "abjad": 70, "meaning": "eye; spring of water", "category": "lunar", "hebrewLetterId": "ayin", "forms": { "isolated": "ع", "initial": "عـ", "medial": "ـعـ", "final": "ـع" } }, + { "index": 17, "name": "fa", "char": "ف", "nameArabic": "فَاء", "transliteration": "f", "ipa": "/f/", "abjad": 80, "meaning": "mouth", "category": "lunar", "hebrewLetterId": "pe", "forms": { "isolated": "ف", "initial": "فـ", "medial": "ـفـ", "final": "ـف" } }, + { "index": 18, "name": "sad", "char": "ص", "nameArabic": "صَاد", "transliteration": "ṣ", "ipa": "/sˤ/", "abjad": 90, "meaning": "twisted; fishhook", "category": "solar", "hebrewLetterId": "tsadi", "forms": { "isolated": "ص", "initial": "صـ", "medial": "ـصـ", "final": "ـص" } }, + { "index": 19, "name": "qaf", "char": "ق", "nameArabic": "قَاف", "transliteration": "q", "ipa": "/q/", "abjad": 100, "meaning": "back of neck; ape", "category": "lunar", "hebrewLetterId": "qof", "forms": { "isolated": "ق", "initial": "قـ", "medial": "ـقـ", "final": "ـق" } }, + { "index": 20, "name": "ra", "char": "ر", "nameArabic": "رَاء", "transliteration": "r", "ipa": "/r/", "abjad": 200, "meaning": "head", "category": "solar", "hebrewLetterId": "resh", "forms": { "isolated": "ر", "final": "ـر" } }, + { "index": 21, "name": "sheen", "char": "ش", "nameArabic": "شِين", "transliteration": "sh", "ipa": "/ʃ/", "abjad": 300, "meaning": "tooth; composite; sun", "category": "solar", "hebrewLetterId": "shin", "forms": { "isolated": "ش", "initial": "شـ", "medial": "ـشـ", "final": "ـش" } }, + { "index": 22, "name": "ta", "char": "ت", "nameArabic": "تَاء", "transliteration": "t", "ipa": "/t/", "abjad": 400, "meaning": "mark; sign; covenant", "category": "solar", "hebrewLetterId": "tav", "forms": { "isolated": "ت", "initial": "تـ", "medial": "ـتـ", "final": "ـت" } }, + { "index": 23, "name": "tha", "char": "ث", "nameArabic": "ثَاء", "transliteration": "th", "ipa": "/θ/", "abjad": 500, "meaning": "thorny; fixed", "category": "solar", "hebrewLetterId": null, "forms": { "isolated": "ث", "initial": "ثـ", "medial": "ـثـ", "final": "ـث" } }, + { "index": 24, "name": "kha", "char": "خ", "nameArabic": "خَاء", "transliteration": "kh", "ipa": "/x/", "abjad": 600, "meaning": "penetrating; veiled", "category": "lunar", "hebrewLetterId": null, "forms": { "isolated": "خ", "initial": "خـ", "medial": "ـخـ", "final": "ـخ" } }, + { "index": 25, "name": "dhal", "char": "ذ", "nameArabic": "ذَال", "transliteration": "dh", "ipa": "/ð/", "abjad": 700, "meaning": "this; possessor of", "category": "solar", "hebrewLetterId": null, "forms": { "isolated": "ذ", "final": "ـذ" } }, + { "index": 26, "name": "dad", "char": "ض", "nameArabic": "ضَاد", "transliteration": "ḍ", "ipa": "/dˤ/", "abjad": 800, "meaning": "ribs; darkness; unique letter", "category": "solar", "hebrewLetterId": null, "forms": { "isolated": "ض", "initial": "ضـ", "medial": "ـضـ", "final": "ـض" } }, + { "index": 27, "name": "dha", "char": "ظ", "nameArabic": "ظَاء", "transliteration": "ẓ", "ipa": "/ðˤ/", "abjad": 900, "meaning": "shadow; manifest; back", "category": "solar", "hebrewLetterId": null, "forms": { "isolated": "ظ", "initial": "ظـ", "medial": "ـظـ", "final": "ـظ" } }, + { "index": 28, "name": "ghayn", "char": "غ", "nameArabic": "غَيْن", "transliteration": "gh", "ipa": "/ɣ/", "abjad": 1000, "meaning": "absence; obscured eye; rain", "category": "lunar", "hebrewLetterId": null, "forms": { "isolated": "غ", "initial": "غـ", "medial": "ـغـ", "final": "ـغ" } } + ], + "enochian": [ + { "index": 1, "id": "A", "char": "A", "title": "Un", "englishLetters": ["A"], "transliteration": "ah", "elementOrPlanet": "taurus", "tarot": "hierophant", "numerology": 6, "hebrewLetterId": "alef" }, + { "index": 2, "id": "B", "char": "B", "title": "Pe", "englishLetters": ["B"], "transliteration": "beh", "elementOrPlanet": "aries", "tarot": "star", "numerology": 5, "hebrewLetterId": "bet" }, + { "index": 3, "id": "C", "char": "C", "title": "Veh", "englishLetters": ["C", "K"], "transliteration": "co", "elementOrPlanet": "fire", "tarot": "judgement", "numerology": 300, "hebrewLetterId": null }, + { "index": 4, "id": "D", "char": "D", "title": "Gal", "englishLetters": ["D"], "transliteration": "deh", "elementOrPlanet": "spirit", "tarot": "empress", "numerology": 4, "hebrewLetterId": "dalet" }, + { "index": 5, "id": "E", "char": "E", "title": "Graph", "englishLetters": ["E"], "transliteration": "ee", "elementOrPlanet": "virgo", "tarot": "hermit", "numerology": 10, "hebrewLetterId": "he" }, + { "index": 6, "id": "F", "char": "F", "title": "Orth", "englishLetters": ["F"], "transliteration": "eff", "elementOrPlanet": "Cauda Draonis", "tarot": "juggler", "numerology": 3, "hebrewLetterId": "vav" }, + { "index": 7, "id": "G", "char": "G", "title": "Ged", "englishLetters": ["G"], "transliteration": "gee", "elementOrPlanet": "cancer", "tarot": "chariot", "numerology": 8, "hebrewLetterId": "gimel" }, + { "index": 8, "id": "H", "char": "H", "title": "Na-hath", "englishLetters": ["H"], "transliteration": "heh", "elementOrPlanet": "air", "tarot": "fool", "numerology": 1, "hebrewLetterId": "he" }, + { "index": 9, "id": "I", "char": "I", "title": "Gon", "englishLetters": ["I", "Y", "J"], "transliteration": "ee", "elementOrPlanet": "sagittarius", "tarot": "temperance", "numerology": 60, "hebrewLetterId": "yod" }, + { "index": 10, "id": "L", "char": "L", "title": "Ur", "englishLetters": ["L"], "transliteration": "el, leh", "elementOrPlanet": "cancer", "tarot": "charit", "numerology": 8, "hebrewLetterId": "lamed" }, + { "index": 11, "id": "M", "char": "M", "title": "Tal", "englishLetters": ["M"], "transliteration": "em", "elementOrPlanet": "aquarius", "tarot": "emperor", "numerology": 90, "hebrewLetterId": "mem" }, + { "index": 12, "id": "N", "char": "N", "title": "Drun", "englishLetters": ["N"], "transliteration": "en", "elementOrPlanet": "scorpio", "tarot": "death", "numerology": 50, "hebrewLetterId": "nun" }, + { "index": 13, "id": "O", "char": "O", "title": "Med", "englishLetters": ["O"], "transliteration": "oh", "elementOrPlanet": "libra", "tarot": "justice", "numerology": 30, "hebrewLetterId": "ayin" }, + { "index": 14, "id": "P", "char": "P", "title": "Mals", "englishLetters": ["P"], "transliteration": "peh", "elementOrPlanet": "leo", "tarot": "strength", "numerology": 9, "hebrewLetterId": "pe" }, + { "index": 15, "id": "Q", "char": "Q", "title": "Ger", "englishLetters": ["Q"], "transliteration": "quo", "elementOrPlanet": "water", "tarot": "hanged man", "numerology": 40, "hebrewLetterId": "qof" }, + { "index": 16, "id": "R", "char": "R", "title": "Don", "englishLetters": ["R"], "transliteration": "reh, ar", "elementOrPlanet": "pesces", "tarot": "moon", "numerology": 100, "hebrewLetterId": "resh" }, + { "index": 17, "id": "S", "char": "S", "title": "Fam", "englishLetters": ["S"], "transliteration": "she, ess", "elementOrPlanet": "gemini", "tarot": "lovers", "numerology": 7, "hebrewLetterId": "samekh" }, + { "index": 18, "id": "T", "char": "T", "title": "Gisa", "englishLetters": ["T"], "transliteration": "teh", "elementOrPlanet": "leo", "tarot": "strength", "numerology": 9, "hebrewLetterId": "tav" }, + { "index": 19, "id": "V", "char": "V", "title": "Vau", "englishLetters": ["U", "V", "W"], "transliteration": "vaa", "elementOrPlanet": "capricorn", "tarot": "devil", "numerology": 70, "hebrewLetterId": "vav" }, + { "index": 20, "id": "X", "char": "X", "title": "Pal", "englishLetters": ["X"], "transliteration": "s, tz", "elementOrPlanet": "earth", "tarot": "universe", "numerology": 400, "hebrewLetterId": "samekh" }, + { "index": 21, "id": "Z", "char": "Z", "title": "Ceph", "englishLetters": ["Z"], "transliteration": "zod, zee", "elementOrPlanet": "leo", "tarot": "strength", "numerology": 9, "hebrewLetterId": "zayin" } + ] +} diff --git a/data/astrology/houses.json b/data/astrology/houses.json new file mode 100644 index 0000000..23155e9 --- /dev/null +++ b/data/astrology/houses.json @@ -0,0 +1,170 @@ +[ + { + "index": 1, + "zodiacId": "aries", + "motto": { + "la": "Vita", + "en": "Life" + }, + "name": { + "en": "House of Self" + }, + "interpretation": { + "en": "Physical appearance, identity and characteristics. Resourcefulness. Outlook and impressions. Ego/personality. Goals. Determination. Beginnings and initiatives." + } + }, + { + "index": 2, + "zodiacId": "taurus", + "motto": { + "la": "Lucrum", + "en": "Gain" + }, + "name": { + "en": "House of Value" + }, + "interpretation": { + "en": "Material and immaterial things of certain value. Money. Possessions and acquisitions. Cultivation. Perseverance. Substance. Self-worth." + } + }, + { + "index": 3, + "zodiacId": "gemini", + "motto": { + "la": "Fratres", + "en": "Order" + }, + "name": { + "en": "House of Sharing" + }, + "interpretation": { + "en": "Communication. Distribution/generosity. Intelligence/development. Siblings, cousins. Locomotion and transportation. Ephemera." + } + }, + { + "index": 4, + "zodiacId": "cancer", + "motto": { + "la": "Genitor", + "en": "Parent" + }, + "name": { + "en": "House of Home and Family" + }, + "interpretation": { + "en": "Ancestry, heritage, roots. Foundation and environment. Mother or caretaker. Housing and the household. Neighborhood matters. Comfort, security/safety. Tidiness. Pets." + } + }, + { + "index": 5, + "zodiacId": "leo", + "motto": { + "la": "Nati", + "en": "Children" + }, + "name": { + "en": "House of Pleasure" + }, + "interpretation": { + "en": "Recreational and leisure activities. Things which make for enjoyment and entertainment. Games/gambling/risk. Romance and limerence. Children/fertility. Self-expression. Courage." + } + }, + { + "index": 6, + "zodiacId": "virgo", + "motto": { + "la": "Valetudo", + "en": "Health" + }, + "name": { + "en": "House of Health" + }, + "interpretation": { + "en": "Routine tasks and duties. Skills or training acquired. Employment (job). Service and being served. Strength, vitality. Wellness and healthcare." + } + }, + { + "index": 7, + "zodiacId": "libra", + "motto": { + "la": "Uxor", + "en": "Spouse" + }, + "name": { + "en": "House of Balance" + }, + "interpretation": { + "en": "Partnerships. Marriage and business matters. Diplomacy. Agreements, contracts and all things official. Equilibrium." + } + }, + { + "index": 8, + "zodiacId": "scorpio", + "motto": { + "la": "Mors", + "en": "Death" + }, + "name": { + "en": "House of Transformation" + }, + "interpretation": { + "en": "Cycles of deaths and rebirth. Sexual relationships and commitments of all kinds. Joint funds, finances. Other person's resource. Karma and debt (judgment). Regeneration. Self-transformation." + } + }, + { + "index": 9, + "zodiacId": "sagittarius", + "motto": { + "la": "Iter", + "en": "Passage" + }, + "name": { + "en": "House of Purpose" + }, + "interpretation": { + "en": "Travel and foreign affairs. Culture. Expansion. Law and ethics. Education/learning/knowledge. Philosophical interests, belief systems. Experience through exploration. Things long-term." + } + }, + { + "index": 10, + "zodiacId": "capricorn", + "motto": { + "la": "Regnum", + "en": "Kingdom" + }, + "name": { + "en": "House of Enterprise" + }, + "interpretation": { + "en": "Ambitions. Motivations. Career, achievements. Society and government. Father or authority. Notoriety. Advantage." + } + }, + { + "index": 11, + "zodiacId": "aquarius", + "motto": { + "la": "Benefacta", + "en": "Support" + }, + "name": { + "en": "House of Blessings" + }, + "interpretation": { + "en": "Benefits from effort. Friends and acquaintances of like-minded attitudes. Belonging. Groups, communities, and associations. Charity. Connectedness/networking. Love. Wish fulfillment. Wealth." + } + }, + { + "index": 12, + "zodiacId": "pisces", + "motto": { + "la": "Carcer", + "en": "Rehabilitation" + }, + "name": { + "en": "House of Sacrifice" + }, + "interpretation": { + "en": "Privacy, refuge; seclusion and retreating. Creativity. Clandestiny↔Revelation. Intuition. Extremes/abundance, but also addictions. Luck, miracles. Releasing/relinquishing, healing/cleansing/rejuvenation, forgiveness, and peacefulness. Finality/completion/conclusion." + } + } +] diff --git a/data/astrology/planets.json b/data/astrology/planets.json new file mode 100644 index 0000000..1e3e779 --- /dev/null +++ b/data/astrology/planets.json @@ -0,0 +1,233 @@ +{ + "primum-mobile": { + "id": "primum-mobile", + "name": { + "en": { + "en": "Primum Mobile" + }, + "he": { + "he": "ראשית הגלגולים", + "roman": "Roshit HaGilgulim" + } + } + }, + "zodiac": { + "id": "zodiac", + "name": { + "en": { + "en": "Zodiac" + }, + "he": { + "he": "מזלות", + "roman": "Mazalot" + } + } + }, + "olam-yesodot": { + "id": "olam-yesodot", + "name": { + "en": { + "en": "World of Foundations" + }, + "he": { + "he": "עולם היסודות", + "roman": "Olam haYesodot" + } + } + }, + "sol": { + "id": "sol", + "symbol": "☉︎", + "hebrewLetterId": "resh", + "name": { + "en": { + "en": "Sol" + }, + "he": { + "he": "שמש", + "roman": "Shemesh", + "en": "Sun" + } + }, + "godNameId": "yhvh-eloha-vedaat", + "archangelId": "michael", + "intelligenceId": "nakhiel", + "spiritId": "sorath", + "magickTypes": { + "en": "Career success and progression, establishing harmony, healing and improving health, leadership skills, acquiring money and resources, promotion, strengthening willpower" + } + }, + "mercury": { + "id": "mercury", + "symbol": "☿︎", + "hebrewLetterId": "bet", + "name": { + "en": { + "en": "Mercury" + }, + "he": { + "he": "כוכב", + "roman": "Kochav" + } + }, + "godNameId": "elohim-tzvaot", + "archangelId": "raphael", + "intelligenceId": "tiriel", + "spiritId": "taphthartharath", + "magickTypes": { + "en": "Business success, improving communication skills, developing knowledge and memory, diplomacy, exam success, divination, developing influence, protection when traveling by air and land, learning music" + } + }, + "venus": { + "id": "venus", + "symbol": "♀︎", + "hebrewLetterId": "dalet", + "name": { + "en": { + "en": "Venus" + }, + "he": { + "he": "נגה", + "roman": "Noga" + } + }, + "godNameId": "yhvh-tzvaot", + "archangelId": "anael", + "intelligenceId": "hagiel", + "spiritId": "kedemel", + "magickTypes": { + "en": "Increasing attractiveness and self-confidence, beauty and passion, enhancing creativity, improving fertility, developing friendships, obtaining love" + } + }, + "earth": { + "id": "earth", + "symbol": "♁︎", + "name": { + "en": { + "en": "Earth" + } + } + }, + "luna": { + "id": "luna", + "symbol": "☾︎", + "hebrewLetterId": "gimel", + "name": { + "en": { + "en": "Luna" + }, + "he": { + "he": "לבנה", + "roman": "Levanah", + "en": "White" + } + }, + "godNameId": "shaddai-el-chai", + "archangelId": "gabriel", + "intelligenceId": "shelachel", + "spiritId": "chasmodai", + "magickTypes": { + "en": "Developing clairvoyance and other psychic skills, ensuring safe childbirth, divination, glamour and illusions, lucid dreaming, protection when traveling by sea" + } + }, + "mars": { + "id": "mars", + "symbol": "♂︎", + "hebrewLetterId": "pe", + "name": { + "en": { + "en": "Mars" + }, + "he": { + "he": "מאדים", + "roman": "Ma'adim" + } + }, + "godNameId": "elohim-gibor", + "archangelId": "zamael", + "intelligenceId": "graphiel", + "spiritId": "bartzabel", + "magickTypes": { + "en": "Controlling anger, increasing courage, enhancing energy and passion, increasing vigour and sex drive" + } + }, + "jupiter": { + "id": "jupiter", + "symbol": "♃︎", + "hebrewLetterId": "kaf", + "name": { + "en": { + "en": "Jupiter" + }, + "he": { + "he": "צדק", + "roman": "Tzedek", + "en": "Justice" + } + }, + "godNameId": "el", + "archangelId": "sachiel", + "intelligenceId": "iophiel", + "spiritId": "hismael", + "magickTypes": { + "en": "Career success, developing ambition and enthusiasm, improving fortune and luck, general health, acquiring honour, improving sense of humour, legal matters and dealing with the establishment, developing leadership skills" + } + }, + "saturn": { + "id": "saturn", + "symbol": "♄︎", + "name": { + "en": { + "en": "Saturn" + }, + "he": { + "he": "שַׁבְּתַאי", + "roman": "Shabbathai" + } + }, + "hebrewLetterId": "tav", + "godNameId": "yhvh-elohim", + "archangelId": "cassiel", + "intelligenceId": "agiel", + "spiritId": "zazel", + "magickTypes": { + "en": "Performing duty, establishing balance and equilibrium, dispelling illusions, protecting the home, legal matters, developing patience and self-discipline" + } + }, + "uranus": { + "id": "uranus", + "symbol": "⛢︎", + "name": { + "en": { + "en": "Uranus" + } + } + }, + "neptune": { + "id": "neptune", + "symbol": "♆︎", + "name": { + "en": { + "en": "Neptune" + } + } + }, + "rahu": { + "id": "rahu", + "symbol": "☊︎", + "name": { + "en": { + "en": "Caput Draconis" + } + } + }, + "ketu": { + "id": "ketu", + "symbol": "☋︎", + "name": { + "en": { + "en": "Cauda Draconic" + } + } + } +} diff --git a/data/astrology/retrograde.json b/data/astrology/retrograde.json new file mode 100644 index 0000000..848b7e6 --- /dev/null +++ b/data/astrology/retrograde.json @@ -0,0 +1,244 @@ +{ + "mercury": [ + [ + [ + 2020, + 10, + 13 + ], + [ + 2020, + 11, + 3 + ] + ], + [ + [ + 2021, + 1, + 30 + ], + [ + 2021, + 2, + 20 + ] + ], + [ + [ + 2021, + 5, + 29 + ], + [ + 2021, + 6, + 22 + ] + ], + [ + [ + 2021, + 9, + 27 + ], + [ + 2021, + 10, + 18 + ] + ], + [ + [ + 2022, + 1, + 14 + ], + [ + 2022, + 2, + 3 + ] + ], + [ + [ + 2022, + 5, + 10 + ], + [ + 2022, + 6, + 2 + ] + ], + [ + [ + 2022, + 9, + 9 + ], + [ + 2022, + 10, + 2 + ] + ], + [ + [ + 2022, + 12, + 29 + ], + [ + 2023, + 1, + 18 + ] + ], + [ + [ + 2023, + 4, + 21 + ], + [ + 2023, + 5, + 14 + ] + ], + [ + [ + 2023, + 8, + 23 + ], + [ + 2023, + 9, + 15 + ] + ], + [ + [ + 2023, + 12, + 13 + ], + [ + 2024, + 1, + 1 + ] + ], + [ + [ + 2024, + 4, + 1 + ], + [ + 2024, + 4, + 25 + ] + ], + [ + [ + 2024, + 8, + 4 + ], + [ + 2024, + 8, + 28 + ] + ], + [ + [ + 2024, + 11, + 25 + ], + [ + 2024, + 12, + 15 + ] + ], + [ + [ + 2025, + 3, + 14 + ], + [ + 2025, + 4, + 7 + ] + ], + [ + [ + 2025, + 7, + 17 + ], + [ + 2025, + 8, + 11 + ] + ], + [ + [ + 2025, + 11, + 9 + ], + [ + 2025, + 11, + 29 + ] + ], + [ + [ + 2026, + 2, + 25 + ], + [ + 2026, + 3, + 20 + ] + ], + [ + [ + 2026, + 6, + 29 + ], + [ + 2026, + 7, + 23 + ] + ], + [ + [ + 2026, + 10, + 24 + ], + [ + 2026, + 11, + 13 + ] + ] + ] +} diff --git a/data/astrology/zodiac.json b/data/astrology/zodiac.json new file mode 100644 index 0000000..a0deaef --- /dev/null +++ b/data/astrology/zodiac.json @@ -0,0 +1,326 @@ +{ + "aries": { + "id": "aries", + "no": 1, + "symbol": "♈︎", + "emoji": "♈️", + "name": { + "en": "Aries" + }, + "meaning": { + "en": "Ram" + }, + "rulesFrom": [ + [ + 3, + 21 + ], + [ + 4, + 19 + ] + ], + "planetId": "mars", + "elementId": "fire", + "quadruplicity": "cardinal", + "tribeOfIsraelId": "gad", + "tetragrammatonPermutation": "yhvH" + }, + "taurus": { + "id": "taurus", + "no": 2, + "symbol": "♉︎", + "emoji": "♉", + "name": { + "en": "Taurus" + }, + "meaning": { + "en": "Bullm" + }, + "rulesFrom": [ + [ + 4, + 20 + ], + [ + 5, + 20 + ] + ], + "planetId": "venus", + "elementId": "earth", + "quadruplicity": "kerubic", + "tribeOfIsraelId": "ephraim", + "tetragrammatonPermutation": "yhHv" + }, + "gemini": { + "id": "gemini", + "no": 3, + "symbol": "♊︎", + "emoji": "♊", + "name": { + "en": "Gemini" + }, + "meaning": { + "en": "Twins" + }, + "rulesFrom": [ + [ + 5, + 21 + ], + [ + 6, + 20 + ] + ], + "planetId": "mercury", + "elementId": "air", + "quadruplicity": "mutable", + "tribeOfIsraelId": "manasseh", + "tetragrammatonPermutation": "yvhH" + }, + "cancer": { + "id": "cancer", + "no": 4, + "symbol": "♋︎", + "emoji": "♋", + "name": { + "en": "Cancer" + }, + "meaning": { + "en": "Crab" + }, + "rulesFrom": [ + [ + 6, + 21 + ], + [ + 7, + 22 + ] + ], + "planetId": "sol", + "elementId": "water", + "quadruplicity": "cardinal", + "tribeOfIsraelId": "issachar", + "tetragrammatonPermutation": "hvyH" + }, + "leo": { + "id": "leo", + "no": 5, + "symbol": "♌︎", + "emoji": "♌", + "name": { + "en": "Leo" + }, + "meaning": { + "en": "Lion" + }, + "rulesFrom": [ + [ + 7, + 23 + ], + [ + 8, + 22 + ] + ], + "planetId": "sol", + "elementId": "fire", + "quadruplicity": "kerubic", + "tribeOfIsraelId": "judah", + "tetragrammatonPermutation": "hvyH" + }, + "virgo": { + "id": "virgo", + "no": 6, + "symbol": "♍︎", + "emoji": "♍", + "name": { + "en": "Virgo" + }, + "meaning": { + "en": "Virgin" + }, + "rulesFrom": [ + [ + 8, + 23 + ], + [ + 9, + 22 + ] + ], + "planetId": "mercury", + "elementId": "earth", + "quadruplicity": "mutable", + "tribeOfIsraelId": "naphtali", + "tetragrammatonPermutation": "hHvy" + }, + "libra": { + "id": "libra", + "no": 7, + "symbol": "♎︎", + "emoji": "♎", + "name": { + "en": "Libra" + }, + "meaning": { + "en": "Scales" + }, + "rulesFrom": [ + [ + 9, + 23 + ], + [ + 10, + 22 + ] + ], + "planetId": "venus", + "elementId": "air", + "quadruplicity": "cardinal", + "tribeOfIsraelId": "asher", + "tetragrammatonPermutation": "vHyh" + }, + "scorpio": { + "id": "scorpio", + "no": 8, + "symbol": "♏︎", + "emoji": "♏", + "name": { + "en": "Scorpio" + }, + "meaning": { + "en": "Scorpion" + }, + "rulesFrom": [ + [ + 10, + 23 + ], + [ + 11, + 21 + ] + ], + "planetId": "jupiter", + "elementId": "water", + "quadruplicity": "kerubic", + "tribeOfIsraelId": "dan", + "tetragrammatonPermutation": "vHhy" + }, + "sagittarius": { + "id": "sagittarius", + "no": 9, + "symbol": "♐︎", + "emoji": "♐", + "name": { + "en": "Sagittarius" + }, + "meaning": { + "en": "Archer" + }, + "rulesFrom": [ + [ + 11, + 22 + ], + [ + 12, + 21 + ] + ], + "planetId": "jupiter", + "elementId": "fire", + "quadruplicity": "mutable", + "tribeOfIsraelId": "benjamin", + "tetragrammatonPermutation": "Hyhv" + }, + "capricorn": { + "id": "capricorn", + "no": 10, + "symbol": "♑︎", + "emoji": "♑", + "name": { + "en": "Capricorn" + }, + "meaning": { + "en": "Sea-goat" + }, + "rulesFrom": [ + [ + 12, + 22 + ], + [ + 1, + 19 + ] + ], + "planetId": "saturn", + "elementId": "earth", + "quadruplicity": "cardinal", + "tribeOfIsraelId": "zebulun", + "tetragrammatonPermutation": "Hyhv" + }, + "aquarius": { + "id": "aquarius", + "no": 11, + "symbol": "♒︎", + "emoji": "♒", + "name": { + "en": "Aquarius" + }, + "meaning": { + "en": "Water-bearer" + }, + "rulesFrom": [ + [ + 1, + 20 + ], + [ + 2, + 18 + ] + ], + "planetId": "saturn", + "elementId": "air", + "quadruplicity": "kerubic", + "tribeOfIsraelId": "reuben", + "tetragrammatonPermutation": "Hyvh" + }, + "pisces": { + "id": "pisces", + "no": 12, + "symbol": "♓︎", + "emoji": "♓", + "name": { + "en": "Pisces" + }, + "meaning": { + "en": "Fish" + }, + "rulesFrom": [ + [ + 2, + 19 + ], + [ + 3, + 20 + ] + ], + "planetId": "jupiter", + "elementId": "water", + "quadruplicity": "mutable", + "tribeOfIsraelId": "simeon", + "tetragrammatonPermutation": "Hhyv" + } +} diff --git a/data/astronomy-cycles.json b/data/astronomy-cycles.json new file mode 100644 index 0000000..c3d4663 --- /dev/null +++ b/data/astronomy-cycles.json @@ -0,0 +1,228 @@ +{ + "meta": { + "version": 1, + "notes": "Core astronomical and calendrical cycles used in astronomy, chronology, and long-term sky modeling." + }, + "cycles": [ + { + "id": "metonic-cycle", + "name": "Metonic Cycle", + "category": "Luni-solar Calendar", + "period": "19 years", + "periodDays": 6939.69, + "description": "19 tropical years are approximately equal to 235 synodic lunar months.", + "significance": "Foundation for lunisolar calendar reconciliation.", + "related": ["Callippic Cycle", "Synodic Month"] + }, + { + "id": "callippic-cycle", + "name": "Callippic Cycle", + "category": "Luni-solar Calendar", + "period": "76 years", + "periodDays": 27758.76, + "description": "Refinement of four Metonic cycles with leap-day correction.", + "significance": "Improves long-term lunisolar alignment.", + "related": ["Metonic Cycle"] + }, + { + "id": "hipparchic-cycle", + "name": "Hipparchic Cycle", + "category": "Luni-solar Calendar", + "period": "304 years", + "periodDays": 111035.04, + "description": "Four Callippic cycles used as a higher-order correction block.", + "significance": "Historical long-cycle refinement in ancient astronomy.", + "related": ["Callippic Cycle"] + }, + { + "id": "synodic-month", + "name": "Synodic Month", + "category": "Lunar", + "period": "29.53059 days", + "periodDays": 29.53059, + "description": "Average interval between identical lunar phases, such as full moon to full moon.", + "significance": "Primary month unit in lunar and lunisolar systems.", + "related": ["Metonic Cycle"] + }, + { + "id": "draconic-month", + "name": "Draconic Month", + "category": "Lunar", + "period": "27.21222 days", + "periodDays": 27.21222, + "description": "Moon's node-to-node period relative to the ecliptic.", + "significance": "Important for eclipse timing models.", + "related": ["Saros Cycle", "Eclipse Season"] + }, + { + "id": "anomalistic-month", + "name": "Anomalistic Month", + "category": "Lunar", + "period": "27.55455 days", + "periodDays": 27.55455, + "description": "Perigee-to-perigee period of the Moon.", + "significance": "Affects supermoon timing and lunar distance patterns.", + "related": ["Saros Cycle"] + }, + { + "id": "eclipse-season", + "name": "Eclipse Season", + "category": "Eclipse", + "period": "173.31 days", + "periodDays": 173.31, + "description": "Interval between windows when the Sun is near a lunar node.", + "significance": "Predicts clusters of solar/lunar eclipses.", + "related": ["Draconic Month", "Saros Cycle"] + }, + { + "id": "saros-cycle", + "name": "Saros Cycle", + "category": "Eclipse", + "period": "18 years 11 days 8 hours", + "periodDays": 6585.321, + "description": "Near-repeat interval for similar eclipses from geometry recurrence.", + "significance": "Classic eclipse prediction cycle.", + "related": ["Draconic Month", "Anomalistic Month", "Eclipse Season"] + }, + { + "id": "inex-cycle", + "name": "Inex Cycle", + "category": "Eclipse", + "period": "29 years minus about 20 days", + "periodDays": 10571.95, + "description": "Longer eclipse recurrence interval connecting different saros families.", + "significance": "Useful for classifying eclipse series transitions.", + "related": ["Saros Cycle"] + }, + { + "id": "solar-sunspot-cycle", + "name": "Solar Sunspot Cycle", + "category": "Solar", + "period": "~11 years", + "periodDays": 4017.75, + "description": "Average magnetic activity cycle of the Sun seen in sunspot counts.", + "significance": "Drives space weather and auroral activity trends.", + "related": ["Hale Magnetic Cycle"] + }, + { + "id": "hale-magnetic-cycle", + "name": "Hale Magnetic Cycle", + "category": "Solar", + "period": "~22 years", + "periodDays": 8035.5, + "description": "Two sunspot cycles required for solar magnetic polarity to return.", + "significance": "Long-form solar dynamo period.", + "related": ["Solar Sunspot Cycle"] + }, + { + "id": "jupiter-saturn-great-conjunction", + "name": "Jupiter-Saturn Great Conjunction", + "category": "Planetary", + "period": "~19.86 years", + "periodDays": 7255.0, + "description": "Mean interval between conjunctions of Jupiter and Saturn.", + "significance": "Major marker in historical chronology and sky cycles.", + "related": ["Great Trigon"] + }, + { + "id": "great-trigon", + "name": "Great Trigon", + "category": "Planetary", + "period": "~240 years", + "periodDays": 87658.0, + "description": "Successive great conjunctions cycle through similar elemental triplicity patterns over centuries.", + "significance": "Long-cycle structure derived from Jupiter-Saturn conjunction drift.", + "related": ["Jupiter-Saturn Great Conjunction"] + }, + { + "id": "venus-pentagram-cycle", + "name": "Venus Pentagram Cycle", + "category": "Planetary", + "period": "8 years", + "periodDays": 2921.94, + "description": "Five synodic Venus cycles align closely with eight Earth years, tracing a pentagram-like pattern.", + "significance": "Famous resonance in naked-eye planetary astronomy.", + "related": ["Synodic Venus Cycle"] + }, + { + "id": "synodic-venus-cycle", + "name": "Synodic Venus Cycle", + "category": "Planetary", + "period": "583.92 days", + "periodDays": 583.92, + "description": "Mean interval between consecutive inferior conjunctions of Venus.", + "significance": "Basis of Venus visibility phases and cycle work.", + "related": ["Venus Pentagram Cycle"] + }, + { + "id": "halley-comet-cycle", + "name": "Halley Comet Cycle", + "category": "Comet", + "period": "~75-76 years", + "periodDays": 27759.0, + "description": "Orbital return interval of 1P/Halley, varying by perturbations.", + "significance": "Most recognized periodic comet cycle.", + "related": ["Comet Period Classes"] + }, + { + "id": "encke-comet-cycle", + "name": "Encke Comet Cycle", + "category": "Comet", + "period": "~3.30 years", + "periodDays": 1205.0, + "description": "Orbital return interval of Comet 2P/Encke.", + "significance": "Classic short-period comet benchmark.", + "related": ["Comet Period Classes"] + }, + { + "id": "precession-of-equinoxes", + "name": "Precession of the Equinoxes", + "category": "Axial", + "period": "~25,772 years", + "periodDays": 9413478.0, + "description": "Slow conical motion of Earth's rotation axis relative to fixed stars.", + "significance": "Shifts equinox positions through zodiacal constellations over millennia.", + "related": ["Great Year (Platonic)"] + }, + { + "id": "great-year-platonic", + "name": "Great Year (Platonic)", + "category": "Axial", + "period": "~25,772 years", + "periodDays": 9413478.0, + "description": "Traditional term for one full precessional cycle of the equinoxes.", + "significance": "Long-duration epochal framing in astronomy and chronology traditions.", + "related": ["Precession of the Equinoxes"] + }, + { + "id": "milankovitch-obliquity", + "name": "Milankovitch Obliquity Cycle", + "category": "Climate-Astronomy", + "period": "~41,000 years", + "periodDays": 14975775.0, + "description": "Variation in Earth's axial tilt angle over tens of millennia.", + "significance": "Major pacing component in glacial-interglacial climate structure.", + "related": ["Milankovitch Precession", "Milankovitch Eccentricity"] + }, + { + "id": "milankovitch-precession", + "name": "Milankovitch Precession Cycle", + "category": "Climate-Astronomy", + "period": "~19,000 to 23,000 years", + "periodDays": 7665000.0, + "description": "Seasonal timing shift from axial precession interacting with orbital geometry.", + "significance": "Controls seasonal insolation pacing over geologic time.", + "related": ["Precession of the Equinoxes", "Milankovitch Obliquity"] + }, + { + "id": "milankovitch-eccentricity", + "name": "Milankovitch Eccentricity Cycle", + "category": "Climate-Astronomy", + "period": "~100,000 years (plus ~405,000-year mode)", + "periodDays": 36524250.0, + "description": "Long-term change in orbital ellipse shape from near-circular to more elliptical.", + "significance": "Dominant long pacing in Quaternary paleoclimate archives.", + "related": ["Milankovitch Obliquity", "Milankovitch Precession"] + } + ] +} diff --git a/data/calendar-holidays.json b/data/calendar-holidays.json new file mode 100644 index 0000000..ba3d8ad --- /dev/null +++ b/data/calendar-holidays.json @@ -0,0 +1,1673 @@ +{ + "meta": { + "version": 1, + "notes": "Unified holiday repository across Gregorian, Hebrew, Islamic, and Wheel calendars. Convert via selected Gregorian reference year." + }, + "holidays": [ + { + "id": "gregorian-lughnasadh-cross-quarter", + "calendarId": "gregorian", + "monthId": "august", + "name": "Lughnasadh Cross-Quarter", + "dateText": "08-01 to 08-02", + "day": 1, + "monthDayStart": "08-01", + "description": "Harvest threshold between solstice and equinox.", + "associations": { + "planetId": "mercury", + "zodiacSignId": "leo", + "tarotCard": "The Hermit", + "godId": "thoth" + }, + "source": "celestial-holidays.holidays[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + }, + { + "id": "gregorian-december-solstice", + "calendarId": "gregorian", + "monthId": "december", + "name": "December Solstice", + "dateText": "12-21", + "day": 21, + "monthDayStart": "12-21", + "description": "Shortest day in the northern hemisphere; longest in the southern hemisphere.", + "associations": { + "planetId": "sol", + "zodiacSignId": "capricorn", + "tarotCard": "The World", + "godId": "saturn" + }, + "source": "celestial-holidays.holidays[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "gregorian-geminids-peak", + "calendarId": "gregorian", + "monthId": "december", + "name": "Geminids Meteor Peak", + "dateText": "12-14", + "day": 14, + "monthDayStart": "12-14", + "description": "Peak activity of the Geminids meteor shower.", + "associations": { + "planetId": "mercury", + "zodiacSignId": "gemini", + "tarotCard": "The Lovers", + "godId": "hermes" + }, + "source": "celestial-holidays.holidays[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "gregorian-imbolc-cross-quarter", + "calendarId": "gregorian", + "monthId": "february", + "name": "Imbolc Cross-Quarter", + "dateText": "02-01 to 02-02", + "day": 1, + "monthDayStart": "02-01", + "description": "Seasonal threshold between winter solstice and spring equinox.", + "associations": { + "planetId": "luna", + "zodiacSignId": "aquarius", + "tarotCard": "The Star", + "godId": "hecate" + }, + "source": "celestial-holidays.holidays[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + }, + { + "id": "gregorian-perihelion", + "calendarId": "gregorian", + "monthId": "january", + "name": "Earth Perihelion", + "dateText": "01-03", + "day": 3, + "monthDayStart": "01-03", + "description": "Earth reaches its closest orbital point to the Sun.", + "associations": { + "planetId": "sol", + "tarotCard": "The Sun", + "godId": "ra" + }, + "source": "celestial-holidays.holidays[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "gregorian-sirius-rising-window", + "calendarId": "gregorian", + "monthId": "july", + "name": "Sirius Rising Window", + "dateText": "07-20 to 08-12", + "day": 20, + "monthDayStart": "07-20", + "description": "Heliacal rising period of Sirius in many northern latitudes.", + "associations": { + "planetId": "sol", + "zodiacSignId": "leo", + "tarotCard": "Strength", + "godId": "isis" + }, + "source": "celestial-holidays.holidays[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + }, + { + "id": "gregorian-june-solstice", + "calendarId": "gregorian", + "monthId": "june", + "name": "June Solstice", + "dateText": "06-21", + "day": 21, + "monthDayStart": "06-21", + "description": "Longest day in the northern hemisphere; shortest in the southern hemisphere.", + "associations": { + "planetId": "sol", + "zodiacSignId": "cancer", + "tarotCard": "The Sun", + "godId": "ra" + }, + "source": "celestial-holidays.holidays[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "gregorian-march-equinox", + "calendarId": "gregorian", + "monthId": "march", + "name": "March Equinox", + "dateText": "03-20", + "day": 20, + "monthDayStart": "03-20", + "description": "Equal day and night; marks spring in the north and autumn in the south.", + "associations": { + "planetId": "sol", + "zodiacSignId": "aries", + "tarotCard": "The Emperor", + "godId": "maat" + }, + "source": "celestial-holidays.holidays[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "gregorian-spring-eclipse-window", + "calendarId": "gregorian", + "monthId": "march", + "name": "Spring Eclipse Window", + "dateText": "03-15 to 04-15", + "day": 15, + "monthDayStart": "03-15", + "description": "Typical annual window for paired solar/lunar eclipses near nodal alignments.", + "associations": { + "planetId": "luna", + "zodiacSignId": "aries", + "tarotCard": "The Moon", + "godId": "thoth" + }, + "source": "celestial-holidays.holidays[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + }, + { + "id": "gregorian-beltane-cross-quarter", + "calendarId": "gregorian", + "monthId": "may", + "name": "Beltane Cross-Quarter", + "dateText": "05-01 to 05-02", + "day": 1, + "monthDayStart": "05-01", + "description": "Fertility and fire threshold between equinox and solstice.", + "associations": { + "planetId": "venus", + "zodiacSignId": "taurus", + "tarotCard": "The Empress", + "godId": "aphrodite" + }, + "source": "celestial-holidays.holidays[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + }, + { + "id": "gregorian-leonids-peak", + "calendarId": "gregorian", + "monthId": "november", + "name": "Leonids Meteor Peak", + "dateText": "11-17", + "day": 17, + "monthDayStart": "11-17", + "description": "Peak of the Leonids meteor shower under dark skies.", + "associations": { + "planetId": "jupiter", + "zodiacSignId": "sagittarius", + "tarotCard": "Temperance", + "godId": "zeus" + }, + "source": "celestial-holidays.holidays[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "gregorian-samhain-cross-quarter", + "calendarId": "gregorian", + "monthId": "october", + "name": "Samhain Cross-Quarter", + "dateText": "10-31 to 11-01", + "day": 31, + "monthDayStart": "10-31", + "description": "Threshold of deep autumn and ancestral observance in many traditions.", + "associations": { + "planetId": "saturn", + "zodiacSignId": "scorpio", + "tarotCard": "Death", + "godId": "hades" + }, + "source": "celestial-holidays.holidays[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + }, + { + "id": "gregorian-autumn-eclipse-window", + "calendarId": "gregorian", + "monthId": "september", + "name": "Autumn Eclipse Window", + "dateText": "09-15 to 10-15", + "day": 15, + "monthDayStart": "09-15", + "description": "Typical annual window for eclipse pairings near opposite nodal season.", + "associations": { + "planetId": "luna", + "zodiacSignId": "libra", + "tarotCard": "The Moon", + "godId": "anubis" + }, + "source": "celestial-holidays.holidays[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + }, + { + "id": "gregorian-september-equinox", + "calendarId": "gregorian", + "monthId": "september", + "name": "September Equinox", + "dateText": "09-22", + "day": 22, + "monthDayStart": "09-22", + "description": "Equal day and night; marks autumn in the north and spring in the south.", + "associations": { + "planetId": "sol", + "zodiacSignId": "libra", + "tarotCard": "Justice", + "godId": "maat" + }, + "source": "celestial-holidays.holidays[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "gregorian-new-year-day", + "calendarId": "gregorian", + "monthId": "january", + "name": "New Year's Day", + "dateText": "01-01", + "day": 1, + "monthDayStart": "01-01", + "description": "First day of the Gregorian civil year.", + "associations": {}, + "source": "popular-holidays.curated", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "gregorian-good-friday", + "calendarId": "gregorian", + "monthId": "movable", + "name": "Good Friday", + "dateText": "Moveable Friday before Easter (March/April)", + "day": null, + "dateRule": "gregorian-good-friday", + "description": "Christian observance commemorating the crucifixion of Jesus.", + "associations": {}, + "source": "popular-holidays.curated", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "gregorian-easter-sunday", + "calendarId": "gregorian", + "monthId": "movable", + "name": "Easter Sunday", + "dateText": "Moveable Sunday (March/April)", + "day": null, + "dateRule": "gregorian-easter-sunday", + "description": "Christian feast celebrating the resurrection of Jesus.", + "associations": {}, + "source": "popular-holidays.curated", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "gregorian-halloween", + "calendarId": "gregorian", + "monthId": "october", + "name": "Halloween", + "dateText": "10-31", + "day": 31, + "monthDayStart": "10-31", + "description": "Popular autumn festival observed on October 31.", + "associations": {}, + "source": "popular-holidays.curated", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "gregorian-thanksgiving-us", + "calendarId": "gregorian", + "monthId": "movable", + "name": "Thanksgiving (US)", + "dateText": "4th Thursday of November", + "day": null, + "dateRule": "gregorian-thanksgiving-us", + "description": "US harvest holiday observed on the fourth Thursday in November.", + "associations": {}, + "source": "popular-holidays.curated", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "gregorian-christmas-eve", + "calendarId": "gregorian", + "monthId": "december", + "name": "Christmas Eve", + "dateText": "12-24", + "day": 24, + "monthDayStart": "12-24", + "description": "Evening before Christmas Day, widely observed globally.", + "associations": {}, + "source": "popular-holidays.curated", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "gregorian-christmas-day", + "calendarId": "gregorian", + "monthId": "december", + "name": "Christmas Day", + "dateText": "12-25", + "day": 25, + "monthDayStart": "12-25", + "description": "Widely observed Christian and cultural holiday on December 25.", + "associations": {}, + "source": "popular-holidays.curated", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "gregorian-boxing-day", + "calendarId": "gregorian", + "monthId": "december", + "name": "Boxing Day", + "dateText": "12-26", + "day": 26, + "monthDayStart": "12-26", + "description": "Public holiday observed in many countries on December 26.", + "associations": {}, + "source": "popular-holidays.curated", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "gregorian-new-years-eve", + "calendarId": "gregorian", + "monthId": "december", + "name": "New Year's Eve", + "dateText": "12-31", + "day": 31, + "monthDayStart": "12-31", + "description": "Last day of the Gregorian civil year.", + "associations": {}, + "source": "popular-holidays.curated", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "hebrew-adar-ii-2-purim", + "calendarId": "hebrew", + "monthId": "adar-ii", + "name": "Purim", + "dateText": "14 Adar II", + "day": 14, + "description": "Purim celebrations in leap years.", + "associations": { + "zodiacSignId": "pisces", + "hebrewLetterId": "qof" + }, + "source": "hebrew-calendar.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "hebrew-adar-ii-1-ta-anit-esther", + "calendarId": "hebrew", + "monthId": "adar-ii", + "name": "Ta'anit Esther", + "dateText": "13 Adar II", + "day": 13, + "description": "Fast of Esther.", + "associations": { + "zodiacSignId": "pisces", + "hebrewLetterId": "qof" + }, + "source": "hebrew-calendar.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "hebrew-adar-2-purim", + "calendarId": "hebrew", + "monthId": "adar", + "name": "Purim", + "dateText": "14 Adar", + "day": 14, + "description": "Joyful holiday celebrating the salvation of the Jews in Persia; megillah reading, costumes, gifts.", + "associations": { + "zodiacSignId": "pisces", + "hebrewLetterId": "qof" + }, + "source": "hebrew-calendar.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "hebrew-adar-3-shushan-purim", + "calendarId": "hebrew", + "monthId": "adar", + "name": "Shushan Purim", + "dateText": "15 Adar", + "day": 15, + "description": "Purim as celebrated in walled cities.", + "associations": { + "zodiacSignId": "pisces", + "hebrewLetterId": "qof" + }, + "source": "hebrew-calendar.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "hebrew-adar-1-ta-anit-esther", + "calendarId": "hebrew", + "monthId": "adar", + "name": "Ta'anit Esther", + "dateText": "13 Adar", + "day": 13, + "description": "Fast of Esther, the day before Purim.", + "associations": { + "zodiacSignId": "pisces", + "hebrewLetterId": "qof" + }, + "source": "hebrew-calendar.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "hebrew-av-1-tisha-b-av", + "calendarId": "hebrew", + "monthId": "av", + "name": "Tisha B'Av", + "dateText": "9 Av", + "day": 9, + "description": "Saddest day on the Jewish calendar; mourns destruction of both Temples and other calamities.", + "associations": { + "zodiacSignId": "leo", + "hebrewLetterId": "tet" + }, + "source": "hebrew-calendar.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "hebrew-av-2-tu-b-av", + "calendarId": "hebrew", + "monthId": "av", + "name": "Tu B'Av", + "dateText": "15 Av", + "day": 15, + "description": "Minor holiday; traditionally a day for finding one's soulmate.", + "associations": { + "zodiacSignId": "leo", + "hebrewLetterId": "tet" + }, + "source": "hebrew-calendar.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "hebrew-elul-2-daily-shofar-blowing", + "calendarId": "hebrew", + "monthId": "elul", + "name": "Daily Shofar blowing", + "dateText": "1–29 Elul", + "day": 1, + "description": "Shofar sounded every morning to call to repentance.", + "associations": { + "zodiacSignId": "virgo", + "hebrewLetterId": "yod" + }, + "source": "hebrew-calendar.months[].observances[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + }, + { + "id": "hebrew-elul-1-selichot-penitential-prayers", + "calendarId": "hebrew", + "monthId": "elul", + "name": "Selichot (penitential prayers)", + "dateText": "Throughout Elul", + "day": null, + "description": "Prayers of forgiveness recited in preparation for Rosh Hashanah.", + "associations": { + "zodiacSignId": "virgo", + "hebrewLetterId": "yod" + }, + "source": "hebrew-calendar.months[].observances[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + }, + { + "id": "hebrew-iyar-3-lag-baomer", + "calendarId": "hebrew", + "monthId": "iyar", + "name": "Lag BaOmer", + "dateText": "18 Iyar", + "day": 18, + "description": "33rd day of the Omer count; bonfire celebrations.", + "associations": { + "zodiacSignId": "taurus", + "hebrewLetterId": "vav" + }, + "source": "hebrew-calendar.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "hebrew-iyar-2-yom-ha-atzmaut", + "calendarId": "hebrew", + "monthId": "iyar", + "name": "Yom Ha'atzmaut", + "dateText": "5 Iyar", + "day": 5, + "description": "Israeli Independence Day.", + "associations": { + "zodiacSignId": "taurus", + "hebrewLetterId": "vav" + }, + "source": "hebrew-calendar.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "hebrew-iyar-1-yom-hashoah", + "calendarId": "hebrew", + "monthId": "iyar", + "name": "Yom HaShoah", + "dateText": "27 Nisan / early Iyar", + "day": 27, + "description": "Holocaust Remembrance Day.", + "associations": { + "zodiacSignId": "taurus", + "hebrewLetterId": "vav" + }, + "source": "hebrew-calendar.months[].observances[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + }, + { + "id": "hebrew-kislev-1-hanukkah-begins", + "calendarId": "hebrew", + "monthId": "kislev", + "name": "Hanukkah begins", + "dateText": "25 Kislev", + "day": 25, + "description": "Eight-day festival of lights commemorating rededication of the Temple; lighting the Hanukkiah.", + "associations": { + "zodiacSignId": "sagittarius", + "hebrewLetterId": "samekh" + }, + "source": "hebrew-calendar.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "hebrew-nisan-2-counting-of-the-omer-begins", + "calendarId": "hebrew", + "monthId": "nisan", + "name": "Counting of the Omer begins", + "dateText": "16 Nisan", + "day": 16, + "description": "49-day count linking Passover to Shavuot.", + "associations": { + "zodiacSignId": "aries", + "hebrewLetterId": "he" + }, + "source": "hebrew-calendar.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "hebrew-nisan-3-last-day-of-passover", + "calendarId": "hebrew", + "monthId": "nisan", + "name": "Last day of Passover", + "dateText": "22 Nisan", + "day": 22, + "description": "Concludes the Passover festival.", + "associations": { + "zodiacSignId": "aries", + "hebrewLetterId": "he" + }, + "source": "hebrew-calendar.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "hebrew-nisan-1-passover-pesach-seder", + "calendarId": "hebrew", + "monthId": "nisan", + "name": "Passover (Pesach) — Seder", + "dateText": "15 Nisan", + "day": 15, + "description": "Eight-day festival commemorating the Exodus from Egypt.", + "associations": { + "zodiacSignId": "aries", + "hebrewLetterId": "he" + }, + "source": "hebrew-calendar.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "hebrew-shvat-1-tu-b-shvat", + "calendarId": "hebrew", + "monthId": "shvat", + "name": "Tu B'Shvat", + "dateText": "15 Shvat", + "day": 15, + "description": "New Year of the Trees; fruit trees wake; associated with Kabbalistic 'Seder of the Four Worlds.'", + "associations": { + "zodiacSignId": "aquarius", + "hebrewLetterId": "tsadi" + }, + "source": "hebrew-calendar.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "hebrew-sivan-1-shavuot", + "calendarId": "hebrew", + "monthId": "sivan", + "name": "Shavuot", + "dateText": "6 Sivan", + "day": 6, + "description": "Feast of Weeks; celebrates the giving of the Torah and the first fruits harvest.", + "associations": { + "zodiacSignId": "gemini", + "hebrewLetterId": "zayin" + }, + "source": "hebrew-calendar.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "hebrew-tammuz-1-17th-of-tammuz", + "calendarId": "hebrew", + "monthId": "tammuz", + "name": "17th of Tammuz", + "dateText": "17 Tammuz", + "day": 17, + "description": "Fast day commemorating the breaching of Jerusalem's walls. Begins the Three Weeks of mourning.", + "associations": { + "zodiacSignId": "cancer", + "hebrewLetterId": "het" + }, + "source": "hebrew-calendar.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "hebrew-tevet-2-10th-of-tevet", + "calendarId": "hebrew", + "monthId": "tevet", + "name": "10th of Tevet", + "dateText": "10 Tevet", + "day": 10, + "description": "Fast day marking the Babylonian siege of Jerusalem.", + "associations": { + "zodiacSignId": "capricorn", + "hebrewLetterId": "ayin" + }, + "source": "hebrew-calendar.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "hebrew-tevet-1-hanukkah-concludes", + "calendarId": "hebrew", + "monthId": "tevet", + "name": "Hanukkah concludes", + "dateText": "2–3 Tevet", + "day": 2, + "description": "Final days of the Festival of Lights.", + "associations": { + "zodiacSignId": "capricorn", + "hebrewLetterId": "ayin" + }, + "source": "hebrew-calendar.months[].observances[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + }, + { + "id": "hebrew-tishrei-1-rosh-hashanah", + "calendarId": "hebrew", + "monthId": "tishrei", + "name": "Rosh Hashanah", + "dateText": "1–2 Tishrei", + "day": 1, + "description": "Jewish New Year; beginning of the Ten Days of Repentance.", + "associations": { + "zodiacSignId": "libra", + "hebrewLetterId": "lamed" + }, + "source": "hebrew-calendar.months[].observances[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + }, + { + "id": "hebrew-tishrei-4-shemini-atzeret-simchat-torah", + "calendarId": "hebrew", + "monthId": "tishrei", + "name": "Shemini Atzeret / Simchat Torah", + "dateText": "22–23 Tishrei", + "day": 22, + "description": "Conclusion of Sukkot; celebrates completing the annual Torah reading cycle.", + "associations": { + "zodiacSignId": "libra", + "hebrewLetterId": "lamed" + }, + "source": "hebrew-calendar.months[].observances[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + }, + { + "id": "hebrew-tishrei-3-sukkot", + "calendarId": "hebrew", + "monthId": "tishrei", + "name": "Sukkot", + "dateText": "15–21 Tishrei", + "day": 15, + "description": "Feast of Tabernacles; seven-day harvest festival in temporary booths.", + "associations": { + "zodiacSignId": "libra", + "hebrewLetterId": "lamed" + }, + "source": "hebrew-calendar.months[].observances[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + }, + { + "id": "hebrew-tishrei-2-yom-kippur", + "calendarId": "hebrew", + "monthId": "tishrei", + "name": "Yom Kippur", + "dateText": "10 Tishrei", + "day": 10, + "description": "Day of Atonement; holiest day of the year. Full-day fast.", + "associations": { + "zodiacSignId": "libra", + "hebrewLetterId": "lamed" + }, + "source": "hebrew-calendar.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "islamic-dhu-al-hijja-1-day-of-arafah", + "calendarId": "islamic", + "monthId": "dhu-al-hijja", + "name": "Day of Arafah", + "dateText": "9 Dhu al-Hijja", + "day": 9, + "description": "Most important day of Hajj; pilgrims stand in prayer at the plain of Arafah. Voluntary fasting recommended for non-pilgrims.", + "associations": {}, + "source": "islamic-calendar.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "islamic-dhu-al-hijja-3-days-of-tashriq", + "calendarId": "islamic", + "monthId": "dhu-al-hijja", + "name": "Days of Tashriq", + "dateText": "11–13 Dhu al-Hijja", + "day": 11, + "description": "Final days of the Hajj rites; pilgrims complete the Stoning of the Devil ritual.", + "associations": {}, + "source": "islamic-calendar.months[].observances[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + }, + { + "id": "islamic-dhu-al-hijja-2-eid-al-adha", + "calendarId": "islamic", + "monthId": "dhu-al-hijja", + "name": "Eid al-Adha", + "dateText": "10 Dhu al-Hijja", + "day": 10, + "description": "Festival of Sacrifice commemorating Ibrahim's willingness to sacrifice his son. Animal sacrifice and sharing of meat with the poor.", + "associations": {}, + "source": "islamic-calendar.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "islamic-dhu-al-qada-1-pilgrims-travel-to-mecca", + "calendarId": "islamic", + "monthId": "dhu-al-qada", + "name": "Pilgrims travel to Mecca", + "dateText": "Throughout Dhu al-Qada", + "day": null, + "description": "Preparation month for the Hajj pilgrimage in Dhu al-Hijja.", + "associations": {}, + "source": "islamic-calendar.months[].observances[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + }, + { + "id": "islamic-muharram-2-ashura", + "calendarId": "islamic", + "monthId": "muharram", + "name": "Ashura", + "dateText": "10 Muharram", + "day": 10, + "description": "For Sunni Muslims: Moses' fast of gratitude; recommended fast. For Shia Muslims: day of mourning for the martyrdom of Husayn ibn Ali at Karbala.", + "associations": {}, + "source": "islamic-calendar.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "islamic-muharram-1-islamic-new-year-ras-as-sana", + "calendarId": "islamic", + "monthId": "muharram", + "name": "Islamic New Year (Ras as-Sana)", + "dateText": "1 Muharram", + "day": 1, + "description": "Beginning of the Hijri new year.", + "associations": {}, + "source": "islamic-calendar.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "islamic-rabi-al-awwal-1-mawlid-al-nabi", + "calendarId": "islamic", + "monthId": "rabi-al-awwal", + "name": "Mawlid al-Nabi", + "dateText": "12 Rabi' al-Awwal", + "day": 12, + "description": "Celebration of the birth of the Prophet Muhammad. Widely observed with prayers, gatherings, and processions.", + "associations": {}, + "source": "islamic-calendar.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "islamic-rajab-1-laylat-al-mi-raj-isra-wal-mi-raj", + "calendarId": "islamic", + "monthId": "rajab", + "name": "Laylat al-Mi'raj (Isra wal Mi'raj)", + "dateText": "27 Rajab", + "day": 27, + "description": "Commemorates the Prophet Muhammad's miraculous night journey to Jerusalem and ascension to the heavens.", + "associations": {}, + "source": "islamic-calendar.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "islamic-ramadan-3-eid-al-fitr", + "calendarId": "islamic", + "monthId": "ramadan", + "name": "Eid al-Fitr", + "dateText": "1 Shawwal (end of Ramadan)", + "day": 1, + "description": "Festival of Breaking the Fast; begins at the sighting of the new moon after Ramadan.", + "associations": {}, + "source": "islamic-calendar.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "islamic-ramadan-2-laylat-al-qadr-night-of-power", + "calendarId": "islamic", + "monthId": "ramadan", + "name": "Laylat al-Qadr (Night of Power)", + "dateText": "One of the last 10 odd nights", + "day": 10, + "description": "Night on which the Quran was first revealed; considered the most blessed night of the year.", + "associations": {}, + "source": "islamic-calendar.months[].observances[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + }, + { + "id": "islamic-ramadan-1-ramadan-fast-begins", + "calendarId": "islamic", + "monthId": "ramadan", + "name": "Ramadan Fast begins", + "dateText": "1 Ramadan", + "day": 1, + "description": "Month-long obligatory fast from dawn to sunset; intensified prayer, Quran recitation, charity.", + "associations": {}, + "source": "islamic-calendar.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "islamic-shaban-1-laylat-al-bara-at-night-of-records", + "calendarId": "islamic", + "monthId": "shaban", + "name": "Laylat al-Bara'at (Night of Records)", + "dateText": "15 Sha'ban", + "day": 15, + "description": "Night believed to be when God decrees the fate of each person for the coming year; many observe voluntary fasting and prayer.", + "associations": {}, + "source": "islamic-calendar.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "islamic-shawwal-1-eid-al-fitr", + "calendarId": "islamic", + "monthId": "shawwal", + "name": "Eid al-Fitr", + "dateText": "1 Shawwal", + "day": 1, + "description": "Three-day celebration ending Ramadan; prayers, feasting, gifts, and charity.", + "associations": {}, + "source": "islamic-calendar.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "islamic-shawwal-2-six-days-of-shawwal-optional-fast", + "calendarId": "islamic", + "monthId": "shawwal", + "name": "Six Days of Shawwal (optional fast)", + "dateText": "2–7 Shawwal", + "day": 2, + "description": "Recommended voluntary fast of six days which completes a full year of fasting in reward.", + "associations": {}, + "source": "islamic-calendar.months[].observances[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + }, + { + "id": "wheel-beltane-1-beltane-bonfire", + "calendarId": "wheel-of-year", + "monthId": "beltane", + "name": "Beltane Bonfire", + "dateText": "May 1", + "day": 1, + "monthDayStart": "05-01", + "description": "Leap over or dance around a bonfire for purification and vitality.", + "associations": { + "direction": "Southeast", + "themes": [ + "fertility", + "passion", + "union", + "abundance", + "sexuality", + "vitality" + ], + "deities": [ + "The Green Man", + "Flora", + "Cernunnos", + "Aphrodite", + "Beltane Fire" + ], + "colors": [ + "red", + "white", + "green", + "gold" + ], + "herbs": [ + "hawthorn", + "rose", + "rowan", + "meadowsweet", + "woodruff" + ], + "element": "Fire" + }, + "source": "wheel-of-year.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "wheel-beltane-2-maypole-dance", + "calendarId": "wheel-of-year", + "monthId": "beltane", + "name": "Maypole dance", + "dateText": "May 1", + "day": 1, + "monthDayStart": "05-01", + "description": "Weaving ribbons around a May pole; ceremony of union and fertility.", + "associations": { + "direction": "Southeast", + "themes": [ + "fertility", + "passion", + "union", + "abundance", + "sexuality", + "vitality" + ], + "deities": [ + "The Green Man", + "Flora", + "Cernunnos", + "Aphrodite", + "Beltane Fire" + ], + "colors": [ + "red", + "white", + "green", + "gold" + ], + "herbs": [ + "hawthorn", + "rose", + "rowan", + "meadowsweet", + "woodruff" + ], + "element": "Fire" + }, + "source": "wheel-of-year.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "wheel-imbolc-1-brigid-s-cross", + "calendarId": "wheel-of-year", + "monthId": "imbolc", + "name": "Brigid's Cross", + "dateText": "February 1–2", + "day": 1, + "monthDayStart": "02-01", + "description": "Weave a Brigid's Cross from rushes to invite protection and blessing for the year.", + "associations": { + "direction": "Northeast", + "themes": [ + "new beginnings", + "purification", + "inspiration", + "hearth", + "healing" + ], + "deities": [ + "Brigid / Bride", + "St. Brigid", + "The Maiden" + ], + "colors": [ + "white", + "silver", + "pale yellow", + "pink" + ], + "herbs": [ + "snowdrop", + "rosemary", + "angelica", + "basil" + ], + "element": "Fire / Earth" + }, + "source": "wheel-of-year.months[].observances[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + }, + { + "id": "wheel-imbolc-2-candle-dedication", + "calendarId": "wheel-of-year", + "monthId": "imbolc", + "name": "Candle dedication", + "dateText": "February 1–2", + "day": 1, + "monthDayStart": "02-01", + "description": "Light and bless candles for the coming season's work.", + "associations": { + "direction": "Northeast", + "themes": [ + "new beginnings", + "purification", + "inspiration", + "hearth", + "healing" + ], + "deities": [ + "Brigid / Bride", + "St. Brigid", + "The Maiden" + ], + "colors": [ + "white", + "silver", + "pale yellow", + "pink" + ], + "herbs": [ + "snowdrop", + "rosemary", + "angelica", + "basil" + ], + "element": "Fire / Earth" + }, + "source": "wheel-of-year.months[].observances[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + }, + { + "id": "wheel-litha-2-bonfire-leaping", + "calendarId": "wheel-of-year", + "monthId": "litha", + "name": "Bonfire leaping", + "dateText": "~June 21", + "day": 21, + "monthDayStart": "06-21", + "description": "Solstice fires ward off evil and bless crops and animals.", + "associations": { + "direction": "South", + "themes": [ + "peak power", + "fullness", + "solar magic", + "fae", + "courage", + "strength" + ], + "deities": [ + "Sol / Sun God", + "Lugh (early)", + "Ra", + "Apollo" + ], + "colors": [ + "gold", + "yellow", + "orange", + "blue" + ], + "herbs": [ + "St. John's Wort", + "lavender", + "chamomile", + "elderflower", + "fern seed" + ], + "element": "Fire" + }, + "source": "wheel-of-year.months[].observances[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + }, + { + "id": "wheel-litha-1-midsummer-vigil", + "calendarId": "wheel-of-year", + "monthId": "litha", + "name": "Midsummer vigil", + "dateText": "~June 21", + "day": 21, + "monthDayStart": "06-21", + "description": "Watch the sunrise on the solstice; gather herbs at dawn for maximum potency.", + "associations": { + "direction": "South", + "themes": [ + "peak power", + "fullness", + "solar magic", + "fae", + "courage", + "strength" + ], + "deities": [ + "Sol / Sun God", + "Lugh (early)", + "Ra", + "Apollo" + ], + "colors": [ + "gold", + "yellow", + "orange", + "blue" + ], + "herbs": [ + "St. John's Wort", + "lavender", + "chamomile", + "elderflower", + "fern seed" + ], + "element": "Fire" + }, + "source": "wheel-of-year.months[].observances[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + }, + { + "id": "wheel-lughnasadh-1-first-loaf-ritual", + "calendarId": "wheel-of-year", + "monthId": "lughnasadh", + "name": "First loaf ritual", + "dateText": "August 1", + "day": 1, + "monthDayStart": "08-01", + "description": "Bake bread from the harvest grain; offer a portion back to the earth.", + "associations": { + "direction": "West / Southwest", + "themes": [ + "first harvest", + "grain", + "sacrifice", + "abundance", + "skill", + "games" + ], + "deities": [ + "Lugh", + "The Corn King", + "John Barleycorn", + "Demeter" + ], + "colors": [ + "gold", + "orange", + "brown", + "yellow" + ], + "herbs": [ + "wheat", + "sunflower", + "corn", + "calendula", + "nasturtium" + ], + "element": "Earth / Fire" + }, + "source": "wheel-of-year.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "wheel-lughnasadh-2-funeral-games", + "calendarId": "wheel-of-year", + "monthId": "lughnasadh", + "name": "Funeral games", + "dateText": "August 1", + "day": 1, + "monthDayStart": "08-01", + "description": "Physical contests and games honoring Tailtiu (Teltown Games tradition).", + "associations": { + "direction": "West / Southwest", + "themes": [ + "first harvest", + "grain", + "sacrifice", + "abundance", + "skill", + "games" + ], + "deities": [ + "Lugh", + "The Corn King", + "John Barleycorn", + "Demeter" + ], + "colors": [ + "gold", + "orange", + "brown", + "yellow" + ], + "herbs": [ + "wheat", + "sunflower", + "corn", + "calendula", + "nasturtium" + ], + "element": "Earth / Fire" + }, + "source": "wheel-of-year.months[].observances[]", + "datePrecision": "exact", + "conversionConfidence": "exact" + }, + { + "id": "wheel-mabon-2-balance-meditation", + "calendarId": "wheel-of-year", + "monthId": "mabon", + "name": "Balance meditation", + "dateText": "~September 22–23", + "day": 22, + "monthDayStart": "09-22", + "description": "Reflect on what you are releasing as you move into the dark half of the year.", + "associations": { + "direction": "West", + "themes": [ + "harvest", + "balance", + "gratitude", + "preparation for darkness", + "completion" + ], + "deities": [ + "Mabon ap Modron", + "Persephone descending", + "The Morrigan", + "Dionysus" + ], + "colors": [ + "deep red", + "brown", + "russet", + "purple", + "gold" + ], + "herbs": [ + "ivy", + "oak", + "blackberry", + "rosehip", + "sage" + ], + "element": "Earth / Water" + }, + "source": "wheel-of-year.months[].observances[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + }, + { + "id": "wheel-mabon-1-second-harvest-feast", + "calendarId": "wheel-of-year", + "monthId": "mabon", + "name": "Second Harvest feast", + "dateText": "~September 22–23", + "day": 22, + "monthDayStart": "09-22", + "description": "Give thanks and share the abundance of the harvest.", + "associations": { + "direction": "West", + "themes": [ + "harvest", + "balance", + "gratitude", + "preparation for darkness", + "completion" + ], + "deities": [ + "Mabon ap Modron", + "Persephone descending", + "The Morrigan", + "Dionysus" + ], + "colors": [ + "deep red", + "brown", + "russet", + "purple", + "gold" + ], + "herbs": [ + "ivy", + "oak", + "blackberry", + "rosehip", + "sage" + ], + "element": "Earth / Water" + }, + "source": "wheel-of-year.months[].observances[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + }, + { + "id": "wheel-ostara-2-balance-ritual", + "calendarId": "wheel-of-year", + "monthId": "ostara", + "name": "Balance ritual", + "dateText": "~March 20–21", + "day": 20, + "monthDayStart": "03-20", + "description": "Meditate on and balance opposing forces in your life at the equinox point.", + "associations": { + "direction": "East", + "themes": [ + "balance", + "rebirth", + "fertility", + "dawn", + "new beginnings" + ], + "deities": [ + "Ēostre", + "Persephone", + "The Maiden", + "Green Man" + ], + "colors": [ + "pale green", + "yellow", + "lilac", + "pastel" + ], + "herbs": [ + "violet", + "jasmine", + "daffodil", + "lavender" + ], + "element": "Air" + }, + "source": "wheel-of-year.months[].observances[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + }, + { + "id": "wheel-ostara-1-seed-blessing", + "calendarId": "wheel-of-year", + "monthId": "ostara", + "name": "Seed blessing", + "dateText": "~March 20–21", + "day": 20, + "monthDayStart": "03-20", + "description": "Bless seeds or intentions for what you wish to grow this season.", + "associations": { + "direction": "East", + "themes": [ + "balance", + "rebirth", + "fertility", + "dawn", + "new beginnings" + ], + "deities": [ + "Ēostre", + "Persephone", + "The Maiden", + "Green Man" + ], + "colors": [ + "pale green", + "yellow", + "lilac", + "pastel" + ], + "herbs": [ + "violet", + "jasmine", + "daffodil", + "lavender" + ], + "element": "Air" + }, + "source": "wheel-of-year.months[].observances[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + }, + { + "id": "wheel-samhain-1-ancestor-altar", + "calendarId": "wheel-of-year", + "monthId": "samhain", + "name": "Ancestor Altar", + "dateText": "October 31 / November 1", + "day": 31, + "monthDayStart": "10-31", + "description": "Set a dumb supper or ancestor altar to honor the beloved dead.", + "associations": { + "direction": "North / Northwest", + "themes": [ + "death and rebirth", + "ancestors", + "the veil", + "divination", + "endings" + ], + "deities": [ + "The Crone", + "Hecate", + "The Morrigan", + "Hades", + "Osiris" + ], + "colors": [ + "black", + "orange", + "deep red", + "purple" + ], + "herbs": [ + "mugwort", + "wormwood", + "dittany of Crete", + "nightshade" + ], + "element": "Earth" + }, + "source": "wheel-of-year.months[].observances[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + }, + { + "id": "wheel-samhain-2-divination-work", + "calendarId": "wheel-of-year", + "monthId": "samhain", + "name": "Divination Work", + "dateText": "October 31 / November 1", + "day": 31, + "monthDayStart": "10-31", + "description": "Thin veil makes scrying, tarot, and astral work potent tonight.", + "associations": { + "direction": "North / Northwest", + "themes": [ + "death and rebirth", + "ancestors", + "the veil", + "divination", + "endings" + ], + "deities": [ + "The Crone", + "Hecate", + "The Morrigan", + "Hades", + "Osiris" + ], + "colors": [ + "black", + "orange", + "deep red", + "purple" + ], + "herbs": [ + "mugwort", + "wormwood", + "dittany of Crete", + "nightshade" + ], + "element": "Earth" + }, + "source": "wheel-of-year.months[].observances[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + }, + { + "id": "wheel-yule-2-sun-vigil", + "calendarId": "wheel-of-year", + "monthId": "yule", + "name": "Sun Vigil", + "dateText": "~December 21", + "day": 21, + "monthDayStart": "12-21", + "description": "Stay up through the night or rise at dawn to greet the reborn sun.", + "associations": { + "direction": "North", + "themes": [ + "rebirth of the sun", + "hope", + "inner light", + "return of warmth", + "solstice" + ], + "deities": [ + "The Oak King", + "Sol Invictus", + "Mithras", + "Odin / Father Christmas" + ], + "colors": [ + "red", + "green", + "gold", + "white" + ], + "herbs": [ + "holly", + "ivy", + "mistletoe", + "pine", + "frankincense" + ], + "element": "Earth" + }, + "source": "wheel-of-year.months[].observances[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + }, + { + "id": "wheel-yule-1-yule-log-burning", + "calendarId": "wheel-of-year", + "monthId": "yule", + "name": "Yule Log burning", + "dateText": "~December 21", + "day": 21, + "monthDayStart": "12-21", + "description": "Oak log burned through the night to welcome the sun's return.", + "associations": { + "direction": "North", + "themes": [ + "rebirth of the sun", + "hope", + "inner light", + "return of warmth", + "solstice" + ], + "deities": [ + "The Oak King", + "Sol Invictus", + "Mithras", + "Odin / Father Christmas" + ], + "colors": [ + "red", + "green", + "gold", + "white" + ], + "herbs": [ + "holly", + "ivy", + "mistletoe", + "pine", + "frankincense" + ], + "element": "Earth" + }, + "source": "wheel-of-year.months[].observances[]", + "datePrecision": "approximate", + "conversionConfidence": "approximate" + } + ] +} diff --git a/data/calendar-months.json b/data/calendar-months.json new file mode 100644 index 0000000..241b5dc --- /dev/null +++ b/data/calendar-months.json @@ -0,0 +1,379 @@ +{ + "meta": { + "version": 1, + "notes": "Month-level correspondences and observances for the Calendar section.", + "links": "holidayIds map to entries in celestial-holidays.json" + }, + "months": [ + { + "id": "january", + "order": 1, + "name": "January", + "start": "01-01", + "end": "01-31", + "seasonNorth": "Winter", + "seasonSouth": "Summer", + "coreTheme": "Beginnings, thresholds, planning", + "associations": { + "planetId": "saturn", + "zodiacSignId": "capricorn", + "tarotCard": "The Devil", + "godId": "janus" + }, + "events": [ + { + "id": "january-threshold-rite", + "name": "Threshold Rite", + "date": "01-01", + "description": "Set year intentions and define a boundary/structure for one major goal.", + "associations": { + "godId": "janus", + "planetId": "saturn", + "tarotCard": "The Fool" + } + } + ], + "holidayIds": ["perihelion"] + }, + { + "id": "february", + "order": 2, + "name": "February", + "start": "02-01", + "end": "02-28", + "seasonNorth": "Winter", + "seasonSouth": "Summer", + "coreTheme": "Purification, preparation, hidden momentum", + "associations": { + "planetId": "saturn", + "zodiacSignId": "aquarius", + "tarotCard": "The Star", + "godId": "uranus" + }, + "events": [ + { + "id": "february-clearing-work", + "name": "Cleansing & Clearing", + "date": "02-11", + "description": "Remove one recurring distraction and reset practical systems.", + "associations": { + "planetId": "saturn", + "tarotCard": "The Star", + "godId": "maat" + } + } + ], + "holidayIds": ["imbolc-cross-quarter"] + }, + { + "id": "march", + "order": 3, + "name": "March", + "start": "03-01", + "end": "03-31", + "seasonNorth": "Spring", + "seasonSouth": "Autumn", + "coreTheme": "Initiation, momentum, first fire", + "associations": { + "planetId": "mars", + "zodiacSignId": "aries", + "tarotCard": "The Emperor", + "godId": "ares" + }, + "events": [ + { + "id": "march-action-cycle", + "name": "Action Cycle Start", + "date": "03-21", + "description": "Launch a focused 30-day push with daily effort tracking.", + "associations": { + "planetId": "mars", + "zodiacSignId": "aries", + "tarotCard": "The Emperor", + "godId": "ares" + } + } + ], + "holidayIds": ["march-equinox", "spring-eclipse-window"] + }, + { + "id": "april", + "order": 4, + "name": "April", + "start": "04-01", + "end": "04-30", + "seasonNorth": "Spring", + "seasonSouth": "Autumn", + "coreTheme": "Grounding growth, material cultivation", + "associations": { + "planetId": "venus", + "zodiacSignId": "taurus", + "tarotCard": "The Hierophant", + "godId": "aphrodite" + }, + "events": [ + { + "id": "april-earth-offering", + "name": "Earth Offering", + "date": "04-18", + "description": "Dedicate one practical act to long-term stability and abundance.", + "associations": { + "planetId": "venus", + "zodiacSignId": "taurus", + "tarotCard": "The Hierophant", + "godId": "demeter" + } + } + ], + "holidayIds": ["spring-eclipse-window"] + }, + { + "id": "may", + "order": 5, + "name": "May", + "start": "05-01", + "end": "05-31", + "seasonNorth": "Spring", + "seasonSouth": "Autumn", + "coreTheme": "Connection, exchange, and curiosity", + "associations": { + "planetId": "mercury", + "zodiacSignId": "gemini", + "tarotCard": "The Lovers", + "godId": "hermes" + }, + "events": [ + { + "id": "may-message-working", + "name": "Message Working", + "date": "05-14", + "description": "Refine communication in one key relationship or collaboration.", + "associations": { + "planetId": "mercury", + "zodiacSignId": "gemini", + "tarotCard": "The Lovers", + "godId": "hermes" + } + } + ], + "holidayIds": ["beltane-cross-quarter"] + }, + { + "id": "june", + "order": 6, + "name": "June", + "start": "06-01", + "end": "06-30", + "seasonNorth": "Summer", + "seasonSouth": "Winter", + "coreTheme": "Nourishment, protection, emotional coherence", + "associations": { + "planetId": "luna", + "zodiacSignId": "cancer", + "tarotCard": "The Chariot", + "godId": "artemis" + }, + "events": [ + { + "id": "june-hearth-vow", + "name": "Hearth Vow", + "date": "06-20", + "description": "Commit to one practice that protects your inner and outer home.", + "associations": { + "planetId": "luna", + "zodiacSignId": "cancer", + "tarotCard": "The Chariot", + "godId": "isis" + } + } + ], + "holidayIds": ["june-solstice"] + }, + { + "id": "july", + "order": 7, + "name": "July", + "start": "07-01", + "end": "07-31", + "seasonNorth": "Summer", + "seasonSouth": "Winter", + "coreTheme": "Radiance, leadership, visibility", + "associations": { + "planetId": "sol", + "zodiacSignId": "leo", + "tarotCard": "Strength", + "godId": "ra" + }, + "events": [ + { + "id": "july-solar-crowning", + "name": "Solar Crowning", + "date": "07-19", + "description": "Present one visible work publicly and receive clear feedback.", + "associations": { + "planetId": "sol", + "zodiacSignId": "leo", + "tarotCard": "Strength", + "godId": "ra" + } + } + ], + "holidayIds": ["sirius-rising-window"] + }, + { + "id": "august", + "order": 8, + "name": "August", + "start": "08-01", + "end": "08-31", + "seasonNorth": "Summer", + "seasonSouth": "Winter", + "coreTheme": "Refinement, discipline, service", + "associations": { + "planetId": "mercury", + "zodiacSignId": "virgo", + "tarotCard": "The Hermit", + "godId": "thoth" + }, + "events": [ + { + "id": "august-harvest-ledger", + "name": "Harvest Ledger", + "date": "08-12", + "description": "Audit progress and simplify tools before the autumn cycle.", + "associations": { + "planetId": "mercury", + "zodiacSignId": "virgo", + "tarotCard": "The Hermit", + "godId": "thoth" + } + } + ], + "holidayIds": ["sirius-rising-window", "lughnasadh-cross-quarter"] + }, + { + "id": "september", + "order": 9, + "name": "September", + "start": "09-01", + "end": "09-30", + "seasonNorth": "Autumn", + "seasonSouth": "Spring", + "coreTheme": "Balance, agreements, calibration", + "associations": { + "planetId": "venus", + "zodiacSignId": "libra", + "tarotCard": "Justice", + "godId": "maat" + }, + "events": [ + { + "id": "september-balance-work", + "name": "Balance Working", + "date": "09-23", + "description": "Rebalance effort across work, body, and relationships.", + "associations": { + "planetId": "venus", + "zodiacSignId": "libra", + "tarotCard": "Justice", + "godId": "maat" + } + } + ], + "holidayIds": ["september-equinox", "autumn-eclipse-window"] + }, + { + "id": "october", + "order": 10, + "name": "October", + "start": "10-01", + "end": "10-31", + "seasonNorth": "Autumn", + "seasonSouth": "Spring", + "coreTheme": "Depth work, release, transformation", + "associations": { + "planetId": "mars", + "zodiacSignId": "scorpio", + "tarotCard": "Death", + "godId": "hades" + }, + "events": [ + { + "id": "october-shadow-ledger", + "name": "Shadow Ledger", + "date": "10-27", + "description": "Name one pattern to end and one to transmute.", + "associations": { + "planetId": "mars", + "zodiacSignId": "scorpio", + "tarotCard": "Death", + "godId": "hades" + } + } + ], + "holidayIds": ["autumn-eclipse-window", "samhain-cross-quarter"] + }, + { + "id": "november", + "order": 11, + "name": "November", + "start": "11-01", + "end": "11-30", + "seasonNorth": "Autumn", + "seasonSouth": "Spring", + "coreTheme": "Meaning, direction, vision", + "associations": { + "planetId": "jupiter", + "zodiacSignId": "sagittarius", + "tarotCard": "Temperance", + "godId": "zeus" + }, + "events": [ + { + "id": "november-vision-arrow", + "name": "Vision Arrow", + "date": "11-22", + "description": "Choose one long-range objective and align this week to it.", + "associations": { + "planetId": "jupiter", + "zodiacSignId": "sagittarius", + "tarotCard": "Temperance", + "godId": "zeus" + } + } + ], + "holidayIds": ["samhain-cross-quarter", "leonids-peak"] + }, + { + "id": "december", + "order": 12, + "name": "December", + "start": "12-01", + "end": "12-31", + "seasonNorth": "Winter", + "seasonSouth": "Summer", + "coreTheme": "Closure, stillness, and renewal", + "associations": { + "planetId": "saturn", + "zodiacSignId": "capricorn", + "tarotCard": "The Devil", + "godId": "kronos" + }, + "events": [ + { + "id": "december-year-seal", + "name": "Year Seal", + "date": "12-31", + "description": "Consolidate lessons, close open loops, and define the next threshold.", + "associations": { + "planetId": "saturn", + "zodiacSignId": "capricorn", + "tarotCard": "The World", + "godId": "saturn" + } + } + ], + "holidayIds": ["december-solstice", "geminids-peak"] + } + ] +} diff --git a/data/celestial-holidays.json b/data/celestial-holidays.json new file mode 100644 index 0000000..6159656 --- /dev/null +++ b/data/celestial-holidays.json @@ -0,0 +1,203 @@ +{ + "meta": { + "version": 1, + "notes": "Special celestial observances (solstices, equinoxes, eclipse windows, and notable sky events)." + }, + "holidays": [ + { + "id": "perihelion", + "name": "Earth Perihelion", + "kind": "solar-point", + "monthId": "january", + "date": "01-03", + "description": "Earth reaches its closest orbital point to the Sun.", + "associations": { + "planetId": "sol", + "tarotCard": "The Sun", + "godId": "ra" + } + }, + { + "id": "imbolc-cross-quarter", + "name": "Imbolc Cross-Quarter", + "kind": "cross-quarter", + "monthId": "february", + "dateRange": "02-01 to 02-02", + "description": "Seasonal threshold between winter solstice and spring equinox.", + "associations": { + "planetId": "luna", + "zodiacSignId": "aquarius", + "tarotCard": "The Star", + "godId": "hecate" + } + }, + { + "id": "march-equinox", + "name": "March Equinox", + "kind": "equinox", + "monthId": "march", + "date": "03-20", + "description": "Equal day and night; marks spring in the north and autumn in the south.", + "associations": { + "planetId": "sol", + "zodiacSignId": "aries", + "tarotCard": "The Emperor", + "godId": "maat" + } + }, + { + "id": "spring-eclipse-window", + "name": "Spring Eclipse Window", + "kind": "eclipse-window", + "monthId": "march", + "dateRange": "03-15 to 04-15", + "description": "Typical annual window for paired solar/lunar eclipses near nodal alignments.", + "associations": { + "planetId": "luna", + "zodiacSignId": "aries", + "tarotCard": "The Moon", + "godId": "thoth" + } + }, + { + "id": "beltane-cross-quarter", + "name": "Beltane Cross-Quarter", + "kind": "cross-quarter", + "monthId": "may", + "dateRange": "05-01 to 05-02", + "description": "Fertility and fire threshold between equinox and solstice.", + "associations": { + "planetId": "venus", + "zodiacSignId": "taurus", + "tarotCard": "The Empress", + "godId": "aphrodite" + } + }, + { + "id": "june-solstice", + "name": "June Solstice", + "kind": "solstice", + "monthId": "june", + "date": "06-21", + "description": "Longest day in the northern hemisphere; shortest in the southern hemisphere.", + "associations": { + "planetId": "sol", + "zodiacSignId": "cancer", + "tarotCard": "The Sun", + "godId": "ra" + } + }, + { + "id": "sirius-rising-window", + "name": "Sirius Rising Window", + "kind": "stellar-observance", + "monthId": "july", + "dateRange": "07-20 to 08-12", + "description": "Heliacal rising period of Sirius in many northern latitudes.", + "associations": { + "planetId": "sol", + "zodiacSignId": "leo", + "tarotCard": "Strength", + "godId": "isis" + } + }, + { + "id": "lughnasadh-cross-quarter", + "name": "Lughnasadh Cross-Quarter", + "kind": "cross-quarter", + "monthId": "august", + "dateRange": "08-01 to 08-02", + "description": "Harvest threshold between solstice and equinox.", + "associations": { + "planetId": "mercury", + "zodiacSignId": "leo", + "tarotCard": "The Hermit", + "godId": "thoth" + } + }, + { + "id": "september-equinox", + "name": "September Equinox", + "kind": "equinox", + "monthId": "september", + "date": "09-22", + "description": "Equal day and night; marks autumn in the north and spring in the south.", + "associations": { + "planetId": "sol", + "zodiacSignId": "libra", + "tarotCard": "Justice", + "godId": "maat" + } + }, + { + "id": "autumn-eclipse-window", + "name": "Autumn Eclipse Window", + "kind": "eclipse-window", + "monthId": "september", + "dateRange": "09-15 to 10-15", + "description": "Typical annual window for eclipse pairings near opposite nodal season.", + "associations": { + "planetId": "luna", + "zodiacSignId": "libra", + "tarotCard": "The Moon", + "godId": "anubis" + } + }, + { + "id": "samhain-cross-quarter", + "name": "Samhain Cross-Quarter", + "kind": "cross-quarter", + "monthId": "october", + "dateRange": "10-31 to 11-01", + "description": "Threshold of deep autumn and ancestral observance in many traditions.", + "associations": { + "planetId": "saturn", + "zodiacSignId": "scorpio", + "tarotCard": "Death", + "godId": "hades" + } + }, + { + "id": "leonids-peak", + "name": "Leonids Meteor Peak", + "kind": "meteor-shower", + "monthId": "november", + "date": "11-17", + "description": "Peak of the Leonids meteor shower under dark skies.", + "associations": { + "planetId": "jupiter", + "zodiacSignId": "sagittarius", + "tarotCard": "Temperance", + "godId": "zeus" + } + }, + { + "id": "december-solstice", + "name": "December Solstice", + "kind": "solstice", + "monthId": "december", + "date": "12-21", + "description": "Shortest day in the northern hemisphere; longest in the southern hemisphere.", + "associations": { + "planetId": "sol", + "zodiacSignId": "capricorn", + "tarotCard": "The World", + "godId": "saturn" + } + }, + { + "id": "geminids-peak", + "name": "Geminids Meteor Peak", + "kind": "meteor-shower", + "monthId": "december", + "date": "12-14", + "description": "Peak activity of the Geminids meteor shower.", + "associations": { + "planetId": "mercury", + "zodiacSignId": "gemini", + "tarotCard": "The Lovers", + "godId": "hermes" + } + } + ] +} diff --git a/data/chakras.json b/data/chakras.json new file mode 100644 index 0000000..8650f97 --- /dev/null +++ b/data/chakras.json @@ -0,0 +1,120 @@ +{ + "root": { + "id": "root", + "name": { + "en": "Root", + "sa": "मूलाधार", + "roman": "Muladhara" + }, + "meaning": { + "en": "Root" + }, + "location": "base-of-spine", + "petals": 4, + "color": "red", + "seed": "Lam", + "seedMeaning": { + "en": "earth" + } + }, + "sacral": { + "id": "sacral", + "name": { + "en": "Sacral", + "sa": "स्वाधिष्ठान", + "roman": "Svadhishthana" + }, + "meaning": { + "en": "Where the self is established" + }, + "location": "genitals", + "petals": 6, + "color": "orange", + "seed": "Vam", + "seedMeaning": { + "en": "water" + } + }, + "solar-plexus": { + "id": "solar-plexus", + "name": { + "en": "Solar Plexus", + "sa": "मणिपूर", + "roman": "Manipura" + }, + "meaning": { + "en": "Jewel city" + }, + "location": "navel", + "petals": 10, + "color": "yellow", + "seed": "Ram", + "seedMeaning": { + "en": "fire" + } + }, + "heart": { + "id": "heart", + "name": { + "en": "Heart", + "sa": "अनाहत", + "roman": "Anahata" + }, + "meaning": { + "en": "Unstruck" + }, + "location": "heart", + "petals": 12, + "color": "green", + "seed": "Yam", + "seedMeaning": { + "en": "air" + } + }, + "throat": { + "id": "throat", + "name": { + "en": "Throat", + "sa": "विशुद्ध", + "roman": "Vishuddha" + }, + "meaning": { + "en": "Purest" + }, + "location": "throat", + "petals": 16, + "color": "blue", + "seed": "Ham", + "seedMeaning": { + "en": "space" + } + }, + "third-eye": { + "id": "third-eye", + "name": { + "en": "Third Eye", + "sa": "आज्ञा", + "roman": "Ajna" + }, + "meaning": { + "en": "Command" + }, + "location": "third-eye", + "petals": 2, + "color": "indigo" + }, + "crown": { + "id": "crown", + "name": { + "en": "Crown", + "sa": "सहस्रार", + "roman": "Sahasrara" + }, + "meaning": { + "en": "Thousand-petaled" + }, + "location": "crown", + "petals": 1000, + "color": "violet" + } +} diff --git a/data/decans.json b/data/decans.json new file mode 100644 index 0000000..e780d8c --- /dev/null +++ b/data/decans.json @@ -0,0 +1,54 @@ +{ + "meta": { + "notes": "Decans reference sign IDs from signs.json to avoid duplicated sign metadata." + }, + "decans": [ + { "id": "aries-1", "signId": "aries", "index": 1, "rulerPlanetId": "mars", "tarotMinorArcana": "2 of Wands" }, + { "id": "aries-2", "signId": "aries", "index": 2, "rulerPlanetId": "sol", "tarotMinorArcana": "3 of Wands" }, + { "id": "aries-3", "signId": "aries", "index": 3, "rulerPlanetId": "venus", "tarotMinorArcana": "4 of Wands" }, + + { "id": "taurus-1", "signId": "taurus", "index": 1, "rulerPlanetId": "mercury", "tarotMinorArcana": "5 of Pentacles" }, + { "id": "taurus-2", "signId": "taurus", "index": 2, "rulerPlanetId": "luna", "tarotMinorArcana": "6 of Pentacles" }, + { "id": "taurus-3", "signId": "taurus", "index": 3, "rulerPlanetId": "saturn", "tarotMinorArcana": "7 of Pentacles" }, + + { "id": "gemini-1", "signId": "gemini", "index": 1, "rulerPlanetId": "jupiter", "tarotMinorArcana": "8 of Swords" }, + { "id": "gemini-2", "signId": "gemini", "index": 2, "rulerPlanetId": "mars", "tarotMinorArcana": "9 of Swords" }, + { "id": "gemini-3", "signId": "gemini", "index": 3, "rulerPlanetId": "sol", "tarotMinorArcana": "10 of Swords" }, + + { "id": "cancer-1", "signId": "cancer", "index": 1, "rulerPlanetId": "venus", "tarotMinorArcana": "2 of Cups" }, + { "id": "cancer-2", "signId": "cancer", "index": 2, "rulerPlanetId": "mercury", "tarotMinorArcana": "3 of Cups" }, + { "id": "cancer-3", "signId": "cancer", "index": 3, "rulerPlanetId": "luna", "tarotMinorArcana": "4 of Cups" }, + + { "id": "leo-1", "signId": "leo", "index": 1, "rulerPlanetId": "saturn", "tarotMinorArcana": "5 of Wands" }, + { "id": "leo-2", "signId": "leo", "index": 2, "rulerPlanetId": "jupiter", "tarotMinorArcana": "6 of Wands" }, + { "id": "leo-3", "signId": "leo", "index": 3, "rulerPlanetId": "mars", "tarotMinorArcana": "7 of Wands" }, + + { "id": "virgo-1", "signId": "virgo", "index": 1, "rulerPlanetId": "sol", "tarotMinorArcana": "8 of Pentacles" }, + { "id": "virgo-2", "signId": "virgo", "index": 2, "rulerPlanetId": "venus", "tarotMinorArcana": "9 of Pentacles" }, + { "id": "virgo-3", "signId": "virgo", "index": 3, "rulerPlanetId": "mercury", "tarotMinorArcana": "10 of Pentacles" }, + + { "id": "libra-1", "signId": "libra", "index": 1, "rulerPlanetId": "luna", "tarotMinorArcana": "2 of Swords" }, + { "id": "libra-2", "signId": "libra", "index": 2, "rulerPlanetId": "saturn", "tarotMinorArcana": "3 of Swords" }, + { "id": "libra-3", "signId": "libra", "index": 3, "rulerPlanetId": "jupiter", "tarotMinorArcana": "4 of Swords" }, + + { "id": "scorpio-1", "signId": "scorpio", "index": 1, "rulerPlanetId": "mars", "tarotMinorArcana": "5 of Cups" }, + { "id": "scorpio-2", "signId": "scorpio", "index": 2, "rulerPlanetId": "sol", "tarotMinorArcana": "6 of Cups" }, + { "id": "scorpio-3", "signId": "scorpio", "index": 3, "rulerPlanetId": "venus", "tarotMinorArcana": "7 of Cups" }, + + { "id": "sagittarius-1", "signId": "sagittarius", "index": 1, "rulerPlanetId": "mercury", "tarotMinorArcana": "8 of Wands" }, + { "id": "sagittarius-2", "signId": "sagittarius", "index": 2, "rulerPlanetId": "luna", "tarotMinorArcana": "9 of Wands" }, + { "id": "sagittarius-3", "signId": "sagittarius", "index": 3, "rulerPlanetId": "saturn", "tarotMinorArcana": "10 of Wands" }, + + { "id": "capricorn-1", "signId": "capricorn", "index": 1, "rulerPlanetId": "jupiter", "tarotMinorArcana": "2 of Pentacles" }, + { "id": "capricorn-2", "signId": "capricorn", "index": 2, "rulerPlanetId": "mars", "tarotMinorArcana": "3 of Pentacles" }, + { "id": "capricorn-3", "signId": "capricorn", "index": 3, "rulerPlanetId": "sol", "tarotMinorArcana": "4 of Pentacles" }, + + { "id": "aquarius-1", "signId": "aquarius", "index": 1, "rulerPlanetId": "venus", "tarotMinorArcana": "5 of Swords" }, + { "id": "aquarius-2", "signId": "aquarius", "index": 2, "rulerPlanetId": "mercury", "tarotMinorArcana": "6 of Swords" }, + { "id": "aquarius-3", "signId": "aquarius", "index": 3, "rulerPlanetId": "luna", "tarotMinorArcana": "7 of Swords" }, + + { "id": "pisces-1", "signId": "pisces", "index": 1, "rulerPlanetId": "saturn", "tarotMinorArcana": "8 of Cups" }, + { "id": "pisces-2", "signId": "pisces", "index": 2, "rulerPlanetId": "jupiter", "tarotMinorArcana": "9 of Cups" }, + { "id": "pisces-3", "signId": "pisces", "index": 3, "rulerPlanetId": "mars", "tarotMinorArcana": "10 of Cups" } + ] +} \ No newline at end of file diff --git a/data/enochian/dictionary.json b/data/enochian/dictionary.json new file mode 100644 index 0000000..9c05a1c --- /dev/null +++ b/data/enochian/dictionary.json @@ -0,0 +1,22640 @@ +{ + "A": { + "gematria": [ + 6 + ], + "meanings": [ + { + "meaning": "I, my", + "source": "EMPM" + }, + { + "meaning": "Un (A)", + "source": "WE" + }, + { + "meaning": "in, with", + "source": "WE", + "source2": "Holy Table of Practice perimeter" + } + ], + "pronounciations": [ + { + "pronounciation": "ah", + "source": "EMPM" + } + ] + }, + "A-": { + "gematria": [], + "meanings": [ + { + "meaning": "with-", + "source": "WE" + } + ], + "pronounciations": [] + }, + "A-BABALOND": { + "gematria": [], + "meanings": [ + { + "meaning": "harlot, (of an)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "A-BAI": { + "gematria": [], + "meanings": [ + { + "meaning": "stooping,(to the); attacking", + "source": "WE" + } + ], + "pronounciations": [] + }, + "A-C-LONDOH": { + "gematria": [], + "meanings": [ + { + "meaning": "kingdom, in thy kingdom", + "source": "WE" + } + ], + "pronounciations": [] + }, + "A-CROODZI": { + "gematria": [], + "meanings": [ + { + "meaning": "beginning, thy beginning", + "source": "WE" + } + ], + "pronounciations": [] + }, + "A-DEUNE": { + "gematria": [], + "meanings": [ + { + "meaning": "across", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "A-SOBAM": { + "gematria": [], + "meanings": [ + { + "meaning": "whom, (on)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AABCO": { + "gematria": [], + "meanings": [ + { + "meaning": "SEPHIROTIC CROSS AIR OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AAETPIO": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior MARS of FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AAI": { + "gematria": [ + 72 + ], + "meanings": [ + { + "meaning": "within you", + "source": "EMPM" + }, + { + "meaning": "among, among you", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "ah-ahee", + "source": "EMPM" + } + ] + }, + "AAIOM": { + "gematria": [], + "meanings": [ + { + "meaning": "among us", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AALA": { + "gematria": [], + "meanings": [ + { + "meaning": "placed you", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AANAA": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Archangel WATER OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AAO": { + "gematria": [], + "meanings": [ + { + "meaning": "AMONG", + "source": "WE" + }, + { + "meaning": "AMONG (alt.sp.)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AAOZAIF": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior JUPITER of AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AAPDOCE": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior VENUS of FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AB": { + "gematria": [], + "meanings": [ + { + "meaning": "DAUGHTER OF DAUGHTER OF LIGHT", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ABAI": { + "gematria": [ + 77 + ], + "meanings": [ + { + "meaning": "stooping, to stoop down", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "ah-ba-ee", + "source": "EMPM" + } + ] + }, + "ABALPT": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross EARTH OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ABAMO": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ABARAMIG": { + "gematria": [], + "meanings": [ + { + "meaning": "PREPARE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ABIORO": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior MARS IN AIR TABLET (VAR)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ABMO": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ABOAPRI": { + "gematria": [], + "meanings": [ + { + "meaning": "SERVE, LET THEM SERVE YOU", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ABOZA": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Archangel AIR OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ABRAASSA": { + "gematria": [ + 143 + ], + "meanings": [ + { + "meaning": "to provide", + "source": "EMPM" + }, + { + "meaning": "provided", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "ah-beh-rah-ah-ess-sah", + "source": "EMPM" + } + ] + }, + "ABRAMG": { + "gematria": [], + "meanings": [ + { + "meaning": "prepared, i have prepared", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ABRAMIG": { + "gematria": [], + "meanings": [ + { + "meaning": "prepared, are prepared", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ABRIOND": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN POP", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ACAM": { + "gematria": [], + "meanings": [ + { + "meaning": 7699, + "source": "WE" + } + ], + "pronounciations": [] + }, + "ACAR": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ACCA": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ACHAPH": { + "gematria": [], + "meanings": [ + { + "meaning": "Augoeides", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ACHOS": { + "gematria": [], + "meanings": [ + { + "meaning": "12 Guardian Angels", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ACO": { + "gematria": [], + "meanings": [ + { + "meaning": "of the holy pentagram", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ACRAR": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ACUCA": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ACURTOH": { + "gematria": [], + "meanings": [ + { + "meaning": "God is triumphant", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ACZINOR": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior JUP of EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AD": { + "gematria": [], + "meanings": [ + { + "meaning": "in the third, with the third", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ADAMH": { + "gematria": [], + "meanings": [ + { + "meaning": "with hosts of the Lord (stars)", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ADAO": { + "gematria": [], + "meanings": [ + { + "meaning": "in [or] with the third star", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ADAPH": { + "gematria": [], + "meanings": [ + { + "meaning": "Unto (or From) the Lord of Hosts", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ADE": { + "gematria": [], + "meanings": [ + { + "meaning": "in the third is the Daughter of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ADEPH": { + "gematria": [], + "meanings": [ + { + "meaning": "Unto (or From) the Lord of Hosts", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ADEPOAD": { + "gematria": [], + "meanings": [ + { + "meaning": "Within the 3 rd Heaven", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ADGMACH": { + "gematria": [], + "meanings": [ + { + "meaning": "much glory", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ADGT": { + "gematria": [], + "meanings": [ + { + "meaning": "can", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ADIN": { + "gematria": [], + "meanings": [ + { + "meaning": "the Sun of God from the divine", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ADIPR": { + "gematria": [], + "meanings": [ + { + "meaning": "the Sun of God from the 3 rd", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ADIRE": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ADLPH": { + "gematria": [], + "meanings": [ + { + "meaning": "among the first to give", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ADMA": { + "gematria": [], + "meanings": [ + { + "meaning": "possess the third", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ADMO": { + "gematria": [], + "meanings": [ + { + "meaning": "God is man", + "source": "WE", + "source2": "Mistake", + "note": "This word was found accidentally by misconstruing the word DAMO to ADMO. It is not found in Liber Loagaeth." + } + ], + "pronounciations": [] + }, + "ADNA": { + "gematria": [ + 66 + ], + "meanings": [ + { + "meaning": "obedience", + "source": "EMPM" + }, + { + "meaning": "obedience", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "ah-deh-nah", + "source": "EMPM" + } + ] + }, + "ADOEOET": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior JUPITER of FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ADOIAN": { + "gematria": [], + "meanings": [ + { + "meaning": "face, the face", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ADON": { + "gematria": [], + "meanings": [ + { + "meaning": "the face (of God)", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ADOPA": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Archangel AIR OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ADPHAHT": { + "gematria": [ + 30, + 36 + ], + "meanings": [ + { + "meaning": "unspeakable", + "source": "EMPM" + }, + { + "meaning": "unspeakable", + "source": "EMPM" + }, + { + "meaning": "unspeakable", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "ah-deh-peh-ah-teh", + "source": "EMPM" + }, + { + "pronounciation": "ah-deh-peh-ah-teh", + "source": "EMPM" + } + ] + }, + "ADPUN": { + "gematria": [], + "meanings": [ + { + "meaning": "With strong fire", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ADRA": { + "gematria": [], + "meanings": [ + { + "meaning": "involutes", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ADRAMAN": { + "gematria": [], + "meanings": [ + { + "meaning": "NAME OF AN EVIL SPIRIT", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ADRE": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ADROCH": { + "gematria": [], + "meanings": [ + { + "meaning": "mount, in the olive mount", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ADRPAN": { + "gematria": [], + "meanings": [ + { + "meaning": "cast down", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ADRPHT": { + "gematria": [], + "meanings": [ + { + "meaning": "casting down (crowley)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ADVORPT": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN TEX", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AETPIO": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior MARS of FIRE TABLET", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AF": { + "gematria": [], + "meanings": [ + { + "meaning": 19, + "source": "WE" + } + ], + "pronounciations": [] + }, + "AFFA": { + "gematria": [ + 18 + ], + "meanings": [ + { + "meaning": "empty", + "source": "EMPM" + }, + { + "meaning": "empty", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "ah-ef-fah", + "source": "EMPM" + } + ] + }, + "AFLAFBEN": { + "gematria": [], + "meanings": [ + { + "meaning": "DEE'S GOOD ANGEL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AG": { + "gematria": [ + 14 + ], + "meanings": [ + { + "meaning": "no", + "source": "EMPM" + }, + { + "meaning": "no, none", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "ah-geh", + "source": "EMPM" + } + ] + }, + "AGAN": { + "gematria": [], + "meanings": [ + { + "meaning": "not the Son of Son of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "AGEFF": { + "gematria": [], + "meanings": [ + { + "meaning": "the Trinity (3) manifests", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "AGEMATOM": { + "gematria": [], + "meanings": [ + { + "meaning": "the Trinity (3) echoes from the 4 th", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "AGES": { + "gematria": [], + "meanings": [ + { + "meaning": "not the 4th", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "AGGS": { + "gematria": [], + "meanings": [ + { + "meaning": "the Magus", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "AGIOD": { + "gematria": [], + "meanings": [ + { + "meaning": "mortal", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "AGO": { + "gematria": [], + "meanings": [ + { + "meaning": "not the fifth", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "AH": { + "gematria": [], + "meanings": [ + { + "meaning": "inner/higher self", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "AHAH": { + "gematria": [], + "meanings": [ + { + "meaning": "inmost God", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "AHAOZPI": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior VENUS of AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AHMLICV": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior MER of EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AHO": { + "gematria": [], + "meanings": [ + { + "meaning": "in sacred measure", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "AIAOAI": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross EARTH OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AIDROM": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior MARS of EARTH TABLET", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AIDROPL": { + "gematria": [], + "meanings": [ + { + "meaning": "governor", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AIGRA": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AIRA": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AISRO": { + "gematria": [], + "meanings": [ + { + "meaning": "PROMISE, THE PROMISE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AKELE": { + "gematria": [], + "meanings": [ + { + "meaning": "(name of an angel)", + "source": "EMPM", + "source2": "pg 25" + }, + { + "meaning": "DAUGHTER OF LIGHT", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "Ah-keh-leh", + "source": "EMPM", + "source2": "pg 25" + } + ] + }, + "ALA": { + "gematria": [], + "meanings": [ + { + "meaning": "place", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ALAR": { + "gematria": [], + "meanings": [ + { + "meaning": "settled, have settled", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ALCA": { + "gematria": [], + "meanings": [ + { + "meaning": "judgment (?)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ALDARAIA": { + "gematria": [], + "meanings": [ + { + "meaning": "will of God (?)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ALDI": { + "gematria": [], + "meanings": [ + { + "meaning": "gathering, of gathering", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ALDON": { + "gematria": [], + "meanings": [ + { + "meaning": "gathered together (they)", + "source": "WE" + }, + { + "meaning": "gird up", + "source": "WE" + }, + { + "meaning": "gather up", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ALGLA": { + "gematria": [], + "meanings": [ + { + "meaning": "invoke the One", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ALHCTGA": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior VEN of EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ALIDA": { + "gematria": [], + "meanings": [ + { + "meaning": "one in name with", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ALLA": { + "gematria": [], + "meanings": [ + { + "meaning": "ALLA (a name of God; the naming of God’s will)", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ALLAR": { + "gematria": [], + "meanings": [ + { + "meaning": "bind up", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ALNDVOD": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior LUNA of FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ALOAI": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross FIRE OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ALPOD": { + "gematria": [], + "meanings": [ + { + "meaning": "infinite", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ALPUDUS": { + "gematria": [], + "meanings": [ + { + "meaning": "Zodiacal King CANCER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ALSPLAN": { + "gematria": [], + "meanings": [ + { + "meaning": "among the angels", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "AMBRIOL": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN LOE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AMCHIH": { + "gematria": [], + "meanings": [ + { + "meaning": "the Son of Light is with the 9", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "AMGEDPHA": { + "gematria": [], + "meanings": [ + { + "meaning": "I begin anew", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AMIDAN": { + "gematria": [], + "meanings": [ + { + "meaning": "fixed to the Son of Son of Light-Mercury", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "AMIPZI": { + "gematria": [], + "meanings": [ + { + "meaning": "fastened, I fastened", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AMIRAN": { + "gematria": [], + "meanings": [ + { + "meaning": "yourselves", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AMLOX": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AMMA": { + "gematria": [ + 192 + ], + "meanings": [ + { + "meaning": "cursed", + "source": "EMPM" + }, + { + "meaning": "cursed", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "ah-em-mah", + "source": "EMPM" + } + ] + }, + "AMOX": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AMPHAN": { + "gematria": [], + "meanings": [ + { + "meaning": "bound by the Son of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "AMRVH": { + "gematria": [], + "meanings": [ + { + "meaning": "The power and presence of the Lord of Hosts in the angel of the East", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "AMUDAS": { + "gematria": [], + "meanings": [ + { + "meaning": "wherefore ye are cursed", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "AN": { + "gematria": [], + "meanings": [ + { + "meaning": "SON OF SON OF LIGHT, MERCURY", + "source": "WE", + "source2": "Holy Table of Practice perimeter" + } + ], + "pronounciations": [] + }, + "ANAA": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Angel WATER OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ANAEEM": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross WATER OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ANANAEL": { + "gematria": [ + 136 + ], + "meanings": [ + { + "meaning": "the Secret Wisdom", + "source": "EMPM" + }, + { + "meaning": "wisdom, of the secret wisdom", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "ah-nah-nah-el", + "source": "EMPM" + } + ] + }, + "ANBPHO": { + "gematria": [], + "meanings": [ + { + "meaning": "the Son of Son of Light (Mercury) gives the holy pentagram.", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ANDISPI": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ZOM", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ANETAB": { + "gematria": [], + "meanings": [ + { + "meaning": "government, in government", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ANGE": { + "gematria": [], + "meanings": [ + { + "meaning": "within the thought [of God]", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ANGELARD": { + "gematria": [], + "meanings": [ + { + "meaning": "thoughts, his thoughts", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ANGPOI": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross AIR OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ANODOIN": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior MERCURY of FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ANOLPHE": { + "gematria": [], + "meanings": [ + { + "meaning": "the Son of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ANS": { + "gematria": [], + "meanings": [ + { + "meaning": "the Son of Son of Light is the holy pentagram", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "AOAYNNL": { + "gematria": [], + "meanings": [ + { + "meaning": "1ST MINISTER OF VENUS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AOIDIAB": { + "gematria": [], + "meanings": [ + { + "meaning": "6TH MINISTER OF SOL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AOIVEAE": { + "gematria": [], + "meanings": [ + { + "meaning": "stars, the stars", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AOURRZ": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross FIRE OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AOZPI": { + "gematria": [], + "meanings": [ + { + "meaning": "A GOD-NAME OF AIR TABLET", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AP": { + "gematria": [ + 15 + ], + "meanings": [ + { + "meaning": "unchanging, same", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "ah-peh", + "source": "EMPM" + } + ] + }, + "APACHANA": { + "gematria": [], + "meanings": [ + { + "meaning": "SLIMY THINGS MADE OF DUST", + "source": "WE" + } + ], + "pronounciations": [] + }, + "APDOCE": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior VENUS of FIRE TABLET", + "source": "WE" + } + ], + "pronounciations": [] + }, + "APHLAFBEN": { + "gematria": [], + "meanings": [ + { + "meaning": "DEE'S GOOD ANGEL (alt. sp.)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "APHRA": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Archangel WATER OF", + "source": "WE" + } + ], + "pronounciations": [] + }, + "APILA": { + "gematria": [ + 89 + ], + "meanings": [ + { + "meaning": "eternal life, to live forever", + "source": "EMPM" + }, + { + "meaning": "liveth", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "ah-pee-lah", + "source": "EMPM" + } + ] + }, + "APLST": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "APOPHRASZ": { + "gematria": [ + 171, + 177 + ], + "meanings": [ + { + "meaning": "motion", + "source": "EMPM" + }, + { + "meaning": "motion", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "ah-poh-peh-rah-seh-zod", + "source": "EMPM" + }, + { + "pronounciation": "ah-poh-peh-rah-seh-zod", + "source": "EMPM" + } + ] + }, + "APST": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AR": { + "gematria": [ + 106 + ], + "meanings": [ + { + "meaning": "the sun, to prtect", + "source": "EMPM" + }, + { + "meaning": "that", + "source": "WE" + }, + { + "meaning": "to fan or winnow", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "are", + "source": "EMPM" + } + ] + }, + "ARBIZ": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross EARTH OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ARCHAD": { + "gematria": [], + "meanings": [ + { + "meaning": "spread amongst the third", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ARCHADS": { + "gematria": [], + "meanings": [ + { + "meaning": "spread amongst the third is the Daughter of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ARCHAS": { + "gematria": [], + "meanings": [ + { + "meaning": "the Daughter of Light is spread amongst the third", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ARDEN": { + "gematria": [], + "meanings": [ + { + "meaning": "The Universal Mind", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ARDOX": { + "gematria": [], + "meanings": [ + { + "meaning": "fire of dissolution", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ARDZA": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross AIR OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ARFAOLG": { + "gematria": [], + "meanings": [ + { + "meaning": "Zodiacal King TAURUS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ARGEDCO": { + "gematria": [], + "meanings": [ + { + "meaning": "invoke (?)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ARGRASPHE": { + "gematria": [], + "meanings": [ + { + "meaning": "the Daughter of Daughter of Light becomes Queen of the Moon", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ARINNAP": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior SATURN of FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ARISSO": { + "gematria": [], + "meanings": [ + { + "meaning": "the mystical marriage", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ARN": { + "gematria": [], + "meanings": [ + { + "meaning": "SECOND AETHYR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ARNI": { + "gematria": [], + "meanings": [ + { + "meaning": "the Beast", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ARP": { + "gematria": [], + "meanings": [ + { + "meaning": "conquer (?)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ARPHE": { + "gematria": [], + "meanings": [ + { + "meaning": "descend", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ARSETH": { + "gematria": [], + "meanings": [ + { + "meaning": "wailing in their places", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ARSL": { + "gematria": [], + "meanings": [ + { + "meaning": "A GOD-NAME OF WATER TABLET", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ARTH": { + "gematria": [], + "meanings": [ + { + "meaning": "gladness, of gladness", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ARVIN": { + "gematria": [], + "meanings": [ + { + "meaning": "God’s glory spread out", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ARXE": { + "gematria": [], + "meanings": [ + { + "meaning": "for the Daughter of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ARZULGE": { + "gematria": [], + "meanings": [ + { + "meaning": "NAME OF EVIL SPIRIT", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AS": { + "gematria": [], + "meanings": [ + { + "meaning": "was", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ASCHA": { + "gematria": [], + "meanings": [ + { + "meaning": "God", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ASCHEDH": { + "gematria": [], + "meanings": [ + { + "meaning": "God receives", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ASCHIN": { + "gematria": [], + "meanings": [ + { + "meaning": "the divine will of the holy Son of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ASCLAD": { + "gematria": [], + "meanings": [ + { + "meaning": "Lucifer was the third", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ASCLEH": { + "gematria": [], + "meanings": [ + { + "meaning": "divine will", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ASCO": { + "gematria": [], + "meanings": [ + { + "meaning": "this God", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ASEV": { + "gematria": [], + "meanings": [ + { + "meaning": "Reflected, ‘was reflected’", + "source": "WE", + "source2": "Lamen" + } + ], + "pronounciations": [] + }, + "ASMT": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Angel FIRE OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ASP": { + "gematria": [], + "meanings": [ + { + "meaning": "21ST AETHYR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ASPAH": { + "gematria": [], + "meanings": [ + { + "meaning": "the infinity within", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ASPIAN": { + "gematria": [], + "meanings": [ + { + "meaning": "QUALITIES, IN THEIR QUALITIES", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ASPIAON": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN DEO", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ASPT": { + "gematria": [ + 25, + 31 + ], + "meanings": [ + { + "meaning": "before, in front of", + "source": "EMPM" + }, + { + "meaning": "before, in front of", + "source": "EMPM" + }, + { + "meaning": "before", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "ah-seh-peh-teh", + "source": "EMPM" + }, + { + "pronounciation": "ah-seh-peh-teh", + "source": "EMPM" + } + ] + }, + "ASTEL": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ASTO": { + "gematria": [], + "meanings": [ + { + "meaning": "was also this", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ASTRAPHOS": { + "gematria": [], + "meanings": [ + { + "meaning": "(was) reflected in the East on the ecliptic", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ASYMP": { + "gematria": [], + "meanings": [ + { + "meaning": "another, with another", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ATAPA": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross FIRE OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ATH": { + "gematria": [ + 10, + 16 + ], + "meanings": [ + { + "meaning": "works", + "source": "EMPM" + }, + { + "meaning": "works", + "source": "EMPM" + }, + { + "meaning": "DAUGHTER OF DAUGHTER OF LIGHT", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "ah-teh", + "source": "EMPM" + }, + { + "pronounciation": "ah-teh", + "source": "EMPM" + } + ] + }, + "ATRAAH": { + "gematria": [], + "meanings": [ + { + "meaning": "girdles, your girdles", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AU": { + "gematria": [], + "meanings": [ + { + "meaning": "Shortened name of Ave, Son of Son of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "AUDCAL": { + "gematria": [], + "meanings": [ + { + "meaning": "gold, philosophical mercury", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AUDROPL": { + "gematria": [], + "meanings": [ + { + "meaning": "governor", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AVABH": { + "gematria": [], + "meanings": [ + { + "meaning": "hiacynth, of hiacynth", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AVAVAGO": { + "gematria": [], + "meanings": [ + { + "meaning": "thunders of increase", + "source": "WE" + }, + { + "meaning": "thunders, the thunders", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AVAVOX": { + "gematria": [], + "meanings": [ + { + "meaning": "pomp, his pomp", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AVDROPT": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN TAN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AVE": { + "gematria": [], + "meanings": [ + { + "meaning": "SON OF SON OF LIGHT, SOL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AVINY": { + "gematria": [], + "meanings": [ + { + "meaning": "millstones", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AVTOTAR": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior MERCURY of AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AVZNILN": { + "gematria": [], + "meanings": [ + { + "meaning": "2ND MINISTER OF LUNA", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AX": { + "gematria": [], + "meanings": [ + { + "meaning": "surround", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "AXA": { + "gematria": [], + "meanings": [ + { + "meaning": "surround the one", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "AXE": { + "gematria": [], + "meanings": [ + { + "meaning": "Surrounds the Daughter of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "AXIR": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AXO": { + "gematria": [], + "meanings": [ + { + "meaning": "microcosm", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "AXOL": { + "gematria": [], + "meanings": [ + { + "meaning": "the glory of God’s creation", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "AXTIR": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AXZIARG": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN PAZ", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AZDOBN": { + "gematria": [], + "meanings": [ + { + "meaning": "(name of an angel)", + "source": "EMPM", + "source2": "pg 25" + }, + { + "meaning": "DAUGHTER OF LIGHT", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "Ah-zod-doh-ben", + "source": "EMPM", + "source2": "pg 25" + } + ] + }, + "AZIAGIER": { + "gematria": [], + "meanings": [ + { + "meaning": "harvest, like unto the harvest", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AZIAZIOR": { + "gematria": [], + "meanings": [ + { + "meaning": "likeness, in the likeness", + "source": "WE" + } + ], + "pronounciations": [] + }, + "AZIEN": { + "gematria": [], + "meanings": [ + { + "meaning": "hands, on whose hands", + "source": "WE" + } + ], + "pronounciations": [] + }, + "B": { + "gematria": [], + "meanings": [ + { + "meaning": "Pa (B)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BAB": { + "gematria": [], + "meanings": [ + { + "meaning": "power, ability, possibility", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BABAGE": { + "gematria": [ + 40 + ], + "meanings": [ + { + "meaning": "South", + "source": "EMPM" + }, + { + "meaning": "south, in the south", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "bah-bah-geh", + "source": "EMPM" + } + ] + }, + "BABAGEN": { + "gematria": [], + "meanings": [ + { + "meaning": "south, of the south", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BABALEL": { + "gematria": [], + "meanings": [ + { + "meaning": "angel of mars in mars, king", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BABALON": { + "gematria": [ + 110 + ], + "meanings": [ + { + "meaning": "evil, wicked", + "source": "EMPM" + }, + { + "meaning": "wicked, the wicked", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "bah-bah-loh-en", + "source": "EMPM" + } + ] + }, + "BABALOND": { + "gematria": [ + 114 + ], + "meanings": [ + { + "meaning": "harlot, seductress", + "source": "EMPM" + }, + { + "meaning": "harlot, a", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "bah-bah-loh-en-deh", + "source": "EMPM" + } + ] + }, + "BABALUN": { + "gematria": [], + "meanings": [ + { + "meaning": "BABALON", + "source": "WE", + "source2": "Holy Table of Practice perimeter" + } + ], + "pronounciations": [] + }, + "BABAPON": { + "gematria": [], + "meanings": [ + { + "meaning": "6TH MINISTER OF BRORGES", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BABEPEN": { + "gematria": [], + "meanings": [ + { + "meaning": "6TH MINISTER OF SATURN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BABLIBO": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL OF SOL IN LUNA", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BAEOVIB": { + "gematria": [], + "meanings": [ + { + "meaning": "righteousness", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BAEQUIB": { + "gematria": [ + 186 + ], + "meanings": [ + { + "meaning": "righteousness", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "bah-eh-oh-u-ee-beh", + "source": "EMPM" + } + ] + }, + "BAG": { + "gematria": [], + "meanings": [ + { + "meaning": "28TH AETHYR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BAGENOL": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL VENUS IN LUNA, PRINCE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BAGHIE": { + "gematria": [ + 90 + ], + "meanings": [ + { + "meaning": "fury", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "bah-geh-hee-eh", + "source": "EMPM" + } + ] + }, + "BAGIE": { + "gematria": [], + "meanings": [ + { + "meaning": "fury, of fury", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BAGLE": { + "gematria": [], + "meanings": [ + { + "meaning": "for", + "source": "WE" + }, + { + "meaning": "for why?", + "source": "WE" + }, + { + "meaning": "because", + "source": "WE" + }, + { + "meaning": "why?", + "source": "WE" + }, + { + "meaning": "for", + "source": "WE" + }, + { + "meaning": "why?", + "source": "WE" + }, + { + "meaning": "because", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BAGLEN": { + "gematria": [], + "meanings": [ + { + "meaning": "because", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BAGNOLE": { + "gematria": [], + "meanings": [ + { + "meaning": "angel venus in sol", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BAHAL": { + "gematria": [ + 26 + ], + "meanings": [ + { + "meaning": "to shout, to yell, to cry", + "source": "EMPM" + }, + { + "meaning": "cry aloud", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "bah-hall", + "source": "EMPM" + } + ] + }, + "BAI": { + "gematria": [], + "meanings": [ + { + "meaning": "stooping, soaring down", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BALCEOR": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL SATURN IN JUPITER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BALDAGO": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL JUPITER IN MERCURY", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BALIE": { + "gematria": [ + 89 + ], + "meanings": [ + { + "meaning": "salt", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "bah-lee-eh", + "source": "EMPM" + } + ] + }, + "BALIGON": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL VENUS IN VENUS, KING", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BALIT": { + "gematria": [ + 82, + 88 + ], + "meanings": [ + { + "meaning": "the just", + "source": "EMPM" + }, + { + "meaning": "the just", + "source": "EMPM" + }, + { + "meaning": "justice, the just", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "bah-lee-teh", + "source": "EMPM" + }, + { + "pronounciation": "bah-lee-teh", + "source": "EMPM" + } + ] + }, + "BALT": { + "gematria": [ + 22, + 28 + ], + "meanings": [ + { + "meaning": "justice", + "source": "EMPM" + }, + { + "meaning": "justice", + "source": "EMPM" + }, + { + "meaning": "justice", + "source": "WE" + }, + { + "meaning": "justice, of justice", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "ball-teh", + "source": "EMPM" + }, + { + "pronounciation": "ball-teh", + "source": "EMPM" + } + ] + }, + "BALTAN": { + "gematria": [], + "meanings": [ + { + "meaning": "justice, in his justice", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BALTIM": { + "gematria": [], + "meanings": [ + { + "meaning": "justice, fury or extreme justi", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BALTOH": { + "gematria": [ + 53, + 59 + ], + "meanings": [ + { + "meaning": "righteous", + "source": "EMPM" + }, + { + "meaning": "righteous", + "source": "EMPM" + }, + { + "meaning": "righteousness, of righteousnes", + "source": "WE" + }, + { + "meaning": "righteousness, of", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "ball-toh", + "source": "EMPM" + }, + { + "pronounciation": "ball-toh", + "source": "EMPM" + } + ] + }, + "BALTOHA": { + "gematria": [], + "meanings": [ + { + "meaning": "righteousness, for my own", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BALYE": { + "gematria": [], + "meanings": [ + { + "meaning": "salt, of salt", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BALZARG": { + "gematria": [ + 136, + 142 + ], + "meanings": [ + { + "meaning": "stewards", + "source": "EMPM" + }, + { + "meaning": "stewards", + "source": "EMPM" + }, + { + "meaning": "stewards", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "bal-zodah-rah-geh", + "source": "EMPM" + }, + { + "pronounciation": "bal-zodah-rah-geh", + "source": "EMPM" + } + ] + }, + "BALZIZRAS": { + "gematria": [ + 198, + 210 + ], + "meanings": [ + { + "meaning": "judgement", + "source": "EMPM" + }, + { + "meaning": "judgement", + "source": "EMPM" + }, + { + "meaning": "judgement, the", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "bal-zodee-zod-rass", + "source": "EMPM" + }, + { + "pronounciation": "bal-zodee-zod-rass", + "source": "EMPM" + } + ] + }, + "BAM": { + "gematria": [], + "meanings": [ + { + "meaning": "forgotten (schuler)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BAMASAN": { + "gematria": [], + "meanings": [ + { + "meaning": "NAME OF A GUARDIAN ANGEL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BAMNODE": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL SATURN IN LUNA", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BAMS": { + "gematria": [ + 108 + ], + "meanings": [ + { + "meaning": "to forget", + "source": "EMPM" + }, + { + "meaning": "forget, let them forgetBANAA Kerubic Archangel WATER OF FIRE", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "bah-mess", + "source": "EMPM" + } + ] + }, + "BANSSZE": { + "gematria": [], + "meanings": [ + { + "meaning": "1ST MINISTER OF SATURN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BANZES": { + "gematria": [], + "meanings": [ + { + "meaning": "generation", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "BAPNIDO": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL OF MARS IN VENUS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BAR": { + "gematria": [], + "meanings": [ + { + "meaning": "prince", + "source": "WE", + "source2": "Holy Table of Practice perimeter" + } + ], + "pronounciations": [] + }, + "BARCES": { + "gematria": [], + "meanings": [ + { + "meaning": "NAME OF HAGONEL'S SEAL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BARFORT": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL MERCURY IN LUNA", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BARIGES": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL OF SOL IN MERCURY", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BARMA": { + "gematria": [], + "meanings": [ + { + "meaning": "NAME OF A DEMON", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BARMAN": { + "gematria": [], + "meanings": [ + { + "meaning": "NAME OF A DEMON", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BARNAFA": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL OF SOL IN JUPITER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BARTIRO": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL JUPITER IN SATURN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BASGIM": { + "gematria": [ + 176 + ], + "meanings": [ + { + "meaning": "day", + "source": "EMPM" + }, + { + "meaning": "day", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "bah-seh-gee-em", + "source": "EMPM" + } + ] + }, + "BASLEDF": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL LUNA IN VENUS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BASM": { + "gematria": [ + 110 + ], + "meanings": [ + { + "meaning": "noon, midday", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "bah-zod-em", + "source": "EMPM" + } + ] + }, + "BASMELO": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL JUPITER IN SOL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BASP": { + "gematria": [], + "meanings": [ + { + "meaning": "substantial", + "source": "WE" + }, + { + "meaning": "substantial", + "source": "WE", + "source2": "Holy Table of Practice perimeter" + } + ], + "pronounciations": [] + }, + "BASPALO": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL LUNA IN MERCURY", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BATAIVA": { + "gematria": [], + "meanings": [ + { + "meaning": "KING OF AIR TABLET (VAR)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BATAIVAH": { + "gematria": [], + "meanings": [ + { + "meaning": "KING OF AIR TABLET", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BATAIVH": { + "gematria": [], + "meanings": [ + { + "meaning": "KING OF AIR TABLET (VAR)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BAZCHIM": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN DES", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BAZM": { + "gematria": [ + 104 + ], + "meanings": [ + { + "meaning": "noon, midday", + "source": "EMPM" + }, + { + "meaning": "midday, noon", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "bah-zod-em", + "source": "EMPM" + } + ] + }, + "BAZME": { + "gematria": [], + "meanings": [ + { + "meaning": "midday, the", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BAZPAMA": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL MERCURY IN MARS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BBAIGAO": { + "gematria": [], + "meanings": [ + { + "meaning": "2ND MINISTER OF JUPITER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BBALPAE": { + "gematria": [], + "meanings": [ + { + "meaning": "3RD MINISTER OF JUPITER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BBANIFG": { + "gematria": [], + "meanings": [ + { + "meaning": "4TH MINISTER OF JUPITER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BBARNFL": { + "gematria": [], + "meanings": [ + { + "meaning": "1ST MINISTER OF JUPITER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BBASNOD": { + "gematria": [], + "meanings": [ + { + "meaning": "6TH MINISTER OF JUPITER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BBOSNIA": { + "gematria": [], + "meanings": [ + { + "meaning": "5TH MINISTER OF JUPITER", + "source": "WE" + }, + { + "meaning": "5TH MINISTER OF JUPITER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BDOPA": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Archangel AIR OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BEFAFES": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL OF MARS IN SOL,PRINCE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BEFES": { + "gematria": [], + "meanings": [ + { + "meaning": "VOCATIVE CASE OF BEFAFES", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BEIGIA": { + "gematria": [], + "meanings": [ + { + "meaning": "SON OF LIGHT, MERCURY OR SATUR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BELMAGEL": { + "gematria": [], + "meanings": [ + { + "meaning": "KELLY'S EVIL ANGEL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BELMARA": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL LUNA IN JUPITER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BEN": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL WHO APPEARED TO D. & K.", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BENPAGI": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL VENUS IN JUPITER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BERIANU": { + "gematria": [], + "meanings": [ + { + "meaning": "unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BERMALE": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL VENUS IN MARS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BERNOLE": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL MERCURY IN SOL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BESGEME": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL JUPITER IN VENUS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BESZ": { + "gematria": [ + 25, + 31 + ], + "meanings": [ + { + "meaning": "matter", + "source": "EMPM" + }, + { + "meaning": "matter", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "bess-zod", + "source": "EMPM" + }, + { + "pronounciation": "bess-zod", + "source": "EMPM" + } + ] + }, + "BEVEGJAH": { + "gematria": [], + "meanings": [ + { + "meaning": "Coagula; gathering all, gathering the ALL", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "BI": { + "gematria": [ + 65 + ], + "meanings": [ + { + "meaning": "voice", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "bee", + "source": "EMPM" + } + ] + }, + "BIA": { + "gematria": [ + 71 + ], + "meanings": [ + { + "meaning": "voices", + "source": "EMPM" + }, + { + "meaning": "voices, yourBIAB stand", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "bee-ah", + "source": "EMPM" + } + ] + }, + "BIAL": { + "gematria": [], + "meanings": [ + { + "meaning": "VOICE, THE VOICE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BIEN": { + "gematria": [], + "meanings": [ + { + "meaning": "VOICE, MY VOICE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BIGLIAD": { + "gematria": [], + "meanings": [ + { + "meaning": "comforter, in our", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BINODAB": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL VENUS IN MERCURY", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BINOFON": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL OF MARS IN MERCURY", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BITOM": { + "gematria": [ + 188, + 194 + ], + "meanings": [ + { + "meaning": "fire", + "source": "EMPM" + }, + { + "meaning": "fire", + "source": "EMPM" + }, + { + "meaning": "FIRE NAME, TABLET OF UNION", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "bee-toh-em", + "source": "EMPM" + }, + { + "pronounciation": "bee-toh-em", + "source": "EMPM" + } + ] + }, + "BLANS": { + "gematria": [], + "meanings": [ + { + "meaning": "harbored, are", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BLBOPOO": { + "gematria": [], + "meanings": [ + { + "meaning": "5TH MINISTER OF SATURN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BLIAR": { + "gematria": [ + 179 + ], + "meanings": [ + { + "meaning": "comfort, ease", + "source": "EMPM" + }, + { + "meaning": "comfort, var. of \"blior\"", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "beh-lee-ah-rah", + "source": "EMPM" + } + ] + }, + "BLIARD": { + "gematria": [ + 183 + ], + "meanings": [ + { + "meaning": "to be with comfort", + "source": "EMPM" + }, + { + "meaning": "comfort, with", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "beh-lee-ah-rah-deh", + "source": "EMPM" + } + ] + }, + "BLIIGAN": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL MERCURY IN SATURN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BLINGEF": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL JUPITER IN LUNA", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BLINTOM": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL SATURN IN MARS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BLIOR": { + "gematria": [], + "meanings": [ + { + "meaning": "comfort, continual comforters", + "source": "WE" + }, + { + "meaning": "comfort", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BLIORAX": { + "gematria": [], + "meanings": [ + { + "meaning": "comfort, shall comfort", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BLIORB": { + "gematria": [], + "meanings": [ + { + "meaning": "comfort, of comfort", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BLIORS": { + "gematria": [], + "meanings": [ + { + "meaning": "comfort, of", + "source": "WE" + }, + { + "meaning": "comfort, to our comfort", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BLIORT": { + "gematria": [], + "meanings": [ + { + "meaning": "comfort, of", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BLISDON": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL MERCURY IN JUPITER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BLLOLOL": { + "gematria": [], + "meanings": [ + { + "meaning": "FIFTH MINISTER OF BRORGES", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BLUMAPO": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL MERCURY IN VENUS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BLUMAZA": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL LUNA IN LUNA, KING", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BMAMGAL": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL SATURN IN VENUS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BMILGES": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL OF MARS IN JUPITER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BMINPOL": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL OF MARS IN SATURN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BNAMGEN": { + "gematria": [], + "meanings": [ + { + "meaning": "3RD MINISTER OF SATURN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BNANGEL": { + "gematria": [], + "meanings": [ + { + "meaning": "3RD MINISTER OF BRORGES", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BNAPSEN": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL SATURN IN SATURN, KING", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BNASPOL": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL MERCURY IN MERCURY, KING", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BNG": { + "gematria": [], + "meanings": [ + { + "meaning": "guardian", + "source": "WE" + }, + { + "meaning": "guardian", + "source": "WE", + "source2": "Holy Table of Practice perimeter" + } + ], + "pronounciations": [] + }, + "BNVAGES": { + "gematria": [], + "meanings": [ + { + "meaning": "4TH MINISTER OF SATURN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BNVIGER": { + "gematria": [], + "meanings": [ + { + "meaning": "4TH MINISTER OF BRORGES", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BOBOGEL": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL OF SOL IN SOL, KING", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BOGEMO": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BOGPA": { + "gematria": [ + 58 + ], + "meanings": [ + { + "meaning": "to govern", + "source": "EMPM" + }, + { + "meaning": "reigns", + "source": "WE" + }, + { + "meaning": "reigns", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "boh-geh-pah", + "source": "EMPM" + } + ] + }, + "BOLP": { + "gematria": [], + "meanings": [ + { + "meaning": "be thou", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BONEFON": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL OF SOL IN MARS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BOOAPIS": { + "gematria": [], + "meanings": [ + { + "meaning": "SERVE, LET HER SERVE THEMBORMILA ANGEL VENUS IN SATURN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BORNOGO": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL OF SOL IN VENUS, PRINCE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BOZA": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Angel AIR OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BPSAC": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Archangel EARTH OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BRAGIOP": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL LUNA IN MARS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BRALGES": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL LUNA IN SATURN, PRINCE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BRANGLO": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL SATURN IN SOL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BRANSG": { + "gematria": [], + "meanings": [ + { + "meaning": "guard", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BRAP": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BRASGES": { + "gematria": [], + "meanings": [ + { + "meaning": "VAR OF BRALGES", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BRGDA": { + "gematria": [], + "meanings": [ + { + "meaning": "sleep", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BRGDO": { + "gematria": [ + 147 + ], + "meanings": [ + { + "meaning": "sleep", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "bar-geh-doh", + "source": "EMPM" + } + ] + }, + "BRIAP": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BRIN": { + "gematria": [], + "meanings": [ + { + "meaning": "have", + "source": "WE" + }, + { + "meaning": "has", + "source": "WE" + }, + { + "meaning": "hast", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BRINTS": { + "gematria": [], + "meanings": [ + { + "meaning": "have", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BRISFLI": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL LUNA IN SOL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BRISFOG": { + "gematria": [], + "meanings": [ + { + "meaning": "with the eclipse", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "BRITA": { + "gematria": [], + "meanings": [ + { + "meaning": "talk, I have talked of you", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BRORGES": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL SATURN IN MERCURY,PRINCE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BUSCNAB": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL OF SOL IN SATURN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BUSD": { + "gematria": [], + "meanings": [ + { + "meaning": "glory, in glory", + "source": "WE" + }, + { + "meaning": "glory, in the glory", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BUSDIR": { + "gematria": [], + "meanings": [ + { + "meaning": "glory, the", + "source": "WE" + }, + { + "meaning": "glory, that the glory", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BUSDUNA": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL OF MARS IN LUNA", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BUTMON": { + "gematria": [], + "meanings": [ + { + "meaning": "mouth, has opened his mouth", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BUTMONA": { + "gematria": [], + "meanings": [ + { + "meaning": "mouth, of his mouth", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BUTMONI": { + "gematria": [], + "meanings": [ + { + "meaning": "mouth, from their mouths", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BUTMONO": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL JUPITER IN MARS, PRINCE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BUZD": { + "gematria": [ + 82, + 88 + ], + "meanings": [ + { + "meaning": "glory", + "source": "EMPM" + }, + { + "meaning": "glory", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "buh-zod-deh", + "source": "EMPM" + }, + { + "pronounciation": "buh-zod-deh", + "source": "EMPM" + } + ] + }, + "BVRISE": { + "gematria": [], + "meanings": [ + { + "meaning": "glorious cry, infinite wail", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "BYAPAOL": { + "gematria": [], + "meanings": [ + { + "meaning": "2ND MINISTER OF BRORGES", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BYAPARE": { + "gematria": [], + "meanings": [ + { + "meaning": "2ND MINISTER OF SATURN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BYNEPOR": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL JUPITER IN JUPITER,KING", + "source": "WE" + } + ], + "pronounciations": [] + }, + "BZIZA": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Archangel FIRE OF FIREC Veh (C or K)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "C": { + "gematria": [], + "meanings": [ + { + "meaning": "of, unto,on, with; o,oh", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CA": { + "gematria": [], + "meanings": [ + { + "meaning": "therefore", + "source": "WE" + }, + { + "meaning": "therefor", + "source": "WE" + }, + { + "meaning": "another", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CAB": { + "gematria": [], + "meanings": [ + { + "meaning": "a rod", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CABA": { + "gematria": [], + "meanings": [ + { + "meaning": "govern, to; (see 'cab')", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CACACOM": { + "gematria": [], + "meanings": [ + { + "meaning": "flourish", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CACARG": { + "gematria": [], + "meanings": [ + { + "meaning": "until", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CACRG": { + "gematria": [], + "meanings": [ + { + "meaning": "until", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CADAAMP": { + "gematria": [], + "meanings": [ + { + "meaning": "Zodiacal King SAGITTARIUS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CAF": { + "gematria": [], + "meanings": [ + { + "meaning": "abides", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "CAFAFAM": { + "gematria": [], + "meanings": [ + { + "meaning": "abiding, var of casasam", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CALZ": { + "gematria": [], + "meanings": [ + { + "meaning": "firmaments, above the", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CALZIRG": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN LIN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CAM": { + "gematria": [], + "meanings": [ + { + "meaning": "speaking", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "CAMASCHETH": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CAMIKAS": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CAMLIAX": { + "gematria": [], + "meanings": [ + { + "meaning": "spoke (p.t. of \"speak\")", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CANAL": { + "gematria": [], + "meanings": [ + { + "meaning": "workers, continual workmen", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CANSE": { + "gematria": [], + "meanings": [ + { + "meaning": "mighty", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CAOSG": { + "gematria": [], + "meanings": [ + { + "meaning": "earth, the", + "source": "WE" + }, + { + "meaning": "earth, on the", + "source": "WE" + }, + { + "meaning": "earth, the", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CAOSGA": { + "gematria": [], + "meanings": [ + { + "meaning": "earth, the", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CAOSGI": { + "gematria": [], + "meanings": [ + { + "meaning": "earth, the", + "source": "WE" + }, + { + "meaning": "earth, than the", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CAOSGIN": { + "gematria": [], + "meanings": [ + { + "meaning": "earth, var of caosg", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CAOSGO": { + "gematria": [], + "meanings": [ + { + "meaning": "earth, of the", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CAOSGON": { + "gematria": [], + "meanings": [ + { + "meaning": "earth, to the earth", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CAPH": { + "gematria": [], + "meanings": [ + { + "meaning": "in turn", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "CAPIMALI": { + "gematria": [], + "meanings": [ + { + "meaning": "successively", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CAPIMAO": { + "gematria": [], + "meanings": [ + { + "meaning": "time, while", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CAPIMAON": { + "gematria": [], + "meanings": [ + { + "meaning": "time, the number of", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CAPMIALI": { + "gematria": [], + "meanings": [ + { + "meaning": "successively (alt.sp.)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CAPPO": { + "gematria": [], + "meanings": [ + { + "meaning": "therefore the Sons of God", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "CARBAF": { + "gematria": [], + "meanings": [ + { + "meaning": "sink", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CARMA": { + "gematria": [], + "meanings": [ + { + "meaning": "come out", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CARMARA": { + "gematria": [], + "meanings": [ + { + "meaning": "KING OF HEPTARCHY", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CARNAT": { + "gematria": [], + "meanings": [ + { + "meaning": "invoke the Lord", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "CAS": { + "gematria": [], + "meanings": [ + { + "meaning": "who is", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "CASARM": { + "gematria": [], + "meanings": [ + { + "meaning": "whom, to whom", + "source": "WE" + }, + { + "meaning": "whom, unto whomCASARMA whom", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CASARMAN": { + "gematria": [], + "meanings": [ + { + "meaning": "whom, of whom", + "source": "WE" + }, + { + "meaning": "whom, under whose", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CASARMG": { + "gematria": [], + "meanings": [ + { + "meaning": "whom, in whom", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CASARMI": { + "gematria": [], + "meanings": [ + { + "meaning": "whom, under whom", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CASASAM": { + "gematria": [], + "meanings": [ + { + "meaning": "abiding, their", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CELPADMAN": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CEM": { + "gematria": [], + "meanings": [ + { + "meaning": "of the nine; unto the nine; with the nine", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "CEPH": { + "gematria": [], + "meanings": [ + { + "meaning": "ENOCHIAN LETTER Z", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CHE": { + "gematria": [], + "meanings": [ + { + "meaning": "of the Son of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "CHIALPS": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN NIA", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CHIEUAK": { + "gematria": [], + "meanings": [ + { + "meaning": "Being with Vaa", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "CHIIS": { + "gematria": [], + "meanings": [ + { + "meaning": "are they", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CHILDAO": { + "gematria": [], + "meanings": [ + { + "meaning": "diamond, with", + "source": "WE" + }, + { + "meaning": "diamonds", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CHIRLAN": { + "gematria": [], + "meanings": [ + { + "meaning": "rejoices", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CHIRZPA": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ASP", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CHIS": { + "gematria": [], + "meanings": [ + { + "meaning": "are", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CHISO": { + "gematria": [], + "meanings": [ + { + "meaning": "are, shall be", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CHR": { + "gematria": [], + "meanings": [ + { + "meaning": "TWENTIETH AETHYR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CHRAMSA": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CHRISTEOS": { + "gematria": [], + "meanings": [ + { + "meaning": "let there be", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CIAI": { + "gematria": [], + "meanings": [ + { + "meaning": 9996, + "source": "WE" + } + ], + "pronounciations": [] + }, + "CIAOFI": { + "gematria": [], + "meanings": [ + { + "meaning": "terror, to the terror of", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CICLE": { + "gematria": [], + "meanings": [ + { + "meaning": "mysteries, the", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CICLES": { + "gematria": [], + "meanings": [ + { + "meaning": "mysteries, of your mysteries", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CINXIR": { + "gematria": [], + "meanings": [ + { + "meaning": "mingled", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CLA": { + "gematria": [], + "meanings": [ + { + "meaning": 456, + "source": "WE" + } + ], + "pronounciations": [] + }, + "CNILA": { + "gematria": [], + "meanings": [ + { + "meaning": "blood, of", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CNOQOD": { + "gematria": [], + "meanings": [ + { + "meaning": "servants, his", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CNOQUODI": { + "gematria": [], + "meanings": [ + { + "meaning": "servants, with the ministers", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CNOQUOL": { + "gematria": [], + "meanings": [ + { + "meaning": "servants, o you", + "source": "WE" + } + ], + "pronounciations": [] + }, + "COAZIOR": { + "gematria": [], + "meanings": [ + { + "meaning": "increase", + "source": "WE" + } + ], + "pronounciations": [] + }, + "COCASB": { + "gematria": [], + "meanings": [ + { + "meaning": "times", + "source": "WE" + }, + { + "meaning": "time", + "source": "WE" + }, + { + "meaning": "time, of", + "source": "WE" + }, + { + "meaning": "time, the", + "source": "WE" + } + ], + "pronounciations": [] + }, + "COLLAL": { + "gematria": [], + "meanings": [ + { + "meaning": "sleeves", + "source": "WE" + } + ], + "pronounciations": [] + }, + "COMANAN": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ZAX", + "source": "WE" + } + ], + "pronounciations": [] + }, + "COMMAH": { + "gematria": [], + "meanings": [ + { + "meaning": "trussed you together", + "source": "WE" + } + ], + "pronounciations": [] + }, + "COMO": { + "gematria": [], + "meanings": [ + { + "meaning": "window, a", + "source": "WE" + } + ], + "pronounciations": [] + }, + "COMSELH": { + "gematria": [], + "meanings": [ + { + "meaning": "circle, a", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CONGAMPHLGH": { + "gematria": [], + "meanings": [ + { + "meaning": "MAN'S SPIRIT; THE HOLY GHOST; 212", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CONISBRA": { + "gematria": [], + "meanings": [ + { + "meaning": "work of man, the", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CONST": { + "gematria": [], + "meanings": [ + { + "meaning": "thunders, the", + "source": "WE" + } + ], + "pronounciations": [] + }, + "COR": { + "gematria": [], + "meanings": [ + { + "meaning": "number", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CORABIEL": { + "gematria": [], + "meanings": [ + { + "meaning": "angel of mercury ???", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CORAXO": { + "gematria": [], + "meanings": [ + { + "meaning": "thunders of judgment & wrath", + "source": "WE" + }, + { + "meaning": "thunders", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CORD": { + "gematria": [], + "meanings": [ + { + "meaning": "made", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CORDZIZ": { + "gematria": [], + "meanings": [ + { + "meaning": "man", + "source": "WE" + }, + { + "meaning": "men, reasoning creatures", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CORFAX": { + "gematria": [], + "meanings": [ + { + "meaning": "name of a guardian angel", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CORMF": { + "gematria": [], + "meanings": [ + { + "meaning": "number", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CORMFA": { + "gematria": [], + "meanings": [ + { + "meaning": "numbers", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CORMP": { + "gematria": [], + "meanings": [ + { + "meaning": "numbered", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CORMPO": { + "gematria": [], + "meanings": [ + { + "meaning": "number, have numbered", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CORMPT": { + "gematria": [], + "meanings": [ + { + "meaning": "number, be numbered", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CORONZON": { + "gematria": [], + "meanings": [ + { + "meaning": "demon", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CORS": { + "gematria": [], + "meanings": [ + { + "meaning": "such, work", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CORSI": { + "gematria": [], + "meanings": [ + { + "meaning": "such, of such as", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CRALPIR": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ZIP", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CRAMSA": { + "gematria": [], + "meanings": [ + { + "meaning": "beginning with 9 in the 4th", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "CRIP": { + "gematria": [], + "meanings": [ + { + "meaning": "but", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CROODZI": { + "gematria": [], + "meanings": [ + { + "meaning": "beginning, 2nd beginning of the", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CRP": { + "gematria": [], + "meanings": [ + { + "meaning": "but (alt.sp.)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CRUSCANSE": { + "gematria": [], + "meanings": [ + { + "meaning": "more mighty", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CUCARPT": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN LEA", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CURES": { + "gematria": [], + "meanings": [ + { + "meaning": "here (?)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CZNS": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "CZONS": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF AIR (VAR)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "D": { + "gematria": [ + 4 + ], + "meanings": [ + { + "meaning": "one third", + "source": "EMPM" + }, + { + "meaning": "Gal (D)", + "source": "WE" + }, + { + "meaning": "third, the third", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "deh", + "source": "EMPM" + } + ] + }, + "DA": { + "gematria": [], + "meanings": [ + { + "meaning": "there", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DABIN": { + "gematria": [], + "meanings": [ + { + "meaning": "(manifested word of God) Logos", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DAH": { + "gematria": [], + "meanings": [ + { + "meaning": "thrice", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DALPH": { + "gematria": [], + "meanings": [ + { + "meaning": "among the first to give", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DALTT": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DAM": { + "gematria": [], + "meanings": [ + { + "meaning": "several", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DAMO": { + "gematria": [], + "meanings": [ + { + "meaning": "several men", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DAMPH": { + "gematria": [], + "meanings": [ + { + "meaning": "various", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DAMPLOZ": { + "gematria": [], + "meanings": [ + { + "meaning": "variety", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DAN": { + "gematria": [], + "meanings": [ + { + "meaning": "3 in 1", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DANPHA": { + "gematria": [], + "meanings": [ + { + "meaning": "the three are One", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DANZAN": { + "gematria": [], + "meanings": [ + { + "meaning": "universal law", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DAOX": { + "gematria": [], + "meanings": [ + { + "meaning": 5678, + "source": "WE" + } + ], + "pronounciations": [] + }, + "DAPH": { + "gematria": [], + "meanings": [ + { + "meaning": "Speaking from there", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DAPI": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DAR": { + "gematria": [], + "meanings": [ + { + "meaning": "The Philosopher’s Stone", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DARBS": { + "gematria": [ + 122 + ], + "meanings": [ + { + "meaning": "obedience", + "source": "EMPM" + }, + { + "meaning": "obey", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "dar-bess", + "source": "EMPM" + } + ] + }, + "DARG": { + "gematria": [], + "meanings": [ + { + "meaning": 6739, + "source": "WE" + } + ], + "pronounciations": [] + }, + "DARR": { + "gematria": [], + "meanings": [ + { + "meaning": "THE PHILOSOPHER'S STONE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DARSAR": { + "gematria": [], + "meanings": [ + { + "meaning": "wherefore", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DASCH": { + "gematria": [], + "meanings": [ + { + "meaning": "a thousand angels of God", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DASMAT": { + "gematria": [], + "meanings": [ + { + "meaning": "a thousand angels", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DASPI": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DATT": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DAVEZ": { + "gematria": [], + "meanings": [ + { + "meaning": "there unto them", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DAX": { + "gematria": [], + "meanings": [ + { + "meaning": "loins", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DAXIL": { + "gematria": [], + "meanings": [ + { + "meaning": "loins, thy", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DAXZUM": { + "gematria": [], + "meanings": [ + { + "meaning": "seed", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DAZIS": { + "gematria": [ + 80, + 86 + ], + "meanings": [ + { + "meaning": "head, heads", + "source": "EMPM" + }, + { + "meaning": "head, heads", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "dah-zodee-ess", + "source": "EMPM" + }, + { + "pronounciation": "dah-zodee-ess", + "source": "EMPM" + } + ] + }, + "DAZIZ": { + "gematria": [], + "meanings": [ + { + "meaning": "heads, the", + "source": "WE" + }, + { + "meaning": "heads, their", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DE": { + "gematria": [], + "meanings": [ + { + "meaning": "of", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DEDVILH": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DEF": { + "gematria": [], + "meanings": [ + { + "meaning": "visiting", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DEGEL": { + "gematria": [], + "meanings": [ + { + "meaning": "not of the first", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DEM": { + "gematria": [], + "meanings": [ + { + "meaning": "separate", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DEMPHE": { + "gematria": [], + "meanings": [ + { + "meaning": "separate unto the Daughter of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DEO": { + "gematria": [], + "meanings": [ + { + "meaning": "SEVENTH AETHYR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DES": { + "gematria": [], + "meanings": [ + { + "meaning": "26TH AETHYR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DEX": { + "gematria": [], + "meanings": [ + { + "meaning": "of the One", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DIAL": { + "gematria": [], + "meanings": [ + { + "meaning": "A GOD-NAME OF EARTH TABLET", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DIALIOAI": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ARNDIARI Servient Angel WATER OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DILZMO": { + "gematria": [], + "meanings": [ + { + "meaning": "differ, let them differ", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DINOXA": { + "gematria": [], + "meanings": [ + { + "meaning": "3 paths", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DIOM": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DIRI": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DIU": { + "gematria": [], + "meanings": [ + { + "meaning": "angle", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DIV": { + "gematria": [], + "meanings": [ + { + "meaning": "angle", + "source": "WE" + }, + { + "meaning": "angle", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DIXOM": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DLASOD": { + "gematria": [], + "meanings": [ + { + "meaning": "ALCHEMICAL SULPHUR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DLUGA": { + "gematria": [], + "meanings": [ + { + "meaning": "give, giving", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DLUGAM": { + "gematria": [], + "meanings": [ + { + "meaning": "given, p.t. \"to give\"", + "source": "WE" + }, + { + "meaning": "give, given", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DLUGAR": { + "gematria": [ + 196 + ], + "meanings": [ + { + "meaning": "to give", + "source": "EMPM" + }, + { + "meaning": "give, gave them", + "source": "WE" + }, + { + "meaning": "give, giving unto them", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "deh-lu-gar", + "source": "EMPM" + } + ] + }, + "DMAL": { + "gematria": [], + "meanings": [ + { + "meaning": "SON OF LIGHT, JUPITER OR MARS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DO": { + "gematria": [ + 34 + ], + "meanings": [ + { + "meaning": "name", + "source": "EMPM" + }, + { + "meaning": "Root of Don (R), which is the root of the word for 'Hell Fire' and the word for 'Sun of God'", + "source": "WE", + "source2": "Holy Table of Practice perimeter" + } + ], + "pronounciations": [ + { + "pronounciation": "doh", + "source": "EMPM" + } + ] + }, + "DOAGNIS": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ARN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DOALIM": { + "gematria": [ + 198 + ], + "meanings": [ + { + "meaning": "sin", + "source": "EMPM" + }, + { + "meaning": "SIN, OF SIN", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "doh-ah-lee-em", + "source": "EMPM" + } + ] + }, + "DOANZIN": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ZIP", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DOBIX": { + "gematria": [], + "meanings": [ + { + "meaning": "FALL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DOCEPAX": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ZIM", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DODPAL": { + "gematria": [], + "meanings": [ + { + "meaning": "VEX, LET THEM VEX", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DODRMNI": { + "gematria": [], + "meanings": [ + { + "meaning": "VEX, VEXED", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DODS": { + "gematria": [], + "meanings": [ + { + "meaning": "VEX, VEXING", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DODSEH": { + "gematria": [ + 56 + ], + "meanings": [ + { + "meaning": "vexation", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "doh-dess-eh", + "source": "EMPM" + } + ] + }, + "DODSIH": { + "gematria": [], + "meanings": [ + { + "meaning": "VEX, VEXATION", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DOH": { + "gematria": [], + "meanings": [ + { + "meaning": "holy fire", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DOLOP": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DOMIOL": { + "gematria": [], + "meanings": [ + { + "meaning": "Making the Lord to Understanding", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DON": { + "gematria": [], + "meanings": [ + { + "meaning": "ENOCHIAN LETTER R", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DONASDOGAMA": { + "gematria": [], + "meanings": [ + { + "meaning": "TASTOS HELL-FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DONGLSES": { + "gematria": [], + "meanings": [ + { + "meaning": "the Daughter of Light pines for the Sun of God", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DONITON": { + "gematria": [], + "meanings": [ + { + "meaning": "the Sun of God is begotten", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DONKNA": { + "gematria": [], + "meanings": [ + { + "meaning": "Sun of God", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DONLA": { + "gematria": [], + "meanings": [ + { + "meaning": "primordial fire", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DONS": { + "gematria": [], + "meanings": [ + { + "meaning": "the Sun of God to the 4 th", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DOOAIN": { + "gematria": [], + "meanings": [ + { + "meaning": "NAME", + "source": "WE" + }, + { + "meaning": "NAME, HIS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DOOAIO": { + "gematria": [], + "meanings": [ + { + "meaning": "NAME, IN THE NAME OF (ALT.SP)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DOOIAP": { + "gematria": [], + "meanings": [ + { + "meaning": "NAME, IN THE NAME OF", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DOOP": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DOPA": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Angel AIR OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DORPHA": { + "gematria": [], + "meanings": [ + { + "meaning": "LOOK, LOOKED ABOUT ME", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DORPHAL": { + "gematria": [], + "meanings": [ + { + "meaning": "LOOK, LOOKING WITH GLADNESS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DOSCH": { + "gematria": [], + "meanings": [ + { + "meaning": "of the night", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DOSIG": { + "gematria": [ + 109 + ], + "meanings": [ + { + "meaning": "night", + "source": "EMPM" + }, + { + "meaning": "NIGHT", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "doh-see-geh", + "source": "EMPM" + } + ] + }, + "DOX": { + "gematria": [], + "meanings": [ + { + "meaning": "the sacrificial fire", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DOXMAEL": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN TEX", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DRAMAH": { + "gematria": [], + "meanings": [ + { + "meaning": "the (third) East is in darkness", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DRILPA": { + "gematria": [], + "meanings": [ + { + "meaning": "GREAT", + "source": "WE" + }, + { + "meaning": "GREAT", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DRILPI": { + "gematria": [], + "meanings": [ + { + "meaning": "GREATER (LARGER?)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DRINOX": { + "gematria": [], + "meanings": [ + { + "meaning": "invoke the Hexagram of dissolution", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DRIX": { + "gematria": [], + "meanings": [ + { + "meaning": "BRING DOWN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DROES": { + "gematria": [], + "meanings": [ + { + "meaning": "at any quarter", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DROLN": { + "gematria": [], + "meanings": [ + { + "meaning": "ANY, AT ANY", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DROXAD": { + "gematria": [], + "meanings": [ + { + "meaning": "any part of the third", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DRUN": { + "gematria": [], + "meanings": [ + { + "meaning": "ENOCHIAN LETTER N", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DRUX": { + "gematria": [], + "meanings": [ + { + "meaning": "ENOCHIAN LETTER N", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DRUXARH": { + "gematria": [], + "meanings": [ + { + "meaning": "the Angel of the East is among the third", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DRVLTHE": { + "gematria": [], + "meanings": [ + { + "meaning": "The angel of the East is seated with the Daughter of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DS": { + "gematria": [], + "meanings": [ + { + "meaning": "WHICH", + "source": "WE" + }, + { + "meaning": "AND", + "source": "WE" + }, + { + "meaning": "THAT", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DST": { + "gematria": [], + "meanings": [ + { + "meaning": "WHICH (alt.sp.)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "DVN": { + "gematria": [], + "meanings": [ + { + "meaning": "the body of God", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "DVXMA": { + "gematria": [], + "meanings": [ + { + "meaning": "the body of God", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "E": { + "gematria": [], + "meanings": [ + { + "meaning": "Graph (E)", + "source": "WE" + }, + { + "meaning": "DAUGHTER OF LIGHT", + "source": "WE" + }, + { + "meaning": "(name of an angel)", + "source": "EMPM", + "source2": "pg 25" + } + ], + "pronounciations": [ + { + "pronounciation": "Eh", + "source": "EMPM", + "source2": "pg 25" + } + ] + }, + "EAI": { + "gematria": [], + "meanings": [ + { + "meaning": "AMONG, VAR OF 'AAI'", + "source": "WE" + } + ], + "pronounciations": [] + }, + "EBAO": { + "gematria": [], + "meanings": [ + { + "meaning": "aethyr", + "source": "WE", + "source2": "Lamen" + } + ], + "pronounciations": [] + }, + "ECAOP": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ECOP": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ECRIN": { + "gematria": [], + "meanings": [ + { + "meaning": "PRAISE, THE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "EDLPRNA": { + "gematria": [], + "meanings": [ + { + "meaning": "KING OF FIRE TABLET (VAR)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "EDLPRNAA": { + "gematria": [], + "meanings": [ + { + "meaning": "ELEMENTAL KING OF FIRE TABLET", + "source": "WE" + } + ], + "pronounciations": [] + }, + "EDNAS": { + "gematria": [ + 77 + ], + "meanings": [ + { + "meaning": "receivers", + "source": "EMPM" + }, + { + "meaning": "RECEIVE, AS RECEIVERS", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "ed-nah-ess", + "source": "EMPM" + } + ] + }, + "EF": { + "gematria": [], + "meanings": [ + { + "meaning": "VISIT US", + "source": "WE" + } + ], + "pronounciations": [] + }, + "EFAFAFE": { + "gematria": [ + 41 + ], + "meanings": [ + { + "meaning": "vessels", + "source": "EMPM" + }, + { + "meaning": "VIALS, YOUR VIOLS", + "source": "WE" + }, + { + "meaning": "VIALS (?VIOLS)", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "eff-aff-aff-eh", + "source": "EMPM" + } + ] + }, + "EFE": { + "gematria": [], + "meanings": [ + { + "meaning": "MEANING UNKNOWN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "EG": { + "gematria": [], + "meanings": [ + { + "meaning": "holy", + "source": "WE", + "source2": "Holy Table of Practice perimeter" + } + ], + "pronounciations": [] + }, + "EGO": { + "gematria": [], + "meanings": [ + { + "meaning": "HOLY, THE", + "source": "WE", + "source2": "Lamen" + } + ], + "pronounciations": [] + }, + "EILOMFO": { + "gematria": [], + "meanings": [ + { + "meaning": "1ST MINISTER OF MARS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "EKIEI": { + "gematria": [], + "meanings": [ + { + "meaning": "DAUGHTER OF DAUGHTER OF LIGHT", + "source": "WE" + } + ], + "pronounciations": [] + }, + "EL": { + "gematria": [ + 18 + ], + "meanings": [ + { + "meaning": "first", + "source": "EMPM" + }, + { + "meaning": "FIRST, THE", + "source": "WE" + }, + { + "meaning": "SON OF SON OF LIGHT, VENUS", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "el", + "source": "EMPM" + } + ] + }, + "ELA": { + "gematria": [], + "meanings": [ + { + "meaning": "FIRST", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ELGNSEB": { + "gematria": [], + "meanings": [ + { + "meaning": "1ST MINISTER OF MERCURY", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ELO": { + "gematria": [ + 48 + ], + "meanings": [ + { + "meaning": "all", + "source": "EMPM" + }, + { + "meaning": "FIRST", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "el-oh", + "source": "EMPM" + } + ] + }, + "ELZAP": { + "gematria": [ + 36, + 42 + ], + "meanings": [ + { + "meaning": "way, course", + "source": "EMPM" + }, + { + "meaning": "way, course", + "source": "EMPM" + }, + { + "meaning": "COURSE, THE COURSE", + "source": "WE" + }, + { + "meaning": "COURSE, COURSES", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "el-zodah-peh", + "source": "EMPM" + }, + { + "pronounciation": "el-zodah-peh", + "source": "EMPM" + } + ] + }, + "EM": { + "gematria": [], + "meanings": [ + { + "meaning": "NINE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "EMETGIS": { + "gematria": [], + "meanings": [ + { + "meaning": "SEAL, THE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "EMNA": { + "gematria": [], + "meanings": [ + { + "meaning": "HERE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "EMOD": { + "gematria": [], + "meanings": [ + { + "meaning": 876, + "source": "WE" + } + ], + "pronounciations": [] + }, + "ENAY": { + "gematria": [], + "meanings": [ + { + "meaning": "LORD, THE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "EO": { + "gematria": [], + "meanings": [ + { + "meaning": "make, making, ‘I made you’", + "source": "WE", + "source2": "Holy Table of Practice perimeter" + } + ], + "pronounciations": [] + }, + "EOAN": { + "gematria": [], + "meanings": [ + { + "meaning": "making", + "source": "WE", + "source2": "Table of 12" + }, + { + "meaning": "‘making’, ‘the Sons of the Son of Light’", + "source": "WE", + "source2": "Holy Table of Practice perimeter" + } + ], + "pronounciations": [] + }, + "EOGA": { + "gematria": [], + "meanings": [ + { + "meaning": "THE PLACE (Schuler)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "EOL": { + "gematria": [], + "meanings": [ + { + "meaning": "MAKE, I MADE YOU", + "source": "WE" + } + ], + "pronounciations": [] + }, + "EOLIS": { + "gematria": [ + 115 + ], + "meanings": [ + { + "meaning": "to make", + "source": "EMPM" + }, + { + "meaning": "MAKE, MAKING", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "eh-oh-lee-es", + "source": "EMPM" + } + ] + }, + "EOO": { + "gematria": [], + "meanings": [ + { + "meaning": "Archetypal man, makes or making man", + "source": "WE", + "source2": "Table of 12" + } + ], + "pronounciations": [] + }, + "EOPHAN": { + "gematria": [ + 106 + ], + "meanings": [ + { + "meaning": "sorrow", + "source": "EMPM" + }, + { + "meaning": "LAMENTATION, OF", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "eh-oh-peh-han", + "source": "EMPM" + } + ] + }, + "EORS": { + "gematria": [], + "meanings": [ + { + "meaning": "HUNDRED, WITH AN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ERAN": { + "gematria": [], + "meanings": [ + { + "meaning": 6332, + "source": "WE" + } + ], + "pronounciations": [] + }, + "ERGDBAB": { + "gematria": [], + "meanings": [ + { + "meaning": "6TH MINISTER OF MERCURY", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ERM": { + "gematria": [], + "meanings": [ + { + "meaning": "ARK, WITH THE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ERZLA": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Archangel AIR OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ES": { + "gematria": [ + 17 + ], + "meanings": [ + { + "meaning": "one fourth, a quarter", + "source": "EMPM" + }, + { + "meaning": "FOURTHESE DAUGHTER OF LIGHT", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "ess", + "source": "EMPM" + } + ] + }, + "ESE": { + "gematria": [], + "meanings": [ + { + "meaning": "(name of an angel)", + "source": "EMPM", + "source2": "pg 25" + } + ], + "pronounciations": [ + { + "pronounciation": "Es-seh", + "source": "EMPM", + "source2": "pg 25" + } + ] + }, + "ESEMELI": { + "gematria": [], + "meanings": [ + { + "meaning": "DAUGHTER OF DAUGHTER OF LIGHT", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ESIASCH": { + "gematria": [], + "meanings": [ + { + "meaning": "BROTHERS, THE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ETAAD": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Archangel AIR OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ETDIM": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Archangel WATER OF", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ETEVLGL": { + "gematria": [], + "meanings": [ + { + "meaning": "6TH MINISTER OF MARS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ETHAMZ": { + "gematria": [], + "meanings": [ + { + "meaning": "COVER, ARE COVERED", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ETHAMZA": { + "gematria": [ + 131 + ], + "meanings": [ + { + "meaning": "to be covered, hidden", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "en-teh-ham-zodah", + "source": "EMPM" + } + ] + }, + "ETHARZI": { + "gematria": [ + 183, + 195 + ], + "meanings": [ + { + "meaning": "in peace", + "source": "EMPM" + }, + { + "meaning": "in peace", + "source": "EMPM" + }, + { + "meaning": "PEACE, IN", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "eh-teh-har-zodee", + "source": "EMPM" + }, + { + "pronounciation": "eh-teh-har-zodee", + "source": "EMPM" + } + ] + }, + "ETNBR": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Archangel EARTH OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "EXARP": { + "gematria": [], + "meanings": [ + { + "meaning": "AIR NAME, TABLET OF UNION", + "source": "WE" + } + ], + "pronounciations": [] + }, + "EXENTASER": { + "gematria": [], + "meanings": [ + { + "meaning": "MOTHER OF ALL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "EXGSD": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Archangel FIRE OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "EYTPA": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Archangel WATER OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "F": { + "gematria": [ + 3 + ], + "meanings": [ + { + "meaning": "to visit", + "source": "EMPM" + }, + { + "meaning": "Orth (F)", + "source": "WE" + }, + { + "meaning": "VISIT", + "source": "WE" + }, + { + "meaning": "VISIT US", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "eff", + "source": "EMPM" + } + ] + }, + "FA": { + "gematria": [], + "meanings": [ + { + "meaning": "arrives", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "FAAIP": { + "gematria": [], + "meanings": [ + { + "meaning": "VOICE, YOUR VOICES", + "source": "WE" + } + ], + "pronounciations": [] + }, + "FABOAN": { + "gematria": [ + 100 + ], + "meanings": [ + { + "meaning": "poison", + "source": "EMPM" + }, + { + "meaning": "POISON, WITH", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "fah-boh-an", + "source": "EMPM" + } + ] + }, + "FAF": { + "gematria": [], + "meanings": [ + { + "meaning": "Your thought", + "source": "WE", + "source2": "Lamen" + } + ], + "pronounciations": [] + }, + "FAFEN": { + "gematria": [ + 72 + ], + "meanings": [ + { + "meaning": "to train", + "source": "EMPM" + }, + { + "meaning": "TRAIN, YOUR", + "source": "WE" + }, + { + "meaning": "INTENT, TO THE INTENT THAT", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "eff-aff-en", + "source": "EMPM" + } + ] + }, + "FALOD": { + "gematria": [], + "meanings": [ + { + "meaning": "the third arrives first", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "FAM": { + "gematria": [], + "meanings": [ + { + "meaning": "ENOCHIAN LETTER S", + "source": "WE" + } + ], + "pronounciations": [] + }, + "FAMOLET": { + "gematria": [], + "meanings": [ + { + "meaning": "the Daughter of Light covers the first", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "FAMSED": { + "gematria": [], + "meanings": [ + { + "meaning": "the Daughter of Light cxrying in the third", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "FAONTS": { + "gematria": [ + 99, + 105 + ], + "meanings": [ + { + "meaning": "to dwell in", + "source": "EMPM" + }, + { + "meaning": "to dwell in", + "source": "EMPM" + }, + { + "meaning": "DWELLING", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "fah-oh-en-tess", + "source": "EMPM" + }, + { + "pronounciation": "fah-oh-en-tess", + "source": "EMPM" + } + ] + }, + "FAORGT": { + "gematria": [], + "meanings": [ + { + "meaning": "DWELLING PLACE, THE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "FARGT": { + "gematria": [], + "meanings": [ + { + "meaning": "DWELLING PLACES, THE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "FARZM": { + "gematria": [], + "meanings": [ + { + "meaning": "VOICE, YOU LIFTED YOUR VOICES", + "source": "WE" + } + ], + "pronounciations": [] + }, + "fatesged": { + "gematria": [], + "meanings": [ + { + "meaning": "the 4 th heaven", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "FAXMAL": { + "gematria": [], + "meanings": [ + { + "meaning": "one with the infinite", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "FAXS": { + "gematria": [], + "meanings": [ + { + "meaning": 7336, + "source": "WE" + } + ], + "pronounciations": [] + }, + "FIAN": { + "gematria": [], + "meanings": [ + { + "meaning": "She is visited upon", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "FIFALZ": { + "gematria": [ + 83, + 89 + ], + "meanings": [ + { + "meaning": "to eliminate, to weed out", + "source": "EMPM" + }, + { + "meaning": "to eliminate, to weed out", + "source": "EMPM" + }, + { + "meaning": "WEED OUT", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "fee-fall-zod", + "source": "EMPM" + }, + { + "pronounciation": "fee-fall-zod", + "source": "EMPM" + } + ] + }, + "FIRE": { + "gematria": [], + "meanings": [ + { + "meaning": "WOE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "FISIS": { + "gematria": [ + 137 + ], + "meanings": [ + { + "meaning": "to do, perform", + "source": "EMPM" + }, + { + "meaning": "EXECUTE, CARRY OUT", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "fee-see-ess", + "source": "EMPM" + } + ] + }, + "FMND": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "FMOND": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF AIR (VAR)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "FR": { + "gematria": [], + "meanings": [ + { + "meaning": "‘that which you have within yourself'", + "source": "WE", + "source2": "Holy Table of Practice perimeter" + } + ], + "pronounciations": [] + }, + "FRES": { + "gematria": [], + "meanings": [ + { + "meaning": "that which you have within you is the fourth", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "G": { + "gematria": [ + 8 + ], + "meanings": [ + { + "meaning": "not, only", + "source": "EMPM" + }, + { + "meaning": "Ged (G)", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "geh", + "source": "EMPM" + } + ] + }, + "G-DO": { + "gematria": [], + "meanings": [ + { + "meaning": "THE NAME OF", + "source": "WE" + } + ], + "pronounciations": [] + }, + "G-RSAM": { + "gematria": [], + "meanings": [ + { + "meaning": "ADMIRATION, WITHGRU DEED, FACT", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GA": { + "gematria": [], + "meanings": [ + { + "meaning": "31, make, with, name of an angel; meaning ‘Last breath of the living’, spirits, the fifth angel", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GAD": { + "gematria": [], + "meanings": [ + { + "meaning": "the third angel", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GAG": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GAH": { + "gematria": [ + 15 + ], + "meanings": [ + { + "meaning": "spirit, spirits", + "source": "EMPM" + }, + { + "meaning": "SPIRIT, THE SPIRITS", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "gah", + "source": "EMPM" + } + ] + }, + "GAHA": { + "gematria": [], + "meanings": [ + { + "meaning": "EXISTED; BABE OF THE ABYSS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GAHAL": { + "gematria": [], + "meanings": [ + { + "meaning": "EXISTS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GAHALANA": { + "gematria": [], + "meanings": [ + { + "meaning": "WILL EXIST", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GAHIRE": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GAHOACHMA": { + "gematria": [], + "meanings": [ + { + "meaning": "I AM THAT I AM, TITLE OF GOD", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GAIOL": { + "gematria": [], + "meanings": [ + { + "meaning": "A GOD-NAME OF WATER TABLET", + "source": "WE" + }, + { + "meaning": "holy name of 5 letters ruling the element of Water", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GAL": { + "gematria": [], + "meanings": [ + { + "meaning": "GAL, ENOCHIAN LETTER D", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GALGOL": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GALSE": { + "gematria": [], + "meanings": [ + { + "meaning": "the night sky", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GALSENOT": { + "gematria": [], + "meanings": [ + { + "meaning": "one star in a company of stars", + "source": "WE", + "source2": "Liber Loagaeth", + "note": "This word was created by accidentally combining two words in Loagaeth and translating them as one." + } + ], + "pronounciations": [] + }, + "GALSUAGAPH": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE", + "note": "We found this word during our translation of Liber Loagaeth and translate it as: The spirit of Va, the 5 th Angel is the immortal nature." + } + ], + "pronounciations": [] + }, + "GALSUAGATH": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE", + "note": "We found this word during our translation of Liber Loagaeth and translate it as: The spirit of Va, the 5 th Angel is the immortal nature." + } + ], + "pronounciations": [] + }, + "GALVAH": { + "gematria": [], + "meanings": [ + { + "meaning": "END, NAME OF AN ANGEL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GAM": { + "gematria": [], + "meanings": [ + { + "meaning": "[the] watery loins", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GAMLED": { + "gematria": [], + "meanings": [ + { + "meaning": "the watery loins of the Daughter of Light initiate the East", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GAMPH": { + "gematria": [], + "meanings": [ + { + "meaning": "that which is not", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GAMPHEDAX": { + "gematria": [], + "meanings": [ + { + "meaning": "[the] watery loins of the Daughter of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GAN": { + "gematria": [], + "meanings": [ + { + "meaning": "of the angel", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GANEBUS": { + "gematria": [], + "meanings": [ + { + "meaning": "angelic", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GANISLAY": { + "gematria": [], + "meanings": [ + { + "meaning": "NAME OF A DEMON", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GANIURAX": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GANPOGAN": { + "gematria": [], + "meanings": [ + { + "meaning": "the angelic image of the Sun of God is made in the 4th", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GANPORT": { + "gematria": [], + "meanings": [ + { + "meaning": "angelic image of the Sun of God", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GAPH": { + "gematria": [], + "meanings": [ + { + "meaning": "I give Ga", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GAR": { + "gematria": [], + "meanings": [ + { + "meaning": "the Archangel of the East", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GARMAL": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GARMES": { + "gematria": [], + "meanings": [ + { + "meaning": "Spirit of the Daughter of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GARNASTEL": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GARP": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GASCAMPHO": { + "gematria": [], + "meanings": [ + { + "meaning": "Why didst thou so?—as God said to Lucifer.", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GASLAH": { + "gematria": [], + "meanings": [ + { + "meaning": "[this is] Why [did] God [?]", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GASSAGEN": { + "gematria": [], + "meanings": [ + { + "meaning": "DIVINE POWER CREATING ANGEL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GAZAVAA": { + "gematria": [], + "meanings": [ + { + "meaning": "A FORMED NAMED", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GB": { + "gematria": [], + "meanings": [ + { + "meaning": "slime", + "source": "WE", + "source2": "Lamen" + } + ], + "pronounciations": [] + }, + "GB-EBAO": { + "gematria": [], + "meanings": [ + { + "meaning": "Milk of the stars", + "source": "WE", + "source2": "Lamen" + } + ], + "pronounciations": [] + }, + "GBAL": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GBEAL": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GCHISGE": { + "gematria": [], + "meanings": [ + { + "meaning": "NOT, ARE NOT", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GE": { + "gematria": [ + 18 + ], + "meanings": [ + { + "meaning": "not, only", + "source": "EMPM" + }, + { + "meaning": "NOT, IS NOT", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "geh", + "source": "EMPM" + } + ] + }, + "GE-OOEEON": { + "gematria": [], + "meanings": [ + { + "meaning": "THE EYES NEED ONLY TO (Schuler)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GEBABAL": { + "gematria": [], + "meanings": [ + { + "meaning": "Zodiacal King LIBRA", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GEBAN": { + "gematria": [], + "meanings": [ + { + "meaning": "not being", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GEBED": { + "gematria": [], + "meanings": [ + { + "meaning": "not gathering the third", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GECAOND": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ZIM", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GED": { + "gematria": [], + "meanings": [ + { + "meaning": "is not the third", + "source": "WE", + "source2": "Liber Loagaeth" + }, + { + "meaning": "GED, ENOCHIAN LETTER G", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GEDO": { + "gematria": [], + "meanings": [ + { + "meaning": "speech", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GEDON": { + "gematria": [], + "meanings": [ + { + "meaning": "holy speech", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GEDOONS": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN LOE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GEDOTBAR": { + "gematria": [], + "meanings": [ + { + "meaning": "BEGOTTEN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GEDVTH": { + "gematria": [], + "meanings": [ + { + "meaning": "three-fold negative God", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GEH": { + "gematria": [], + "meanings": [ + { + "meaning": "ARE, ART (f.p.sing \"to be\")", + "source": "WE" + }, + { + "meaning": "THOU ART", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GEIAD": { + "gematria": [], + "meanings": [ + { + "meaning": "OUR LORD AND MASTER (alt.sp.)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GEM": { + "gematria": [], + "meanings": [ + { + "meaning": "is not the 9", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GEMAN": { + "gematria": [], + "meanings": [ + { + "meaning": "The Son of Son of Light is not the 9", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GEMEDSOL": { + "gematria": [], + "meanings": [ + { + "meaning": "from the 3 rd Heaven", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GEMEGANZA": { + "gematria": [], + "meanings": [ + { + "meaning": "YOUR WILL BE DONE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GEMNIMB": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN TEX", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GEMPH": { + "gematria": [], + "meanings": [ + { + "meaning": "without the water", + "source": "WE", + "source2": "Liber Loagaeth" + }, + { + "meaning": "yield", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GENA": { + "gematria": [], + "meanings": [ + { + "meaning": "from the Lord of Hosts (with)", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GENADOL": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN DEO", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GENILE": { + "gematria": [], + "meanings": [ + { + "meaning": "from the Lord of Hosts, the Son of Son of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GENO": { + "gematria": [], + "meanings": [ + { + "meaning": "from the Lord of Hosts", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GENS": { + "gematria": [], + "meanings": [ + { + "meaning": "from the 4 th", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GENSO": { + "gematria": [], + "meanings": [ + { + "meaning": "from the Lord of Hosts, the holy Pentagram", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GENZE": { + "gematria": [], + "meanings": [ + { + "meaning": "from the Daughter of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GEPHNA": { + "gematria": [], + "meanings": [ + { + "meaning": "MEANING UNKNOWN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GER": { + "gematria": [], + "meanings": [ + { + "meaning": "ENOCHIAN LETTER Q", + "source": "WE" + }, + { + "meaning": "choose, choice", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GERPALO": { + "gematria": [], + "meanings": [ + { + "meaning": "not remaining in this place", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GES": { + "gematria": [], + "meanings": [ + { + "meaning": "is not the 4 th", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GESCO": { + "gematria": [], + "meanings": [ + { + "meaning": "is not the fourth, but with the holy Pentagram...", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GESCOGIER": { + "gematria": [], + "meanings": [ + { + "meaning": "HARVEST", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GEST": { + "gematria": [], + "meanings": [ + { + "meaning": "it also is not the 4 th", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GETA": { + "gematria": [], + "meanings": [ + { + "meaning": "OUT OF HIM", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GEVAMNA": { + "gematria": [], + "meanings": [ + { + "meaning": "BEGINNING (?)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GG": { + "gematria": [], + "meanings": [ + { + "meaning": "possess, inhabit", + "source": "WE", + "source2": "Holy Table of Practice perimeter" + } + ], + "pronounciations": [] + }, + "GGLPPSA": { + "gematria": [], + "meanings": [ + { + "meaning": "4TH MINISTER OF VENUS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GI": { + "gematria": [], + "meanings": [ + { + "meaning": "WITH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GIGIPAH": { + "gematria": [ + 152 + ], + "meanings": [ + { + "meaning": "living braeth", + "source": "EMPM" + }, + { + "meaning": "BREATH, LIVING BREATH", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "gee-gee-pah", + "source": "EMPM" + } + ] + }, + "GIL": { + "gematria": [], + "meanings": [ + { + "meaning": "WE WANT (?)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GISA": { + "gematria": [], + "meanings": [ + { + "meaning": "ENOCHIAN LETTER T", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GISG": { + "gematria": [], + "meanings": [ + { + "meaning": "ENOCHIAN LETTER T", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GITHGULCAG": { + "gematria": [], + "meanings": [ + { + "meaning": "NAME OF A DEMON", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GIVI": { + "gematria": [], + "meanings": [ + { + "meaning": "STRONGER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GIXYAX": { + "gematria": [], + "meanings": [ + { + "meaning": "EARTHQUAKES", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GLA": { + "gematria": [], + "meanings": [ + { + "meaning": "the first of the Daughters of Light", + "source": "WE", + "source2": "Lamen" + } + ], + "pronounciations": [] + }, + "GLO": { + "gematria": [ + 46 + ], + "meanings": [ + { + "meaning": "things", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "geh-loh", + "source": "EMPM" + } + ] + }, + "GMDNM": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GMICALZ": { + "gematria": [], + "meanings": [ + { + "meaning": "POWER, A POWERFUL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GMICALZO": { + "gematria": [], + "meanings": [ + { + "meaning": "POWER, IN P. AND PRESENCE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GMNM": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GNA": { + "gematria": [], + "meanings": [ + { + "meaning": "MEANING UNKNOWN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GNAY": { + "gematria": [ + 124 + ], + "meanings": [ + { + "meaning": "to do, does", + "source": "EMPM" + }, + { + "meaning": "DO, DOES", + "source": "WE" + }, + { + "meaning": "DO, DOTH", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "geh-nay", + "source": "EMPM" + } + ] + }, + "GNETAAB": { + "gematria": [ + 88, + 94 + ], + "meanings": [ + { + "meaning": "government, only government", + "source": "EMPM" + }, + { + "meaning": "government, only government", + "source": "EMPM" + }, + { + "meaning": "GOVERNMENT, YOUR GOVERNMENTS", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "gen-etah-ah-beh", + "source": "EMPM" + }, + { + "pronounciation": "gen-etah-ah-beh", + "source": "EMPM" + } + ] + }, + "GNONP": { + "gematria": [], + "meanings": [ + { + "meaning": "GARNISH, I GARNISHED", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GO (LE)": { + "gematria": [], + "meanings": [ + { + "meaning": "Speaks", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GOHED": { + "gematria": [], + "meanings": [ + { + "meaning": "ONE, EVERLASTING", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GOHEL": { + "gematria": [], + "meanings": [ + { + "meaning": "SAY, SAYS THE FIRST", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GOHIA": { + "gematria": [], + "meanings": [ + { + "meaning": "SAY, WE SAY", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GOHO": { + "gematria": [ + 69 + ], + "meanings": [ + { + "meaning": "to say, to speak", + "source": "EMPM" + }, + { + "meaning": "SAY, SAYETH, SAYS", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "goh-hoh", + "source": "EMPM" + } + ] + }, + "GOHOL": { + "gematria": [], + "meanings": [ + { + "meaning": "SAY, SAYING", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GOHOLOR": { + "gematria": [], + "meanings": [ + { + "meaning": "LIFT UP", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GOHON": { + "gematria": [], + "meanings": [ + { + "meaning": "SAY, HAVE SPOKEN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GOHULIM": { + "gematria": [], + "meanings": [ + { + "meaning": "SAY, IT IS SAID", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GOHUS": { + "gematria": [], + "meanings": [ + { + "meaning": "SAY, I SAY", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GOMZIAM": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN RII", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GON": { + "gematria": [], + "meanings": [ + { + "meaning": "ENOCHIAN LETTER I,Y", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GONO": { + "gematria": [ + 118 + ], + "meanings": [ + { + "meaning": "faith", + "source": "EMPM" + }, + { + "meaning": "FAITH", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "goh-noh", + "source": "EMPM" + } + ] + }, + "GONSAG": { + "gematria": [], + "meanings": [ + { + "meaning": "MEANING UNKNOWN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GORS": { + "gematria": [], + "meanings": [ + { + "meaning": "praise, praises", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GOSA": { + "gematria": [ + 51 + ], + "meanings": [ + { + "meaning": "strange", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "goh-sah", + "source": "EMPM" + } + ] + }, + "GOSAA": { + "gematria": [ + 57 + ], + "meanings": [ + { + "meaning": "a stranger", + "source": "EMPM" + }, + { + "meaning": "STRANGER, A", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "goh-sah-ah", + "source": "EMPM" + } + ] + }, + "GR": { + "gematria": [], + "meanings": [ + { + "meaning": "Ancestors", + "source": "WE", + "source2": "Lamen" + } + ], + "pronounciations": [] + }, + "GRAA": { + "gematria": [ + 120 + ], + "meanings": [ + { + "meaning": "the Moon", + "source": "EMPM" + }, + { + "meaning": "MOON", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "geh-rah-ah", + "source": "EMPM" + } + ] + }, + "GRAD": { + "gematria": [], + "meanings": [ + { + "meaning": "moonlight", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GRAM": { + "gematria": [], + "meanings": [ + { + "meaning": "lunar", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GRAMFA": { + "gematria": [], + "meanings": [ + { + "meaning": "full moon", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GRAN": { + "gematria": [], + "meanings": [ + { + "meaning": "ELDERS, ?VAR ON 'URAN'", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GRANSE": { + "gematria": [], + "meanings": [ + { + "meaning": "the cry of the Elders", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GRAPAD": { + "gematria": [], + "meanings": [ + { + "meaning": "Moons (pl.)", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GRAPH": { + "gematria": [], + "meanings": [ + { + "meaning": "ENOCHIAN LETTER E", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GRONADOX": { + "gematria": [], + "meanings": [ + { + "meaning": "The wrath of God", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "GROSB": { + "gematria": [], + "meanings": [ + { + "meaning": "STING, A BITTER STING", + "source": "WE" + } + ], + "pronounciations": [] + }, + "GZE": { + "gematria": [], + "meanings": [ + { + "meaning": "ONLY (Schuler)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "H": { + "gematria": [], + "meanings": [ + { + "meaning": "Na-hath (H)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HA": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HAATH": { + "gematria": [], + "meanings": [ + { + "meaning": "WORKS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HABIORO": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior MARS of AIR", + "source": "WE" + }, + { + "meaning": "a Senior of AIR", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "Hah-bee-oh-roh", + "source": "EMPM" + } + ] + }, + "HAGONEL": { + "gematria": [], + "meanings": [ + { + "meaning": "PRINCE OF HEPTARCHY", + "source": "WE" + }, + { + "meaning": "SON OF SON OF LIGHT, SATURN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HAL": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HAMI": { + "gematria": [], + "meanings": [ + { + "meaning": "CREATURE, LIVING CREATURES", + "source": "WE" + }, + { + "meaning": "CREATURES", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HANDA": { + "gematria": [], + "meanings": [ + { + "meaning": "the seed of God", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "HANZVQ": { + "gematria": [], + "meanings": [ + { + "meaning": "the will of God", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "HAOZPI": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior VENUS IN AIR TABLET", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HARDIMI": { + "gematria": [], + "meanings": [ + { + "meaning": "AN ANGEL OF THE EARTH TABLET", + "source": "WE" + }, + { + "meaning": "AN ANGEL OF ORO", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HARG": { + "gematria": [ + 115 + ], + "meanings": [ + { + "meaning": "to plant, to sow", + "source": "EMPM" + }, + { + "meaning": "PLANT, HAS PLANTED", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "har-geh", + "source": "EMPM" + } + ] + }, + "HCOMA": { + "gematria": [], + "meanings": [ + { + "meaning": "WATER NAME, TABLET OF UNION", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HCTGA": { + "gematria": [], + "meanings": [ + { + "meaning": "A GOD-NAME OF EARTH TABLET", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HECOA": { + "gematria": [], + "meanings": [ + { + "meaning": "SON OF LIGHT, MARS OR JUPITER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HEEOA": { + "gematria": [], + "meanings": [ + { + "meaning": "A SON OF LIGHT", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HELECH": { + "gematria": [], + "meanings": [ + { + "meaning": "IN OURS (?)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HIPOTGA": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior SATURN of AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HMAGL": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Archangel EARTH OF", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HNLRX": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Archangel FIRE OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HOATH": { + "gematria": [ + 41, + 47 + ], + "meanings": [ + { + "meaning": "true worshipper, devotee", + "source": "EMPM" + }, + { + "meaning": "true worshipper, devotee", + "source": "EMPM" + }, + { + "meaning": "WORSHIPER, TRUE", + "source": "WE" + }, + { + "meaning": "WORSHIPER, THE TRUE", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "hoh-ah-teh", + "source": "EMPM" + }, + { + "pronounciation": "hoh-ah-teh", + "source": "EMPM" + } + ] + }, + "HOL": { + "gematria": [ + 39 + ], + "meanings": [ + { + "meaning": "to measure", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "hoh-el", + "source": "EMPM" + } + ] + }, + "HOLDO": { + "gematria": [], + "meanings": [ + { + "meaning": "GROANED", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HOLQ": { + "gematria": [ + 79 + ], + "meanings": [ + { + "meaning": "are measured", + "source": "EMPM" + }, + { + "meaning": "MEASURETH", + "source": "WE" + }, + { + "meaning": "MEASURE, IT IS MEASURED", + "source": "WE" + }, + { + "meaning": "MEASURED", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "hoh-el-que", + "source": "EMPM" + } + ] + }, + "HOM": { + "gematria": [ + 121 + ], + "meanings": [ + { + "meaning": "to live", + "source": "EMPM" + }, + { + "meaning": "LIVE, LIVES (verb)", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "hoh-meh", + "source": "EMPM" + } + ] + }, + "HOMIL": { + "gematria": [], + "meanings": [ + { + "meaning": "AGES, THE TRUE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HOMIN": { + "gematria": [], + "meanings": [ + { + "meaning": "AGE, WITH AGE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HOMTOH": { + "gematria": [], + "meanings": [ + { + "meaning": "TRIUMPH, VAR ON 'HOM OD TOH'", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HONONOL": { + "gematria": [], + "meanings": [ + { + "meaning": "Zodiacal King LEO", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HOXMARCH": { + "gematria": [], + "meanings": [ + { + "meaning": "FEAR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HOXPOR": { + "gematria": [], + "meanings": [ + { + "meaning": "Bringing fear", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "HTAAD": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Archangel AIR OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HTDIM": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Archangel WATER OF", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HTMORDA": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior LUNA of AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HTNBR": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Archangel EARTH OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HUBAIO": { + "gematria": [], + "meanings": [ + { + "meaning": "LAMP, VAR ON HUBARO", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HUBAR": { + "gematria": [], + "meanings": [ + { + "meaning": "LAMPS, WITH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HUBARO": { + "gematria": [], + "meanings": [ + { + "meaning": "LAMPS, THE LANTERNS", + "source": "WE" + }, + { + "meaning": "LAMPS, LIVING LAMPS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HUCACHA": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HUSEH": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "HXGSD": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Archangel FIR E OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "I": { + "gematria": [ + 60 + ], + "meanings": [ + { + "meaning": "is, is not", + "source": "EMPM" + }, + { + "meaning": "(name of an angel, sol)", + "source": "" + }, + { + "meaning": "Gon (I)", + "source": "WE" + }, + { + "meaning": "IS", + "source": "WE" + }, + { + "meaning": "SON OF LIGHT, SOL OR VENUS", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "ee", + "source": "EMPM" + }, + { + "pronounciation": "Ee", + "source": "" + } + ] + }, + "IA": { + "gematria": [ + 66 + ], + "meanings": [ + { + "meaning": "truth", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "ee-ah", + "source": "EMPM" + } + ] + }, + "IAAASD": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross FIRE OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IABA": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IABES": { + "gematria": [], + "meanings": [ + { + "meaning": "LORD, SUPREME LIFE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IAD": { + "gematria": [ + 70 + ], + "meanings": [ + { + "meaning": "a god, God", + "source": "EMPM" + }, + { + "meaning": "God, the / our / your God, the Lord", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "ee-ah-doh", + "source": "EMPM" + } + ] + }, + "IADNAH": { + "gematria": [ + 127 + ], + "meanings": [ + { + "meaning": "knowledge", + "source": "EMPM" + }, + { + "meaning": "KNOWLEDGE, OF", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "ee-ah-deh-nah", + "source": "EMPM" + } + ] + }, + "IADNAMAD": { + "gematria": [], + "meanings": [ + { + "meaning": "KNOWLEDGE, UNDEFILED K.", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IADOIASMOMA": { + "gematria": [], + "meanings": [ + { + "meaning": "HIM THAT WAS,IS,AND SHALL BE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IADPIL": { + "gematria": [], + "meanings": [ + { + "meaning": "HIM, TO HIM", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IADS": { + "gematria": [], + "meanings": [ + { + "meaning": "the gods", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "IAHL": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IAIADIX": { + "gematria": [], + "meanings": [ + { + "meaning": "HONOR, OF", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IAIAL": { + "gematria": [], + "meanings": [ + { + "meaning": "CONCLUDE US", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IAIDA": { + "gematria": [ + 136 + ], + "meanings": [ + { + "meaning": "the highest", + "source": "EMPM" + }, + { + "meaning": "HIGHEST, THE", + "source": "WE" + }, + { + "meaning": "HIGHEST, OF THE", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "ee-ah-ee-dah", + "source": "EMPM" + } + ] + }, + "IAIDON": { + "gematria": [], + "meanings": [ + { + "meaning": "GOD, THE ALL-POWERFUL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IAL": { + "gematria": [ + 74 + ], + "meanings": [ + { + "meaning": "to consume", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "ee-all", + "source": "EMPM" + } + ] + }, + "IALPIRGAH": { + "gematria": [], + "meanings": [ + { + "meaning": "FIRE, GOD-FLAMES", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IALPON": { + "gematria": [], + "meanings": [ + { + "meaning": "BURN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IALPOR": { + "gematria": [], + "meanings": [ + { + "meaning": "FLAMING", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IALPRG": { + "gematria": [ + 191 + ], + "meanings": [ + { + "meaning": "burning flames", + "source": "EMPM" + }, + { + "meaning": "BURNING FLAME", + "source": "WE" + }, + { + "meaning": "BURNINGS FLAMES", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "ee-al-par-geh", + "source": "EMPM" + } + ] + }, + "IALPRT": { + "gematria": [], + "meanings": [ + { + "meaning": "FLAME", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IAMHL": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IAN": { + "gematria": [], + "meanings": [ + { + "meaning": "I am the Daughter of Light (also the formal name: IAN)", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "IANA": { + "gematria": [], + "meanings": [ + { + "meaning": "(name of an angel)", + "source": "EMPM", + "source2": "pg 25" + }, + { + "meaning": "A DAUGHTER OF LIGHT", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "Ee-ah-nah", + "source": "EMPM", + "source2": "pg 25" + } + ] + }, + "IANBA": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IAO": { + "gematria": [], + "meanings": [ + { + "meaning": "IAO", + "source": "WE", + "source2": "Holy Table of Practice perimeter" + } + ], + "pronounciations": [] + }, + "IAOD": { + "gematria": [ + 100 + ], + "meanings": [ + { + "meaning": "the beginning", + "source": "EMPM" + }, + { + "meaning": "BEGINNING", + "source": "WE" + }, + { + "meaning": "BEGINNING, THE", + "source": "WE" + }, + { + "meaning": "BEGINNING, THE B. OF", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "ee-ah-oh-deh", + "source": "EMPM" + } + ] + }, + "IAODAF": { + "gematria": [], + "meanings": [ + { + "meaning": "BEGINNING, IN THE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IBAH": { + "gematria": [], + "meanings": [ + { + "meaning": "A GOD-NAME OF AIR TABLET", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ICH": { + "gematria": [], + "meanings": [ + { + "meaning": "ELEVENTH AETHYR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ICHISGE": { + "gematria": [], + "meanings": [ + { + "meaning": "AND ARE NOT", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ICZHIHA": { + "gematria": [], + "meanings": [ + { + "meaning": "KING OF EARTH TABLET (VAR)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ICZHIHAL": { + "gematria": [], + "meanings": [ + { + "meaning": "ELEMENTAL KING OF EARTHICZHIHL KING OF EARTH TABLET (VAR)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IDLUGAM": { + "gematria": [], + "meanings": [ + { + "meaning": "GIVE, IS GIVEN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IDOIGO": { + "gematria": [], + "meanings": [ + { + "meaning": "HE WHO SITS ON THE HOLY THRONE", + "source": "WE" + }, + { + "meaning": "Sephirotic Cross AIR OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IEH": { + "gematria": [], + "meanings": [ + { + "meaning": "merciful", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "IEHUSOZ": { + "gematria": [], + "meanings": [ + { + "meaning": "MERCY, HIS MERCIES", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IH": { + "gematria": [], + "meanings": [ + { + "meaning": "SON OF LIGHT (Silver),", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IHEDVTHARH": { + "gematria": [], + "meanings": [ + { + "meaning": "the fixed stars as receivers of the one spread out against the sky", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "IHEHUDZ": { + "gematria": [], + "meanings": [ + { + "meaning": "Childrenof the Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "IHEHVDETHA": { + "gematria": [], + "meanings": [ + { + "meaning": "the fixed stars", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "IHEHVSCH": { + "gematria": [], + "meanings": [ + { + "meaning": "Ecstasy, also a formal noun; a name for a star: Augoeides", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "IIDPO": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IIPO": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IL": { + "gematria": [ + 68 + ], + "meanings": [ + { + "meaning": "Aethyr", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "ee-el", + "source": "EMPM" + } + ] + }, + "ILEMESE": { + "gematria": [], + "meanings": [ + { + "meaning": "SON OF SON OF LIGHT, LUNA", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ILI": { + "gematria": [], + "meanings": [ + { + "meaning": "FIRST, IN THE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ILMO": { + "gematria": [], + "meanings": [ + { + "meaning": "Angel or Essence of the Sun; heart of the Sun", + "source": "WE", + "source2": "Table of 12" + } + ], + "pronounciations": [] + }, + "ILPIZ": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross FIRE OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ILRO": { + "gematria": [], + "meanings": [ + { + "meaning": "Name from the tablet of 12", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ILS": { + "gematria": [], + "meanings": [ + { + "meaning": "THOU, O THOU", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IMUAMAR": { + "gematria": [], + "meanings": [ + { + "meaning": "ACT TOWARDS US", + "source": "WE" + } + ], + "pronounciations": [] + }, + "INOAS": { + "gematria": [], + "meanings": [ + { + "meaning": "BECOME, THEY ARE BECOME", + "source": "WE" + } + ], + "pronounciations": [] + }, + "INSI": { + "gematria": [ + 177 + ], + "meanings": [ + { + "meaning": "to walk on, to tread", + "source": "EMPM" + }, + { + "meaning": "WALKS", + "source": "WE" + }, + { + "meaning": "WALK", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "ee-ness-ee", + "source": "EMPM" + } + ] + }, + "IOAESPM": { + "gematria": [], + "meanings": [ + { + "meaning": "3RD MINISTER OF VENUS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IOIAD": { + "gematria": [ + 160 + ], + "meanings": [ + { + "meaning": "Eternal God", + "source": "EMPM" + }, + { + "meaning": "HIM THAT LIVES FOREVER", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "ee-oh-ee-ah-deh", + "source": "EMPM" + } + ] + }, + "IP": { + "gematria": [ + 69 + ], + "meanings": [ + { + "meaning": "not", + "source": "EMPM" + }, + { + "meaning": "NOT", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "ee-peh", + "source": "EMPM" + } + ] + }, + "IPAM": { + "gematria": [], + "meanings": [ + { + "meaning": "IS NOT", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IPAMIS": { + "gematria": [], + "meanings": [ + { + "meaning": "CAN NOT BE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IPURAN": { + "gematria": [], + "meanings": [ + { + "meaning": "SHALL NOT SEE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IRGIL": { + "gematria": [], + "meanings": [ + { + "meaning": "HOW MANY", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ISR": { + "gematria": [], + "meanings": [ + { + "meaning": "SON OF LIGHT, VENUS OR SOL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ISRO": { + "gematria": [ + 197 + ], + "meanings": [ + { + "meaning": "promise", + "source": "EMPM" + }, + { + "meaning": "PROMISE, THE", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "ee-ess-roh", + "source": "EMPM" + } + ] + }, + "IUBANLADAEC": { + "gematria": [], + "meanings": [ + { + "meaning": "NAME OF AN ANGEL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IUBENLADECE": { + "gematria": [], + "meanings": [ + { + "meaning": "VAR OF IUBANLADAEC", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IUDRA": { + "gematria": [], + "meanings": [ + { + "meaning": "MEANING UNKNOWN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IVMD": { + "gematria": [], + "meanings": [ + { + "meaning": "CALL, IS CALLED", + "source": "WE" + }, + { + "meaning": "CALL, IS CALLED", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IXOMAXIP": { + "gematria": [], + "meanings": [ + { + "meaning": "KNOW, LET HER BE KNOWN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IZAZAS": { + "gematria": [], + "meanings": [ + { + "meaning": "FRAME, HAVE FRAMEDIZED DAUGHTER OF DAUGHTER OF LIGHT", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IZINR": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IZIXP": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IZIZOP": { + "gematria": [], + "meanings": [ + { + "meaning": "VESSELS, FROM YOUR HIGHEST", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IZNR": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "IZXP": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "KAPENE": { + "gematria": [], + "meanings": [ + { + "meaning": "Therefore, the house is holy", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "L": { + "gematria": [ + 8 + ], + "meanings": [ + { + "meaning": "first", + "source": "EMPM" + }, + { + "meaning": "Ur (L)", + "source": "WE" + }, + { + "meaning": "OF THE FIRST", + "source": "WE" + }, + { + "meaning": "FIRST", + "source": "WE" + }, + { + "meaning": "ONE", + "source": "WE" + }, + { + "meaning": "THE FIRST", + "source": "WE" + }, + { + "meaning": "ALL ONE.", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "el", + "source": "EMPM" + } + ] + }, + "LA": { + "gematria": [], + "meanings": [ + { + "meaning": "Of THE FIRST", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LABDGRE": { + "gematria": [], + "meanings": [ + { + "meaning": "6TH MINISTER OF LUNA", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LABNIXP": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN BAG", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LAF": { + "gematria": [], + "meanings": [ + { + "meaning": "MEANING UNKNOWN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LAH": { + "gematria": [], + "meanings": [ + { + "meaning": "MEANING UNKNOWN", + "source": "WE" + }, + { + "meaning": "God", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "LAIAD": { + "gematria": [ + 84 + ], + "meanings": [ + { + "meaning": "secrets of truth", + "source": "EMPM" + }, + { + "meaning": "TRUTH, THE SECRETS OF", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "lah-ee-ah-deh", + "source": "EMPM" + } + ] + }, + "LAIDROM": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior MARS of EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LAM": { + "gematria": [], + "meanings": [ + { + "meaning": "except the first", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "LAN": { + "gematria": [], + "meanings": [ + { + "meaning": "first God", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "LANG": { + "gematria": [], + "meanings": [ + { + "meaning": "MINISTERING ANGELS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LANGED": { + "gematria": [], + "meanings": [ + { + "meaning": "the first utterance", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "LANSH": { + "gematria": [], + "meanings": [ + { + "meaning": "POWER, IN POWER EXALTED", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LAOAXRP": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior LUNA of WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LAP": { + "gematria": [], + "meanings": [ + { + "meaning": "FOR", + "source": "WE" + }, + { + "meaning": "FOR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LAPARIN": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ZIM", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LARAG": { + "gematria": [], + "meanings": [ + { + "meaning": "NOR", + "source": "WE" + }, + { + "meaning": "NEITHER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LAS": { + "gematria": [], + "meanings": [ + { + "meaning": "RICH, THE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LASBEN": { + "gematria": [], + "meanings": [ + { + "meaning": "NAME OF AN ANGEL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LASCO": { + "gematria": [], + "meanings": [ + { + "meaning": "strong foundation", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "LASDI": { + "gematria": [], + "meanings": [ + { + "meaning": "FEET, MY", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LAUACON": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN LEA", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LAVA": { + "gematria": [], + "meanings": [ + { + "meaning": "PRAY", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LAVAVOTH": { + "gematria": [], + "meanings": [ + { + "meaning": "Zodiacal King ARIES", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LAX": { + "gematria": [], + "meanings": [ + { + "meaning": "Abbreviation for Alt. Part in LIN; Angel of the East", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "LAXDIZI": { + "gematria": [], + "meanings": [ + { + "meaning": "ALT. PART IN LIN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LAZDIXI": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN LIT", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LBBNAAV": { + "gematria": [], + "meanings": [ + { + "meaning": "2ND MINISTER OF VENUS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LE": { + "gematria": [], + "meanings": [ + { + "meaning": "phrase: ‘first,the Daughter of Light’", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "LEA": { + "gematria": [], + "meanings": [ + { + "meaning": "SIXTEENTH AETHYR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LEAOC": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LEAORIB": { + "gematria": [], + "meanings": [ + { + "meaning": "4TH MINISTER OF SOL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LED": { + "gematria": [], + "meanings": [ + { + "meaning": "first, the Daughter of Light to the East", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "LEENARB": { + "gematria": [], + "meanings": [ + { + "meaning": "1ST MINISTER OF SOL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LEFA": { + "gematria": [], + "meanings": [ + { + "meaning": "frist, the Daughter of Light visits the interior", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "LEFE": { + "gematria": [], + "meanings": [ + { + "meaning": "first, the Daughter of Light appears", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "LEHUSAN": { + "gematria": [], + "meanings": [ + { + "meaning": "MEANING UNKNOWN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LEHUSLACH": { + "gematria": [], + "meanings": [ + { + "meaning": "MEANING UNKNOWN, SEE LEHUSAN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LEL": { + "gematria": [], + "meanings": [ + { + "meaning": "SAME, THE SAME", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LEN": { + "gematria": [], + "meanings": [ + { + "meaning": "presense of the Daughter of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "LENGES": { + "gematria": [], + "meanings": [ + { + "meaning": "the 22 nd Aethyr is not the fourth", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "LEOC": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LEOHA": { + "gematria": [], + "meanings": [ + { + "meaning": "First, the Daughter of Light in woe", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "LEOZ": { + "gematria": [], + "meanings": [ + { + "meaning": "First, the Daughter of Light beholds the Son of Son of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "LEPHE": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LESCO": { + "gematria": [], + "meanings": [ + { + "meaning": "first, the Daughter of Light with 5", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "LESGAMPH": { + "gematria": [], + "meanings": [ + { + "meaning": "first, the watery loins of the Daughter of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "LEVANAEL": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL OF LUNA", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LEVITHMONG": { + "gematria": [], + "meanings": [ + { + "meaning": "BEASTS OF THE FIELD, FOR THE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LEXARPH": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ZAX", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LGAIOL": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior VENUS of WATER TABLET", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LHCTGA": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior VENUS of EARTH TABLET", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LI": { + "gematria": [], + "meanings": [ + { + "meaning": "FIRST - VAR ON 'ILI'", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LIALPRT": { + "gematria": [ + 200 + ], + "meanings": [ + { + "meaning": "the First Glame", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "lee-al-par-teh", + "source": "EMPM" + } + ] + }, + "LIBA": { + "gematria": [], + "meanings": [ + { + "meaning": "SON OF SON OF LIGHT, MARS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LIGDISA": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior SATURN of WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LIIANSA": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior SAT of EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LIL": { + "gematria": [], + "meanings": [ + { + "meaning": "THE FIRST AIRE", + "source": "WE" + }, + { + "meaning": "FIRST AETHYR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LILONON": { + "gematria": [], + "meanings": [ + { + "meaning": "BRANCHES", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LIMLAL": { + "gematria": [ + 180 + ], + "meanings": [ + { + "meaning": "treasure", + "source": "EMPM" + }, + { + "meaning": "TREASURE, HIS", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "lee-em-lah-leh", + "source": "EMPM" + } + ] + }, + "LIN": { + "gematria": [], + "meanings": [ + { + "meaning": "22ND AETHYR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LIT": { + "gematria": [], + "meanings": [ + { + "meaning": "FIFTH AETHYR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LIXIPSP": { + "gematria": [], + "meanings": [ + { + "meaning": "WARDEN OF AETHYR 'BAG'", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LLACZA": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross WATER OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LML": { + "gematria": [], + "meanings": [ + { + "meaning": "TREASURE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LN": { + "gematria": [], + "meanings": [ + { + "meaning": "the, that", + "source": "WE", + "source2": "Holy Table of Practice perimeter" + } + ], + "pronounciations": [] + }, + "LN-NIA-O": { + "gematria": [], + "meanings": [ + { + "meaning": "THE BEAST (Shueler?)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LNANAEB": { + "gematria": [], + "meanings": [ + { + "meaning": "2ND MINISTER OF SOL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LO": { + "gematria": [], + "meanings": [ + { + "meaning": "FIRST, THE", + "source": "WE" + }, + { + "meaning": "THE FIRST", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LOADOHI": { + "gematria": [], + "meanings": [ + { + "meaning": "KINGDOM, VAR ON 'LONDOH'", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LOAGAETH": { + "gematria": [], + "meanings": [ + { + "meaning": "SPEECH FROM GOD, VAR. 1", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LOE": { + "gematria": [], + "meanings": [ + { + "meaning": "TWELFTH AETHYR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LOGAAH": { + "gematria": [], + "meanings": [ + { + "meaning": "SPEECH FROM GOD, VAR. 3", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LOGAETH": { + "gematria": [], + "meanings": [ + { + "meaning": "SPEECH FROM GOD, VAR. 2", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LOGAH": { + "gematria": [], + "meanings": [ + { + "meaning": "SPEECH FROM GOD, VAR. 4", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LOH": { + "gematria": [], + "meanings": [ + { + "meaning": "beams", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "LOHOLO": { + "gematria": [], + "meanings": [ + { + "meaning": "SHINES", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LOLCIS": { + "gematria": [], + "meanings": [ + { + "meaning": "BUCKLERS (SHIELDS)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LONCHO": { + "gematria": [], + "meanings": [ + { + "meaning": "FALL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LONDOH": { + "gematria": [ + 123 + ], + "meanings": [ + { + "meaning": "kingdoms", + "source": "EMPM" + }, + { + "meaning": "KINGDOMS", + "source": "WE" + }, + { + "meaning": "KINGDOMSLONSA POWER", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "loh-en-doh", + "source": "EMPM" + } + ] + }, + "LONSA": { + "gematria": [ + 101 + ], + "meanings": [ + { + "meaning": "everyone", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "loh-en-sah", + "source": "EMPM" + } + ] + }, + "LONSHI": { + "gematria": [ + 156 + ], + "meanings": [ + { + "meaning": "power", + "source": "EMPM" + }, + { + "meaning": "POWER, THE", + "source": "WE" + }, + { + "meaning": "POWER", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "loh-en-ess-hee", + "source": "EMPM" + } + ] + }, + "LONSHIN": { + "gematria": [], + "meanings": [ + { + "meaning": "POWER, THEIR POWERS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LORSLQ": { + "gematria": [ + 193 + ], + "meanings": [ + { + "meaning": "flowers", + "source": "EMPM" + }, + { + "meaning": "FLOWERS", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "loh-ress-el-que", + "source": "EMPM" + } + ] + }, + "LRASD": { + "gematria": [ + 125 + ], + "meanings": [ + { + "meaning": "to dispose of, to eliminate", + "source": "EMPM" + }, + { + "meaning": "DISPOSE, TO", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "el-rah-ess-deh", + "source": "EMPM" + } + ] + }, + "LRING": { + "gematria": [], + "meanings": [ + { + "meaning": "STIR UP", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LRL": { + "gematria": [], + "meanings": [ + { + "meaning": "‘first changing one’; God; movement; work", + "source": "WE", + "source2": "Table of 12" + }, + { + "meaning": "‘first changing one’; God; movement; work", + "source": "WE", + "source2": "Holy Table of Practice perimeter" + } + ], + "pronounciations": [] + }, + "LRS": { + "gematria": [], + "meanings": [ + { + "meaning": "'to charge' (either as in a talisman or as in marching forward), 'to rid or banish' and 'to change or alter'", + "source": "WE", + "source2": "Holy Table of Practice perimeter" + } + ], + "pronounciations": [] + }, + "LSEANN": { + "gematria": [], + "meanings": [ + { + "meaning": "The Ecliptic", + "source": "WE", + "source2": "Lamen" + } + ], + "pronounciations": [] + }, + "LSRAHPM": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior MARS of WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LSSN": { + "gematria": [], + "meanings": [ + { + "meaning": "constellations, lords", + "source": "WE", + "source2": "Holy Table of Practice perimeter" + } + ], + "pronounciations": [] + }, + "LUACH": { + "gematria": [], + "meanings": [ + { + "meaning": "PRAISING ANGELS, VAR. 2", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LUAH": { + "gematria": [], + "meanings": [ + { + "meaning": "PRAISING ANGELS, VAR. 1", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LUCAL": { + "gematria": [], + "meanings": [ + { + "meaning": "NORTH, IN THE NORTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LUCIFTIAN": { + "gematria": [], + "meanings": [ + { + "meaning": "BRIGHTNESS, THE", + "source": "WE" + }, + { + "meaning": "BRIGHTNESS, ORNAMENTS OF", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LUIAHE": { + "gematria": [], + "meanings": [ + { + "meaning": "HONOR, A SON OF", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LULO": { + "gematria": [], + "meanings": [ + { + "meaning": "TARTAR OR MOTHER OF VINEGAR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LURFANDO": { + "gematria": [], + "meanings": [ + { + "meaning": "MEANING UNKNOWN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LUSD": { + "gematria": [ + 89 + ], + "meanings": [ + { + "meaning": "feet", + "source": "EMPM" + }, + { + "meaning": "FEET, YOUR", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "luh-ess-deh", + "source": "EMPM" + } + ] + }, + "LUSDA": { + "gematria": [], + "meanings": [ + { + "meaning": "FEET, THEIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LUSDAN": { + "gematria": [], + "meanings": [ + { + "meaning": "FEET, WITH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "LUSEROTH": { + "gematria": [], + "meanings": [ + { + "meaning": "MEANING UNKNOWN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "lutudah": { + "gematria": [], + "meanings": [ + { + "meaning": "thrice great", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "LVCE": { + "gematria": [], + "meanings": [ + { + "meaning": "the North Star", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "LVMRAD": { + "gematria": [], + "meanings": [ + { + "meaning": "all named to the East are the third", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "LZINOPO": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior LUNA of EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "M": { + "gematria": [], + "meanings": [ + { + "meaning": "Tal (M)", + "source": "WE" + }, + { + "meaning": "EXCEPT", + "source": "WE" + }, + { + "meaning": "OF (Schuler)", + "source": "WE" + }, + { + "meaning": "except, 9", + "source": "WE", + "source2": "Holy Table of Practice perimeter" + } + ], + "pronounciations": [] + }, + "MA": { + "gematria": [], + "meanings": [ + { + "meaning": "possess", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "MAAH": { + "gematria": [], + "meanings": [ + { + "meaning": "hidden god", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "MAASI": { + "gematria": [], + "meanings": [ + { + "meaning": "laid up (stored)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MABBERAM": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MABETH": { + "gematria": [], + "meanings": [ + { + "meaning": "expanse, the", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "MABZA": { + "gematria": [], + "meanings": [ + { + "meaning": "coat, the", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MACOM": { + "gematria": [], + "meanings": [ + { + "meaning": "encompass", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MAD": { + "gematria": [ + 100 + ], + "meanings": [ + { + "meaning": "god, your god", + "source": "EMPM" + }, + { + "meaning": "god, your", + "source": "WE" + }, + { + "meaning": "god, of", + "source": "WE" + }, + { + "meaning": "god, your", + "source": "WE" + }, + { + "meaning": "god, of your", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "mah-deh", + "source": "EMPM" + } + ] + }, + "MADIMI": { + "gematria": [], + "meanings": [ + { + "meaning": "DAUGHTER OF DAUGHTER OF LIGHT", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MADIMIEL": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL OF MARS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MADOL": { + "gematria": [], + "meanings": [ + { + "meaning": "God’s creation", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "MADRIAX": { + "gematria": [], + "meanings": [ + { + "meaning": "heaven, you heavens", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MADRID": { + "gematria": [], + "meanings": [ + { + "meaning": "iniquity, her", + "source": "WE" + }, + { + "meaning": "iniquities", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MADRIIAX": { + "gematria": [], + "meanings": [ + { + "meaning": "heaven, you heavens", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MADZILODARP": { + "gematria": [], + "meanings": [ + { + "meaning": "GOD OF STRETCH- FORTH- AND- CONQU 327", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MAGL": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Angel EARTH OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MAGM": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MAH": { + "gematria": [], + "meanings": [ + { + "meaning": "In Darkness", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "MAHAD": { + "gematria": [], + "meanings": [ + { + "meaning": "the third is in darkness", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "MAHORELA": { + "gematria": [], + "meanings": [ + { + "meaning": "dark heavens (crowley)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MAIM": { + "gematria": [], + "meanings": [ + { + "meaning": "continuance", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MAL": { + "gematria": [ + 104 + ], + "meanings": [ + { + "meaning": "arrow", + "source": "EMPM" + }, + { + "meaning": "Shortened spelling of the Enochian letter P, 8", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [ + { + "pronounciation": "mah-el", + "source": "EMPM" + } + ] + }, + "MALADI": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross EARTH OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MALGM": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MALPIRGI": { + "gematria": [], + "meanings": [ + { + "meaning": "fires of life and increase", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MALPRG": { + "gematria": [], + "meanings": [ + { + "meaning": "fire, through-thrusting", + "source": "WE" + }, + { + "meaning": "fiery darts", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MALS": { + "gematria": [], + "meanings": [ + { + "meaning": "ENOCHIAN LETTER P", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MAN": { + "gematria": [], + "meanings": [ + { + "meaning": "root of 'in the mind' or ‘subtle body’", + "source": "WE", + "source2": "Holy Table of Practice perimeter" + } + ], + "pronounciations": [] + }, + "MANCH": { + "gematria": [], + "meanings": [ + { + "meaning": "in the mind of God/Universal Mind", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "MANGET": { + "gematria": [], + "meanings": [ + { + "meaning": "descended of God", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "MANIN": { + "gematria": [], + "meanings": [ + { + "meaning": "mind, in the", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MANO": { + "gematria": [], + "meanings": [ + { + "meaning": "the soul of humanity", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "MAOFFAS": { + "gematria": [], + "meanings": [ + { + "meaning": "measure, not to be measured", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MAPM": { + "gematria": [], + "meanings": [ + { + "meaning": 9639, + "source": "WE" + } + ], + "pronounciations": [] + }, + "MAPSAMA": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL'S NAME, 'TELL THEM'", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MAR": { + "gematria": [], + "meanings": [ + { + "meaning": "the Magickal Childe", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "MARA": { + "gematria": [], + "meanings": [ + { + "meaning": "the Son of Light with the Daughter of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "MARADON": { + "gematria": [], + "meanings": [ + { + "meaning": "the Son of Light unites with the Daughter of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "MARB": { + "gematria": [], + "meanings": [ + { + "meaning": "according", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MARLAN": { + "gematria": [], + "meanings": [ + { + "meaning": "Son of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "MARS": { + "gematria": [], + "meanings": [ + { + "meaning": "the Son of Light is the fourth", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "MARTIBAH": { + "gematria": [], + "meanings": [ + { + "meaning": "the Magickal Childe is the sacrifice unto the higher self", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "MARUNE": { + "gematria": [], + "meanings": [ + { + "meaning": "the Son of Light joins the Daughter of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "MATA": { + "gematria": [], + "meanings": [ + { + "meaning": "millenia", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "MATB": { + "gematria": [], + "meanings": [ + { + "meaning": "thousand, a", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MATHULA": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ZAA", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MATORB": { + "gematria": [], + "meanings": [ + { + "meaning": "echoing (acc. to Laycock)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MAX": { + "gematria": [], + "meanings": [ + { + "meaning": "of the dissolution", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "MAZ": { + "gematria": [ + 99, + 105 + ], + "meanings": [ + { + "meaning": "appearance", + "source": "EMPM" + }, + { + "meaning": "appearance", + "source": "EMPM" + }, + { + "meaning": "SIXTH AETHYR", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "mah-zod", + "source": "EMPM" + }, + { + "pronounciation": "mah-zod", + "source": "EMPM" + } + ] + }, + "ME": { + "gematria": [], + "meanings": [ + { + "meaning": "(name of an angel)", + "source": "EMPM", + "source2": "pg 25" + }, + { + "meaning": "DAUGHTER OF LIGHT", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "Meh", + "source": "EMPM", + "source2": "pg 25" + } + ] + }, + "MECASMAN": { + "gematria": [], + "meanings": [ + { + "meaning": "mighty or powerful soul; highest soul; highest heaven", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "MED": { + "gematria": [], + "meanings": [ + { + "meaning": "ENOCHIAN LETTER O", + "source": "WE" + }, + { + "meaning": "the star of five", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "MEL-": { + "gematria": [], + "meanings": [ + { + "meaning": "F to speedily encounter (schuler)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MERIFRI": { + "gematria": [], + "meanings": [ + { + "meaning": "angel", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MI": { + "gematria": [ + 150 + ], + "meanings": [ + { + "meaning": "power", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "mee", + "source": "EMPM" + } + ] + }, + "MIAM": { + "gematria": [], + "meanings": [ + { + "meaning": "continuance", + "source": "WE" + }, + { + "meaning": "continuance", + "source": "WE" + }, + { + "meaning": "continuance, long cont.", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MIAN": { + "gematria": [], + "meanings": [ + { + "meaning": 3663, + "source": "WE" + } + ], + "pronounciations": [] + }, + "MICALP": { + "gematria": [], + "meanings": [ + { + "meaning": "mightier", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MICALZO": { + "gematria": [], + "meanings": [ + { + "meaning": "power, in power", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MICAOLI": { + "gematria": [], + "meanings": [ + { + "meaning": "mighty", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MICAOLZ": { + "gematria": [], + "meanings": [ + { + "meaning": "mighty", + "source": "WE" + }, + { + "meaning": "mighty", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MICES": { + "gematria": [], + "meanings": [ + { + "meaning": "Countenance of the Daughter of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "MICMA": { + "gematria": [], + "meanings": [ + { + "meaning": "behold", + "source": "WE" + }, + { + "meaning": "behold", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MIINOAG": { + "gematria": [], + "meanings": [ + { + "meaning": "corner, the corners", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MIKETH": { + "gematria": [], + "meanings": [ + { + "meaning": "wisdom", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MINODAL": { + "gematria": [], + "meanings": [ + { + "meaning": "one who is cornered", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "MIR": { + "gematria": [], + "meanings": [ + { + "meaning": "torment, a", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MIRC": { + "gematria": [], + "meanings": [ + { + "meaning": "upon", + "source": "WE" + }, + { + "meaning": "upon", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MIRZIND": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN UTI", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MLU": { + "gematria": [], + "meanings": [ + { + "meaning": "surge, outpouring", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MOLAP": { + "gematria": [ + 143 + ], + "meanings": [ + { + "meaning": "men", + "source": "EMPM" + }, + { + "meaning": "men, of", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "moh-lah-peh", + "source": "EMPM" + } + ] + }, + "MOLPAND": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ICH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MOLUI": { + "gematria": [], + "meanings": [ + { + "meaning": "surges", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MOM": { + "gematria": [], + "meanings": [ + { + "meaning": "moss", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MOMAO": { + "gematria": [], + "meanings": [ + { + "meaning": "crown, the crownsMOMAR crown, to crown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MONASCI": { + "gematria": [], + "meanings": [ + { + "meaning": "name, the great name", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MONONS": { + "gematria": [], + "meanings": [ + { + "meaning": "heart, the", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MOOOAH": { + "gematria": [ + 187 + ], + "meanings": [ + { + "meaning": "it rejoices me", + "source": "EMPM" + }, + { + "meaning": "it repenteth me", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "moh-oh-oh-ah", + "source": "EMPM" + } + ] + }, + "MOR": { + "gematria": [], + "meanings": [ + { + "meaning": "A GOD-NAME OF EARTH TABLET", + "source": "WE" + }, + { + "meaning": "DIAL HCTGA GOD-NAMES OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MOREORGRAN": { + "gematria": [], + "meanings": [ + { + "meaning": "NAME OF AN ANGEL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MOROH": { + "gematria": [], + "meanings": [ + { + "meaning": "the appearance of the 9 woes", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "MORVORGRAN": { + "gematria": [], + "meanings": [ + { + "meaning": "VAR OF MOREORGRAN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MOSPLEH": { + "gematria": [ + 155 + ], + "meanings": [ + { + "meaning": "horns", + "source": "EMPM" + }, + { + "meaning": "horn, the horns", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "moh-seh-pel-eh-heh", + "source": "EMPM" + } + ] + }, + "MOZ": { + "gematria": [ + 123, + 129 + ], + "meanings": [ + { + "meaning": "joy", + "source": "EMPM" + }, + { + "meaning": "joy", + "source": "EMPM" + }, + { + "meaning": "joy", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "moh-zod", + "source": "EMPM" + }, + { + "pronounciation": "moh-zod", + "source": "EMPM" + } + ] + }, + "MOZOD": { + "gematria": [], + "meanings": [ + { + "meaning": "joy of god", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MPH": { + "gematria": [], + "meanings": [ + { + "meaning": "A GOD-NAME OF WATER TABLET", + "source": "WE" + }, + { + "meaning": "ARSL GOD-NAMES OF WATER TABLET", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MSAP": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MSMAP": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MURIFRI": { + "gematria": [], + "meanings": [ + { + "meaning": "angel", + "source": "WE" + } + ], + "pronounciations": [] + }, + "MUZPA": { + "gematria": [], + "meanings": [ + { + "meaning": "These are with Joy", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "N": { + "gematria": [], + "meanings": [ + { + "meaning": "Drun (N)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NA": { + "gematria": [], + "meanings": [ + { + "meaning": "ENOCHIAN LETTER H", + "source": "WE" + }, + { + "meaning": "LORD OF HOSTS, TRINITY", + "source": "WE" + }, + { + "meaning": "(also, the formal name NA)", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "NA-": { + "gematria": [], + "meanings": [ + { + "meaning": "HATH ENOCHIAN LETTER H", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NAB": { + "gematria": [], + "meanings": [ + { + "meaning": "The Infinite God", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "NABAOMI": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ZEN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NACO": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NACRO": { + "gematria": [], + "meanings": [ + { + "meaning": "renewal or resurrection", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "NAD": { + "gematria": [], + "meanings": [ + { + "meaning": "the Holy Spirit", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "NADO": { + "gematria": [], + "meanings": [ + { + "meaning": "fiery God", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "NAGEL": { + "gematria": [], + "meanings": [ + { + "meaning": "the Lord of Hosts is self-begotten", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "NAH": { + "gematria": [], + "meanings": [ + { + "meaning": "glorious", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "NAHA": { + "gematria": [], + "meanings": [ + { + "meaning": "Glory of God", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "NAI": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NALVAGE": { + "gematria": [], + "meanings": [ + { + "meaning": "NAME OF AN ANGEL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NANAEEL": { + "gematria": [], + "meanings": [ + { + "meaning": "POWER, MY POWER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NANBA": { + "gematria": [ + 117 + ], + "meanings": [ + { + "meaning": "thorns", + "source": "EMPM" + }, + { + "meaning": "THORNS", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "nah-en-beh", + "source": "EMPM" + } + ] + }, + "NANTA": { + "gematria": [], + "meanings": [ + { + "meaning": "EARTH NAME, TABLET OF UNION", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NAOCO": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NAP": { + "gematria": [], + "meanings": [ + { + "meaning": "sword", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "NAPEA": { + "gematria": [ + 81 + ], + "meanings": [ + { + "meaning": "two-edged sword", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "nah-peh-ah", + "source": "EMPM" + } + ] + }, + "NAPEAI": { + "gematria": [], + "meanings": [ + { + "meaning": "SWORD, O YE SWORDS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NAPTA": { + "gematria": [], + "meanings": [ + { + "meaning": "SWORD, WITH TWO-EDGED SWORDS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NASMT": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Archangel FIRE OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NAT": { + "gematria": [], + "meanings": [ + { + "meaning": "the Lord", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "NAX": { + "gematria": [], + "meanings": [ + { + "meaning": "Wrath of God", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "NAXT": { + "gematria": [], + "meanings": [ + { + "meaning": "The Ruler of the Earth", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "NAZ": { + "gematria": [], + "meanings": [ + { + "meaning": "pillars", + "source": "WE" + }, + { + "meaning": "pillars", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NAZPS": { + "gematria": [ + 75, + 81 + ], + "meanings": [ + { + "meaning": "a sword", + "source": "EMPM" + }, + { + "meaning": "a sword", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "nah-zod-pess", + "source": "EMPM" + }, + { + "pronounciation": "nah-zod-pess", + "source": "EMPM" + } + ] + }, + "NAZPSAD": { + "gematria": [], + "meanings": [ + { + "meaning": "sword", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NBOZA": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Archangel AIR OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NDAZN": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NDZN": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NE": { + "gematria": [], + "meanings": [ + { + "meaning": "HOLY", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NEC": { + "gematria": [], + "meanings": [ + { + "meaning": "holiness", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "NECRA": { + "gematria": [], + "meanings": [ + { + "meaning": "beginning with the Tree-of-Life", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "NEG": { + "gematria": [], + "meanings": [ + { + "meaning": "Holy", + "source": "WE", + "source2": "Lamen" + } + ], + "pronounciations": [] + }, + "NEH": { + "gematria": [], + "meanings": [ + { + "meaning": "holy God", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "NEICIAB": { + "gematria": [], + "meanings": [ + { + "meaning": "5TH MINISTER OF SOL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NELAPR": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross WATER OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NENNI": { + "gematria": [], + "meanings": [ + { + "meaning": "YOU HAVE BECOME (Crowley)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NEOTPTA": { + "gematria": [], + "meanings": [ + { + "meaning": "2ND MINISTER OF MARS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NEPH": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NES": { + "gematria": [], + "meanings": [ + { + "meaning": "holiness", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "NETAAB": { + "gematria": [ + 80, + 86 + ], + "meanings": [ + { + "meaning": "government", + "source": "EMPM" + }, + { + "meaning": "government", + "source": "EMPM" + }, + { + "meaning": "government", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "neh-tah-ah-beh", + "source": "EMPM" + }, + { + "pronounciation": "neh-tah-ah-beh", + "source": "EMPM" + } + ] + }, + "NGRSATEM": { + "gematria": [], + "meanings": [ + { + "meaning": "Unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NHDD": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NHODD": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NI": { + "gematria": [], + "meanings": [ + { + "meaning": 28, + "source": "WE" + } + ], + "pronounciations": [] + }, + "NIA": { + "gematria": [], + "meanings": [ + { + "meaning": "24TH AETHYR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NIDALI": { + "gematria": [], + "meanings": [ + { + "meaning": "noise, your noises", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NIGRANA": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN DES", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NIIS": { + "gematria": [ + 177 + ], + "meanings": [ + { + "meaning": "to come", + "source": "EMPM" + }, + { + "meaning": "come ye", + "source": "WE" + }, + { + "meaning": "come", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "nee-ee-ess", + "source": "EMPM" + } + ] + }, + "NIISO": { + "gematria": [], + "meanings": [ + { + "meaning": "come away", + "source": "WE" + }, + { + "meaning": "come away", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NIMB": { + "gematria": [], + "meanings": [ + { + "meaning": "season", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NIZ": { + "gematria": [], + "meanings": [ + { + "meaning": "’28 of them’ or ‘they, the 28’", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "NLINZVB": { + "gematria": [], + "meanings": [ + { + "meaning": "2ND MINISTER OF VENUS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NLLRLNA": { + "gematria": [], + "meanings": [ + { + "meaning": "6TH MINISTER OF VENUS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NLRX": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Angel FIRE OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NN": { + "gematria": [], + "meanings": [ + { + "meaning": "root of ‘interiority: within, inside, self-hood', power, ‘my power', thorns, the 'Earth Name, Tablet of Union' (NANAEEL)", + "source": "WE", + "source2": "Holy Table of Practice perimeter" + } + ], + "pronounciations": [] + }, + "NO": { + "gematria": [], + "meanings": [ + { + "meaning": "the hexagram", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "NOALMR": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross AIR OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NOALN": { + "gematria": [], + "meanings": [ + { + "meaning": "BECOME, MAY BE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NOAN": { + "gematria": [], + "meanings": [ + { + "meaning": "BECOME, YOU ARE BECOME", + "source": "WE" + }, + { + "meaning": "BECOME, THUS YOU ARE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NOAR": { + "gematria": [], + "meanings": [ + { + "meaning": "BECOME, IS BECOME", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NOAS": { + "gematria": [], + "meanings": [ + { + "meaning": "BECOME", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NOASMI": { + "gematria": [], + "meanings": [ + { + "meaning": "BECOME, LET THEM BECOME", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NOBLOH": { + "gematria": [], + "meanings": [ + { + "meaning": "PALMS, THE (OF HANDS)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NOCAMAL": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN LIT", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NOCIABI": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN OXO", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NOCNC": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Archangel EARTH OF", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NOCO": { + "gematria": [], + "meanings": [ + { + "meaning": "SERVANT, THE", + "source": "WE" + }, + { + "meaning": "SERVANT, THE", + "source": "WE" + }, + { + "meaning": "MINISTER, THE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NOGAHEL": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL OF VENUS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NOGES": { + "gematria": [], + "meanings": [ + { + "meaning": "the Hexagram is not the fourth", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "NOIB": { + "gematria": [ + 145 + ], + "meanings": [ + { + "meaning": "yes, affirmation", + "source": "EMPM" + }, + { + "meaning": "YEA", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "noh-ee-beh", + "source": "EMPM" + } + ] + }, + "NOL": { + "gematria": [], + "meanings": [ + { + "meaning": "the first hexagram", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "NOMIG": { + "gematria": [], + "meanings": [ + { + "meaning": "EVEN AS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NONCA": { + "gematria": [], + "meanings": [ + { + "meaning": "UNTO YOU", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NONCF": { + "gematria": [], + "meanings": [ + { + "meaning": "YOU", + "source": "WE" + }, + { + "meaning": "YOU", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NONCI": { + "gematria": [], + "meanings": [ + { + "meaning": "YOU, O YOU", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NONCP": { + "gematria": [], + "meanings": [ + { + "meaning": "YOU, FOR YOU", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NONNIS": { + "gematria": [], + "meanings": [ + { + "meaning": "You come away", + "source": "WE", + "source2": "Lamen" + } + ], + "pronounciations": [] + }, + "NOONMAN": { + "gematria": [], + "meanings": [ + { + "meaning": "5TH MINISTER OF MARS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NOQOL": { + "gematria": [ + 158 + ], + "meanings": [ + { + "meaning": "servants", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "noh-quo-leh", + "source": "EMPM" + } + ] + }, + "NOR": { + "gematria": [], + "meanings": [ + { + "meaning": "SONS, YOUNOR SONS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NOROMI": { + "gematria": [], + "meanings": [ + { + "meaning": "SONS, O YOU SONS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NORZ": { + "gematria": [], + "meanings": [ + { + "meaning": "SIX", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NOSTOAH": { + "gematria": [], + "meanings": [ + { + "meaning": "IT WAS (?)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NOT": { + "gematria": [], + "meanings": [ + { + "meaning": "inside", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "NOTHOA": { + "gematria": [], + "meanings": [ + { + "meaning": "MIDST, IN THE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NPHRA": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Archangel WATER OF", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NPNT": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NPRNT": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NRPCRRB": { + "gematria": [], + "meanings": [ + { + "meaning": "5TH MINISTER OF MERCURY", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NRRCPRN": { + "gematria": [], + "meanings": [ + { + "meaning": "5TH MINISTER OF LUNA", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NRSOGOO": { + "gematria": [], + "meanings": [ + { + "meaning": "4TH MINISTER OF LUNA", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NRZFM": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross FIRE OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "NUAM": { + "gematria": [], + "meanings": [ + { + "meaning": "CONTINUANCE, VAR ON 'MIAM'?", + "source": "WE" + } + ], + "pronounciations": [] + }, + "O": { + "gematria": [], + "meanings": [ + { + "meaning": "Med (O)", + "source": "WE" + }, + { + "meaning": "5, this", + "source": "WE" + }, + { + "meaning": "the Holy Pentagram", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "OADO": { + "gematria": [ + 70 + ], + "meanings": [ + { + "meaning": "to wave", + "source": "EMPM" + }, + { + "meaning": "WEAVE", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "oh-ah-doh", + "source": "EMPM" + } + ] + }, + "OADRIAX": { + "gematria": [], + "meanings": [ + { + "meaning": "HEAVENS, THE LOWER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OAI": { + "gematria": [], + "meanings": [ + { + "meaning": "AMONG, VAR ON AAI", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OAID": { + "gematria": [], + "meanings": [ + { + "meaning": "GOD, OF GOD", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OALI": { + "gematria": [], + "meanings": [ + { + "meaning": "PLACE;PUT, I HAVE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OALO": { + "gematria": [], + "meanings": [ + { + "meaning": "I am", + "source": "WE", + "source2": "Lamen" + } + ], + "pronounciations": [] + }, + "OANIO": { + "gematria": [], + "meanings": [ + { + "meaning": "MOMENT, OF A", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OANR": { + "gematria": [], + "meanings": [ + { + "meaning": "Angel, companion of Roan. Also Oacnr.", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OB": { + "gematria": [], + "meanings": [ + { + "meaning": "(entry not defined)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OBAUA": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross WATER OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OBELISON": { + "gematria": [], + "meanings": [ + { + "meaning": "PLEASANT DELIVERER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OBELISONG": { + "gematria": [], + "meanings": [ + { + "meaning": "AS PLEASANT DELIVERERS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OBGOTA": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross AIR OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OBL": { + "gematria": [], + "meanings": [ + { + "meaning": "garland", + "source": "WE", + "source2": "Holy Table of Practice perimeter" + } + ], + "pronounciations": [] + }, + "OBLOC": { + "gematria": [], + "meanings": [ + { + "meaning": "GARLAND, A", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OBOLEH": { + "gematria": [], + "meanings": [ + { + "meaning": "GARMENTS, YOUR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OBVAORS": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN UTI", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OBZa": { + "gematria": [ + 44, + 50 + ], + "meanings": [ + { + "meaning": "one half, dual", + "source": "EMPM" + }, + { + "meaning": "one half, dual", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "oh-beh-zodah", + "source": "EMPM" + }, + { + "pronounciation": "oh-beh-zodah", + "source": "EMPM" + } + ] + }, + "OBZA": { + "gematria": [], + "meanings": [ + { + "meaning": "HALF, A", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OCANM": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OCCODON": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN LIL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OCNC": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Angel EARTH OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OCNM": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OD": { + "gematria": [], + "meanings": [ + { + "meaning": "AND", + "source": "WE" + }, + { + "meaning": "NOR (AND)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ODDIORG": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ZIP", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ODIDOS": { + "gematria": [], + "meanings": [ + { + "meaning": "she who awakens the eld of the king", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ODO": { + "gematria": [ + 64 + ], + "meanings": [ + { + "meaning": "to open", + "source": "EMPM" + }, + { + "meaning": "OPEN", + "source": "WE" + }, + { + "meaning": "OPEN", + "source": "WE" + }, + { + "meaning": "OPENS", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "oh-doh", + "source": "EMPM" + } + ] + }, + "ODRAXTI": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN RII", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OECRIMI": { + "gematria": [], + "meanings": [ + { + "meaning": "SINGING PRAISES", + "source": "WE" + }, + { + "meaning": "SINGING PRAISES", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OEEOOEZ": { + "gematria": [], + "meanings": [ + { + "meaning": "5TH MINISTER OF VENUS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OEMATENODAH": { + "gematria": [], + "meanings": [ + { + "meaning": "at the start of the millennia, the angel of death", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "OESNGLE": { + "gematria": [], + "meanings": [ + { + "meaning": "1ST MINISTER OF LUNA", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OFAFAFE": { + "gematria": [], + "meanings": [ + { + "meaning": "VIAL, VAR ON EFAFAFE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OFEKUFA": { + "gematria": [], + "meanings": [ + { + "meaning": "ELEVATED TO (Crowley)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OGES": { + "gematria": [], + "meanings": [ + { + "meaning": "with the 4th", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "OGHE": { + "gematria": [], + "meanings": [ + { + "meaning": "the fourth begotten Son of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "OGI": { + "gematria": [], + "meanings": [ + { + "meaning": "‘with this’", + "source": "WE", + "source2": "Lamen" + } + ], + "pronounciations": [] + }, + "OH": { + "gematria": [], + "meanings": [ + { + "meaning": "Root of OHIO; woe", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "OHA": { + "gematria": [], + "meanings": [ + { + "meaning": "in or with woe", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "OHE": { + "gematria": [], + "meanings": [ + { + "meaning": "woe of the Daughter of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "OHELOKA": { + "gematria": [], + "meanings": [ + { + "meaning": "DUKE, (Crowley's trans.)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OHIO": { + "gematria": [], + "meanings": [ + { + "meaning": "WOE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OHOOOHAATAN": { + "gematria": [], + "meanings": [ + { + "meaning": "GREAT ELEMENTAL KING OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OHORELA": { + "gematria": [], + "meanings": [ + { + "meaning": "LAW, I MADE A LAW", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OI": { + "gematria": [], + "meanings": [ + { + "meaning": "THIS", + "source": "WE" + }, + { + "meaning": "THIS", + "source": "WE", + "source2": "Holy Table of Practice perimeter" + } + ], + "pronounciations": [] + }, + "OIAD": { + "gematria": [ + 100 + ], + "meanings": [ + { + "meaning": "god, the just", + "source": "EMPM" + }, + { + "meaning": "GOD, OF", + "source": "WE" + }, + { + "meaning": "JUST, OF THE JUST", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "oh-ee-ah-deh", + "source": "EMPM" + } + ] + }, + "OIAS": { + "gematria": [], + "meanings": [ + { + "meaning": "WAS, IS, AND SHALL BE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OIIIT": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross EARTH OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OIP": { + "gematria": [], + "meanings": [ + { + "meaning": "A GOD-NAME OF FIRE TABLET", + "source": "WE" + }, + { + "meaning": "TEAA PDOCE GOD-NAMES OF FIRE TABLET", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OIT": { + "gematria": [], + "meanings": [ + { + "meaning": "THIS IS; THIS IS IT, THAT", + "source": "WE" + }, + { + "meaning": "THIS IS; THIS IS IT, THAT", + "source": "WE", + "source2": "Table of 12" + }, + { + "meaning": "THIS IS; THIS IS IT, THAT, God", + "source": "WE", + "source2": "Holy Table of Practice perimeter" + } + ], + "pronounciations": [] + }, + "OL": { + "gematria": [ + 38 + ], + "meanings": [ + { + "meaning": "to make, maker", + "source": "EMPM" + }, + { + "meaning": "I (poss. \"The Maker\"- see OLN,", + "source": "WE" + }, + { + "meaning": "IN THE 24TH PART", + "source": "WE" + }, + { + "meaning": "(NONE SHOWN)", + "source": "WE" + }, + { + "meaning": 24, + "source": "WE" + }, + { + "meaning": "MAKE, I MADE", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "oh-el", + "source": "EMPM" + } + ] + }, + "OLAAD": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross EARTH OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OLAHO": { + "gematria": [], + "meanings": [ + { + "meaning": "FOR THE SECOND TIME (Crowley)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OLANI": { + "gematria": [], + "meanings": [ + { + "meaning": "FOR TWO TIMES", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OLLAR": { + "gematria": [ + 152 + ], + "meanings": [ + { + "meaning": "man", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "oh-leh-lar", + "source": "EMPM" + } + ] + }, + "OLLOG": { + "gematria": [ + 84 + ], + "meanings": [ + { + "meaning": "men", + "source": "EMPM" + }, + { + "meaning": "MEN", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "oh-el-loh-geh", + "source": "EMPM" + } + ] + }, + "OLLOR": { + "gematria": [], + "meanings": [ + { + "meaning": "MAN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OLN": { + "gematria": [], + "meanings": [ + { + "meaning": "MADE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OLNA": { + "gematria": [], + "meanings": [ + { + "meaning": "created within", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "OLOAG": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross AIR OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OLONTAX": { + "gematria": [], + "meanings": [ + { + "meaning": "man's twin star", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "OLORA": { + "gematria": [ + 174 + ], + "meanings": [ + { + "meaning": "man", + "source": "EMPM" + }, + { + "meaning": "MAN, OF MAN", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "oh-loh-rah", + "source": "EMPM" + } + ] + }, + "OLPAGED": { + "gematria": [], + "meanings": [ + { + "meaning": "Zodiacal King SCORPIO", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OLPIRT": { + "gematria": [], + "meanings": [ + { + "meaning": "LIGHT", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OLPRT": { + "gematria": [ + 150, + 156 + ], + "meanings": [ + { + "meaning": "light", + "source": "EMPM" + }, + { + "meaning": "light", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "oh-el-par-teh", + "source": "EMPM" + }, + { + "pronounciation": "oh-el-par-teh", + "source": "EMPM" + } + ] + }, + "OM": { + "gematria": [ + 120 + ], + "meanings": [ + { + "meaning": "to know, understand", + "source": "EMPM" + }, + { + "meaning": "KNOW", + "source": "WE" + }, + { + "meaning": "UNDERSTAND", + "source": "WE" + }, + { + "meaning": "THE UNDERSTANDING", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "oh-meh", + "source": "EMPM" + } + ] + }, + "OMA": { + "gematria": [ + 126 + ], + "meanings": [ + { + "meaning": "understanding", + "source": "EMPM" + }, + { + "meaning": "UNDERSTANDING", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "oh-mah", + "source": "EMPM" + } + ] + }, + "OMAGG": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OMAGRAP": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN POP", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OMAOAS": { + "gematria": [], + "meanings": [ + { + "meaning": "NAMES, THEIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OMAX": { + "gematria": [], + "meanings": [ + { + "meaning": "KNOW, KNOWEST", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OMEBB": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross WATER OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OMGG": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OMICAOLZ": { + "gematria": [], + "meanings": [ + { + "meaning": "MIGHTY, BE MIGHTY", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OMLO": { + "gematria": [], + "meanings": [ + { + "meaning": "KNOWLEDGE OF THE FIRST", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OMP": { + "gematria": [], + "meanings": [ + { + "meaning": "UNDERSTANDING", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OMSOMNA": { + "gematria": [], + "meanings": [ + { + "meaning": "MEANING UNKNOWN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ON": { + "gematria": [], + "meanings": [ + { + "meaning": "MADE, BUILT", + "source": "WE" + }, + { + "meaning": "MADE, BUILT", + "source": "WE", + "source2": "Holy Table of Practice perimeter" + }, + { + "meaning": "Made, built", + "source": "WE", + "source2": "Lamen" + } + ], + "pronounciations": [] + }, + "ONEDON": { + "gematria": [], + "meanings": [ + { + "meaning": "completion", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ONEDPON": { + "gematria": [], + "meanings": [ + { + "meaning": "4TH MINISTER OF MARS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ONIXDAR": { + "gematria": [], + "meanings": [ + { + "meaning": "Proclaiming", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ONIZIMP": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN TOR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "onphe": { + "gematria": [], + "meanings": [ + { + "meaning": "begotten", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ONR": { + "gematria": [], + "meanings": [ + { + "meaning": "Motivation, inspiration—‘inertia’", + "source": "WE", + "source2": "Lamen" + } + ], + "pronounciations": [] + }, + "OOANAMB": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN UTA", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OOANOAN": { + "gematria": [], + "meanings": [ + { + "meaning": "EYES, IN THEIR EYES", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OOAONA": { + "gematria": [], + "meanings": [ + { + "meaning": "EYES", + "source": "WE" + }, + { + "meaning": "EYES", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OODPZ": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OOE": { + "gematria": [], + "meanings": [ + { + "meaning": "‘archetypal man’; ‘makes man’; ‘making man’", + "source": "WE", + "source2": "Table of 12" + }, + { + "meaning": "‘archetypal man’; ‘makes man’; ‘making man’", + "source": "WE", + "source2": "Holy Table of Practice perimeter" + } + ], + "pronounciations": [] + }, + "OOGE": { + "gematria": [], + "meanings": [ + { + "meaning": "CHAMBER, FOR THE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OOGOSRB": { + "gematria": [], + "meanings": [ + { + "meaning": "4TH MINISTER OF BLISDON", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OOGOSRS": { + "gematria": [], + "meanings": [ + { + "meaning": "4TH MINISTER OF MERCURY", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OOPZ": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OP": { + "gematria": [], + "meanings": [ + { + "meaning": 22, + "source": "WE" + } + ], + "pronounciations": [] + }, + "OPAMN": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OPANA": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OPHES": { + "gematria": [], + "meanings": [ + { + "meaning": "the 22 by 4", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "OPHIDE": { + "gematria": [], + "meanings": [ + { + "meaning": "coitus, of riding, rides", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "OPMACAS": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN DEO", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OPMN": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OPMNIR": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross FIRE OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OPNA": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OQ": { + "gematria": [], + "meanings": [ + { + "meaning": "EXCEPT IN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OR": { + "gematria": [], + "meanings": [ + { + "meaning": "ENOCHIAN LETTER F", + "source": "WE" + }, + { + "meaning": "[visit, visit us]—appear, ‘appear before us’", + "source": "WE", + "source2": "Lamen" + } + ], + "pronounciations": [] + }, + "ORA": { + "gematria": [], + "meanings": [ + { + "meaning": "third,the third", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ORCANIR": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN NIA", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ORCANOR": { + "gematria": [], + "meanings": [ + { + "meaning": "the mighty manifest", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ORDA": { + "gematria": [], + "meanings": [ + { + "meaning": "will indwell", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ORDAN": { + "gematria": [], + "meanings": [ + { + "meaning": "manifest", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ORH": { + "gematria": [], + "meanings": [ + { + "meaning": "NAME OF A SPIRIT", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ORIPAT": { + "gematria": [], + "meanings": [ + { + "meaning": "life shall not form", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ORLO": { + "gematria": [], + "meanings": [ + { + "meaning": "Initiation, Visitation; n.Initiate, visit; v.", + "source": "WE", + "source2": "Table of 12" + } + ], + "pronounciations": [] + }, + "ORMATENODAH": { + "gematria": [], + "meanings": [ + { + "meaning": "at the start of the millennia, the angel of death", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ORMN": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF EARTHORO A GOD-NAME OF AIR TABLET", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ORNO": { + "gematria": [], + "meanings": [ + { + "meaning": "divine visitation", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ORO": { + "gematria": [], + "meanings": [ + { + "meaning": "[the] third man", + "source": "WE", + "source2": "Liber Loagaeth" + }, + { + "meaning": "IBAH AOZPI GOD-NAMES OF AIR TABLET", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OROCH": { + "gematria": [], + "meanings": [ + { + "meaning": "UNDER YOU", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OROCHA": { + "gematria": [], + "meanings": [ + { + "meaning": "UNDERNEATH YOU", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OROPHAS": { + "gematria": [], + "meanings": [ + { + "meaning": "[I will] give in secret", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ORPANIB": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ZAA", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ORPMN": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ORRI": { + "gematria": [], + "meanings": [ + { + "meaning": "STONE, BARREN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ORS": { + "gematria": [ + 137 + ], + "meanings": [ + { + "meaning": "darkness", + "source": "EMPM" + }, + { + "meaning": "DARKNESS, WITH", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "oh-ress", + "source": "EMPM" + } + ] + }, + "ORSBA": { + "gematria": [ + 148 + ], + "meanings": [ + { + "meaning": "drunken, intoxicated", + "source": "EMPM" + }, + { + "meaning": "DRUNKEN", + "source": "WE" + }, + { + "meaning": "DRUNKEN", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "oh-ress-bah", + "source": "EMPM" + } + ] + }, + "ORSCA": { + "gematria": [], + "meanings": [ + { + "meaning": "BUILDINGS, THE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ORSCOR": { + "gematria": [], + "meanings": [ + { + "meaning": "DRYNESS, WITH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ORTH": { + "gematria": [], + "meanings": [ + { + "meaning": "ENOCHIAN LETTER F", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OS": { + "gematria": [], + "meanings": [ + { + "meaning": 12, + "source": "WE" + }, + { + "meaning": 12, + "source": "WE", + "source2": "Holy Table of Practice perimeter" + } + ], + "pronounciations": [] + }, + "OSCH": { + "gematria": [], + "meanings": [ + { + "meaning": "are 12 (12 are); let there be 12", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "OSF": { + "gematria": [], + "meanings": [ + { + "meaning": "DISCORD", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OSHE": { + "gematria": [], + "meanings": [ + { + "meaning": "The 12 Lights", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "OSSNGLE": { + "gematria": [], + "meanings": [ + { + "meaning": "FIRST MINISTER OF HAGONEL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OSSON": { + "gematria": [], + "meanings": [ + { + "meaning": "the 12 reign [over]", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "OTHIL": { + "gematria": [ + 102, + 108 + ], + "meanings": [ + { + "meaning": "seat", + "source": "EMPM" + }, + { + "meaning": "seat", + "source": "EMPM" + }, + { + "meaning": "SEAT, THE SEATS", + "source": "WE" + }, + { + "meaning": "SEAT, I HAVE SEATED", + "source": "WE" + }, + { + "meaning": "SEAT, THE SEATS", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "oh-teh-hee-el", + "source": "EMPM" + }, + { + "pronounciation": "oh-teh-hee-el", + "source": "EMPM" + } + ] + }, + "OTOI": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OTROI": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OUCHO": { + "gematria": [], + "meanings": [ + { + "meaning": "CONFOUND, LET IT CONFOUND", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OVOARS": { + "gematria": [], + "meanings": [ + { + "meaning": "CENTER, TO THE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OVOF": { + "gematria": [ + 133 + ], + "meanings": [ + { + "meaning": "to be magnified", + "source": "EMPM" + }, + { + "meaning": "MAGNIFY, MAY BE MAGNIFIED", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "oh-voh-feh", + "source": "EMPM" + } + ] + }, + "OX": { + "gematria": [], + "meanings": [ + { + "meaning": 26, + "source": "WE" + } + ], + "pronounciations": [] + }, + "OXAMAX": { + "gematria": [], + "meanings": [ + { + "meaning": "the 26 comprise the all", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "OXEX": { + "gematria": [], + "meanings": [ + { + "meaning": "VOMIT OUT", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OXIAYAL": { + "gematria": [], + "meanings": [ + { + "meaning": "SEAT, THE MIGHTY SEAT", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OXLOPAR": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN BAG", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OXO": { + "gematria": [], + "meanings": [ + { + "meaning": "FIFTEENTH AETHYR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OXOX": { + "gematria": [], + "meanings": [ + { + "meaning": "MEANING UNKNOWN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OYAUB": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OYUB": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OZAZM": { + "gematria": [], + "meanings": [ + { + "meaning": "MAKE ME", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OZAZMA": { + "gematria": [], + "meanings": [ + { + "meaning": "MAKE US", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OZIDAIA": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN LIN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OZIEN": { + "gematria": [], + "meanings": [ + { + "meaning": "HANDS, MY", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OZOL": { + "gematria": [], + "meanings": [ + { + "meaning": "HEADS, THEIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "OZONGON": { + "gematria": [], + "meanings": [ + { + "meaning": "WINDS, MANIFOLD", + "source": "WE" + } + ], + "pronounciations": [] + }, + "P": { + "gematria": [], + "meanings": [ + { + "meaning": "Mals (P)", + "source": "WE" + }, + { + "meaning": 8, + "source": "WE" + } + ], + "pronounciations": [] + }, + "PA": { + "gematria": [], + "meanings": [ + { + "meaning": "keep", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PAAOX": { + "gematria": [], + "meanings": [ + { + "meaning": "remain", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PAAOXT": { + "gematria": [], + "meanings": [ + { + "meaning": "remain, let it remain", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PAAX": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PACADABAAH": { + "gematria": [], + "meanings": [ + { + "meaning": "profess the truth", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PACADUASAM": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PACAPH": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PACASNA": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ARN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PACHAD": { + "gematria": [], + "meanings": [ + { + "meaning": "being of the holy trinity", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PACO": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PACOC": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PADGZE": { + "gematria": [], + "meanings": [ + { + "meaning": "justice from divine power without defect", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PAEB": { + "gematria": [ + 30 + ], + "meanings": [ + { + "meaning": "oak", + "source": "EMPM" + }, + { + "meaning": "oak, an", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "pah-eh-beh", + "source": "EMPM" + } + ] + }, + "PAGE": { + "gematria": [ + 33 + ], + "meanings": [ + { + "meaning": "to rest", + "source": "EMPM" + }, + { + "meaning": "rest", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "pah-geh", + "source": "EMPM" + } + ] + }, + "PAGE-": { + "gematria": [], + "meanings": [ + { + "meaning": "IP rest not", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PAH": { + "gematria": [], + "meanings": [ + { + "meaning": "the Ogdoad (eightfold star)", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PAID": { + "gematria": [ + 79 + ], + "meanings": [ + { + "meaning": "always", + "source": "EMPM" + }, + { + "meaning": "always", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "pah-ee-deh", + "source": "EMPM" + } + ] + }, + "PAL": { + "gematria": [], + "meanings": [ + { + "meaning": "ENOCHIAN LETTER X", + "source": "WE" + }, + { + "meaning": "dissolution", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PALA": { + "gematria": [], + "meanings": [ + { + "meaning": "TWO (SEPARATED), PAIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PALAM": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross WATER OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PALCE": { + "gematria": [], + "meanings": [ + { + "meaning": "All is in the One", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PALCO": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PALGEH": { + "gematria": [], + "meanings": [ + { + "meaning": "thou art separated", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PALI": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PALME": { + "gematria": [], + "meanings": [ + { + "meaning": "dissolves into Daughter of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PALMES": { + "gematria": [], + "meanings": [ + { + "meaning": "dissolution into the Daughter of Daughter of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PALO": { + "gematria": [], + "meanings": [ + { + "meaning": "dissolves into man", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PALSE": { + "gematria": [], + "meanings": [ + { + "meaning": "raging fire", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PAM": { + "gematria": [ + 105 + ], + "meanings": [ + { + "meaning": "beginning", + "source": "EMPM" + }, + { + "meaning": "8 unto into 9", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [ + { + "pronounciation": "pah-em", + "source": "EMPM" + } + ] + }, + "PAMBT": { + "gematria": [], + "meanings": [ + { + "meaning": "unto me", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PAMPHES": { + "gematria": [], + "meanings": [ + { + "meaning": "Babalon astride the Beast", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PAMPHICA": { + "gematria": [], + "meanings": [ + { + "meaning": "infernal mother", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PAMPHICAS": { + "gematria": [], + "meanings": [ + { + "meaning": "mean.unk. contemptuous tone", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PAN": { + "gematria": [], + "meanings": [ + { + "meaning": "Fire pouring down", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PANDAS": { + "gematria": [], + "meanings": [ + { + "meaning": "a thousand angels keep holy", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PANGEPI": { + "gematria": [], + "meanings": [ + { + "meaning": "She who is NOT, pouring down", + "source": "WE", + "source2": "Liber Loagaeth" + }, + { + "meaning": "She who is NOT, pouring down", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PANLI": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PANOSCH": { + "gematria": [], + "meanings": [ + { + "meaning": "there are 12 pouring down", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PANPIR": { + "gematria": [], + "meanings": [ + { + "meaning": "pouring down", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PAOC": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF AIRPAOMBD members, her (poss.\"limbs\"?)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PAPBOR": { + "gematria": [], + "meanings": [ + { + "meaning": "remember, to this remembrance", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PAPHRES": { + "gematria": [], + "meanings": [ + { + "meaning": "praising the Lord of Hosts in rememberance", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PAPNOR": { + "gematria": [], + "meanings": [ + { + "meaning": "to this remembrance (alt.sp.)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PAR": { + "gematria": [], + "meanings": [ + { + "meaning": "in them", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PARACH": { + "gematria": [], + "meanings": [ + { + "meaning": "equal", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PARACLEDA": { + "gematria": [], + "meanings": [ + { + "meaning": "wedding, for a", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PARADIAL": { + "gematria": [], + "meanings": [ + { + "meaning": "dwellings, living", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PARADIZ": { + "gematria": [ + 194 + ], + "meanings": [ + { + "meaning": "a virgin", + "source": "EMPM" + }, + { + "meaning": "virgins", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "pah-rah-dee-zod", + "source": "EMPM" + } + ] + }, + "PARAOAN": { + "gematria": [], + "meanings": [ + { + "meaning": "part in lin", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PARM": { + "gematria": [], + "meanings": [ + { + "meaning": "run", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PARMGI": { + "gematria": [], + "meanings": [ + { + "meaning": "run, let it run", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PARNIX": { + "gematria": [], + "meanings": [ + { + "meaning": "the daughters reside in the 4 th", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PARSA": { + "gematria": [], + "meanings": [ + { + "meaning": "with the Son of Son of Light in the 4 th", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PARSODAN": { + "gematria": [], + "meanings": [ + { + "meaning": "the Son of Son of Light (Mercury) in the 4 th", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PART": { + "gematria": [], + "meanings": [ + { + "meaning": "also in them", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PARZIBA": { + "gematria": [], + "meanings": [ + { + "meaning": "part in chr", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PASBS": { + "gematria": [], + "meanings": [ + { + "meaning": "daughters, the", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PASCOMB": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN LIL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PASDAES": { + "gematria": [], + "meanings": [ + { + "meaning": "profess the truth", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PASHS": { + "gematria": [ + 30 + ], + "meanings": [ + { + "meaning": "children", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "pah-seh-ess", + "source": "EMPM" + } + ] + }, + "PASMT": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Archangel FIRE OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PATAX": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PATRALX": { + "gematria": [], + "meanings": [ + { + "meaning": "ROCK", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PAULACARP": { + "gematria": [], + "meanings": [ + { + "meaning": "NAME OF AN EVIL SPIRIT", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PAX": { + "gematria": [], + "meanings": [ + { + "meaning": "keep the one", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PAZ": { + "gematria": [], + "meanings": [ + { + "meaning": "FOURTH AETHYR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PD": { + "gematria": [], + "meanings": [ + { + "meaning": 33, + "source": "WE" + } + ], + "pronounciations": [] + }, + "PDOCE": { + "gematria": [], + "meanings": [ + { + "meaning": "A GOD-NAME OF FIRE TABLET", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PE": { + "gematria": [], + "meanings": [ + { + "meaning": "PE, ENOCHIAN LETTER 'B'", + "source": "WE" + }, + { + "meaning": "The eight Daughters of Light", + "source": "WE", + "source2": "Lamen" + } + ], + "pronounciations": [] + }, + "PELE": { + "gematria": [], + "meanings": [ + { + "meaning": "HE WHO WORKS WONDERS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PELEH": { + "gematria": [], + "meanings": [ + { + "meaning": "MEANING UNKNOWN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PENGON": { + "gematria": [], + "meanings": [ + { + "meaning": "the voice of the eight Daughters of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PEOAL": { + "gematria": [], + "meanings": [ + { + "meaning": 69636, + "source": "WE" + } + ], + "pronounciations": [] + }, + "PERAL": { + "gematria": [], + "meanings": [ + { + "meaning": "GARNISH, ARE GARNISHED", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PERIPSAX": { + "gematria": [], + "meanings": [ + { + "meaning": "HEAVENS, WITH THE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PERIPSOL": { + "gematria": [], + "meanings": [ + { + "meaning": "HEAVENS, OF THE", + "source": "WE" + }, + { + "meaning": "HEAVENS, IN THE BRIGHTNESS OF", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PHAMA": { + "gematria": [], + "meanings": [ + { + "meaning": "I WILL GIVE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PHAMAH": { + "gematria": [], + "meanings": [ + { + "meaning": "GIVE, VAR ON 'PHAMA'", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PHANAEL": { + "gematria": [], + "meanings": [ + { + "meaning": "NAME OF AN ANGEL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PHAR": { + "gematria": [], + "meanings": [ + { + "meaning": "surrender", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PHE": { + "gematria": [], + "meanings": [ + { + "meaning": "the eight Daughters of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PHRA": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Angel WATER OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PI": { + "gematria": [], + "meanings": [ + { + "meaning": "PLACE", + "source": "WE" + }, + { + "meaning": "SHEPIAD YOUR GOD (alt.sp.)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PIADPH": { + "gematria": [], + "meanings": [ + { + "meaning": "JAWS, IN THE DEPTHS OF", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PIAMOS": { + "gematria": [], + "meanings": [ + { + "meaning": "RIGHTEOUSNESS, OF", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PIAP": { + "gematria": [ + 84 + ], + "meanings": [ + { + "meaning": "balance, scale", + "source": "EMPM" + }, + { + "meaning": "BALANCE, THE", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "pee-ah-peh", + "source": "EMPM" + } + ] + }, + "PIBLIAR": { + "gematria": [], + "meanings": [ + { + "meaning": "PLACES OF COMFORT", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PIDIAI": { + "gematria": [], + "meanings": [ + { + "meaning": "MARBLE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PILAH": { + "gematria": [], + "meanings": [ + { + "meaning": "MOREOVER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PILD": { + "gematria": [], + "meanings": [ + { + "meaning": "CONTINUALLY", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PILZIN": { + "gematria": [], + "meanings": [ + { + "meaning": "FIRMAMENT OF WATERS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PINZU-": { + "gematria": [], + "meanings": [ + { + "meaning": "A MEANING UNKNOWN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PIR": { + "gematria": [ + 169 + ], + "meanings": [ + { + "meaning": "bright", + "source": "EMPM" + }, + { + "meaning": "HOLY ONES", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "pee-ar", + "source": "EMPM" + } + ] + }, + "PIRIPSON": { + "gematria": [], + "meanings": [ + { + "meaning": "HEAVEN, THE THIRD", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PL": { + "gematria": [], + "meanings": [ + { + "meaning": "partakers, ‘as many’", + "source": "WE", + "source2": "Holy Table of Practice perimeter" + } + ], + "pronounciations": [] + }, + "PLAPLI": { + "gematria": [ + 100 + ], + "meanings": [ + { + "meaning": "users, partakers", + "source": "EMPM" + }, + { + "meaning": "PARTAKERS", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "peh-lah-peh-lee", + "source": "EMPM" + } + ] + }, + "PLIGNASE": { + "gematria": [], + "meanings": [ + { + "meaning": "The eternal cry", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PLOSI": { + "gematria": [], + "meanings": [ + { + "meaning": "AS MANY", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PMOX": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PMZOX": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "POAMAL": { + "gematria": [ + 149 + ], + "meanings": [ + { + "meaning": "palace", + "source": "EMPM" + }, + { + "meaning": "PALACE, OF YOUR", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "poh-ah-mal", + "source": "EMPM" + } + ] + }, + "POCISNI": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN BAG", + "source": "WE" + } + ], + "pronounciations": [] + }, + "POCNC": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Archangel EARTH OF", + "source": "WE" + } + ], + "pronounciations": [] + }, + "POHO": { + "gematria": [], + "meanings": [ + { + "meaning": "eightfold law", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "POILP": { + "gematria": [ + 116 + ], + "meanings": [ + { + "meaning": "to be divided", + "source": "EMPM" + }, + { + "meaning": "DIVIDE, ARE DIVIDED", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "poh-ee-el-pel", + "source": "EMPM" + } + ] + }, + "POLA": { + "gematria": [], + "meanings": [ + { + "meaning": "TWO (TOGETHER), PAIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PON": { + "gematria": [ + 89 + ], + "meanings": [ + { + "meaning": "to destroy", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "poh-en", + "source": "EMPM" + } + ] + }, + "PONODOL": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ICH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "POP": { + "gematria": [], + "meanings": [ + { + "meaning": "NINTEENTH AETHYR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "POPHAND": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN DES", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PORTEX": { + "gematria": [], + "meanings": [ + { + "meaning": "separate sun of God", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "POTHNIR": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN PAZ", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PPSAC": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Archangel EARTH OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PRAC": { + "gematria": [], + "meanings": [ + { + "meaning": "dwelling in", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PRAF": { + "gematria": [], + "meanings": [ + { + "meaning": "dwell", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PRAGMA": { + "gematria": [], + "meanings": [ + { + "meaning": "dwell (?)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PRAP": { + "gematria": [], + "meanings": [ + { + "meaning": "balance", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PRAS": { + "gematria": [], + "meanings": [ + { + "meaning": "unite", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PRDZAR": { + "gematria": [], + "meanings": [ + { + "meaning": "diminish", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PRES": { + "gematria": [], + "meanings": [ + { + "meaning": "praise", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "PRGE": { + "gematria": [ + 127 + ], + "meanings": [ + { + "meaning": "fire", + "source": "EMPM" + }, + { + "meaning": "fire, with the fire", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "par-geh", + "source": "EMPM" + } + ] + }, + "PRIAZ": { + "gematria": [], + "meanings": [ + { + "meaning": "those", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PRIAZI": { + "gematria": [], + "meanings": [ + { + "meaning": "those, with those", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PRISTAC": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ZID", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PS": { + "gematria": [], + "meanings": [ + { + "meaning": "cubed", + "source": "WE", + "source2": "Lamen" + } + ], + "pronounciations": [] + }, + "PSAC": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Angel EARTH OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PSEA": { + "gematria": [], + "meanings": [ + { + "meaning": "THE WAY (Schuler)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PUGO": { + "gematria": [], + "meanings": [ + { + "meaning": "AS UNTO", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PUIM": { + "gematria": [], + "meanings": [ + { + "meaning": "SICKLES, SHARPPURGEL FIRE OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "PZIZA": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Archangel FIRE OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "Q": { + "gematria": [], + "meanings": [ + { + "meaning": "Ger (Q)", + "source": "WE" + }, + { + "meaning": "OR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "Q-": { + "gematria": [], + "meanings": [ + { + "meaning": "COCASB TIME, THE CONTENTS OF", + "source": "WE" + } + ], + "pronounciations": [] + }, + "QAA": { + "gematria": [ + 52 + ], + "meanings": [ + { + "meaning": "creation", + "source": "EMPM" + }, + { + "meaning": "GARMENTS, YOUR", + "source": "WE" + }, + { + "meaning": "CREATION, OF YOUR", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "quah-ah", + "source": "EMPM" + } + ] + }, + "QAADAH": { + "gematria": [], + "meanings": [ + { + "meaning": "CREATOR, OF THE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "QAAL": { + "gematria": [], + "meanings": [ + { + "meaning": "CREATOR, THE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "QAAN": { + "gematria": [], + "meanings": [ + { + "meaning": "CREATION, OF YOUR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "QAANIS": { + "gematria": [], + "meanings": [ + { + "meaning": "OLIVES (alt.sp.)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "QAAON": { + "gematria": [], + "meanings": [ + { + "meaning": "CREATION, IN YOUR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "QAAS": { + "gematria": [], + "meanings": [ + { + "meaning": "CREATION, YOUR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "QANIS": { + "gematria": [ + 163 + ], + "meanings": [ + { + "meaning": "olives", + "source": "EMPM" + }, + { + "meaning": "OLIVES", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "quah-nee-ess", + "source": "EMPM" + } + ] + }, + "QTING": { + "gematria": [], + "meanings": [ + { + "meaning": "ROTTEN, THE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "QUAR": { + "gematria": [], + "meanings": [ + { + "meaning": 1636, + "source": "WE" + } + ], + "pronounciations": [] + }, + "QUASAHI": { + "gematria": [ + 190 + ], + "meanings": [ + { + "meaning": "pleasure, delight", + "source": "EMPM" + }, + { + "meaning": "PLEASURE, OF", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "quah-sah-hee", + "source": "EMPM" + } + ] + }, + "QUASB": { + "gematria": [ + 128 + ], + "meanings": [ + { + "meaning": "to ruin, destroy", + "source": "EMPM" + }, + { + "meaning": "DESTROY", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "quah-seh-beh", + "source": "EMPM" + } + ] + }, + "QUIIN": { + "gematria": [], + "meanings": [ + { + "meaning": "WHEREIN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "QURLST": { + "gematria": [], + "meanings": [ + { + "meaning": "HANDMAID, A", + "source": "WE" + } + ], + "pronounciations": [] + }, + "R": { + "gematria": [], + "meanings": [ + { + "meaning": "Don (R)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RA": { + "gematria": [], + "meanings": [ + { + "meaning": "east", + "source": "WE", + "source2": "Lamen" + } + ], + "pronounciations": [] + }, + "RAAGIOL": { + "gematria": [], + "meanings": [ + { + "meaning": "KING OF WATER TABLET (VAR)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RAAGIOS": { + "gematria": [], + "meanings": [ + { + "meaning": "KING OF WATER TABLET (VAR)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RAAGIOSL": { + "gematria": [], + "meanings": [ + { + "meaning": "ELEMENTAL KING OF WATER TABLET", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RAAS": { + "gematria": [ + 119 + ], + "meanings": [ + { + "meaning": "the East", + "source": "EMPM" + }, + { + "meaning": "east, the", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "rah-ah-seh", + "source": "EMPM" + } + ] + }, + "RAASY": { + "gematria": [], + "meanings": [ + { + "meaning": "east, into the", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RACLIR": { + "gematria": [], + "meanings": [ + { + "meaning": "weeping", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RANGLAM": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN UTI", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RBNH": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RBXNH": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "REST": { + "gematria": [], + "meanings": [ + { + "meaning": "PRAISE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RESTEL": { + "gematria": [], + "meanings": [ + { + "meaning": "THAT YOU MAY PRAISE HIM", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RGAN": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RGOAN": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RII": { + "gematria": [], + "meanings": [ + { + "meaning": "29TH AETHYR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RIOR": { + "gematria": [], + "meanings": [ + { + "meaning": "WIDOW, OF A", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RIPIR": { + "gematria": [], + "meanings": [ + { + "meaning": "NO PLACE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RIT": { + "gematria": [ + 163, + 169 + ], + "meanings": [ + { + "meaning": "mercy", + "source": "EMPM" + }, + { + "meaning": "mercy", + "source": "EMPM" + }, + { + "meaning": "MERCY, OF", + "source": "WE" + }, + { + "meaning": "MERCY, OF", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "ree-teh", + "source": "EMPM" + }, + { + "pronounciation": "ree-teh", + "source": "EMPM" + } + ] + }, + "RLEMU": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RLMU": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RLODNR": { + "gematria": [], + "meanings": [ + { + "meaning": "RLU furnace ( ?), crucible (?)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RLU": { + "gematria": [], + "meanings": [ + { + "meaning": "'is moving’; completed; ending", + "source": "WE", + "source2": "Table of 12" + }, + { + "meaning": "‘not moving’, ‘not-ing’ or ‘making into not (non-existence)’, destroying", + "source": "WE", + "source2": "Holy Table of Practice perimeter" + } + ], + "pronounciations": [] + }, + "RNAH": { + "gematria": [], + "meanings": [ + { + "meaning": "sunrise", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "RNDIL": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RNIL": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RO": { + "gematria": [], + "meanings": [ + { + "meaning": "3 rd minister of Sol (a Son of Son of Light (Jupiter) [cf. Rocle on 7x7 Tablet]", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ROCLE": { + "gematria": [], + "meanings": [ + { + "meaning": "SON OF SON OF LIGHT, JUPITER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ROEMNAB": { + "gematria": [], + "meanings": [ + { + "meaning": "3RD MINISTER OF SOL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RONOOMB": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN TOR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ROR": { + "gematria": [], + "meanings": [ + { + "meaning": "sun", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ROWGH": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ROXTAN": { + "gematria": [], + "meanings": [ + { + "meaning": "wine", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RSAM": { + "gematria": [], + "meanings": [ + { + "meaning": "admiration", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RSNI": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RSONI": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RUDNA": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RV": { + "gematria": [], + "meanings": [ + { + "meaning": "Angel of the East", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "RVOI": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RVROI": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RXAO": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF EARTHRXPAO Servient Angel EARTH OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RZIONR": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross FIRE OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "RZLA": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Angel AIR OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "S": { + "gematria": [], + "meanings": [ + { + "meaning": "Fam (S)", + "source": "WE" + }, + { + "meaning": "FOURTH", + "source": "WE" + }, + { + "meaning": "DAUGHTER OF DAUGHTER OF LIGHT", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SA": { + "gematria": [ + 13 + ], + "meanings": [ + { + "meaning": "within", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "sah", + "source": "EMPM" + } + ] + }, + "SAANIR": { + "gematria": [], + "meanings": [ + { + "meaning": "PARTS, BY HER", + "source": "WE" + }, + { + "meaning": "PARTS, IN THE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SABA": { + "gematria": [], + "meanings": [ + { + "meaning": "WHOSE, VAR ON 'SOBA'", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SABULAN": { + "gematria": [], + "meanings": [ + { + "meaning": "who proclaims", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "SACH": { + "gematria": [], + "meanings": [ + { + "meaning": "CONFIRMING ANGELS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SADGLA": { + "gematria": [], + "meanings": [ + { + "meaning": "the Daughter of Light is God’s glory", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "SAGA": { + "gematria": [], + "meanings": [ + { + "meaning": "ONE, ENTIRE, WHOLE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SAGACIY": { + "gematria": [], + "meanings": [ + { + "meaning": "3RD MINISTER OF MARS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SAGACOR": { + "gematria": [], + "meanings": [ + { + "meaning": "NUMBER, IN ONE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SAIINOU": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior JUPITER of WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SAIX": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SALBROX": { + "gematria": [], + "meanings": [ + { + "meaning": "SULPHUR, LIVE SULPHUR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SALD": { + "gematria": [ + 25 + ], + "meanings": [ + { + "meaning": "wonder", + "source": "EMPM" + }, + { + "meaning": "WONDER, OF", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "sal-deh", + "source": "EMPM" + } + ] + }, + "SALMAN": { + "gematria": [ + 167 + ], + "meanings": [ + { + "meaning": "a house", + "source": "EMPM" + }, + { + "meaning": "HOUSE, THE", + "source": "WE" + }, + { + "meaning": "HOUSE, A", + "source": "WE" + }, + { + "meaning": "HOUSE", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "sah-leh-mah-neh", + "source": "EMPM" + } + ] + }, + "SAMA": { + "gematria": [], + "meanings": [ + { + "meaning": "the 4 th possesses", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "SAMAPHA": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ZOM", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SAMHAMPORS": { + "gematria": [], + "meanings": [ + { + "meaning": "the righteous creatures of the Sun of God are separated from the 4th", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "SAMVELG": { + "gematria": [], + "meanings": [ + { + "meaning": "RIGHTEOUS, TO THE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SANGEF": { + "gematria": [], + "meanings": [ + { + "meaning": "Sangef (the Master Magickian)", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "SAPAH": { + "gematria": [], + "meanings": [ + { + "meaning": "SOUNDS, THE MIGHTY", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SAPPOH": { + "gematria": [], + "meanings": [ + { + "meaning": "the mighty ogdoad", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "SAXTOMP": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN MAZ", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SAXX": { + "gematria": [], + "meanings": [ + { + "meaning": "the 4 th dissolves", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "SAZIAMI": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ZAA", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SCIO": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SCMIO": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SE": { + "gematria": [], + "meanings": [ + { + "meaning": "mourning, cry", + "source": "WE", + "source2": "Holy Table of Practice perimeter" + } + ], + "pronounciations": [] + }, + "SEBO": { + "gematria": [], + "meanings": [ + { + "meaning": "separation", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "SEBRA": { + "gematria": [], + "meanings": [ + { + "meaning": "warning", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "SEDLO": { + "gematria": [], + "meanings": [ + { + "meaning": "cry gives us the 5 –or- cry gives us the Holy Pentagram", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "SEMBABAM": { + "gematria": [], + "meanings": [ + { + "meaning": "MEANING UNKNOWN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SEMELABUGEN": { + "gematria": [], + "meanings": [ + { + "meaning": "Semeliel, the angel of the Lord is made strong by the Daughter of Light.", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "SEMELIEL": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL OF SOL ???", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SEMEROH": { + "gematria": [], + "meanings": [ + { + "meaning": "MEANING UNKNOWN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SEMNA": { + "gematria": [], + "meanings": [ + { + "meaning": "nine cries of God", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "SENDENNA": { + "gematria": [], + "meanings": [ + { + "meaning": "NAME OF AN EVIL SPIRIT, VAR 1", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SER": { + "gematria": [], + "meanings": [ + { + "meaning": "MOURNING, LAMENTATION", + "source": "WE" + } + ], + "pronounciations": [] + }, + "Servient": { + "gematria": [], + "meanings": [ + { + "meaning": "Angel AIR OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SESQVI": { + "gematria": [], + "meanings": [ + { + "meaning": "the cry of the 4 th , wherein is.../Wherein is the cry of the Daughter of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "SFAMLLB": { + "gematria": [], + "meanings": [ + { + "meaning": "3RD MINISTER OF MERCURY", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SHAL": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SHIAL": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SIAION": { + "gematria": [], + "meanings": [ + { + "meaning": "TEMPLE, OF THE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SIAS": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SIATRIS": { + "gematria": [], + "meanings": [ + { + "meaning": "SCORPIONS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SIBSI": { + "gematria": [ + 139 + ], + "meanings": [ + { + "meaning": "covenant", + "source": "EMPM" + }, + { + "meaning": "COVENANT, THE", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "see-beh-see", + "source": "EMPM" + } + ] + }, + "SIGAS": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF AIR (VAR)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SIGMORF": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN TAN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SIH": { + "gematria": [], + "meanings": [ + { + "meaning": "the temple and covenant of God", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "SIODA": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross EARTH OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SIOSP": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SISP": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SLGAIOL": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior VENUS of WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SMAN-": { + "gematria": [], + "meanings": [ + { + "meaning": "N ITS REPRESENTATIVE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SMNAD": { + "gematria": [], + "meanings": [ + { + "meaning": "ANOTHER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SNICOL": { + "gematria": [], + "meanings": [ + { + "meaning": "the Seven Sheaths", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "SOAGEEL": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN NIA", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SOBA": { + "gematria": [], + "meanings": [ + { + "meaning": "WHOSE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SOBAM": { + "gematria": [], + "meanings": [ + { + "meaning": "WHOM", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SOBOLN": { + "gematria": [], + "meanings": [ + { + "meaning": "WEST, IN THE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SOBOLZAR": { + "gematria": [], + "meanings": [ + { + "meaning": "WHOSE COURSES (alt.sp.)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SOBRA": { + "gematria": [], + "meanings": [ + { + "meaning": "WHOM, IN WHOSE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SOCHIAL": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN LEA", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SOE": { + "gematria": [ + 47 + ], + "meanings": [ + { + "meaning": "savior", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "soh-eh", + "source": "EMPM" + } + ] + }, + "SOLPETH": { + "gematria": [], + "meanings": [ + { + "meaning": "HEARKEN UNTO", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SONDENNA": { + "gematria": [], + "meanings": [ + { + "meaning": "NAME OF EVIL SPIRIT, VAR 2", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SONDN": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross WATER OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SONF": { + "gematria": [ + 90 + ], + "meanings": [ + { + "meaning": "to reign", + "source": "EMPM" + }, + { + "meaning": "REIGN", + "source": "WE" + }, + { + "meaning": "REIGNS", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "soh-en-eff", + "source": "EMPM" + } + ] + }, + "SONIZNT": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior MERCURY of WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SOR": { + "gematria": [], + "meanings": [ + { + "meaning": "ACTION", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SOYGA": { + "gematria": [], + "meanings": [ + { + "meaning": "WILL OF GOD, SAINTLY", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SPA": { + "gematria": [], + "meanings": [ + { + "meaning": "the Daughter of Light keeps", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "SRAHPM": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior MARS of WATER TABLET", + "source": "WE" + } + ], + "pronounciations": [] + }, + "STIM": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "STIMCUL": { + "gematria": [], + "meanings": [ + { + "meaning": "(name of an angel)", + "source": "EMPM", + "source2": "pg 25" + }, + { + "meaning": "DAUGHTER OF LIGHT", + "source": "WE" + }, + { + "meaning": "SON OF LIGHT, SATURN OR LUNA", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "Ess-tee-em-kul", + "source": "EMPM", + "source2": "pg 25" + } + ] + }, + "STRIM": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SUDSAMMA": { + "gematria": [], + "meanings": [ + { + "meaning": "KELLY'S GOOD ANGEL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SUNDENNA": { + "gematria": [], + "meanings": [ + { + "meaning": "NAME OF EVIL SPIRIT, VAR 3", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SURZAS": { + "gematria": [], + "meanings": [ + { + "meaning": "SWEAR, HE HAS SWORN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "SYMP": { + "gematria": [], + "meanings": [ + { + "meaning": "ANOTHER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "T": { + "gematria": [ + 9 + ], + "meanings": [ + { + "meaning": "is, as, like, likeness, equality, equilibrium", + "source": "EMPM" + }, + { + "meaning": "Gisa (T)", + "source": "WE" + }, + { + "meaning": "IT", + "source": "WE" + }, + { + "meaning": "ALSO", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "teh", + "source": "EMPM" + } + ] + }, + "TA": { + "gematria": [ + 9, + 15 + ], + "meanings": [ + { + "meaning": "is, as, like, likeness, equality, equilibrium", + "source": "EMPM" + }, + { + "meaning": "is, as, like, likeness, equality, equilibrium", + "source": "EMPM" + }, + { + "meaning": "AS", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "tah", + "source": "EMPM" + }, + { + "pronounciation": "tah", + "source": "EMPM" + } + ] + }, + "TAAD": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Angel AIR OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TABA": { + "gematria": [], + "meanings": [ + { + "meaning": "GOVERN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TABAAM": { + "gematria": [], + "meanings": [ + { + "meaning": "GOVERNOR, THE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TABAAN": { + "gematria": [ + 76, + 82 + ], + "meanings": [ + { + "meaning": "governor", + "source": "EMPM" + }, + { + "meaning": "governor", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "tah-bah-an", + "source": "EMPM" + }, + { + "pronounciation": "tah-bah-an", + "source": "EMPM" + } + ] + }, + "TABAORD": { + "gematria": [], + "meanings": [ + { + "meaning": "GOVERN, LET HER BE GOVERNED", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TABAORI": { + "gematria": [], + "meanings": [ + { + "meaning": "GOVERN", + "source": "WE" + }, + { + "meaning": "GOVERN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TABAS": { + "gematria": [], + "meanings": [ + { + "meaning": "GOVERN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TABGES": { + "gematria": [ + 39, + 45 + ], + "meanings": [ + { + "meaning": "a cave", + "source": "EMPM" + }, + { + "meaning": "a cave", + "source": "EMPM" + }, + { + "meaning": "CAVES", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "tah-beh-gess", + "source": "EMPM" + }, + { + "pronounciation": "tah-beh-gess", + "source": "EMPM" + } + ] + }, + "TABITOM": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ZAX", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TAFA": { + "gematria": [ + 18, + 24 + ], + "meanings": [ + { + "meaning": "poison", + "source": "EMPM" + }, + { + "meaning": "poison", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "tah-fah", + "source": "EMPM" + }, + { + "pronounciation": "tah-fah", + "source": "EMPM" + } + ] + }, + "TAHAMDO": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN OXO", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TAHAOELOI": { + "gematria": [], + "meanings": [ + { + "meaning": "GREAT ELEMENTAL KING OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TAL": { + "gematria": [], + "meanings": [ + { + "meaning": "ENOCHIAN LETTER M", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TALHO": { + "gematria": [ + 48, + 54 + ], + "meanings": [ + { + "meaning": "a cup", + "source": "EMPM" + }, + { + "meaning": "a cup", + "source": "EMPM" + }, + { + "meaning": "CUPS", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "tah-leh-hoh", + "source": "EMPM" + }, + { + "pronounciation": "tah-leh-hoh", + "source": "EMPM" + } + ] + }, + "TAN": { + "gematria": [], + "meanings": [ + { + "meaning": "SEVENTEENTH AETHYR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TAOAGLA": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN TEX", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TAPAMAL": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN LOE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TASTOXO": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN OXO", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TATAN": { + "gematria": [], + "meanings": [ + { + "meaning": "WORMWOOD", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TDIM": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Angel WATER OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TEAA": { + "gematria": [], + "meanings": [ + { + "meaning": "A GOD-NAME OF FIRE TABLET", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TEDOOND": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN UTA", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TELOAH": { + "gematria": [], + "meanings": [ + { + "meaning": "DEATH, VAR ON 'TELOCH'", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TELOCH": { + "gematria": [], + "meanings": [ + { + "meaning": "DEATH, OF", + "source": "WE" + }, + { + "meaning": "DEATH, OF", + "source": "WE" + }, + { + "meaning": "DEATH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TELOCVOVIM": { + "gematria": [], + "meanings": [ + { + "meaning": "DEATH-DRAGON", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TEMPA": { + "gematria": [], + "meanings": [ + { + "meaning": "the exception of death is life", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "TEX": { + "gematria": [], + "meanings": [ + { + "meaning": "30TH AETHYR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "THAHAAOTAHE": { + "gematria": [], + "meanings": [ + { + "meaning": "GREAT ELEMENTAL KING OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "THAHEBIOBEE": { + "gematria": [], + "meanings": [ + { + "meaning": "ATAN GREAT ELEMENTAL KING OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "THIL": { + "gematria": [ + 72, + 78 + ], + "meanings": [ + { + "meaning": "seat", + "source": "EMPM" + }, + { + "meaning": "seat", + "source": "EMPM" + }, + { + "meaning": "SEATS", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "teh-hee-el", + "source": "EMPM" + }, + { + "pronounciation": "teh-hee-el", + "source": "EMPM" + } + ] + }, + "THILD": { + "gematria": [], + "meanings": [ + { + "meaning": "SEATS, THEIR OWN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "THILN": { + "gematria": [], + "meanings": [ + { + "meaning": "SEATS, IN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "THOTANP": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN PAZ", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TIA": { + "gematria": [], + "meanings": [ + { + "meaning": "UNTO US", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TIANTA": { + "gematria": [ + 128, + 140 + ], + "meanings": [ + { + "meaning": "a bed", + "source": "EMPM" + }, + { + "meaning": "a bed", + "source": "EMPM" + }, + { + "meaning": "BED, THE", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "tee-an-en-tah", + "source": "EMPM" + }, + { + "pronounciation": "tee-an-en-tah", + "source": "EMPM" + } + ] + }, + "TIARPAX": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN LIT", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TIBIBP": { + "gematria": [ + 142, + 148 + ], + "meanings": [ + { + "meaning": "sorrow", + "source": "EMPM" + }, + { + "meaning": "sorrow", + "source": "EMPM" + }, + { + "meaning": "SORROW, OF", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "tee-bee-beh-peh", + "source": "EMPM" + }, + { + "pronounciation": "tee-bee-beh-peh", + "source": "EMPM" + } + ] + }, + "TILB": { + "gematria": [], + "meanings": [ + { + "meaning": "HER, OF", + "source": "WE" + }, + { + "meaning": "HERTIO TOP LINE OF TABLET OF 12 SQUAR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TIOBL": { + "gematria": [], + "meanings": [ + { + "meaning": "HER, IN", + "source": "WE" + }, + { + "meaning": "HER, IN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TLB": { + "gematria": [], + "meanings": [ + { + "meaning": "HIM, OF", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TLIOB": { + "gematria": [], + "meanings": [ + { + "meaning": "SEPARATE (verb)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TNBR": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Angel EARTH OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TOANT": { + "gematria": [ + 92, + 104 + ], + "meanings": [ + { + "meaning": "love, union", + "source": "EMPM" + }, + { + "meaning": "love, union", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "toh-an-teh", + "source": "EMPM" + }, + { + "pronounciation": "toh-an-teh", + "source": "EMPM" + } + ] + }, + "TOANTOM": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ASP", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TOATAR": { + "gematria": [], + "meanings": [ + { + "meaning": "HARKEN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TOCARZI": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN TAN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TOCO": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TODNAON": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ZID", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TOFGLO": { + "gematria": [], + "meanings": [ + { + "meaning": "THINGS, ALL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TOGCO": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TOH": { + "gematria": [ + 34, + 40 + ], + "meanings": [ + { + "meaning": "triumph", + "source": "EMPM" + }, + { + "meaning": "triumph", + "source": "EMPM" + }, + { + "meaning": "TRIUMPHS", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "toh", + "source": "EMPM" + }, + { + "pronounciation": "toh", + "source": "EMPM" + } + ] + }, + "TOHCOTH": { + "gematria": [], + "meanings": [ + { + "meaning": "FAERIES", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TOHOMAPHALA": { + "gematria": [], + "meanings": [ + { + "meaning": "NAME OF A GUARDIAN ANGEL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TOITT": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF AIR (VAR)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TOL": { + "gematria": [ + 41, + 47 + ], + "meanings": [ + { + "meaning": "all, everything", + "source": "EMPM" + }, + { + "meaning": "all, everything", + "source": "EMPM" + }, + { + "meaning": "ALL", + "source": "WE" + }, + { + "meaning": "ON ALL", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "toh-el", + "source": "EMPM" + }, + { + "pronounciation": "toh-el", + "source": "EMPM" + } + ] + }, + "TOLTORG": { + "gematria": [], + "meanings": [ + { + "meaning": "CREATURES OF EARTH, THE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TOLTORGI": { + "gematria": [], + "meanings": [ + { + "meaning": "CREATURES, WITH HER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TOLTORN": { + "gematria": [], + "meanings": [ + { + "meaning": "CREATURES", + "source": "WE" + }, + { + "meaning": "CREATURE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TON": { + "gematria": [], + "meanings": [ + { + "meaning": "ALL, VAR. ON 'TOL'", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TONUG": { + "gematria": [], + "meanings": [ + { + "meaning": "DEFACE, LET THEM BE DEFACED", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TOOAT": { + "gematria": [ + 72, + 84 + ], + "meanings": [ + { + "meaning": "to provide", + "source": "EMPM" + }, + { + "meaning": "to provide", + "source": "EMPM" + }, + { + "meaning": "FURNISHING", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "toh-oh-ah-teh", + "source": "EMPM" + }, + { + "pronounciation": "toh-oh-ah-teh", + "source": "EMPM" + } + ] + }, + "TOR": { + "gematria": [], + "meanings": [ + { + "meaning": "23RD AETHYR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TORGU": { + "gematria": [], + "meanings": [ + { + "meaning": "ARISE (alt.sp.)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TORZOXI": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN POP", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TORZU": { + "gematria": [], + "meanings": [ + { + "meaning": "ARISE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TORZUL": { + "gematria": [], + "meanings": [ + { + "meaning": "RISE, SHALL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TORZULP": { + "gematria": [], + "meanings": [ + { + "meaning": "RISE, ROSE UP", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TOTO": { + "gematria": [ + 66, + 78 + ], + "meanings": [ + { + "meaning": "cycles", + "source": "EMPM" + }, + { + "meaning": "cycles", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "toh-toh", + "source": "EMPM" + }, + { + "pronounciation": "toh-toh", + "source": "EMPM" + } + ] + }, + "TOTOCAN": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN CHR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TOTT": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TOX": { + "gematria": [], + "meanings": [ + { + "meaning": "HIM, OF", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TPRDEMAH": { + "gematria": [], + "meanings": [ + { + "meaning": "of darkness", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "TRANAN": { + "gematria": [], + "meanings": [ + { + "meaning": "MARROW, THE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TRIAN": { + "gematria": [], + "meanings": [ + { + "meaning": "SHALL BE", + "source": "WE" + }, + { + "meaning": "SHALL BE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TRINT": { + "gematria": [], + "meanings": [ + { + "meaning": "SIT", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TROF": { + "gematria": [], + "meanings": [ + { + "meaning": "BUILDING, A", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TULE": { + "gematria": [], + "meanings": [ + { + "meaning": "'It ends with [the goddess] El’; ‘Completed by the goddess’ or ‘Ending with the goddess’", + "source": "WE", + "source2": "Table of 12" + }, + { + "meaning": "Name from T12Sqr", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TULE ILRO": { + "gematria": [], + "meanings": [ + { + "meaning": "LETTERS OF T12SQR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TURBS": { + "gematria": [], + "meanings": [ + { + "meaning": "BEAUTY, IN THEIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TUSTAX": { + "gematria": [], + "meanings": [ + { + "meaning": "GOING", + "source": "WE" + } + ], + "pronounciations": [] + }, + "TZESTS": { + "gematria": [], + "meanings": [ + { + "meaning": "Being of the 4", + "source": "WE" + } + ], + "pronounciations": [] + }, + "U": { + "gematria": [], + "meanings": [ + { + "meaning": "U Val (U, V, W)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "UCIM": { + "gematria": [], + "meanings": [ + { + "meaning": "they frown not", + "source": "WE" + } + ], + "pronounciations": [] + }, + "UGEAR": { + "gematria": [], + "meanings": [ + { + "meaning": "strength, the s. of men", + "source": "WE" + } + ], + "pronounciations": [] + }, + "UGEG": { + "gematria": [], + "meanings": [ + { + "meaning": "strong, grow", + "source": "WE" + }, + { + "meaning": "strong, waxes", + "source": "WE" + }, + { + "meaning": "strong, become", + "source": "WE" + } + ], + "pronounciations": [] + }, + "UL": { + "gematria": [], + "meanings": [ + { + "meaning": "end", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ULCININ": { + "gematria": [], + "meanings": [ + { + "meaning": "happy is he", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ULR": { + "gematria": [], + "meanings": [ + { + "meaning": "Name from T12Sqr", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ULS": { + "gematria": [], + "meanings": [ + { + "meaning": "ends, the", + "source": "WE" + } + ], + "pronounciations": [] + }, + "UM": { + "gematria": [], + "meanings": [ + { + "meaning": "called, named, var on 'vmd'", + "source": "WE" + } + ], + "pronounciations": [] + }, + "UMADEA": { + "gematria": [], + "meanings": [ + { + "meaning": "towers, strong", + "source": "WE" + } + ], + "pronounciations": [] + }, + "UMBLOSDA": { + "gematria": [], + "meanings": [ + { + "meaning": "one who resides in the skies", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "UML": { + "gematria": [], + "meanings": [ + { + "meaning": "add", + "source": "WE" + } + ], + "pronounciations": [] + }, + "UMPLIF": { + "gematria": [], + "meanings": [ + { + "meaning": "strength, our", + "source": "WE" + } + ], + "pronounciations": [] + }, + "UN": { + "gematria": [], + "meanings": [ + { + "meaning": "ENOCHIAN LETTER A", + "source": "WE" + } + ], + "pronounciations": [] + }, + "UNAL": { + "gematria": [], + "meanings": [ + { + "meaning": "these", + "source": "WE" + } + ], + "pronounciations": [] + }, + "UNALAB": { + "gematria": [], + "meanings": [ + { + "meaning": "skirt", + "source": "WE" + } + ], + "pronounciations": [] + }, + "UNALAH": { + "gematria": [], + "meanings": [ + { + "meaning": "skirts, the", + "source": "WE" + } + ], + "pronounciations": [] + }, + "UNBA": { + "gematria": [], + "meanings": [ + { + "meaning": "is powerful", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "UNCAL": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "UNCHI": { + "gematria": [], + "meanings": [ + { + "meaning": "confound", + "source": "WE" + } + ], + "pronounciations": [] + }, + "UNDES": { + "gematria": [], + "meanings": [ + { + "meaning": "leaves the 4th", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "UNDL": { + "gematria": [], + "meanings": [ + { + "meaning": "rest; remainder, the", + "source": "WE" + } + ], + "pronounciations": [] + }, + "UNIG": { + "gematria": [], + "meanings": [ + { + "meaning": "requires", + "source": "WE" + } + ], + "pronounciations": [] + }, + "UNIGLAG": { + "gematria": [], + "meanings": [ + { + "meaning": "descend", + "source": "WE" + } + ], + "pronounciations": [] + }, + "UNNAX": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross AIR OF EARTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "UNPH": { + "gematria": [], + "meanings": [ + { + "meaning": "anger, wrath. var on 'vonph'?", + "source": "WE" + } + ], + "pronounciations": [] + }, + "UOLXDO": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross EARTH OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "UPAAH": { + "gematria": [], + "meanings": [ + { + "meaning": "WINGS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "UR": { + "gematria": [], + "meanings": [ + { + "meaning": "ENOCHIAN LETTER L", + "source": "WE" + } + ], + "pronounciations": [] + }, + "URAN": { + "gematria": [], + "meanings": [ + { + "meaning": "ELDERS, THE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "URCH": { + "gematria": [], + "meanings": [ + { + "meaning": "CONFOUNDING ANGELS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "USNARDA": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ICH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "UTA": { + "gematria": [], + "meanings": [ + { + "meaning": "FOURTEENTH AETHYR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "UTI": { + "gematria": [], + "meanings": [ + { + "meaning": "25TH AETHYR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "V": { + "gematria": [], + "meanings": [ + { + "meaning": "star", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VA": { + "gematria": [], + "meanings": [ + { + "meaning": "spirit of Vaa", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VAA": { + "gematria": [], + "meanings": [ + { + "meaning": "NAME OF AN ANGEL", + "source": "WE" + }, + { + "meaning": "(angel of the 4 moons)", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VAASA": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VABZIR": { + "gematria": [], + "meanings": [ + { + "meaning": "eagle, the", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VADALI": { + "gematria": [], + "meanings": [ + { + "meaning": "Sephirotic Cross WATER OF FIRE", + "source": "WE" + }, + { + "meaning": "Sephirotic Cross WATER OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VADGS": { + "gematria": [], + "meanings": [ + { + "meaning": "time", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VALGARS": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN LIL", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VAMI": { + "gematria": [], + "meanings": [ + { + "meaning": "the way of the Lord", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VAN": { + "gematria": [], + "meanings": [ + { + "meaning": "ENOCHIAN LETTER V,U", + "source": "WE" + }, + { + "meaning": "star", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VANA": { + "gematria": [], + "meanings": [ + { + "meaning": "starry, stars", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VANDEMINAXAT": { + "gematria": [], + "meanings": [ + { + "meaning": "constellations", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VANDRES": { + "gematria": [], + "meanings": [ + { + "meaning": "the Scepter of the Daughter of Daughter of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VANGEM": { + "gematria": [], + "meanings": [ + { + "meaning": "the will of heaven", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VANGET": { + "gematria": [], + "meanings": [ + { + "meaning": "not the fourth star", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VANGLOR": { + "gematria": [], + "meanings": [ + { + "meaning": "fruit of heaven", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VANS": { + "gematria": [], + "meanings": [ + { + "meaning": "fourth star", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VANSAMPLE": { + "gematria": [], + "meanings": [ + { + "meaning": "the fabric of stars", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VANSAX": { + "gematria": [], + "meanings": [ + { + "meaning": "the circle of stars", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VAOAN": { + "gematria": [ + 162 + ], + "meanings": [ + { + "meaning": "truth", + "source": "EMPM" + }, + { + "meaning": "truth", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "vah-oh-ah-en", + "source": "EMPM" + } + ] + }, + "VAR": { + "gematria": [], + "meanings": [ + { + "meaning": "that star, the star in 9", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VARCA": { + "gematria": [], + "meanings": [ + { + "meaning": "spiritual sun", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VARO": { + "gematria": [], + "meanings": [ + { + "meaning": "Va’aro (from Loagaeth: Leaf 1A vs. 10)", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VARSG": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VAS": { + "gematria": [], + "meanings": [ + { + "meaning": "angel of Daughter of Light", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VASA": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VASG": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel AIR OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VASTRIM": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN RII", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VAU": { + "gematria": [], + "meanings": [ + { + "meaning": "ENOCHIAN LETTER V,U", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VAUL": { + "gematria": [ + 154 + ], + "meanings": [ + { + "meaning": "to work, toil", + "source": "EMPM" + }, + { + "meaning": "work", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "vah-u-el", + "source": "EMPM" + } + ] + }, + "VAUN": { + "gematria": [], + "meanings": [ + { + "meaning": "work, that ye might", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VAVAAMP": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN MAZ", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VAX": { + "gematria": [], + "meanings": [ + { + "meaning": "orbit", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VBRAH": { + "gematria": [], + "meanings": [ + { + "meaning": "guardian star", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VD": { + "gematria": [], + "meanings": [ + { + "meaning": "third star", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VDRIOS": { + "gematria": [], + "meanings": [ + { + "meaning": "the Zodiac", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VE": { + "gematria": [], + "meanings": [ + { + "meaning": "the spark of life", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VEH": { + "gematria": [], + "meanings": [ + { + "meaning": "VEH, ENOCHIAN LETTER C OR K", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VELUCORSAPA": { + "gematria": [], + "meanings": [ + { + "meaning": "ENTHRONED", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VEP": { + "gematria": [], + "meanings": [ + { + "meaning": "flame, as a", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VEROX": { + "gematria": [], + "meanings": [ + { + "meaning": "the Holy Spirit", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VICAP": { + "gematria": [], + "meanings": [ + { + "meaning": "meaning unknown", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VIROOLI": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ZOM", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VIRQ": { + "gematria": [], + "meanings": [ + { + "meaning": "nests", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VIRUDEN": { + "gematria": [], + "meanings": [ + { + "meaning": "I have beautified (Crowley)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VIV": { + "gematria": [], + "meanings": [ + { + "meaning": "second", + "source": "WE" + }, + { + "meaning": "in the second", + "source": "WE" + }, + { + "meaning": "the second", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VIVIPOS": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN UTA", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VIXPALG": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ASP", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VLLA": { + "gematria": [], + "meanings": [ + { + "meaning": "the end of the beginning", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VLOH": { + "gematria": [], + "meanings": [ + { + "meaning": "the end of sorrow", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VLS": { + "gematria": [ + 85 + ], + "meanings": [ + { + "meaning": "the ends, farthest reaches", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "fel-ess", + "source": "EMPM" + } + ] + }, + "VNAEM": { + "gematria": [], + "meanings": [ + { + "meaning": "nine skirts", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VNDANPEL": { + "gematria": [], + "meanings": [ + { + "meaning": "the Master Magickian", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VNDAT": { + "gematria": [], + "meanings": [ + { + "meaning": "also, the Master Magickian", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VNIG": { + "gematria": [ + 188 + ], + "meanings": [ + { + "meaning": "to require, need", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "veh-nee-geh", + "source": "EMPM" + } + ] + }, + "VNPH": { + "gematria": [ + 130 + ], + "meanings": [ + { + "meaning": "anger", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "ven-pen", + "source": "EMPM" + } + ] + }, + "VNRA": { + "gematria": [], + "meanings": [ + { + "meaning": "the wrathful sun", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VO": { + "gematria": [], + "meanings": [ + { + "meaning": "wherein", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VOHIM": { + "gematria": [], + "meanings": [ + { + "meaning": "mighty", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VOM": { + "gematria": [], + "meanings": [ + { + "meaning": "of everyone", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VOMSARG": { + "gematria": [], + "meanings": [ + { + "meaning": "unto every one of you", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VON": { + "gematria": [], + "meanings": [ + { + "meaning": "image of God", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VONPHO": { + "gematria": [], + "meanings": [ + { + "meaning": "wrath, of", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VONPOVNPH": { + "gematria": [], + "meanings": [ + { + "meaning": "wrath in anger", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VOOAN": { + "gematria": [], + "meanings": [ + { + "meaning": "truth", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VOR": { + "gematria": [], + "meanings": [ + { + "meaning": "appearance", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VORS": { + "gematria": [], + "meanings": [ + { + "meaning": "over", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VORSG": { + "gematria": [], + "meanings": [ + { + "meaning": "over you", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VORX": { + "gematria": [], + "meanings": [ + { + "meaning": "visits", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VOTOL": { + "gematria": [], + "meanings": [ + { + "meaning": "wherein all", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VOVIN": { + "gematria": [], + "meanings": [ + { + "meaning": "dragons", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VOVINA": { + "gematria": [], + "meanings": [ + { + "meaning": "dragon, the", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VOX": { + "gematria": [], + "meanings": [ + { + "meaning": "wherein they are (separated)", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VOXAD": { + "gematria": [], + "meanings": [ + { + "meaning": "wherein they are in the third", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VPAAH": { + "gematria": [ + 92 + ], + "meanings": [ + { + "meaning": "wings", + "source": "EMPM" + }, + { + "meaning": "wings", + "source": "WE" + }, + { + "meaning": "wings", + "source": "WE" + }, + { + "meaning": "wings, the", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "veh-pah-ah", + "source": "EMPM" + } + ] + }, + "VRANKRAN": { + "gematria": [], + "meanings": [ + { + "meaning": "The Son of Son of Light, unto the eld[ers]", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VRBS": { + "gematria": [], + "meanings": [ + { + "meaning": "beautified", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VRDRAH": { + "gematria": [], + "meanings": [ + { + "meaning": "dark star", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VRELP": { + "gematria": [ + 197 + ], + "meanings": [ + { + "meaning": "an adept, seer", + "source": "EMPM" + }, + { + "meaning": "seething, a strong", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "var-el-peh", + "source": "EMPM" + } + ] + }, + "VREPREZ": { + "gematria": [], + "meanings": [ + { + "meaning": "with beautiful praises", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VRO": { + "gematria": [], + "meanings": [ + { + "meaning": "this one", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "VSPSN": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VSSN": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel WATER OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "VX": { + "gematria": [], + "meanings": [ + { + "meaning": 42, + "source": "WE" + } + ], + "pronounciations": [] + }, + "X": { + "gematria": [], + "meanings": [ + { + "meaning": "Pal (X)", + "source": "WE" + }, + { + "meaning": "dissolution", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "XA": { + "gematria": [], + "meanings": [ + { + "meaning": "in dissolution", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "XGSD": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Angel FIRE OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "XPACN": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "XPCN": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel FIRE OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "XRINH": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "XRNH": { + "gematria": [], + "meanings": [ + { + "meaning": "Servient Angel EARTH OF WATER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "YALPAMB": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ZEN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "YARRY": { + "gematria": [], + "meanings": [ + { + "meaning": "PROVIDENCE, TO THE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "YLLMAFS": { + "gematria": [], + "meanings": [ + { + "meaning": "3RD MINISTER OF LUNA", + "source": "WE" + } + ], + "pronounciations": [] + }, + "YLSI": { + "gematria": [], + "meanings": [ + { + "meaning": "BEFORE THEE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "YOLCAM": { + "gematria": [], + "meanings": [ + { + "meaning": "BRING FORTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "YOLCI": { + "gematria": [], + "meanings": [ + { + "meaning": "BRINGS FORTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "YOR": { + "gematria": [], + "meanings": [ + { + "meaning": "ROAR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "YRPOIL": { + "gematria": [], + "meanings": [ + { + "meaning": "DIVISION", + "source": "WE" + } + ], + "pronounciations": [] + }, + "YTPA": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Angel WATER OF AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "Z": { + "gematria": [], + "meanings": [ + { + "meaning": "Ceph (Z)", + "source": "WE" + }, + { + "meaning": "THEY", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZA": { + "gematria": [ + 9, + 15 + ], + "meanings": [ + { + "meaning": "within", + "source": "EMPM" + }, + { + "meaning": "within", + "source": "EMPM" + }, + { + "meaning": "NAME OF AN ANGEL", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "zodah", + "source": "EMPM" + }, + { + "pronounciation": "zodah", + "source": "EMPM" + } + ] + }, + "ZAA": { + "gematria": [], + "meanings": [ + { + "meaning": "27TH AETHYR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZAAOZAIF": { + "gematria": [], + "meanings": [ + { + "meaning": "Senior JUPITER of AIR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZABLIS": { + "gematria": [], + "meanings": [ + { + "meaning": "both S and Ab (should be followed by a verb—such as to say: both S and Ab went to the store; or even preceded by a verb—such as to say: Henry invited both S and Ab); these are names of two of the Daughters of Daughters of Light.", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ZACAM": { + "gematria": [], + "meanings": [ + { + "meaning": "I MOVE YOU", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZACAR": { + "gematria": [], + "meanings": [ + { + "meaning": "MOVE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZADZACZADLI": { + "gematria": [], + "meanings": [ + { + "meaning": "ADAM, IN BOOK OF SOYGA", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZAFASAI": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ZEN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZAH": { + "gematria": [ + 10, + 16 + ], + "meanings": [ + { + "meaning": "inside, inner", + "source": "EMPM" + }, + { + "meaning": "inside, inner", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "zodah", + "source": "EMPM" + }, + { + "pronounciation": "zodah", + "source": "EMPM" + } + ] + }, + "ZAMFRES": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN ZID", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZAMRAN": { + "gematria": [], + "meanings": [ + { + "meaning": "SHOW YOURSELVES", + "source": "WE" + }, + { + "meaning": "APPEAR", + "source": "WE" + }, + { + "meaning": "SHOW YOURSELVES", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZAR": { + "gematria": [ + 109, + 115 + ], + "meanings": [ + { + "meaning": "ways, paths, courses", + "source": "EMPM" + }, + { + "meaning": "ways, paths, courses", + "source": "EMPM" + }, + { + "meaning": "COURSE, COURSES", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "zod-ar", + "source": "EMPM" + }, + { + "pronounciation": "zod-ar", + "source": "EMPM" + } + ] + }, + "ZARNAAH": { + "gematria": [], + "meanings": [ + { + "meaning": "Zodiacal King GEMINI", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZARZILG": { + "gematria": [], + "meanings": [ + { + "meaning": "Zodiacal King VIRGO", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZAX": { + "gematria": [], + "meanings": [ + { + "meaning": "TENTH AETHYR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZAXANIN": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN TOR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZCHIS": { + "gematria": [], + "meanings": [ + { + "meaning": "THEY ARE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZE": { + "gematria": [], + "meanings": [ + { + "meaning": "Daughter of Light", + "source": "WE", + "source2": "Holy Table of Practice perimeter" + } + ], + "pronounciations": [] + }, + "ZEBOG": { + "gematria": [], + "meanings": [ + { + "meaning": "the Daughter of Light reigns over", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ZED": { + "gematria": [], + "meanings": [ + { + "meaning": "The Daughter of Light; also a medieval way of pronouncing the English letter Z", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ZEDEKIEL": { + "gematria": [], + "meanings": [ + { + "meaning": "ANGEL OF JUPITER", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZEMBVGES": { + "gematria": [], + "meanings": [ + { + "meaning": "the Daughter of Light’s 9 glories from the 4th", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ZEN": { + "gematria": [ + 63, + 69 + ], + "meanings": [ + { + "meaning": "sacrifice", + "source": "EMPM" + }, + { + "meaning": "sacrifice", + "source": "EMPM" + }, + { + "meaning": "EIGHTEENTH AETHYR", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "zod-en", + "source": "EMPM" + }, + { + "pronounciation": "zod-en", + "source": "EMPM" + } + ] + }, + "ZEZ": { + "gematria": [], + "meanings": [ + { + "meaning": "firey angels", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ZID": { + "gematria": [], + "meanings": [ + { + "meaning": "EIGHTH AETHYR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZIEN": { + "gematria": [], + "meanings": [ + { + "meaning": "HANDS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZIL": { + "gematria": [], + "meanings": [ + { + "meaning": "?STRETCH FORTH", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZILDAR": { + "gematria": [ + 181, + 187 + ], + "meanings": [ + { + "meaning": "to fly", + "source": "EMPM" + }, + { + "meaning": "to fly", + "source": "EMPM" + }, + { + "meaning": "FLEW", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "zodee-leh-dar", + "source": "EMPM" + }, + { + "pronounciation": "zodee-leh-dar", + "source": "EMPM" + } + ] + }, + "ZILDRON": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN CHR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZILODARP": { + "gematria": [], + "meanings": [ + { + "meaning": "NAME OF GOD", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZIM": { + "gematria": [], + "meanings": [ + { + "meaning": "THIRTEENTH AETHYR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZIMAH": { + "gematria": [], + "meanings": [ + { + "meaning": "clothed with God", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + }, + "ZIMII": { + "gematria": [], + "meanings": [ + { + "meaning": "HAVE ENTERED", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZIMZ": { + "gematria": [], + "meanings": [ + { + "meaning": "VESTURES, MY VESTURES", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZIN": { + "gematria": [ + 113, + 119 + ], + "meanings": [ + { + "meaning": "waters", + "source": "EMPM" + }, + { + "meaning": "waters", + "source": "EMPM" + } + ], + "pronounciations": [ + { + "pronounciation": "zodee-en", + "source": "EMPM" + }, + { + "pronounciation": "zodee-en", + "source": "EMPM" + } + ] + }, + "ZINGGEN": { + "gematria": [], + "meanings": [ + { + "meaning": "Zodiacal King CAPRICORN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZIP": { + "gematria": [], + "meanings": [ + { + "meaning": "NINTH AETHYR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZIR": { + "gematria": [], + "meanings": [ + { + "meaning": "I AM", + "source": "WE" + }, + { + "meaning": "PRESENCE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZIRACAH": { + "gematria": [], + "meanings": [ + { + "meaning": "Zodiacal King AQUARIUS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZIRDO": { + "gematria": [], + "meanings": [ + { + "meaning": "I AMZIRENAIAD I AM THE LORD YOUR GOD", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZIRN": { + "gematria": [], + "meanings": [ + { + "meaning": "WONDERS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZIROM": { + "gematria": [], + "meanings": [ + { + "meaning": "THEY WERE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZIROP": { + "gematria": [], + "meanings": [ + { + "meaning": "WAS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZIRZIRD": { + "gematria": [], + "meanings": [ + { + "meaning": "PART IN MAZ", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZIXLAY": { + "gematria": [], + "meanings": [ + { + "meaning": "TO STIR UP", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZIZA": { + "gematria": [], + "meanings": [ + { + "meaning": "Kerubic Angel FIRE OF FIRE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZIZOP": { + "gematria": [ + 105, + 117 + ], + "meanings": [ + { + "meaning": "vessels", + "source": "EMPM" + }, + { + "meaning": "vessels", + "source": "EMPM" + }, + { + "meaning": "VESSELS", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "zodee-zodoh-pah", + "source": "EMPM" + }, + { + "pronounciation": "zodee-zodoh-pah", + "source": "EMPM" + } + ] + }, + "ZLIDA": { + "gematria": [], + "meanings": [ + { + "meaning": "WATER, TO", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZNA": { + "gematria": [], + "meanings": [ + { + "meaning": "MOTION, MOVEMENT", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZNRZA": { + "gematria": [], + "meanings": [ + { + "meaning": "SWORE", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZOL": { + "gematria": [], + "meanings": [ + { + "meaning": "HANDS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZOM": { + "gematria": [], + "meanings": [ + { + "meaning": "THIRD AETHYR", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZOMD": { + "gematria": [], + "meanings": [ + { + "meaning": "IN THE MIDST", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZON": { + "gematria": [ + 83, + 89 + ], + "meanings": [ + { + "meaning": "form", + "source": "EMPM" + }, + { + "meaning": "form", + "source": "EMPM", + "note": "In the book this appears as “zod-en”, which is clearly wrong, but also, the word appears with the correct pronounciation for gematria 83." + } + ], + "pronounciations": [ + { + "pronounciation": "zodoh-en", + "source": "EMPM" + }, + { + "pronounciation": "zodoh-en", + "source": "EMPM", + "note": "In the book this appears as “zod-en”, which is clearly wrong, but also, the word appears with the correct pronounciation for gematria 83." + } + ] + }, + "ZON-": { + "gematria": [], + "meanings": [ + { + "meaning": "L THE FIRST FORM (Schuler)", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZONAC": { + "gematria": [], + "meanings": [ + { + "meaning": "THEY ARE APPARELED", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZONG": { + "gematria": [], + "meanings": [ + { + "meaning": "OF THE WINDS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZONRENSG": { + "gematria": [], + "meanings": [ + { + "meaning": "DELIVERED YOU", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZORGE": { + "gematria": [ + 151, + 157 + ], + "meanings": [ + { + "meaning": "love, friendly", + "source": "EMPM" + }, + { + "meaning": "love, friendly", + "source": "EMPM" + }, + { + "meaning": "BE FRIENDLY TO ME", + "source": "WE" + } + ], + "pronounciations": [ + { + "pronounciation": "zodah-ra-geh", + "source": "EMPM" + }, + { + "pronounciation": "zodoh-ra-geh", + "source": "EMPM" + } + ] + }, + "ZUDNA": { + "gematria": [], + "meanings": [ + { + "meaning": "MEANING UNKNOWN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZUMVI": { + "gematria": [], + "meanings": [ + { + "meaning": "SEAS", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZURAAH": { + "gematria": [], + "meanings": [ + { + "meaning": "FERVENTLY, WITH HUMILITY", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZURAH": { + "gematria": [], + "meanings": [ + { + "meaning": "FERVENTLY, WITH HUMILITY", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZURCHOL": { + "gematria": [], + "meanings": [ + { + "meaning": "Zodiacal King PISCES", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZURE": { + "gematria": [], + "meanings": [ + { + "meaning": "MEANING UNKNOWN", + "source": "WE" + } + ], + "pronounciations": [] + }, + "ZURESK": { + "gematria": [], + "meanings": [ + { + "meaning": "Fervently unto the 4 th Heaven, Rushing", + "source": "WE", + "source2": "Liber Loagaeth" + } + ], + "pronounciations": [] + } +} diff --git a/data/enochian/letters.json b/data/enochian/letters.json new file mode 100644 index 0000000..a445c01 --- /dev/null +++ b/data/enochian/letters.json @@ -0,0 +1,212 @@ +{ + "A": { + "id": "A", + "enochian": "A", + "title": "Un", + "english": "A", + "pronounciation": "ah", + "planet/element": "taurus", + "tarot": "hierophant", + "gematria": 6 + }, + "B": { + "id": "B", + "enochian": "B", + "title": "Pe", + "english": "B", + "pronounciation": "beh", + "planet/element": "aries", + "tarot": "star", + "gematria": 5 + }, + "C": { + "id": "C", + "enochian": "C", + "title": "Veh", + "english": "C,K", + "pronounciation": "co", + "planet/element": "fire", + "tarot": "judgement", + "gematria": 300 + }, + "D": { + "id": "D", + "enochian": "D", + "title": "Gal", + "english": "D", + "pronounciation": "deh", + "planet/element": "spirit", + "tarot": "empress", + "gematria": 4 + }, + "E": { + "id": "E", + "enochian": "E", + "title": "Graph", + "english": "E", + "pronounciation": "ee", + "planet/element": "virgo", + "tarot": "hermit", + "gematria": 10 + }, + "F": { + "id": "F", + "enochian": "F", + "title": "Orth", + "english": "F", + "pronounciation": "eff", + "planet/element": "Cauda Draonis", + "tarot": "juggler", + "gematria": 3 + }, + "G": { + "id": "G", + "enochian": "G", + "title": "Ged", + "english": "G", + "pronounciation": "gee", + "planet/element": "cancer", + "tarot": "chariot", + "gematria": 8 + }, + "H": { + "id": "H", + "enochian": "H", + "title": "Na-hath", + "english": "H", + "pronounciation": "heh", + "planet/element": "air", + "tarot": "fool", + "gematria": 1 + }, + "I": { + "id": "I", + "enochian": "I", + "title": "Gon", + "english": "I,Y,J", + "pronounciation": "ee", + "planet/element": "sagittarius", + "tarot": "temperance", + "gematria": 60 + }, + "L": { + "id": "L", + "enochian": "L", + "title": "Ur", + "english": "L", + "pronounciation": "el, leh", + "planet/element": "cancer", + "tarot": "charit", + "gematria": 8 + }, + "M": { + "id": "M", + "enochian": "M", + "title": "Tal", + "english": "M", + "pronounciation": "em", + "planet/element": "aquarius", + "tarot": "emperor", + "gematria": 90 + }, + "N": { + "id": "N", + "enochian": "N", + "title": "Drun", + "english": "N", + "pronounciation": "en", + "planet/element": "scorpio", + "tarot": "death", + "gematria": 50 + }, + "O": { + "id": "O", + "enochian": "O", + "title": "Med", + "english": "O", + "pronounciation": "oh", + "planet/element": "libra", + "tarot": "justice", + "gematria": 30 + }, + "P": { + "id": "P", + "enochian": "P", + "title": "Mals", + "english": "P", + "pronounciation": "peh", + "planet/element": "leo", + "tarot": "strength", + "gematria": 9 + }, + "Q": { + "id": "Q", + "enochian": "Q", + "title": "Ger", + "english": "Q", + "pronounciation": "quo", + "planet/element": "water", + "tarot": "hanged man", + "gematria": 40 + }, + "R": { + "id": "R", + "enochian": "R", + "title": "Don", + "english": "R", + "pronounciation": "reh, ar", + "planet/element": "pesces", + "tarot": "moon", + "gematria": 100 + }, + "S": { + "id": "S", + "enochian": "S", + "title": "Fam", + "english": "S", + "pronounciation": "she, ess", + "planet/element": "gemini", + "tarot": "lovers", + "gematria": 7 + }, + "T": { + "id": "T", + "enochian": "T", + "title": "Gisa", + "english": "T", + "pronounciation": "teh", + "planet/element": "leo", + "tarot": "strength", + "gematria": 9 + }, + "V": { + "id": "V", + "enochian": "V", + "title": "Vau", + "english": "U,V,W", + "pronounciation": "vaa", + "planet/element": "capricorn", + "tarot": "devil", + "gematria": 70 + }, + "X": { + "id": "X", + "enochian": "X", + "title": "Pal", + "english": "X", + "pronounciation": "s, tz", + "planet/element": "earth", + "tarot": "universe", + "gematria": 400 + }, + "Z": { + "id": "Z", + "enochian": "Z", + "title": "Ceph", + "english": "Z", + "pronounciation": "zod, zee", + "planet/element": "leo", + "tarot": "strength", + "gematria": 9 + } +} diff --git a/data/enochian/tablets.json b/data/enochian/tablets.json new file mode 100644 index 0000000..fe77b5f --- /dev/null +++ b/data/enochian/tablets.json @@ -0,0 +1,376 @@ +{ + "earth": { + "id": "earth", + "grid": [ + [ + "b", + "O", + "a", + "Z", + "a", + "R", + "o", + "P", + "h", + "a", + "R", + "a" + ], + [ + "v", + "N", + "n", + "a", + "x", + "o", + "P", + "S", + "o", + "n", + "d", + "n" + ], + [ + "a", + "i", + "g", + "r", + "a", + "n", + "o", + "o", + "m", + "a", + "g", + "g" + ], + [ + "o", + "r", + "P", + "m", + "n", + "i", + "n", + "g", + "b", + "e", + "a", + "l" + ], + [ + "r", + "s", + "O", + "n", + "i", + "z", + "i", + "r", + "l", + "e", + "m", + "v" + ], + [ + "i", + "z", + "i", + "n", + "r", + "C", + "z", + "i", + "a", + "M", + "h", + "l" + ], + [ + "M", + "O", + "r", + "d", + "i", + "a", + "l", + "h", + "C", + "t", + "G", + "a" + ], + [ + "O", + "C", + "a", + "n", + "c", + "h", + "i", + "a", + "s", + "o", + "m", + "t" + ], + [ + "A", + "r", + "b", + "i", + "z", + "m", + "i", + "i", + "l", + "P", + "i", + "z" + ], + [ + "O", + "P", + "a", + "n", + "a", + "L", + "a", + "m", + "S", + "m", + "a", + "P" + ], + [ + "d", + "O", + "l", + "o", + "P", + "i", + "n", + "i", + "a", + "n", + "b", + "a" + ], + [ + "r", + "x", + "P", + "a", + "o", + "c", + "s", + "i", + "z", + "i", + "x", + "P" + ], + [ + "a", + "x", + "t", + "i", + "r", + "V", + "a", + "s", + "t", + "r", + "i", + "m" + ] + ] + }, + "air": { + "id": "air", + "grid": [ + [ + "r", + "Z", + "i", + "l", + "a", + "f", + "A", + "Y", + "t", + "l", + "P", + "a" + ], + [ + "a", + "r", + "d", + "Z", + "a", + "i", + "d", + "P", + "a", + "L", + "a", + "m" + ], + [ + "c", + "z", + "o", + "n", + "s", + "a", + "r", + "o", + "Y", + "a", + "v", + "b" + ], + [ + "T", + "o", + "i", + "T", + "t", + "z", + "o", + "P", + "a", + "c", + "o", + "C" + ], + [ + "S", + "i", + "g", + "a", + "s", + "o", + "m", + "r", + "b", + "z", + "n", + "h" + ], + [ + "f", + "m", + "o", + "n", + "d", + "a", + "T", + "d", + "i", + "a", + "r", + "i" + ], + [ + "o", + "r", + "o", + "i", + "b", + "A", + "h", + "a", + "o", + "z", + "P", + "i" + ], + [ + "t", + "N", + "a", + "b", + "r", + "V", + "i", + "x", + "g", + "a", + "s", + "d" + ], + [ + "O", + "i", + "i", + "i", + "t", + "T", + "P", + "a", + "l", + "O", + "a", + "i" + ], + [ + "A", + "b", + "a", + "m", + "o", + "o", + "o", + "a", + "C", + "v", + "c", + "a" + ], + [ + "N", + "a", + "o", + "c", + "O", + "T", + "t", + "n", + "P", + "r", + "n", + "T" + ], + [ + "o", + "c", + "a", + "n", + "m", + "a", + "g", + "o", + "t", + "r", + "o", + "i" + ], + [ + "S", + "h", + "i", + "a", + "l", + "r", + "a", + "P", + "m", + "z", + "o", + "x" + ] + ] + } +} diff --git a/data/gd/degrees.json b/data/gd/degrees.json new file mode 100644 index 0000000..84f3c32 --- /dev/null +++ b/data/gd/degrees.json @@ -0,0 +1,14 @@ +{ + "1st": { + "id": "1st", + "pillarId": "severity" + }, + "2nd": { + "id": "2nd", + "pillarId": "mercy" + }, + "3rd": { + "id": "3rd", + "pillarId": "middle" + } +} diff --git a/data/gd/grades.json b/data/gd/grades.json new file mode 100644 index 0000000..d10717f --- /dev/null +++ b/data/gd/grades.json @@ -0,0 +1,115 @@ +{ + "0=0": { + "id": "0=0", + "name": "Neophyte", + "orderId": "1st", + "degreeId": "1st", + "next": "1=10" + }, + "1=10": { + "id": "1=10", + "name": "Zelator", + "elementId": "earth", + "planetId": "earth", + "orderId": "1st", + "degreeId": "1st", + "sephirahId": "malchut", + "prev": "0=0", + "next": "2=9" + }, + "2=9": { + "id": "2=9", + "name": "Theoricus", + "elementId": "air", + "planetId": "luna", + "orderId": "1st", + "degreeId": "1st", + "sephirahId": "yesod", + "prev": "1=10", + "next": "3=8" + }, + "3=8": { + "id": "3=8", + "name": "Practicus", + "elementId": "water", + "planetId": "mercury", + "orderId": "1st", + "degreeId": "1st", + "sephirahId": "hod", + "prev": "2=9", + "next": "4=7" + }, + "4=7": { + "id": "4=7", + "name": "Philosophus", + "elementId": "fire", + "planetId": "venus", + "orderId": "1st", + "degreeId": "1st", + "sephirahId": "netzach", + "prev": "3=8", + "next": "portal" + }, + "portal": { + "id": "portal", + "name": "Portal", + "elementId": "spirit", + "degreeId": "2nd", + "prev": "4=7", + "next": "5=6" + }, + "5=6": { + "id": "5=6", + "name": "Adeptus Minor", + "planetId": "sol", + "orderId": "2nd", + "degreeId": "3rd", + "sephirahId": "tiferet", + "prev": "portal", + "next": "6=5" + }, + "6=5": { + "id": "6=5", + "name": "Adeptus Major", + "planetId": "mars", + "orderId": "2nd", + "degreeId": "3rd", + "sephirahId": "gevurah", + "prev": "5=6", + "next": "7=4" + }, + "7=4": { + "id": "7=4", + "name": "Adeptus Exemptus", + "planetId": "jupiter", + "orderId": "2nd", + "degreeId": "3rd", + "sephirahId": "hesed", + "prev": "6=5", + "next": "8=3" + }, + "8=3": { + "id": "8=3", + "name": "Magister Templi", + "planetId": "saturn", + "orderId": "3rd", + "sephirahId": "binah", + "prev": "7=4", + "next": "9=2" + }, + "9=2": { + "id": "9=2", + "name": "Magus", + "orderId": "3rd", + "sephirahId": "chochmah", + "prev": "8=3", + "next": "10=1" + }, + "10=1": { + "id": "10=1", + "name": "Ipsissimus", + "orderId": "3rd", + "sephirahId": "keter", + "prev": "9=2" + } +} diff --git a/data/gematria-ciphers.json b/data/gematria-ciphers.json new file mode 100644 index 0000000..e2ec029 --- /dev/null +++ b/data/gematria-ciphers.json @@ -0,0 +1,33 @@ +{ + "meta": { + "description": "Positional gematria ciphers based on a single universal alphabet.", + "notes": "All ciphers map A-Z positions (1-26) to numeric values." + }, + "baseAlphabet": "abcdefghijklmnopqrstuvwxyz", + "ciphers": [ + { + "id": "simple-ordinal", + "name": "Simple Ordinal", + "description": "A=1 ... Z=26", + "values": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26] + }, + { + "id": "full-reduction", + "name": "Full Reduction", + "description": "A=1 ... I=9, J=1 ... Z=8", + "values": [1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8] + }, + { + "id": "reverse-ordinal", + "name": "Reverse Ordinal", + "description": "A=26 ... Z=1", + "values": [26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1] + }, + { + "id": "ordinal-plus-9", + "name": "Ordinal +9", + "description": "A=10 ... Z=35 (e.g. 11th position = 20)", + "values": [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35] + } + ] +} diff --git a/data/geomancy/houses.json b/data/geomancy/houses.json new file mode 100644 index 0000000..4ef2f7f --- /dev/null +++ b/data/geomancy/houses.json @@ -0,0 +1,75 @@ +[ + {}, + { + "id": 1, + "meaning": { + "en": "Personality of the Querent, his life, health, habits, behavior and other personal characteristics." + } + }, + { + "id": 2, + "meaning": { + "en": "Money, property, finances, possible theft through negligence or loss." + } + }, + { + "id": 3, + "meaning": { + "en": "Communication, relatives by blood, letters, news, short journeys, writing, other pursuits associated with Mercury." + } + }, + { + "id": 4, + "meaning": { + "en": "Home, inheritance, possessions, hidden treasure, environment, retirement, the conclusion of any matter. Information about theft or thieves." + } + }, + { + "id": 5, + "meaning": { + "en": "Women, luxury, drinking, procreation, creation, artistic work. Gambling and speculation." + } + }, + { + "id": 6, + "meaning": { + "en": "Employees, sickness, which body parts are most likely to suffer illness or injury. Domestic animals and close relatives other than immediate family." + } + }, + { + "id": 7, + "meaning": { + "en": "Love, marriage, prostitution, partnerships. Public enemies, lawsuits, war opponents, thieves and dishonor." + } + }, + { + "id": 8, + "meaning": { + "en": "Death, Wills, legacies. Poverty." + } + }, + { + "id": 9, + "meaning": { + "en": "Long journeys, relationships with foreigners, science, the church, art, religion dreams and divinations." + } + }, + { + "id": 10, + "meaning": { + "en": "Fame, reputation, honor, mother, trade authority." + } + }, + { + "id": 11, + "meaning": { + "en": "Friends, social contacts, altruistic organizations." + } + }, + { + "id": 12, + "meaning": { + "en": "Sorrow, hospitals, intrigue, prisons, restrictions, unseen dangers, fears." + } + } +] diff --git a/data/geomancy/tetragrams.json b/data/geomancy/tetragrams.json new file mode 100644 index 0000000..c3fe842 --- /dev/null +++ b/data/geomancy/tetragrams.json @@ -0,0 +1,990 @@ +{ + "acquisitio": { + "id": "acquisitio", + "rows": [ + 2, + 1, + 2, + 1 + ], + "title": { + "en": "Acquisitio" + }, + "translation": { + "en": "gain" + }, + "meaning": { + "en": "Generally good for profit and gain." + }, + "meanings": [ + {}, + { + "en": "Happy, success in all things." + }, + { + "en": "Very prosperous." + }, + { + "en": "Favor and riches." + }, + { + "en": "Good fortune and success." + }, + { + "en": "Good success." + }, + { + "en": "Good - especially if it agrees with the 5th." + }, + { + "en": "Reasonably good." + }, + { + "en": "Rather good, but not very. The sick shall die." + }, + { + "en": "Good in all demands." + }, + { + "en": "Good in suits." + }, + { + "en": "Good in all." + }, + { + "en": "Unfavourable, pain and loss." + } + ], + "zodiacId": "sagittarius", + "elementId": "fire", + "planetId": "jupiter", + "rulerId": "hismael" + }, + "albus": { + "id": "albus", + "rows": [ + 2, + 2, + 1, + 2 + ], + "title": { + "en": "Albus" + }, + "translation": { + "en": "white" + }, + "meaning": { + "en": "Good for profit and for entering into a place or undertaking." + }, + "meanings": [ + {}, + { + "en": "Good for marriage. Mercurial. Peace." + }, + { + "en": "Good in all." + }, + { + "en": "Very good." + }, + { + "en": "Very good except in War." + }, + { + "en": "Good." + }, + { + "en": "Good in all things." + }, + { + "en": "Good except in all things." + }, + { + "en": "Good." + }, + { + "en": "A messenger brings a letter." + }, + { + "en": "Excellent in all." + }, + { + "en": "Very good." + }, + { + "en": "Marvelously good." + } + ], + "zodiacId": "gemini", + "elementId": "air", + "planetId": "mercury", + "rulerId": "taphtatharath" + }, + "amissio": { + "id": "amissio", + "title": { + "en": "Amissio" + }, + "rows": [ + 1, + 2, + 1, + 2 + ], + "translation": { + "en": "loss" + }, + "meaning": { + "en": "Good for loss of substance and sometimes for love; but very bad for gain." + }, + "meanings": [ + {}, + { + "en": "Ill in all things but for prisoners." + }, + { + "en": "Very Ill for money, but good for love." + }, + { + "en": "Ill end-except for quarrels." + }, + { + "en": "Ill in all." + }, + { + "en": "Unfavourable except for agriculture." + }, + { + "en": "Rather unfavourable for love." + }, + { + "en": "Very good for love, otherwise unfavourable." + }, + { + "en": "Excellent in all questions." + }, + { + "en": "Unfavourable in all things." + }, + { + "en": "Unfavourable except for favor with women." + }, + { + "en": "Good for love, otherwise bad." + }, + { + "en": "Unfavourable in all things." + } + ], + "zodiacId": "taurus", + "elementId": "earth", + "planetId": "venus", + "rulerId": "kedemel" + }, + "caput_draconis": { + "id": "caput_draconis", + "rows": [ + 2, + 1, + 1, + 1 + ], + "title": { + "en": "Caput Draconis" + }, + "translation": { + "en": "dragon's head" + }, + "meaning": { + "en": "Good with good; evil with evil. Gives good issue for gain." + }, + "meanings": [ + {}, + { + "en": "Good in all things." + }, + { + "en": "Good." + }, + { + "en": "Very good." + }, + { + "en": "Good save in war." + }, + { + "en": "Very good." + }, + { + "en": "Good for immorality only." + }, + { + "en": "Good especially for peace." + }, + { + "en": "Good." + }, + { + "en": "Very good." + }, + { + "en": "Good in all." + }, + { + "en": "Good for the church and ecclesiastical gain." + }, + { + "en": "Not very good." + } + ], + "zodiacId": null, + "elementId": "earth", + "planetId": [ + "venus", + "jupiter" + ], + "rulerId": [ + "kedemel", + "hismael" + ] + }, + "carcer": { + "id": "carcer", + "rows": [ + 1, + 2, + 2, + 1 + ], + "title": { + "en": "Carcer" + }, + "translation": { + "en": "prison" + }, + "meaning": { + "en": "Generally unfavourable. Delay, binding, bar, restriction." + }, + "meanings": [ + {}, + { + "en": "Unfavourable except to fortify a place." + }, + { + "en": "Good in Saturnine questions; else unfavourable." + }, + { + "en": "Unfavourable." + }, + { + "en": "Good only for melancholy." + }, + { + "en": "Receive a letter within three days. Unfavourable." + }, + { + "en": "Very unfavourable." + }, + { + "en": "Unfavourable." + }, + { + "en": "Very unfavourable." + }, + { + "en": "Unfavourable in all." + }, + { + "en": "Unfavourable save in hldden treasure." + }, + { + "en": "Much anxiety." + }, + { + "en": "Rather good." + } + ], + "zodiacId": "capricorn", + "elementId": "earth", + "planetId": "saturn", + "rulerId": "zazel" + }, + "cauda_draconis": { + "id": "cauda_draconis", + "title": { + "en": "Cauda Draconis" + }, + "rows": [ + 1, + 1, + 1, + 2 + ], + "translation": { + "en": "dragon's tail" + }, + "meaning": { + "en": "Good with evil, and evil with good. Good for loss, and for passing out of an affair." + }, + "meanings": [ + {}, + { + "en": "Destroy figure if it falls here! Makes judgment worthless." + }, + { + "en": "Very unfavourable." + }, + { + "en": "Unfavourable in all." + }, + { + "en": "Good especially for conclusion of the matter." + }, + { + "en": "Very unfavourable." + }, + { + "en": "Rather good." + }, + { + "en": "Unfavourable, war, and fire." + }, + { + "en": "No good, except for magic." + }, + { + "en": "Good for science only. Bad for journeys. Robbery." + }, + { + "en": "Unfavourable save in works of fire." + }, + { + "en": "Unfavourable save for favors." + }, + { + "en": "Rather good." + } + ], + "zodiacId": null, + "elementId": "fire", + "planetId": [ + "saturn", + "mars" + ], + "rulerId": [ + "zazel", + "bartzabel" + ] + }, + "conjunctio": { + "id": "conjunctio", + "rows": [ + 2, + 1, + 1, + 2 + ], + "title": { + "en": "Conjunctio" + }, + "translation": { + "en": "conjunction" + }, + "meaning": { + "en": "Good with good, or evil with evil. Recovery from things lost." + }, + "meanings": [ + {}, + { + "en": "Good with good, evil with evil." + }, + { + "en": "Commonly good." + }, + { + "en": "Good fortune." + }, + { + "en": "Good save for health; see the eighth." + }, + { + "en": "Medium." + }, + { + "en": "Good for immorality only." + }, + { + "en": "Rather good." + }, + { + "en": "Unfavourable, death." + }, + { + "en": "Medium good." + }, + { + "en": "For love, good. For sickness, unfavourable." + }, + { + "en": "Good in all." + }, + { + "en": "Medium. Bad for prisoners" + } + ], + "zodiacId": "virgo", + "elementId": "earth", + "planetId": "mercury", + "rulerId": "taphthartharath" + }, + "fortuna_major": { + "id": "fortuna_major", + "rows": [ + 2, + 2, + 1, + 1 + ], + "title": { + "en": "Fortuna Major" + }, + "translation": { + "en": "greater fortune" + }, + "meaning": { + "en": "Good for gain in all things where a person has hopes to win." + }, + "meanings": [ + {}, + { + "en": "Good save in secrecy." + }, + { + "en": "Good except in sad things." + }, + { + "en": "Good in all." + }, + { + "en": "Good in all, but melancholy." + }, + { + "en": "Very good in all things." + }, + { + "en": "Very good except for debauchery." + }, + { + "en": "Good in all." + }, + { + "en": "Moderately good." + }, + { + "en": "Very good." + }, + { + "en": "Exceedingly good. Go to superiors." + }, + { + "en": "Very good." + }, + { + "en": "Good in all." + } + ], + "zodiacId": "leo", + "elementId": "fire", + "planetId": "sol", + "rulerId": "sorath" + }, + "fortuna_minor": { + "id": "fortuna_minor", + "rows": [ + 1, + 1, + 2, + 2 + ], + "title": { + "en": "Fortuna Minor" + }, + "translation": { + "en": "lesser fortune" + }, + "meaning": { + "en": "Good in any manner in which a person wishes to proceed quickly." + }, + "meanings": [ + {}, + { + "en": "Speed in victory and in love, but choleric." + }, + { + "en": "Very good." + }, + { + "en": "Good - but wrathful." + }, + { + "en": "Haste; rather unfavourable except for peace." + }, + { + "en": "Good in all things." + }, + { + "en": "Medium in all." + }, + { + "en": "Unfavourable except for war or love." + }, + { + "en": "Unfavourable generally." + }, + { + "en": "Good, but choleric." + }, + { + "en": "Good, except for peace." + }, + { + "en": "Good, especially for love." + }, + { + "en": "Good, except for alternation, or for serving another." + } + ], + "zodiacId": "leo", + "elementId": "fire", + "planetId": "sol", + "rulerId": "sorath" + }, + "laetitia": { + "id": "laetitia", + "rows": [ + 1, + 2, + 2, + 2 + ], + "title": { + "en": "Laetitia" + }, + "translation": { + "en": "joy" + }, + "meaning": { + "en": "Good for joy, present or to come." + }, + "meanings": [ + {}, + { + "en": "Good, except in war." + }, + { + "en": "Sickly." + }, + { + "en": "Ill." + }, + { + "en": "Mainly good." + }, + { + "en": "Excellently good." + }, + { + "en": "Unfavourable generally." + }, + { + "en": "Indifferent." + }, + { + "en": "Unfavourable generally." + }, + { + "en": "Very good." + }, + { + "en": "Good, rather in war than in peace." + }, + { + "en": "Good in all." + }, + { + "en": "Unfavourable generally." + } + ], + "zodiacId": "pisces", + "elementId": "water", + "planetId": "jupiter", + "rulerId": "hismael" + }, + "populus": { + "id": "populus", + "rows": [ + 2, + 2, + 2, + 2 + ], + "title": { + "en": "Populus" + }, + "translation": { + "en": "people" + }, + "meaning": { + "en": "Sometimes good and sometimes bad; good with good, and evil with evil." + }, + "meanings": [ + {}, + { + "en": "Good in marriages." + }, + { + "en": "Medium good." + }, + { + "en": "Rather good than bad." + }, + { + "en": "Good in all but love." + }, + { + "en": "Good in most things." + }, + { + "en": "Good." + }, + { + "en": "In war good; else medium." + }, + { + "en": "Unfavourable." + }, + { + "en": "Look for letters." + }, + { + "en": "Good." + }, + { + "en": "Good in all." + }, + { + "en": "Very unfavourable." + } + ], + "zodiacId": "cancer", + "elementId": "water", + "rulerId": "chasmodai", + "planetId": "luna" + }, + "puella": { + "id": "puella", + "rows": [ + 1, + 2, + 1, + 1 + ], + "title": { + "en": "Puella" + }, + "translation": { + "en": "girl" + }, + "meaning": { + "en": "Good in all demands, especially in those relating to women." + }, + "meanings": [ + {}, + { + "en": "Good except in war." + }, + { + "en": "Very good." + }, + { + "en": "Good." + }, + { + "en": "Indifferent." + }, + { + "en": "Very good, but notice the aspects." + }, + { + "en": "Good, but especially for debauchery." + }, + { + "en": "Good except for war." + }, + { + "en": "Good." + }, + { + "en": "Good for music. Otherwise only medium." + }, + { + "en": "Good for peace." + }, + { + "en": "Good, and love ofladies." + }, + { + "en": "Good in all." + } + ], + "zodiacId": "libra", + "elementId": "air", + "planetId": "venus", + "rulerId": "kedemel" + }, + "puer": { + "id": "puer", + "rows": [ + 1, + 1, + 2, + 1 + ], + "title": { + "en": "Puer" + }, + "translation": { + "en": "boy" + }, + "meaning": { + "en": "Unfavourable in most demands, except in those relating to War or Love." + }, + "meanings": [ + {}, + { + "en": "Indifferent. Best in War." + }, + { + "en": "Good, but with trouble" + }, + { + "en": "Good fortune." + }, + { + "en": "Unfavourable, except in war and love." + }, + { + "en": "Medium good." + }, + { + "en": "Medium." + }, + { + "en": "Unfavourable, save in War." + }, + { + "en": "Unfavourable, save for love." + }, + { + "en": "Unfavourable except for War." + }, + { + "en": "Rather unfavourable. But good for love and War. Most other things medium." + }, + { + "en": "Medium; good favor." + }, + { + "en": "Very good in all." + } + ], + "elementId": "fire", + "zodiacId": "aries", + "planetId": "mars", + "rulerId": "bartzabel" + }, + "rubeus": { + "id": "rubeus", + "rows": [ + 2, + 1, + 2, + 2 + ], + "title": { + "en": "Rubeus" + }, + "translation": { + "en": "red" + }, + "meaning": { + "en": "Unfavourable in all that is good and Good in all that is Unfavourable." + }, + "meanings": [ + {}, + { + "en": "Destroy the figure if it falls here! It makes the judgment worthless." + }, + { + "en": "Unfavourable in all demands." + }, + { + "en": "Unfavourable except to let blood." + }, + { + "en": "Unfavourable except in War and Fire." + }, + { + "en": "Unfavourable save for love, and sowing seed." + }, + { + "en": "Unfavourable except for bloodletting." + }, + { + "en": "Unfavourable except for war and fire." + }, + { + "en": "Unfavourable." + }, + { + "en": "Very Unfavourable." + }, + { + "en": "Dissolute. Love, fire." + }, + { + "en": "Unfavourable, except to let blood." + }, + { + "en": "Unfavourable in all things." + } + ], + "zodiacId": "scorpio", + "elementId": "water", + "planetId": "mars", + "rulerId": "bartzabel" + }, + "tristitia": { + "id": "tristitia", + "rows": [ + 2, + 2, + 2, + 1 + ], + "title": { + "en": "Tristitia" + }, + "translation": { + "en": "sorrow" + }, + "meaning": { + "en": "Unfavourable in almost all things." + }, + "meanings": [ + {}, + { + "en": "Medium, but good for treasure and fortifying." + }, + { + "en": "Medium, but good to fortify." + }, + { + "en": "Unfavourable in all." + }, + { + "en": "Unfavourable in all." + }, + { + "en": "Very Unfavourable." + }, + { + "en": "Unfavourable, except for debauchery." + }, + { + "en": "Unfavourable for inheritance and magic only." + }, + { + "en": "Unfavourable, but in secrecy good." + }, + { + "en": "Unfavourable except for magic." + }, + { + "en": "Unfavourable except for fortifications." + }, + { + "en": "Unfavourable in all." + }, + { + "en": "Unfavourable. But good for magic and treasure." + } + ], + "zodiacId": "aquarius", + "elementId": "air", + "planetId": "saturn", + "rulerId": "zazel" + }, + "via": { + "id": "via", + "rows": [ + 1, + 1, + 1, + 1 + ], + "title": { + "en": "Via" + }, + "translation": { + "en": "way" + }, + "meaning": { + "en": "Injurious to the goodness of other figures generally, but good for journeys and voyages." + }, + "meanings": [ + {}, + { + "en": "Unfavourable except for prison." + }, + { + "en": "Indifferent." + }, + { + "en": "Very good in all." + }, + { + "en": "Good in all save love." + }, + { + "en": "Voyages good." + }, + { + "en": "Unfavourable." + }, + { + "en": "Rather good, especially for voyages." + }, + { + "en": "Unfavourable." + }, + { + "en": "Indifferent. good for journeys." + }, + { + "en": "Good." + }, + { + "en": "Very good." + }, + { + "en": "Excellent." + } + ], + "zodiacId": "cancer", + "elementId": "water", + "planetId": "luna", + "rulerId": "chasmodai" + } +} diff --git a/data/gods.json b/data/gods.json new file mode 100644 index 0000000..3409046 --- /dev/null +++ b/data/gods.json @@ -0,0 +1,578 @@ +{ + "source": "Liber 777 by Aleister Crowley / adamblvck/open_777 dataset", + "description": "Divine correspondences mapped to the 32 paths of the Kabbalistic Tree of Life. Rows 1–10 are the ten Sephiroth; rows 11–32 are the 22 Hebrew letter paths. Row 0 is Ain Soph Aur (the limitless light beyond the Tree).", + "pantheons": [ + { "id": "greek", "label": "Greek Gods", "emoji": "🏛️", "source": "Liber 777, Col. XXXIV" }, + { "id": "roman", "label": "Roman Gods", "emoji": "🦅", "source": "Liber 777, Col. XXXV" }, + { "id": "egyptian", "label": "Egyptian Gods", "emoji": "𓂀", "source": "Liber 777, Cols. XIX (Selection) & XX (Practical)" }, + { "id": "elohim", "label": "God Names", "emoji": "✡️", "source": "Liber 777, Col. V — God Names in Assiah (Sephiroth)" }, + { "id": "archangels", "label": "Archangels", "emoji": "☀️", "source": "Liber 777, Col. XCIX — Archangels of Assiah (Sephiroth)" }, + { "id": "angelicOrders", "label": "Angelic Orders", "emoji": "✨", "source": "Liber 777, Col. C — Angels of Assiah (Sephiroth)" } + ], + "byPath": { + "0": { + "type": "special", + "label": "Ain Soph Aur", + "description": "The Limitless Light — three veils of negative existence beyond the Tree of Life", + "greek": "Pan", + "roman": null, + "egyptian": "Harpocrates, Amoun, Nuith", + "egyptianPractical": "Heru-pa-Kraath", + "hindu": "AUM", + "elohim": null, + "archangel": null, + "angelicOrder": null + }, + "1": { + "type": "sephirah", + "no": 1, + "name": "Kether", + "meaning": "The Crown", + "greek": "Zeus, Ikarus", + "roman": "Jupiter", + "egyptian": "Ptah, Asar un Nefer, Hadith", + "egyptianPractical": "Ptah", + "scandinavian": "Wotan", + "hindu": "Parabrahm", + "elohim": { "hebrew": "אהיה", "transliteration": "Eheieh", "meaning": "I Am / Being" }, + "archangel": { "hebrew": "מטטרון", "transliteration": "Metatron" }, + "angelicOrder": { "hebrew": "חיות הקדש", "transliteration": "Chaioth ha-Qodesh", "meaning": "Holy Living Creatures" } + }, + "2": { + "type": "sephirah", + "no": 2, + "name": "Chokmah", + "meaning": "Wisdom", + "greek": "Athena, Uranus", + "roman": "Janus", + "egyptian": "Amoun, Thoth, Nuith", + "egyptianPractical": "Isis (as Wisdom)", + "scandinavian": "Odin", + "hindu": "Shiva, Vishnu, Akasa, Lingam", + "elohim": { "hebrew": "יה", "transliteration": "Yah", "meaning": "The Lord" }, + "archangel": { "hebrew": "רציאל", "transliteration": "Ratziel" }, + "angelicOrder": { "hebrew": "אופנים", "transliteration": "Auphanim", "meaning": "Wheels" } + }, + "3": { + "type": "sephirah", + "no": 3, + "name": "Binah", + "meaning": "Understanding", + "greek": "Cybele, Demeter, Rhea, Heré, Kronos, Psyché", + "roman": "Juno, Cybele, Hecate", + "egyptian": "Maut, Isis, Nephthys", + "egyptianPractical": "Nephthys", + "scandinavian": "Frigga", + "hindu": "Bhavani, Prana, Yoni", + "elohim": { "hebrew": "יהוה אלהים", "transliteration": "YHVH Elohim", "meaning": "The Lord God" }, + "archangel": { "hebrew": "צאפקיאל", "transliteration": "Tzaphkiel" }, + "angelicOrder": { "hebrew": "אראלים", "transliteration": "Aralim", "meaning": "Strong and Mighty Ones / Thrones" } + }, + "4": { + "type": "sephirah", + "no": 4, + "name": "Chesed", + "meaning": "Mercy", + "greek": "Poseidon, Zeus", + "roman": "Jupiter", + "egyptian": "Amoun, Isis", + "egyptianPractical": "Amoun", + "scandinavian": "Wotan", + "hindu": "Indra, Brahma", + "elohim": { "hebrew": "אל", "transliteration": "El", "meaning": "God (the Strong)" }, + "archangel": { "hebrew": "צדקיאל", "transliteration": "Tzadkiel" }, + "angelicOrder": { "hebrew": "חשמלים", "transliteration": "Chashmalim", "meaning": "Brilliant / Shining Ones" } + }, + "5": { + "type": "sephirah", + "no": 5, + "name": "Geburah", + "meaning": "Strength / Severity", + "greek": "Ares, Hades", + "roman": "Mars", + "egyptian": "Horus, Nephthys", + "egyptianPractical": "Horus", + "scandinavian": "Thor", + "hindu": "Vishnu, Varruna-Avatar", + "elohim": { "hebrew": "אלהים גבור", "transliteration": "Elohim Gibor", "meaning": "Almighty God" }, + "archangel": { "hebrew": "כמאל", "transliteration": "Kamael" }, + "angelicOrder": { "hebrew": "שרפים", "transliteration": "Seraphim", "meaning": "Fiery Serpents" } + }, + "6": { + "type": "sephirah", + "no": 6, + "name": "Tiphareth", + "meaning": "Beauty", + "greek": "Ikarus, Apollo, Adonis, Dionysis, Bacchus", + "roman": "Apollo, Bacchus, Aurora", + "egyptian": "Asar, Ra", + "egyptianPractical": "Ra", + "scandinavian": null, + "hindu": "Vishnu-Hari-Krishna-Rama", + "elohim": { "hebrew": "יהוה אלוה ודעת", "transliteration": "YHVH Eloah ve-Daath", "meaning": "God manifest in the Sphere of the Mind" }, + "archangel": { "hebrew": "רפאל", "transliteration": "Raphael" }, + "angelicOrder": { "hebrew": "מלכים", "transliteration": "Malakim", "meaning": "Kings" } + }, + "7": { + "type": "sephirah", + "no": 7, + "name": "Netzach", + "meaning": "Victory", + "greek": "Aphrodite, Nike", + "roman": "Venus", + "egyptian": "Hathoor", + "egyptianPractical": "Hathoor", + "scandinavian": "Freya", + "hindu": "Bhavani (etc.)", + "elohim": { "hebrew": "יהוה צבאות", "transliteration": "YHVH Tzabaoth", "meaning": "The Lord of Hosts" }, + "archangel": { "hebrew": "חאניאל", "transliteration": "Haniel" }, + "angelicOrder": { "hebrew": "אלהים", "transliteration": "Elohim", "meaning": "Gods" } + }, + "8": { + "type": "sephirah", + "no": 8, + "name": "Hod", + "meaning": "Splendour", + "greek": "Hermes", + "roman": "Mercury", + "egyptian": "Anubis", + "egyptianPractical": "Thoth", + "scandinavian": "Odin, Loki", + "hindu": "Hanuman", + "elohim": { "hebrew": "אלהים צבאות", "transliteration": "Elohim Tzabaoth", "meaning": "God of Hosts" }, + "archangel": { "hebrew": "מיכאל", "transliteration": "Michael" }, + "angelicOrder": { "hebrew": "בני אלהים", "transliteration": "Beni Elohim", "meaning": "Sons of God" } + }, + "9": { + "type": "sephirah", + "no": 9, + "name": "Yesod", + "meaning": "Foundation", + "greek": "Zeus (as Air), Diana of Ephesus, Eros", + "roman": "Diana (as Water), Terminus, Jupiter", + "egyptian": "Shu, Hermanubis", + "egyptianPractical": "Shu", + "scandinavian": null, + "hindu": "Ganesha, Vishnu (Kurm Avatar)", + "elohim": { "hebrew": "שדי אל חי", "transliteration": "Shaddai El Chai", "meaning": "Almighty Living God" }, + "archangel": { "hebrew": "גבריאל", "transliteration": "Gabriel" }, + "angelicOrder": { "hebrew": "כרובים", "transliteration": "Kerubim", "meaning": "Angels of the Elements" } + }, + "10": { + "type": "sephirah", + "no": 10, + "name": "Malkuth", + "meaning": "The Kingdom", + "greek": "Persephone, Adonis, Psyche", + "roman": "Ceres", + "egyptian": "Seb, Isis and Nephthys", + "egyptianPractical": "Osiris", + "scandinavian": null, + "hindu": "Lakshmi", + "elohim": { "hebrew": "אדני מלך", "transliteration": "Adonai Melek", "meaning": "Lord and King" }, + "archangel": { "hebrew": "סנדלפון", "transliteration": "Sandalphon" }, + "angelicOrder": { "hebrew": "אשים", "transliteration": "Ashim", "meaning": "Flames / Souls of Fire" } + }, + "11": { + "type": "path", + "no": 11, + "hebrewLetter": "Aleph", + "hebrewChar": "א", + "tarot": "The Fool", + "attribute": "Air", + "greek": "Zeus", + "roman": "Jupiter, Juno, Æolus", + "egyptian": "Nu, Hoor-pa-kraat", + "egyptianPractical": "Mout", + "elohim": { "hebrew": "יהוה", "transliteration": "YHVH" }, + "archangel": null, + "angelicOrder": null + }, + "12": { + "type": "path", + "no": 12, + "hebrewLetter": "Beth", + "hebrewChar": "ב", + "tarot": "The Magician", + "attribute": "Mercury", + "greek": "Hermes", + "roman": "Mercury", + "egyptian": "Thoth and Cynocephalus", + "egyptianPractical": "Thoth", + "elohim": null, + "archangel": null, + "angelicOrder": null + }, + "13": { + "type": "path", + "no": 13, + "hebrewLetter": "Gimel", + "hebrewChar": "ג", + "tarot": "The High Priestess", + "attribute": "Luna", + "greek": "Artemis, Hekate", + "roman": "Diana (as Water), Terminus, Jupiter", + "egyptian": "Chomse", + "egyptianPractical": "Chomse", + "elohim": null, + "archangel": null, + "angelicOrder": null + }, + "14": { + "type": "path", + "no": 14, + "hebrewLetter": "Daleth", + "hebrewChar": "ד", + "tarot": "The Empress", + "attribute": "Venus", + "greek": "Aphrodite", + "roman": "Venus", + "egyptian": "Hathor", + "egyptianPractical": "Hathoor", + "elohim": null, + "archangel": null, + "angelicOrder": null + }, + "15": { + "type": "path", + "no": 15, + "hebrewLetter": "Heh", + "hebrewChar": "ה", + "tarot": "The Emperor", + "attribute": "Aries", + "greek": "Athena", + "roman": "Mars, Minerva", + "egyptian": "Men Thu", + "egyptianPractical": "Isis", + "elohim": null, + "archangel": null, + "angelicOrder": null + }, + "16": { + "type": "path", + "no": 16, + "hebrewLetter": "Vav", + "hebrewChar": "ו", + "tarot": "The Hierophant", + "attribute": "Taurus", + "greek": "Heré", + "roman": "Venus, Hymen", + "egyptian": "Asar, Ameshet, Apis", + "egyptianPractical": "Osiris", + "elohim": null, + "archangel": null, + "angelicOrder": null + }, + "17": { + "type": "path", + "no": 17, + "hebrewLetter": "Zayin", + "hebrewChar": "ז", + "tarot": "The Lovers", + "attribute": "Gemini", + "greek": "Castor and Pollux, Apollo the Diviner, Eros", + "roman": "Castor and Pollux, Janus, Hymen", + "egyptian": "Twin Deities, Rekht, Merti", + "egyptianPractical": "The Twin Merti", + "elohim": null, + "archangel": null, + "angelicOrder": null + }, + "18": { + "type": "path", + "no": 18, + "hebrewLetter": "Cheth", + "hebrewChar": "ח", + "tarot": "The Chariot", + "attribute": "Cancer", + "greek": "Apollo The Charioteer", + "roman": "Mercury, Lares and Penates", + "egyptian": "Khephra", + "egyptianPractical": "Hormakhu", + "elohim": null, + "archangel": null, + "angelicOrder": null + }, + "19": { + "type": "path", + "no": 19, + "hebrewLetter": "Teth", + "hebrewChar": "ט", + "tarot": "Strength", + "attribute": "Leo", + "greek": "Demeter", + "roman": "Venus (Repressing Fire of Vulcan)", + "egyptian": "Ra-Hoor-Khuit, Pasht, Sekhet, Mau", + "egyptianPractical": "Horus", + "elohim": null, + "archangel": null, + "angelicOrder": null + }, + "20": { + "type": "path", + "no": 20, + "hebrewLetter": "Yod", + "hebrewChar": "י", + "tarot": "The Hermit", + "attribute": "Virgo", + "greek": "Attis", + "roman": "Attis, Ceres, Adonis, Vesta, Flora", + "egyptian": "Isis (as Virgin)", + "egyptianPractical": "Heru-pa-Kraath", + "elohim": null, + "archangel": null, + "angelicOrder": null + }, + "21": { + "type": "path", + "no": 21, + "hebrewLetter": "Kaph", + "hebrewChar": "כ", + "tarot": "Wheel of Fortune", + "attribute": "Jupiter", + "greek": "Zeus", + "roman": "Jupiter, Pluto", + "egyptian": "Amoun-Ra", + "egyptianPractical": "Amoun-Ra", + "elohim": { "hebrew": "אבא, אל אב", "transliteration": "Abba, El Ab", "meaning": "Father, God the Father" }, + "archangel": null, + "angelicOrder": null + }, + "22": { + "type": "path", + "no": 22, + "hebrewLetter": "Lamed", + "hebrewChar": "ל", + "tarot": "Justice", + "attribute": "Libra", + "greek": "Themis, Minos, Aecus and Rhadamanthus", + "roman": "Vulcan, Venus, Nemesis", + "egyptian": "Ma (Maat)", + "egyptianPractical": "Maat", + "elohim": null, + "archangel": null, + "angelicOrder": null + }, + "23": { + "type": "path", + "no": 23, + "hebrewLetter": "Mem", + "hebrewChar": "מ", + "tarot": "The Hanged Man", + "attribute": "Water", + "greek": "Poseidon", + "roman": "Neptune, Rhea", + "egyptian": "Tum, Ptah, Auramoth, Asar, Hekar, Isis", + "egyptianPractical": null, + "elohim": { "hebrew": "אל", "transliteration": "El", "meaning": "God (the Strong)" }, + "archangel": null, + "angelicOrder": null + }, + "24": { + "type": "path", + "no": 24, + "hebrewLetter": "Nun", + "hebrewChar": "נ", + "tarot": "Death", + "attribute": "Scorpio", + "greek": "Ares, Apollo the Pythean, Thanatos", + "roman": "Mars, Mors", + "egyptian": "Merti Goddesses, Typhon, Apep, Khephra", + "egyptianPractical": "Hammemit", + "elohim": null, + "archangel": null, + "angelicOrder": null + }, + "25": { + "type": "path", + "no": 25, + "hebrewLetter": "Samekh", + "hebrewChar": "ס", + "tarot": "Temperance", + "attribute": "Sagittarius", + "greek": "Apollo, Artemis (hunters)", + "roman": "Diana (Archer), Iris", + "egyptian": "Nephthys", + "egyptianPractical": null, + "elohim": null, + "archangel": null, + "angelicOrder": null + }, + "26": { + "type": "path", + "no": 26, + "hebrewLetter": "Ayin", + "hebrewChar": "ע", + "tarot": "The Devil", + "attribute": "Capricorn", + "greek": "Pan, Priapus", + "roman": "Pan, Vesta, Bacchus", + "egyptian": "Khem, Set", + "egyptianPractical": "Set", + "elohim": null, + "archangel": null, + "angelicOrder": null + }, + "27": { + "type": "path", + "no": 27, + "hebrewLetter": "Pe", + "hebrewChar": "פ", + "tarot": "The Tower", + "attribute": "Mars", + "greek": "Ares, Athena", + "roman": "Mars", + "egyptian": "Horus", + "egyptianPractical": "Mentu", + "elohim": { "hebrew": "אדני", "transliteration": "Adonai", "meaning": "My Lord" }, + "archangel": null, + "angelicOrder": null + }, + "28": { + "type": "path", + "no": 28, + "hebrewLetter": "Tzaddi", + "hebrewChar": "צ", + "tarot": "The Star", + "attribute": "Aquarius", + "greek": "Athena, Ganymede", + "roman": "Juno, Æolus", + "egyptian": "Ahepi, Aroueris", + "egyptianPractical": "Nuit", + "elohim": null, + "archangel": null, + "angelicOrder": null + }, + "29": { + "type": "path", + "no": 29, + "hebrewLetter": "Qoph", + "hebrewChar": "ק", + "tarot": "The Moon", + "attribute": "Pisces", + "greek": "Poseidon, Hermes Psychopompus", + "roman": "Neptune", + "egyptian": "Khephra (as Scarab)", + "egyptianPractical": "Anubi", + "elohim": null, + "archangel": null, + "angelicOrder": null + }, + "30": { + "type": "path", + "no": 30, + "hebrewLetter": "Resh", + "hebrewChar": "ר", + "tarot": "The Sun", + "attribute": "Sol", + "greek": "Helios, Apollo", + "roman": "Apollo, Ops", + "egyptian": "Ra", + "egyptianPractical": "Ra", + "elohim": { "hebrew": "אלה", "transliteration": "Elah", "meaning": "God" }, + "archangel": null, + "angelicOrder": null + }, + "31": { + "type": "path", + "no": 31, + "hebrewLetter": "Shin", + "hebrewChar": "ש", + "tarot": "The Aeon / Judgement", + "attribute": "Fire", + "greek": "Hades", + "roman": "Vulcan, Pluto", + "egyptian": "Thoum-Aesh-Neith, Mau, Kabeshunt, Horus, Tarpesheth", + "egyptianPractical": "Mau", + "elohim": { "hebrew": "אלהים", "transliteration": "Elohim", "meaning": "Gods (plural)" }, + "archangel": null, + "angelicOrder": null + }, + "32": { + "type": "path", + "no": 32, + "hebrewLetter": "Tav", + "hebrewChar": "ת", + "tarot": "The Universe", + "attribute": "Saturn", + "greek": "Athena", + "roman": "Saturn, Terminus, Astræa", + "egyptian": "Sebek, Mako", + "egyptianPractical": null, + "elohim": { "hebrew": "אב יה", "transliteration": "Ab Yah", "meaning": "Father, The Lord" }, + "archangel": null, + "angelicOrder": null + } + }, + "gods": [ + {"id":"zeus","name":"Zeus","pantheon":"greek","epithet":"King of the Gods · Cloud-Gatherer · Father of Men","role":"King of the Olympian Gods; ruler of sky, thunder, and divine law","domains":["sky","thunder","lightning","law","justice","kingship","hospitality"],"parents":["Kronos (Titan)","Rhea (Titaness)"],"siblings":["Hestia","Demeter","Hera","Hades","Poseidon"],"consorts":["Hera","Metis","Themis","Mnemosyne","Demeter","Leto","many others"],"children":["Athena","Apollo","Artemis","Hermes","Ares","Hephaestus","Dionysus","Persephone","Perseus","Heracles","Minos"],"symbols":["thunderbolt","eagle","oak tree","scales","sceptre"],"sacredAnimals":["eagle","bull","swan","cuckoo"],"sacredPlaces":["Mount Olympus","Dodona","Olympia"],"equivalents":["Jupiter (Roman)"],"kabbalahPaths":[1,4,9,11,21],"description":"Zeus was the youngest son of Kronos and Rhea, hidden from his father's devouring hunger and raised in secret on Crete. After overthrowing the Titans he drew lots with his brothers: the sky fell to Zeus, the sea to Poseidon, and the underworld to Hades. His rule is based on cosmic law (themis) rather than brute force, though both are available to him.","myth":"","poem":""}, + {"id":"hera","name":"Hera","pantheon":"greek","epithet":"Queen of the Gods · Queen of Heaven · Lady of Olympus","role":"Queen of the Olympians; goddess of marriage, women, childbirth, and the loyalty of oaths","domains":["marriage","women","childbirth","royalty","sky","oaths"],"parents":["Kronos","Rhea"],"siblings":["Zeus","Poseidon","Demeter","Hestia","Hades"],"consorts":["Zeus"],"children":["Ares","Hephaestus","Hebe","Eileithyia","Enyo"],"symbols":["peacock","lily","diadem","sceptre","pomegranate","cuckoo"],"sacredAnimals":["peacock","cow","cuckoo"],"equivalents":["Juno (Roman)"],"kabbalahPaths":[3,16],"description":"Hera was swallowed at birth by Kronos like her siblings, and liberated when Zeus forced him to disgorge them. She is the eternal principle of sacred marriage and the dignity of wifely commitment. Her jealous pursuit of Zeus's lovers and illegitimate children reflects the cosmic tension between order and creative excess.","myth":"","poem":""}, + {"id":"poseidon","name":"Poseidon","pantheon":"greek","epithet":"Earth-Shaker · Lord of the Deep · Brother of Zeus","role":"God of the sea, earthquakes, storms, and horses","domains":["sea","earthquakes","storms","horses","fresh water"],"parents":["Kronos","Rhea"],"siblings":["Zeus","Hera","Demeter","Hestia","Hades"],"consorts":["Amphitrite","Demeter","Medusa","many others"],"children":["Triton","Theseus","Polyphemus","Pegasus","Chrysaor"],"symbols":["trident","horse","dolphin","bull","fish"],"sacredAnimals":["horse","dolphin","bull"],"sacredPlaces":["Corinth","Cape Sounion","Aegean Sea"],"equivalents":["Neptune (Roman)"],"kabbalahPaths":[4,23,29],"description":"Poseidon drew the sea when the universe was divided by lot. He is the great mover — earthquakes are his footsteps, storms his breath. He created the horse by striking the earth with his trident during the contest for Athens. Though he lost that contest to Athena, he remained a god respected and feared by all seafarers.","myth":"","poem":""}, + {"id":"demeter","name":"Demeter","pantheon":"greek","epithet":"Great Mother · Goddess of the Grain · Giver of Laws","role":"Goddess of the harvest, agriculture, fertility of the earth, and the sacred mysteries","domains":["harvest","grain","agriculture","fertility","seasons","sacred law","the mysteries"],"parents":["Kronos","Rhea"],"siblings":["Zeus","Hera","Poseidon","Hestia","Hades"],"consorts":["Zeus","Poseidon","Iasion"],"children":["Persephone","Arion","Plutos","Despoina"],"symbols":["sheaf of wheat","torch","cornucopia","poppy","serpent"],"sacredAnimals":["snake","pig","gecko"],"sacredPlaces":["Eleusis"],"equivalents":["Ceres (Roman)"],"kabbalahPaths":[3,10,19],"description":"Demeter governed the cycle of life and death through her daughter Persephone's abduction. When Hades took Persephone, Demeter wandered the earth in grief and nothing grew. The Eleusinian Mysteries — the most sacred rites of the ancient world — reenacted her search and reunification with Persephone, promising initiates a blessed afterlife.","myth":"","poem":""}, + {"id":"athena","name":"Athena","pantheon":"greek","epithet":"Grey-Eyed Goddess · Lady of Athens · Pallas Athena","role":"Goddess of wisdom, craft, and strategic warfare; patron of Athens and civilised life","domains":["wisdom","warfare (strategic)","crafts","weaving","justice","civilization","arts"],"parents":["Zeus","Metis (swallowed by Zeus before birth)"],"siblings":["Apollo","Artemis","Ares","Hermes","Hephaestus","Dionysus"],"consorts":[],"children":["Erichthonius (adoptive)"],"symbols":["owl","olive tree","aegis","spear","helmet","shield (Gorgoneion)"],"sacredAnimals":["owl","serpent","rooster"],"sacredPlaces":["Athens (Parthenon)","Troy","Pergamon"],"equivalents":["Minerva (Roman)"],"kabbalahPaths":[2,15,27,28,32],"description":"Born fully armoured from Zeus's forehead after he swallowed her pregnant mother Metis, Athena represents the intellect's victory over instinct. She guided heroes like Odysseus and Perseus with divine cunning rather than brute force. Her rival Poseidon struck the Acropolis to produce a salt spring; she struck it to grow an olive tree — and Athens chose her gift.","myth":"","poem":""}, + {"id":"apollo","name":"Apollo","pantheon":"greek","epithet":"Phoebus · The Far-Shooter · God of Light","role":"God of the sun, music, poetry, prophecy, healing, archery, and truth","domains":["sun","light","music","poetry","prophecy","healing","archery","truth","plague"],"parents":["Zeus","Leto (Titaness)"],"siblings":["Artemis (twin)"],"consorts":["Coronis","Cassandra (unrequited)","Hyacinthus","many others"],"children":["Asclepius","Orpheus","Ion","Aristaeus"],"symbols":["lyre","silver bow and arrows","laurel wreath","raven","python","sun disk"],"sacredAnimals":["raven","swan","dolphin","mouse","wolf"],"sacredPlaces":["Delphi","Delos"],"equivalents":["Apollo (Roman, same name)","Helios (solar aspect)"],"kabbalahPaths":[6,18,24,25,30],"description":"Apollo is the ideal of the kouros — the perfect, radiant male youth. As god of Delphi he delivered the most famous oracle in the ancient world: 'Know thyself.' His twin Artemis governs the moon while Apollo governs the sun. He also brought plague — his arrows, like the rays of the sun, could kill at a distance.","myth":"","poem":""}, + {"id":"artemis","name":"Artemis","pantheon":"greek","epithet":"Lady of the Hunt · Mistress of Animals · Phoebe","role":"Goddess of the hunt, wilderness, the moon, chastity, and the protection of young women","domains":["hunt","wilderness","moon","chastity","childbirth","protection of youth","wild nature"],"parents":["Zeus","Leto"],"siblings":["Apollo (twin)"],"consorts":[],"children":[],"symbols":["silver bow","quiver of arrows","crescent moon","torch","deer"],"sacredAnimals":["deer","bear","dogs","boar","guinea fowl"],"sacredPlaces":["Ephesus (great temple)","Brauron","Delos"],"equivalents":["Diana (Roman)"],"kabbalahPaths":[9,13,25],"description":"Artemis was the first-born twin, immediately helping her mother deliver Apollo. She demanded absolute chastity from herself and her nymphs, punishing any violation with fierce transformation — Actaeon was turned into a stag for seeing her bathing. Her great temple at Ephesus was one of the Seven Wonders of the ancient world.","myth":"","poem":""}, + {"id":"ares","name":"Ares","pantheon":"greek","epithet":"The Bloodied · Bane of Mortals · Unsated in War","role":"God of war, violence, bloodshed, and the raw courage of battle","domains":["war","violence","bloodshed","courage","civil order (through fear)"],"parents":["Zeus","Hera"],"siblings":["Hephaestus","Hebe","Eileithyia"],"consorts":["Aphrodite"],"children":["Phobos (Fear)","Deimos (Terror)","Harmonia","Anteros","Eros (some traditions)"],"symbols":["spear","shield","helmet","sword","boar","vulture"],"sacredAnimals":["dog","vulture","boar","snake","woodpecker"],"sacredPlaces":["Thrace"],"equivalents":["Mars (Roman)"],"kabbalahPaths":[5,24,27],"description":"Unlike Athena who represents strategic wisdom in war, Ares is the raw, unthinking force of conflict — violent, passionate, and bloodthirsty. He was despised by both his father Zeus and nearly all the gods for cruelty. Yet the Romans, as a martial people, honoured his equivalent Mars as one of their greatest gods and as the divine father of Romulus.","myth":"","poem":""}, + {"id":"aphrodite","name":"Aphrodite","pantheon":"greek","epithet":"Foam-Born · Golden Aphrodite · Lady of Cyprus","role":"Goddess of love, beauty, pleasure, desire, and procreation","domains":["love","beauty","desire","pleasure","fertility","procreation","the sea (born from it)"],"parents":["Born from sea-foam where Uranos's severed genitals fell, or Zeus and Dione"],"siblings":[],"consorts":["Hephaestus (husband)","Ares (lover)","Anchises","Adonis"],"children":["Eros","Anteros","Phobos","Deimos","Harmonia","Aeneas"],"symbols":["dove","sparrow","rose","myrtle","girdle","apple","mirror"],"sacredAnimals":["dove","sparrow","swan","goose","bee"],"sacredPlaces":["Cyprus (Paphos)","Cythera","Corinth"],"equivalents":["Venus (Roman)"],"kabbalahPaths":[7,14],"description":"Rising from the sea near Cyprus, Aphrodite embodies eros as a cosmic force older than the Olympians — desire that moves all creation. Her girdle could inspire desire in any being, mortal or immortal. The Trojan War began with her promise to Paris: in exchange for the golden apple of discord, she gave him the love of the most beautiful mortal woman, Helen.","myth":"","poem":""}, + {"id":"hermes","name":"Hermes","pantheon":"greek","epithet":"Psychopomp · Divine Messenger · Master Thief · Trickster","role":"Messenger of the gods; guide of souls to Hades; god of commerce, language, and boundaries","domains":["communication","travel","trade","thieves","language","boundaries","sleep","guide of souls"],"parents":["Zeus","Maia (Pleiad nymph)"],"siblings":["Apollo","Artemis","Ares","Athena","Hephaestus","Dionysus"],"consorts":["Aphrodite","Dryope","many others"],"children":["Pan","Hermaphroditus","Autolycus"],"symbols":["caduceus","winged sandals","traveller's hat (petasos)","tortoise-shell lyre","purse"],"sacredAnimals":["tortoise","rooster","ram","hawk"],"sacredPlaces":["Mount Cyllene (birthplace)"],"equivalents":["Mercury (Roman)","Thoth (Egyptian)"],"kabbalahPaths":[8,12,29],"description":"The only Olympian who moves freely between the worlds of gods, mortals, and the dead. On his first day of life he invented the lyre, stole Apollo's cattle, and negotiated his way to a position of honour among the gods. As psychopomp he guides souls to Hades. He was the patron of Hermeticism, whose practice of inner alchemy traces back to his identification with the Egyptian Thoth.","myth":"","poem":""}, + {"id":"dionysus","name":"Dionysus","pantheon":"greek","epithet":"Twice-Born · The Liberator · Bringer of Joy","role":"God of wine, ecstasy, ritual madness, theatre, and the mystery of rebirth","domains":["wine","ecstasy","theatre","ritual madness","fertility","rebirth","vegetation","mystery"],"parents":["Zeus","Semele (mortal princess of Thebes)"],"siblings":["Apollo","Hermes","Ares","Athena"],"consorts":["Ariadne"],"children":["Thoas","Staphylus","Oenopion","Phthonus"],"symbols":["thyrsos (fennel staff topped with pinecone)","grapevine","ivy","leopard skin","cup","mask"],"sacredAnimals":["bull","panther","leopard","goat","serpent","donkey"],"sacredPlaces":["Delphi (shared with Apollo)","Thebes","Naxos"],"equivalents":["Bacchus (Roman)","Osiris (Egyptian mystery parallel)"],"kabbalahPaths":[6],"description":"Born twice — first from Semele who was incinerated by Zeus's divine glory, then stitched into Zeus's thigh to gestate — Dionysus embodies the paradox of life through death and the dissolution of individual self in collective ecstasy. His journey to retrieve his mother from the underworld mirrors the initiatory death-and-rebirth pattern central to all mystery cults.","myth":"","poem":""}, + {"id":"persephone","name":"Persephone","pantheon":"greek","epithet":"Queen of the Underworld · Kore (The Maiden) · The Dread Goddess","role":"Queen of the Underworld; goddess of spring growth and the mysteries of death and rebirth","domains":["underworld","spring","vegetation","grain","death","mysteries","pomegranates"],"parents":["Zeus","Demeter"],"siblings":[],"consorts":["Hades"],"children":["Zagreus (by Zeus, in Orphic tradition)","Melinoe","Plutus (some traditions)"],"symbols":["pomegranate","narcissus","wheat","torch","flowers","bat"],"sacredAnimals":["bat","ram","parrot"],"sacredPlaces":["Eleusis (mysteries)","The Underworld itself"],"equivalents":["Proserpina (Roman)"],"kabbalahPaths":[9,10],"description":"Abducted to the Underworld by Hades while picking flowers in the meadow, Persephone ate six pomegranate seeds, binding her to spend half the year below. Her annual descent brings winter; her return brings spring. The Eleusinian Mysteries dramatised her story as a metaphor for the soul's descent, death, and triumphant ascent.","myth":"","poem":""}, + {"id":"hades","name":"Hades","pantheon":"greek","epithet":"The Unseen One · Lord of the Dead · Wealthy One","role":"God of the underworld and its mineral riches; impartial ruler of all the dead","domains":["underworld","death","wealth (metals in the earth)","the dead","invisibility"],"parents":["Kronos","Rhea"],"siblings":["Zeus","Poseidon","Hera","Demeter","Hestia"],"consorts":["Persephone"],"children":["Macaria","Melinoe","Zagreus (some traditions)"],"symbols":["Cerberus","key","bident","helmet of invisibility","narcissus","cypress","black poplar"],"sacredAnimals":["Cerberus (three-headed dog)","screech owl","serpent"],"sacredPlaces":["The Underworld","Alcyonian Lake","mouth of Avernus"],"equivalents":["Pluto (Roman)"],"kabbalahPaths":[5,31],"description":"Hades drew the underworld when he and his brothers divided creation by lot. Unlike Thanatos (death personified), Hades administers the realm of the dead — neutral and inevitable, rarely actively malevolent. The Greeks avoided speaking his name, preferring euphemisms like Plouton (Wealth-Giver) for the mineral riches that rise from the earth's depths.","myth":"","poem":""}, + {"id":"hecate","name":"Hecate","pantheon":"greek","epithet":"Triple Goddess · Mistress of Witchcraft · Queen of the Night","role":"Goddess of magic, witchcraft, crossroads, necromancy, and the liminal spaces between worlds","domains":["magic","witchcraft","crossroads","necromancy","ghosts","moon","herbs","night","liminal spaces"],"parents":["Perses (Titan)","Asteria (Titaness of Falling Stars)"],"siblings":[],"consorts":[],"children":["Scylla (some traditions)"],"symbols":["twin torches","key","dagger","rope","serpents","polecat","dogs"],"sacredAnimals":["dog","polecat","red mullet","serpent","owl"],"sacredPlaces":["crossroads (trivia)","Samothrace","Eleusis"],"equivalents":["Diana Trivia (Roman)","partial: Isis (Egyptian)"],"kabbalahPaths":[9,13],"description":"A triple goddess spanning heaven (moon), earth (nature magic), and the underworld (necromancy). She stands at crossroads — liminal spaces where worlds meet — and was propitiated at midnight with offerings left at three-way intersections. She alone heard Persephone's cries as Hades took her, and carried torches to light Demeter's search.","myth":"","poem":""}, + {"id":"pan","name":"Pan","pantheon":"greek","epithet":"All · Lord of the Wild · Panic-Bringer","role":"God of the wild, shepherds, flocks, rustic music, and natural fertility","domains":["wilderness","shepherds","flocks","nature","rustic music","fertility","hunting"],"parents":["Hermes (most common tradition)","Zeus","Kronos (various traditions)"],"siblings":[],"consorts":["many nymphs (Syrinx, Echo, Luna, Pitys)"],"children":["Silenus (some traditions)"],"symbols":["pan pipes (syrinx)","goat legs and horns","pine wreath","shepherd's staff"],"sacredAnimals":["goat","tortoise"],"sacredPlaces":["Arcadia","cave of Mount Parthenion"],"equivalents":["Faunus (Roman)"],"kabbalahPaths":[0,9,26],"description":"Pan's name means 'All' — he represents the totality of wild nature, simultaneously animalistic and divine. His sudden appearance causes 'panic' (from his name). In Liber 777 he corresponds to Ain Soph Aur — the Limitless All beyond the Tree — because he encompasses everything. In later tradition he became the horned archetype of the Devil.","myth":"","poem":""}, + {"id":"eros","name":"Eros","pantheon":"greek","epithet":"Primordial Love · The Wing'd One · Desire Before Form","role":"God of love, attraction, desire, and the generative force binding all creation","domains":["love","desire","attraction","procreation","beauty","the bonds between things"],"parents":["Chaos (primordial), or Aphrodite and Ares, or Nyx and Erebus (various traditions)"],"siblings":["Anteros","Phobos","Deimos","Harmonia"],"consorts":["Psyche"],"children":["Hedone (Pleasure)"],"symbols":["bow and golden/leaden arrows","wings","torch","lyre","roses"],"sacredAnimals":["hare","rooster"],"equivalents":["Cupid / Amor (Roman)"],"kabbalahPaths":[7,17],"description":"In the oldest cosmogonies, Eros is primordial — one of the first forces to emerge from Chaos, the attractive power that draws matter together into form. In later mythology he became the son of Aphrodite and Ares, the mischievous archer who ignites love without regard for consequence. His marriage to Psyche is one of the greatest love stories of antiquity.","myth":"","poem":""}, + {"id":"helios","name":"Helios","pantheon":"greek","epithet":"The Sun · Lord of the Light · The Seeing God","role":"Titan god of the sun; personification of the solar orb; all-seeing witness of oaths","domains":["sun","sight","light","oaths","time (as measured by solar movement)","cattle"],"parents":["Hyperion (Titan of Light)","Theia (Titaness of Sight)"],"siblings":["Selene (Moon)","Eos (Dawn)"],"consorts":["Rhode","Perse","Clymene"],"children":["Aeetes","Circe","Phaethon","Pasiphae","the Heliades (transformed into amber-weeping poplars)"],"symbols":["golden chariot of four fire-breathing horses","crown of solar rays","globe of light"],"equivalents":["Apollo (later absorbed solar functions)","Ra (Egyptian)","Sol (Roman)"],"kabbalahPaths":[30],"description":"Helios drives his golden chariot of four fire-breathing horses across the sky each day from east to west, descending into the Ocean at night and sailing beneath the earth to rise again in the east. As the all-seeing eye of heaven, he witnessed Hades' abduction of Persephone and told Demeter. His son Phaethon's disastrous attempt to drive the solar chariot scorched the earth and was struck down by Zeus.","myth":"","poem":""}, + {"id":"ikarus","name":"Ikarus","pantheon":"greek","epithet":"Winged Youth · The Overreacher · Son of Daidalos","role":"Mythic youth whose flight toward the sun became the archetype of ambition, ecstasy, and tragic excess","domains":["flight","ambition","initiation through risk","hubris","ecstatic ascent","tragic fall"],"parents":["Daidalos (Daedalus)","Naukrate (in some traditions)"],"siblings":[],"consorts":[],"children":[],"symbols":["wax wings","feathers","sun","sea"],"sacredAnimals":["gull"],"sacredPlaces":["Crete","Ikarian Sea","Sicily"],"equivalents":["Phaethon (mythic parallel of overreaching ascent)"],"kabbalahPaths":[1,6],"description":"Ikarus escaped imprisonment with his father Daidalos by means of crafted wings of feathers and wax. Ignoring the middle path between sea-damp and sun-heat, he flew too high; the wax melted and he fell into the sea that now bears his name. In esoteric interpretation he signifies the danger and necessity of aspiration: ascent is required, but unbalanced ecstasy without measure becomes ruin.","myth":"","poem":""}, + {"id":"kronos","name":"Kronos","pantheon":"greek","epithet":"Lord of Time · Harvest Titan · The Reaper","role":"Titan king; god of time, the harvest, and agricultural abundance; ruler of the Golden Age","domains":["time","harvest","agriculture","fate","the Golden Age","history"],"parents":["Uranus (Sky)","Gaia (Earth)"],"siblings":["Rhea (wife)","Oceanus","Tethys","Hyperion","Theia","Mnemosyne","Themis","others"],"consorts":["Rhea"],"children":["Zeus","Hera","Poseidon","Demeter","Hestia","Hades"],"symbols":["scythe / sickle / harpe","grain","serpent eating its tail (ouroboros)"],"equivalents":["Saturn (Roman)"],"kabbalahPaths":[3,32],"description":"Kronos castrated his father Uranus with an adamantine sickle to free his siblings from Uranus's oppression, then ruled the Golden Age of ease and plenty. But fearing a prophecy that his own children would overthrow him, he swallowed each at birth. Overthrown by Zeus after Rhea tricked him with a stone bundled as a baby, he was imprisoned in Tartarus — or in other traditions, exiled to the Isles of the Blessed to rule a realm of the heroic dead.","myth":"","poem":""}, + {"id":"uranus","name":"Uranus","pantheon":"greek","epithet":"Father Sky · The Heavens · First King","role":"Primordial god of the sky; first ruler of the cosmos; father of the Titans","domains":["sky","heaven","stars","cosmos","fate (through stars)"],"parents":["Gaia (self-born, or emerged from Chaos)"],"siblings":[],"consorts":["Gaia"],"children":["Titans (12)","Cyclopes (3: Brontes, Steropes, Arges)","Hecatoncheires (3: hundred-handed giants)","Aphrodite (born from his severed genitals)"],"symbols":["vault of the sky","stars","night sky"],"equivalents":["Caelus (Roman)"],"kabbalahPaths":[2],"description":"Uranus covered Gaia (Earth) as a lid covers a pot, and their union produced the Titans. He was so horrified by the monstrous Cyclopes and Hecatoncheires that he thrust them back into Gaia's womb, causing her tremendous suffering. Gaia persuaded her son Kronos to castrate Uranus with an adamantine sickle — from his blood the Erinyes and Giants were born, from his severed genitals thrown into the sea, Aphrodite arose.","myth":"","poem":""}, + {"id":"rhea","name":"Rhea","pantheon":"greek","epithet":"Mother of the Gods · Lady of the Wild Beasts · Great Mother","role":"Titaness of motherhood, fertility, the passage of generations, and mountain wilds","domains":["motherhood","fertility","generations","mountains","wild animals","protection of children"],"parents":["Uranus","Gaia"],"siblings":["Kronos (husband)","Oceanus","Themis","Mnemosyne","other Titans"],"consorts":["Kronos"],"children":["Zeus","Hera","Poseidon","Demeter","Hestia","Hades"],"symbols":["lions","turreted crown","drum (tympanum)","key"],"sacredAnimals":["lion","eagle"],"equivalents":["Cybele (Phrygian Great Mother)","Ops (Roman)"],"kabbalahPaths":[3,10,23],"description":"When Kronos swallowed her children one by one, Rhea hid the infant Zeus in a cave on Crete, guarded by the Curetes whose loud bronze clashing masked the child's cries. She gave Kronos a stone wrapped in swaddling cloths, which he swallowed. She represents the eternal mother who preserves life through cunning when brute power fails.","myth":"","poem":""}, + {"id":"themis","name":"Themis","pantheon":"greek","epithet":"Lady of Good Counsel · Divine Law · She Who Brought the Gods Together","role":"Titaness of divine law, order, natural law, and the oracular voice","domains":["divine law","order","custom","the oracle","justice","seasons (via her daughters)","fate (via Moirai)"],"parents":["Uranus","Gaia"],"siblings":["Kronos","Rhea","other Titans"],"consorts":["Zeus (second wife, before Hera)"],"children":["The Horae (Seasons: Eunomia, Dike, Eirene)","The Moirai (Fates: Clotho, Lachesis, Atropos)","Nemesis (some traditions)"],"symbols":["scales of justice","sword","blindfold (later addition)","cornucopia"],"equivalents":["Iustitia (Roman)"],"kabbalahPaths":[22],"description":"Themis was the second consort of Zeus after Metis. She convened the gods to assembly and maintained the cosmic order through law rather than through force. Her daughters the Moirai spun the thread of fate for every mortal, measuring and cutting it — even Zeus could not override their decree.","myth":"","poem":""}, + {"id":"nike","name":"Nike","pantheon":"greek","epithet":"Winged Victory · Daughter of the River Styx","role":"Goddess of victory, speed, and the glory that comes from achievement","domains":["victory","speed","strength","fame","success"],"parents":["Pallas (Titan)","Styx (river of the underworld)"],"siblings":["Kratos (Strength)","Bia (Force)","Zelus (Rivalry)"],"consorts":[],"children":[],"symbols":["wings","laurel wreath","palm branch","victory trophy"],"equivalents":["Victoria (Roman)"],"kabbalahPaths":[7],"description":"Nike sided with Zeus in the Titan War as her family all did, because Styx (their mother) swore her children's loyalty. Nike became the constant companion of Zeus and Athena, and was often depicted hovering over victorious athletes and armies. The winged logo of the athletic brand bears her name.","myth":"","poem":""}, + {"id":"jupiter","name":"Jupiter","pantheon":"roman","epithet":"Optimus Maximus · Best and Greatest · Father of the State","role":"King of the Roman gods; god of sky, thunder, lightning, law, and the Roman state itself","domains":["sky","thunder","lightning","justice","law","the Roman state","oaths"],"parents":["Saturn","Ops"],"siblings":["Juno","Neptune","Ceres","Pluto","Vesta"],"consorts":["Juno"],"children":["Minerva","Mars","Vulcan","Mercury","Bacchus","Diana"],"symbols":["eagle","thunderbolt","oak tree","sceptre","white bulls (his sacrifices)"],"sacredAnimals":["eagle","bull"],"sacredPlaces":["Capitoline Hill (Rome)","Mons Albanus"],"equivalents":["Zeus (Greek)"],"kabbalahPaths":[1,4,11,21],"description":"Jupiter was the supreme deity of Rome, his great temple on the Capitoline Hill the centre of Roman religion and statecraft. Generals who celebrated a triumph wore his purple robes and rode in his chariot. He protected the state through his thunderbolts and preserved the sanctity of oaths — perjury was punished directly by him.","myth":"","poem":""}, + {"id":"juno","name":"Juno","pantheon":"roman","epithet":"Regina · Queen of the Gods · Protectress of Rome","role":"Queen of the gods; goddess of marriage, women, childbirth, and the genius of the Roman state","domains":["marriage","women","the Roman state","childbirth","sky","the civic community"],"parents":["Saturn","Ops"],"siblings":["Jupiter","Neptune","Ceres","Pluto","Vesta"],"consorts":["Jupiter"],"children":["Mars","Vulcan","Juventas"],"symbols":["peacock","diadem","sceptre","pomegranate"],"sacredAnimals":["peacock","cow","cuckoo"],"equivalents":["Hera (Greek)"],"kabbalahPaths":[3,11,28],"description":"Juno Regina (Queen Juno) was the special protectress of Rome. The month of June is named for her. The sacred geese in her temple on the Capitoline Hill warned Rome of the Gallic attack in 390 BCE, saving the city. She represented the foundational importance of the institution of marriage and civic community in Roman life.","myth":"","poem":""}, + {"id":"mars","name":"Mars","pantheon":"roman","epithet":"Gradivus · Pater Martius · Divine Father of Rome","role":"God of war and agriculture; divine father of Romulus, legendary founder of Rome","domains":["war","military power","agriculture (original domain)","spring","youth","the Roman legion"],"parents":["Jupiter","Juno"],"siblings":["Vulcan","Juventas"],"consorts":["Rhea Silvia (Vestal Virgin he visited in the sacred grove)"],"children":["Romulus","Remus"],"symbols":["spear","shield (Ancile)","wolf","woodpecker","armor","oak"],"sacredAnimals":["wolf","woodpecker","horse"],"equivalents":["Ares (Greek)"],"kabbalahPaths":[5,24,27],"description":"Unlike the despised Ares, Mars was Rome's second-most-important god. As father of Romulus, he was the divine ancestor of the Roman people. The month of March (Martius) is named for him, as is Tuesday (from the Germanic Tiw, identified with Mars: Old French Mardi). Originally an agricultural deity who protected crops and cattle from blight, he became primarily martial as Rome expanded.","myth":"","poem":""}, + {"id":"venus","name":"Venus","pantheon":"roman","epithet":"Genetrix · Mother of the Roman People · Golden One","role":"Goddess of love, beauty, sex, fertility, prosperity, and the divine ancestor of Rome","domains":["love","beauty","fertility","prosperity","desire","victory","grace","+the sea (born from it)"],"parents":["born from sea-foam (as Aphrodite)"],"siblings":[],"consorts":["Vulcan (husband)","Mars (lover)","Anchises"],"children":["Cupid","Aeneas (by Anchises, Trojan hero and ancestor of Romans)"],"symbols":["dove","rose","myrtle","mirror","apple","shell"],"sacredAnimals":["dove","sparrow","swan","goose"],"sacredPlaces":["Cyprus","Eryx (Sicily)","Forum of Caesar (Rome)"],"equivalents":["Aphrodite (Greek)"],"kabbalahPaths":[7,14,16,22],"description":"Through Aeneas (son of Venus and the Trojan prince Anchises) the Romans traced their divine lineage to the gods. Julius Caesar and Augustus claimed descent from Venus. The planet Venus bears her name; the day Vendredi/Friday honours her. She was at once the universal generative force and the ancestor of the Roman imperial family.","myth":"","poem":""}, + {"id":"mercury","name":"Mercury","pantheon":"roman","epithet":"Messenger of the Gods · Lord of Commerce · Guide of Souls","role":"God of commerce, communication, thieves, travellers, eloquence, and the guide of souls to the underworld","domains":["commerce","communication","travel","thieves","messages","eloquence","psychopomp (guide of souls)"],"parents":["Jupiter","Maia"],"siblings":["Minerva","Mars","Vulcan"],"consorts":[],"children":["Cupid (some traditions)"],"symbols":["caduceus (two serpents on a staff)","winged hat (petasus)","winged sandals (talaria)","purse"],"equivalents":["Hermes (Greek)","Thoth (Egyptian parallel)"],"kabbalahPaths":[8,12],"description":"The fleet-footed messenger of the gods who escorted souls to the underworld. The Romans particularly stressed his role as god of merchants and commercial success. The planet Mercury, the metal mercury (quicksilver), and Wednesday (Mercredi in French, Mercury's day) bear his name. The caduceus — his staff of intertwined serpents — became the symbol of medicine (through confusion with Asclepius's rod).","myth":"","poem":""}, + {"id":"saturn","name":"Saturn","pantheon":"roman","epithet":"Lord of Time · God of the Golden Age · Father of Jupiter","role":"God of time, generation, wealth, dissolution, agriculture, and the memory of the Golden Age","domains":["time","generation","dissolution","wealth","agriculture","the Golden Age","the depths of earth"],"parents":["Caelus (Uranus/Sky)","Terra (Gaia/Earth)"],"siblings":["Ops (wife)","Titan equivalents"],"consorts":["Ops"],"children":["Jupiter","Juno","Neptune","Ceres","Pluto","Vesta"],"symbols":["scythe","sickle","harpe","grain","serpent eating its tail","hourglass"],"equivalents":["Kronos (Greek)"],"kabbalahPaths":[3,32],"description":"Saturn ruled the Golden Age of abundance and equality before Jupiter overthrew him. The Saturnalia festivals (December 17–23) — Rome's greatest holiday — honoured this primordial age by temporarily reversing social hierarchies: masters served slaves, gambling was legal, and gifts were exchanged. The planet Saturn, Saturday, and the metal lead bear his name.","myth":"","poem":""}, + {"id":"neptune","name":"Neptune","pantheon":"roman","epithet":"Earth-Shaker · Lord of the Seas · God of Freshwater","role":"God of the sea, storms, earthquakes, horses, and all waters","domains":["sea","freshwater","storms","earthquakes","horses"],"parents":["Saturn","Ops"],"siblings":["Jupiter","Juno","Ceres","Pluto","Vesta"],"consorts":["Amphitrite","Salacia"],"children":["Triton","Polyphemus","Proteus"],"symbols":["trident","dolphin","horse","bull","sea-green robes"],"equivalents":["Poseidon (Greek)"],"kabbalahPaths":[23,29],"description":"Neptune was originally a god of freshwater before absorbing Poseidon's marine domain. His Neptunalia festival was held in late July, when water was scarce — water was drunk from springs and arbors were built in the open air. The planet Neptune bears his name.","myth":"","poem":""}, + {"id":"diana","name":"Diana","pantheon":"roman","epithet":"Lady of Three Forms · Diana Trivia · Goddess of the Hunt","role":"Goddess of the hunt, the moon, childbirth, wilderness, and the crossroads","domains":["hunt","moon","childbirth","wilderness","chastity","crossroads","the night"],"parents":["Jupiter","Latona (Leto)"],"siblings":["Apollo (twin)"],"consorts":[],"children":[],"symbols":["crescent moon","quiver and bow","hunting dogs","torch","deer"],"equivalents":["Artemis (Greek)","triple goddess fusion with Luna and Hecate"],"kabbalahPaths":[9,13,25],"description":"Diana was a triple goddess: Diana (huntress on earth), Luna (moon in heaven), and Hecate/Trivia (crossroads/underworld). Her most ancient sanctuary was the grove at Aricia in the Alban Hills, where her priest-king won his position by killing his predecessor in single combat — a rite that fascinated the anthropologist Frazer in The Golden Bough and shaped modern comparative mythology.","myth":"","poem":""}, + {"id":"vulcan","name":"Vulcan","pantheon":"roman","epithet":"Lord of Fire · Divine Smith · God Below","role":"God of fire, metalworking, volcanoes, craft, and the power of transformation through heat","domains":["fire","forge","metalworking","volcanoes","destruction (purifying)","craft"],"parents":["Jupiter","Juno"],"siblings":["Mars","Juventas"],"consorts":["Venus"],"children":["Caeculus (some traditions)","the Cyclopes (his workers)"],"symbols":["hammer","anvil","tongs","forge","pileus (felt workman's cap)"],"equivalents":["Hephaestus (Greek)"],"kabbalahPaths":[22,31],"description":"Vulcan's forge is located beneath Mount Etna (or on the Aeolian Islands). He crafted Jupiter's thunderbolts, Achilles's divine armour, the unbreakable chains that held Prometheus, and the net in which he entrapped his adulterous wife Venus with Mars. Volcanoes bear his name. His Vulcanalia festival was held in August when fires most threatened granaries.","myth":"","poem":""}, + {"id":"janus","name":"Janus","pantheon":"roman","epithet":"God of Beginnings · Doorkeeper of Heaven · Two-Faced One","role":"God of beginnings, passages, doorways, time, duality, and all transitions","domains":["beginnings","endings","doorways","passages","time","duality","transitions","gates","bridges"],"parents":["uniquely Roman: often listed as son of Caelus or self-born from Chaos"],"siblings":[],"consorts":["Juturna (water nymph)"],"children":["Tiberinus (river god of the Tiber)","Canens","Fontus"],"symbols":["two faces (one looking forward, one back)","key","sceptre","door","staff"],"equivalents":["No Greek equivalent — uniquely Roman"],"kabbalahPaths":[2],"description":"Janus has no Greek equivalent, making him one of the most distinctively Roman gods. He looks to past and future simultaneously because he stands at every threshold. The month of January (Ianuarius) bears his name as the gateway of the year. His temple in the Roman Forum had two gates: both gates open signified Rome was at war; both closed (a rare occurrence) that peace reigned throughout the empire.","myth":"","poem":""}, + {"id":"pluto","name":"Pluto","pantheon":"roman","epithet":"The Wealthy · Rich One · God Below","role":"God of the underworld, wealth from the earth, and the dead; ruler of the realm below","domains":["underworld","wealth (minerals from earth)","the dead","dark places","seeds buried in earth"],"parents":["Saturn","Ops"],"siblings":["Jupiter","Juno","Neptune","Ceres","Vesta"],"consorts":["Proserpina"],"children":[],"symbols":["bident","helmet of darkness","Cerberus","cornucopia (from below)","keys"],"equivalents":["Hades (Greek)"],"kabbalahPaths":[21,31],"description":"Pluto (Plouton, 'the Wealthy') is the Latin name for the underworld god, emphasising the riches that come from below — metals, gems, and the fertility that seeds develop when buried. Unlike the feared Hades of the Greeks, Pluto was seen in a slightly more benign light, associated with the abundant treasure hidden underground.","myth":"","poem":""}, + {"id":"ceres","name":"Ceres","pantheon":"roman","epithet":"Mother of Crops · The Nourisher · Grain Mother","role":"Goddess of grain, agriculture, fertility, and the laws governing human civilization","domains":["grain","agriculture","fertility","the harvest","civilization","laws","motherhood"],"parents":["Saturn","Ops"],"siblings":["Jupiter","Juno","Neptune","Pluto","Vesta"],"consorts":["Jupiter"],"children":["Proserpina"],"symbols":["sheaf of grain","torch","poppy","sow (pig)","serpent","cornucopia"],"sacredAnimals":["pig","snake"],"equivalents":["Demeter (Greek)"],"kabbalahPaths":[10,20],"description":"Ceres was worshipped on the Aventine Hill in Rome alongside Liber (Dionysus) and Libera (Persephone). The Latin word 'cereal' derives from her name. The Cerealia festival each April celebrated the return of grain. Her daughter Proserpina's abduction by Pluto mirrored Demeter's search for Persephone in the Eleusinian mysteries.","myth":"","poem":""}, + {"id":"ra","name":"Ra","pantheon":"egyptian","epithet":"Lord of All · He Who Made Himself · The Great Cat · Lord of Maat","role":"Solar god and creator deity; king of the gods; the power of the sun that sustains all life","domains":["sun","creation","kingship","light","warmth","growth","the solar barque","ordering chaos"],"parents":["self-created (in most traditions)","or born from the primordial Nun (waters of chaos)"],"siblings":[],"consorts":["Hathor","Mut (in some forms)"],"children":["Shu (air) and Tefnut (moisture) in some traditions","Thoth","Hathor","Maat"],"symbols":["solar disk","sun barque","falcon head","uraeus serpent","ankh","was sceptre"],"sacredAnimals":["scarab (as Khepri at dawn)","falcon (as Ra-Horakhty at noon)","cat (battles Apophis)","Bennu bird (phoenix)"],"sacredPlaces":["Heliopolis (On)","Karnak (merged with Amun)"],"equivalents":["Apollo/Helios (Greek-Roman)"],"kabbalahPaths":[6,30],"description":"Ra traverses the sky in his solar barque by day — accompanied by his divine crew including Thoth, Maat, and Horus — and through the Duat (underworld) by night, battling the chaos-serpent Apophis to rise again each dawn. His union with Osiris in the Underworld (Ra-Osiris) represents the joining of the living sun with the regenerating power of death.","myth":"","poem":""}, + {"id":"osiris","name":"Osiris","pantheon":"egyptian","epithet":"Foremost of the Westerners · Lord of the Afterlife · The Green God","role":"God of the afterlife, resurrection, judgment, agriculture, and the cyclical renewal of life","domains":["afterlife","resurrection","judgment of souls","agriculture","fertility","vegetation","the Nile inundation"],"parents":["Geb (earth god)","Nut (sky goddess)"],"siblings":["Isis (wife)","Set (brother who killed him)","Nephthys","Horus the Elder"],"consorts":["Isis"],"children":["Horus the Younger"],"symbols":["crook and flail","white Atef crown","djed pillar","green or black skin","mummy wrappings","red throne"],"sacredAnimals":["bull (Apis)","ram","Bennu bird","Djed fish"],"sacredPlaces":["Abydos","Busiris","Philae"],"equivalents":["Dionysus (Greek mystery parallel)","Christ (resurrection parallel noted by Church Fathers)"],"kabbalahPaths":[6,10,16,23],"description":"Osiris was the first king of Egypt, the giver of agriculture and civilisation. His brother Set murdered and dismembered him out of jealousy, scattering the pieces across Egypt. Isis reassembled his body and Thoth restored it; Anubis performed the first embalming rites. Osiris was resurrected to become ruler of the dead, while his posthumously conceived son Horus inherited the throne. Every pharaoh was Horus in life and Osiris in death.","myth":"","poem":""}, + {"id":"isis","name":"Isis","pantheon":"egyptian","epithet":"Great of Magic · Lady of Ten Thousand Names · Seat of the Pharaoh","role":"Goddess of magic, healing, motherhood, fertility, the throne, and the binding force of all life","domains":["magic","healing","motherhood","fertility","the throne","wind","resurrection","nature","knowledge"],"parents":["Geb","Nut"],"siblings":["Osiris (husband)","Set","Nephthys","Horus the Elder"],"consorts":["Osiris"],"children":["Horus the Younger"],"symbols":["throne headdress","wings","ankh","sistrum","knot of Isis (tyet)","star Sirius (heralds Nile flood)"],"sacredAnimals":["cow","swallow","scorpion","kite","cobra"],"sacredPlaces":["Philae","Dendera","Behbeit el-Hagar"],"equivalents":["Demeter (maternal grief myth)","Aphrodite (love and beauty)","Athena (wisdom and magic)","later Virgin Mary (mother/son imagery)"],"kabbalahPaths":[2,3,10,15,20,23],"description":"Isis is the greatest magician in the Egyptian pantheon. She collected the scattered pieces of Osiris's body, restored him with her magic, and conceived Horus posthumously. Her cult spread throughout the Roman Empire and profoundly influenced early Christianity — the madonna-and-child iconography of Isis nursing the infant Horus shaped Christian depictions of Mary and Jesus. She was worshipped in Rome, Greece, and as far north as Britain.","myth":"","poem":""}, + {"id":"horus","name":"Horus","pantheon":"egyptian","epithet":"He Who Is Above · Falcon Lord of the Sky · Eye of Light","role":"Sky god; god of kingship and the living pharaoh; avenger of his father Osiris","domains":["sky","kingship","the pharaoh","war","protection","sun (right eye) and moon (left eye)","the horizon"],"parents":["Osiris","Isis"],"siblings":[],"consorts":["Hathor"],"children":["The Four Sons of Horus (Imsety, Hapy, Duamutef, Qebehsenuef)"],"symbols":["falcon","Eye of Horus (Wedjat/Udjat)","double crown (Pschent)","ankh","solar disk on falcon head"],"sacredAnimals":["falcon"],"sacredPlaces":["Edfu","Kom Ombo","Behdet","Hierakonpolis"],"equivalents":["Apollo (solar youth aspect)","the Christ (the son who conquers death)"],"kabbalahPaths":[5,19,27,31],"description":"Horus fought the chaos-god Set for 80 years in a series of contests to avenge his father Osiris and reclaim the throne of Egypt. In some battles his eye was torn out by Set and ground into pieces; Thoth (or Hathor) restored it — making the Eye of Horus the most potent protective symbol in all Egyptian magic. The living pharaoh was Horus incarnate; at death he became Osiris.","myth":"","poem":""}, + {"id":"set","name":"Set","pantheon":"egyptian","epithet":"Lord of Chaos · Red One · Great of Strength · The Outcast","role":"God of chaos, storms, war, deserts, foreigners, and the creative/destructive force; guardian of Ra's solar barque","domains":["chaos","storms","deserts","war","foreigners","strength","darkness","creative destruction"],"parents":["Geb","Nut"],"siblings":["Osiris (who he killed)","Isis","Nephthys (wife)","Horus the Elder"],"consorts":["Nephthys","Anat","Astarte"],"children":["Anubis (by Nephthys, in some traditions)","Upuaut (some traditions)"],"symbols":["Set-animal (a fictional creature — pointed snout, square-tipped tail, ears like a donkey)","was sceptre","red crown","red animals"],"sacredAnimals":["Set animal","ass/donkey","hippopotamus","crocodile","red animals generally","pigs","scorpions"],"sacredPlaces":["Ombos","Sepermeru","Avaris (capital of Hyksos who honored him)","the Red Land (desert)"],"equivalents":["Ares (Greek, in violence aspect)","Typhon (Greek chaos-monster)"],"kabbalahPaths":[26],"description":"Set murdered and dismembered Osiris out of jealousy for his brother's popularity and kingship. Yet Set also stood at the prow of Ra's solar barque each night, using his great strength to hold off the chaos-serpent Apophis and ensure the sun could rise. He embodies necessary creative/destructive chaos — without the desert, the fertile Nile valley could not be defined.","myth":"","poem":""}, + {"id":"nephthys","name":"Nephthys","pantheon":"egyptian","epithet":"Mistress of the House · Lady of Death · Friend of the Dead","role":"Goddess of death, mourning, night, transitions, and the service of the dead","domains":["death","mourning","night","service of the dead","protection of the dead","transitions","the horizon"],"parents":["Geb","Nut"],"siblings":["Osiris","Isis","Set","Horus the Elder"],"consorts":["Set"],"children":["Anubis (by Osiris, in some traditions)"],"symbols":["house + basket glyph on head","wings","kite (funeral bird)"],"sacredAnimals":["kite","crow","phoenix (in her mourning role)"],"equivalents":["Hecate (Greek liminal aspect)"],"kabbalahPaths":[3,5,10,25],"description":"Though married to Set, Nephthys mourned Osiris and helped Isis collect and reassemble his body. She and Isis were depicted as paired mourning birds (kites) on coffins, their outstretched wings protecting the dead. She guards the Abydos mystery plays that reenacted Osiris's death and resurrection. Her name means 'Lady of the House' — the House being the metaphorical temple-mansion of the gods.","myth":"","poem":""}, + {"id":"thoth","name":"Thoth","pantheon":"egyptian","epithet":"Ibis-Headed · Lord of Moon, Magic, and Writing · Thrice-Great","role":"God of the moon, wisdom, writing, magic, judgment, mediation, and the measurement of time","domains":["moon","wisdom","writing","magic","science","judgment of souls","mediation","time measurement","language"],"parents":["Ra (in most traditions)","self-created (in Hermopolitan tradition)"],"siblings":[],"consorts":["Ma'at","Seshat (scribal goddess)"],"children":["Seshat"],"symbols":["ibis head","crescent moon","papyrus scroll and writing palette","ankh","caduceus (as Hermes)"],"sacredAnimals":["ibis","baboon"],"sacredPlaces":["Hermopolis (Khemenu)","Tell el-Amarna"],"equivalents":["Hermes (Greek)","Mercury (Roman)","the synthesis creating Hermes Trismegistus, 'Thrice-Great', patron of Hermeticism and Western alchemy"],"kabbalahPaths":[2,8,12],"description":"Thoth invented writing, language, mathematics, astronomy, and magic. He recorded the weighing of the heart against Ma'at's feather in the Hall of Two Truths. Greek philosophy identified him with Hermes, producing Hermes Trismegistus — the legendary author of the Hermetic texts, the Corpus Hermeticum, which shaped Neoplatonism, alchemy, Kabbalah, and the Western magical tradition.","myth":"","poem":""}, + {"id":"hathor","name":"Hathor","pantheon":"egyptian","epithet":"House of Horus · Golden One · Mistress of Love · Eye of Ra","role":"Goddess of love, beauty, music, dance, fertility, the sky, and the joyful aspect of feminine divinity","domains":["love","beauty","music","dance","fertility","sky","women","the dead","drunkenness","foreign lands","gold","turquoise"],"parents":["Ra"],"siblings":[],"consorts":["Horus"],"children":["Ihy (god of music and sistrum)"],"symbols":["cow horns encircling solar disk","sistrum (rattle)","mirror","menat necklace","turquoise"],"sacredAnimals":["cow","falcon","lioness (when she becomes the Eye of Ra in her wrathful aspect, Sekhmet)"],"sacredPlaces":["Dendera","Deir el-Bahari","Serabit el-Khadim (Sinai)"],"equivalents":["Aphrodite/Venus","Isis (overlapping functions)"],"kabbalahPaths":[7,14],"description":"Hathor embodies the feminine principle in its most joyous aspect — music, dance, love, fragrance, and beauty. She also received the dead in the west and offered them bread and beer. At Dendera her cult provided medical therapies alongside ritual. When Ra sent her as Sekhmet to destroy humanity, she was stopped by being given red-dyed beer to drink, which she mistook for blood.","myth":"","poem":""}, + {"id":"anubis","name":"Anubis","pantheon":"egyptian","epithet":"Lord of the Sacred Land · He Upon His Mountain · Guardian of the Scales","role":"God of embalming, mummification, cemeteries, and the weighing of souls at judgment","domains":["embalming","mummification","the dead","cemeteries","judgment of souls","protection of graves","the Western horizon"],"parents":["Nephthys and Osiris (most common)","Set and Nephthys","Ra and Nephthys"],"siblings":["Wepwawet (Wolf-path opener)"],"consorts":["Anput"],"children":["Kebechet (purification)"],"symbols":["jackal or dog head","flail","scales (for weighing hearts)","mummy wrappings","embalming tools"],"sacredAnimals":["jackal","dog"],"sacredPlaces":["Cynopolis","the embalming tents of all Egypt"],"equivalents":["Hermes Psychopomp (Greek — the later Greeks called him Hermanubis, fusing both guides of souls)"],"kabbalahPaths":[8,29],"description":"Anubis guided souls through the Duat (underworld) and oversaw the most critical rite of Egyptian religion: the weighing of the heart against the feather of Ma'at. If the heart was heavier than the feather, the demon Ammit devoured it; if balanced, the soul entered paradise. He invented embalming when he cared for Osiris's murdered body. His sharp jackal senses could detect spiritual impurity.","myth":"","poem":""}, + {"id":"maat","name":"Ma'at","pantheon":"egyptian","epithet":"Lady of Truth · The Feather · Daughter of Ra","role":"Goddess and personification of cosmic truth, justice, balance, order, and the harmony on which all existence depends","domains":["truth","justice","balance","cosmic order","law","morality","the weighing of souls","the seasons","stars in their courses"],"parents":["Ra"],"siblings":["Thoth (consort)"],"consorts":["Thoth"],"children":[],"symbols":["ostrich feather","scales","ankh","sceptre","outstretched wings"],"equivalents":["Themis (Greek)","Astraea (Roman)","Iustitia (Roman)"],"kabbalahPaths":[22],"description":"Ma'at's single feather was the counterweight against which the heart of every deceased person was weighed. The heart (containing all one's deeds and character) had to balance precisely with the feather of truth to enter paradise. All pharaohs ruled 'in ma'at' — in truth and cosmic order. The opposite of ma'at was isfet: chaos, injustice, disorder. The entire project of Egyptian civilization was to maintain ma'at against the encroachment of isfet.","myth":"","poem":""}, + {"id":"ptah","name":"Ptah","pantheon":"egyptian","epithet":"The Beautiful Face · Lord of Truth · Father of Beginnings","role":"Creator god of Memphis; patron of craftsmen, architects, and the arts; creator through thought and logos","domains":["creation","craftsmanship","architecture","thought","speech (logos)","the arts","the Underworld's stability (as Sokar-Osiris-Ptah)"],"parents":["self-created (no parents in Memphite theology)"],"siblings":[],"consorts":["Sekhmet (lioness goddess)","Bast (cat goddess, some traditions)"],"children":["Nefertum (lotus god)","Imhotep (deified as his son)"],"symbols":["mummy wrappings (but standing upright)","was sceptre","djed pillar","straight false beard"],"sacredAnimals":["Apis bull (lived in his Memphis temple)"],"sacredPlaces":["Memphis (Hikuptah, 'temple of the ka of Ptah' — giving Egypt its name in Greek)"],"equivalents":["Hephaestus/Vulcan (craft)","the Logos principle (philosophical creation through speech)"],"kabbalahPaths":[1,23],"description":"The Memphite theology of Ptah is perhaps the most philosophically sophisticated of all ancient religious texts: Ptah created the world through his heart (thought) and tongue (speech) — a doctrine of creation by logos that anticipates both the Greek Logos concept of Plato and the opening of the Gospel of John. The name Egypt derives from the Greek corruption of 'Hikuptah' — the temple of Ptah's ka in Memphis.","myth":"","poem":""}, + {"id":"amoun","name":"Amoun (Amun)","pantheon":"egyptian","epithet":"The Hidden One · King of the Gods · Lord of the Thrones of the Two Lands","role":"King of the gods and the national deity of Egypt; god of the hidden creative force, wind, and fertility","domains":["hidden things","wind","fertility","kingship","the sun (as Amun-Ra the composite deity)","air","creation"],"parents":["self-created (primordial god of the Ogdoad of Hermopolis)"],"siblings":["Amunet (his female counterpart)"],"consorts":["Mut"],"children":["Khonsu (moon god)"],"symbols":["double plumed crown","ram horns","blue skin (the colour of invisibility)","ankh and sceptre"],"sacredAnimals":["ram","goose"],"sacredPlaces":["Karnak (Thebes) — the largest temple complex ever built","Siwa Oasis (Oracle of Amun, consulted by Alexander the Great)"],"equivalents":["Zeus/Jupiter (king of gods aspect)","identification with Ramesses II who called himself 'son of Amun'"],"kabbalahPaths":[0,2,4,21],"description":"Amun means 'the hidden one' — the creative breath behind all manifestation that cannot be seen or named directly. As Amun-Ra he united primordial hiddenness with solar radiance, becoming the most powerful god of the New Kingdom. The Oracle of Amun at Siwa was the most famous oracle in the ancient world: Alexander the Great made a legendary journey through the desert to hear it, and was told he was the son of Amun.","myth":"","poem":""}, + {"id":"khephra","name":"Khephra","pantheon":"egyptian","epithet":"The Becoming · He Who Transforms · Dawn Scarab","role":"God of the sunrise, self-transformation, self-creation, and the daily resurrection of the sun","domains":["sunrise","transformation","self-creation","resurrection","dawn","the becoming","potential"],"parents":["Ra (as dawn-aspect of the same solar deity)"],"siblings":["Ra (noon)","Atum (sunset/evening)"],"consorts":[],"children":[],"symbols":["scarab beetle rolling a ball of dung (= the sun)","solar disk above scarab","ouroboros"],"sacredAnimals":["scarab beetle (Scarabaeus sacer)"],"equivalents":["Helios (Greek dawn aspect)"],"kabbalahPaths":[18,24,29],"description":"Khephra is Ra at the moment of dawn — the scarab beetle who rolls the solar disc over the horizon just as dung beetles roll their balls of dung. The Egyptian word for 'scarab' (kheper) means 'to come into being, to become' — making Khephra the god of perpetual becoming and transformation rather than static being. Scarab amulets were the most common protective charm in ancient Egypt, placed over the heart in mummies.","myth":"","poem":""}, + {"id":"nuit","name":"Nuit","pantheon":"egyptian","epithet":"Lady of the Stars · She Who Holds a Thousand Souls · Infinite Space","role":"Goddess of the sky, night, stars, and the cosmic vault of heaven who swallows and rebirths the sun","domains":["sky","stars","night","the cosmos","afterlife (she swallows and rebirths the sun)","infinity","space"],"parents":["Shu (god of air)","Tefnut (goddess of moisture)"],"siblings":["Geb (earth god, her husband)"],"consorts":["Geb","Ra"],"children":["Osiris","Isis","Set","Nephthys","Horus the Elder"],"symbols":["star-covered body arching over the earth","blue or black skin covered with stars","pot (meaning 'sky')","coffin lids"],"equivalents":["Ouranos/Caelus (sky)","in Thelema: infinite space and the body of the goddess"],"kabbalahPaths":[0,2,28],"description":"Nuit arches her body over the earth (Geb), and the stars adorn her skin. She swallows the sun each evening and gives birth to it each morning. In the Thelemic system of Aleister Crowley (whose work inspired Liber 777), Nuit became a central cosmic deity representing infinite space, the totality of all possibilities, the night sky as the body of the goddess. Her first words in the Book of the Law: 'Every man and every woman is a star.'","myth":"","poem":""}, + {"id":"eheieh","name":"Eheieh","pantheon":"hebrew","epithet":"I Am · Pure Being · That Which Was, Is, and Will Be · Ain Soph Aur made manifest","role":"Divine Name of Kether; the first and purest emanation of existence itself; pure being without attributes","domains":["pure being","the Crown","the source","the unmoved mover","unity","the point before creation"],"hebrew":"אהיה","meaning":"I Am / I Will Be (from the root HYH, 'to be')","sephirah":1,"kabbalahPaths":[1],"description":"Eheieh (אהיה) is the divine name given to Moses at the Burning Bush — 'Eheieh asher Eheieh' (I am that I am / I will be what I will be). In Kabbalah it is the name of pure undifferentiated existence at the Crown of the Tree. It has no attributes or qualities — only the simple, inexhaustible fact of being. To vibrate this name in meditation is to touch the ground of existence itself.","myth":"","poem":""}, + {"id":"yah","name":"Yah","pantheon":"hebrew","epithet":"The Contracted Name · The Father · The Primordial Wisdom","role":"Divine Name of Chokmah; the first two letters of the Tetragrammaton; pure creative wisdom","domains":["wisdom","the Father principle","the creative impulse","the word before manifestation","the starry cosmos"],"hebrew":"יה","meaning":"The Lord (contracted form of YHVH, the first two letters Yod-Heh)","sephirah":2,"kabbalahPaths":[2],"description":"Yah is the most contracted form of the ineffable name. As the divine name of Chokmah (Wisdom), it represents the point of pure wisdom before it unfolds into understanding. Yah appears repeatedly in Psalms ('Hallelujah' = 'Praise Yah'). In Kabbalistic meditation, Yah is the name associated with the primordial Father and the cosmic seed of all creation.","myth":"","poem":""}, + {"id":"yhvh-elohim","name":"YHVH Elohim","pantheon":"hebrew","epithet":"The Lord God · Father-Mother · The Understanding","role":"Divine Name of Binah; the Lord God of Understanding; the union of the masculine name with the feminine plural","domains":["understanding","the Great Sea","the womb of creation","time","structure","the cosmic mother","sorrow and joy"],"hebrew":"יהוה אלהים","meaning":"The Lord God (masculine singular name + feminine plural noun with singular verb = grammatical paradox of divine unity in duality)","sephirah":3,"kabbalahPaths":[3],"description":"The combination of the Tetragrammaton (YHVH) with Elohim (a grammatically feminine plural used with masculine singular verbs) represents the paradoxical union of masculine and feminine in Binah, the Great Sea of Understanding. Binah is the dark womb that gives all forms to the pure wisdom of Chokmah — the primal matter that receives and shapes the divine seed.","myth":"","poem":""}, + {"id":"el","name":"El","pantheon":"hebrew","epithet":"God the Strong · The Mighty One · Ancient of Days","role":"Divine Name of Chesed; the all-powerful, all-merciful expression of divine strength turned toward blessing","domains":["mercy","strength","benevolence","grace","abundance","divine power (turned to healing not destruction)"],"hebrew":"אל","meaning":"God / The Strong One (identical root with Arabic 'Allah' and Akkadian 'Ilu')","sephirah":4,"kabbalahPaths":[4,23],"description":"El is the most ancient Semitic word for God, with cognates in every Semitic language including Arabic 'Allah.' In Kabbalah, El governs Chesed — divine mercy, boundless abundance, and the benevolent expression of power. The divine name El is the root of many angelic names: Micha-El, Gabri-El, Rapha-El, all meaning '[attribute] of God.'","myth":"","poem":""}, + {"id":"elohim-gibor","name":"Elohim Gibor","pantheon":"hebrew","epithet":"Almighty God · God of Armies · The Severe One","role":"Divine Name of Geburah; the all-powerful God who executes justice, removes what is corrupt, and tests with fire","domains":["strength","severity","justice","war","divine wrath","purification","the surgeon's knife"],"hebrew":"אלהים גבור","meaning":"Almighty God / The Mighty Gods (Elohim = plural of majesty; Gibor = mighty, hero)","sephirah":5,"kabbalahPaths":[5],"description":"Elohim Gibor represents the severe and just dimension of God — not cruelty, but the absolute necessity of cutting away what does not serve divine purpose. Geburah is the sphere of Mars, of divine warriors, of judgment that cannot be swayed by sentiment. In traditional Kabbalah, an unbalanced Geburah (too much severity) descends into cruelty; an unbalanced Chesed (too much mercy) descends into weakness.","myth":"","poem":""}, + {"id":"yhvh-eloah","name":"YHVH Eloah ve-Daath","pantheon":"hebrew","epithet":"God Manifest in Knowledge · Lord of Beauty · The Solar Heart","role":"Divine Name of Tiphareth; God made knowable; the heart of the Tree; the solar-sacrifice principle","domains":["beauty","the heart","solar consciousness","sacrifice","the son/sun","healing","harmony","the redeemed self"],"hebrew":"יהוה אלוה ודעת","meaning":"God (YHVH) manifest as Eloah (singular) in/through Da'ath (Knowledge)","sephirah":6,"kabbalahPaths":[6],"description":"This divine name speaks of God becoming personally knowable — the abstract descending into a form that conscious beings can encounter directly. Tiphareth is the solar heart of the Tree, associated with all the solar-sacrifice figures across traditions: Osiris, Dionysus, Adonis, and the Christ. At Tiphareth the initiate experiences the Knowledge and Conversation of the Holy Guardian Angel — the higher self.","myth":"","poem":""}, + {"id":"yhvh-tzabaoth","name":"YHVH Tzabaoth","pantheon":"hebrew","epithet":"Lord of Hosts · Lord of Armies · Victorious God","role":"Divine Name of Netzach; God as commander of the armies of nature, of passion, and of creative force","domains":["victory","natural forces","the passions","emotion","creativity","instinct","the armies of the divine"],"hebrew":"יהוה צבאות","meaning":"The Lord of Hosts / Armies (Tzabaoth = multitudes, hosts, armies)","sephirah":7,"kabbalahPaths":[7],"description":"Netzach represents the natural, instinctual energies — the force of desire, creativity, and natural beauty. YHVH Tzabaoth is God as the commander of all these forces. In magical work, Netzach corresponds to nature spirits, the elemental powers of creativity, and the raw material that intellect (Hod) works upon. The tension between nature (Netzach) and mind (Hod) is one of the fundamental polarities of psychological and magical work.","myth":"","poem":""}, + {"id":"elohim-tzabaoth","name":"Elohim Tzabaoth","pantheon":"hebrew","epithet":"God of Hosts · God of Splendour · Lord of the Divine Mind","role":"Divine Name of Hod; God as the source of intellect, language, magical symbolism, and divine splendour","domains":["splendour","intellect","communication","magic","the divine mind","language","symbolic thought"],"hebrew":"אלהים צבאות","meaning":"God of Hosts / God of Armies (with Elohim's grammatical feminine-plural paradox)","sephirah":8,"kabbalahPaths":[8],"description":"Hod (Splendour) governs the abstract mind, language, and magic — Hermes, Mercury, Thoth are its planetary rulers. Elohim Tzabaoth reflects Hod's diversity: the plural Elohim represents the many forms through which divine mind manifests in language, mathematics, and magical correspondence. The Hermetic arts — astrology, alchemy, ritual magic — all operate within the sphere of Hod.","myth":"","poem":""}, + {"id":"shaddai-el-chai","name":"Shaddai El Chai","pantheon":"hebrew","epithet":"Almighty Living God · The Eternal Life · Foundation of All","role":"Divine Name of Yesod; the living generative force of creation; the etheric current beneath the physical world","domains":["the living force","the etheric substratum","sexuality","the Foundation","the moon","dreams","the astral plane","fertility"],"hebrew":"שדי אל חי","meaning":"Almighty Living God (Shaddai = the Almighty, from 'breast/nourisher'; El = God; Chai = Living/Life)","sephirah":9,"kabbalahPaths":[9],"description":"Yesod is the Foundation — the invisible etheric substratum that underlies and supports material existence. Shaddai El Chai is the Living God as the perpetual creative life-force flowing through all matter. The moon governs Yesod; dreams, visions, and the astral plane are its territory. The divine name Shaddai is found on mezuzot and amulets as a protective word at every threshold.","myth":"","poem":""}, + {"id":"adonai-melek","name":"Adonai Melek","pantheon":"hebrew","epithet":"Lord and King · God of Earth · The Immanent One","role":"Divine Name of Malkuth; God as present, immanent, and incarnate in the material world itself","domains":["the Kingdom","earth","the physical world","nature","the body","the Shekinah (divine presence in the world)"],"hebrew":"אדני מלך","meaning":"Lord and King (Adonai = My Lord, used in place of YHVH in prayer; Melek = King)","sephirah":10,"kabbalahPaths":[10],"description":"Malkuth is the culmination of all the higher sephiroth made physical in matter. Adonai Melek is God present and immanent — the divine spark within physical matter, the Shekinah (the indwelling female presence of God in the world). The mystical tradition holds that the goal of the Kabbalistic path is not to escape the material world but to raise Malkuth — to reveal the Kingdom in all its divine splendour.","myth":"","poem":""}, + {"id":"metatron","name":"Metatron","pantheon":"archangel","epithet":"Chancellor of Heaven · Prince of the Divine Countenance · The Recording Angel","role":"Highest archangel; scribe of God; guardian of Kether; sometimes identified as the transformed prophet Enoch","domains":["the Crown","the divine throne","heavenly scribe who records all deeds","the angelic hierarchy","divine mysteries","transmutation of human to angelic"],"hebrew":"מטטרון","sephirah":1,"kabbalahPaths":[1],"description":"Metatron holds a unique position among angels — he was once the prophet Enoch ('he walked with God and was not, for God took him,' Genesis 5:24), transformed into the highest archangel. He is the celestial scribe who records all human deeds in the Book of Life, and is the being closest to the divine throne. Some texts describe him as so exalted that another angel mistook him for God and was corrected.","myth":"","poem":""}, + {"id":"ratziel","name":"Ratziel","pantheon":"archangel","epithet":"Herald of God · Angel of Mysteries · Keeper of Divine Secrets","role":"Archangel of Chokmah; keeper of the mysteries of divine wisdom and cosmic law","domains":["divine mysteries","cosmic law","wisdom","heavenly secrets","the stars","astrology","the primordial Torah"],"hebrew":"רציאל","sephirah":2,"kabbalahPaths":[2],"description":"Ratziel (Raziel, 'Secret of God') is said to have written the Sefer Raziel — a compendium of divine secrets overheard standing beside the throne of God. He reportedly gave this book to Adam after the Fall, warning him of the consequences of sin. Later Noah received it before the flood. The Sefer Raziel ha-Malakh is an actual medieval Kabbalistic text of angelic magic attributed to him.","myth":"","poem":""}, + {"id":"tzaphkiel","name":"Tzaphkiel","pantheon":"archangel","epithet":"Contemplation of God · The Watcher · Angel of the Abyss","role":"Archangel of Binah; the divine contemplator; angel of grief, time, structure, and profound understanding","domains":["understanding","contemplation","sorrow","time","structure","the Abyss","the nature of limitation"],"hebrew":"צאפקיאל","sephirah":3,"kabbalahPaths":[3],"description":"Tzaphkiel (Zaphkiel/Jophiel in some traditions) governs Binah — the Great Sea of Understanding, the sphere of Saturn and the divine mother. Binah is associated with sorrow not because it is negative, but because Understanding necessarily involves limitation: to understand something is to give it form and boundary, which means accepting what it is not. Tzaphkiel embodies the contemplative stillness required for this deepest kind of knowing.","myth":"","poem":""}, + {"id":"tzadkiel","name":"Tzadkiel","pantheon":"archangel","epithet":"Righteousness of God · Angel of Mercy · Interceding Angel","role":"Archangel of Chesed; divine mercy and benevolence; the angel who stayed Abraham's hand at Mount Moriah","domains":["mercy","benevolence","abundance","grace","justice turned to compassion","the virtue of charity","the planet Jupiter"],"hebrew":"צדקיאל","sephirah":4,"kabbalahPaths":[4],"description":"Tzadkiel (Zadkiel, 'Righteousness of God') governs Chesed — mercy, abundance, and expansion. He is the angel who stayed Abraham's hand before the sacrifice of Isaac, replacing the boy with a ram. He is invoked for mercy, forgiveness, and for dissolving rigid patterns with the warmth of divine love. In magical tradition he is the angel of Jupiter, associated with all forms of increase and blessing.","myth":"","poem":""}, + {"id":"kamael","name":"Kamael","pantheon":"archangel","epithet":"Strength of God · The Burning One · Angel of Divine Wrath","role":"Archangel of Geburah; the divine warrior; angel of strength, courage, justice, and the purifying fire","domains":["strength","courage","severity","divine justice","war","the removal of impurity","the planet Mars","the Seraphim"],"hebrew":"כמאל","sephirah":5,"kabbalahPaths":[5],"description":"Kamael (Camael, Chamuel — 'Strength of God') is the archangel of Geburah — divine severity and martial power. He is the warrior who executes divine justice, cuts away spiritual corruption, and governs the Seraphim (the Fiery Serpents). In some traditions he is the angel who wrestled with Jacob at the ford of Jabbok. His fire purifies what cannot be healed any other way.","myth":"","poem":""}, + {"id":"raphael","name":"Raphael","pantheon":"archangel","epithet":"God Has Healed · The Divine Physician · Angel of Healing and Travel","role":"Archangel of Tiphareth; healer of God; guide of travellers; regent of the solar heart","domains":["healing","the sun","beauty","travel","sight","the planet Mercury (in some attributions)","Tiphareth's solar warmth"],"hebrew":"רפאל","sephirah":6,"kabbalahPaths":[6],"description":"Raphael ('God Heals') is named explicitly in the Book of Tobit, where he accompanies the young Tobias disguised as a human companion, heals Tobias's blind father with fish gall, and defeats the demon Asmodeus. In the Book of Enoch he is set over all diseases and wounds. His name teaches that genuine healing is a divine act, a restoration of wholeness in alignment with the divine image.","myth":"","poem":""}, + {"id":"haniel","name":"Haniel","pantheon":"archangel","epithet":"Grace of God · Glory of God · Angel of Love and Beauty","role":"Archangel of Netzach; angel of love, beauty, the arts, natural instinct, and the planet Venus","domains":["love","beauty","joy","natural forces","the arts","desire","the planet Venus","the emotions"],"hebrew":"חאניאל","sephirah":7,"kabbalahPaths":[7],"description":"Haniel ('Grace of God') governs Netzach — the sphere of natural beauty, desire, and creative instinct. He is invoked in magical work related to love, art, and the healing of emotional wounds. In some traditions he escorted Enoch to heaven. His domain is the gap between natural desire (Netzach) and its refining by the mind (Hod) — the raw material of all art and spiritual longing.","myth":"","poem":""}, + {"id":"michael","name":"Michael","pantheon":"archangel","epithet":"Who Is Like God? · Prince of Light · Champion of Heaven","role":"Archangel of Hod; greatest of archangels; divine champion who cast Lucifer from heaven; protector against evil","domains":["protection","justice","war against evil","the angelic armies","communication","the planet Mercury","spiritual warfare","the weighing of souls (with Gabriel)"],"hebrew":"מיכאל","sephirah":8,"kabbalahPaths":[8],"description":"Michael ('Who is like God?' — the question implies: no one) is the greatest archangel in Jewish, Christian, and Islamic traditions (as Mika'il). He cast Lucifer from heaven, guarded the gate of Eden, fought with the archangel for Moses's body, and will lead the heavenly armies at the Last Battle. In Christianity he is the patron of knights, police, and the military; in Islam he brings rain and thunder. He will weigh souls at the Last Judgment.","myth":"","poem":""}, + {"id":"gabriel","name":"Gabriel","pantheon":"archangel","epithet":"God Is My Strength · The Divine Herald · Angel of Revelation","role":"Archangel of Yesod; divine messenger par excellence; angel of annunciation, prophecy, and the moon's etheric current","domains":["messages","revelation","prophecy","dreams","the moon","water","the etheric body","the Foundation","the unconscious"],"hebrew":"גבריאל","sephirah":9,"kabbalahPaths":[9],"description":"Gabriel ('God is my Strength') announced the birth of Christ to Mary (the Annunciation), revealed the Quran to Muhammad over 23 years, and explained Daniel's apocalyptic visions. He governs Yesod — the etheric substratum where dreams, visions, and psychic current flow. In the Talmud he is the angel who turned Sodom to ash. He stands at the West in elemental magic, governing Water and the psychic unconscious.","myth":"","poem":""}, + {"id":"sandalphon","name":"Sandalphon","pantheon":"archangel","epithet":"The Twin Brother · Guardian of Earth · Weaver of Prayers","role":"Archangel of Malkuth; guardian of the material earth and physical world; twin of Metatron; weaver of human prayers into garlands before the divine throne","domains":["earth","the Kingdom","physical manifestation","prayers ascending to God","the material world's sanctity","the body as temple"],"hebrew":"סנדלפון","sephirah":10,"kabbalahPaths":[10],"description":"Sandalphon corresponds to Malkuth — the physical earth — and stands as the twin polarity of Metatron at Kether. One governs the Crown, the other the Kingdom; between them stretches the entire Tree. Ancient texts describe Sandalphon as so immeasurably tall that his feet touch the earth while his head reaches into heaven — a perfect symbol of Malkuth as both the lowest and the most fully present sphere. He is said to gather all human prayers and weave them into garlands for God's crown.","myth":"","poem":""} + ] +} diff --git a/data/hebrew-calendar.json b/data/hebrew-calendar.json new file mode 100644 index 0000000..0087545 --- /dev/null +++ b/data/hebrew-calendar.json @@ -0,0 +1,239 @@ +{ + "meta": { + "version": 1, + "notes": "Hebrew (Jewish) lunisolar calendar months with traditional associations. Holiday records are centralized in calendar-holidays.json." + }, + "calendar": { + "id": "hebrew", + "label": "Hebrew Calendar", + "type": "lunisolar", + "script": "Hebrew", + "description": "The Hebrew calendar is a lunisolar calendar used for Jewish religious observances. The religious year begins with Nisan (month of Passover); the civil year begins with Tishrei. A leap year adds a second Adar (Adar II), making 13 months." + }, + "months": [ + { + "id": "nisan", + "order": 1, + "name": "Nisan", + "nativeName": "נִיסָן", + "days": 30, + "season": "Spring", + "zodiacSign": "aries", + "tribe": "Judah", + "sense": "Speech", + "hebrewLetter": "He (ה)", + "associations": { + "zodiacSignId": "aries", + "hebrewLetterId": "he" + }, + "description": "The first month of the religious year. Called the 'month of redemption.' Passover (Pesach) commemorates the Exodus from Egypt." + }, + { + "id": "iyar", + "order": 2, + "name": "Iyar", + "nativeName": "אִיָּר", + "days": 29, + "season": "Spring", + "zodiacSign": "taurus", + "tribe": "Issachar", + "sense": "Thought", + "hebrewLetter": "Vav (ו)", + "associations": { + "zodiacSignId": "taurus", + "hebrewLetterId": "vav" + }, + "description": "A month of counting and transition between Passover and Shavuot. Yom HaShoah and Yom Ha'atzmaut fall here." + }, + { + "id": "sivan", + "order": 3, + "name": "Sivan", + "nativeName": "סִיוָן", + "days": 30, + "season": "Late Spring", + "zodiacSign": "gemini", + "tribe": "Zebulun", + "sense": "Walking", + "hebrewLetter": "Zayin (ז)", + "associations": { + "zodiacSignId": "gemini", + "hebrewLetterId": "zayin" + }, + "description": "Month of the giving of the Torah at Sinai. Shavuot (Festival of Weeks) falls on the 6th." + }, + { + "id": "tammuz", + "order": 4, + "name": "Tammuz", + "nativeName": "תַּמּוּז", + "days": 29, + "season": "Summer", + "zodiacSign": "cancer", + "tribe": "Reuben", + "sense": "Sight", + "hebrewLetter": "Het (ח)", + "associations": { + "zodiacSignId": "cancer", + "hebrewLetterId": "het" + }, + "description": "A month associated with mourning. The Golden Calf was made and the Tablets were broken on the 17th." + }, + { + "id": "av", + "order": 5, + "name": "Av", + "nativeName": "אָב", + "days": 30, + "season": "Summer", + "zodiacSign": "leo", + "tribe": "Simeon", + "sense": "Hearing", + "hebrewLetter": "Tet (ט)", + "associations": { + "zodiacSignId": "leo", + "hebrewLetterId": "tet" + }, + "description": "A month of mourning that ends with celebration. Tisha B'Av commemorates the destruction of both Temples." + }, + { + "id": "elul", + "order": 6, + "name": "Elul", + "nativeName": "אֱלוּל", + "days": 29, + "season": "Late Summer", + "zodiacSign": "virgo", + "tribe": "Gad", + "sense": "Action", + "hebrewLetter": "Yod (י)", + "associations": { + "zodiacSignId": "virgo", + "hebrewLetterId": "yod" + }, + "description": "A month of preparation and teshuva (repentance) before the High Holy Days. The shofar is blown daily." + }, + { + "id": "tishrei", + "order": 7, + "name": "Tishrei", + "nativeName": "תִּשְׁרֵי", + "days": 30, + "season": "Autumn", + "zodiacSign": "libra", + "tribe": "Ephraim", + "sense": "Coition", + "hebrewLetter": "Lamed (ל)", + "associations": { + "zodiacSignId": "libra", + "hebrewLetterId": "lamed" + }, + "description": "The holiest month of the year. Contains Rosh Hashanah (New Year), Yom Kippur (Day of Atonement), and Sukkot." + }, + { + "id": "cheshvan", + "order": 8, + "name": "Cheshvan", + "nativeName": "חֶשְׁוָן", + "days": 29, + "daysVariant": 30, + "season": "Autumn", + "zodiacSign": "scorpio", + "tribe": "Manasseh", + "sense": "Smell", + "hebrewLetter": "Nun (נ)", + "associations": { + "zodiacSignId": "scorpio", + "hebrewLetterId": "nun" + }, + "description": "Also called Marcheshvan. The only month with no Jewish holidays. Day count varies between 29–30 in different years." + }, + { + "id": "kislev", + "order": 9, + "name": "Kislev", + "nativeName": "כִּסְלֵו", + "days": 30, + "daysVariant": 29, + "season": "Early Winter", + "zodiacSign": "sagittarius", + "tribe": "Benjamin", + "sense": "Sleep", + "hebrewLetter": "Samekh (ס)", + "associations": { + "zodiacSignId": "sagittarius", + "hebrewLetterId": "samekh" + }, + "description": "Month of Hanukkah (Festival of Lights), which begins on the 25th. Day count varies 29–30." + }, + { + "id": "tevet", + "order": 10, + "name": "Tevet", + "nativeName": "טֵבֵת", + "days": 29, + "season": "Winter", + "zodiacSign": "capricorn", + "tribe": "Dan", + "sense": "Anger", + "hebrewLetter": "Ayin (ע)", + "associations": { + "zodiacSignId": "capricorn", + "hebrewLetterId": "ayin" + }, + "description": "Hanukkah continues into the early days of Tevet. The 10th is a fast day marking the siege of Jerusalem." + }, + { + "id": "shvat", + "order": 11, + "name": "Shvat", + "nativeName": "שְׁבָט", + "days": 30, + "season": "Late Winter", + "zodiacSign": "aquarius", + "tribe": "Asher", + "sense": "Taste", + "hebrewLetter": "Tzaddi (צ)", + "associations": { + "zodiacSignId": "aquarius", + "hebrewLetterId": "tsadi" + }, + "description": "The month of Tu B'Shvat, the 'New Year for Trees.' Trees begin to wake in the Land of Israel." + }, + { + "id": "adar", + "order": 12, + "name": "Adar (Adar I)", + "nativeName": "אֲדָר", + "days": 29, + "season": "Late Winter / Early Spring", + "zodiacSign": "pisces", + "tribe": "Naphtali", + "sense": "Laughter", + "hebrewLetter": "Qof (ק)", + "associations": { + "zodiacSignId": "pisces", + "hebrewLetterId": "qof" + }, + "description": "A month of joy. Purim falls on the 14th. In a leap year this becomes Adar I and Purim is celebrated in Adar II." + }, + { + "id": "adar-ii", + "order": 13, + "name": "Adar II", + "nativeName": "אֲדָר ב", + "days": 29, + "leapYearOnly": true, + "season": "Early Spring", + "zodiacSign": "pisces", + "tribe": "Naphtali", + "sense": "Laughter", + "hebrewLetter": "Qof (ק)", + "associations": { + "zodiacSignId": "pisces", + "hebrewLetterId": "qof" + }, + "description": "Added in a Hebrew leap year (7 times in every 19-year Metonic cycle). Purim is celebrated in Adar II in leap years." + } + ] +} diff --git a/data/hebrewLetters.json b/data/hebrewLetters.json new file mode 100644 index 0000000..184d17b --- /dev/null +++ b/data/hebrewLetters.json @@ -0,0 +1,353 @@ +{ + "alef": { + "id": "alef", + "letter": { + "he": "א", + "name": "Aleph", + "latin": "A" + }, + "index": 1, + "value": 1, + "meaning": { + "en": "ox" + } + }, + "bet": { + "id": "bet", + "letter": { + "he": "ב", + "name": "Bet", + "latin": "B" + }, + "index": 2, + "value": 2, + "meaning": { + "en": "house" + } + }, + "gimel": { + "id": "gimel", + "letter": { + "he": "ג", + "name": "Gimel", + "latin": "G" + }, + "index": 3, + "value": 3, + "meaning": { + "en": "camel" + } + }, + "dalet": { + "id": "dalet", + "letter": { + "he": "ד", + "name": "Dalet", + "latin": "D" + }, + "index": 4, + "value": 4, + "meaning": { + "en": "door" + } + }, + "he": { + "id": "he", + "letter": { + "he": "ה", + "name": "He", + "latin": "H" + }, + "index": 5, + "value": 5, + "meaning": { + "en": "window" + } + }, + "vav": { + "id": "vav", + "letter": { + "he": "ו", + "name": "Vav", + "latin": "V" + }, + "index": 6, + "value": 6, + "meaning": { + "en": "peg, nail, pin, hook" + } + }, + "zayin": { + "id": "zayin", + "letter": { + "he": "ז", + "name": "Zayin", + "latin": "Z" + }, + "index": 7, + "value": 7, + "meaning": { + "en": "sword, armor, weapon" + } + }, + "het": { + "id": "het", + "letter": { + "he": "ח", + "name": "Het", + "latin": "Ch" + }, + "index": 8, + "value": 8, + "meaning": { + "en": "enclosure, fence" + } + }, + "tet": { + "id": "tet", + "letter": { + "he": "ט", + "name": "Tet", + "latin": "T" + }, + "index": 9, + "value": 9, + "meaning": { + "en": "serpent" + } + }, + "yod": { + "id": "yod", + "letter": { + "he": "י", + "name": "Yod", + "latin": "I" + }, + "index": 10, + "value": 10, + "meaning": { + "en": "hand" + } + }, + "kaf": { + "id": "kaf", + "letter": { + "he": "כ", + "name": "Kaf", + "latin": "K" + }, + "index": 11, + "value": 20, + "meaning": { + "en": "palm of hand" + } + }, + "kaf-sofit": { + "id": "kaf-sofit", + "letter": { + "he": "ך", + "name": "Kaf-Sofit", + "latin": "K(f)" + }, + "index": 11, + "value": 500, + "meaning": { + "en": "palm of hand" + } + }, + "lamed": { + "id": "lamed", + "letter": { + "he": "ל", + "name": "Lamed", + "latin": "L" + }, + "index": 12, + "value": 30, + "meaning": { + "en": "ox-goad" + } + }, + "mem": { + "id": "mem", + "letter": { + "he": "מ", + "name": "Mem", + "latin": "M" + }, + "index": 13, + "value": 40, + "meaning": { + "en": "water" + } + }, + "mem-sofit": { + "id": "mem-sofit", + "letter": { + "he": "ם", + "name": "Mem-Sofit", + "latin": "M(f)" + }, + "index": 13, + "value": 600, + "meaning": { + "en": "water" + } + }, + "nun": { + "id": "nun", + "letter": { + "he": "נ", + "name": "Nun", + "latin": "N" + }, + "index": 14, + "value": 50, + "meaning": { + "en": "fish" + } + }, + "nun-sofit": { + "id": "nun-sofit", + "letter": { + "he": "ן", + "name": "Nun-Sofit", + "latin": "N(f)" + }, + "index": 14, + "value": 700, + "meaning": { + "en": "fish" + } + }, + "samekh": { + "id": "samekh", + "letter": { + "he": "ס", + "name": "Samekh", + "latin": "S" + }, + "index": 15, + "value": 60, + "meaning": { + "en": "prop, support" + } + }, + "ayin": { + "id": "ayin", + "letter": { + "he": "ע", + "name": "Ayin", + "latin": "O" + }, + "index": 16, + "value": 70, + "meaning": { + "en": "eye" + } + }, + "pe": { + "id": "pe", + "letter": { + "he": "פ", + "name": "Pe", + "latin": "P" + }, + "index": 17, + "value": 80, + "meaning": { + "en": "mouth" + } + }, + "pe-sofit": { + "id": "pe-sofit", + "letter": { + "he": "ף", + "name": "Pe-Sofit", + "latin": "P(f)" + }, + "index": 17, + "value": 800, + "meaning": { + "en": "mouth" + } + }, + "tsadi": { + "id": "tsadi", + "letter": { + "he": "צ", + "name": "Tsadi", + "latin": "Tz" + }, + "index": 18, + "value": 90, + "meaning": { + "en": "fishhook" + } + }, + "tsadi-sofit": { + "id": "tsadi-sofit", + "letter": { + "he": "ץ", + "name": "Tsadi-Sofit", + "latin": "Tz(f)" + }, + "index": 18, + "value": 900, + "meaning": { + "en": "fishhook" + } + }, + "qof": { + "id": "qof", + "letter": { + "he": "ק", + "name": "Qof", + "latin": "Q" + }, + "index": 19, + "value": 100, + "meaning": { + "en": "back of the head, ear" + } + }, + "resh": { + "id": "resh", + "letter": { + "he": "ר", + "name": "Resh", + "latin": "R" + }, + "index": 20, + "value": 200, + "meaning": { + "en": "head" + } + }, + "shin": { + "id": "shin", + "letter": { + "he": "ש", + "name": "Shin", + "latin": "Sh" + }, + "index": 21, + "value": 300, + "meaning": { + "en": "tooth" + } + }, + "tav": { + "id": "tav", + "letter": { + "he": "ת", + "name": "Tav", + "latin": "Th" + }, + "index": 22, + "value": 400, + "meaning": { + "en": "cross" + } + } +} diff --git a/data/i-ching.json b/data/i-ching.json new file mode 100644 index 0000000..c778ee7 --- /dev/null +++ b/data/i-ching.json @@ -0,0 +1,1581 @@ +{ + "meta": { + "source": "iChing.py", + "notes": "Extracted from Python I Ching specs for web dataset view. Included Tarot↔I Ching correspondences extracted from DOCX table." + }, + "trigrams": [ + { + "name": "Qian", + "chineseName": "乾", + "pinyin": "Qián", + "element": "Heaven", + "attribute": "Creative", + "binary": "111", + "description": "Pure yang drive that initiates action.", + "lineDiagram": "|||" + }, + { + "name": "Dui", + "chineseName": "兑", + "pinyin": "Duì", + "element": "Lake", + "attribute": "Joyous", + "binary": "011", + "description": "Open delight that invites community.", + "lineDiagram": "||:" + }, + { + "name": "Li", + "chineseName": "离", + "pinyin": "Lí", + "element": "Fire", + "attribute": "Clinging", + "binary": "101", + "description": "Radiant clarity that adheres to insight.", + "lineDiagram": "|:|" + }, + { + "name": "Zhen", + "chineseName": "震", + "pinyin": "Zhèn", + "element": "Thunder", + "attribute": "Arousing", + "binary": "001", + "description": "Sudden awakening that shakes stagnation.", + "lineDiagram": "|::" + }, + { + "name": "Xun", + "chineseName": "巽", + "pinyin": "Xùn", + "element": "Wind", + "attribute": "Gentle", + "binary": "110", + "description": "Penetrating influence that persuades subtly.", + "lineDiagram": ":||" + }, + { + "name": "Kan", + "chineseName": "坎", + "pinyin": "Kǎn", + "element": "Water", + "attribute": "Abysmal", + "binary": "010", + "description": "Depth, risk, and sincere feeling.", + "lineDiagram": ":|:" + }, + { + "name": "Gen", + "chineseName": "艮", + "pinyin": "Gèn", + "element": "Mountain", + "attribute": "Stillness", + "binary": "100", + "description": "Grounded rest that establishes boundaries.", + "lineDiagram": "::|" + }, + { + "name": "Kun", + "chineseName": "坤", + "pinyin": "Kūn", + "element": "Earth", + "attribute": "Receptive", + "binary": "000", + "description": "Vast receptivity that nurtures form.", + "lineDiagram": ":::" + }, + { + "name": "Xuan", + "chineseName": "玄", + "pinyin": "Xuán", + "element": "Spirit", + "attribute": "Spirit Helper", + "special": true, + "description": "A liminal spiritual force that assists the magician; not counted among the eight classic trigrams." + }, + { + "name": "XuanWind", + "chineseName": "玄風", + "pinyin": "Xuán Fēng", + "element": "Spirit Wind", + "attribute": "Subtle Force", + "special": true, + "description": "Xuan fused with Wind: transformative, liminal passage between states." + } + ], + "hexagrams": [ + { + "number": 1, + "name": "Creative Force", + "chineseName": "乾", + "pinyin": "Qián", + "judgement": "Initiative succeeds when anchored in integrity.", + "image": "Heaven above and below mirrors unstoppable drive.", + "upperTrigram": "Qian", + "lowerTrigram": "Qian", + "keywords": [ + "Leadership", + "Momentum", + "Clarity" + ], + "planetaryInfluence": "Sun", + "binary": "111111", + "lineDiagram": "||||||" + }, + { + "number": 2, + "name": "Receptive Field", + "chineseName": "坤", + "pinyin": "Kūn", + "judgement": "Grounded support flourishes through patience.", + "image": "Earth layered upon earth offers fertile space.", + "upperTrigram": "Kun", + "lowerTrigram": "Kun", + "keywords": [ + "Nurture", + "Support", + "Yielding" + ], + "planetaryInfluence": "Moon", + "binary": "000000", + "lineDiagram": "::::::" + }, + { + "number": 3, + "name": "Sprouting", + "chineseName": "屯", + "pinyin": "Zhūn", + "judgement": "Challenges at the start need perseverance.", + "image": "Water over thunder shows storms that germinate seeds.", + "upperTrigram": "Kan", + "lowerTrigram": "Zhen", + "keywords": [ + "Beginnings", + "Struggle", + "Resolve" + ], + "planetaryInfluence": "Mercury", + "binary": "010001", + "lineDiagram": "|:::|:" + }, + { + "number": 4, + "name": "Youthful Insight", + "chineseName": "蒙", + "pinyin": "Méng", + "judgement": "Ignorance yields to steady guidance.", + "image": "Mountain above water signals learning via restraint.", + "upperTrigram": "Gen", + "lowerTrigram": "Kan", + "keywords": [ + "Study", + "Mentorship", + "Humility" + ], + "planetaryInfluence": "Venus", + "binary": "100010", + "lineDiagram": ":|:::|" + }, + { + "number": 5, + "name": "Waiting", + "chineseName": "需", + "pinyin": "Xū", + "judgement": "Hold position until nourishment arrives.", + "image": "Water above heaven depicts clouds gathering provision.", + "upperTrigram": "Kan", + "lowerTrigram": "Qian", + "keywords": [ + "Patience", + "Faith", + "Preparation" + ], + "planetaryInfluence": "Mars", + "binary": "010111", + "lineDiagram": "|||:|:" + }, + { + "number": 6, + "name": "Conflict", + "chineseName": "訟", + "pinyin": "Sòng", + "judgement": "Clarity and fairness prevent escalation.", + "image": "Heaven above water shows tension seeking balance.", + "upperTrigram": "Qian", + "lowerTrigram": "Kan", + "keywords": [ + "Debate", + "Justice", + "Boundaries" + ], + "planetaryInfluence": "Jupiter", + "binary": "111010", + "lineDiagram": ":|:|||" + }, + { + "number": 7, + "name": "Collective Force", + "chineseName": "師", + "pinyin": "Shī", + "judgement": "Coordinated effort requires disciplined leadership.", + "image": "Earth over water mirrors troops marshaling supplies.", + "upperTrigram": "Kun", + "lowerTrigram": "Kan", + "keywords": [ + "Discipline", + "Leadership", + "Community" + ], + "planetaryInfluence": "Saturn", + "binary": "000010", + "lineDiagram": ":|::::" + }, + { + "number": 8, + "name": "Union", + "chineseName": "比", + "pinyin": "Bǐ", + "judgement": "Shared values attract loyal allies.", + "image": "Water over earth highlights bonds formed through empathy.", + "upperTrigram": "Kan", + "lowerTrigram": "Kun", + "keywords": [ + "Alliance", + "Affinity", + "Trust" + ], + "planetaryInfluence": "Earth", + "binary": "010000", + "lineDiagram": "::::|:" + }, + { + "number": 9, + "name": "Small Accumulating", + "chineseName": "小畜", + "pinyin": "Xiǎo Chù", + "judgement": "Gentle restraint nurtures gradual gains.", + "image": "Wind over heaven indicates tender guidance on great power.", + "upperTrigram": "Xun", + "lowerTrigram": "Qian", + "keywords": [ + "Restraint", + "Cultivation", + "Care" + ], + "planetaryInfluence": "Sun", + "binary": "110111", + "lineDiagram": "|||:||" + }, + { + "number": 10, + "name": "Treading", + "chineseName": "履", + "pinyin": "Lǚ", + "judgement": "Walk with awareness when near power.", + "image": "Heaven over lake shows respect between ranks.", + "upperTrigram": "Qian", + "lowerTrigram": "Dui", + "keywords": [ + "Conduct", + "Respect", + "Sensitivity" + ], + "planetaryInfluence": "Moon", + "binary": "111011", + "lineDiagram": "||:|||" + }, + { + "number": 11, + "name": "Peace", + "chineseName": "泰", + "pinyin": "Tài", + "judgement": "Harmony thrives when resources circulate freely.", + "image": "Earth over heaven signals prosperity descending.", + "upperTrigram": "Kun", + "lowerTrigram": "Qian", + "keywords": [ + "Harmony", + "Prosperity", + "Flourish" + ], + "planetaryInfluence": "Mercury", + "binary": "000111", + "lineDiagram": "|||:::" + }, + { + "number": 12, + "name": "Standstill", + "chineseName": "否", + "pinyin": "Pǐ", + "judgement": "When channels close, conserve strength.", + "image": "Heaven over earth reveals blocked exchange.", + "upperTrigram": "Qian", + "lowerTrigram": "Kun", + "keywords": [ + "Stagnation", + "Reflection", + "Pause" + ], + "planetaryInfluence": "Venus", + "binary": "111000", + "lineDiagram": ":::|||" + }, + { + "number": 13, + "name": "Fellowship", + "chineseName": "同人", + "pinyin": "Tóng Rén", + "judgement": "Shared purpose unites distant hearts.", + "image": "Heaven over fire shows clarity within community.", + "upperTrigram": "Qian", + "lowerTrigram": "Li", + "keywords": [ + "Community", + "Shared Vision", + "Openness" + ], + "planetaryInfluence": "Mars", + "binary": "111101", + "lineDiagram": "|:||||" + }, + { + "number": 14, + "name": "Great Possession", + "chineseName": "大有", + "pinyin": "Dà Yǒu", + "judgement": "Generosity cements lasting influence.", + "image": "Fire over heaven reflects radiance sustained by ethics.", + "upperTrigram": "Li", + "lowerTrigram": "Qian", + "keywords": [ + "Wealth", + "Stewardship", + "Confidence" + ], + "planetaryInfluence": "Jupiter", + "binary": "101111", + "lineDiagram": "||||:|" + }, + { + "number": 15, + "name": "Modesty", + "chineseName": "謙", + "pinyin": "Qiān", + "judgement": "Balance is found by lowering the proud.", + "image": "Earth over mountain reveals humility safeguarding strength.", + "upperTrigram": "Kun", + "lowerTrigram": "Gen", + "keywords": [ + "Humility", + "Balance", + "Service" + ], + "planetaryInfluence": "Saturn", + "binary": "000100", + "lineDiagram": "::|:::" + }, + { + "number": 16, + "name": "Enthusiasm", + "chineseName": "豫", + "pinyin": "Yù", + "judgement": "Inspired music rallies the people.", + "image": "Thunder over earth depicts drums stirring hearts.", + "upperTrigram": "Zhen", + "lowerTrigram": "Kun", + "keywords": [ + "Inspiration", + "Celebration", + "Momentum" + ], + "planetaryInfluence": "Earth", + "binary": "001000", + "lineDiagram": ":::|::" + }, + { + "number": 17, + "name": "Following", + "chineseName": "隨", + "pinyin": "Suí", + "judgement": "Adapt willingly to timely leadership.", + "image": "Lake over thunder points to joyful allegiance.", + "upperTrigram": "Dui", + "lowerTrigram": "Zhen", + "keywords": [ + "Adaptation", + "Loyalty", + "Flow" + ], + "planetaryInfluence": "Sun", + "binary": "011001", + "lineDiagram": "|::||:" + }, + { + "number": 18, + "name": "Repairing", + "chineseName": "蠱", + "pinyin": "Gǔ", + "judgement": "Address decay with responsibility and care.", + "image": "Mountain over wind shows correction of lineages.", + "upperTrigram": "Gen", + "lowerTrigram": "Xun", + "keywords": [ + "Restoration", + "Accountability", + "Healing" + ], + "planetaryInfluence": "Moon", + "binary": "100110", + "lineDiagram": ":||::|" + }, + { + "number": 19, + "name": "Approach", + "chineseName": "臨", + "pinyin": "Lín", + "judgement": "Leaders draw near to listen sincerely.", + "image": "Earth over lake signifies compassion visiting the people.", + "upperTrigram": "Kun", + "lowerTrigram": "Dui", + "keywords": [ + "Empathy", + "Guidance", + "Presence" + ], + "planetaryInfluence": "Mercury", + "binary": "000011", + "lineDiagram": "||::::" + }, + { + "number": 20, + "name": "Contemplation", + "chineseName": "觀", + "pinyin": "Guān", + "judgement": "Observation inspires ethical alignment.", + "image": "Wind over earth is the elevated view of the sage.", + "upperTrigram": "Xun", + "lowerTrigram": "Kun", + "keywords": [ + "Perspective", + "Ritual", + "Vision" + ], + "planetaryInfluence": "Venus", + "binary": "110000", + "lineDiagram": "::::||" + }, + { + "number": 21, + "name": "Biting Through", + "chineseName": "噬嗑", + "pinyin": "Shì Kè", + "judgement": "Decisive action cuts through obstruction.", + "image": "Fire over thunder shows justice enforced with clarity.", + "upperTrigram": "Li", + "lowerTrigram": "Zhen", + "keywords": [ + "Decision", + "Justice", + "Resolve" + ], + "planetaryInfluence": "Mars", + "binary": "101001", + "lineDiagram": "|::|:|" + }, + { + "number": 22, + "name": "Grace", + "chineseName": "賁", + "pinyin": "Bì", + "judgement": "Beauty adorns substance when humility remains.", + "image": "Mountain over fire highlights poise and restraint.", + "upperTrigram": "Gen", + "lowerTrigram": "Li", + "keywords": [ + "Aesthetics", + "Poise", + "Form" + ], + "planetaryInfluence": "Jupiter", + "binary": "100101", + "lineDiagram": "|:|::|" + }, + { + "number": 23, + "name": "Splitting Apart", + "chineseName": "剝", + "pinyin": "Bō", + "judgement": "When decay spreads, strip away excess.", + "image": "Mountain over earth signals outer shells falling.", + "upperTrigram": "Gen", + "lowerTrigram": "Kun", + "keywords": [ + "Decline", + "Release", + "Truth" + ], + "planetaryInfluence": "Saturn", + "binary": "100000", + "lineDiagram": ":::::|" + }, + { + "number": 24, + "name": "Return", + "chineseName": "復", + "pinyin": "Fù", + "judgement": "Cycles renew when rest follows completion.", + "image": "Earth over thunder marks the turning of the year.", + "upperTrigram": "Kun", + "lowerTrigram": "Zhen", + "keywords": [ + "Renewal", + "Rhythm", + "Faith" + ], + "planetaryInfluence": "Earth", + "binary": "000001", + "lineDiagram": "|:::::" + }, + { + "number": 25, + "name": "Innocence", + "chineseName": "無妄", + "pinyin": "Wú Wàng", + "judgement": "Sincerity triumphs over scheming.", + "image": "Heaven over thunder shows spontaneous virtue.", + "upperTrigram": "Qian", + "lowerTrigram": "Zhen", + "keywords": [ + "Authenticity", + "Spontaneity", + "Trust" + ], + "planetaryInfluence": "Sun", + "binary": "111001", + "lineDiagram": "|::|||" + }, + { + "number": 26, + "name": "Great Taming", + "chineseName": "大畜", + "pinyin": "Dà Chù", + "judgement": "Conserve strength until action serves wisdom.", + "image": "Mountain over heaven portrays restraint harnessing power.", + "upperTrigram": "Gen", + "lowerTrigram": "Qian", + "keywords": [ + "Discipline", + "Reserve", + "Mastery" + ], + "planetaryInfluence": "Moon", + "binary": "100111", + "lineDiagram": "|||::|" + }, + { + "number": 27, + "name": "Nourishment", + "chineseName": "頤", + "pinyin": "Yí", + "judgement": "Words and food alike must be chosen with care.", + "image": "Mountain over thunder emphasizes mindful sustenance.", + "upperTrigram": "Gen", + "lowerTrigram": "Zhen", + "keywords": [ + "Nutrition", + "Speech", + "Mindfulness" + ], + "planetaryInfluence": "Mercury", + "binary": "100001", + "lineDiagram": "|::::|" + }, + { + "number": 28, + "name": "Great Exceeding", + "chineseName": "大過", + "pinyin": "Dà Guò", + "judgement": "Bearing heavy loads demands flexibility.", + "image": "Lake over wind shows a beam bending before it breaks.", + "upperTrigram": "Dui", + "lowerTrigram": "Xun", + "keywords": [ + "Weight", + "Adaptability", + "Responsibility" + ], + "planetaryInfluence": "Venus", + "binary": "011110", + "lineDiagram": ":||||:" + }, + { + "number": 29, + "name": "The Abyss", + "chineseName": "坎", + "pinyin": "Kǎn", + "judgement": "Repeated trials teach sincere caution.", + "image": "Water over water is the perilous gorge.", + "upperTrigram": "Kan", + "lowerTrigram": "Kan", + "keywords": [ + "Trial", + "Honesty", + "Depth" + ], + "planetaryInfluence": "Mars", + "binary": "010010", + "lineDiagram": ":|::|:" + }, + { + "number": 30, + "name": "Radiance", + "chineseName": "離", + "pinyin": "Lí", + "judgement": "Clarity is maintained by tending the flame.", + "image": "Fire over fire represents brilliance sustained through care.", + "upperTrigram": "Li", + "lowerTrigram": "Li", + "keywords": [ + "Illumination", + "Culture", + "Attention" + ], + "planetaryInfluence": "Jupiter", + "binary": "101101", + "lineDiagram": "|:||:|" + }, + { + "number": 31, + "name": "Influence", + "chineseName": "咸", + "pinyin": "Xián", + "judgement": "Sincere attraction arises from mutual respect.", + "image": "Lake over mountain highlights responsive hearts.", + "upperTrigram": "Dui", + "lowerTrigram": "Gen", + "keywords": [ + "Attraction", + "Mutuality", + "Sensitivity" + ], + "planetaryInfluence": "Saturn", + "binary": "011100", + "lineDiagram": "::|||:" + }, + { + "number": 32, + "name": "Duration", + "chineseName": "恒", + "pinyin": "Héng", + "judgement": "Commitment endures when balanced.", + "image": "Thunder over wind speaks of constancy amid change.", + "upperTrigram": "Zhen", + "lowerTrigram": "Xun", + "keywords": [ + "Commitment", + "Consistency", + "Rhythm" + ], + "planetaryInfluence": "Earth", + "binary": "001110", + "lineDiagram": ":|||::" + }, + { + "number": 33, + "name": "Retreat", + "chineseName": "遯", + "pinyin": "Dùn", + "judgement": "Strategic withdrawal preserves integrity.", + "image": "Heaven over mountain shows noble retreat.", + "upperTrigram": "Qian", + "lowerTrigram": "Gen", + "keywords": [ + "Withdrawal", + "Strategy", + "Self-care" + ], + "planetaryInfluence": "Sun", + "binary": "111100", + "lineDiagram": "::||||" + }, + { + "number": 34, + "name": "Great Power", + "chineseName": "大壯", + "pinyin": "Dà Zhuàng", + "judgement": "Strength must remain aligned with virtue.", + "image": "Thunder over heaven affirms action matched with purpose.", + "upperTrigram": "Zhen", + "lowerTrigram": "Qian", + "keywords": [ + "Power", + "Ethics", + "Momentum" + ], + "planetaryInfluence": "Moon", + "binary": "001111", + "lineDiagram": "||||::" + }, + { + "number": 35, + "name": "Progress", + "chineseName": "晉", + "pinyin": "Jìn", + "judgement": "Advancement arrives through clarity and loyalty.", + "image": "Fire over earth depicts dawn spreading across the plain.", + "upperTrigram": "Li", + "lowerTrigram": "Kun", + "keywords": [ + "Advancement", + "Visibility", + "Service" + ], + "planetaryInfluence": "Mercury", + "binary": "101000", + "lineDiagram": ":::|:|" + }, + { + "number": 36, + "name": "Darkening Light", + "chineseName": "明夷", + "pinyin": "Míng Yí", + "judgement": "Protect the inner light when circumstances grow harsh.", + "image": "Earth over fire shows brilliance concealed for safety.", + "upperTrigram": "Kun", + "lowerTrigram": "Li", + "keywords": [ + "Protection", + "Subtlety", + "Endurance" + ], + "planetaryInfluence": "Venus", + "binary": "000101", + "lineDiagram": "|:|:::" + }, + { + "number": 37, + "name": "Family", + "chineseName": "家人", + "pinyin": "Jiā Rén", + "judgement": "Clear roles nourish household harmony.", + "image": "Wind over fire indicates rituals ordering the home.", + "upperTrigram": "Xun", + "lowerTrigram": "Li", + "keywords": [ + "Home", + "Roles", + "Care" + ], + "planetaryInfluence": "Mars", + "binary": "110101", + "lineDiagram": "|:|:||" + }, + { + "number": 38, + "name": "Opposition", + "chineseName": "睽", + "pinyin": "Kuí", + "judgement": "Recognize difference without hostility.", + "image": "Fire over lake reflects contrast seeking balance.", + "upperTrigram": "Li", + "lowerTrigram": "Dui", + "keywords": [ + "Contrast", + "Perspective", + "Tolerance" + ], + "planetaryInfluence": "Jupiter", + "binary": "101011", + "lineDiagram": "||:|:|" + }, + { + "number": 39, + "name": "Obstruction", + "chineseName": "蹇", + "pinyin": "Jiǎn", + "judgement": "Turn hindrance into training.", + "image": "Water over mountain shows difficult ascent.", + "upperTrigram": "Kan", + "lowerTrigram": "Gen", + "keywords": [ + "Obstacle", + "Effort", + "Learning" + ], + "planetaryInfluence": "Saturn", + "binary": "010100", + "lineDiagram": "::|:|:" + }, + { + "number": 40, + "name": "Deliverance", + "chineseName": "解", + "pinyin": "Xiè", + "judgement": "Relief comes when knots are untied.", + "image": "Thunder over water portrays release after storm.", + "upperTrigram": "Zhen", + "lowerTrigram": "Kan", + "keywords": [ + "Release", + "Solution", + "Breath" + ], + "planetaryInfluence": "Earth", + "binary": "001010", + "lineDiagram": ":|:|::" + }, + { + "number": 41, + "name": "Decrease", + "chineseName": "損", + "pinyin": "Sǔn", + "judgement": "Voluntary simplicity restores balance.", + "image": "Mountain over lake shows graceful sharing of resources.", + "upperTrigram": "Gen", + "lowerTrigram": "Dui", + "keywords": [ + "Simplicity", + "Offering", + "Balance" + ], + "planetaryInfluence": "Sun", + "binary": "100011", + "lineDiagram": "||:::|" + }, + { + "number": 42, + "name": "Increase", + "chineseName": "益", + "pinyin": "Yì", + "judgement": "Blessings multiply when shared.", + "image": "Wind over thunder reveals generous expansion.", + "upperTrigram": "Xun", + "lowerTrigram": "Zhen", + "keywords": [ + "Growth", + "Generosity", + "Opportunity" + ], + "planetaryInfluence": "Moon", + "binary": "110001", + "lineDiagram": "|:::||" + }, + { + "number": 43, + "name": "Breakthrough", + "chineseName": "夬", + "pinyin": "Guài", + "judgement": "Speak truth boldly to clear corruption.", + "image": "Lake over heaven highlights decisive proclamation.", + "upperTrigram": "Dui", + "lowerTrigram": "Qian", + "keywords": [ + "Resolution", + "Declaration", + "Courage" + ], + "planetaryInfluence": "Mercury", + "binary": "011111", + "lineDiagram": "|||||:" + }, + { + "number": 44, + "name": "Encounter", + "chineseName": "姤", + "pinyin": "Gòu", + "judgement": "Unexpected influence requires discernment.", + "image": "Heaven over wind shows potent visitors arriving.", + "upperTrigram": "Qian", + "lowerTrigram": "Xun", + "keywords": [ + "Encounter", + "Discernment", + "Temptation" + ], + "planetaryInfluence": "Venus", + "binary": "111110", + "lineDiagram": ":|||||" + }, + { + "number": 45, + "name": "Gathering", + "chineseName": "萃", + "pinyin": "Cuì", + "judgement": "Unity grows when motive is sincere.", + "image": "Lake over earth signifies assembly around shared cause.", + "upperTrigram": "Dui", + "lowerTrigram": "Kun", + "keywords": [ + "Assembly", + "Devotion", + "Focus" + ], + "planetaryInfluence": "Mars", + "binary": "011000", + "lineDiagram": ":::||:" + }, + { + "number": 46, + "name": "Ascending", + "chineseName": "升", + "pinyin": "Shēng", + "judgement": "Slow steady progress pierces obstacles.", + "image": "Earth over wind shows roots pushing upward.", + "upperTrigram": "Kun", + "lowerTrigram": "Xun", + "keywords": [ + "Growth", + "Perseverance", + "Aspiration" + ], + "planetaryInfluence": "Jupiter", + "binary": "000110", + "lineDiagram": ":||:::" + }, + { + "number": 47, + "name": "Oppression", + "chineseName": "困", + "pinyin": "Kùn", + "judgement": "Constraints refine inner resolve.", + "image": "Lake over water indicates fatigue relieved only by integrity.", + "upperTrigram": "Dui", + "lowerTrigram": "Kan", + "keywords": [ + "Constraint", + "Endurance", + "Faith" + ], + "planetaryInfluence": "Saturn", + "binary": "011010", + "lineDiagram": ":|:||:" + }, + { + "number": 48, + "name": "The Well", + "chineseName": "井", + "pinyin": "Jǐng", + "judgement": "Communal resources must be maintained.", + "image": "Water over wind depicts a well drawing fresh insight.", + "upperTrigram": "Kan", + "lowerTrigram": "Xun", + "keywords": [ + "Resource", + "Maintenance", + "Depth" + ], + "planetaryInfluence": "Earth", + "binary": "010110", + "lineDiagram": ":||:|:" + }, + { + "number": 49, + "name": "Revolution", + "chineseName": "革", + "pinyin": "Gé", + "judgement": "Change succeeds when timing and virtue align.", + "image": "Lake over fire indicates shedding the old skin.", + "upperTrigram": "Dui", + "lowerTrigram": "Li", + "keywords": [ + "Change", + "Timing", + "Renewal" + ], + "planetaryInfluence": "Sun", + "binary": "011101", + "lineDiagram": "|:|||:" + }, + { + "number": 50, + "name": "The Vessel", + "chineseName": "鼎", + "pinyin": "Dǐng", + "judgement": "Elevated service transforms the culture.", + "image": "Fire over wind depicts the cauldron that refines offerings.", + "upperTrigram": "Li", + "lowerTrigram": "Xun", + "keywords": [ + "Service", + "Transformation", + "Heritage" + ], + "planetaryInfluence": "Moon", + "binary": "101110", + "lineDiagram": ":|||:|" + }, + { + "number": 51, + "name": "Arousing Thunder", + "chineseName": "震", + "pinyin": "Zhèn", + "judgement": "Shock awakens the heart to reverence.", + "image": "Thunder over thunder doubles the drumbeat of alertness.", + "upperTrigram": "Zhen", + "lowerTrigram": "Zhen", + "keywords": [ + "Shock", + "Awakening", + "Movement" + ], + "planetaryInfluence": "Mercury", + "binary": "001001", + "lineDiagram": "|::|::" + }, + { + "number": 52, + "name": "Still Mountain", + "chineseName": "艮", + "pinyin": "Gèn", + "judgement": "Cultivate stillness to master desire.", + "image": "Mountain over mountain shows unmoving focus.", + "upperTrigram": "Gen", + "lowerTrigram": "Gen", + "keywords": [ + "Stillness", + "Meditation", + "Boundaries" + ], + "planetaryInfluence": "Venus", + "binary": "100100", + "lineDiagram": "::|::|" + }, + { + "number": 53, + "name": "Gradual Development", + "chineseName": "漸", + "pinyin": "Jiàn", + "judgement": "Lasting progress resembles a tree growing rings.", + "image": "Wind over mountain displays slow maturation.", + "upperTrigram": "Xun", + "lowerTrigram": "Gen", + "keywords": [ + "Patience", + "Evolution", + "Commitment" + ], + "planetaryInfluence": "Mars", + "binary": "110100", + "lineDiagram": "::|:||" + }, + { + "number": 54, + "name": "Marrying Maiden", + "chineseName": "歸妹", + "pinyin": "Guī Mèi", + "judgement": "Adjust expectations when circumstances limit rank.", + "image": "Thunder over lake spotlights unequal partnerships.", + "upperTrigram": "Zhen", + "lowerTrigram": "Dui", + "keywords": [ + "Transition", + "Adaptation", + "Protocol" + ], + "planetaryInfluence": "Jupiter", + "binary": "001011", + "lineDiagram": "||:|::" + }, + { + "number": 55, + "name": "Abundance", + "chineseName": "豐", + "pinyin": "Fēng", + "judgement": "Radiant success must be handled with balance.", + "image": "Thunder over fire illuminates the hall at noon.", + "upperTrigram": "Zhen", + "lowerTrigram": "Li", + "keywords": [ + "Splendor", + "Responsibility", + "Timing" + ], + "planetaryInfluence": "Saturn", + "binary": "001101", + "lineDiagram": "|:||::" + }, + { + "number": 56, + "name": "The Wanderer", + "chineseName": "旅", + "pinyin": "Lǚ", + "judgement": "Travel lightly and guard reputation.", + "image": "Fire over mountain marks a traveler tending the campfire.", + "upperTrigram": "Li", + "lowerTrigram": "Gen", + "keywords": [ + "Travel", + "Restraint", + "Awareness" + ], + "planetaryInfluence": "Earth", + "binary": "101100", + "lineDiagram": "::||:|" + }, + { + "number": 57, + "name": "Gentle Wind", + "chineseName": "巽", + "pinyin": "Xùn", + "judgement": "Persistent influence accomplishes what force cannot.", + "image": "Wind over wind indicates subtle penetration.", + "upperTrigram": "Xun", + "lowerTrigram": "Xun", + "keywords": [ + "Penetration", + "Diplomacy", + "Subtlety" + ], + "planetaryInfluence": "Sun", + "binary": "110110", + "lineDiagram": ":||:||" + }, + { + "number": 58, + "name": "Joyous Lake", + "chineseName": "兌", + "pinyin": "Duì", + "judgement": "Openhearted dialogue dissolves resentment.", + "image": "Lake over lake celebrates shared delight.", + "upperTrigram": "Dui", + "lowerTrigram": "Dui", + "keywords": [ + "Joy", + "Conversation", + "Trust" + ], + "planetaryInfluence": "Moon", + "binary": "011011", + "lineDiagram": "||:||:" + }, + { + "number": 59, + "name": "Dispersion", + "chineseName": "渙", + "pinyin": "Huàn", + "judgement": "Loosen rigid structures so spirit can move.", + "image": "Wind over water shows breath dispersing fear.", + "upperTrigram": "Xun", + "lowerTrigram": "Kan", + "keywords": [ + "Dissolve", + "Freedom", + "Relief" + ], + "planetaryInfluence": "Mercury", + "binary": "110010", + "lineDiagram": ":|::||" + }, + { + "number": 60, + "name": "Limitation", + "chineseName": "節", + "pinyin": "Jié", + "judgement": "Clear boundaries enable real freedom.", + "image": "Water over lake portrays calibrated vessels.", + "upperTrigram": "Kan", + "lowerTrigram": "Dui", + "keywords": [ + "Boundaries", + "Measure", + "Discipline" + ], + "planetaryInfluence": "Venus", + "binary": "010011", + "lineDiagram": "||::|:" + }, + { + "number": 61, + "name": "Inner Truth", + "chineseName": "中孚", + "pinyin": "Zhōng Fú", + "judgement": "Trustworthiness unites disparate groups.", + "image": "Wind over lake depicts resonance within the heart.", + "upperTrigram": "Xun", + "lowerTrigram": "Dui", + "keywords": [ + "Sincerity", + "Empathy", + "Alignment" + ], + "planetaryInfluence": "Mars", + "binary": "110011", + "lineDiagram": "||::||" + }, + { + "number": 62, + "name": "Small Exceeding", + "chineseName": "小過", + "pinyin": "Xiǎo Guò", + "judgement": "Attend to details when stakes are delicate.", + "image": "Thunder over mountain reveals careful movement.", + "upperTrigram": "Zhen", + "lowerTrigram": "Gen", + "keywords": [ + "Detail", + "Caution", + "Adjustment" + ], + "planetaryInfluence": "Jupiter", + "binary": "001100", + "lineDiagram": "::||::" + }, + { + "number": 63, + "name": "After Completion", + "chineseName": "既濟", + "pinyin": "Jì Jì", + "judgement": "Success endures only if vigilance continues.", + "image": "Water over fire displays balance maintained through work.", + "upperTrigram": "Kan", + "lowerTrigram": "Li", + "keywords": [ + "Completion", + "Maintenance", + "Balance" + ], + "planetaryInfluence": "Saturn", + "binary": "010101", + "lineDiagram": "|:|:|:" + }, + { + "number": 64, + "name": "Before Completion", + "chineseName": "未濟", + "pinyin": "Wèi Jì", + "judgement": "Stay attentive as outcomes crystallize.", + "image": "Fire over water illustrates the final push before harmony.", + "upperTrigram": "Li", + "lowerTrigram": "Kan", + "keywords": [ + "Transition", + "Focus", + "Preparation" + ], + "planetaryInfluence": "Earth", + "binary": "101010", + "lineDiagram": ":|:|:|" + } + ], + "correspondences": { + "meta": { + "sourceDocument": "i-ching-and-tarot-correspondences-table.docx", + "extractedOn": "2026-02-24", + "notes": "Tarot-to-trigram correspondences extracted from DOCX table rows where headers are Tarot | Trigram." + }, + "tarotToTrigram": [ + { + "tarot": "Ace of Cups", + "trigram": "Kan" + }, + { + "tarot": "Ace of Pentacles", + "trigram": "Kun" + }, + { + "tarot": "Ace of Swords", + "trigram": "Qian" + }, + { + "tarot": "Ace of Wands", + "trigram": "Li" + }, + { + "tarot": "Eight of Cups", + "trigram": "Xun" + }, + { + "tarot": "Eight of Pentacles", + "trigram": "Gen" + }, + { + "tarot": "Eight of Swords", + "trigram": "Dui" + }, + { + "tarot": "Eight of Wands", + "trigram": "Zhen" + }, + { + "tarot": "Five of Cups", + "trigram": "Kan" + }, + { + "tarot": "Five of Pentacles", + "trigram": "Kun" + }, + { + "tarot": "Five of Swords", + "trigram": "Qian" + }, + { + "tarot": "Five of Wands", + "trigram": "Li" + }, + { + "tarot": "Four of Cups", + "trigram": "Xun" + }, + { + "tarot": "Four of Pentacles", + "trigram": "Gen" + }, + { + "tarot": "Four of Swords", + "trigram": "Dui" + }, + { + "tarot": "Four of Wands", + "trigram": "Zhen" + }, + { + "tarot": "Key 0: The Fool", + "trigram": "Qian" + }, + { + "tarot": "Key 1: The Magician", + "trigram": "Xuan" + }, + { + "tarot": "Key 10: Wheel of Fortune", + "trigram": "Xun" + }, + { + "tarot": "Key 11: Justice / Strength", + "trigram": "Xuan" + }, + { + "tarot": "Key 12: The Hanged Man", + "trigram": "Kan" + }, + { + "tarot": "Key 13: Death", + "trigram": "XuanWind" + }, + { + "tarot": "Key 14: Temperance", + "trigram": "Xuan" + }, + { + "tarot": "Key 15: The Devil", + "trigram": "Kun" + }, + { + "tarot": "Key 16: The Tower", + "trigram": "Zhen" + }, + { + "tarot": "Key 17: The Star", + "trigram": "Dui" + }, + { + "tarot": "Key 18: The Moon", + "trigram": "Xuan" + }, + { + "tarot": "Key 19: The Sun", + "trigram": "Li" + }, + { + "tarot": "Key 2: The High Priestess", + "trigram": "Kan" + }, + { + "tarot": "Key 20: Judgement", + "trigram": "Zhen" + }, + { + "tarot": "Key 21: The World", + "trigram": "Kun" + }, + { + "tarot": "Key 3: The Empress", + "trigram": "Qian" + }, + { + "tarot": "Key 4: The Emperor", + "trigram": "Li" + }, + { + "tarot": "Key 5: The Hierophant", + "trigram": "Gen" + }, + { + "tarot": "Key 6: The Lovers", + "trigram": "Dui" + }, + { + "tarot": "Key 7: The Chariot", + "trigram": "Xuan" + }, + { + "tarot": "Key 8: Strength / Justice", + "trigram": "Xuan" + }, + { + "tarot": "Key 9: The Hermit", + "trigram": "Gen" + }, + { + "tarot": "King of Cups", + "trigram": "Kan" + }, + { + "tarot": "King of Pentacles", + "trigram": "Kun" + }, + { + "tarot": "King of Swords", + "trigram": "Qian" + }, + { + "tarot": "King of Wands", + "trigram": "Li" + }, + { + "tarot": "Knight of Cups", + "trigram": "Kan" + }, + { + "tarot": "Knight of Pentacles", + "trigram": "Kun" + }, + { + "tarot": "Knight of Swords", + "trigram": "Qian" + }, + { + "tarot": "Knight of Wands", + "trigram": "Li" + }, + { + "tarot": "Nine of Cups", + "trigram": "Kan" + }, + { + "tarot": "Nine of Pentacles", + "trigram": "Kun" + }, + { + "tarot": "Nine of Swords", + "trigram": "Qian" + }, + { + "tarot": "Nine of Wands", + "trigram": "Li" + }, + { + "tarot": "Page of Cups", + "trigram": "Xun" + }, + { + "tarot": "Page of Pentacles", + "trigram": "Gen" + }, + { + "tarot": "Page of Swords", + "trigram": "Dui" + }, + { + "tarot": "Page of Wands", + "trigram": "Zhen" + }, + { + "tarot": "Queen of Cups", + "trigram": "Xun" + }, + { + "tarot": "Queen of Pentacles", + "trigram": "Gen" + }, + { + "tarot": "Queen of Swords", + "trigram": "Dui" + }, + { + "tarot": "Queen of Wands", + "trigram": "Zhen" + }, + { + "tarot": "Seven of Cups", + "trigram": "Kan" + }, + { + "tarot": "Seven of Pentacles", + "trigram": "Kun" + }, + { + "tarot": "Seven of Swords", + "trigram": "Qian" + }, + { + "tarot": "Seven of Wands", + "trigram": "Li" + }, + { + "tarot": "Six of Cups", + "trigram": "Xun" + }, + { + "tarot": "Six of Pentacles", + "trigram": "Gen" + }, + { + "tarot": "Six of Swords", + "trigram": "Dui" + }, + { + "tarot": "Six of Wands", + "trigram": "Zhen" + }, + { + "tarot": "Ten of Cups", + "trigram": "Xun" + }, + { + "tarot": "Ten of Pentacles", + "trigram": "Gen" + }, + { + "tarot": "Ten of Swords", + "trigram": "Dui" + }, + { + "tarot": "Ten of Wands", + "trigram": "Zhen" + }, + { + "tarot": "Three of Cups", + "trigram": "Kan" + }, + { + "tarot": "Three of Pentacles", + "trigram": "Kun" + }, + { + "tarot": "Three of Swords", + "trigram": "Qian" + }, + { + "tarot": "Three of Wands", + "trigram": "Li" + }, + { + "tarot": "Two of Cups", + "trigram": "Xun" + }, + { + "tarot": "Two of Pentacles", + "trigram": "Gen" + }, + { + "tarot": "Two of Swords", + "trigram": "Dui" + }, + { + "tarot": "Two of Wands", + "trigram": "Zhen" + } + ] + } +} diff --git a/data/islamic-calendar.json b/data/islamic-calendar.json new file mode 100644 index 0000000..60d1d49 --- /dev/null +++ b/data/islamic-calendar.json @@ -0,0 +1,136 @@ +{ + "meta": { + "version": 1, + "notes": "Islamic (Hijri) lunar calendar months with traditional associations and major observances. Holiday records are centralized in calendar-holidays.json." + }, + "calendar": { + "id": "islamic", + "label": "Islamic Calendar", + "type": "lunar", + "script": "Arabic", + "description": "The Islamic calendar (Hijri/AH) is a purely lunar calendar of 12 months in a year of 354 or 355 days. It began with the Prophet Muhammad's migration (Hijra) from Mecca to Medina in 622 CE. Four months are considered sacred (Al-Ashhur Al-Hurum): Muharram, Rajab, Dhu al-Qada, and Dhu al-Hijja." + }, + "months": [ + { + "id": "muharram", + "order": 1, + "name": "Muharram", + "nativeName": "مُحَرَّم", + "meaning": "Forbidden / Sacred", + "days": 30, + "sacred": true, + "description": "The first month of the Islamic year and one of the four sacred months in which warfare is prohibited. Islamic New Year (Hijri New Year) begins on the 1st. Ashura falls on the 10th." + }, + { + "id": "safar", + "order": 2, + "name": "Safar", + "nativeName": "صَفَر", + "meaning": "Empty / Yellow", + "days": 29, + "sacred": false, + "description": "Traditionally a month associated with misfortune in pre-Islamic Arab belief; Islam discouraged such superstition. No major holidays." + }, + { + "id": "rabi-al-awwal", + "order": 3, + "name": "Rabi' al-Awwal", + "nativeName": "رَبِيع الأَوَّل", + "meaning": "First Spring", + "days": 30, + "sacred": false, + "description": "The month of the Prophet Muhammad's birth (Mawlid al-Nabi) and his migration to Medina. A month of celebration in many Muslim communities." + }, + { + "id": "rabi-al-akhir", + "order": 4, + "name": "Rabi' al-Akhir", + "nativeName": "رَبِيع الآخِر", + "meaning": "Second Spring / Last Spring", + "days": 29, + "sacred": false, + "description": "Also called Rabi' al-Thani (Second Spring). No major holidays." + }, + { + "id": "jumada-al-awwal", + "order": 5, + "name": "Jumada al-Awwal", + "nativeName": "جُمَادَى الأُولَى", + "meaning": "First of Parched Land", + "days": 30, + "sacred": false, + "description": "The fifth month. No major holidays. The name reflects an ancient season of early drought." + }, + { + "id": "jumada-al-akhir", + "order": 6, + "name": "Jumada al-Akhir", + "nativeName": "جُمَادَى الآخِرَة", + "meaning": "Last of Parched Land", + "days": 29, + "sacred": false, + "description": "Also called Jumada al-Thani. Concludes the first half of the Islamic year." + }, + { + "id": "rajab", + "order": 7, + "name": "Rajab", + "nativeName": "رَجَب", + "meaning": "To Respect", + "days": 30, + "sacred": true, + "description": "One of the four sacred months. Warfare was forbidden. Contains Laylat al-Mi'raj (Night of Ascension)." + }, + { + "id": "shaban", + "order": 8, + "name": "Sha'ban", + "nativeName": "شَعْبَان", + "meaning": "To be Scattered / Dispersed", + "days": 29, + "sacred": false, + "description": "The month between Rajab and Ramadan. A time of spiritual preparation for fasting. Contains the Night of Records (Laylat al-Bara'at)." + }, + { + "id": "ramadan", + "order": 9, + "name": "Ramadan", + "nativeName": "رَمَضَان", + "meaning": "Scorching Heat", + "days": 30, + "sacred": false, + "description": "The holiest month of the Islamic year. Fasting (Sawm) from dawn to sunset is obligatory for adult Muslims — one of the Five Pillars of Islam. The Quran was revealed in this month. Ends with Eid al-Fitr." + }, + { + "id": "shawwal", + "order": 10, + "name": "Shawwal", + "nativeName": "شَوَّال", + "meaning": "Raised / Lifted", + "days": 29, + "sacred": false, + "description": "The month following Ramadan. Begins with Eid al-Fitr. Voluntary six-day fast (Shawwal fast) is recommended." + }, + { + "id": "dhu-al-qada", + "order": 11, + "name": "Dhu al-Qada", + "nativeName": "ذُو الْقَعْدَة", + "meaning": "Possessor of Truce / Sitting", + "days": 30, + "sacred": true, + "description": "One of the four sacred months. Pilgrims begin gathering for Hajj. Warfare was traditionally prohibited." + }, + { + "id": "dhu-al-hijja", + "order": 12, + "name": "Dhu al-Hijja", + "nativeName": "ذُو الْحِجَّة", + "meaning": "Possessor of the Pilgrimage", + "days": 29, + "daysVariant": 30, + "sacred": true, + "description": "The month of Hajj pilgrimage — the fifth Pillar of Islam. Contains Eid al-Adha, one of the two major Islamic festivals. The first ten days are especially sacred." + } + ] +} diff --git a/data/kabbalah/angelicOrders.json b/data/kabbalah/angelicOrders.json new file mode 100644 index 0000000..d931fa0 --- /dev/null +++ b/data/kabbalah/angelicOrders.json @@ -0,0 +1,79 @@ +{ + "chayot-hakodesh": { + "name": { + "he": "חיות הקודש", + "en": "Holy Living Creatures", + "roman": "Chayot Hakodesh" + } + }, + "auphanim": { + "name": { + "he": "אופנים", + "en": "Wheels", + "roman": "Auphaneem" + } + }, + "aralim": { + "name": { + "he": "אראלים", + "en": "Thrones", + "roman": "Ar'aleem" + } + }, + "chashmalim": { + "name": { + "he": "חשמלים", + "en": "Shining Ones", + "roman": "Chashmaleem" + } + }, + "seraphim": { + "name": { + "he": "שרפים", + "en": "Flaming Ones", + "roman": "Seraphim" + } + }, + "malachim": { + "name": { + "he": "מלאכים", + "en": "Messengers", + "roman": "Malecheem" + } + }, + "elohim": { + "name": { + "he": "אלוהים", + "en": "Gods & Goddesses", + "roman": "Eloheem" + } + }, + "bnei-elohim": { + "name": { + "he": "בני אלוהים", + "en": "Children of the Gods", + "roman": "Bnei Eloheem" + } + }, + "kerubim": { + "name": { + "he": "כרובים", + "en": "Strong Ones", + "roman": "Kerubeem" + } + }, + "ishim": { + "name": { + "he": "אישים", + "en": "Souls of Fire", + "roman": "Isheem" + } + }, + "": { + "name": { + "he": "", + "en": "", + "roman": "" + } + } +} diff --git a/data/kabbalah/archangels.json b/data/kabbalah/archangels.json new file mode 100644 index 0000000..0c596d4 --- /dev/null +++ b/data/kabbalah/archangels.json @@ -0,0 +1,101 @@ +{ + "cassiel": { + "id": "cassiel", + "name": { + "he": "כסיאל", + "roman": "Kassiel" + }, + "planetId": "saturn" + }, + "sachiel": { + "id": "sachiel", + "name": { + "he": "סחיאל", + "roman": "Sachiel" + }, + "planetId": "jupiter" + }, + "zamael": { + "id": "zamael", + "name": { + "he": "זמאל", + "roman": "Zamael" + }, + "planetId": "mars" + }, + "michael": { + "id": "michael", + "name": { + "he": "מיכאל", + "roman": "Michael" + }, + "planetId": "sol", + "sephirahId": "hod" + }, + "anael": { + "id": "anael", + "name": { + "he": "אנאל", + "roman": "Anael" + }, + "planetId": "venus" + }, + "raphael": { + "id": "raphael", + "name": { + "he": "רפאל", + "roman": "Raphael" + }, + "planetId": "mercury", + "sephirahId": "raphael" + }, + "gabriel": { + "id": "gabriel", + "name": { + "he": "גבריאל", + "roman": "Gavriel", + "en": "Gabriel" + }, + "planetId": "luna", + "sephirahId": "yesod" + }, + "uriel": { + "id": "uriel", + "name": { + "he": "אוריאל", + "roman": "Uriel" + } + }, + "tzaphkiel": { + "id": "tzaphkiel", + "name": { + "he": "צַפְקִיאֵל", + "roman": "Tzaphkiel" + }, + "sephirahId": "binah" + }, + "khamael": { + "id": "khamael", + "name": { + "he": "חמאל", + "roman": "Khamael" + }, + "sephirahId": "gevurah" + }, + "tzadkiel": { + "id": "tzadkiel", + "name": { + "he": "צדקיאל", + "roman": "Tzadkiel" + }, + "sephirahId": "chesed" + }, + "haniel": { + "id": "haniel", + "name": { + "he": "הניאל", + "roman": "Haniel" + } + }, + "sephirahId": "netzach" +} diff --git a/data/kabbalah/cube.json b/data/kabbalah/cube.json new file mode 100644 index 0000000..8552acb --- /dev/null +++ b/data/kabbalah/cube.json @@ -0,0 +1,230 @@ +{ + "meta": { + "description": "Cube of Space wall and edge correspondences adapted from the cube Python module.", + "sources": [ + "cube/attributes.py", + "Hermetic Qabalah" + ], + "notes": "The twelve simple letters map to the 12 edges of the Cube of Space. Walls retain elemental and planetary correspondences." + }, + "center": { + "name": "Primal Point", + "letter": "Tav", + "hebrewLetterId": "tav", + "element": "Spirit", + "keywords": [ + "Unity", + "Source", + "All" + ], + "description": "Primal point at the center of the Cube of Space - synthesis of all forces", + "associations": { + "hebrewLetterId": "tav", + "kabbalahPathNumber": 32, + "tarotCard": "The World", + "tarotTrumpNumber": 21 + } + }, + "edges": [ + { + "id": "north-east", + "name": "North East", + "walls": ["north", "east"], + "hebrewLetterId": "he" + }, + { + "id": "south-east", + "name": "South East", + "walls": ["south", "east"], + "hebrewLetterId": "vav" + }, + { + "id": "east-above", + "name": "East Above", + "walls": ["east", "above"], + "hebrewLetterId": "zayin" + }, + { + "id": "east-below", + "name": "East Below", + "walls": ["east", "below"], + "hebrewLetterId": "het" + }, + { + "id": "north-above", + "name": "North Above", + "walls": ["north", "above"], + "hebrewLetterId": "tet" + }, + { + "id": "north-below", + "name": "North Below", + "walls": ["north", "below"], + "hebrewLetterId": "yod" + }, + { + "id": "north-west", + "name": "North West", + "walls": ["north", "west"], + "hebrewLetterId": "lamed" + }, + { + "id": "south-west", + "name": "South West", + "walls": ["south", "west"], + "hebrewLetterId": "nun" + }, + { + "id": "west-above", + "name": "West Above", + "walls": ["west", "above"], + "hebrewLetterId": "samekh" + }, + { + "id": "west-below", + "name": "West Below", + "walls": ["west", "below"], + "hebrewLetterId": "ayin" + }, + { + "id": "south-above", + "name": "South Above", + "walls": ["south", "above"], + "hebrewLetterId": "tsadi" + }, + { + "id": "south-below", + "name": "South Below", + "walls": ["south", "below"], + "hebrewLetterId": "qof" + } + ], + "walls": [ + { + "id": "north", + "name": "North", + "hebrewLetterId": "pe", + "side": "north", + "opposite": "South", + "oppositeWallId": "south", + "element": "Air", + "planet": "Mars", + "archangel": "Samael", + "keywords": ["Thought", "Communication", "Intellect"], + "description": "Northern Wall - Air element, Mars correspondence", + "associations": { + "planetId": "mars", + "godName": "Samael", + "hebrewLetterId": "pe", + "kabbalahPathNumber": 27, + "tarotCard": "The Tower", + "tarotTrumpNumber": 16 + } + }, + { + "id": "south", + "name": "South", + "hebrewLetterId": "resh", + "side": "south", + "opposite": "North", + "oppositeWallId": "north", + "element": "Fire", + "planet": "Sun", + "archangel": "Michael", + "keywords": ["Will", "Action", "Passion"], + "description": "Southern Wall - Fire element, Sun correspondence", + "associations": { + "planetId": "sol", + "godName": "Michael", + "hebrewLetterId": "resh", + "kabbalahPathNumber": 30, + "tarotCard": "The Sun", + "tarotTrumpNumber": 19 + } + }, + { + "id": "east", + "name": "East", + "hebrewLetterId": "dalet", + "side": "east", + "opposite": "West", + "oppositeWallId": "west", + "element": "Air", + "planet": "Venus", + "archangel": "Haniel", + "keywords": ["Dawn", "Beginning", "Ascent"], + "description": "Eastern Wall - Air element, new beginnings", + "associations": { + "planetId": "venus", + "godName": "Haniel", + "hebrewLetterId": "dalet", + "kabbalahPathNumber": 14, + "tarotCard": "The Empress", + "tarotTrumpNumber": 3 + } + }, + { + "id": "west", + "name": "West", + "hebrewLetterId": "kaf", + "side": "west", + "opposite": "East", + "oppositeWallId": "east", + "element": "Water", + "planet": "Jupiter", + "archangel": "Tzadkiel", + "keywords": ["Emotion", "Decline", "Closure"], + "description": "Western Wall - Water element, Jupiter correspondence", + "associations": { + "planetId": "jupiter", + "godName": "Tzadkiel", + "hebrewLetterId": "kaf", + "kabbalahPathNumber": 21, + "tarotCard": "Wheel of Fortune", + "tarotTrumpNumber": 10 + } + }, + { + "id": "above", + "name": "Above", + "hebrewLetterId": "bet", + "side": "above", + "opposite": "Below", + "oppositeWallId": "below", + "element": "Fire", + "planet": "Mercury", + "archangel": "Raphael", + "keywords": ["Heaven", "Spirit", "Light"], + "description": "Upper Wall - Fire element, Mercury correspondence", + "associations": { + "planetId": "mercury", + "godName": "Raphael", + "hebrewLetterId": "bet", + "kabbalahPathNumber": 12, + "tarotCard": "The Magician", + "tarotTrumpNumber": 1 + } + }, + { + "id": "below", + "name": "Below", + "hebrewLetterId": "gimel", + "side": "below", + "opposite": "Above", + "oppositeWallId": "above", + "element": "Earth", + "planet": "Luna", + "archangel": "Gabriel", + "keywords": ["Matter", "Foundation", "Manifestation"], + "description": "Lower Wall - Earth element, Luna correspondence", + "associations": { + "planetId": "luna", + "godName": "Gabriel", + "hebrewLetterId": "gimel", + "kabbalahPathNumber": 13, + "tarotCard": "The High Priestess", + "tarotTrumpNumber": 2 + } + } + ] +} diff --git a/data/kabbalah/fourWorlds.json b/data/kabbalah/fourWorlds.json new file mode 100644 index 0000000..07827b7 --- /dev/null +++ b/data/kabbalah/fourWorlds.json @@ -0,0 +1,112 @@ +{ + "atzilut": { + "id": "atzilut", + "name": { + "en": "Nobility", + "roman": "Atziluth", + "he": "אצילות" + }, + "desc": { + "en": "Archetypal" + }, + "residentsTitle": { + "en": "Pure Deity" + }, + "tetragrammaton": { + "slot": "Yod", + "letterChar": "י", + "hebrewLetterId": "yod", + "position": 1 + }, + "worldLayer": { + "en": "Archetypal World (God's Will)" + }, + "worldDescription": { + "en": "World of gods or specific facets or divine qualities." + }, + "soulId": "chaya" + }, + "briah": { + "id": "briah", + "name": { + "en": "Creation", + "roman": "Briah", + "he": "בריאה" + }, + "desc": { + "en": "Creative" + }, + "residentsTitle": { + "en": "Archangelic" + }, + "tetragrammaton": { + "slot": "Heh", + "letterChar": "ה", + "hebrewLetterId": "he", + "position": 2, + "isFinal": false + }, + "worldLayer": { + "en": "Creative World (God's Love)" + }, + "worldDescription": { + "en": "World of archangels, executors of divine qualities." + }, + "soulId": "neshama" + }, + "yetzirah": { + "id": "yetzirah", + "name": { + "en": "Formation", + "roman": "Yetzirah", + "he": "יצירה" + }, + "desc": { + "en": "Formative" + }, + "residentsTitle": { + "en": "Angelic" + }, + "tetragrammaton": { + "slot": "Vav", + "letterChar": "ו", + "hebrewLetterId": "vav", + "position": 3 + }, + "worldLayer": { + "en": "Formative World (God's Mind)" + }, + "worldDescription": { + "en": "World of angels, who work under the direction of archangels." + }, + "soulId": "ruach" + }, + "assiah": { + "id": "assiah", + "name": { + "en": "Doing", + "roman": "Assiah", + "he": "עשייה" + }, + "desc": { + "en": "Action" + }, + "residentsTitle": { + "en": "Man, Matter, Shells, Demons" + }, + "tetragrammaton": { + "slot": "Heh", + "letterChar": "ה", + "hebrewLetterId": "he", + "position": 4, + "isFinal": true + }, + "worldLayer": { + "en": "Material World (God's Creation)" + }, + "worldDescription": { + "en": "World of spirits, who focus the specialized duties of their ruling angel to infuse matter and energy." + }, + "soulId": "nephesh" + } +} diff --git a/data/kabbalah/godNames.json b/data/kabbalah/godNames.json new file mode 100644 index 0000000..685773b --- /dev/null +++ b/data/kabbalah/godNames.json @@ -0,0 +1,72 @@ +{ + "ehiyeh": { + "name": { + "he": "אהיה", + "roman": "Ehiyeh", + "en": "I am" + } + }, + "yah": { + "name": { + "he": "יה", + "roman": "Yah", + "en": "Lord" + } + }, + "yhvh-elohim": { + "name": { + "he": "יהוה אלוהים", + "roman": "YHVH Elohim", + "en": "The Lord God" + } + }, + "el": { + "name": { + "he": "אל", + "roman": "El", + "en": "God" + } + }, + "elohim-gibor": { + "name": { + "he": "אלוהים גיבור", + "roman": "Elohim Gibor", + "en": "God of Power" + } + }, + "yhvh-eloha-vedaat": { + "name": { + "he": "יהוה אלוה ודעת", + "roman": "YHVH Eloah Ve-da'at", + "en": "Lord God of Knowledge" + } + }, + "yhvh-tzvaot": { + "name": { + "he": "יהוה צבעות", + "roman": "YHVH Tzva-oht", + "en": "Lord of Armies" + } + }, + "elohim-tzvaot": { + "name": { + "he": "אלוהים צבעות", + "roman": "Elohim Tzvaot", + "en": "God of Armies" + } + }, + "shadai-el-chai": { + "name": { + "he": "שדאי אל חי", + "roman": "Shaddai El Chai", + "en": "Almighty Living God" + } + }, + "adonai-haaretz": { + "name": { + "he": "אדוני הארץ", + "roman": "Adonai Ha'aretz", + "en": "Lord of Earth" + } + } +} diff --git a/data/kabbalah/kabbalah-tree.json b/data/kabbalah/kabbalah-tree.json new file mode 100644 index 0000000..918e056 --- /dev/null +++ b/data/kabbalah/kabbalah-tree.json @@ -0,0 +1,500 @@ +{ + "meta": { + "description": "Kabbalah Tree of Life — unified 32-path dataset. Paths 1–10 are the Sephiroth; paths 11–32 are the 22 Hebrew letter paths. Foreign keys reference sephirot.json (sephiraId) and hebrewLetters.json (letterTransliteration). Tarot and astrological correspondences follow the Hermetic / Golden Dawn tradition (Liber 777).", + "sources": [ + "Sefer Yetzirah", + "Liber 777 — Aleister Crowley", + "open_777 paths dataset — adamblvck (github.com/adamblvck/open_777)" + ], + "tradition": "Hermetic Qabalah / Golden Dawn", + "notes": "The 22 letter paths use the Hermetic GD attribution (Tzaddi = Aquarius / The Star, He = Aries / The Emperor)." + }, + "sephiroth": [ + { + "number": 1, + "sephiraId": "keter", + "name": "Kether", + "nameHebrew": "כתר", + "translation": "Crown", + "planet": "Primum Mobile", + "intelligence": "The Admirable or Hidden Intelligence", + "tarot": "The 4 Aces", + "description": "The first path is called the Great or Hidden Intelligence (Supreme Crown). It is the light that expresses the first principle without a source, and is the primordial glory, that nothing created can attain its essence." + }, + { + "number": 2, + "sephiraId": "chochmah", + "name": "Chokmah", + "nameHebrew": "חכמה", + "translation": "Wisdom", + "planet": "Zodiac", + "intelligence": "The Illuminating Intelligence", + "tarot": "The 4 Twos — Kings / Knights", + "description": "The second path is the path of the enlightened intellect, the crown of creation, the light of its equal unity, exalted above all like the head, which the Kabbalists call the Second Glory." + }, + { + "number": 3, + "sephiraId": "binah", + "name": "Binah", + "nameHebrew": "בינה", + "translation": "Understanding", + "planet": "Saturn", + "intelligence": "The Sanctifying Intelligence", + "tarot": "The 4 Threes — Queens", + "description": "The third way is the sanctifying intelligence, the foundation of primordial wisdom, which is called the giver of faith and its root. It is the parent of faith, from which faith comes." + }, + { + "number": 4, + "sephiraId": "hesed", + "name": "Chesed", + "nameHebrew": "חסד", + "translation": "Mercy", + "planet": "Jupiter", + "intelligence": "The Measuring, Cohesive, or Receptacular Intelligence", + "tarot": "The 4 Fours", + "description": "The fourth path is called Measure, or Cohesion, or Container, so called because it contains all the divine powers, and from it all the spiritual virtues of the highest beings emerge. The virtues are radiated from each other by the primordial radiant force." + }, + { + "number": 5, + "sephiraId": "gevurah", + "name": "Geburah", + "nameHebrew": "גבורה", + "translation": "Strength", + "planet": "Mars", + "intelligence": "The Radical Intelligence", + "tarot": "The 4 Fives", + "description": "The Fifth Path is called the Radical Intelligence, which itself is the Equal Essence of the Oneness, and considers itself Wisdom, or Binah, or Intelligence, radiating from the primordial depths of Hochmah." + }, + { + "number": 6, + "sephiraId": "tiferet", + "name": "Tiphareth", + "nameHebrew": "תפארת", + "translation": "Beauty", + "planet": "Sol", + "intelligence": "The Intelligence of the Mediating Influence", + "tarot": "The 4 Sixes — Princes", + "description": "The Sixth Path is called the Intelligence of Mediating Influence, for within it the influx of Emitters increases. For it causes an influx that flows into all the reservoirs of blessing, which become one with them." + }, + { + "number": 7, + "sephiraId": "netzach", + "name": "Netzach", + "nameHebrew": "נצח", + "translation": "Victory", + "planet": "Venus", + "intelligence": "The Occult or Hidden Intelligence", + "tarot": "The 4 Sevens", + "description": "The Seventh Path is the Hidden Intelligence, which is the radiance of all the intellectual virtues recognized by the eyes of the intellect and the contemplation of faith." + }, + { + "number": 8, + "sephiraId": "hod", + "name": "Hod", + "nameHebrew": "הוד", + "translation": "Splendour", + "planet": "Mercury", + "intelligence": "The Absolute or Perfect Intelligence", + "tarot": "The 4 Eights", + "description": "The Eighth Path is called the Absolute Path, or the Perfect Path, because it is the means of the First. The first has no root to cling to or depend on but the Gedulah from its own nature, the hidden places of its majesty." + }, + { + "number": 9, + "sephiraId": "yesod", + "name": "Yesod", + "nameHebrew": "יסוד", + "translation": "Foundation", + "planet": "Luna", + "intelligence": "The Pure or Clear Intelligence", + "tarot": "The 4 Nines", + "description": "The Ninth Path is pure intelligence, so called because it purifies the counting laws, substantiates and corrects the conception of their explanations, and apportions their unity. They are united to that unity without diminution or division." + }, + { + "number": 10, + "sephiraId": "malchut", + "name": "Malkuth", + "nameHebrew": "מלכות", + "translation": "Kingdom", + "planet": "Earth / Sphere of Elements", + "intelligence": "The Resplendent Intelligence", + "tarot": "The 4 Tens — Princesses", + "description": "The tenth path is the brilliant intelligence, for it is exalted above all heads and sits on the throne of Binah (the intelligence spoken of in the third path). It illuminates the radiance of all light, and causes the supply of influence to radiate from the King of Faces." + } + ], + "paths": [ + { + "pathNumber": 11, + "connectKey": "1_2", + "connects": { "from": 1, "to": 2 }, + "connectIds": { "from": "keter", "to": "chochmah" }, + "pillar": "horizontal-supernal", + "hebrewLetter": { + "char": "א", + "transliteration": "Aleph", + "letterType": "mother", + "meaning": "Ox" + }, + "astrology": { "type": "element", "name": "Air" }, + "tarot": { "card": "The Fool", "trumpNumber": 0, "tarotId": "0" }, + "intelligence": "Scintillating Intelligence", + "description": "The Eleventh Path is the sparkling intelligence, for it is the nature of the veil placed close to the order of arrangement, and it is the special dignity given to it that it may stand before the face of the cause of causes.\n\nThis path is the essence of the 'veil' (ordained by this system). Path number 11 represents the relationship between paths, and it is here that we stand in the face of the 'Cause of Causes'." + }, + { + "pathNumber": 12, + "connectKey": "1_3", + "connects": { "from": 1, "to": 3 }, + "connectIds": { "from": "keter", "to": "binah" }, + "pillar": "left-supernal", + "hebrewLetter": { + "char": "ב", + "transliteration": "Beth", + "letterType": "double", + "meaning": "House" + }, + "astrology": { "type": "planet", "name": "Mercury" }, + "tarot": { "card": "The Magician", "trumpNumber": 1, "tarotId": "1" }, + "intelligence": "Intelligence of Transparency", + "description": "The Twelfth Path is the intellect of invisibility, for it is a kind of magnificence called Chazchazit. Khazkhajit is called the place from which visions of those who see in visions (i.e. prophecies received by prophets in visions) come from.\n\nThis path is the essence of the 'Ophan-wheel' of 'Greatness'. This is called 'the one who realizes in front of his eyes (Visualizer)', and 'Those who can see (Seers)' see what is realized here as 'vision'." + }, + { + "pathNumber": 13, + "connectKey": "1_6", + "connects": { "from": 1, "to": 6 }, + "connectIds": { "from": "keter", "to": "tiferet" }, + "pillar": "middle", + "hebrewLetter": { + "char": "ג", + "transliteration": "Gimel", + "letterType": "double", + "meaning": "Camel" + }, + "astrology": { "type": "planet", "name": "Luna" }, + "tarot": { "card": "The High Priestess", "trumpNumber": 2, "tarotId": "2" }, + "intelligence": "Uniting Intelligence", + "description": "The thirteenth path is the unifying intelligence, so called because it is itself the essence of glory. It is the perfection of the truth of individual spiritual beings.\n\nThis path is the essence of 'Glory'. It represents the achievement of true essence by united spirit beings.\n\nThe Thirteenth Path, as Gimel, has been called 'the dark night of the soul'. This thirteenth path crosses the abyss leading directly to Kether. The letter Gimel means 'camel' — a medium that crosses the seemingly infinite abyss desert alone. Gimel is the ultimate source of water and expresses the primordial essence of consciousness.\n\nTogether with Temperance (Samekh) and the Universe (Tav), these paths constitute the middle pillar in the tree of life. Walking the thirteenth path unites the human incarnation with the eternal self, and when safely crossing the abyss, the greatest initiation is bestowed. This path connects Tiphareth (the divine incarnation) with Kether, the highest source." + }, + { + "pathNumber": 14, + "connectKey": "2_3", + "connects": { "from": 2, "to": 3 }, + "connectIds": { "from": "chochmah", "to": "binah" }, + "pillar": "horizontal-supernal", + "hebrewLetter": { + "char": "ד", + "transliteration": "Daleth", + "letterType": "double", + "meaning": "Door" + }, + "astrology": { "type": "planet", "name": "Venus" }, + "tarot": { "card": "The Empress", "trumpNumber": 3, "tarotId": "3" }, + "intelligence": "Illuminating Intelligence", + "description": "The fourteenth path is the enlightening intelligence, so called because it is itself that Chashmal. Kashmal is the founder of the hidden fundamental concept of holiness and its preparatory stages.\n\nThis path is the essence of 'Speaking of Silence'. On this path one obtains esoteric teachings about the sacred secrets and their structures." + }, + { + "pathNumber": 15, + "connectKey": "2_6", + "connects": { "from": 2, "to": 6 }, + "connectIds": { "from": "chochmah", "to": "tiferet" }, + "pillar": "right", + "hebrewLetter": { + "char": "ה", + "transliteration": "Heh", + "letterType": "simple", + "meaning": "Window" + }, + "astrology": { "type": "zodiac", "name": "Aries" }, + "tarot": { "card": "The Emperor", "trumpNumber": 4, "tarotId": "4" }, + "intelligence": "Constituting Intelligence", + "description": "The fifteenth path is the constructive intelligence, so called because it constitutes the reality of creation in pure darkness. People have spoken of these meditations as 'the darkness that the Bible speaks of as the thick darkness that is the belt around it' (Job 38:9).\n\nThis path is a state of consolidation of the essence of creation in the 'Glooms of Purity'." + }, + { + "pathNumber": 16, + "connectKey": "2_4", + "connects": { "from": 2, "to": 4 }, + "connectIds": { "from": "chochmah", "to": "hesed" }, + "pillar": "right", + "hebrewLetter": { + "char": "ו", + "transliteration": "Vav", + "letterType": "simple", + "meaning": "Nail" + }, + "astrology": { "type": "zodiac", "name": "Taurus" }, + "tarot": { "card": "The Hierophant", "trumpNumber": 5, "tarotId": "5" }, + "intelligence": "Eternal Intelligence", + "description": "The sixteenth path is the victorious or eternal intelligence, so called because it is the joy of glory. Beyond it there is no other glory like it. It is also called the paradise prepared for the righteous.\n\nThis path is 'the Delight of the Glory'. Therefore, it is the lowest state of 'glory'. This is called the Garden of Eden, and it is a place prepared (as a reward) for the saint." + }, + { + "pathNumber": 17, + "connectKey": "3_6", + "connects": { "from": 3, "to": 6 }, + "connectIds": { "from": "binah", "to": "tiferet" }, + "pillar": "left", + "hebrewLetter": { + "char": "ז", + "transliteration": "Zayin", + "letterType": "simple", + "meaning": "Sword" + }, + "astrology": { "type": "zodiac", "name": "Gemini" }, + "tarot": { "card": "The Lovers", "trumpNumber": 6, "tarotId": "6" }, + "intelligence": "Disposing Intelligence", + "description": "The seventeenth path is the disposing intelligence, which gives faith to the righteous, by which they are clothed with the Holy Spirit, and which is called the fountain of virtue in the state of higher beings.\n\nThis path is reserved for faithful saints, and saints are sanctified here. It belongs to the supernal Entities, and it is called 'the foundation of beauty'." + }, + { + "pathNumber": 18, + "connectKey": "3_5", + "connects": { "from": 3, "to": 5 }, + "connectIds": { "from": "binah", "to": "gevurah" }, + "pillar": "left", + "hebrewLetter": { + "char": "ח", + "transliteration": "Cheth", + "letterType": "simple", + "meaning": "Fence" + }, + "astrology": { "type": "zodiac", "name": "Cancer" }, + "tarot": { "card": "The Chariot", "trumpNumber": 7, "tarotId": "7" }, + "intelligence": "House of Influence", + "description": "The Eighteenth Path is called the House of Influence (the flow of good things to created beings is increased by the greatness of its abundance), and from the midst of the search are drawn secrets and hidden meanings, which are the cause of all causes. It comes from the cause, dwells in its shadow, and clings to it.\n\nIn exploring this path, those who dwell in the shadow of the 'cause of causes' are handed over hidden mysteries and hints, and vow to thoroughly dig out the material that has flowed in from 'the cause of causes'." + }, + { + "pathNumber": 19, + "connectKey": "4_5", + "connects": { "from": 4, "to": 5 }, + "connectIds": { "from": "hesed", "to": "gevurah" }, + "pillar": "horizontal-middle", + "hebrewLetter": { + "char": "ט", + "transliteration": "Teth", + "letterType": "simple", + "meaning": "Serpent" + }, + "astrology": { "type": "zodiac", "name": "Leo" }, + "tarot": { "card": "Strength", "trumpNumber": 8, "tarotId": "8" }, + "intelligence": "Intelligence of the Secret of Spiritual Activities", + "description": "The Nineteenth Path is the intelligence of all the deeds of spiritual beings, so called because of the abundance spread by it from the highest blessings and the most exalted and sublime glory.\n\nThis path points to an influx from the 'Supreme Blessing' and the 'Glory of the Most High'." + }, + { + "pathNumber": 20, + "connectKey": "4_6", + "connects": { "from": 4, "to": 6 }, + "connectIds": { "from": "hesed", "to": "tiferet" }, + "pillar": "right", + "hebrewLetter": { + "char": "י", + "transliteration": "Yod", + "letterType": "simple", + "meaning": "Hand" + }, + "astrology": { "type": "zodiac", "name": "Virgo" }, + "tarot": { "card": "The Hermit", "trumpNumber": 9, "tarotId": "9" }, + "intelligence": "Intelligence of the Will", + "description": "The Twentieth Path is the intelligence of the will, so called because it is the means of preparing all and each of created beings. By this intellect the existence of primordial wisdom becomes known.\n\nThis path is the structure of all that is formed. Through this state of consciousness, we can know the nature of 'Original Wisdom'." + }, + { + "pathNumber": 21, + "connectKey": "4_7", + "connects": { "from": 4, "to": 7 }, + "connectIds": { "from": "hesed", "to": "netzach" }, + "pillar": "right", + "hebrewLetter": { + "char": "כ", + "transliteration": "Kaph", + "letterType": "double", + "meaning": "Palm" + }, + "astrology": { "type": "planet", "name": "Jupiter" }, + "tarot": { "card": "Wheel of Fortune", "trumpNumber": 10, "tarotId": "10" }, + "intelligence": "Intelligence of Conciliation", + "description": "The Twenty-first Path is the Intelligence of Reconciliation, so called because it receives the influence of God flowing into it from the blessings bestowed on all beings and each being.\n\nThis path is the state of receiving the influx of divinity, through which all beings receive the blessings of the gods.\n\nThe 21st path connects Netzach and Chesed, and directly reflects the power of Jupiter — benevolent king. The alphabetic attribute is the letter Kaph, which in its general form represents a cupped hand for receptivity. The letter Kaph means 'palm' as well as 'closed and clenched hands' and has the numeric value 100. Kaph is layered with mystical meanings closely linked to prophecy and vision.\n\nFundamental to the mysticism of this key is the cyclical nature of life — the regularity of the seasons, the repetitive actions of mankind and astronomical movements in the sky. Kabbalah teaches that the secret to mastering the environment is to be found through enlightenment, and that the center of the wheel is the only resting place." + }, + { + "pathNumber": 22, + "connectKey": "5_6", + "connects": { "from": 5, "to": 6 }, + "connectIds": { "from": "gevurah", "to": "tiferet" }, + "pillar": "left", + "hebrewLetter": { + "char": "ל", + "transliteration": "Lamed", + "letterType": "simple", + "meaning": "Ox Goad" + }, + "astrology": { "type": "zodiac", "name": "Libra" }, + "tarot": { "card": "Justice", "trumpNumber": 11, "tarotId": "11" }, + "intelligence": "Faithful Intelligence", + "description": "The Twenty-Second Path is the Faithful Intelligence, so called because by it spiritual virtues are increased and almost all the inhabitants of the earth are under its shadow.\n\nA state of enhanced spiritual power, through which all beings 'dwelling in the shadows' can be approached." + }, + { + "pathNumber": 23, + "connectKey": "5_8", + "connects": { "from": 5, "to": 8 }, + "connectIds": { "from": "gevurah", "to": "hod" }, + "pillar": "horizontal-middle", + "hebrewLetter": { + "char": "מ", + "transliteration": "Mem", + "letterType": "mother", + "meaning": "Water" + }, + "astrology": { "type": "element", "name": "Water" }, + "tarot": { "card": "The Hanged Man", "trumpNumber": 12, "tarotId": "12" }, + "intelligence": "Stable Intelligence", + "description": "The Twenty-Third Path is the Stable Intelligence, so called because it possesses the virtue of unity in all ranks.\n\nThis path is the power that sustains all Sephiroth. The 23rd Path runs between Hod and Geburah and is governed by the water element. The Hebrew letter Mem means 'waters' or sea. Water is considered the first principle in alchemy, the most stable and fundamental of the elements.\n\nThe Hanged Man depicts this stable intelligence because he cannot separate it from the Unity that water expresses. The most implicit symbolic system is the symbolic system of reversal or suspension of general consciousness, symbolizing immersion in superconsciousness and the absolute surrender of the individual will. The mythological concept of sacrifice — the dying god — is cosmic and cross-cultural: Odin hung on the world tree, Osiris in Egypt, Dionysus in Greece. The main image of a dying god is that of complete liberation, restoring the Creator back to the throne." + }, + { + "pathNumber": 24, + "connectKey": "6_7", + "connects": { "from": 6, "to": 7 }, + "connectIds": { "from": "tiferet", "to": "netzach" }, + "pillar": "right", + "hebrewLetter": { + "char": "נ", + "transliteration": "Nun", + "letterType": "simple", + "meaning": "Fish" + }, + "astrology": { "type": "zodiac", "name": "Scorpio" }, + "tarot": { "card": "Death", "trumpNumber": 13, "tarotId": "13" }, + "intelligence": "Imaginative Intelligence", + "description": "The twenty-fourth path is the intellect of the imagination, so called because it gives its harmonious elegance and likeness to all likenesses created in like ways.\n\nIn this path, all the illusions created appear in a form appropriate to their status." + }, + { + "pathNumber": 25, + "connectKey": "6_9", + "connects": { "from": 6, "to": 9 }, + "connectIds": { "from": "tiferet", "to": "yesod" }, + "pillar": "middle", + "hebrewLetter": { + "char": "ס", + "transliteration": "Samekh", + "letterType": "simple", + "meaning": "Prop" + }, + "astrology": { "type": "zodiac", "name": "Sagittarius" }, + "tarot": { "card": "Temperance", "trumpNumber": 14, "tarotId": "14" }, + "intelligence": "Intelligence of Temptation and Trial", + "description": "The twenty-fifth path is the intelligence of testing or trial, so called because it is the first temptation by which the Creator tests all righteous men.\n\nGod causes the called ones to undergo a fundamental test here. Together with the High Priestess (Gimel) and the Universe (Tav), Samekh (Temperance) constitutes the middle pillar in the tree of life. Both Samekh and Gimel have been called 'the dark night of the soul', because both cross an abyss — Samekh crosses the lesser abyss on its journey to Tiphareth." + }, + { + "pathNumber": 26, + "connectKey": "6_8", + "connects": { "from": 6, "to": 8 }, + "connectIds": { "from": "tiferet", "to": "hod" }, + "pillar": "left", + "hebrewLetter": { + "char": "ע", + "transliteration": "Ayin", + "letterType": "simple", + "meaning": "Eye" + }, + "astrology": { "type": "zodiac", "name": "Capricorn" }, + "tarot": { "card": "The Devil", "trumpNumber": 15, "tarotId": "15" }, + "intelligence": "Renovating Intelligence", + "description": "The twenty-sixth path is called the Renovating Intelligence, because the Holy God by it renews all the changing things that are renewed by the creation of the world.\n\nThe 26th Path is called the 'Resurrecting Intelligence'. Ayin is said to harbor many mysteries; Gematria equates Ayin to 70 and also the word for 'secret' (SVD). The Ayin, or eye, is the organ of vision — it sees the exterior and appearance of things. In Qabalah, the world of appearances is an illusion arising from our own perception. At Ayin, we learn to use the intelligent eyes of Hod to see anew in the light of beauty illuminated by Tiphareth. Ascension on this path is the process of transferring consciousness from the concrete mind (ego) to the abstract mind (Higher Self)." + }, + { + "pathNumber": 27, + "connectKey": "7_8", + "connects": { "from": 7, "to": 8 }, + "connectIds": { "from": "netzach", "to": "hod" }, + "pillar": "horizontal-lower", + "hebrewLetter": { + "char": "פ", + "transliteration": "Pe", + "letterType": "double", + "meaning": "Mouth" + }, + "astrology": { "type": "planet", "name": "Mars" }, + "tarot": { "card": "The Tower", "trumpNumber": 16, "tarotId": "16" }, + "intelligence": "Exciting Intelligence", + "description": "The Twenty-seventh Path is the Exciting Intelligence, so called because by it the intelligence of all created beings and their stimuli or motions under the highest heavens was created.\n\nAll creatures created in the higher dimensions — including their sentience — were created through this path." + }, + { + "pathNumber": 28, + "connectKey": "7_9", + "connects": { "from": 7, "to": 9 }, + "connectIds": { "from": "netzach", "to": "yesod" }, + "pillar": "right", + "hebrewLetter": { + "char": "צ", + "transliteration": "Tzaddi", + "letterType": "simple", + "meaning": "Fish-hook" + }, + "astrology": { "type": "zodiac", "name": "Aquarius" }, + "tarot": { "card": "The Star", "trumpNumber": 17, "tarotId": "17" }, + "intelligence": "Natural Intelligence", + "description": "The twenty-eighth path is the natural intelligence, so called because the characteristics of all beings under the solar sphere are perfected and established through it.\n\nThe nature of everything that exists under the sun (belonging to the realm of the sun) is established through this consciousness." + }, + { + "pathNumber": 29, + "connectKey": "7_10", + "connects": { "from": 7, "to": 10 }, + "connectIds": { "from": "netzach", "to": "malchut" }, + "pillar": "right", + "hebrewLetter": { + "char": "ק", + "transliteration": "Qoph", + "letterType": "simple", + "meaning": "Back of Head" + }, + "astrology": { "type": "zodiac", "name": "Pisces" }, + "tarot": { "card": "The Moon", "trumpNumber": 18, "tarotId": "18" }, + "intelligence": "Corporeal Intelligence", + "description": "The twenty-ninth path is the bodily intelligence, so called because it gives form to all the worlds and all bodies formed under their multiplication.\n\nThis path describes the development of the materialized according to the systems of each domain.\n\nThe 29th route is another route that threatens the traveler with the risk of disintegration. Since this path leads to Netzach it can cause mental disruption — what is indicated is the renunciation of individuality. The journey from Malkuth to Netzach is a confluence of physical and emotional natures. All paths leading towards Netzach are Orphic paths, similar to Orpheus who enchanted nature with the gift of song.\n\nThe letter Qoph has a value of 100 and also translates as 'awakening'. Called physical intelligence, this is truly the pathway to body consciousness. One of the most important lessons of this path is that it is not to our advantage to try to separate the 'higher state of mind' from the 'lower coarse' form of the body." + }, + { + "pathNumber": 30, + "connectKey": "8_9", + "connects": { "from": 8, "to": 9 }, + "connectIds": { "from": "hod", "to": "yesod" }, + "pillar": "horizontal-lower", + "hebrewLetter": { + "char": "ר", + "transliteration": "Resh", + "letterType": "double", + "meaning": "Head" + }, + "astrology": { "type": "planet", "name": "Sol" }, + "tarot": { "card": "The Sun", "trumpNumber": 19, "tarotId": "19" }, + "intelligence": "Collecting Intelligence", + "description": "The Thirtieth Path is the Gathering Intelligence, so called because the astrologers infer from it the judgments of the stars and celestial signs, and the perfection of their learning, according to the laws of their revolutions.\n\nThrough this path, 'the one who spreads the heavens' increases control over the stars and constellations while establishing the knowledge of the chariot wheels in each realm.\n\nThis 30th route runs between Yesod and Hod. This path leads from the realm of the moon to the realm of reason, and although it is gloomy and cold, it is the coolness of the creative imagination by the power of the intellect. However, the full power of the sun shines there. The Hebrew letter Resh means 'head', and its spelling equals 510 or 15 — the same numeric value as Hod (HVD = 15). This path is a balance between mind and body, standing at the heart of the relationship between intellect and intuition. It is often described as a point of profound encounter with the inner master." + }, + { + "pathNumber": 31, + "connectKey": "8_10", + "connects": { "from": 8, "to": 10 }, + "connectIds": { "from": "hod", "to": "malchut" }, + "pillar": "left", + "hebrewLetter": { + "char": "ש", + "transliteration": "Shin", + "letterType": "mother", + "meaning": "Tooth" + }, + "astrology": { "type": "element", "name": "Fire" }, + "tarot": { "card": "Judgement", "trumpNumber": 20, "tarotId": "20" }, + "intelligence": "Perpetual Intelligence", + "description": "The Thirty-First Path is the Enduring Intelligence, so why is it called that? For it regulates the motions of the sun and moon in their own order, each in its own proper orbit.\n\nThis path manages the path of the sun and moon so that they maintain their respective optimal orbits according to the laws of nature." + }, + { + "pathNumber": 32, + "connectKey": "9_10", + "connects": { "from": 9, "to": 10 }, + "connectIds": { "from": "yesod", "to": "malchut" }, + "pillar": "middle", + "hebrewLetter": { + "char": "ת", + "transliteration": "Tav", + "letterType": "double", + "meaning": "Cross (Tau)" + }, + "astrology": { "type": "planet", "name": "Saturn" }, + "tarot": { "card": "The Universe", "trumpNumber": 21, "tarotId": "21" }, + "intelligence": "Administrative Intelligence", + "description": "The Thirty-Second Path is the Administrative Intelligence, so called because it directs and unites the seven planets in all their operations and even all of them in their proper course.\n\nThis path is given this name because it is the path that governs the passage between the formative world and the material world. The Universe card on this path is called 'The Door to the Inner Plane'." + } + ] +} diff --git a/data/kabbalah/kerubim.json b/data/kabbalah/kerubim.json new file mode 100644 index 0000000..2743a36 --- /dev/null +++ b/data/kabbalah/kerubim.json @@ -0,0 +1,54 @@ +{ + "earth": { + "id": "earth", + "title": { + "en": "Kerub of Earth" + }, + "face": { + "en": "Bull", + "he": "שור", + "roman": "Shor" + }, + "zodiacId": "taurus", + "elementId": "earth" + }, + "air": { + "id": "air", + "title": { + "en": "Kerub of Air" + }, + "face": { + "en": "Man", + "he": "אדם", + "roman": "Adam" + }, + "zodiacId": "aquarius", + "elementId": "air" + }, + "water": { + "id": "water", + "title": { + "en": "Kerub of Water" + }, + "face": { + "en": "Eagle", + "he": "נשר", + "roman": "Nesher" + }, + "zodiacId": "scorpio", + "elementId": "water" + }, + "fire": { + "id": "fire", + "title": { + "en": "Kerub of Fire" + }, + "face": { + "en": "Lion", + "he": "אריה", + "roman": "Aryeh" + }, + "zodiacId": "leo", + "elementId": "fire" + } +} diff --git a/data/kabbalah/paths.json b/data/kabbalah/paths.json new file mode 100644 index 0000000..7443a8a --- /dev/null +++ b/data/kabbalah/paths.json @@ -0,0 +1,250 @@ +{ + "1_2": { + "id": "1_2", + "hermetic": { + "hebrewLetterId": "alef", + "pathNo": 11, + "tarotId": "0" + }, + "hebrew": { + "hebrewLetterId": "he" + } + }, + "1_3": { + "id": "1_3", + "hermetic": { + "hebrewLetterId": "bet", + "pathNo": 12, + "tarotId": "1" + }, + "hebrew": { + "hebrewLetterId": "vav" + } + }, + "1_6": { + "id": "1_6", + "hermetic": { + "hebrewLetterId": "gimel", + "pathNo": 13, + "tarotId": "2" + }, + "hebrew": { + "hebrewLetterId": "dalet" + } + }, + "2_3": { + "id": "2_3", + "hermetic": { + "hebrewLetterId": "dalet", + "pathNo": 14, + "tarotId": "3" + }, + "hebrew": { + "hebrewLetterId": "shin" + } + }, + "2_4": { + "id": "2_4", + "hermetic": { + "hebrewLetterId": "vav", + "pathNo": 16, + "tarotId": "5" + }, + "hebrew": { + "hebrewLetterId": "bet" + } + }, + "2_5": { + "id": "2_5", + "hebrew": { + "hebrewLetterId": "zayin" + } + }, + "2_6": { + "id": "2_6", + "hermetic": { + "hebrewLetterId": "he", + "pathNo": 15, + "tarotId": "4" + }, + "hebrew": { + "hebrewLetterId": "tet" + } + }, + "3_4": { + "id": "3_4", + "hebrew": { + "hebrewLetterId": "qof" + } + }, + "3_5": { + "id": "3_5", + "hermetic": { + "hebrewLetterId": "het", + "pathNo": 18, + "tarotId": "7" + }, + "hebrew": { + "hebrewLetterId": "gimel" + } + }, + "3_6": { + "id": "3_6", + "hermetic": { + "hebrewLetterId": "zayin", + "pathNo": 17, + "tarotId": "6" + }, + "hebrew": { + "hebrewLetterId": "ayin" + } + }, + "4_5": { + "id": "4_5", + "hermetic": { + "hebrewLetterId": "tet", + "pathNo": 19, + "tarotId": "8" + }, + "hebrew": { + "hebrewLetterId": "alef" + } + }, + "4_6": { + "id": "4_6", + "hermetic": { + "hebrewLetterId": "yod", + "pathNo": 20, + "tarotId": "9" + }, + "hebrew": { + "hebrewLetterId": "het" + } + }, + "4_7": { + "id": "4_7", + "hermetic": { + "hebrewLetterId": "kaf", + "pathNo": 21, + "tarotId": "10" + }, + "hebrew": { + "hebrewLetterId": "kaf" + } + }, + "5_6": { + "id": "5_6", + "hermetic": { + "hebrewLetterId": "lamed", + "pathNo": 22, + "tarotId": "11" + }, + "hebrew": { + "hebrewLetterId": "tsadi" + } + }, + "5_8": { + "id": "5_8", + "hermetic": { + "hebrewLetterId": "mem", + "pathNo": 23, + "tarotId": "12" + }, + "hebrew": { + "hebrewLetterId": "pe" + } + }, + "6_7": { + "id": "6_7", + "hermetic": { + "hebrewLetterId": "nun", + "pathNo": 24, + "tarotId": "13" + }, + "hebrew": { + "hebrewLetterId": "yod" + } + }, + "6_8": { + "id": "6_8", + "hermetic": { + "hebrewLetterId": "ayin", + "pathNo": 26, + "tarotId": "15" + }, + "hebrew": { + "hebrewLetterId": "samekh" + } + }, + "6_9": { + "id": "6_9", + "hermetic": { + "hebrewLetterId": "samekh", + "pathNo": 25, + "tarotId": "14" + }, + "hebrew": { + "hebrewLetterId": "resh" + } + }, + "7_8": { + "id": "7_8", + "hermetic": { + "hebrewLetterId": "pe", + "pathNo": 27, + "tarotId": "16" + }, + "hebrew": { + "hebrewLetterId": "mem" + } + }, + "7_9": { + "id": "7_9", + "hermetic": { + "hebrewLetterId": "tsadi", + "pathNo": 28, + "tarotId": "17" + }, + "hebrew": { + "hebrewLetterId": "nun" + } + }, + "7_10": { + "id": "7_10", + "hermetic": { + "hebrewLetterId": "qof", + "pathNo": 29, + "tarotId": "18" + } + }, + "8_9": { + "id": "8_9", + "hermetic": { + "hebrewLetterId": "resh", + "pathNo": 30, + "tarotId": "19" + }, + "hebrew": { + "hebrewLetterId": "lamed" + } + }, + "8_10": { + "id": "8_10", + "hermetic": { + "hebrewLetterId": "shin", + "pathNo": 31, + "tarotId": "20" + } + }, + "9_10": { + "id": "9_10", + "hermetic": { + "hebrewLetterId": "tav", + "pathNo": 32, + "tarotId": "21" + }, + "hebrew": { + "hebrewLetterId": "tav" + } + } +} diff --git a/data/kabbalah/sephirot.json b/data/kabbalah/sephirot.json new file mode 100644 index 0000000..02044bd --- /dev/null +++ b/data/kabbalah/sephirot.json @@ -0,0 +1,324 @@ +{ + "keter": { + "index": 1, + "id": "keter", + "name": { + "he": "כתר", + "roman": "Keter", + "en": "Crown" + }, + "color": { + "king": "brillliance", + "kingWeb": "white", + "queen": "white", + "queenWeb": "white" + }, + "chakraId": "crown", + "godNameId": "ehiyeh", + "scent": "ambergris", + "body": "cranium", + "bodyPos": "above-head", + "planetId": "primum-mobile", + "tenHeavens": { + "en": "1st Swirlings / Primum Mobile", + "he": "ראשית הגלגולים", + "roman": "Roshit haGilgulim" + }, + "stone": "diamond", + "archangelIdId": "metatron", + "soulId": "yechidah", + "angelicOrderId": "chayot-hakodesh", + "gdGradeId": "10=1", + "next": "chochmah" + }, + "chochmah": { + "index": 2, + "id": "chochmah", + "name": { + "he": "חכמה", + "en": "Wisdom", + "roman": "Chochmah" + }, + "color": { + "king": "soft-blue", + "kingWeb": "SkyBlue", + "queen": "gray", + "queenWeb": "gray" + }, + "scent": "musk", + "chakraId": "third-eye", + "godNameId": "yah", + "body": "left-face", + "bodyPos": "left-temple", + "planetId": "zodiac", + "tenHeavens": { + "en": "The Zodiac", + "roman": "Mazalot", + "he": "מזלות" + }, + "stone": "Star Ruby; Turquoise", + "archangelId": "raziel", + "soulId": "chaya", + "angelicOrderId": "auphanim", + "gdGradeId": "9=2", + "prev": "keter", + "next": "binah" + }, + "binah": { + "index": 3, + "id": "binah", + "name": { + "he": "בינה", + "en": "Understanding", + "roman": "Binah" + }, + "color": { + "king": "deep-red-violet", + "kingWeb": "#c0448f", + "queen": "black", + "queenWeb": "#222", + "queenWebText": "#ccc" + }, + "chakraId": "third-eye", + "godNameId": "yhvh-elohim", + "scent": "myrrh; civet", + "body": "right-face", + "bodyPos": "right-temple", + "planetId": "saturn", + "stone": "pearl; star sapphire", + "archangelId": "tzaphkiel", + "soulId": "neshama", + "angelicOrderId": "aralim", + "gdGradeId": "8=3", + "prev": "chochmah", + "next": "hesed" + }, + "hesed": { + "index": 4, + "id": "hesed", + "name": { + "he": "חסד", + "en": "Mercy", + "roman": "Chesed" + }, + "color": { + "king": "deep-violet", + "kingWeb": "#330066", + "kingWebText": "#ccc", + "queen": "blue", + "queenWeb": "#0066ff" + }, + "chakraId": "throat", + "godNameId": "el", + "scent": "cedar", + "body": "left-arm", + "bodyPos": "left-shoulder", + "planetId": "jupiter", + "stone": "saphire; amethyst", + "archangelId": "tzadkiel", + "soulId": null, + "angelicOrderId": "chashmalim", + "gdGradeId": "7=4", + "prev": "binah", + "next": "gevurah" + }, + "gevurah": { + "index": 5, + "id": "gevurah", + "name": { + "he": "גבורה", + "en": "Strength", + "roman": "Gevurah" + }, + "color": { + "king": "orange", + "kingWeb": "orange", + "queen": "scarlet", + "queenWeb": "#ff2400" + }, + "chakraId": "throat", + "godNameId": "elohim-gibor", + "scent": "tobacco", + "body": "right-arm", + "bodyPos": "right-shoulder", + "planetId": "mars", + "stone": "ruby", + "archangelId": "khamael", + "soulId": null, + "angelicOrderId": "seraphim", + "gdGradeId": "6=5", + "prev": "hesed", + "next": "tiferet" + }, + "tiferet": { + "index": 6, + "id": "tiferet", + "name": { + "he": "תפארת", + "en": "Beauty", + "roman": "Tiferet" + }, + "color": { + "king": "rose-pink", + "kingWeb": "#f7cac1", + "queen": "gold", + "queenWeb": "gold" + }, + "chakraId": "heart", + "godNameId": "yhvh-eloha-vedaat", + "scent": "olibanum", + "body": "breast", + "bodyPos": "heart", + "planetId": "sol", + "stone": "topaz", + "archangelId": "raphael", + "soulId": "ruach", + "angelicOrderId": "malachim", + "gdGradeId": "5=6", + "prev": "gevurah", + "next": "netzach" + }, + "netzach": { + "index": 7, + "id": "netzach", + "name": { + "he": "נצח", + "en": "Victory", + "roman": "Netzach" + }, + "color": { + "king": "yellow-orange", + "kingWeb": "#ffae42", + "queen": "emerald", + "queenWeb": "#50C878" + }, + "chakraId": "solar-plexus", + "godNameId": "yhvh-tzvaot", + "scent": "rose, red sandal", + "body": "loins; hips", + "bodyPos": "left-hip", + "planetId": "venus", + "stone": "emerald", + "archangelId": "haniel", + "soulId": null, + "angelicOrderId": "elohim", + "gdGradeId": "4=7", + "prev": "tiferet", + "next": "hod" + }, + "hod": { + "index": 8, + "id": "hod", + "name": { + "he": "הוד", + "en": "Splendor", + "roman": "Hod" + }, + "color": { + "king": "violet", + "kingWeb": "#ee82ee", + "queen": "orange", + "queenWeb": "orange" + }, + "chakraId": "solar-plexus", + "godNameId": "elohim-tzvaot", + "scent": "storax", + "body": "loins; legs", + "bodyPos": "right-hip", + "planetId": "mercury", + "stone": "quartz", + "archangelId": "michael", + "soulId": null, + "angelicOrderId": "bnei-elohim", + "gdGradeId": "3=8", + "prev": "netzach", + "next": "yesod" + }, + "yesod": { + "index": 9, + "id": "yesod", + "name": { + "he": "יסוד", + "en": "Foundation", + "roman": "Yesod" + }, + "color": { + "king": "blue-violet", + "kingWeb": "#2014d4", + "kingWebText": "#aaa", + "queen": "violet", + "queenWeb": "violet" + }, + "chakraId": "sacral", + "godNameId": "shadai-el-chai", + "scent": "jasmine", + "body": "genitals", + "bodyPos": "genitals", + "planetId": "luna", + "stone": "quartz", + "archangelId": "gabriel", + "soulId": "nephesh", + "angelicOrderId": "kerubim", + "gdGradeId": "2=9", + "prev": "hod", + "next": "malchut" + }, + "malchut": { + "index": 10, + "id": "malchut", + "name": { + "he": "מלכות", + "en": "Kingdom", + "roman": "Malchut" + }, + "color": { + "king": "yellow", + "kingWeb": "yellow", + "queen": "russet,citrine,olive,black", + "queenWeb": "#80461B,#ba0,#880,#000000", + "queenWebText": "#eee" + }, + "chakraId": "root", + "godNameId": "adonai-haaretz", + "scent": "", + "body": "feet", + "bodyPos": "feet", + "planetId": "olam-yesodot", + "tenHeavens": { + "en": "Sphere of the Elements", + "he": "עולם יסודות", + "roman": "Olam Yesodoth" + }, + "stone": "rock crystal", + "archangelId": "sandalphon", + "soulId": "guph", + "angelicOrderId": "ishim", + "gdGradeId": "1=10", + "prev": "yesod" + }, + "daat": { + "index": 11, + "id": "daat", + "name": { + "he": "דעת", + "en": "Knowledge", + "roman": "Da'at" + }, + "color": { + "queen": "lavendar", + "queenWeb": "#E6E6FAEE", + "queenWebText": "#000", + "strokeDasharray": 2, + "strokeColor": "#000000AA" + }, + "chakraId": "", + "godNameId": "yhvh-elohim", + "scent": "", + "body": "throat", + "bodyPos": "throat", + "stone": "", + "archangelId": "", + "soulId": "", + "angelicOrderId": "" + } +} diff --git a/data/kabbalah/seventyTwoAngels.json b/data/kabbalah/seventyTwoAngels.json new file mode 100644 index 0000000..bb5f7c8 --- /dev/null +++ b/data/kabbalah/seventyTwoAngels.json @@ -0,0 +1,107 @@ +[ + { + "name": { + "he": "והויה", + "en": "Vehuiah" + }, + "attribute": { + "en": "God is High and Exalted above all things." + }, + "godName": "Jehovah", + "angelicOrderId": "seraphim", + "presidesOver": [ + [ + 3, + 20 + ], + [ + 4, + 30 + ], + [ + 8, + 11 + ], + [ + 10, + 22 + ], + [ + 1, + 2 + ] + ], + "text": { + "en": "I'r, genius, Vehuiah והויה. Its attribute is interpreted (God high and “exalted above all things). He does-mine on the Hebrews. The name of God, according to this language, is named Jehovah. He governs the first ray of the East in the spring season, that is to say the five first degrees of, the sphere which begin on March 20 at midnight until the 24th, inclusive, corresponding to the first decade of the sacred calendar, and at the first genius, named Chontaré, under the influence of Mars: this genius, and those which follow, up to the 8th, belong to the first order of angels that the Orthodox call the choir of seraphim. He lives in the region of fire; his sign is the ram, and he presides over the following five days: March 20, April 31, August II, October 22 and January 2; the invocation is made towards the Orient, from precisely midnight until 20 minutes past midnight, to get lights. It is by virtue of these names divine that one becomes illuminated with the spirit of God; we must pronounce them at midnight precisely until midnight 20 mid-nut:s, reciting 16 third verse of the 3rd psalm. (And you Domine susceplor meus et gloria mea 61 exallans caput meum). You must have your talisman prepared according to the principles principles of cabalistic art. (See chapter 8 on this subject.) The person who is born under the influence of this genius has the took subtle; she is gifted with great sagacity, passion- born for the sciences and the arts, capable of undertaking and to perform the most difficult things; she will love the state military, because of the influence of Mars; she will have burst of energy being damaged by fire. The evil genius influences turbulent men; he quickness and anger dominate.\n(1) See the book entitled Tbrezcze, or the only Way of Divine and human sciences, edition of year 7, page 226." + } + }, + { + "name": { + "he": "יליאל", + "en": "Jeliel" + }, + "attribute": { + "en": "Helpful God" + }, + "godName": "Aydy", + "presidesOver": [ + [ + 3, + 21 + ], + [ + 6, + 1 + ], + [ + 8, + 12 + ], + [ + 10, + 23 + ], + [ + 1, + 3 + ] + ], + "text": { + "en": "2° Jeliel יליאל. His attribute (helpful God). 11 do- sure mine Turkey (these people give God the name Aydy). Its radius starts from 16 6* degree up to . 10%, inclusive, corresponding to the influence of genius . named Asican (see the sacred calendar), and at the first first decade. He presides over the following 165 days: March 21, June 1, August 12, October 23, January 3*. \nThis genius is invoked to appease popular seditions. laries, and to obtain victory against those who attack you attack unfairly. The request must be pronounced with the name of the genius, and recite 16 20° verse of Psalm 21. (Tu autem Domine ne eldngaveris auxillum tuum a me ad defenseem meam conspice). The favorable hour includes starts from midnight 20 minutes until midnight 40. \nThis genius dominates over kings and princes; he maintains their subjects in obedience; it influences the generation of all 165 beings that exist in 16 animal kingdoms; he restores peace among spouses and marital fidelity. Those who are born under this influence have a cheerful spirit, ma- pleasant and gallant manners; they will be passionate about it sex.\nThe contrary genius dominates everything that is harmful to animated beings; he likes to separate spouses by pushing them aside . 06 their duties; he inspires a taste for celibacy and the bad ‘good morals." + } + }, + { + "name": { + "en": "Sitael", + "he": "סיטאל" + }, + "attribute": { + "en": "God, the hope of all creatures." + }, + "presidesOver": [ + [ + 3, + 22 + ], + [ + 6, + 2 + ], + [ + 8, + 13 + ], + [ + 10, + 24 + ], + [ + 1, + 4 + ] + ], + "text": { + "en": "Sitael סיטאל. His attribute (God, the hope all creatures.) Its radius starts from 16 5 degree of .1 500616 up to 15°., inclusively corres- laying in the second decade and the genius named Chon- stained, under the influence of the sun; he presides over the following days: March 22, June 2, August 13, October 24, January 4. We invoke this genius against 165 adversities; we pronounce the request with the divine names and the 2nd verse of the psalm 90. (Dicet Domino: suceblor meus es tu et refugium meum: Deus meus, sperabo in eum.) The favorable hour starts from midnight 40 minutes until one o'clock. He dominates over nobility, magnanimity and great em- plows; it protects against weapons and ferocious beasts. There no one born under this influence loves the truth; she will keep her word, and she will be happy to oblige those who will need its services. The contrary genius dominates hypocrisy, ingratitude and perjury." + } + } +] diff --git a/data/kabbalah/souls.json b/data/kabbalah/souls.json new file mode 100644 index 0000000..a85f6f8 --- /dev/null +++ b/data/kabbalah/souls.json @@ -0,0 +1,88 @@ +{ + "yechidah": { + "id": "yechidah", + "name": { + "en": "unity", + "he": "יחידה", + "roman": "yechida" + }, + "title": { + "en": "Unity" + } + }, + "chaya": { + "id": "chaya", + "aliases": [ + "chiah" + ], + "name": { + "en": "life force", + "he": "חיה", + "roman": "chiah", + "altRoman": "chaya" + }, + "title": { + "en": "Life Force" + }, + "desc": { + "en": "The Chiah is the Life Force itself. It is our true identity, ultimately a pure reflection of the Supreme Consciousness of deity." + } + }, + "neshama": { + "id": "neshama", + "aliases": [ + "neshamah" + ], + "name": { + "en": "soul-intuition", + "he": "נשמה", + "roman": "neshamah", + "altRoman": "neshama" + }, + "title": { + "en": "Soul-Intuition" + }, + "desc": { + "en": "The Neshamah is the part of our soul that transcends our thinking processes." + } + }, + "ruach": { + "id": "ruach", + "name": { + "en": "intellect", + "he": "רוח", + "roman": "ruach" + }, + "title": { + "en": "Intellect" + }, + "desc": { + "en": "The Ruach is the part of our soul that monopolizes attention to such a degree that we identify with the thinking process." + } + }, + "nephesh": { + "id": "nephesh", + "name": { + "en": "animal soul", + "he": "נפש", + "roman": "nephesh" + }, + "title": { + "en": "Animal Soul" + }, + "desc": { + "en": "The Nephesh is primitive consciousness shared with animal, plant, and mineral kingdoms, expressed through instincts, appetite, emotion, sex drive, and survival mechanisms." + } + }, + "guph": { + "id": "guph", + "name": { + "en": "body", + "he": "גוף", + "roman": "guph" + }, + "title": { + "en": "Body" + } + } +} diff --git a/data/kabbalah/tribesOfIsrael.json b/data/kabbalah/tribesOfIsrael.json new file mode 100644 index 0000000..5dac027 --- /dev/null +++ b/data/kabbalah/tribesOfIsrael.json @@ -0,0 +1,100 @@ +{ + "reuben": { + "id": "reuben", + "name": { + "en": "Reuben", + "he": "ראובן" + } + }, + "simeon": { + "id": "simeon", + "name": { + "en": "Simeon", + "he": "שמעון" + } + }, + "levi": { + "id": "levi", + "name": { + "en": "Levi", + "he": "לוי" + } + }, + "judah": { + "id": "judah", + "name": { + "en": "Judah", + "he": "יהודה" + } + }, + "dan": { + "id": "dan", + "name": { + "en": "Dan", + "he": "דן" + } + }, + "naphtali": { + "id": "naphtali", + "name": { + "en": "Naphtali", + "he": "נפתלי" + } + }, + "gad": { + "id": "gad", + "name": { + "en": "Gad", + "he": "גד" + } + }, + "asher": { + "id": "asher", + "name": { + "en": "Asher", + "he": "אשר" + } + }, + "issachar": { + "id": "issachar", + "name": { + "en": "Issachar", + "he": "יששכר" + } + }, + "zebulun": { + "id": "zebulun", + "name": { + "en": "Zebulun", + "he": "זבולון" + } + }, + "joseph": { + "id": "joseph", + "name": { + "en": "Joseph", + "he": "יוסף" + } + }, + "benjamin": { + "id": "benjamin", + "name": { + "en": "Benjamin", + "he": "בנימין" + } + }, + "ephraim": { + "id": "ephraim", + "name": { + "en": "Ephraim", + "he": "אפרים" + } + }, + "manasseh": { + "id": "manasseh", + "name": { + "en": "Menasseh", + "he": "בנימין" + } + } +} diff --git a/data/numbers.json b/data/numbers.json new file mode 100644 index 0000000..665d40a --- /dev/null +++ b/data/numbers.json @@ -0,0 +1,120 @@ +{ + "meta": { + "description": "Digital root number correspondences for values 0 through 9, including arithmetic opposites that sum to 9.", + "notes": "Use this dataset to attach future number meanings, keywords, tarot links, and kabbalah links without changing UI code.", + "version": 1 + }, + "entries": [ + { + "value": 0, + "label": "Zero", + "opposite": 9, + "digitalRoot": 0, + "summary": "Potential, void, and the unmanifest source before form.", + "keywords": [], + "associations": { + "kabbalahNode": 10, + "tarotTrumpNumbers": [0] + } + }, + { + "value": 1, + "label": "One", + "opposite": 8, + "digitalRoot": 1, + "summary": "Initiation, identity, and singular will.", + "keywords": [], + "associations": { + "kabbalahNode": 1 + } + }, + { + "value": 2, + "label": "Two", + "opposite": 7, + "digitalRoot": 2, + "summary": "Polarity, reflection, and relationship.", + "keywords": [], + "associations": { + "kabbalahNode": 2 + } + }, + { + "value": 3, + "label": "Three", + "opposite": 6, + "digitalRoot": 3, + "summary": "Formation, synthesis, and creative expression.", + "keywords": [], + "associations": { + "kabbalahNode": 3 + } + }, + { + "value": 4, + "label": "Four", + "opposite": 5, + "digitalRoot": 4, + "summary": "Order, structure, and stable foundation.", + "keywords": [], + "associations": { + "kabbalahNode": 4 + } + }, + { + "value": 5, + "label": "Five", + "opposite": 4, + "digitalRoot": 5, + "summary": "Motion, adaptation, and dynamic change.", + "keywords": [], + "associations": { + "kabbalahNode": 5 + } + }, + { + "value": 6, + "label": "Six", + "opposite": 3, + "digitalRoot": 6, + "summary": "Balance, harmony, and reciprocal flow.", + "keywords": [], + "associations": { + "kabbalahNode": 6 + } + }, + { + "value": 7, + "label": "Seven", + "opposite": 2, + "digitalRoot": 7, + "summary": "Insight, analysis, and inner seeking.", + "keywords": [], + "associations": { + "kabbalahNode": 7 + } + }, + { + "value": 8, + "label": "Eight", + "opposite": 1, + "digitalRoot": 8, + "summary": "Power, momentum, and cyclical force.", + "keywords": [], + "associations": { + "kabbalahNode": 8 + } + }, + { + "value": 9, + "label": "Nine", + "opposite": 0, + "digitalRoot": 9, + "summary": "Completion, integration, and return.", + "keywords": [], + "associations": { + "kabbalahNode": 9 + } + } + ] +} diff --git a/data/pentagram.svg b/data/pentagram.svg new file mode 100644 index 0000000..e7f07b1 --- /dev/null +++ b/data/pentagram.svg @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/data/planet-science.json b/data/planet-science.json new file mode 100644 index 0000000..c60111f --- /dev/null +++ b/data/planet-science.json @@ -0,0 +1,269 @@ +{ + "meta": { + "source": "NASA Planetary Fact Sheet (selected values, rounded)", + "notes": "Core physical/orbital values are approximate and intended for educational UI display." + }, + "planets": [ + { + "id": "mercury", + "name": "Mercury", + "symbol": "☿︎", + "classification": "Terrestrial planet", + "summary": "The smallest planet and closest to the Sun, with extreme day/night temperature swings.", + "meanDistanceFromSun": { + "kmMillions": 57.9, + "au": 0.387 + }, + "orbitalPeriod": { + "days": 87.969, + "years": 0.241 + }, + "rotationPeriodHours": 1407.6, + "radiusKm": 2439.7, + "diameterKm": 4879.4, + "massKg": 3.3011e23, + "gravityMs2": 3.7, + "escapeVelocityKms": 4.25, + "axialTiltDeg": 0.03, + "averageTempC": 167, + "moons": 0, + "atmosphere": "Extremely thin exosphere (oxygen, sodium, hydrogen, helium, potassium).", + "notableFacts": [ + "A solar day lasts about 176 Earth days.", + "Its surface is heavily cratered like Earth’s Moon." + ] + }, + { + "id": "venus", + "name": "Venus", + "symbol": "♀︎", + "classification": "Terrestrial planet", + "summary": "Earth-sized but shrouded in dense clouds, with a runaway greenhouse effect.", + "meanDistanceFromSun": { + "kmMillions": 108.2, + "au": 0.723 + }, + "orbitalPeriod": { + "days": 224.701, + "years": 0.615 + }, + "rotationPeriodHours": -5832.5, + "radiusKm": 6051.8, + "diameterKm": 12103.6, + "massKg": 4.8675e24, + "gravityMs2": 8.87, + "escapeVelocityKms": 10.36, + "axialTiltDeg": 177.36, + "averageTempC": 464, + "moons": 0, + "atmosphere": "Very dense carbon dioxide with sulfuric acid clouds.", + "notableFacts": [ + "Hottest planetary surface in the Solar System.", + "Rotates retrograde (east-to-west)." + ] + }, + { + "id": "earth", + "name": "Earth", + "symbol": "⊕", + "classification": "Terrestrial planet", + "summary": "The only known world with stable liquid water oceans and life.", + "meanDistanceFromSun": { + "kmMillions": 149.6, + "au": 1.0 + }, + "orbitalPeriod": { + "days": 365.256, + "years": 1.0 + }, + "rotationPeriodHours": 23.934, + "radiusKm": 6371, + "diameterKm": 12742, + "massKg": 5.97237e24, + "gravityMs2": 9.807, + "escapeVelocityKms": 11.186, + "axialTiltDeg": 23.44, + "averageTempC": 15, + "moons": 1, + "atmosphere": "Mostly nitrogen and oxygen.", + "notableFacts": [ + "About 71% of the surface is covered by water.", + "Plate tectonics helps regulate long-term climate." + ] + }, + { + "id": "mars", + "name": "Mars", + "symbol": "♂︎", + "classification": "Terrestrial planet", + "summary": "A cold desert planet with the largest volcano and canyon in the Solar System.", + "meanDistanceFromSun": { + "kmMillions": 227.9, + "au": 1.524 + }, + "orbitalPeriod": { + "days": 686.98, + "years": 1.881 + }, + "rotationPeriodHours": 24.623, + "radiusKm": 3389.5, + "diameterKm": 6779, + "massKg": 6.4171e23, + "gravityMs2": 3.721, + "escapeVelocityKms": 5.03, + "axialTiltDeg": 25.19, + "averageTempC": -63, + "moons": 2, + "atmosphere": "Thin carbon dioxide atmosphere.", + "notableFacts": [ + "Home to Olympus Mons and Valles Marineris.", + "Evidence suggests ancient liquid water on its surface." + ] + }, + { + "id": "jupiter", + "name": "Jupiter", + "symbol": "♃︎", + "classification": "Gas giant", + "summary": "The largest planet, with powerful storms and a deep atmosphere of hydrogen and helium.", + "meanDistanceFromSun": { + "kmMillions": 778.6, + "au": 5.203 + }, + "orbitalPeriod": { + "days": 4332.59, + "years": 11.86 + }, + "rotationPeriodHours": 9.925, + "radiusKm": 69911, + "diameterKm": 139822, + "massKg": 1.8982e27, + "gravityMs2": 24.79, + "escapeVelocityKms": 59.5, + "axialTiltDeg": 3.13, + "averageTempC": -110, + "moons": 95, + "atmosphere": "Mostly hydrogen and helium.", + "notableFacts": [ + "The Great Red Spot is a centuries-old storm.", + "Its magnetic field is the strongest of any planet." + ] + }, + { + "id": "saturn", + "name": "Saturn", + "symbol": "♄︎", + "classification": "Gas giant", + "summary": "Known for its bright ring system made largely of ice particles.", + "meanDistanceFromSun": { + "kmMillions": 1433.5, + "au": 9.537 + }, + "orbitalPeriod": { + "days": 10759.22, + "years": 29.45 + }, + "rotationPeriodHours": 10.656, + "radiusKm": 58232, + "diameterKm": 116464, + "massKg": 5.6834e26, + "gravityMs2": 10.44, + "escapeVelocityKms": 35.5, + "axialTiltDeg": 26.73, + "averageTempC": -140, + "moons": 146, + "atmosphere": "Mostly hydrogen and helium.", + "notableFacts": [ + "Its average density is lower than water.", + "Rings are broad but extremely thin." + ] + }, + { + "id": "uranus", + "name": "Uranus", + "symbol": "♅︎", + "classification": "Ice giant", + "summary": "An ice giant that rotates on its side relative to its orbital plane.", + "meanDistanceFromSun": { + "kmMillions": 2872.5, + "au": 19.191 + }, + "orbitalPeriod": { + "days": 30688.5, + "years": 84.02 + }, + "rotationPeriodHours": -17.24, + "radiusKm": 25362, + "diameterKm": 50724, + "massKg": 8.681e25, + "gravityMs2": 8.69, + "escapeVelocityKms": 21.3, + "axialTiltDeg": 97.77, + "averageTempC": -195, + "moons": 28, + "atmosphere": "Hydrogen, helium, methane.", + "notableFacts": [ + "Its sideways tilt likely comes from an ancient giant impact.", + "Methane in the atmosphere gives it a blue-green color." + ] + }, + { + "id": "neptune", + "name": "Neptune", + "symbol": "♆︎", + "classification": "Ice giant", + "summary": "A distant, dynamic world with supersonic winds and dark storm systems.", + "meanDistanceFromSun": { + "kmMillions": 4495.1, + "au": 30.07 + }, + "orbitalPeriod": { + "days": 60182, + "years": 164.8 + }, + "rotationPeriodHours": 16.11, + "radiusKm": 24622, + "diameterKm": 49244, + "massKg": 1.02413e26, + "gravityMs2": 11.15, + "escapeVelocityKms": 23.5, + "axialTiltDeg": 28.32, + "averageTempC": -200, + "moons": 16, + "atmosphere": "Hydrogen, helium, methane.", + "notableFacts": [ + "Fastest sustained winds measured on any planet.", + "Triton, its largest moon, orbits in retrograde." + ] + }, + { + "id": "pluto", + "name": "Pluto", + "symbol": "♇︎", + "classification": "Dwarf planet", + "summary": "A Kuiper Belt dwarf planet with nitrogen ice plains and a complex seasonal cycle.", + "meanDistanceFromSun": { + "kmMillions": 5906.4, + "au": 39.48 + }, + "orbitalPeriod": { + "days": 90560, + "years": 248.0 + }, + "rotationPeriodHours": -153.3, + "radiusKm": 1188.3, + "diameterKm": 2376.6, + "massKg": 1.303e22, + "gravityMs2": 0.62, + "escapeVelocityKms": 1.21, + "axialTiltDeg": 122.53, + "averageTempC": -225, + "moons": 5, + "atmosphere": "Thin, variable nitrogen atmosphere with methane and carbon monoxide.", + "notableFacts": [ + "Charon is large enough that Pluto-Charon is often described as a binary system.", + "First close-up images were captured by New Horizons in 2015." + ] + } + ] +} diff --git a/data/planetary-correspondences.json b/data/planetary-correspondences.json new file mode 100644 index 0000000..a477915 --- /dev/null +++ b/data/planetary-correspondences.json @@ -0,0 +1,86 @@ +{ + "meta": { + "source": "https://github.com/gadicc/magick.ly/tree/master/data", + "derivedFrom": "data/astrology/planets.json5", + "notes": "Planet names/symbols/magick types are adapted from magick.ly; tarot correspondences are Golden Dawn style defaults." + }, + "planets": { + "saturn": { + "id": "saturn", + "name": "Saturn", + "symbol": "♄︎", + "weekday": "Saturday", + "tarot": { + "majorArcana": "The World", + "number": 21 + }, + "magickTypes": "Performing duty, establishing balance and equilibrium, dispelling illusions, protecting the home, legal matters, developing patience and self-discipline" + }, + "jupiter": { + "id": "jupiter", + "name": "Jupiter", + "symbol": "♃︎", + "weekday": "Thursday", + "tarot": { + "majorArcana": "Wheel of Fortune", + "number": 10 + }, + "magickTypes": "Career success, developing ambition and enthusiasm, improving fortune and luck, general health, acquiring honour, improving sense of humour, legal matters and dealing with the establishment, developing leadership skills" + }, + "mars": { + "id": "mars", + "name": "Mars", + "symbol": "♂︎", + "weekday": "Tuesday", + "tarot": { + "majorArcana": "The Tower", + "number": 16 + }, + "magickTypes": "Controlling anger, increasing courage, enhancing energy and passion, increasing vigour and sex drive" + }, + "sol": { + "id": "sol", + "name": "Sol", + "symbol": "☉︎", + "weekday": "Sunday", + "tarot": { + "majorArcana": "The Sun", + "number": 19 + }, + "magickTypes": "Career success and progression, establishing harmony, healing and improving health, leadership skills, acquiring money and resources, promotion, strengthening willpower" + }, + "venus": { + "id": "venus", + "name": "Venus", + "symbol": "♀︎", + "weekday": "Friday", + "tarot": { + "majorArcana": "The Empress", + "number": 3 + }, + "magickTypes": "Increasing attractiveness and self-confidence, beauty and passion, enhancing creativity, improving fertility, developing friendships, obtaining love" + }, + "mercury": { + "id": "mercury", + "name": "Mercury", + "symbol": "☿︎", + "weekday": "Wednesday", + "tarot": { + "majorArcana": "The Magician", + "number": 1 + }, + "magickTypes": "Business success, improving communication skills, developing knowledge and memory, diplomacy, exam success, divination, developing influence, protection when traveling by air and land, learning music" + }, + "luna": { + "id": "luna", + "name": "Luna", + "symbol": "☾︎", + "weekday": "Monday", + "tarot": { + "majorArcana": "The High Priestess", + "number": 2 + }, + "magickTypes": "Developing clairvoyance and other psychic skills, ensuring safe childbirth, divination, glamour and illusions, lucid dreaming, protection when traveling by sea" + } + } +} \ No newline at end of file diff --git a/data/playing-cards-52.json b/data/playing-cards-52.json new file mode 100644 index 0000000..794a505 --- /dev/null +++ b/data/playing-cards-52.json @@ -0,0 +1,65 @@ +{ + "meta": { + "description": "Standard 52-card playing deck mapped to tarot suits and minor tarot card names.", + "notes": "Suit mapping: Hearts→Cups, Diamonds→Pentacles, Clubs→Wands, Spades→Swords. Ace→Ace, King→Knight, Queen→Queen, Jack→Prince, 10→Princess.", + "version": 1, + "count": 52 + }, + "entries": [ + { "id": "AH", "suit": "hearts", "suitLabel": "Hearts", "suitSymbol": "♥", "rank": "A", "rankLabel": "Ace", "rankValue": 1, "digitalRoot": 1, "tarotSuit": "Cups", "tarotCard": "Ace of Cups" }, + { "id": "2H", "suit": "hearts", "suitLabel": "Hearts", "suitSymbol": "♥", "rank": "2", "rankLabel": "Two", "rankValue": 2, "digitalRoot": 2, "tarotSuit": "Cups", "tarotCard": "Two of Cups" }, + { "id": "3H", "suit": "hearts", "suitLabel": "Hearts", "suitSymbol": "♥", "rank": "3", "rankLabel": "Three", "rankValue": 3, "digitalRoot": 3, "tarotSuit": "Cups", "tarotCard": "Three of Cups" }, + { "id": "4H", "suit": "hearts", "suitLabel": "Hearts", "suitSymbol": "♥", "rank": "4", "rankLabel": "Four", "rankValue": 4, "digitalRoot": 4, "tarotSuit": "Cups", "tarotCard": "Four of Cups" }, + { "id": "5H", "suit": "hearts", "suitLabel": "Hearts", "suitSymbol": "♥", "rank": "5", "rankLabel": "Five", "rankValue": 5, "digitalRoot": 5, "tarotSuit": "Cups", "tarotCard": "Five of Cups" }, + { "id": "6H", "suit": "hearts", "suitLabel": "Hearts", "suitSymbol": "♥", "rank": "6", "rankLabel": "Six", "rankValue": 6, "digitalRoot": 6, "tarotSuit": "Cups", "tarotCard": "Six of Cups" }, + { "id": "7H", "suit": "hearts", "suitLabel": "Hearts", "suitSymbol": "♥", "rank": "7", "rankLabel": "Seven", "rankValue": 7, "digitalRoot": 7, "tarotSuit": "Cups", "tarotCard": "Seven of Cups" }, + { "id": "8H", "suit": "hearts", "suitLabel": "Hearts", "suitSymbol": "♥", "rank": "8", "rankLabel": "Eight", "rankValue": 8, "digitalRoot": 8, "tarotSuit": "Cups", "tarotCard": "Eight of Cups" }, + { "id": "9H", "suit": "hearts", "suitLabel": "Hearts", "suitSymbol": "♥", "rank": "9", "rankLabel": "Nine", "rankValue": 9, "digitalRoot": 9, "tarotSuit": "Cups", "tarotCard": "Nine of Cups" }, + { "id": "10H", "suit": "hearts", "suitLabel": "Hearts", "suitSymbol": "♥", "rank": "10", "rankLabel": "Ten", "rankValue": 10, "digitalRoot": 1, "tarotSuit": "Cups", "tarotCard": "Princess of Cups" }, + { "id": "JH", "suit": "hearts", "suitLabel": "Hearts", "suitSymbol": "♥", "rank": "J", "rankLabel": "Jack", "rankValue": null, "digitalRoot": null, "tarotSuit": "Cups", "tarotCard": "Prince of Cups" }, + { "id": "QH", "suit": "hearts", "suitLabel": "Hearts", "suitSymbol": "♥", "rank": "Q", "rankLabel": "Queen", "rankValue": null, "digitalRoot": null, "tarotSuit": "Cups", "tarotCard": "Queen of Cups" }, + { "id": "KH", "suit": "hearts", "suitLabel": "Hearts", "suitSymbol": "♥", "rank": "K", "rankLabel": "King", "rankValue": null, "digitalRoot": null, "tarotSuit": "Cups", "tarotCard": "Knight of Cups" }, + + { "id": "AD", "suit": "diamonds", "suitLabel": "Diamonds", "suitSymbol": "♦", "rank": "A", "rankLabel": "Ace", "rankValue": 1, "digitalRoot": 1, "tarotSuit": "Pentacles", "tarotCard": "Ace of Pentacles" }, + { "id": "2D", "suit": "diamonds", "suitLabel": "Diamonds", "suitSymbol": "♦", "rank": "2", "rankLabel": "Two", "rankValue": 2, "digitalRoot": 2, "tarotSuit": "Pentacles", "tarotCard": "Two of Pentacles" }, + { "id": "3D", "suit": "diamonds", "suitLabel": "Diamonds", "suitSymbol": "♦", "rank": "3", "rankLabel": "Three", "rankValue": 3, "digitalRoot": 3, "tarotSuit": "Pentacles", "tarotCard": "Three of Pentacles" }, + { "id": "4D", "suit": "diamonds", "suitLabel": "Diamonds", "suitSymbol": "♦", "rank": "4", "rankLabel": "Four", "rankValue": 4, "digitalRoot": 4, "tarotSuit": "Pentacles", "tarotCard": "Four of Pentacles" }, + { "id": "5D", "suit": "diamonds", "suitLabel": "Diamonds", "suitSymbol": "♦", "rank": "5", "rankLabel": "Five", "rankValue": 5, "digitalRoot": 5, "tarotSuit": "Pentacles", "tarotCard": "Five of Pentacles" }, + { "id": "6D", "suit": "diamonds", "suitLabel": "Diamonds", "suitSymbol": "♦", "rank": "6", "rankLabel": "Six", "rankValue": 6, "digitalRoot": 6, "tarotSuit": "Pentacles", "tarotCard": "Six of Pentacles" }, + { "id": "7D", "suit": "diamonds", "suitLabel": "Diamonds", "suitSymbol": "♦", "rank": "7", "rankLabel": "Seven", "rankValue": 7, "digitalRoot": 7, "tarotSuit": "Pentacles", "tarotCard": "Seven of Pentacles" }, + { "id": "8D", "suit": "diamonds", "suitLabel": "Diamonds", "suitSymbol": "♦", "rank": "8", "rankLabel": "Eight", "rankValue": 8, "digitalRoot": 8, "tarotSuit": "Pentacles", "tarotCard": "Eight of Pentacles" }, + { "id": "9D", "suit": "diamonds", "suitLabel": "Diamonds", "suitSymbol": "♦", "rank": "9", "rankLabel": "Nine", "rankValue": 9, "digitalRoot": 9, "tarotSuit": "Pentacles", "tarotCard": "Nine of Pentacles" }, + { "id": "10D", "suit": "diamonds", "suitLabel": "Diamonds", "suitSymbol": "♦", "rank": "10", "rankLabel": "Ten", "rankValue": 10, "digitalRoot": 1, "tarotSuit": "Pentacles", "tarotCard": "Princess of Pentacles" }, + { "id": "JD", "suit": "diamonds", "suitLabel": "Diamonds", "suitSymbol": "♦", "rank": "J", "rankLabel": "Jack", "rankValue": null, "digitalRoot": null, "tarotSuit": "Pentacles", "tarotCard": "Prince of Pentacles" }, + { "id": "QD", "suit": "diamonds", "suitLabel": "Diamonds", "suitSymbol": "♦", "rank": "Q", "rankLabel": "Queen", "rankValue": null, "digitalRoot": null, "tarotSuit": "Pentacles", "tarotCard": "Queen of Pentacles" }, + { "id": "KD", "suit": "diamonds", "suitLabel": "Diamonds", "suitSymbol": "♦", "rank": "K", "rankLabel": "King", "rankValue": null, "digitalRoot": null, "tarotSuit": "Pentacles", "tarotCard": "Knight of Pentacles" }, + + { "id": "AC", "suit": "clubs", "suitLabel": "Clubs", "suitSymbol": "♣", "rank": "A", "rankLabel": "Ace", "rankValue": 1, "digitalRoot": 1, "tarotSuit": "Wands", "tarotCard": "Ace of Wands" }, + { "id": "2C", "suit": "clubs", "suitLabel": "Clubs", "suitSymbol": "♣", "rank": "2", "rankLabel": "Two", "rankValue": 2, "digitalRoot": 2, "tarotSuit": "Wands", "tarotCard": "Two of Wands" }, + { "id": "3C", "suit": "clubs", "suitLabel": "Clubs", "suitSymbol": "♣", "rank": "3", "rankLabel": "Three", "rankValue": 3, "digitalRoot": 3, "tarotSuit": "Wands", "tarotCard": "Three of Wands" }, + { "id": "4C", "suit": "clubs", "suitLabel": "Clubs", "suitSymbol": "♣", "rank": "4", "rankLabel": "Four", "rankValue": 4, "digitalRoot": 4, "tarotSuit": "Wands", "tarotCard": "Four of Wands" }, + { "id": "5C", "suit": "clubs", "suitLabel": "Clubs", "suitSymbol": "♣", "rank": "5", "rankLabel": "Five", "rankValue": 5, "digitalRoot": 5, "tarotSuit": "Wands", "tarotCard": "Five of Wands" }, + { "id": "6C", "suit": "clubs", "suitLabel": "Clubs", "suitSymbol": "♣", "rank": "6", "rankLabel": "Six", "rankValue": 6, "digitalRoot": 6, "tarotSuit": "Wands", "tarotCard": "Six of Wands" }, + { "id": "7C", "suit": "clubs", "suitLabel": "Clubs", "suitSymbol": "♣", "rank": "7", "rankLabel": "Seven", "rankValue": 7, "digitalRoot": 7, "tarotSuit": "Wands", "tarotCard": "Seven of Wands" }, + { "id": "8C", "suit": "clubs", "suitLabel": "Clubs", "suitSymbol": "♣", "rank": "8", "rankLabel": "Eight", "rankValue": 8, "digitalRoot": 8, "tarotSuit": "Wands", "tarotCard": "Eight of Wands" }, + { "id": "9C", "suit": "clubs", "suitLabel": "Clubs", "suitSymbol": "♣", "rank": "9", "rankLabel": "Nine", "rankValue": 9, "digitalRoot": 9, "tarotSuit": "Wands", "tarotCard": "Nine of Wands" }, + { "id": "10C", "suit": "clubs", "suitLabel": "Clubs", "suitSymbol": "♣", "rank": "10", "rankLabel": "Ten", "rankValue": 10, "digitalRoot": 1, "tarotSuit": "Wands", "tarotCard": "Princess of Wands" }, + { "id": "JC", "suit": "clubs", "suitLabel": "Clubs", "suitSymbol": "♣", "rank": "J", "rankLabel": "Jack", "rankValue": null, "digitalRoot": null, "tarotSuit": "Wands", "tarotCard": "Prince of Wands" }, + { "id": "QC", "suit": "clubs", "suitLabel": "Clubs", "suitSymbol": "♣", "rank": "Q", "rankLabel": "Queen", "rankValue": null, "digitalRoot": null, "tarotSuit": "Wands", "tarotCard": "Queen of Wands" }, + { "id": "KC", "suit": "clubs", "suitLabel": "Clubs", "suitSymbol": "♣", "rank": "K", "rankLabel": "King", "rankValue": null, "digitalRoot": null, "tarotSuit": "Wands", "tarotCard": "Knight of Wands" }, + + { "id": "AS", "suit": "spades", "suitLabel": "Spades", "suitSymbol": "♠", "rank": "A", "rankLabel": "Ace", "rankValue": 1, "digitalRoot": 1, "tarotSuit": "Swords", "tarotCard": "Ace of Swords" }, + { "id": "2S", "suit": "spades", "suitLabel": "Spades", "suitSymbol": "♠", "rank": "2", "rankLabel": "Two", "rankValue": 2, "digitalRoot": 2, "tarotSuit": "Swords", "tarotCard": "Two of Swords" }, + { "id": "3S", "suit": "spades", "suitLabel": "Spades", "suitSymbol": "♠", "rank": "3", "rankLabel": "Three", "rankValue": 3, "digitalRoot": 3, "tarotSuit": "Swords", "tarotCard": "Three of Swords" }, + { "id": "4S", "suit": "spades", "suitLabel": "Spades", "suitSymbol": "♠", "rank": "4", "rankLabel": "Four", "rankValue": 4, "digitalRoot": 4, "tarotSuit": "Swords", "tarotCard": "Four of Swords" }, + { "id": "5S", "suit": "spades", "suitLabel": "Spades", "suitSymbol": "♠", "rank": "5", "rankLabel": "Five", "rankValue": 5, "digitalRoot": 5, "tarotSuit": "Swords", "tarotCard": "Five of Swords" }, + { "id": "6S", "suit": "spades", "suitLabel": "Spades", "suitSymbol": "♠", "rank": "6", "rankLabel": "Six", "rankValue": 6, "digitalRoot": 6, "tarotSuit": "Swords", "tarotCard": "Six of Swords" }, + { "id": "7S", "suit": "spades", "suitLabel": "Spades", "suitSymbol": "♠", "rank": "7", "rankLabel": "Seven", "rankValue": 7, "digitalRoot": 7, "tarotSuit": "Swords", "tarotCard": "Seven of Swords" }, + { "id": "8S", "suit": "spades", "suitLabel": "Spades", "suitSymbol": "♠", "rank": "8", "rankLabel": "Eight", "rankValue": 8, "digitalRoot": 8, "tarotSuit": "Swords", "tarotCard": "Eight of Swords" }, + { "id": "9S", "suit": "spades", "suitLabel": "Spades", "suitSymbol": "♠", "rank": "9", "rankLabel": "Nine", "rankValue": 9, "digitalRoot": 9, "tarotSuit": "Swords", "tarotCard": "Nine of Swords" }, + { "id": "10S", "suit": "spades", "suitLabel": "Spades", "suitSymbol": "♠", "rank": "10", "rankLabel": "Ten", "rankValue": 10, "digitalRoot": 1, "tarotSuit": "Swords", "tarotCard": "Princess of Swords" }, + { "id": "JS", "suit": "spades", "suitLabel": "Spades", "suitSymbol": "♠", "rank": "J", "rankLabel": "Jack", "rankValue": null, "digitalRoot": null, "tarotSuit": "Swords", "tarotCard": "Prince of Swords" }, + { "id": "QS", "suit": "spades", "suitLabel": "Spades", "suitSymbol": "♠", "rank": "Q", "rankLabel": "Queen", "rankValue": null, "digitalRoot": null, "tarotSuit": "Swords", "tarotCard": "Queen of Swords" }, + { "id": "KS", "suit": "spades", "suitLabel": "Spades", "suitSymbol": "♠", "rank": "K", "rankLabel": "King", "rankValue": null, "digitalRoot": null, "tarotSuit": "Swords", "tarotCard": "Knight of Swords" } + ] +} diff --git a/data/sabian-symbols.json b/data/sabian-symbols.json new file mode 100644 index 0000000..4a13887 --- /dev/null +++ b/data/sabian-symbols.json @@ -0,0 +1,2166 @@ +{ + "source": "https://sabiansymbols.astrologyweekly.com/list.php", + "count": 360, + "symbols": [ + { + "absoluteDegree": 1, + "sign": "Aries", + "degreeInSign": 1, + "phrase": "A WOMAN HAS RISEN OUT OF THE OCEAN, A SEAL IS EMBRACING HER." + }, + { + "absoluteDegree": 2, + "sign": "Aries", + "degreeInSign": 2, + "phrase": "A COMEDIAN ENTERTAINING THE GROUP." + }, + { + "absoluteDegree": 3, + "sign": "Aries", + "degreeInSign": 3, + "phrase": "A CAMEO PROFILE OF A MAN IN THE OUTLINE OF HIS COUNTRY." + }, + { + "absoluteDegree": 4, + "sign": "Aries", + "degreeInSign": 4, + "phrase": "TWO LOVERS STROLLING THROUGH A SECLUDED WALK." + }, + { + "absoluteDegree": 5, + "sign": "Aries", + "degreeInSign": 5, + "phrase": "A TRIANGLE WITH WINGS." + }, + { + "absoluteDegree": 6, + "sign": "Aries", + "degreeInSign": 6, + "phrase": "A SQUARE BRIGHTLY LIGHTED ON ONE SIDE." + }, + { + "absoluteDegree": 7, + "sign": "Aries", + "degreeInSign": 7, + "phrase": "A MAN SUCCESSFULLY EXPRESSING HIMSELF IN TWO REALMS AT ONCE." + }, + { + "absoluteDegree": 8, + "sign": "Aries", + "degreeInSign": 8, + "phrase": "A WOMAN'S HAT WITH STREAMERS BLOWN BY THE EAST WIND." + }, + { + "absoluteDegree": 9, + "sign": "Aries", + "degreeInSign": 9, + "phrase": "A CRYSTAL GAZER." + }, + { + "absoluteDegree": 10, + "sign": "Aries", + "degreeInSign": 10, + "phrase": "A TEACHER GIVES NEW SYMBOLIC FORMS TO TRADITIONAL IMAGES." + }, + { + "absoluteDegree": 11, + "sign": "Aries", + "degreeInSign": 11, + "phrase": "THE RULER OF A NATION." + }, + { + "absoluteDegree": 12, + "sign": "Aries", + "degreeInSign": 12, + "phrase": "A FLOCK OF WILD GEESE." + }, + { + "absoluteDegree": 13, + "sign": "Aries", + "degreeInSign": 13, + "phrase": "A BOMB WHICH FAILED TO EXPLODE IS NOW SAFELY CONCEALED." + }, + { + "absoluteDegree": 14, + "sign": "Aries", + "degreeInSign": 14, + "phrase": "A SERPENT COILING NEAR A MAN AND A WOMAN." + }, + { + "absoluteDegree": 15, + "sign": "Aries", + "degreeInSign": 15, + "phrase": "AN INDIAN WEAVING A BLANKET." + }, + { + "absoluteDegree": 16, + "sign": "Aries", + "degreeInSign": 16, + "phrase": "BROWNIES DANCING IN THE SETTING SUN." + }, + { + "absoluteDegree": 17, + "sign": "Aries", + "degreeInSign": 17, + "phrase": "TWO PRIM SPINSTERS SITTING TOGETHER IN SILENCE." + }, + { + "absoluteDegree": 18, + "sign": "Aries", + "degreeInSign": 18, + "phrase": "AN EMPTY HAMMOCK." + }, + { + "absoluteDegree": 19, + "sign": "Aries", + "degreeInSign": 19, + "phrase": "THE MAGIC CARPET OF ORIENTAL IMAGERY." + }, + { + "absoluteDegree": 20, + "sign": "Aries", + "degreeInSign": 20, + "phrase": "A YOUNG GIRL FEEDING BIRDS IN WINTER." + }, + { + "absoluteDegree": 21, + "sign": "Aries", + "degreeInSign": 21, + "phrase": "A PUGILIST (BOXER) ENTERING THE RING." + }, + { + "absoluteDegree": 22, + "sign": "Aries", + "degreeInSign": 22, + "phrase": "THE GATE TO THE GARDEN OF ALL FULFILLED DESIRES." + }, + { + "absoluteDegree": 23, + "sign": "Aries", + "degreeInSign": 23, + "phrase": "A WOMAN IN PASTEL COLORS CARRYING A HEAVY AND VALUABLE BUT VEILED LOAD." + }, + { + "absoluteDegree": 24, + "sign": "Aries", + "degreeInSign": 24, + "phrase": "AN OPEN WINDOW AND A NET CURTAIN BLOWING INTO A CORNUCOPIA." + }, + { + "absoluteDegree": 25, + "sign": "Aries", + "degreeInSign": 25, + "phrase": "A DOUBLE PROMISE REVEALS ITS INNER AND OUTER MEANINGS." + }, + { + "absoluteDegree": 26, + "sign": "Aries", + "degreeInSign": 26, + "phrase": "A MAN POSSESSED OF MORE GIFTS THAN HE CAN HOLD." + }, + { + "absoluteDegree": 27, + "sign": "Aries", + "degreeInSign": 27, + "phrase": "THROUGH IMAGINATION, A LOST OPPORTUNITY IS REGAINED." + }, + { + "absoluteDegree": 28, + "sign": "Aries", + "degreeInSign": 28, + "phrase": "A LARGE DISAPPOINTED AUDIENCE." + }, + { + "absoluteDegree": 29, + "sign": "Aries", + "degreeInSign": 29, + "phrase": "THE MUSIC OF THE SPHERES." + }, + { + "absoluteDegree": 30, + "sign": "Aries", + "degreeInSign": 30, + "phrase": "A DUCK POND AND ITS BROOD." + }, + { + "absoluteDegree": 31, + "sign": "Taurus", + "degreeInSign": 1, + "phrase": "A CLEAR MOUNTAIN STREAM." + }, + { + "absoluteDegree": 32, + "sign": "Taurus", + "degreeInSign": 2, + "phrase": "AN ELECTRICAL STORM." + }, + { + "absoluteDegree": 33, + "sign": "Taurus", + "degreeInSign": 3, + "phrase": "STEPS UP TO A LAWN BLOOMING WITH CLOVER." + }, + { + "absoluteDegree": 34, + "sign": "Taurus", + "degreeInSign": 4, + "phrase": "THE POT OF GOLD AT THE END OF THE RAINBOW." + }, + { + "absoluteDegree": 35, + "sign": "Taurus", + "degreeInSign": 5, + "phrase": "A WIDOW AT AN OPEN GRAVE." + }, + { + "absoluteDegree": 36, + "sign": "Taurus", + "degreeInSign": 6, + "phrase": "A BRIDGE BEING BUILT ACROSS A GORGE." + }, + { + "absoluteDegree": 37, + "sign": "Taurus", + "degreeInSign": 7, + "phrase": "A WOMAN OF SAMARIA COMES TO DRAW WATER FROM THE WELL." + }, + { + "absoluteDegree": 38, + "sign": "Taurus", + "degreeInSign": 8, + "phrase": "A SLEIGH WITHOUT SNOW." + }, + { + "absoluteDegree": 39, + "sign": "Taurus", + "degreeInSign": 9, + "phrase": "A CHRISTMAS TREE DECORATED." + }, + { + "absoluteDegree": 40, + "sign": "Taurus", + "degreeInSign": 10, + "phrase": "A RED CROSS NURSE." + }, + { + "absoluteDegree": 41, + "sign": "Taurus", + "degreeInSign": 11, + "phrase": "A WOMAN SPRINKLING FLOWERS." + }, + { + "absoluteDegree": 42, + "sign": "Taurus", + "degreeInSign": 12, + "phrase": "A YOUNG COUPLE WALK DOWN MAIN- STREET, WINDOW-SHOPPING." + }, + { + "absoluteDegree": 43, + "sign": "Taurus", + "degreeInSign": 13, + "phrase": "A PORTER CARRYING HEAVY BAGGAGE." + }, + { + "absoluteDegree": 44, + "sign": "Taurus", + "degreeInSign": 14, + "phrase": "ON THE BEACH, CHILDREN PLAY WHILE SHELLFISH GROPE AT THE EDGE OF THE WATER." + }, + { + "absoluteDegree": 45, + "sign": "Taurus", + "degreeInSign": 15, + "phrase": "A MAN WITH RAKISH SILK HAT, MUFFLED AGAINST THE COLD, BRAVES A STORM." + }, + { + "absoluteDegree": 46, + "sign": "Taurus", + "degreeInSign": 16, + "phrase": "AN OLD TEACHER FAILS TO INTEREST HIS PUPILS IN TRADITIONAL KNOWLEDGE." + }, + { + "absoluteDegree": 47, + "sign": "Taurus", + "degreeInSign": 17, + "phrase": "A SYMBOLICAL BATTLE BETWEEN 'SWORDS' AND 'TORCHES'." + }, + { + "absoluteDegree": 48, + "sign": "Taurus", + "degreeInSign": 18, + "phrase": "A WOMAN AIRING AN OLD BAG THROUGH A SUNNY WINDOW." + }, + { + "absoluteDegree": 49, + "sign": "Taurus", + "degreeInSign": 19, + "phrase": "A NEW CONTINENT RISING OUT OF THE OCEAN." + }, + { + "absoluteDegree": 50, + "sign": "Taurus", + "degreeInSign": 20, + "phrase": "WISPS OF CLOUDS, LIKE WINGS, ARE STREAMING ACROSS THE SKY." + }, + { + "absoluteDegree": 51, + "sign": "Taurus", + "degreeInSign": 21, + "phrase": "MOVING FINGER POINTS TO SIGNIFICANT PASSAGES IN A BOOK." + }, + { + "absoluteDegree": 52, + "sign": "Taurus", + "degreeInSign": 22, + "phrase": "WHITE DOVE FLYING OVER TROUBLED WATERS." + }, + { + "absoluteDegree": 53, + "sign": "Taurus", + "degreeInSign": 23, + "phrase": "A JEWELLERY SHOP FILLED WITH THE MOST MAGNIFICENT JEWELS." + }, + { + "absoluteDegree": 54, + "sign": "Taurus", + "degreeInSign": 24, + "phrase": "AN INDIAN WARRIOR RIDING FIERCELY, HUMAN SCALPS HANGING AT HIS BELT." + }, + { + "absoluteDegree": 55, + "sign": "Taurus", + "degreeInSign": 25, + "phrase": "A LARGE WELL-KEPT PUBLIC PARK." + }, + { + "absoluteDegree": 56, + "sign": "Taurus", + "degreeInSign": 26, + "phrase": "A SPANIARD SERENADING HIS SENORITA." + }, + { + "absoluteDegree": 57, + "sign": "Taurus", + "degreeInSign": 27, + "phrase": "AN OLD INDIAN WOMAN SELLING BEADS." + }, + { + "absoluteDegree": 58, + "sign": "Taurus", + "degreeInSign": 28, + "phrase": "A MATURE WOMAN REAWAKENED TO ROMANCE." + }, + { + "absoluteDegree": 59, + "sign": "Taurus", + "degreeInSign": 29, + "phrase": "TWO COBBLERS WORKING AT A TABLE." + }, + { + "absoluteDegree": 60, + "sign": "Taurus", + "degreeInSign": 30, + "phrase": "A PEACOCK PARADING ON THE TERRACE OF AN OLD CASTLE." + }, + { + "absoluteDegree": 61, + "sign": "Gemini", + "degreeInSign": 1, + "phrase": "A GLASS-BOTTOMED BOAT REVEALS UNDER-SEA WONDERS." + }, + { + "absoluteDegree": 62, + "sign": "Gemini", + "degreeInSign": 2, + "phrase": "SANTA CLAUSE FILLING STOCKINGS FURTIVELY." + }, + { + "absoluteDegree": 63, + "sign": "Gemini", + "degreeInSign": 3, + "phrase": "THE GARDEN OF THE TUILERIES IN PARIS." + }, + { + "absoluteDegree": 64, + "sign": "Gemini", + "degreeInSign": 4, + "phrase": "HOLLY AND MISTLETOE BRING CHRISTMAS SPIRIT TO A HOME." + }, + { + "absoluteDegree": 65, + "sign": "Gemini", + "degreeInSign": 5, + "phrase": "A RADICAL MAGAZINE, ASKING FOR ACTION, DISPLAYS A SENSATIONAL FRONT PAGE." + }, + { + "absoluteDegree": 66, + "sign": "Gemini", + "degreeInSign": 6, + "phrase": "WORKMEN DRILLING FOR OIL." + }, + { + "absoluteDegree": 67, + "sign": "Gemini", + "degreeInSign": 7, + "phrase": "AN OLD-FASHIONED WELL." + }, + { + "absoluteDegree": 68, + "sign": "Gemini", + "degreeInSign": 8, + "phrase": "AROUSED STRIKERS ROUND A FACTORY." + }, + { + "absoluteDegree": 69, + "sign": "Gemini", + "degreeInSign": 9, + "phrase": "A QUIVER FILLED WITH ARROWS." + }, + { + "absoluteDegree": 70, + "sign": "Gemini", + "degreeInSign": 10, + "phrase": "AEROPLANE PERFORMING A NOSE-DIVE." + }, + { + "absoluteDegree": 71, + "sign": "Gemini", + "degreeInSign": 11, + "phrase": "NEWLY OPENED LANDS OFFER THE PIONEER NEW OPPORTUNITIES FOR EXPERIENCE." + }, + { + "absoluteDegree": 72, + "sign": "Gemini", + "degreeInSign": 12, + "phrase": "A BLACK SLAVE-GIRL DEMANDS HER RIGHTS OF HER MISTRESS." + }, + { + "absoluteDegree": 73, + "sign": "Gemini", + "degreeInSign": 13, + "phrase": "WORLD FAMOUS PIANIST GIVING A CONCERT PERFORMANCE." + }, + { + "absoluteDegree": 74, + "sign": "Gemini", + "degreeInSign": 14, + "phrase": "TWO PEOPLE, LIVING FAR APART, IN TELEPATHIC COMMUNICATION." + }, + { + "absoluteDegree": 75, + "sign": "Gemini", + "degreeInSign": 15, + "phrase": "TWO DUTCH CHILDREN TALKING." + }, + { + "absoluteDegree": 76, + "sign": "Gemini", + "degreeInSign": 16, + "phrase": "A WOMAN ACTIVIST IN AN EMOTIONAL SPEECH, DRAMATIZING HER CAUSE." + }, + { + "absoluteDegree": 77, + "sign": "Gemini", + "degreeInSign": 17, + "phrase": "THE HEAD OF A ROBUST YOUTH CHANGES INTO THAT OF A MATURE THINKER." + }, + { + "absoluteDegree": 78, + "sign": "Gemini", + "degreeInSign": 18, + "phrase": "TWO CHINESE MEN TALKING CHINESE (IN A WESTERN CROWD)." + }, + { + "absoluteDegree": 79, + "sign": "Gemini", + "degreeInSign": 19, + "phrase": "A LARGE ARCHAIC VOLUME REVEALS A TRADITIONAL WISDOM." + }, + { + "absoluteDegree": 80, + "sign": "Gemini", + "degreeInSign": 20, + "phrase": "A CAFETERIA WITH AN ABUNDANCE OF CHOICES." + }, + { + "absoluteDegree": 81, + "sign": "Gemini", + "degreeInSign": 21, + "phrase": "A TUMULTUOUS LABOR DEMONSTRATION." + }, + { + "absoluteDegree": 82, + "sign": "Gemini", + "degreeInSign": 22, + "phrase": "DANCING COUPLES CROWD THE BARN IN A HARVEST FESTIVAL." + }, + { + "absoluteDegree": 83, + "sign": "Gemini", + "degreeInSign": 23, + "phrase": "THREE FLEDGLINGS IN A NEST HIGH IN A TREE." + }, + { + "absoluteDegree": 84, + "sign": "Gemini", + "degreeInSign": 24, + "phrase": "CHILDREN SKATING ON ICE." + }, + { + "absoluteDegree": 85, + "sign": "Gemini", + "degreeInSign": 25, + "phrase": "A GARDENER TRIMMING LARGE PALM TREES." + }, + { + "absoluteDegree": 86, + "sign": "Gemini", + "degreeInSign": 26, + "phrase": "WINTER FROST IN THE WOODS." + }, + { + "absoluteDegree": 87, + "sign": "Gemini", + "degreeInSign": 27, + "phrase": "A YOUNG GYPSY EMERGING FROM THE WOODS GAZES AT FAR CITIES." + }, + { + "absoluteDegree": 88, + "sign": "Gemini", + "degreeInSign": 28, + "phrase": "SOCIETY GRANTING BANKRUPTCY TO HIM, A MAN LEAVES THE COURT." + }, + { + "absoluteDegree": 89, + "sign": "Gemini", + "degreeInSign": 29, + "phrase": "THE FIRST MOCKINGBIRD OF SPRING SINGS FROM THE TREE TOP." + }, + { + "absoluteDegree": 90, + "sign": "Gemini", + "degreeInSign": 30, + "phrase": "A PARADE OF BATHING BEAUTIES BEFORE LARGE BEACH CROWDS." + }, + { + "absoluteDegree": 91, + "sign": "Cancer", + "degreeInSign": 1, + "phrase": "ON A SHIP THE SAILORS LOWER AN OLD FLAG AND RAISE A NEW ONE." + }, + { + "absoluteDegree": 92, + "sign": "Cancer", + "degreeInSign": 2, + "phrase": "A MAN ON A MAGIC CARPET OBSERVES VAST VISTAS BELOW HIM." + }, + { + "absoluteDegree": 93, + "sign": "Cancer", + "degreeInSign": 3, + "phrase": "AN ARCTIC EXPLORER LEADS A REINDEER THROUGH ICY CANYONS." + }, + { + "absoluteDegree": 94, + "sign": "Cancer", + "degreeInSign": 4, + "phrase": "A CAT ARGUING WITH A MOUSE." + }, + { + "absoluteDegree": 95, + "sign": "Cancer", + "degreeInSign": 5, + "phrase": "AT A RAILROAD CROSSING, AN AUTOMOBILE IS WRECKED BY A TRAIN." + }, + { + "absoluteDegree": 96, + "sign": "Cancer", + "degreeInSign": 6, + "phrase": "GAME BIRDS FEATHERING THEIR NESTS." + }, + { + "absoluteDegree": 97, + "sign": "Cancer", + "degreeInSign": 7, + "phrase": "TWO FAIRIES (NATURE SPIRITS) DANCING ON A MOONLIT NIGHT." + }, + { + "absoluteDegree": 98, + "sign": "Cancer", + "degreeInSign": 8, + "phrase": "A GROUP RABBITS DRESSED IN CLOTHES AND ON PARADE." + }, + { + "absoluteDegree": 99, + "sign": "Cancer", + "degreeInSign": 9, + "phrase": "A SMALL, NAKED GIRL BENDS OVER A POND TRYING TO CATCH A FISH." + }, + { + "absoluteDegree": 100, + "sign": "Cancer", + "degreeInSign": 10, + "phrase": "A LARGE DIAMOND IN THE FIRST STAGES OF THE CUTTING PROCESS." + }, + { + "absoluteDegree": 101, + "sign": "Cancer", + "degreeInSign": 11, + "phrase": "A CLOWN CARICATURING WELL-KNOWN PERSONALITIES." + }, + { + "absoluteDegree": 102, + "sign": "Cancer", + "degreeInSign": 12, + "phrase": "A CHINESE WOMAN NURSING A BABY WHOSE AURA REVEALS HIM TO BE THE REINCARNATION OF A GREAT TEACHER." + }, + { + "absoluteDegree": 103, + "sign": "Cancer", + "degreeInSign": 13, + "phrase": "ONE HAND SLIGHTLY FLEXED WITH A VERY PROMINENT THUMB." + }, + { + "absoluteDegree": 104, + "sign": "Cancer", + "degreeInSign": 14, + "phrase": "A VERY OLD MAN FACING A VAST DARK SPACE TO THE NORTHEAST." + }, + { + "absoluteDegree": 105, + "sign": "Cancer", + "degreeInSign": 15, + "phrase": "A GROUP OF PEOPLE WHO HAVE OVEREATEN AND ENJOYED IT." + }, + { + "absoluteDegree": 106, + "sign": "Cancer", + "degreeInSign": 16, + "phrase": "A MAN STUDYING A MANDALA IN FRONT OF HIM, WITH THE HELP OF A VERY ANCIENT BOOK." + }, + { + "absoluteDegree": 107, + "sign": "Cancer", + "degreeInSign": 17, + "phrase": "THE SEED GROWS INTO KNOWLEDGE AND LIFE." + }, + { + "absoluteDegree": 108, + "sign": "Cancer", + "degreeInSign": 18, + "phrase": "A HEN SCRATCHING FOR HER CHICKS." + }, + { + "absoluteDegree": 109, + "sign": "Cancer", + "degreeInSign": 19, + "phrase": "A PRIEST PERFORMING A MARRIAGE CEREMONY." + }, + { + "absoluteDegree": 110, + "sign": "Cancer", + "degreeInSign": 20, + "phrase": "VENETIAN GONDOLIERS IN A SERENADE." + }, + { + "absoluteDegree": 111, + "sign": "Cancer", + "degreeInSign": 21, + "phrase": "A PRIMA DONNA SINGING." + }, + { + "absoluteDegree": 112, + "sign": "Cancer", + "degreeInSign": 22, + "phrase": "A YOUNG WOMAN AWAITING A SAILBOAT." + }, + { + "absoluteDegree": 113, + "sign": "Cancer", + "degreeInSign": 23, + "phrase": "THE MEETING OF A LITERARY SOCIETY." + }, + { + "absoluteDegree": 114, + "sign": "Cancer", + "degreeInSign": 24, + "phrase": "A WOMAN AND TWO MEN CASTAWAYS ON A SMALL ISLAND OF THE SOUTH SEAS." + }, + { + "absoluteDegree": 115, + "sign": "Cancer", + "degreeInSign": 25, + "phrase": "A LEADER OF MEN WRAPPED IN AN INVISIBLE MANTLE OF POWER." + }, + { + "absoluteDegree": 116, + "sign": "Cancer", + "degreeInSign": 26, + "phrase": "GUESTS ARE READING IN THE LIBRARY OF A LUXURIOUS HOME." + }, + { + "absoluteDegree": 117, + "sign": "Cancer", + "degreeInSign": 27, + "phrase": "A VIOLENT STORM IN A CANYON FILLED WITH EXPENSIVE HOMES." + }, + { + "absoluteDegree": 118, + "sign": "Cancer", + "degreeInSign": 28, + "phrase": "INDIAN GIRL INTRODUCES COLLEGE BOY-FRIEND TO HER ASSEMBLED TRIBE." + }, + { + "absoluteDegree": 119, + "sign": "Cancer", + "degreeInSign": 29, + "phrase": "A GREEK MUSE WEIGHING NEW BORN TWINS IN GOLDEN SCALES." + }, + { + "absoluteDegree": 120, + "sign": "Cancer", + "degreeInSign": 30, + "phrase": "A DAUGHTER OF THE AMERICAN REVOLUTION." + }, + { + "absoluteDegree": 121, + "sign": "Leo", + "degreeInSign": 1, + "phrase": "UNDER EMOTIONAL STRESS, BLOOD RUSHES TO A MAN'S HEAD." + }, + { + "absoluteDegree": 122, + "sign": "Leo", + "degreeInSign": 2, + "phrase": "AN EPIDEMIC OF MUMPS." + }, + { + "absoluteDegree": 123, + "sign": "Leo", + "degreeInSign": 3, + "phrase": "A MATURE WOMAN, KEEPING UP WITH THE TIMES, HAVING HER HAIR BOBBED." + }, + { + "absoluteDegree": 124, + "sign": "Leo", + "degreeInSign": 4, + "phrase": "A MAN FORMALLY DRESSED STANDS NEAR TROPHIES HE BROUGHT BACK FROM A HUNTING EXPEDITION." + }, + { + "absoluteDegree": 125, + "sign": "Leo", + "degreeInSign": 5, + "phrase": "ROCK FORMATIONS TOWER OVER A DEEP CANYON." + }, + { + "absoluteDegree": 126, + "sign": "Leo", + "degreeInSign": 6, + "phrase": "AN OLD FASHIONED 'CONSERVATIVE' WOMAN IS CONFRONTED BY AN UP-TO-DATE GIRL." + }, + { + "absoluteDegree": 127, + "sign": "Leo", + "degreeInSign": 7, + "phrase": "THE CONSTELLATIONS OF STARS IN THE SKY." + }, + { + "absoluteDegree": 128, + "sign": "Leo", + "degreeInSign": 8, + "phrase": "GLASS BLOWERS SHAPE BEAUTIFUL VASES WITH THEIR CONTROLLED BREATHING." + }, + { + "absoluteDegree": 129, + "sign": "Leo", + "degreeInSign": 9, + "phrase": "A COMMUNIST ACTIVIST SPREADING HIS REVOLUTIONARY IDEALS." + }, + { + "absoluteDegree": 130, + "sign": "Leo", + "degreeInSign": 10, + "phrase": "EARLY MORNING DEW." + }, + { + "absoluteDegree": 131, + "sign": "Leo", + "degreeInSign": 11, + "phrase": "CHILDREN ON A SWING IN A HUGE OAK TREE." + }, + { + "absoluteDegree": 132, + "sign": "Leo", + "degreeInSign": 12, + "phrase": "AN EVENING LAWN PARTY OF ADULTS." + }, + { + "absoluteDegree": 133, + "sign": "Leo", + "degreeInSign": 13, + "phrase": "AN OLD SEA CAPTAIN ROCKING ON THE PORCH OF HIS COTTAGE." + }, + { + "absoluteDegree": 134, + "sign": "Leo", + "degreeInSign": 14, + "phrase": "CHERUB-LIKE, A HUMAN SOUL WHISPERS, SEEKING TO MANIFEST." + }, + { + "absoluteDegree": 135, + "sign": "Leo", + "degreeInSign": 15, + "phrase": "A PAGEANT MOVING ALONG A STREET PACKED WITH PEOPLE." + }, + { + "absoluteDegree": 136, + "sign": "Leo", + "degreeInSign": 16, + "phrase": "BRILLIANT SUNSHINE JUST AFTER A STORM." + }, + { + "absoluteDegree": 137, + "sign": "Leo", + "degreeInSign": 17, + "phrase": "VOLUNTEER CHURCH CHOIR MAKES SOCIAL EVENT OF REHEARSAL." + }, + { + "absoluteDegree": 138, + "sign": "Leo", + "degreeInSign": 18, + "phrase": "A CHEMIST CONDUCTS AN EXPERIMENT FOR HIS STUDENTS." + }, + { + "absoluteDegree": 139, + "sign": "Leo", + "degreeInSign": 19, + "phrase": "A HOUSEBOAT PARTY." + }, + { + "absoluteDegree": 140, + "sign": "Leo", + "degreeInSign": 20, + "phrase": "AMERICAN INDIANS PERFORM A RITUAL TO THE SUN." + }, + { + "absoluteDegree": 141, + "sign": "Leo", + "degreeInSign": 21, + "phrase": "INTOXICATED CHICKENS DIZZILY FLAP THEIR WINGS TRYING TO FLY." + }, + { + "absoluteDegree": 142, + "sign": "Leo", + "degreeInSign": 22, + "phrase": "A CARRIER PIGEON FULFILLING ITS MISSION." + }, + { + "absoluteDegree": 143, + "sign": "Leo", + "degreeInSign": 23, + "phrase": "A BAREBACK RIDER IN A CIRCUS DISPLAYS HER DANGEROUS SKILL." + }, + { + "absoluteDegree": 144, + "sign": "Leo", + "degreeInSign": 24, + "phrase": "TOTALLY CONCENTRATED UPON INNER SPIRITUAL ATTAINMENT, A MAN IS SITTING IN A STATE OF COMPLETE NEGLECT OF HIS BODY." + }, + { + "absoluteDegree": 145, + "sign": "Leo", + "degreeInSign": 25, + "phrase": "A LARGE CAMEL CROSSING A VAST AND FORBIDDING DESERT." + }, + { + "absoluteDegree": 146, + "sign": "Leo", + "degreeInSign": 26, + "phrase": "AFTER A HEAVY STORM, A RAINBOW." + }, + { + "absoluteDegree": 147, + "sign": "Leo", + "degreeInSign": 27, + "phrase": "DAYBREAK - THE LUMINESCENCE OF DAWN IN THE EASTERN SKY." + }, + { + "absoluteDegree": 148, + "sign": "Leo", + "degreeInSign": 28, + "phrase": "MANY LITTLE BIRDS ON THE LIMB OF A LARGE TREE." + }, + { + "absoluteDegree": 149, + "sign": "Leo", + "degreeInSign": 29, + "phrase": "A MERMAID EMERGES FROM THE OCEAN READY FOR REBIRTH IN HUMAN FORM." + }, + { + "absoluteDegree": 150, + "sign": "Leo", + "degreeInSign": 30, + "phrase": "AN UNSEALED LETTER." + }, + { + "absoluteDegree": 151, + "sign": "Virgo", + "degreeInSign": 1, + "phrase": "IN A PORTRAIT THE BEST OF A MAN'S FEATURES AND TRAITS ARE IDEALIZED." + }, + { + "absoluteDegree": 152, + "sign": "Virgo", + "degreeInSign": 2, + "phrase": "A LARGE WHITE CROSS-DOMINATING THE LANDSCAPE-STANDS ALONE ON TOP OF A HIGH HILL." + }, + { + "absoluteDegree": 153, + "sign": "Virgo", + "degreeInSign": 3, + "phrase": "TWO GUARDIAN ANGELS BRINGING PROTECTION." + }, + { + "absoluteDegree": 154, + "sign": "Virgo", + "degreeInSign": 4, + "phrase": "BLACK AND WHITE CHILDREN PLAYING HAPPILY TOGETHER." + }, + { + "absoluteDegree": 155, + "sign": "Virgo", + "degreeInSign": 5, + "phrase": "A MAN BECOMING AWARE OF NATURE SPIRITS AND NORMALLY UNSEEN SPIRITUAL ENERGIES." + }, + { + "absoluteDegree": 156, + "sign": "Virgo", + "degreeInSign": 6, + "phrase": "A MERRY-GO-ROUND." + }, + { + "absoluteDegree": 157, + "sign": "Virgo", + "degreeInSign": 7, + "phrase": "A HAREM." + }, + { + "absoluteDegree": 158, + "sign": "Virgo", + "degreeInSign": 8, + "phrase": "A GIRL TAKES HER FIRST DANCING INSTRUCTION." + }, + { + "absoluteDegree": 159, + "sign": "Virgo", + "degreeInSign": 9, + "phrase": "A EXPRESSIONIST PAINTER MAKING A FUTURISTIC DRAWING." + }, + { + "absoluteDegree": 160, + "sign": "Virgo", + "degreeInSign": 10, + "phrase": "TWO HEADS LOOKING OUT AND BEYOND THE SHADOWS." + }, + { + "absoluteDegree": 161, + "sign": "Virgo", + "degreeInSign": 11, + "phrase": "A BOY MOULDED IN HIS MOTHER'S ASPIRATIONS FOR HIM." + }, + { + "absoluteDegree": 162, + "sign": "Virgo", + "degreeInSign": 12, + "phrase": "A BRIDE WITH HER VEIL SNATCHED AWAY." + }, + { + "absoluteDegree": 163, + "sign": "Virgo", + "degreeInSign": 13, + "phrase": "A POWERFUL STATESMAN OVERCOMES A STATE OF POLITICAL HYSTERIA." + }, + { + "absoluteDegree": 164, + "sign": "Virgo", + "degreeInSign": 14, + "phrase": "A FAMILY TREE." + }, + { + "absoluteDegree": 165, + "sign": "Virgo", + "degreeInSign": 15, + "phrase": "A FINE LACE ORNAMENTAL HANDKERCHIEF." + }, + { + "absoluteDegree": 166, + "sign": "Virgo", + "degreeInSign": 16, + "phrase": "CHILDREN CROWD AROUND THE ORANG-UTANG CAGE IN A ZOO." + }, + { + "absoluteDegree": 167, + "sign": "Virgo", + "degreeInSign": 17, + "phrase": "A VOLCANIC ERUPTION." + }, + { + "absoluteDegree": 168, + "sign": "Virgo", + "degreeInSign": 18, + "phrase": "TWO GIRLS PLAYING WITH A OUIJA BOARD." + }, + { + "absoluteDegree": 169, + "sign": "Virgo", + "degreeInSign": 19, + "phrase": "A SWIMMING RACE." + }, + { + "absoluteDegree": 170, + "sign": "Virgo", + "degreeInSign": 20, + "phrase": "A CARAVAN OF CARS HEADED FOR PROMISED LANDS." + }, + { + "absoluteDegree": 171, + "sign": "Virgo", + "degreeInSign": 21, + "phrase": "A GIRL'S BASKETBALL TEAM." + }, + { + "absoluteDegree": 172, + "sign": "Virgo", + "degreeInSign": 22, + "phrase": "A ROYAL COAT OF ARMS ENRICHED WITH PRECIOUS STONES." + }, + { + "absoluteDegree": 173, + "sign": "Virgo", + "degreeInSign": 23, + "phrase": "A LION-TAMER RUSHES FEARLESSLY INTO THE CIRCUS ARENA." + }, + { + "absoluteDegree": 174, + "sign": "Virgo", + "degreeInSign": 24, + "phrase": "MARY AND HER WHITE LAMB." + }, + { + "absoluteDegree": 175, + "sign": "Virgo", + "degreeInSign": 25, + "phrase": "A FLAG AT HALF-MAST IN FRONT OF A PUBLIC BUILDING." + }, + { + "absoluteDegree": 176, + "sign": "Virgo", + "degreeInSign": 26, + "phrase": "A BOY WITH A CENSER SERVES NEAR THE PRIEST AT THE ALTAR." + }, + { + "absoluteDegree": 177, + "sign": "Virgo", + "degreeInSign": 27, + "phrase": "ARISTOCRATIC ELDERLY LADIES DRINKING AFTERNOON TEA IN A WEALTHY HOME." + }, + { + "absoluteDegree": 178, + "sign": "Virgo", + "degreeInSign": 28, + "phrase": "A BALD-HEADED MAN WHO HAS SEIZED POWER." + }, + { + "absoluteDegree": 179, + "sign": "Virgo", + "degreeInSign": 29, + "phrase": "A MAN GAINING SECRET KNOWLEDGE FROM AN ANCIENT SCROLL HE IS READING." + }, + { + "absoluteDegree": 180, + "sign": "Virgo", + "degreeInSign": 30, + "phrase": "HAVING AN URGENT TASK TO COMPLETE, A MAN DOESN'T LOOK TO ANY DISTRACTIONS." + }, + { + "absoluteDegree": 181, + "sign": "Libra", + "degreeInSign": 1, + "phrase": "A BUTTERFLY PRESERVED AND MADE PERFECT WITH A DART THROUGH IT." + }, + { + "absoluteDegree": 182, + "sign": "Libra", + "degreeInSign": 2, + "phrase": "THE LIGHT OF THE SIXTH RACE TRANSMUTED TO THE SEVENTH." + }, + { + "absoluteDegree": 183, + "sign": "Libra", + "degreeInSign": 3, + "phrase": "THE DAWN OF A NEW DAY REVEALS EVERYTHING CHANGED." + }, + { + "absoluteDegree": 184, + "sign": "Libra", + "degreeInSign": 4, + "phrase": "A GROUP OF YOUNG PEOPLE SIT IN SPIRITUAL COMMUNION AROUND A CAMPFIRE." + }, + { + "absoluteDegree": 185, + "sign": "Libra", + "degreeInSign": 5, + "phrase": "A MAN TEACHING THE TRUE INNER KNOWLEDGE OF THE NEW WORLD TO HIS STUDENTS." + }, + { + "absoluteDegree": 186, + "sign": "Libra", + "degreeInSign": 6, + "phrase": "A MAN WATCHES HIS IDEALS TAKING A CONCRETE FORM BEFORE HIS INNER VISION." + }, + { + "absoluteDegree": 187, + "sign": "Libra", + "degreeInSign": 7, + "phrase": "A WOMAN FEEDING CHICKENS AND PROTECTING THEM FROM THE HAWKS." + }, + { + "absoluteDegree": 188, + "sign": "Libra", + "degreeInSign": 8, + "phrase": "A BLAZING FIREPLACE IN A DESERTED HOME." + }, + { + "absoluteDegree": 189, + "sign": "Libra", + "degreeInSign": 9, + "phrase": "THREE OLD MASTERS HANGING IN A SPECIAL ROOM IN AN ART GALLERY." + }, + { + "absoluteDegree": 190, + "sign": "Libra", + "degreeInSign": 10, + "phrase": "A CANOE APPROACHING SAFETY THROUGH DANGEROUS WATERS." + }, + { + "absoluteDegree": 191, + "sign": "Libra", + "degreeInSign": 11, + "phrase": "A PROFESSOR PEERING OVER HIS GLASSES AT HIS STUDENTS." + }, + { + "absoluteDegree": 192, + "sign": "Libra", + "degreeInSign": 12, + "phrase": "MINERS ARE EMERGING FROM A DEEP COAL MINE." + }, + { + "absoluteDegree": 193, + "sign": "Libra", + "degreeInSign": 13, + "phrase": "CHILDREN BLOWING SOAP BUBBLES." + }, + { + "absoluteDegree": 194, + "sign": "Libra", + "degreeInSign": 14, + "phrase": "IN THE HEAT OF THE NOON, A MAN TAKES A SIESTA." + }, + { + "absoluteDegree": 195, + "sign": "Libra", + "degreeInSign": 15, + "phrase": "CIRCULAR PATHS." + }, + { + "absoluteDegree": 196, + "sign": "Libra", + "degreeInSign": 16, + "phrase": "AFTER A STORM, A BOAT LANDING STANDS IN NEED OF RECONSTRUCTION." + }, + { + "absoluteDegree": 197, + "sign": "Libra", + "degreeInSign": 17, + "phrase": "A RETIRED SEA CAPTAIN WATCHES SHIPS ENTERING AND LEAVING THE HARBOUR." + }, + { + "absoluteDegree": 198, + "sign": "Libra", + "degreeInSign": 18, + "phrase": "TWO MEN PLACED UNDER ARREST." + }, + { + "absoluteDegree": 199, + "sign": "Libra", + "degreeInSign": 19, + "phrase": "A GANG OF ROBBERS IN HIDING." + }, + { + "absoluteDegree": 200, + "sign": "Libra", + "degreeInSign": 20, + "phrase": "A JEWISH RABBI PERFORMING HIS DUTIES." + }, + { + "absoluteDegree": 201, + "sign": "Libra", + "degreeInSign": 21, + "phrase": "A CROWD UPON A BEACH." + }, + { + "absoluteDegree": 202, + "sign": "Libra", + "degreeInSign": 22, + "phrase": "A CHILD GIVING BIRDS A DRINK AT A FOUNTAIN." + }, + { + "absoluteDegree": 203, + "sign": "Libra", + "degreeInSign": 23, + "phrase": "CHANTICLEER'S VOICE HERALDS THE RISING SUN WITH EXUBERANT TONES." + }, + { + "absoluteDegree": 204, + "sign": "Libra", + "degreeInSign": 24, + "phrase": "A THIRD WING ON THE LEFT SIDE OF A BUTTERFLY." + }, + { + "absoluteDegree": 205, + "sign": "Libra", + "degreeInSign": 25, + "phrase": "THE SIGHT OF AN AUTUMN LEAF BRINGS TO A PILGRIM THE SUDDEN REVELATION OF THE MYSTERY OF LIFE AND DEATH." + }, + { + "absoluteDegree": 206, + "sign": "Libra", + "degreeInSign": 26, + "phrase": "AN EAGLE AND A LARGE WHITE DOVE TURNING INTO EACH OTHER." + }, + { + "absoluteDegree": 207, + "sign": "Libra", + "degreeInSign": 27, + "phrase": "AN AIRPLANE SAILS, HIGH IN THE CLEAR SKY." + }, + { + "absoluteDegree": 208, + "sign": "Libra", + "degreeInSign": 28, + "phrase": "A MAN IN DEEP GLOOM. UNNOTICED, ANGELS COME TO HIS HELP." + }, + { + "absoluteDegree": 209, + "sign": "Libra", + "degreeInSign": 29, + "phrase": "MANKIND'S VAST ENDURING EFFORT TO REACH FOR KNOWLEDGE TRANSFERABLE FROM GENERATION TO GENERATION. KNOWLEDGE." + }, + { + "absoluteDegree": 210, + "sign": "Libra", + "degreeInSign": 30, + "phrase": "THREE MOUNDS OF KNOWLEDGE ON A PHILOSOPHER'S HEAD." + }, + { + "absoluteDegree": 211, + "sign": "Scorpio", + "degreeInSign": 1, + "phrase": "A SIGHT-SEEING BUS FILLED WITH TOURISTS." + }, + { + "absoluteDegree": 212, + "sign": "Scorpio", + "degreeInSign": 2, + "phrase": "A BROKEN BOTTLE AND SPILLED PERFUME." + }, + { + "absoluteDegree": 213, + "sign": "Scorpio", + "degreeInSign": 3, + "phrase": "NEIGHBOURS HELP IN A HOUSE- RAISING PARTY IN A SMALL VILLAGE." + }, + { + "absoluteDegree": 214, + "sign": "Scorpio", + "degreeInSign": 4, + "phrase": "A YOUTH HOLDING A LIGHTED CANDLE IN A DEVOTIONAL RITUAL." + }, + { + "absoluteDegree": 215, + "sign": "Scorpio", + "degreeInSign": 5, + "phrase": "A MASSIVE, ROCKY SHORE RESISTS THE POUNDING OF THE SEA." + }, + { + "absoluteDegree": 216, + "sign": "Scorpio", + "degreeInSign": 6, + "phrase": "A GOLD RUSH TEARS MEN AWAY FROM THEIR NATIVE SOIL." + }, + { + "absoluteDegree": 217, + "sign": "Scorpio", + "degreeInSign": 7, + "phrase": "DEEP-SEA DIVERS." + }, + { + "absoluteDegree": 218, + "sign": "Scorpio", + "degreeInSign": 8, + "phrase": "THE MOON SHINING ACROSS A LAKE." + }, + { + "absoluteDegree": 219, + "sign": "Scorpio", + "degreeInSign": 9, + "phrase": "A DENTIST AT WORK." + }, + { + "absoluteDegree": 220, + "sign": "Scorpio", + "degreeInSign": 10, + "phrase": "A FELLOWSHIP SUPPER REUNITES OLD COMRADES." + }, + { + "absoluteDegree": 221, + "sign": "Scorpio", + "degreeInSign": 11, + "phrase": "A DROWNING MAN IS BEING RESCUED." + }, + { + "absoluteDegree": 222, + "sign": "Scorpio", + "degreeInSign": 12, + "phrase": "AN OFFICIAL EMBASSY BALL." + }, + { + "absoluteDegree": 223, + "sign": "Scorpio", + "degreeInSign": 13, + "phrase": "AN INVENTOR PERFORMS A LABORATORY EXPERIMENT." + }, + { + "absoluteDegree": 224, + "sign": "Scorpio", + "degreeInSign": 14, + "phrase": "TELEPHONE LINEMEN AT WORK INSTALLING NEW CONNECTIONS." + }, + { + "absoluteDegree": 225, + "sign": "Scorpio", + "degreeInSign": 15, + "phrase": "CHILDREN PLAYING AROUND FIVE MOUNDS OF SAND." + }, + { + "absoluteDegree": 226, + "sign": "Scorpio", + "degreeInSign": 16, + "phrase": "A GIRL'S FACE BREAKING INTO A SMILE." + }, + { + "absoluteDegree": 227, + "sign": "Scorpio", + "degreeInSign": 17, + "phrase": "A WOMAN, FILLED WITH HER OWN SPIRIT, IS THE FATHER OF HER OWN CHILD." + }, + { + "absoluteDegree": 228, + "sign": "Scorpio", + "degreeInSign": 18, + "phrase": "A PATH THROUGH WOODS RICH IN AUTUMN COLORING." + }, + { + "absoluteDegree": 229, + "sign": "Scorpio", + "degreeInSign": 19, + "phrase": "A PARROT LISTENING AND THEN TALKING, REPEATS A CONVERSATION HE HAS OVERHEARD." + }, + { + "absoluteDegree": 230, + "sign": "Scorpio", + "degreeInSign": 20, + "phrase": "A WOMAN DRAWING ASIDE TWO DARK CURTAINS THAT CLOSED THE ENTRANCE TO A SACRED PATHWAY." + }, + { + "absoluteDegree": 231, + "sign": "Scorpio", + "degreeInSign": 21, + "phrase": "OBEYING HIS CONSCIENCE, A SOLDIER RESISTS ORDERS." + }, + { + "absoluteDegree": 232, + "sign": "Scorpio", + "degreeInSign": 22, + "phrase": "HUNTERS SHOOTING WILD DUCKS." + }, + { + "absoluteDegree": 233, + "sign": "Scorpio", + "degreeInSign": 23, + "phrase": "A RABBIT METAMORPHOSED INTO A FAIRY (NATURE SPIRIT)." + }, + { + "absoluteDegree": 234, + "sign": "Scorpio", + "degreeInSign": 24, + "phrase": "CROWDS COMING DOWN THE MOUNTAIN TO LISTEN TO ONE INSPIRED MAN." + }, + { + "absoluteDegree": 235, + "sign": "Scorpio", + "degreeInSign": 25, + "phrase": "AN X RAY PHOTOGRAPH." + }, + { + "absoluteDegree": 236, + "sign": "Scorpio", + "degreeInSign": 26, + "phrase": "INDIANS MAKING CAMP (IN NEW TERRITORY)" + }, + { + "absoluteDegree": 237, + "sign": "Scorpio", + "degreeInSign": 27, + "phrase": "A MILITARY BAND MARCHES NOISILY ON THROUGH THE CITY STREETS." + }, + { + "absoluteDegree": 238, + "sign": "Scorpio", + "degreeInSign": 28, + "phrase": "THE KING OF THE FAIRIES APPROACHING HIS DOMAIN." + }, + { + "absoluteDegree": 239, + "sign": "Scorpio", + "degreeInSign": 29, + "phrase": "AN INDIAN WOMAN PLEADING TO THE CHIEF FOR THE LIVES OF HER CHILDREN." + }, + { + "absoluteDegree": 240, + "sign": "Scorpio", + "degreeInSign": 30, + "phrase": "CHILDREN IN HALLOWEEN COSTUMES INDULGING IN VARIOUS PRANKS." + }, + { + "absoluteDegree": 241, + "sign": "Sagittarius", + "degreeInSign": 1, + "phrase": "RETIRED ARMY VETERANS GATHER TO REAWAKEN OLD MEMORIES." + }, + { + "absoluteDegree": 242, + "sign": "Sagittarius", + "degreeInSign": 2, + "phrase": "THE OCEAN COVERED WITH WHITECAPS." + }, + { + "absoluteDegree": 243, + "sign": "Sagittarius", + "degreeInSign": 3, + "phrase": "TWO MEN PLAYING CHESS." + }, + { + "absoluteDegree": 244, + "sign": "Sagittarius", + "degreeInSign": 4, + "phrase": "A LITTLE CHILD LEARNING TO WALK." + }, + { + "absoluteDegree": 245, + "sign": "Sagittarius", + "degreeInSign": 5, + "phrase": "AN OLD OWL UP IN A TREE." + }, + { + "absoluteDegree": 246, + "sign": "Sagittarius", + "degreeInSign": 6, + "phrase": "A GAME OF CRICKET." + }, + { + "absoluteDegree": 247, + "sign": "Sagittarius", + "degreeInSign": 7, + "phrase": "CUPID KNOCKING AT THE DOOR OF A HUMAN HEART." + }, + { + "absoluteDegree": 248, + "sign": "Sagittarius", + "degreeInSign": 8, + "phrase": "DEEP WITHIN THE DEPTHS OF THE EARTH, NEW ELEMENTS ARE BEING FORMED." + }, + { + "absoluteDegree": 249, + "sign": "Sagittarius", + "degreeInSign": 9, + "phrase": "A MOTHER LEADS HER SMALL CHILD STEP BY STEP UP THE STAIRS." + }, + { + "absoluteDegree": 250, + "sign": "Sagittarius", + "degreeInSign": 10, + "phrase": "A THEATRICAL REPRESENTATION OF A GOLDEN HAIRED 'GODDESS OF OPPORTUNITY'." + }, + { + "absoluteDegree": 251, + "sign": "Sagittarius", + "degreeInSign": 11, + "phrase": "THE LAMP OF PHYSICAL ENLIGHTENMENT AT THE LEFT TEMPLE." + }, + { + "absoluteDegree": 252, + "sign": "Sagittarius", + "degreeInSign": 12, + "phrase": "A FLAG THAT TURNS INTO AN EAGLE THAT CROWS." + }, + { + "absoluteDegree": 253, + "sign": "Sagittarius", + "degreeInSign": 13, + "phrase": "A WIDOW'S PAST IS BROUGHT TO LIGHT." + }, + { + "absoluteDegree": 254, + "sign": "Sagittarius", + "degreeInSign": 14, + "phrase": "THE PYRAMIDS AND THE SPHINX." + }, + { + "absoluteDegree": 255, + "sign": "Sagittarius", + "degreeInSign": 15, + "phrase": "THE GROUND HOG LOOKING FOR ITS SHADOW ON GROUND HOG DAY." + }, + { + "absoluteDegree": 256, + "sign": "Sagittarius", + "degreeInSign": 16, + "phrase": "SEA GULLS FLY AROUND A SHIP LOOKING FOR FOOD." + }, + { + "absoluteDegree": 257, + "sign": "Sagittarius", + "degreeInSign": 17, + "phrase": "AN EASTER SUNRISE SERVICE." + }, + { + "absoluteDegree": 258, + "sign": "Sagittarius", + "degreeInSign": 18, + "phrase": "TINY CHILDREN IN SUNBONNETS." + }, + { + "absoluteDegree": 259, + "sign": "Sagittarius", + "degreeInSign": 19, + "phrase": "PELICANS, DISTURBED BY THE GARBAGE OF PEOPLE MOVE THEIR YOUNG TO A NEW HABITAT." + }, + { + "absoluteDegree": 260, + "sign": "Sagittarius", + "degreeInSign": 20, + "phrase": "IN WINTER PEOPLE CUTTING ICE FROM A FROZEN POND, FOR SUMMER USE." + }, + { + "absoluteDegree": 261, + "sign": "Sagittarius", + "degreeInSign": 21, + "phrase": "A CHILD AND A DOG WEARING BORROWED EYEGLASSES." + }, + { + "absoluteDegree": 262, + "sign": "Sagittarius", + "degreeInSign": 22, + "phrase": "A CHINESE LAUNDRY." + }, + { + "absoluteDegree": 263, + "sign": "Sagittarius", + "degreeInSign": 23, + "phrase": "IMMIGRANTS ENTERING A NEW COUNTRY." + }, + { + "absoluteDegree": 264, + "sign": "Sagittarius", + "degreeInSign": 24, + "phrase": "A BLUEBIRD STANDING AT THE DOOR OF THE HOUSE." + }, + { + "absoluteDegree": 265, + "sign": "Sagittarius", + "degreeInSign": 25, + "phrase": "A CHUBBY BOY ON A HOBBYHORSE." + }, + { + "absoluteDegree": 266, + "sign": "Sagittarius", + "degreeInSign": 26, + "phrase": "A FLAG-BEARER IN A BATTLE." + }, + { + "absoluteDegree": 267, + "sign": "Sagittarius", + "degreeInSign": 27, + "phrase": "THE SCULPTOR'S VISION IS TAKING FORM." + }, + { + "absoluteDegree": 268, + "sign": "Sagittarius", + "degreeInSign": 28, + "phrase": "AN OLD BRIDGE OVER A BEAUTIFUL STREAM IN CONSTANT USE." + }, + { + "absoluteDegree": 269, + "sign": "Sagittarius", + "degreeInSign": 29, + "phrase": "A FAT BOY MOWING THE LAWN." + }, + { + "absoluteDegree": 270, + "sign": "Sagittarius", + "degreeInSign": 30, + "phrase": "THE POPE BLESSING THE FAITHFUL." + }, + { + "absoluteDegree": 271, + "sign": "Capricorn", + "degreeInSign": 1, + "phrase": "AN INDIAN CHIEF CLAIMS POWER FROM THE ASSEMBLED TRIBE." + }, + { + "absoluteDegree": 272, + "sign": "Capricorn", + "degreeInSign": 2, + "phrase": "THREE STAINED-GLASS WINDOWS IN A GOTHIC CHURCH, ONE DAMAGED BY WAR." + }, + { + "absoluteDegree": 273, + "sign": "Capricorn", + "degreeInSign": 3, + "phrase": "THE HUMAN SOUL, IN ITS EAGERNESS FOR NEW EXPERIENCES, SEEKS EMBODIMENT." + }, + { + "absoluteDegree": 274, + "sign": "Capricorn", + "degreeInSign": 4, + "phrase": "A GROUP OF PEOPLE ENTERING A LARGE CANOE FOR A JOURNEY BY WATER." + }, + { + "absoluteDegree": 275, + "sign": "Capricorn", + "degreeInSign": 5, + "phrase": "INDIANS - SOME ROWING A CANOE AND OTHERS DANCING A WAR DANCE IN IT." + }, + { + "absoluteDegree": 276, + "sign": "Capricorn", + "degreeInSign": 6, + "phrase": "TEN LOGS LIE UNDER AN ARCHWAY LEADING TO DARKER WOODS." + }, + { + "absoluteDegree": 277, + "sign": "Capricorn", + "degreeInSign": 7, + "phrase": "A VEILED PROPHET SPEAKS, SEIZED BY THE POWER OF A GOD." + }, + { + "absoluteDegree": 278, + "sign": "Capricorn", + "degreeInSign": 8, + "phrase": "BIRDS IN THE HOUSE SINGING HAPPILY." + }, + { + "absoluteDegree": 279, + "sign": "Capricorn", + "degreeInSign": 9, + "phrase": "AN ANGEL CARRYING A HARP." + }, + { + "absoluteDegree": 280, + "sign": "Capricorn", + "degreeInSign": 10, + "phrase": "AN ALBATROSS FEEDING FROM THE HAND OF A SAILOR." + }, + { + "absoluteDegree": 281, + "sign": "Capricorn", + "degreeInSign": 11, + "phrase": "PHEASANTS DISPLAY THEIR BRILLIANT COLORS ON A PRIVATE ESTATE." + }, + { + "absoluteDegree": 282, + "sign": "Capricorn", + "degreeInSign": 12, + "phrase": "A STUDENT OF NATURE LECTURING REVEALING LITTLE-KNOWN ASPECTS OF LIFE." + }, + { + "absoluteDegree": 283, + "sign": "Capricorn", + "degreeInSign": 13, + "phrase": "A FIRE WORSHIPPER MEDITATES ON THE ULTIMATE REALITIES OF EXISTENCE." + }, + { + "absoluteDegree": 284, + "sign": "Capricorn", + "degreeInSign": 14, + "phrase": "AN ANCIENT BAS-RELIEF CARVED IN GRANITE REMAINS A WITNESS TO A LONG- FORGOTTEN CULTURE." + }, + { + "absoluteDegree": 285, + "sign": "Capricorn", + "degreeInSign": 15, + "phrase": "IN A HOSPITAL, THE CHILDREN'S WARD IS FILLED WITH TOYS." + }, + { + "absoluteDegree": 286, + "sign": "Capricorn", + "degreeInSign": 16, + "phrase": "SCHOOL GROUNDS FILLED WITH BOYS AND GIRLS IN GYMNASIUM SUITS." + }, + { + "absoluteDegree": 287, + "sign": "Capricorn", + "degreeInSign": 17, + "phrase": "A GIRL SURREPTITIOUSLY BATHING IN THE NUDE." + }, + { + "absoluteDegree": 288, + "sign": "Capricorn", + "degreeInSign": 18, + "phrase": "THE UNION JACK FLIES FROM A NEW BRITISH WARSHIP." + }, + { + "absoluteDegree": 289, + "sign": "Capricorn", + "degreeInSign": 19, + "phrase": "A CHILD OF ABOUT FIVE CARRYING A HUGE SHOPPING BAG FILLED WITH GROCERIES." + }, + { + "absoluteDegree": 290, + "sign": "Capricorn", + "degreeInSign": 20, + "phrase": "A HIDDEN CHOIR SINGING DURING A RELIGIOUS SERVICE." + }, + { + "absoluteDegree": 291, + "sign": "Capricorn", + "degreeInSign": 21, + "phrase": "A RELAY RACE." + }, + { + "absoluteDegree": 292, + "sign": "Capricorn", + "degreeInSign": 22, + "phrase": "A GENERAL ACCEPTING DEFEAT GRACEFULLY." + }, + { + "absoluteDegree": 293, + "sign": "Capricorn", + "degreeInSign": 23, + "phrase": "A SOLDIER RECEIVING TWO AWARDS FOR BRAVERY IN COMBAT." + }, + { + "absoluteDegree": 294, + "sign": "Capricorn", + "degreeInSign": 24, + "phrase": "A WOMAN ENTERING A CONVENT." + }, + { + "absoluteDegree": 295, + "sign": "Capricorn", + "degreeInSign": 25, + "phrase": "AN ORIENTAL RUG DEALER IN A STORE FILLED WITH PRECIOUS ORNAMENTAL RUGS." + }, + { + "absoluteDegree": 296, + "sign": "Capricorn", + "degreeInSign": 26, + "phrase": "A NATURE SPIRIT DANCING IN THE MIST OF A WATERFALL." + }, + { + "absoluteDegree": 297, + "sign": "Capricorn", + "degreeInSign": 27, + "phrase": "A MOUNTAIN PILGRIMAGE." + }, + { + "absoluteDegree": 298, + "sign": "Capricorn", + "degreeInSign": 28, + "phrase": "A LARGE AVIARY." + }, + { + "absoluteDegree": 299, + "sign": "Capricorn", + "degreeInSign": 29, + "phrase": "A WOMAN READING TEA LEAVES." + }, + { + "absoluteDegree": 300, + "sign": "Capricorn", + "degreeInSign": 30, + "phrase": "DIRECTORS OF A LARGE FIRM MEET IN SECRET CONFERENCE." + }, + { + "absoluteDegree": 301, + "sign": "Aquarius", + "degreeInSign": 1, + "phrase": "AN OLD ADOBE MISSION." + }, + { + "absoluteDegree": 302, + "sign": "Aquarius", + "degreeInSign": 2, + "phrase": "AN UNEXPECTED THUNDERSTORM." + }, + { + "absoluteDegree": 303, + "sign": "Aquarius", + "degreeInSign": 3, + "phrase": "A DESERTER FROM THE NAVY." + }, + { + "absoluteDegree": 304, + "sign": "Aquarius", + "degreeInSign": 4, + "phrase": "A HINDU HEALER." + }, + { + "absoluteDegree": 305, + "sign": "Aquarius", + "degreeInSign": 5, + "phrase": "A COUNCIL OF ANCESTORS." + }, + { + "absoluteDegree": 306, + "sign": "Aquarius", + "degreeInSign": 6, + "phrase": "A MASKED FIGURE PERFORMS RITUALISTIC ACTS IN A MYSTERY PLAY." + }, + { + "absoluteDegree": 307, + "sign": "Aquarius", + "degreeInSign": 7, + "phrase": "A CHILD BORN OUT OF AN EGGSHELL." + }, + { + "absoluteDegree": 308, + "sign": "Aquarius", + "degreeInSign": 8, + "phrase": "BEAUTIFULLY GOWNED WAX FIGURES ON DISPLAY." + }, + { + "absoluteDegree": 309, + "sign": "Aquarius", + "degreeInSign": 9, + "phrase": "A FLAG IS SEEN TURNING INTO AN EAGLE." + }, + { + "absoluteDegree": 310, + "sign": "Aquarius", + "degreeInSign": 10, + "phrase": "A POPULARITY THAT PROVES TO BE FLEETING." + }, + { + "absoluteDegree": 311, + "sign": "Aquarius", + "degreeInSign": 11, + "phrase": "DURING A SILENT HOUR, A MAN RECEIVES A NEW INSPIRATION WHICH MAY CHANGE HIS LIFE." + }, + { + "absoluteDegree": 312, + "sign": "Aquarius", + "degreeInSign": 12, + "phrase": "PEOPLE ON A VAST STAIRCASE, GRADUATED UPWARDS." + }, + { + "absoluteDegree": 313, + "sign": "Aquarius", + "degreeInSign": 13, + "phrase": "A BAROMETER." + }, + { + "absoluteDegree": 314, + "sign": "Aquarius", + "degreeInSign": 14, + "phrase": "A TRAIN ENTERING A TUNNEL." + }, + { + "absoluteDegree": 315, + "sign": "Aquarius", + "degreeInSign": 15, + "phrase": "TWO LOVEBIRDS SITTING ON A FENCE AND SINGING HAPPILY." + }, + { + "absoluteDegree": 316, + "sign": "Aquarius", + "degreeInSign": 16, + "phrase": "A BIG-BUSINESSMAN AT HIS DESK." + }, + { + "absoluteDegree": 317, + "sign": "Aquarius", + "degreeInSign": 17, + "phrase": "A WATCHDOG STANDING GUARD, PROTECTING HIS MASTER AND HIS POSSESSIONS." + }, + { + "absoluteDegree": 318, + "sign": "Aquarius", + "degreeInSign": 18, + "phrase": "A MAN BEING UNMASKED AT A MASQUERADE." + }, + { + "absoluteDegree": 319, + "sign": "Aquarius", + "degreeInSign": 19, + "phrase": "A FOREST FIRE QUENCHED." + }, + { + "absoluteDegree": 320, + "sign": "Aquarius", + "degreeInSign": 20, + "phrase": "A LARGE WHITE DOVE BEARING A MESSAGE." + }, + { + "absoluteDegree": 321, + "sign": "Aquarius", + "degreeInSign": 21, + "phrase": "A WOMAN DISAPPOINTED AND DISILLUSIONED, COURAGEOUSLY FACING A SEEMINGLY EMPTY LIFE." + }, + { + "absoluteDegree": 322, + "sign": "Aquarius", + "degreeInSign": 22, + "phrase": "A RUG PLACED ON A FLOOR FOR CHILDREN TO PLAY ON." + }, + { + "absoluteDegree": 323, + "sign": "Aquarius", + "degreeInSign": 23, + "phrase": "A BIG BEAR SITTING DOWN AND WAVING ALL ITS PAWS." + }, + { + "absoluteDegree": 324, + "sign": "Aquarius", + "degreeInSign": 24, + "phrase": "A MAN TURNING HIS BACK ON HIS PASSIONS TEACHES DEEP WISDOM FROM HIS EXPERIENCE." + }, + { + "absoluteDegree": 325, + "sign": "Aquarius", + "degreeInSign": 25, + "phrase": "A BUTTERFLY WITH THE RIGHT WING MORE PERFECTLY FORMED." + }, + { + "absoluteDegree": 326, + "sign": "Aquarius", + "degreeInSign": 26, + "phrase": "A GARAGE MAN TESTING A CAR'S BATTERY WITH A HYDROMETER." + }, + { + "absoluteDegree": 327, + "sign": "Aquarius", + "degreeInSign": 27, + "phrase": "AN ANCIENT POTTERY BOWL FILLED WITH FRESH VIOLETS." + }, + { + "absoluteDegree": 328, + "sign": "Aquarius", + "degreeInSign": 28, + "phrase": "A TREE FELLED AND SAWED TO ENSURE A SUPPLY OF WOOD FOR THE WINTER." + }, + { + "absoluteDegree": 329, + "sign": "Aquarius", + "degreeInSign": 29, + "phrase": "BUTTERFLY EMERGING FROM A CHRYSALIS." + }, + { + "absoluteDegree": 330, + "sign": "Aquarius", + "degreeInSign": 30, + "phrase": "MOON-LIT FIELDS, ONCE BABYLON, ARE BLOOMING WHITE." + }, + { + "absoluteDegree": 331, + "sign": "Pisces", + "degreeInSign": 1, + "phrase": "A CROWDED PUBLIC MARKET PLACE." + }, + { + "absoluteDegree": 332, + "sign": "Pisces", + "degreeInSign": 2, + "phrase": "A SQUIRREL HIDING FROM HUNTERS." + }, + { + "absoluteDegree": 333, + "sign": "Pisces", + "degreeInSign": 3, + "phrase": "A PETRIFIED FOREST." + }, + { + "absoluteDegree": 334, + "sign": "Pisces", + "degreeInSign": 4, + "phrase": "HEAVY CAR TRAFFIC ON A NARROW ISTHMUS LINKING TWO SEASIDE RESORTS." + }, + { + "absoluteDegree": 335, + "sign": "Pisces", + "degreeInSign": 5, + "phrase": "A CHURCH BAZAAR." + }, + { + "absoluteDegree": 336, + "sign": "Pisces", + "degreeInSign": 6, + "phrase": "A PARADE OF ARMY OFFICERS IN FULL DRESS." + }, + { + "absoluteDegree": 337, + "sign": "Pisces", + "degreeInSign": 7, + "phrase": "ILLUMINATED BY A SHAFT OF LIGHT, A LARGE CROSS LIES ON ROCKS SURROUNDED BY SEA AND MIST." + }, + { + "absoluteDegree": 338, + "sign": "Pisces", + "degreeInSign": 8, + "phrase": "A GIRL BLOWING A BUGLE." + }, + { + "absoluteDegree": 339, + "sign": "Pisces", + "degreeInSign": 9, + "phrase": "THE RACE BEGINS: INTENT ON OUTDISTANCING HIS RIVALS, A JOCKEY SPURS HIS HORSE TO GREAT SPEED." + }, + { + "absoluteDegree": 340, + "sign": "Pisces", + "degreeInSign": 10, + "phrase": "AN AVIATOR IN THE CLOUDS." + }, + { + "absoluteDegree": 341, + "sign": "Pisces", + "degreeInSign": 11, + "phrase": "MEN TRAVELLING A NARROW PATH, SEEKING ILLUMINATION." + }, + { + "absoluteDegree": 342, + "sign": "Pisces", + "degreeInSign": 12, + "phrase": "AN EXAMINATION OF INITIATES IN THE SANCTUARY OF AN OCCULT BROTHERHOOD." + }, + { + "absoluteDegree": 343, + "sign": "Pisces", + "degreeInSign": 13, + "phrase": "A SWORD, USED IN MANY BATTLES, IN A MUSEUM." + }, + { + "absoluteDegree": 344, + "sign": "Pisces", + "degreeInSign": 14, + "phrase": "A LADY WRAPPED IN FOX FUR." + }, + { + "absoluteDegree": 345, + "sign": "Pisces", + "degreeInSign": 15, + "phrase": "AN OFFICER DRILLING HIS MEN IN A SIMULATED ATTACK." + }, + { + "absoluteDegree": 346, + "sign": "Pisces", + "degreeInSign": 16, + "phrase": "IN A QUITE MOMENT, A CREATIVE INDIVIDUAL EXPERIENCES THE FLOW OF INSPIRATION." + }, + { + "absoluteDegree": 347, + "sign": "Pisces", + "degreeInSign": 17, + "phrase": "AN EASTER PROMENADE." + }, + { + "absoluteDegree": 348, + "sign": "Pisces", + "degreeInSign": 18, + "phrase": "IN A HUGE TENT A FAMOUS REVIVALIST CONDUCTS HIS MEETING WITH A SPECTACULAR PERFORMANCE." + }, + { + "absoluteDegree": 349, + "sign": "Pisces", + "degreeInSign": 19, + "phrase": "A MASTER INSTRUCTING HIS DISCIPLE." + }, + { + "absoluteDegree": 350, + "sign": "Pisces", + "degreeInSign": 20, + "phrase": "A TABLE SET FOR AN EVENING MEAL." + }, + { + "absoluteDegree": 351, + "sign": "Pisces", + "degreeInSign": 21, + "phrase": "A LITTLE WHITE LAMB, A CHILD AND A CHINESE SERVANT." + }, + { + "absoluteDegree": 352, + "sign": "Pisces", + "degreeInSign": 22, + "phrase": "A PROPHET BRINGING DOWN THE NEW LAW FROM MOUNT SINAI." + }, + { + "absoluteDegree": 353, + "sign": "Pisces", + "degreeInSign": 23, + "phrase": "A 'MATERIALIZING MEDIUM' GIVING A SEANCE." + }, + { + "absoluteDegree": 354, + "sign": "Pisces", + "degreeInSign": 24, + "phrase": "AN INHABITED ISLAND." + }, + { + "absoluteDegree": 355, + "sign": "Pisces", + "degreeInSign": 25, + "phrase": "THE PURGING OF THE PRIESTHOOD." + }, + { + "absoluteDegree": 356, + "sign": "Pisces", + "degreeInSign": 26, + "phrase": "A NEW MOON REVEALS THAT IT'S TIME FOR PEOPLE TO GO AHEAD WITH THEIR DIFFERENT PROJECTS." + }, + { + "absoluteDegree": 357, + "sign": "Pisces", + "degreeInSign": 27, + "phrase": "A HARVEST MOON ILLUMINATES THE SKY." + }, + { + "absoluteDegree": 358, + "sign": "Pisces", + "degreeInSign": 28, + "phrase": "A FERTILE GARDEN UNDER THE FULL MOON." + }, + { + "absoluteDegree": 359, + "sign": "Pisces", + "degreeInSign": 29, + "phrase": "LIGHT BREAKING INTO MANY COLORS AS IT PASSES THROUGH A PRISM." + }, + { + "absoluteDegree": 360, + "sign": "Pisces", + "degreeInSign": 30, + "phrase": "A MAJESTIC ROCK FORMATION RESEMBLING A FACE IS IDEALIZED BY A BOY WHO TAKES IT AS HIS IDEAL OF GREATNESS, AND AS HE GROWS UP, BEGINS TO LOOK LIKE IT. (adsbygoogle = window.adsbygoogle || []).push({}); Search in this site Astrology Forums Enter the forums! (adsbygoogle = window.adsbygoogle || []).push({}); Affordable reports from AstrologyWeekly: Natal Chart Report Get a thorough natal chart analysis, the key to discovering and understanding your birth potential Astrology Compatibility Report Understand your relationship and its potential, by ordering this synastry report. Forecast Report for 1 year Discover the main astrological trends in your life over the next year (transits and progressions). Forecast Report day by day Learn about the daily astrological weather triggering your daily life events and states of mind. Relocation Report Understand the astrological potential of moving to different geographical places for personal or business success. --> (adsbygoogle = window.adsbygoogle || []).push({}); Articles Astrology articles Selected articles Article archives Astrology news Interviews Weekly newsletter Horary Astrology Horary astrology William Lilly Special horary More horary Astrology Data Celebrity birthdates Celebrity charts Countries' charts Data archive Online ephemeris Void of Course Moon More information Meditation program Meditation music Astrology Humour Miscellaneous Online Tools Chart Generator World Atlas Sabian Symbols Triple transits Chakra system Sabian Oracle Moon phases Declinations Convertor Zodiac Signs Sun signs Zodiac pictures Zodiacal music Zodiac faces Get creative! Learn Astrology Basic interpretation Astrology glyphs Astrology dictionary Astrology books Astrological Techniques Understanding planets Live Chat Resources Links Planets Now Sun in Pisces Moon in Gemini Mercury in Pisces Venus in Pisces Mars in Aquarius Jupiter in Cancer Saturn in Aries Uranus in Taurus Neptune in Aries Pluto in Aquarius Next aspects of the Moon in Gemini Moon trine Pluto Moon square Sun Moon square Venus Moon square Mercury Moon trine Mars Sabian Symbols for this moment For the Sun: A parade of army officers in full dress. For the Moon: Santa clause filling stockings furtively. Current Moon phase Waxing Moon - First quarter Mansion of the Moon 5th lunar mansion Site map | Copyright | Disclaimer | Advertise | Contact Back to top var vglnk = { api_url: '//api.viglink.com/api', key: '195db594191c407b070c39901b9edd6a' }; (function(d, t) { var s = d.createElement(t); s.type = 'text/javascript'; s.async = true; s.src = ('https:' == document.location.protocol ? vglnk.api_url : '//cdn.viglink.com/api') + '/vglnk.js'; var r = d.getElementsByTagName(t)[0]; r.parentNode.insertBefore(s, r); }(document, 'script')); var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-3679982-21']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })(); (function(){function c(){var b=a.contentDocument||a.contentWindow.document;if(b){var d=b.createElement('script');d.innerHTML=\"window.__CF$cv$params={r:'9d2c94134956ef1c',t:'MTc3MTkxMTEwNA=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);\";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();" + } + ] +} diff --git a/data/signs.json b/data/signs.json new file mode 100644 index 0000000..6920da3 --- /dev/null +++ b/data/signs.json @@ -0,0 +1,164 @@ +{ + "meta": { + "source": "https://github.com/gadicc/magick.ly/blob/master/data/astrology/zodiac.json5", + "notes": "Sign timing/ordering/elements/modality are adapted from magick.ly; tarot major arcana correspondences are organized here to avoid UI hardcoding." + }, + "signs": [ + { + "id": "aries", + "order": 1, + "name": "Aries", + "symbol": "♈︎", + "start": "03-21", + "end": "04-19", + "element": "fire", + "modality": "cardinal", + "sourceQuadruplicity": "cardinal", + "rulingPlanetId": "mars", + "tarot": { "majorArcana": "The Emperor", "number": 4 } + }, + { + "id": "taurus", + "order": 2, + "name": "Taurus", + "symbol": "♉︎", + "start": "04-20", + "end": "05-20", + "element": "earth", + "modality": "fixed", + "sourceQuadruplicity": "kerubic", + "rulingPlanetId": "venus", + "tarot": { "majorArcana": "The Hierophant", "number": 5 } + }, + { + "id": "gemini", + "order": 3, + "name": "Gemini", + "symbol": "♊︎", + "start": "05-21", + "end": "06-20", + "element": "air", + "modality": "mutable", + "sourceQuadruplicity": "mutable", + "rulingPlanetId": "mercury", + "tarot": { "majorArcana": "The Lovers", "number": 6 } + }, + { + "id": "cancer", + "order": 4, + "name": "Cancer", + "symbol": "♋︎", + "start": "06-21", + "end": "07-22", + "element": "water", + "modality": "cardinal", + "sourceQuadruplicity": "cardinal", + "rulingPlanetId": "sol", + "tarot": { "majorArcana": "The Chariot", "number": 7 } + }, + { + "id": "leo", + "order": 5, + "name": "Leo", + "symbol": "♌︎", + "start": "07-23", + "end": "08-22", + "element": "fire", + "modality": "fixed", + "sourceQuadruplicity": "kerubic", + "rulingPlanetId": "sol", + "tarot": { "majorArcana": "Strength", "number": 8 } + }, + { + "id": "virgo", + "order": 6, + "name": "Virgo", + "symbol": "♍︎", + "start": "08-23", + "end": "09-22", + "element": "earth", + "modality": "mutable", + "sourceQuadruplicity": "mutable", + "rulingPlanetId": "mercury", + "tarot": { "majorArcana": "The Hermit", "number": 9 } + }, + { + "id": "libra", + "order": 7, + "name": "Libra", + "symbol": "♎︎", + "start": "09-23", + "end": "10-22", + "element": "air", + "modality": "cardinal", + "sourceQuadruplicity": "cardinal", + "rulingPlanetId": "venus", + "tarot": { "majorArcana": "Justice", "number": 11 } + }, + { + "id": "scorpio", + "order": 8, + "name": "Scorpio", + "symbol": "♏︎", + "start": "10-23", + "end": "11-21", + "element": "water", + "modality": "fixed", + "sourceQuadruplicity": "kerubic", + "rulingPlanetId": "jupiter", + "tarot": { "majorArcana": "Death", "number": 13 } + }, + { + "id": "sagittarius", + "order": 9, + "name": "Sagittarius", + "symbol": "♐︎", + "start": "11-22", + "end": "12-21", + "element": "fire", + "modality": "mutable", + "sourceQuadruplicity": "mutable", + "rulingPlanetId": "jupiter", + "tarot": { "majorArcana": "Temperance", "number": 14 } + }, + { + "id": "capricorn", + "order": 10, + "name": "Capricorn", + "symbol": "♑︎", + "start": "12-22", + "end": "01-19", + "element": "earth", + "modality": "cardinal", + "sourceQuadruplicity": "cardinal", + "rulingPlanetId": "saturn", + "tarot": { "majorArcana": "The Devil", "number": 15 } + }, + { + "id": "aquarius", + "order": 11, + "name": "Aquarius", + "symbol": "♒︎", + "start": "01-20", + "end": "02-18", + "element": "air", + "modality": "fixed", + "sourceQuadruplicity": "kerubic", + "rulingPlanetId": "saturn", + "tarot": { "majorArcana": "The Star", "number": 17 } + }, + { + "id": "pisces", + "order": 12, + "name": "Pisces", + "symbol": "♓︎", + "start": "02-19", + "end": "03-20", + "element": "water", + "modality": "mutable", + "sourceQuadruplicity": "mutable", + "rulingPlanetId": "jupiter", + "tarot": { "majorArcana": "The Moon", "number": 18 } + } + ] +} diff --git a/data/tarot-database.json b/data/tarot-database.json new file mode 100644 index 0000000..cda7450 --- /dev/null +++ b/data/tarot-database.json @@ -0,0 +1,756 @@ +{ + "majorCards": [ + { + "number": 0, + "name": "The Fool", + "summary": "Open-hearted beginnings, leap of faith, and the sacred unknown.", + "upright": "Fresh starts, trust in the path, and inspired spontaneity.", + "reversed": "Recklessness, fear of risk, or resistance to a needed new beginning.", + "keywords": [ + "beginnings", + "faith", + "innocence", + "freedom", + "journey" + ], + "relations": [ + "Element: Air", + "Hebrew Letter: Aleph" + ] + }, + { + "number": 1, + "name": "The Magus", + "summary": "Focused will and conscious manifestation through aligned tools.", + "upright": "Skill, initiative, concentration, and transforming intent into action.", + "reversed": "Scattered power, manipulation, or blocked self-belief.", + "keywords": [ + "will", + "manifestation", + "focus", + "skill", + "agency" + ], + "relations": [ + "Planet: Mercury", + "Hebrew Letter: Beth" + ] + }, + { + "number": 2, + "name": "The High Priestess", + "summary": "Inner knowing, sacred silence, and intuitive perception.", + "upright": "Intuition, subtle insight, patience, and hidden wisdom.", + "reversed": "Disconnection from inner voice or confusion around truth.", + "keywords": [ + "intuition", + "mystery", + "stillness", + "inner-voice", + "receptivity" + ], + "relations": [ + "Planet: Luna", + "Hebrew Letter: Gimel" + ] + }, + { + "number": 3, + "name": "The Empress", + "summary": "Creative abundance, nurture, and embodied growth.", + "upright": "Fertility, care, beauty, comfort, and creative flourishing.", + "reversed": "Creative drought, overgiving, or neglecting self-nourishment.", + "keywords": [ + "abundance", + "creation", + "nurture", + "beauty", + "growth" + ], + "relations": [ + "Planet: Venus", + "Hebrew Letter: Daleth" + ] + }, + { + "number": 4, + "name": "The Emperor", + "summary": "Structure, authority, and stable leadership.", + "upright": "Order, discipline, protection, and strategic direction.", + "reversed": "Rigidity, domination, or weak boundaries.", + "keywords": [ + "structure", + "authority", + "stability", + "leadership", + "boundaries" + ], + "relations": [ + "Zodiac: Aries", + "Hebrew Letter: He" + ] + }, + { + "number": 5, + "name": "The Hierophant", + "summary": "Tradition, spiritual instruction, and shared values.", + "upright": "Learning from lineage, ritual, ethics, and mentorship.", + "reversed": "Dogma, rebellion without grounding, or spiritual stagnation.", + "keywords": [ + "tradition", + "teaching", + "ritual", + "ethics", + "lineage" + ], + "relations": [ + "Zodiac: Taurus", + "Hebrew Letter: Vav" + ] + }, + { + "number": 6, + "name": "The Lovers", + "summary": "Union, alignment, and value-centered choices.", + "upright": "Harmony, connection, and conscious commitment.", + "reversed": "Misalignment, indecision, or disconnection in relationship.", + "keywords": [ + "union", + "choice", + "alignment", + "connection", + "values" + ], + "relations": [ + "Zodiac: Gemini", + "Hebrew Letter: Zayin" + ] + }, + { + "number": 7, + "name": "The Chariot", + "summary": "Directed momentum and mastery through discipline.", + "upright": "Determination, movement, and victory through control.", + "reversed": "Loss of direction, conflict of will, or stalled progress.", + "keywords": [ + "drive", + "control", + "momentum", + "victory", + "direction" + ], + "relations": [ + "Zodiac: Cancer", + "Hebrew Letter: Cheth" + ] + }, + { + "number": 8, + "name": "Lust", + "summary": "Courageous life-force, passionate integrity, and heart-led power.", + "upright": "Vitality, confidence, and wholehearted creative expression.", + "reversed": "Self-doubt, depletion, or misdirected desire.", + "keywords": [ + "vitality", + "passion", + "courage", + "magnetism", + "heart-power" + ], + "relations": [ + "Zodiac: Leo", + "Hebrew Letter: Teth" + ] + }, + { + "number": 9, + "name": "The Hermit", + "summary": "Inner pilgrimage, discernment, and sacred solitude.", + "upright": "Reflection, guidance, and deepening wisdom.", + "reversed": "Isolation, avoidance, or overanalysis.", + "keywords": [ + "solitude", + "wisdom", + "search", + "reflection", + "discernment" + ], + "relations": [ + "Zodiac: Virgo", + "Hebrew Letter: Yod" + ] + }, + { + "number": 10, + "name": "Fortune", + "summary": "Cycles, timing, and turning points guided by greater rhythms.", + "upright": "Opportunity, momentum, and fated change.", + "reversed": "Resistance to cycles, delay, or repeating old patterns.", + "keywords": [ + "cycles", + "timing", + "change", + "destiny", + "turning-point" + ], + "relations": [ + "Planet: Jupiter", + "Hebrew Letter: Kaph" + ] + }, + { + "number": 11, + "name": "Justice", + "summary": "Balance, accountability, and clear consequence.", + "upright": "Fairness, truth, and ethical alignment.", + "reversed": "Bias, denial, or avoidance of responsibility.", + "keywords": [ + "balance", + "truth", + "accountability", + "law", + "clarity" + ], + "relations": [ + "Zodiac: Libra", + "Hebrew Letter: Lamed" + ] + }, + { + "number": 12, + "name": "The Hanged Man", + "summary": "Sacred pause, surrender, and transformed perspective.", + "upright": "Release, contemplation, and spiritual reframing.", + "reversed": "Stagnation, martyrdom, or refusing to let go.", + "keywords": [ + "surrender", + "pause", + "perspective", + "suspension", + "release" + ], + "relations": [ + "Element: Water", + "Hebrew Letter: Mem" + ] + }, + { + "number": 13, + "name": "Death", + "summary": "Endings that clear space for rebirth and renewal.", + "upright": "Transformation, completion, and deep release.", + "reversed": "Clinging, fear of change, or prolonged transition.", + "keywords": [ + "transformation", + "ending", + "renewal", + "release", + "rebirth" + ], + "relations": [ + "Zodiac: Scorpio", + "Hebrew Letter: Nun" + ] + }, + { + "number": 14, + "name": "Art", + "summary": "Alchemy, integration, and harmonizing opposites.", + "upright": "Balance through blending, refinement, and patience.", + "reversed": "Imbalance, excess, or fragmented energies.", + "keywords": [ + "alchemy", + "integration", + "balance", + "blending", + "refinement" + ], + "relations": [ + "Zodiac: Sagittarius", + "Hebrew Letter: Samekh" + ] + }, + { + "number": 15, + "name": "The Devil", + "summary": "Attachment, shadow desire, and material entanglement.", + "upright": "Confronting bondage, ambition, and raw instinct.", + "reversed": "Liberation, breaking chains, or denial of shadow.", + "keywords": [ + "attachment", + "shadow", + "instinct", + "temptation", + "bondage" + ], + "relations": [ + "Zodiac: Capricorn", + "Hebrew Letter: Ayin" + ] + }, + { + "number": 16, + "name": "The Tower", + "summary": "Sudden revelation that dismantles false structures.", + "upright": "Shock, breakthrough, and necessary collapse.", + "reversed": "Delayed upheaval, denial, or internal crisis.", + "keywords": [ + "upheaval", + "revelation", + "breakthrough", + "collapse", + "truth" + ], + "relations": [ + "Planet: Mars", + "Hebrew Letter: Pe" + ] + }, + { + "number": 17, + "name": "The Star", + "summary": "Healing hope, guidance, and spiritual renewal.", + "upright": "Inspiration, serenity, and trust in the future.", + "reversed": "Discouragement, doubt, or loss of faith.", + "keywords": [ + "hope", + "healing", + "guidance", + "renewal", + "faith" + ], + "relations": [ + "Zodiac: Aquarius", + "Hebrew Letter: Tzaddi" + ] + }, + { + "number": 18, + "name": "The Moon", + "summary": "Dream, uncertainty, and passage through the subconscious.", + "upright": "Intuition, mystery, and emotional depth.", + "reversed": "Confusion clearing, projection, or fear illusions.", + "keywords": [ + "dream", + "mystery", + "intuition", + "subconscious", + "illusion" + ], + "relations": [ + "Zodiac: Pisces", + "Hebrew Letter: Qoph" + ] + }, + { + "number": 19, + "name": "The Sun", + "summary": "Radiance, confidence, and vital life affirmation.", + "upright": "Joy, clarity, success, and openness.", + "reversed": "Temporary clouding, ego glare, or delayed confidence.", + "keywords": [ + "joy", + "clarity", + "vitality", + "success", + "radiance" + ], + "relations": [ + "Planet: Sol", + "Hebrew Letter: Resh" + ] + }, + { + "number": 20, + "name": "Aeon", + "summary": "Awakening call, reckoning, and soul-level renewal.", + "upright": "Judgment, liberation, and answering purpose.", + "reversed": "Self-judgment, hesitation, or resistance to calling.", + "keywords": [ + "awakening", + "reckoning", + "calling", + "renewal", + "release" + ], + "relations": [ + "Element: Fire", + "Hebrew Letter: Shin" + ] + }, + { + "number": 21, + "name": "Universe", + "summary": "Completion, integration, and embodied wholeness.", + "upright": "Fulfillment, mastery, and successful closure.", + "reversed": "Incomplete cycle, loose ends, or delayed completion.", + "keywords": [ + "completion", + "wholeness", + "integration", + "mastery", + "closure" + ], + "relations": [ + "Planet: Saturn", + "Hebrew Letter: Tav" + ] + } + ], + "suits": [ + "Cups", + "Wands", + "Swords", + "Disks" + ], + "numberRanks": [ + "Ace", + "Two", + "Three", + "Four", + "Five", + "Six", + "Seven", + "Eight", + "Nine", + "Ten" + ], + "courtRanks": [ + "Knight", + "Queen", + "Prince", + "Princess" + ], + "suitInfo": { + "cups": { + "element": "Water", + "domain": "emotion, intuition, relationship", + "keywords": [ + "emotion", + "intuition", + "connection" + ] + }, + "wands": { + "element": "Fire", + "domain": "will, creativity, drive", + "keywords": [ + "will", + "drive", + "inspiration" + ] + }, + "swords": { + "element": "Air", + "domain": "mind, truth, communication", + "keywords": [ + "mind", + "truth", + "clarity" + ] + }, + "disks": { + "element": "Earth", + "domain": "body, craft, material life", + "keywords": [ + "resources", + "craft", + "stability" + ] + } + }, + "rankInfo": { + "ace": { + "number": 1, + "upright": "Seed-force, pure potential, and concentrated essence.", + "reversed": "Blocked potential, delay, or difficulty beginning.", + "keywords": [ + "seed", + "potential", + "spark" + ] + }, + "two": { + "number": 2, + "upright": "Polarity seeking balance, exchange, and response.", + "reversed": "Imbalance, friction, or uncertain choices.", + "keywords": [ + "duality", + "balance", + "exchange" + ] + }, + "three": { + "number": 3, + "upright": "Initial growth, expansion, and expression.", + "reversed": "Scattered growth or stalled development.", + "keywords": [ + "growth", + "expansion", + "expression" + ] + }, + "four": { + "number": 4, + "upright": "Structure, boundaries, and stabilizing form.", + "reversed": "Rigidity, inertia, or unstable foundations.", + "keywords": [ + "structure", + "stability", + "foundation" + ] + }, + "five": { + "number": 5, + "upright": "Challenge, disruption, and catalytic conflict.", + "reversed": "Lingering tension or fear of needed change.", + "keywords": [ + "challenge", + "conflict", + "change" + ] + }, + "six": { + "number": 6, + "upright": "Rebalancing, harmony, and restorative flow.", + "reversed": "Partial recovery or unresolved imbalance.", + "keywords": [ + "harmony", + "repair", + "flow" + ] + }, + "seven": { + "number": 7, + "upright": "Testing, strategy, and spiritual/mental refinement.", + "reversed": "Doubt, overdefense, or poor strategy.", + "keywords": [ + "test", + "strategy", + "refinement" + ] + }, + "eight": { + "number": 8, + "upright": "Adjustment, movement, and disciplined power.", + "reversed": "Restriction, frustration, or scattered effort.", + "keywords": [ + "movement", + "discipline", + "adjustment" + ] + }, + "nine": { + "number": 9, + "upright": "Intensity, culmination, and mature force.", + "reversed": "Excess, overload, or unresolved pressure.", + "keywords": [ + "culmination", + "intensity", + "maturity" + ] + }, + "ten": { + "number": 10, + "upright": "Completion, overflow, and transition into a new cycle.", + "reversed": "Overextension, depletion, or difficulty releasing.", + "keywords": [ + "completion", + "threshold", + "transition" + ] + } + }, + "courtInfo": { + "knight": { + "role": "active catalyst and questing force", + "elementalFace": "Fire of", + "upright": "Bold pursuit, direct action, and forward momentum.", + "reversed": "Impulsiveness, burnout, or unfocused drive.", + "keywords": [ + "action", + "drive", + "initiative" + ] + }, + "queen": { + "role": "magnetic vessel and emotional intelligence", + "elementalFace": "Water of", + "upright": "Receptive mastery, mature feeling, and embodied wisdom.", + "reversed": "Withholding, moodiness, or passive control.", + "keywords": [ + "receptivity", + "maturity", + "depth" + ] + }, + "prince": { + "role": "strategic mediator and organizing mind", + "elementalFace": "Air of", + "upright": "Insight, communication, and adaptive coordination.", + "reversed": "Overthinking, detachment, or mixed signals.", + "keywords": [ + "strategy", + "communication", + "adaptability" + ] + }, + "princess": { + "role": "manifesting ground and practical seed-force", + "elementalFace": "Earth of", + "upright": "Practical growth, devotion, and tangible follow-through.", + "reversed": "Stagnation, timidity, or blocked growth.", + "keywords": [ + "manifestation", + "grounding", + "devotion" + ] + } + }, + "courtDecanWindows": { + "Knight of Wands": [ + "scorpio-3", + "sagittarius-1", + "sagittarius-2" + ], + "Queen of Disks": [ + "sagittarius-3", + "capricorn-1", + "capricorn-2" + ], + "Prince of Swords": [ + "capricorn-3", + "aquarius-1", + "aquarius-2" + ], + "Knight of Cups": [ + "aquarius-3", + "pisces-1", + "pisces-2" + ], + "Queen of Wands": [ + "pisces-3", + "aries-1", + "aries-2" + ], + "Prince of Disks": [ + "aries-3", + "taurus-1", + "taurus-2" + ], + "Knight of Swords": [ + "taurus-3", + "gemini-1", + "gemini-2" + ], + "Queen of Cups": [ + "gemini-3", + "cancer-1", + "cancer-2" + ], + "Prince of Wands": [ + "cancer-3", + "leo-1", + "leo-2" + ], + "Knight of Disks": [ + "leo-3", + "virgo-1", + "virgo-2" + ], + "Queen of Swords": [ + "virgo-3", + "libra-1", + "libra-2" + ], + "Prince of Cups": [ + "libra-3", + "scorpio-1", + "scorpio-2" + ] + }, + "meanings": { + "majorByTrumpNumber": { + "0": "Beginning of the Great Work, innocence; a fool for love; divine madness. Reason is transcended. Take the leap. Gain or loss through foolish actions.", + "1": "Communication; Conscious Will; the process of continuous creation; ambiguity; deception. Things may not be as they appear. Concentration; meditation; mind used to direct the Will. Manipulation; crafty maneuverings.", + "2": "Symbol of highest initiation; link between the Archetypal and Formative Worlds. An initiatrix. Wooing by enchantment. Possibility. The Idea behind the Form. Fluctuation. Time may not be right for a decision concerning mundane matters.", + "3": "The Holy Grail. Love unites the Will. Love; beauty; friendship; success; passive balance. The feminine point of view. The door is open. Disregard the details and concentrate on the big picture.", + "4": "Creative wisdom radiating upon the organized man and woman. Domination after conquest; quarrelsomeness; paternal love; ambition. Thought ruled by creative, masculine, fiery energy. Stubbornness; war; authority; energy in its most temporal form. Swift impermanent action over confidence.", + "5": "The Holy Guardian Angel. The uniting of that which is above with that which is below. Love is indicated, but the nature of that love is not yet to be revealed. Inspiration; teaching; organization; discipline; strength; endurance; toil; help from superiors.", + "6": "Intuition. Be open to your own inner voice. A well-intended, arranged marriage. An artificial union; analysis followed by synthesis; indecision; instability; superficiality.", + "7": "Light in the darkness. The burden you carry may be the Holy Grail. Faithfulness; hope; obedience; a protective relationship; firm, even violent adherence to dogma or tradition. Glory; riches; enlightened civilization; victory; triumph; chain of command.", + "8": "Equilibrium; karmic law; the dance of life; all possibilities. The woman satisfied. Balance; weigh each thought against its opposite. Lawsuits; treaties. Pause and look before you leap.", + "9": "Divine seed of all things. By silence comes inspiration and wisdom. Wandering alone; temporary solitude; creative contemplation; a virgin. Retirement from involvement in current events.", + "10": "Continual change. In the midst of revolving phenomena, reach joyously the motionless center. Carefree love; wanton pleasure; amusement; fun; change of fortune, usually good.", + "11": "Understanding; the Will of the New Aeon; passion; sense smitten with ecstasy. Let love devour all. Energy independent of reason. Strength; courage; utilization of magical power.", + "12": "Redemption, sacrifice, annihilation in the beloved; martyrdom; loss; torment; suspension; death; suffering.", + "13": "End of a cycle; transformation; raw sexuality. Sex is death. Stress becomes intolerable. Any change is welcome. Time; age; unexpected change; death.", + "14": "Transmutation through union of opposites. A perfect marriage exalts and transforms each partner. The scientific method. Success follows complex maneuvers.", + "15": "“Thou hast no right but to do thy will.” Obsession; temptation; ecstasy found in every phenomenon; creative action, yet sublimely careless of result; unscrupulous ambition; strength.", + "16": "Escape from the prison of organized life; renunciation of love; quarreling. Plans are destroyed. War; danger; sudden death.", + "17": "Clairvoyance; visions; dreams; hope; love; yearning; realization of inexhaustible possibilities; dreaminess; unexpected help; renewal.", + "18": "The Dark Night of the Soul; deception; falsehood; illusion; madness; the threshold of significant change.", + "19": "Lord of the New Aeon. Spiritual emancipation. Pleasure; shamelessness; vanity; frankness. Freedom brings sanity. Glory; riches; enlightened civilization.", + "20": "Let every act be an act of Worship; let every act be an act of Love. Final decision; judgement. Learn from the past. Prepare for the future.", + "21": "Completion of the Great Work; patience; perseverance; stubbornness; serious meditation. Work accomplished." + }, + "byCardName": { + "ace of wands": "Primordial energy as yet unmanifest. Seed of the Will. Masculine archetype. Natural force.", + "knight of wands": "He is active, generous, fierce, sudden, impetuous. If ill-dignified, he is evil-minded, cruel, bigoted, brutal.", + "queen of wands": "Adaptability, steady force applied to an object, steady rule, great attractive power, power of command, yet liked. Kind and generous when not opposed. If ill-dignified, obstinate, revengeful, domineering, tyrannical, and apt to turn against another without a cause.", + "prince of wands": "Swift, strong, hasty; rather violent, yet just and generous; noble and scorning meanness. If ill-dignified, cruel, intolerant, prejudiced, and ill-natured.", + "princess of wands": "Brilliance, courage, beauty, force, sudden in anger or love, desire of power, enthusiasm, revenge. If ill dignified, she is superficial, theatrical, cruel, unstable, domineering.", + "two of wands": "Dominion. First manifestation of Fire. Ideal will, independent and creative. Control over circumstances.", + "three of wands": "Pride, arrogance, self-assertion. Established force, strength, realization of hope. Completion of labor. Success after struggle. Pride, nobility, wealth, power, conceit. Rude self-assumption and insolence. Generosity, obstinacy, etc.", + "four of wands": "Settlement, arrangement, completion. Perfection or completion of a thing built up with trouble and labor. Rest after labor, subtlety, cleverness, beauty, mirth, success in completion. Reasoning faculty, conclusions drawnfrom previous knowledge. Unreadiness, unreliable and unsteady through overanxiety and hurriedness of action. Graceful in manner, at times insincere, etc.", + "five of wands": "Quarreling and fighting. Violent strife and boldness, rashness, cruelty, violence, lust, desire, prodigality and generosity, depending on whether the card is well- or ill-dignified.", + "six of wands": "Gain. Victory after strife. Love; pleasure gained by labor; carefulness, sociability and avoiding of strife, yet victory therein; also insolence and pride of riches and success, etc. The whole is dependent on the dignity.", + "seven of wands": "Opposition, yet courage. Possible victory, depending on the energy and courage exercised; valor; opposition, obstacles and difficulties, yet courage to meet them; quarreling, ignorance, pretense, wrangling, and threatening; also victory in small and unimportant things and influence upon subordinates.", + "eight of wands": "Hasty communications and messages; swiftness. Too much force applied too suddenly. Very rapid rush, but quickly passed and expended. Violent, but not lasting. Swiftness, rapidity, courage, boldness, confidence, freedom, warfare, violence; love of open air, field sports, gardens, and meadows. Generous, subtle, eloquent, yet somewhat untrustworthy; rapacious, insolent, oppressive. Theft and robbery. According to dignity.", + "nine of wands": "Strength, power, health, recovery from sickness. Tremendous and steady force that cannot be shaken. Herculean strength, yet sometimes scientifically applied. Great success, but with strife and energy. Victory, preceded by apprehension and fear. Health good, and recovery not in doubt. Generous, questioning and curious; fond of external appearances; intractable, obstinate.", + "ten of wands": "Cruelty, malice, revenge, injustice. Cruel and overbearing force and energy, but applied only to material and selfish ends. Sometimes shows failure in a matter and the opposition too strong to be controlled, arising from the person’s too great selfishness at the beginning. Ill will, levity, lying, malice, slander, envy, obstinacy; swiftness in evil and deceit, if ill-dignified. Also generosity, disinterestedness, and self-sacrifice, when well dignified. CUPS", + "ace of cups": "It symbolizes fertility—productiveness, beauty, pleasure, happiness, etc.", + "knight of cups": "Graceful, poetic, Venusian, indolent, but enthusiastic if roused. Ill-dignified, he is sensual, idle, and untruthful.", + "queen of cups": "She is imaginative, poetic, kind, yet not willing to take much trouble for another. Coquettish, good-natured, and underneath a dreamy appearance. Imagination stronger than feeling. Very much affected by other influences, and therefore more dependent upon dignity than most symbols.", + "prince of cups": "He is subtle, violent, crafty and artistic; a fierce nature with calm exterior. Powerful for good or evil but more attracted by the evil if allied with apparent Power or Wisdom. If ill-dignified, he is intensely evil and merciless.", + "princess of cups": "Sweetness, poetry, gentleness, and kindness. Imaginative, dreamy, at times indolent, yet courageous if roused. When ill-dignified, she is selfish and luxurious.", + "two of cups": "Marriage, love, pleasure. Harmony of masculine and feminine united. Harmony, pleasure, mirth, subtlety; but if", + "three of cups": "Plenty, hospitality, eating and drinking, pleasure, dancing, new clothes, merriment. Abundance, plenty, success, pleasure, sensuality, passive success, good luck and fortune; love, gladness, kindness, liberality.", + "four of cups": "Receiving pleasure or kindness from others, but some discomfort therewith. Success or pleasure approaching their end. A stationary period in happiness, which may, or may not, continue. It does not mean love and marriage so much as the previous symbol. It is too passive a symbol to represent perfectly completehappiness. Swiftness, hunting and pursuing. Acquisition by contention; injustice sometimes; some drawbacks to pleasure implied.", + "five of cups": "Disappointment in love, marriage broken off, unkindness of a friend; loss of friendship. Death, or end of pleasure; disappointment, sorrow, and loss in those things from which pleasure is expected. Sadness, treachery, deceit; ill will, detraction; charity and kindness ill-requited; all kinds of anxieties and troubles from unsuspected and unexpected sources.", + "six of cups": "Beginning of wish, happiness, success, or enjoyment. Commencement of steady increase, gain and pleasure; but commencement only. Also affront, detection, knowledge, and in some instances contention and strife arising from unwarranted self-assertion and vanity. Sometimes thankless and presumptuous; sometimes amiable and patient. According to dignity as usual.", + "seven of cups": "Lying, promises unfulfilled; illusion, deception, error; slight success at outset, not retained. Possible victory, but neutralized by the supineness of the person: illusionary success, deception in the moment of apparent victory. Lying, error, promises unfulfilled. Drunkenness, wrath, vanity. Lust, fornication, violence against women, selfish dissipation, deception in love and friendship. Often success gained, but not followed up. Modified as usual by dignity.", + "eight of cups": "Success abandoned; decline of interest. Temporary success, but without further results. Thing thrown aside as soon as gained. Not lasting, even in the matter in hand. Indolence in success. Journeying from place to place. Misery and repining without cause. Seeking after riches. Instability.", + "nine of cups": "Complete success, pleasure and happiness, wishes fulfilled. Complete and perfect realization of pleasure and happiness, almost perfect; self-praise, vanity, conceit, much talking of self, yet kind and lovable, and may be self-denying therewith. High-minded, not easily satisfied with small and limited ideas. Apt to be maligned through too much self-assumption. A good and generous, but sometimes foolish nature.", + "ten of cups": "Matter settled; complete good fortune. Permanent and lasting success and happiness, because inspired from above. Not so sensual as “Lord of Material Happiness,” yet almost more truly happy. Pleasure, dissipation,debauchery, quietness, peacemaking. Kindness, pity, generosity, wantonness, waste, etc., according to dignity.", + "ace of swords": "It symbolizes “invoked,” as contrasted with natural force: for it is the Invocation of the Sword. Raised upward, it invokes the divine crown of spiritual brightness, but reversed it is the invocation of demonic force and becomes a fearfully evil symbol. It represents, therefore, very great power for good or evil, but invoked; and it also represents whirling force and strength through trouble. It is the affirmation of Justice upholding divine authority; and it may become the Sword of Wrath, Punishment, and Affliction.", + "knight of swords": "He is active, clever, subtle, fierce, delicate, courageous, skillful, but inclined to domineer. Also to overvalue small things, unless well-dignified. If ill-dignified, deceitful, tyrannical, and crafty.", + "queen of swords": "Intensely perceptive, keen observation, subtle, quick and confident; often persevering, accurate in superficial things, graceful, fond of dancing and balancing. If ill-dignified, cruel, sly, deceitful, unreliable, though with a good exterior.", + "prince of swords": "Full of ideas and thoughts and designs, distrustful, suspicious, firm in friendship and enmity; careful, observant, slow, overcautious. Symbolizes Alpha and Omega; he slays as fast as he creates. If ill-dignified: harsh, malicious, plotting; obstinate, yet hesitating; unreliable.", + "princess of swords": "Wisdom, strength, acuteness; subtlety in material things; grace and dexterity. If ill-dignified, she is frivolous and cunning.", + "two of swords": "Quarrel made up, yet still some tension in relations: actions sometimes selfish, sometimes unselfish. Contradictory characters in the same nature; strength through suffering, pleasure after pain. Sacrifice and trouble, yet strength arising therefrom, symbolized by the position of the rose, as though the pain itself had brought forth beauty. Arrangement, peace restored; truce; truth and untruth; sorrow and sympathy. Aid to the weak; justice; unselfishness; also a tendency to repetition of affronts on being pardoned; injury when meaning well; given to petitions; also a want of tact and asking questions of little moment; talkative.", + "three of swords": "Unhappiness, sorrow, and tears. Disruption, interruption, separation, quarreling; sowing of discord and strife, mischief-making, sorrow and tears; yet mirth in platonic pleasures; singing, faithfulness in promises, honesty in money transactions, selfish and dissipated, yet sometimes generous; deceitful in words and repetitions; the whole according to dignity.", + "four of swords": "Convalescence, recovery from sickness; change for the better. Rest from sorrow, yet after and through it. Peace from and after war. Relaxation of anxiety. Quietness, rest, ease and plenty, yet after struggle. Goods of this life; abundance; modified by dignity as usual.", + "five of swords": "Defeat, loss, malice, spite, slander, evil-speaking. Contest finished and decided against the person; failure, defeat, anxiety, trouble, poverty, avarice, grieving after gain; laborious, unresting; loss and vileness of nature; malicious, slanderous, lying, spiteful and tale-bearing. A busybody and separator of friends, hating to see peace and love between others. Cruel, yet cowardly, thankless and unreliable. Clever and quick in thought and speech. Feelings of pity easily roused, but unenduring.", + "six of swords": "Labor, work, journey by water. Success after anxiety and trouble; self-esteem, beauty, conceit, but sometimes modesty therewith; dominance, patience, labor, etc.", + "seven of swords": "Journey by land; in character untrustworthy. Partial success. Yielding when victory is within grasp, as if the last reserves of strength were used up. Inclination to lose when on the point of gaining through not continuing the effort. Love of abundance; fascinated by display; given to compliments, affronts, and insolences and to spy upon others. Inclined to betray confidences, not always intentionally. Rather vacillatory and unreliable.", + "eight of swords": "Narrow, restricted, petty, a prison. Too much force applied to small things; too much attention to detail at the expense of the principal and more important points. When ill-dignified, these qualities produce malice, pettiness, and domineering characteristics. Patience in detail of study; great care in some things, counterbalanced by equal disorder in others. Impulsive; equally fond of giving or receiving money or presents; generous, clever, acute, selfish and without strong feeling of affection. Admires wisdom, yet applies it to small and unworthy objects.", + "nine of swords": "Illness, suffering, malice, cruelty, pain. Despair, cruelty, pitilessness, malice, suffering, want, loss, misery. Burden, oppression, labor; subtlety and craft, dishonesty, lying and slander. Yet also obedience, faithfulness, patience, unselfishness, etc., according to dignity.", + "ten of swords": "Ruin, death, defeat, disruption. Almost a worse symbol than the Nine of Swords. Undisciplined, warring force, complete disruption and failure. Ruin of all plans and projects. Disdain, insolence, and impertinence, yet mirth and jollity therewith. A marplot, loving to overthrow the happiness of others; a repeater of things; given to much unprofitable speech, and of many words. Yet clever, eloquent, etc., according to dignity.", + "ace of disks": "It represents materiality in all senses, good and evil, and is, therefore, in a sense, illusionary. It shows material gain, labor, power, wealth, etc.", + "knight of disks": "Unless very well-dignified he is heavy, dull, and material. Laborious, clever, and patient in material matters. If", + "queen of disks": "She is impetuous, kind, timid, rather charming, greathearted, intelligent, melancholy, truthful, yet of many moods. If ill-dignified, she is undecided, capricious, changeable, foolish.", + "prince of disks": "Increase of matter. Increases good or evil, solidifies; practically applies things. Steady, reliable. If ill-dignified, he is selfish, animal and material, stupid. In either case slow to anger, but furious if roused.", + "princess of disks": "She is generous, kind, diligent, benevolent, careful, courageous, persevering, pitiful. If ill-dignified, she is wasteful and prodigal.", + "two of disks": "Pleasant change; visit to friends. The harmony of change; alternation of gain and loss, weakness and strength; ever-changing occupation; wandering, discontented with any fixed condition of things; now elated, then melancholy; industrious, yet unreliable; fortunate through prudence of management, yet sometimes unaccountably foolish; alternatively talkative and suspicious. Kind, yet wavering and inconsistent. Fortunate in journeying. Argumentative.", + "three of disks": "Business, paid employment, commercial transaction. Working and constructive force, building up, creation, erection; realization and increase of material things; gain in commercial transactions, rank; increase of substance, influence, cleverness in business, selfishness. Commencement of matters to be established later. Narrow and prejudiced. Keen in matters of gain; sometimes given to seeking after impossibilities.", + "four of disks": "Gain of money or influence; a present. Assured material gain: success, rank, dominion, earthy power, completed but leading to nothing beyond. Prejudicial, covetous, suspicious, careful and orderly, but discontented. Little enterprise or originality. According to dignity as usual.", + "five of disks": "Loss of profession; loss of money; monetary anxiety. Loss of money or position. Trouble about material things. Labor, toil, land cultivation; building, knowledge and acuteness of earthly things; poverty; carefulness, kindness; sometimes money regained after severe toil and labor. Unimaginative, harsh, stern, determined, obstinate.", + "six of disks": "Success in material things, prosperity in business. Success and gain in material undertakings. Power, influence, rank, nobility, rule over the people. Fortunate, successful, liberal, and just. If ill-dignified, may be purse-proud, insolent from excess, or prodigal.", + "seven of disks": "Unprofitable speculations and employments; little gain for much labor. Promises of success unfulfilled (shown, as it were, by the fact that the rosebuds do not come to anything). Loss of apparently promising fortune. Hopes deceived and crushed. Disappointment, misery, slavery, necessity, and baseness. A cultivator of land, yet a loser thereby. Sometimes it denotes slight and isolated gains with no fruits resulting therefrom, and of no further account, though seeming to promise well.", + "eight of disks": "Skill; prudence; cunning. Overcareful in small things at the expense of great. Penny wise and pound foolish. Gain of ready money in small sums; mean, avaricious; industrious; cultivation of land; hoarding; lacking in enterprise.", + "nine of disks": "Inheritance; much increase of goods. Complete realization of material gain, good, riches; inheritance; covetous, treasuring of goods, and sometimes theft and knavery. The whole according to dignity.", + "ten of disks": "Riches and wealth. Completion of material gain and fortune; but nothing beyond; as it were, at the very pinnacle of success. Old age, slothfulness; great wealth, yet sometimes loss in part; heaviness; dullness of mind, yet clever and prosperous in money transactions." + } + } +} diff --git a/data/wheel-of-year.json b/data/wheel-of-year.json new file mode 100644 index 0000000..be3498a --- /dev/null +++ b/data/wheel-of-year.json @@ -0,0 +1,338 @@ +{ + "meta": { + "version": 1, + "notes": "Celtic / Pagan Wheel of the Year — eight seasonal festivals (Sabbats) with astronomical and agricultural associations. Holiday records are centralized in calendar-holidays.json." + }, + "calendar": { + "id": "wheel-of-year", + "label": "Wheel of the Year", + "type": "solar-cycle", + "script": "Latin", + "description": "The Wheel of the Year is an annual cycle of eight seasonal festivals (Sabbats) observed in Wicca, Druidry, and many modern Pagan traditions. Four are 'cross-quarter' fire festivals tied to agricultural seasons; four are solar festivals at the solstices and equinoxes." + }, + "months": [ + { + "id": "samhain", + "order": 1, + "name": "Samhain", + "nativeName": "Samhain", + "pronounce": "SOW-in", + "date": "October 31 / November 1", + "type": "cross-quarter", + "season": "Autumn cross-quarter", + "element": "Earth", + "description": "The Celtic New Year and the beginning of the dark half of the year. The boundary between the living and the dead is thinnest. Ancestors are honored; divination is practiced.", + "associations": { + "direction": "North / Northwest", + "themes": [ + "death and rebirth", + "ancestors", + "the veil", + "divination", + "endings" + ], + "deities": [ + "The Crone", + "Hecate", + "The Morrigan", + "Hades", + "Osiris" + ], + "colors": [ + "black", + "orange", + "deep red", + "purple" + ], + "herbs": [ + "mugwort", + "wormwood", + "dittany of Crete", + "nightshade" + ] + } + }, + { + "id": "yule", + "order": 2, + "name": "Yule (Winter Solstice)", + "nativeName": "Yule / Alban Arthan", + "date": "~December 21", + "type": "solar", + "season": "Winter solstice", + "element": "Earth", + "description": "The longest night and the rebirth of the Sun. The Oak King defeats the Holly King to begin the light's return. Log-burning, evergreen decoration, and candlelight vigils mark this Solar festival.", + "associations": { + "direction": "North", + "themes": [ + "rebirth of the sun", + "hope", + "inner light", + "return of warmth", + "solstice" + ], + "deities": [ + "The Oak King", + "Sol Invictus", + "Mithras", + "Odin / Father Christmas" + ], + "colors": [ + "red", + "green", + "gold", + "white" + ], + "herbs": [ + "holly", + "ivy", + "mistletoe", + "pine", + "frankincense" + ] + } + }, + { + "id": "imbolc", + "order": 3, + "name": "Imbolc", + "nativeName": "Imbolc / Oimelc", + "date": "February 1–2", + "type": "cross-quarter", + "season": "Late winter cross-quarter", + "element": "Fire / Earth", + "description": "The first stirrings of spring. A festival of the returning light, purification, and the goddess Brigid. Corresponds to Candlemas in Christian tradition.", + "associations": { + "direction": "Northeast", + "themes": [ + "new beginnings", + "purification", + "inspiration", + "hearth", + "healing" + ], + "deities": [ + "Brigid / Bride", + "St. Brigid", + "The Maiden" + ], + "colors": [ + "white", + "silver", + "pale yellow", + "pink" + ], + "herbs": [ + "snowdrop", + "rosemary", + "angelica", + "basil" + ] + } + }, + { + "id": "ostara", + "order": 4, + "name": "Ostara (Spring Equinox)", + "nativeName": "Ostara / Alban Eilir", + "date": "~March 20–21", + "type": "solar", + "season": "Spring equinox", + "element": "Air", + "description": "The Spring Equinox; equal day and night, balance of light and dark. Seeds of spring are planted, hares and eggs symbolize fertility. Named for the Germanic dawn goddess Ēostre.", + "associations": { + "direction": "East", + "themes": [ + "balance", + "rebirth", + "fertility", + "dawn", + "new beginnings" + ], + "deities": [ + "Ēostre", + "Persephone", + "The Maiden", + "Green Man" + ], + "colors": [ + "pale green", + "yellow", + "lilac", + "pastel" + ], + "herbs": [ + "violet", + "jasmine", + "daffodil", + "lavender" + ] + } + }, + { + "id": "beltane", + "order": 5, + "name": "Beltane", + "nativeName": "Beltane / Bealtainn", + "date": "May 1", + "type": "cross-quarter", + "season": "Spring cross-quarter", + "element": "Fire", + "description": "The great Celtic fire festival celebrating the peak of spring and the beginning of summer. Union of the God and Goddess. Maypole dancing, bonfires, and the gathering of hawthorn (may) blossom.", + "associations": { + "direction": "Southeast", + "themes": [ + "fertility", + "passion", + "union", + "abundance", + "sexuality", + "vitality" + ], + "deities": [ + "The Green Man", + "Flora", + "Cernunnos", + "Aphrodite", + "Beltane Fire" + ], + "colors": [ + "red", + "white", + "green", + "gold" + ], + "herbs": [ + "hawthorn", + "rose", + "rowan", + "meadowsweet", + "woodruff" + ] + } + }, + { + "id": "litha", + "order": 6, + "name": "Litha (Summer Solstice)", + "nativeName": "Litha / Alban Hefin / Midsummer", + "date": "~June 21", + "type": "solar", + "season": "Summer solstice", + "element": "Fire", + "description": "The longest day; the sun is at its zenith. The Holly King begins to reclaim power from the Oak King. A time of peak solar energy, magic, and celebration. Corresponds to Midsummer (Shakespeare's A Midsummer Night's Dream).", + "associations": { + "direction": "South", + "themes": [ + "peak power", + "fullness", + "solar magic", + "fae", + "courage", + "strength" + ], + "deities": [ + "Sol / Sun God", + "Lugh (early)", + "Ra", + "Apollo" + ], + "colors": [ + "gold", + "yellow", + "orange", + "blue" + ], + "herbs": [ + "St. John's Wort", + "lavender", + "chamomile", + "elderflower", + "fern seed" + ] + } + }, + { + "id": "lughnasadh", + "order": 7, + "name": "Lughnasadh", + "nativeName": "Lughnasadh / Lammas", + "date": "August 1", + "type": "cross-quarter", + "season": "Autumn cross-quarter (first harvest)", + "element": "Earth / Fire", + "description": "The first harvest festival, celebrating grain and abundance. Named for the Celtic sun god Lugh, who ordained funeral games for his foster-mother Tailtiu. The first loaf of bread is baked from the new grain.", + "associations": { + "direction": "West / Southwest", + "themes": [ + "first harvest", + "grain", + "sacrifice", + "abundance", + "skill", + "games" + ], + "deities": [ + "Lugh", + "The Corn King", + "John Barleycorn", + "Demeter" + ], + "colors": [ + "gold", + "orange", + "brown", + "yellow" + ], + "herbs": [ + "wheat", + "sunflower", + "corn", + "calendula", + "nasturtium" + ] + } + }, + { + "id": "mabon", + "order": 8, + "name": "Mabon (Autumn Equinox)", + "nativeName": "Mabon / Alban Elfed", + "date": "~September 22–23", + "type": "solar", + "season": "Autumn equinox", + "element": "Earth / Water", + "description": "The Autumn Equinox and second harvest. A time of balance and thanksgiving; day and night are equal. The God begins his descent into the Underworld. Grapes, corn, and root vegetables are harvested.", + "associations": { + "direction": "West", + "themes": [ + "harvest", + "balance", + "gratitude", + "preparation for darkness", + "completion" + ], + "deities": [ + "Mabon ap Modron", + "Persephone descending", + "The Morrigan", + "Dionysus" + ], + "colors": [ + "deep red", + "brown", + "russet", + "purple", + "gold" + ], + "herbs": [ + "ivy", + "oak", + "blackberry", + "rosehip", + "sage" + ] + } + } + ] +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..7d670e5 --- /dev/null +++ b/index.html @@ -0,0 +1,781 @@ + + + + + + Tarot Time! + + + + + + + + + + + + + + + +
+ Tarot Time! +
+
+ + +
+
+ + +
+
+ + +
+ + +
+ + +
+ + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
Current Planetary Hour
+ Current planetary hour card +
--
+
--
+
+ -- + > -- +
+
+
+
Moon Phase
+ Current moon phase card +
--
+
--
+
+ -- + > -- +
+
+
+
Current Decan (Sun)
+ Current decan card +
--
+
--
+
+ -- + > -- +
+
+
+
Current Planet Positions & Sabian Symbol
+
--
+
--
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..2ba5934 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1339 @@ +{ + "name": "tarot-clean", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "@fontsource/amiri": "^5.2.8", + "@fontsource/noto-naskh-arabic": "^5.2.11", + "@fontsource/noto-sans-hebrew": "^5.2.8", + "@fontsource/noto-sans-phoenician": "^5.2.6", + "@fontsource/noto-serif": "^5.2.9", + "@toast-ui/calendar": "^2.1.3", + "astronomy-engine": "^2.1.19", + "suncalc": "^1.9.0" + }, + "devDependencies": { + "http-server": "^14.1.1" + } + }, + "node_modules/@fontsource/amiri": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource/amiri/-/amiri-5.2.8.tgz", + "integrity": "sha512-HT/wBlEC/ummjXWg5yH+V0Tzw1vITtI9UM8mwxhwOkNZWD2zuie5vXpAVSCZORArT/LUQ4NP0fRcjiElUwI/8A==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/noto-naskh-arabic": { + "version": "5.2.11", + "resolved": "https://registry.npmjs.org/@fontsource/noto-naskh-arabic/-/noto-naskh-arabic-5.2.11.tgz", + "integrity": "sha512-mi4QOG3EK+iho//vjB+13Ww4Oy1ulkgBgUuVpVZQBjBPkaMoHOJZBpSIZOXTzL7ZFTvMPzdSTvt0juE9NVl+PA==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/noto-sans-hebrew": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource/noto-sans-hebrew/-/noto-sans-hebrew-5.2.8.tgz", + "integrity": "sha512-FN/GDpE709JQN5f7vmqlbIwz2/vAM6ZnXqRsw47Piq731hvti55V/FLbyU2RWUvlis16ezPf0r+zKTjfdeXF/g==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/noto-sans-phoenician": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/@fontsource/noto-sans-phoenician/-/noto-sans-phoenician-5.2.6.tgz", + "integrity": "sha512-l8DmuR7vO1UsRaPccsg5amOue5Svo6uZAybv0mPAuqOzfoKk5XPw8Y2f3HPNkHHyi2ubSq/tUeTGYKBhEdvcIQ==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/noto-serif": { + "version": "5.2.9", + "resolved": "https://registry.npmjs.org/@fontsource/noto-serif/-/noto-serif-5.2.9.tgz", + "integrity": "sha512-A2b7XBo0dSxxTZg95Ap/HLinXq1pEM8PzSvbBFtPMZM5/1BLKEU1dR+PBnLixvOdcC7mup+/NjmyLryQ1E9WeA==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@toast-ui/calendar": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@toast-ui/calendar/-/calendar-2.1.3.tgz", + "integrity": "sha512-QxEf6MhcpqQ3dLSv6pxww1i6Ks/Ejp6mqy8GG7INvNYezmoXeo1IUBN4Cy9zLZeIgQHs02GEfusN5UzB+FyPhg==", + "license": "MIT", + "dependencies": { + "immer": "^9.0.15", + "isomorphic-dompurify": "^0.20.0", + "preact": "^10.10.0", + "preact-render-to-string": "^5.2.1", + "tui-date-picker": "^4.0.1", + "tui-time-picker": "^2.0.1" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@types/dompurify": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-2.4.0.tgz", + "integrity": "sha512-IDBwO5IZhrKvHFUl+clZxgf3hn2b/lU6H1KaBShPkQyGJUQ0xwebezIPSuiyGwfz1UzJWQl4M7BDxtHtCCPlTg==", + "license": "MIT", + "dependencies": { + "@types/trusted-types": "*" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT" + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead", + "license": "BSD-3-Clause" + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "license": "MIT", + "dependencies": { + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/astronomy-engine": { + "version": "2.1.19", + "resolved": "https://registry.npmjs.org/astronomy-engine/-/astronomy-engine-2.1.19.tgz", + "integrity": "sha512-8yWKNf7UeNbH458h3sAJ6ZgAjE5jTXp/mNNRFoC20j2SHwZIjAQeEsBB2Q3uCFRaTCCJRv33K2XhkhZQMXoX6w==", + "license": "MIT" + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/corser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", + "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "license": "MIT" + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "license": "MIT", + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "license": "MIT" + }, + "node_modules/data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "license": "MIT", + "dependencies": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "deprecated": "Use your platform's native DOMException instead", + "license": "MIT", + "dependencies": { + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dompurify": { + "version": "2.5.8", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.5.8.tgz", + "integrity": "sha512-o1vSNgrmYMQObbSSvF/1brBYEQPHhV1+gsmrusO7/GXtp1T9rCS8cXFqVxK/9crT1jA6Ccv+5MTSjBNqr7Sovw==", + "license": "(MPL-2.0 OR Apache-2.0)" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true, + "license": "MIT" + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-server": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", + "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "basic-auth": "^2.0.1", + "chalk": "^4.1.2", + "corser": "^2.0.1", + "he": "^1.2.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy": "^1.18.1", + "mime": "^1.6.0", + "minimist": "^1.2.6", + "opener": "^1.5.1", + "portfinder": "^1.0.28", + "secure-compare": "3.0.1", + "union": "~0.5.0", + "url-join": "^4.0.1" + }, + "bin": { + "http-server": "bin/http-server" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "license": "MIT" + }, + "node_modules/isomorphic-dompurify": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/isomorphic-dompurify/-/isomorphic-dompurify-0.20.0.tgz", + "integrity": "sha512-ha3GBgVb9faPfGknAkwokYxqvdnh6c+R33LtgoXj4Lttg3b5DN5YH1P3zX1AHFFvTCOL7TER5zwGnUbJcQ85Cw==", + "license": "MIT", + "dependencies": { + "@types/dompurify": "^2.3.3", + "dompurify": "^2.3.8", + "jsdom": "^20.0.0" + } + }, + "node_modules/jsdom": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", + "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "license": "MIT", + "dependencies": { + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nwsapi": { + "version": "2.2.23", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.23.tgz", + "integrity": "sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==", + "license": "MIT" + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true, + "license": "(WTFPL OR MIT)", + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/portfinder": { + "version": "1.0.38", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.38.tgz", + "integrity": "sha512-rEwq/ZHlJIKw++XtLAO8PPuOQA/zaPJOZJ37BVuN97nLpMJeuDVLVGRwbFoBgLudgdTMP2hdRJP++H+8QOA3vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "async": "^3.2.6", + "debug": "^4.3.6" + }, + "engines": { + "node": ">= 10.12" + } + }, + "node_modules/preact": { + "version": "10.28.4", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.28.4.tgz", + "integrity": "sha512-uKFfOHWuSNpRFVTnljsCluEFq57OKT+0QdOiQo8XWnQ/pSvg7OpX5eNOejELXJMWy+BwM2nobz0FkvzmnpCNsQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/preact-render-to-string": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz", + "integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==", + "license": "MIT", + "dependencies": { + "pretty-format": "^3.8.0" + }, + "peerDependencies": { + "preact": ">=10" + } + }, + "node_modules/pretty-format": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", + "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==", + "license": "MIT" + }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "license": "MIT" + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT" + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/secure-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", + "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", + "dev": true, + "license": "MIT" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/suncalc": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/suncalc/-/suncalc-1.9.0.tgz", + "integrity": "sha512-vMJ8Byp1uIPoj+wb9c1AdK4jpkSKVAywgHX0lqY7zt6+EWRRC3Z+0Ucfjy/0yxTVO1hwwchZe4uoFNqrIC24+A==" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "license": "MIT" + }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "license": "MIT", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/tui-date-picker": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/tui-date-picker/-/tui-date-picker-4.3.3.tgz", + "integrity": "sha512-/2YoLnj5c1e+Ag1ZZYOgzEs2o0v7Ol7c5UAnBj438zGlkwkMxyH0HwP2pVqqIYX05WE7K0+6nTWVMybS8otBgw==", + "license": "MIT", + "dependencies": { + "tui-time-picker": "^2.1.6" + } + }, + "node_modules/tui-time-picker": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/tui-time-picker/-/tui-time-picker-2.1.6.tgz", + "integrity": "sha512-4Jmo3wjGS+Ii4/qQgt5DaFEohHpB3U6BzWeTODVVFHD9sx3NOsbomY9K0xMobSLODi+tQEH7wfOtNU0IJmcQ6Q==", + "license": "MIT" + }, + "node_modules/union": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", + "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", + "dev": true, + "dependencies": { + "qs": "^6.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true, + "license": "MIT" + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "license": "MIT", + "dependencies": { + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "license": "MIT", + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "license": "Apache-2.0", + "engines": { + "node": ">=12" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "license": "MIT" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..878e933 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "dependencies": { + "@fontsource/amiri": "^5.2.8", + "@fontsource/noto-naskh-arabic": "^5.2.11", + "@fontsource/noto-sans-hebrew": "^5.2.8", + "@fontsource/noto-sans-phoenician": "^5.2.6", + "@fontsource/noto-serif": "^5.2.9", + "@toast-ui/calendar": "^2.1.3", + "astronomy-engine": "^2.1.19", + "suncalc": "^1.9.0" + }, + "devDependencies": { + "http-server": "^14.1.1" + }, + "scripts": { + "generate:decks": "node scripts/generate-decks-registry.cjs", + "validate:decks": "node scripts/generate-decks-registry.cjs --validate-only --strict", + "postinstall": "npm run generate:decks", + "prestart": "npm run generate:decks", + "start": "npx http-server . -o index.html", + "dev": "npm run start" + } +} diff --git a/scripts/generate-decks-registry.cjs b/scripts/generate-decks-registry.cjs new file mode 100644 index 0000000..c468312 --- /dev/null +++ b/scripts/generate-decks-registry.cjs @@ -0,0 +1,516 @@ +const fs = require("fs"); +const path = require("path"); + +const projectRoot = path.resolve(__dirname, ".."); +const decksRoot = path.join(projectRoot, "asset", "tarot deck"); +const registryPath = path.join(decksRoot, "decks.json"); +const ignoredFolderNames = new Set(["template", "templates", "example", "examples"]); +const tarotSuits = ["wands", "cups", "swords", "disks"]; +const majorTrumpNumbers = Array.from({ length: 22 }, (_, index) => index); +const expectedMinorCardCount = 56; + +function isPlainObject(value) { + return Boolean(value) && typeof value === "object" && !Array.isArray(value); +} + +function slugifyId(input) { + return String(input || "") + .trim() + .toLowerCase() + .replace(/&/g, " and ") + .replace(/['\"`]/g, "") + .replace(/[^a-z0-9]+/g, "-") + .replace(/^-+|-+$/g, "") + .replace(/-{2,}/g, "-"); +} + +function asNonEmptyString(value) { + const normalized = String(value || "").trim(); + return normalized || null; +} + +function toTitleCase(value) { + const normalized = String(value || "").trim().toLowerCase(); + if (!normalized) { + return ""; + } + + return normalized.charAt(0).toUpperCase() + normalized.slice(1); +} + +function applyTemplate(template, variables) { + return String(template || "") + .replace(/\{([a-zA-Z0-9_]+)\}/g, (_, token) => { + const value = variables[token]; + return value == null ? "" : String(value); + }); +} + +function readManifestJson(manifestPath) { + try { + const text = fs.readFileSync(manifestPath, "utf8"); + const parsed = JSON.parse(text); + return parsed && typeof parsed === "object" ? parsed : null; + } catch { + return null; + } +} + +function hasNonEmptyStringMapEntries(value) { + if (!isPlainObject(value)) { + return false; + } + + return Object.values(value).some((entryValue) => String(entryValue || "").trim()); +} + +function hasRankResolver(minorRule) { + if (!isPlainObject(minorRule)) { + return false; + } + + const rankOrder = Array.isArray(minorRule.rankOrder) ? minorRule.rankOrder.filter((entry) => String(entry || "").trim()) : []; + if (rankOrder.length) { + return true; + } + + return hasNonEmptyStringMapEntries(minorRule.rankIndexByKey); +} + +function hasSuitNumberMap(value) { + if (!isPlainObject(value)) { + return false; + } + + return tarotSuits.every((suitId) => Number.isFinite(Number(value[suitId]))); +} + +function hasSuitStringMap(value) { + if (!isPlainObject(value)) { + return false; + } + + return tarotSuits.every((suitId) => String(value[suitId] || "").trim()); +} + +function isValidMinorOverrideKey(value) { + return /^(ace|two|three|four|five|six|seven|eight|nine|ten|knight|queen|prince|princess|king|page|[2-9]|10)\s+of\s+(cups|wands|swords|pentacles|disks)$/i.test(String(value || "").trim()); +} + +function getDefinedMajorCount(majors) { + const mode = asNonEmptyString(majors?.mode); + if (mode === "trump-template") { + return majorTrumpNumbers.length; + } + + if (mode === "canonical-map" || mode === "trump-map") { + return Object.values(majors?.cards || {}) + .map((value) => String(value || "").trim()) + .filter(Boolean) + .length; + } + + return 0; +} + +function validateMajorsRule(majors) { + if (!isPlainObject(majors)) { + return "majors must be an object"; + } + + const mode = asNonEmptyString(majors.mode); + if (!mode) { + return "majors.mode is required"; + } + + if (mode === "canonical-map" || mode === "trump-map") { + if (!hasNonEmptyStringMapEntries(majors.cards)) { + return `majors.cards must be a non-empty object for mode '${mode}'`; + } + + if (getDefinedMajorCount(majors) < majorTrumpNumbers.length) { + return `majors.cards must define all ${majorTrumpNumbers.length} major trumps for mode '${mode}'`; + } + + return null; + } + + if (mode === "trump-template") { + const template = asNonEmptyString(majors.template); + if (!template) { + return "majors.template is required for mode 'trump-template'"; + } + + if (majors.numberPad != null && !Number.isInteger(Number(majors.numberPad))) { + return "majors.numberPad must be an integer when provided"; + } + + return null; + } + + return `unsupported majors.mode '${mode}'`; +} + +function validateMinorsRule(minors) { + if (!isPlainObject(minors)) { + return "minors must be an object"; + } + + const mode = asNonEmptyString(minors.mode); + if (!mode) { + return "minors.mode is required"; + } + + if (!hasRankResolver(minors)) { + return "minors must define rankOrder or rankIndexByKey"; + } + + if (getRankEntries(minors).length !== 14) { + return `minors must define exactly 14 ranks to cover ${expectedMinorCardCount} cards`; + } + + if (mode === "suit-prefix-and-rank-order") { + if (!hasSuitStringMap(minors.suitPrefix)) { + return "minors.suitPrefix must define wands, cups, swords, and disks"; + } + + if (minors.indexStart != null && !Number.isInteger(Number(minors.indexStart))) { + return "minors.indexStart must be an integer when provided"; + } + + if (minors.indexPad != null && !Number.isInteger(Number(minors.indexPad))) { + return "minors.indexPad must be an integer when provided"; + } + + return null; + } + + if (mode === "suit-base-and-rank-order" || mode === "suit-base-number-template") { + if (!hasSuitNumberMap(minors.suitBase)) { + return "minors.suitBase must define numeric bases for wands, cups, swords, and disks"; + } + + if (minors.numberPad != null && !Number.isInteger(Number(minors.numberPad))) { + return "minors.numberPad must be an integer when provided"; + } + + return null; + } + + return `unsupported minors.mode '${mode}'`; +} + +function validateDeckManifest(manifest) { + const errors = []; + + if (!isPlainObject(manifest)) { + return ["deck.json must contain an object"]; + } + + const majorsError = validateMajorsRule(manifest.majors); + if (majorsError) { + errors.push(majorsError); + } + + const minorsError = validateMinorsRule(manifest.minors); + if (minorsError) { + errors.push(minorsError); + } + + if (manifest.nameOverrides != null && !isPlainObject(manifest.nameOverrides)) { + errors.push("nameOverrides must be an object when provided"); + } + + if (manifest.minorNameOverrides != null && !isPlainObject(manifest.minorNameOverrides)) { + errors.push("minorNameOverrides must be an object when provided"); + } + + if (isPlainObject(manifest.minorNameOverrides)) { + Object.entries(manifest.minorNameOverrides).forEach(([rawKey, rawValue]) => { + if (!isValidMinorOverrideKey(rawKey)) { + errors.push(`minorNameOverrides contains unsupported card key '${rawKey}'`); + } + + if (!asNonEmptyString(rawValue)) { + errors.push(`minorNameOverrides '${rawKey}' must map to a non-empty string`); + } + }); + } + + if (manifest.majorNameOverridesByTrump != null && !isPlainObject(manifest.majorNameOverridesByTrump)) { + errors.push("majorNameOverridesByTrump must be an object when provided"); + } + + return errors; +} + +function getRankEntries(minors) { + const rankOrder = Array.isArray(minors?.rankOrder) + ? minors.rankOrder + .map((entry) => String(entry || "").trim()) + .filter(Boolean) + : []; + + if (rankOrder.length) { + return rankOrder.map((rankWord, rankIndex) => ({ + rankWord, + rankKey: rankWord.toLowerCase(), + rankIndex + })); + } + + const rankIndexByKey = isPlainObject(minors?.rankIndexByKey) ? minors.rankIndexByKey : {}; + return Object.entries(rankIndexByKey) + .map(([rankKey, rankIndex]) => ({ + rankWord: toTitleCase(rankKey), + rankKey: String(rankKey || "").trim().toLowerCase(), + rankIndex: Number(rankIndex) + })) + .filter((entry) => entry.rankKey && Number.isInteger(entry.rankIndex) && entry.rankIndex >= 0) + .sort((left, right) => left.rankIndex - right.rankIndex); +} + +function getReferencedMajorFiles(manifest) { + const majors = manifest?.majors; + const mode = asNonEmptyString(majors?.mode); + if (!mode) { + return []; + } + + if (mode === "canonical-map" || mode === "trump-map") { + return Object.values(majors.cards || {}) + .map((value) => String(value || "").trim()) + .filter(Boolean); + } + + if (mode === "trump-template") { + const template = String(majors.template || "{number}.png"); + const numberPad = Number.isInteger(Number(majors.numberPad)) ? Number(majors.numberPad) : 2; + return majorTrumpNumbers.map((trump) => applyTemplate(template, { + trump, + number: String(trump).padStart(numberPad, "0") + })); + } + + return []; +} + +function getReferencedMinorFiles(manifest) { + const minors = manifest?.minors; + const mode = asNonEmptyString(minors?.mode); + if (!mode) { + return []; + } + + const rankEntries = getRankEntries(minors); + if (!rankEntries.length) { + return []; + } + + if (mode === "suit-prefix-and-rank-order") { + const template = String(minors.template || "{suit}{index}.png"); + const indexStart = Number.isInteger(Number(minors.indexStart)) ? Number(minors.indexStart) : 1; + const indexPad = Number.isInteger(Number(minors.indexPad)) ? Number(minors.indexPad) : 2; + return tarotSuits.flatMap((suitId) => { + const suitPrefix = String(minors?.suitPrefix?.[suitId] || "").trim(); + return rankEntries.map((entry) => applyTemplate(template, { + suit: suitPrefix, + suitId, + index: String(indexStart + entry.rankIndex).padStart(indexPad, "0"), + rank: entry.rankWord, + rankKey: entry.rankKey + })); + }); + } + + if (mode === "suit-base-and-rank-order") { + const template = String(minors.template || "{number}_{rank} {suit}.webp"); + const numberPad = Number.isInteger(Number(minors.numberPad)) ? Number(minors.numberPad) : 2; + return tarotSuits.flatMap((suitId) => { + const suitBase = Number(minors?.suitBase?.[suitId]); + const suitWord = String(minors?.suitLabel?.[suitId] || toTitleCase(suitId)); + return rankEntries.map((entry) => applyTemplate(template, { + number: String(suitBase + entry.rankIndex).padStart(numberPad, "0"), + rank: entry.rankWord, + rankKey: entry.rankKey, + suit: suitWord, + suitId, + index: entry.rankIndex + 1 + })); + }); + } + + if (mode === "suit-base-number-template") { + const template = String(minors.template || "{number}.png"); + const numberPad = Number.isInteger(Number(minors.numberPad)) ? Number(minors.numberPad) : 2; + return tarotSuits.flatMap((suitId) => { + const suitBase = Number(minors?.suitBase?.[suitId]); + return rankEntries.map((entry) => applyTemplate(template, { + number: String(suitBase + entry.rankIndex).padStart(numberPad, "0"), + rank: entry.rankWord, + rankKey: entry.rankKey, + suitId, + index: entry.rankIndex + })); + }); + } + + return []; +} + +function summarizeMissingFiles(fileList) { + const maxPreview = 8; + const preview = fileList.slice(0, maxPreview).join(", "); + if (fileList.length <= maxPreview) { + return preview; + } + + return `${preview}, ... (+${fileList.length - maxPreview} more)`; +} + +function auditDeckFiles(folderName, manifest) { + const deckFolderPath = path.join(decksRoot, folderName); + const referencedFiles = [ + ...getReferencedMajorFiles(manifest), + ...getReferencedMinorFiles(manifest) + ] + .map((relativePath) => String(relativePath || "").trim()) + .filter(Boolean); + + const uniqueReferencedFiles = Array.from(new Set(referencedFiles)); + const missingFiles = uniqueReferencedFiles.filter((relativePath) => !fs.existsSync(path.join(deckFolderPath, relativePath))); + + if (!missingFiles.length) { + return null; + } + + return `missing ${missingFiles.length} referenced image file${missingFiles.length === 1 ? "" : "s"}: ${summarizeMissingFiles(missingFiles)}`; +} + +function shouldIgnoreDeckFolder(folderName) { + const normalized = String(folderName || "").trim().toLowerCase(); + if (!normalized) { + return true; + } + + return normalized.startsWith("_") || normalized.startsWith(".") || ignoredFolderNames.has(normalized); +} + +function compileDeckRegistry() { + if (!fs.existsSync(decksRoot)) { + throw new Error(`Deck root not found: ${decksRoot}`); + } + + const entries = fs.readdirSync(decksRoot, { withFileTypes: true }); + const deckRows = []; + const warnings = []; + const seenIds = new Set(); + + entries.forEach((entry) => { + if (!entry.isDirectory()) { + return; + } + + const folderName = entry.name; + if (shouldIgnoreDeckFolder(folderName)) { + return; + } + + const manifestFsPath = path.join(decksRoot, folderName, "deck.json"); + + if (!fs.existsSync(manifestFsPath)) { + warnings.push(`Skipped '${folderName}': missing deck.json`); + return; + } + + const manifest = readManifestJson(manifestFsPath); + if (!manifest) { + warnings.push(`Skipped '${folderName}': deck.json is invalid JSON`); + return; + } + + const validationErrors = validateDeckManifest(manifest); + if (validationErrors.length) { + warnings.push(`Skipped '${folderName}': ${validationErrors.join("; ")}`); + return; + } + + const auditError = auditDeckFiles(folderName, manifest); + if (auditError) { + warnings.push(`Skipped '${folderName}': ${auditError}`); + return; + } + + const idFromManifest = asNonEmptyString(manifest.id); + const labelFromManifest = asNonEmptyString(manifest.label); + const fallbackId = slugifyId(folderName); + + if (!fallbackId && !idFromManifest) { + warnings.push(`Skipped '${folderName}': unable to infer deck id`); + return; + } + + const id = (idFromManifest || fallbackId).toLowerCase(); + const label = labelFromManifest || folderName; + const basePath = `asset/tarot deck/${folderName}`; + + if (seenIds.has(id)) { + warnings.push(`Skipped '${folderName}': duplicate deck id '${id}'`); + return; + } + + seenIds.add(id); + + deckRows.push({ + id, + label, + basePath, + manifestPath: `${basePath}/deck.json` + }); + }); + + // Keep output deterministic for stable diffs. + deckRows.sort((a, b) => a.label.localeCompare(b.label, "en", { sensitivity: "base" })); + + return { + registry: { decks: deckRows }, + warnings + }; +} + +function writeDeckRegistry(registry) { + fs.writeFileSync(registryPath, `${JSON.stringify(registry, null, 2)}\n`, "utf8"); +} + +function parseCliOptions(argv) { + const args = new Set(Array.isArray(argv) ? argv : []); + return { + validateOnly: args.has("--validate-only"), + strict: args.has("--strict") + }; +} + +function main() { + const options = parseCliOptions(process.argv.slice(2)); + const { registry, warnings } = compileDeckRegistry(); + + const count = Array.isArray(registry.decks) ? registry.decks.length : 0; + + if (options.validateOnly) { + console.log(`[decks] Validated ${count} deck manifest${count === 1 ? "" : "s"}`); + } else { + writeDeckRegistry(registry); + console.log(`[decks] Wrote ${count} deck entr${count === 1 ? "y" : "ies"} to ${path.relative(projectRoot, registryPath)}`); + } + + warnings.forEach((warning) => { + console.warn(`[decks] ${warning}`); + }); + + if (options.strict && warnings.length) { + process.exitCode = 1; + console.error(`[decks] Validation failed with ${warnings.length} warning${warnings.length === 1 ? "" : "s"}`); + } +} + +main();