"""Data loader and numerology helpers for Tarot correspondences."""

from __future__ import annotations

from dataclasses import dataclass, field
from datetime import datetime
from typing import TYPE_CHECKING, Dict, List, Optional, Union, overload

from ..attributes import (
    Cipher,
    CipherResult,
    ClockHour,
    Color,
    Colorscale,
    Day,
    ElementType,
    EnglishAlphabet,
    God,
    GreekAlphabet,
    HebrewAlphabet,
    Hexagram,
    Letter,
    Month,
    Number,
    Path,
    Perfume,
    PeriodicTable,
    Planet,
    Sephera,
    Weekday,
)
from ..deck import Card, Deck

if TYPE_CHECKING:
    from ..deck import TemporalQuery


@dataclass(frozen=True)
class TemporalCorrespondence:
    """Snapshot of Tarot correspondences for a particular moment in time."""

    timestamp: datetime
    weekday: Optional[Weekday]
    clock_hour: Optional[ClockHour]
    card: Card
    planet: Optional[Planet]
    number: Optional[Number]
    color: Optional[Color]
    letters: List[str] = field(default_factory=list)
    hexagram: Optional[Hexagram] = None


def calculate_digital_root(value: int) -> int:
    """
    Calculate the digital root of a number by repeatedly summing its digits.

    Digital root reduces any number to a single digit (1-9) by repeatedly
    summing its digits until a single digit remains.

    Args:
        value: The number to reduce to digital root

    Returns:
        The digital root (1-9)

    Examples:
        >>> calculate_digital_root(14)  # 1+4 = 5
        5
        >>> calculate_digital_root(99)  # 9+9 = 18, 1+8 = 9
        9
        >>> calculate_digital_root(5)
        5
    """
    if value < 1:
        raise ValueError(f"Value must be positive, got {value}")

    while value >= 10:
        value = sum(int(digit) for digit in str(value))

    return value


class WordCipherRequest:
    """Fluent helper for applying ciphers to a given word."""

    def __init__(
        self, loader: "CardDataLoader", text: str, default_alphabet: Optional[str] = None
    ) -> None:
        self._loader = loader
        self._text = text
        self._default_alphabet = default_alphabet

    def cipher(self, cipher_name: str, *, alphabet: Optional[str] = None) -> CipherResult:
        """Apply the named cipher, optionally overriding the alphabet."""
        return self._loader._apply_cipher(
            text=self._text,
            cipher_name=cipher_name,
            alphabet_name=alphabet or self._default_alphabet,
        )


class CardDataLoader:
    """Provides access to all Kabbalistic data and card attributes."""

    _numbers: Dict[int, Number] = {}
    _colors: Dict[int, Color] = {}
    _colorscales: Dict[int, Colorscale] = {}
    _ciphers: Dict[str, Cipher] = {}
    _alphabets: Dict[str, List] = {}
    _sephera: Dict[int, Sephera] = {}
    _planets: Dict[str, Planet] = {}
    _weekdays: Dict[str, Weekday] = {}
    _clock_hours: Dict[int, ClockHour] = {}
    _elements: Dict[str, ElementType] = {}
    _gods: Dict[str, God] = {}
    _perfumes: Dict[str, Perfume] = {}
    _paths: Dict[int, Path] = {}
    _periodic_table: Dict[int, PeriodicTable] = {}
    _initialized: bool = False

    def __init__(self) -> None:
        """Initialize the data loader."""
        if not CardDataLoader._initialized:
            self._load_all_data()
            CardDataLoader._initialized = True

    @classmethod
    def _load_all_data(cls) -> None:
        """Load all Kabbalistic data."""
        cls._load_elements()
        cls._load_sephera()
        cls._load_colorscales()
        cls._color()
        cls._load_numbers()
        cls._load_ciphers()
        cls._load_alphabets()
        cls._load_planets()
        cls._load_periodic_table()
        cls._load_weekdays()
        cls._load_clock_hours()
        cls._load_gods()
        cls._load_perfumes()
        cls._load_paths()
        cls._attach_colorscales_to_paths()
        cls._attach_path_correspondences()

    @classmethod
    def _load_elements(cls) -> None:
        """Load the five elemental types with Kabbalistic properties."""
        specs = [
            {
                "name": "Fire",
                "symbol": "△",
                "direction": "South",
                "polarity": "Active",
                "color": "Red",
                "tarot_suits": ["Wands"],
                "zodiac_signs": ["Aries", "Leo", "Sagittarius"],
                "keywords": ["Passion", "Will", "Energy", "Transformation"],
                "description": "Pure creative drive and spiritual momentum.",
            },
            {
                "name": "Water",
                "symbol": "▽",
                "direction": "West",
                "polarity": "Passive",
                "color": "Blue",
                "tarot_suits": ["Cups"],
                "zodiac_signs": ["Cancer", "Scorpio", "Pisces"],
                "keywords": ["Emotion", "Intuition", "Receptivity", "Flow"],
                "description": "Feeling, compassion, and subconscious depths.",
            },
            {
                "name": "Air",
                "symbol": "△ with line",
                "direction": "East",
                "polarity": "Active",
                "color": "Yellow",
                "tarot_suits": ["Swords"],
                "zodiac_signs": ["Gemini", "Libra", "Aquarius"],
                "keywords": ["Intellect", "Communication", "Clarity", "Thought"],
                "description": "Mind, speech, and intellectual clarity.",
            },
            {
                "name": "Earth",
                "symbol": "▽ with line",
                "direction": "North",
                "polarity": "Passive",
                "color": "Green",
                "tarot_suits": ["Pentacles"],
                "zodiac_signs": ["Taurus", "Virgo", "Capricorn"],
                "keywords": ["Body", "Matter", "Manifestation", "Abundance"],
                "description": "Physical form, material wealth, and grounding.",
            },
            {
                "name": "Spirit",
                "symbol": "•",
                "direction": "Center",
                "polarity": "Neutral",
                "color": "White",
                "tarot_suits": ["Major Arcana"],
                "zodiac_signs": [],
                "keywords": ["Unity", "Consciousness", "Transcendence", "Source"],
                "description": "Quintessential synthesis transcending the four elements.",
            },
        ]
        cls._elements = {}
        for spec in specs:
            name = spec.get("name")
            if not name:
                raise ValueError("Element spec missing 'name'")
            cls._elements[name.lower()] = ElementType(
                name=name,
                symbol=spec.get("symbol", ""),
                direction=spec.get("direction", ""),
                polarity=spec.get("polarity", ""),
                color=spec.get("color", ""),
                tarot_suits=spec.get("tarot_suits", []),
                zodiac_signs=spec.get("zodiac_signs", []),
                keywords=spec.get("keywords", []),
                description=spec.get("description", ""),
            )  # type: ignore[arg-type]

    @classmethod
    def _load_sephera(cls) -> None:
        """Load the ten Sephiroth of the Tree of Life."""
        specs = [
            {
                "number": 1,
                "name": "Kether",
                "hebrew_name": "כתר",
                "meaning": "The Crown",
                "archangel": "Metatron",
                "order_of_angels": "Chayot ha Qodesh",
                "mundane_chakra": "Primum Mobile",
            },
            {
                "number": 2,
                "name": "Chokmah",
                "hebrew_name": "חכמה",
                "meaning": "Wisdom",
                "archangel": "Ratziel",
                "order_of_angels": "Auphanim",
                "mundane_chakra": "Zodiac",
            },
            {
                "number": 3,
                "name": "Binah",
                "hebrew_name": "בינה",
                "meaning": "Understanding",
                "archangel": "Tzaphqiel",
                "order_of_angels": "Aralim",
                "mundane_chakra": "Saturn",
            },
            {
                "number": 4,
                "name": "Chesed",
                "hebrew_name": "חסד",
                "meaning": "Mercy",
                "archangel": "Tzadkiel",
                "order_of_angels": "Hashmalim",
                "mundane_chakra": "Jupiter",
            },
            {
                "number": 5,
                "name": "Geburah",
                "hebrew_name": "גבורה",
                "meaning": "Strength",
                "archangel": "Samael",
                "order_of_angels": "Seraphim",
                "mundane_chakra": "Mars",
            },
            {
                "number": 6,
                "name": "Tiphereth",
                "hebrew_name": "תפארת",
                "meaning": "Beauty",
                "archangel": "Raphael",
                "order_of_angels": "Malachim",
                "mundane_chakra": "Sun",
            },
            {
                "number": 7,
                "name": "Netzach",
                "hebrew_name": "נצח",
                "meaning": "Victory",
                "archangel": "Haniel",
                "order_of_angels": "Tarshishim",
                "mundane_chakra": "Venus",
            },
            {
                "number": 8,
                "name": "Hod",
                "hebrew_name": "הוד",
                "meaning": "Splendor",
                "archangel": "Michael",
                "order_of_angels": "Beni Elohim",
                "mundane_chakra": "Mercury",
            },
            {
                "number": 9,
                "name": "Yesod",
                "hebrew_name": "יסוד",
                "meaning": "Foundation",
                "archangel": "Gabriel",
                "order_of_angels": "Cherubim",
                "mundane_chakra": "Moon",
            },
            {
                "number": 10,
                "name": "Malkuth",
                "hebrew_name": "מלכות",
                "meaning": "Kingdom",
                "archangel": "Sandalphon",
                "order_of_angels": "Ashim",
                "mundane_chakra": "Earth",
            },
        ]
        for spec in specs:
            number = spec.get("number")
            name = spec.get("name")
            if number is None or not name:
                raise ValueError("Sephera spec missing required fields")
            self_hebrew = spec.get("hebrew_name", "")
            self_meaning = spec.get("meaning", "")
            self_archangel = spec.get("archangel", "")
            self_order = spec.get("order_of_angels", "")
            self_mundane = spec.get("mundane_chakra", "")
            cls._sephera[number] = Sephera(
                number=number,
                name=name,
                hebrew_name=self_hebrew,
                meaning=self_meaning,
                archangel=self_archangel,
                order_of_angels=self_order,
                mundane_chakra=self_mundane,
                element=None,
                planetary_ruler=None,
                tarot_trump=None,
            )

    @classmethod
    def _load_colorscales(cls) -> None:
        """Load Golden Dawn color scales for Sephiroth and Paths."""
        # 10 Sephiroth color scales
        sephera_colorscales = [
            (
                "Kether",
                1,
                "Brilliance",
                "White brilliance",
                "White brilliance",
                "White flecked gold",
                "Concealed Light",
            ),
            (
                "Chokmah",
                2,
                "Pure soft blue",
                "Grey",
                "Blue pearl grey",
                "White flecked red, blue, yellow",
                "Sky Blue",
            ),
            ("Binah", 3, "Crimson", "Black", "Dark brown", "Grey flecked pink", "Yellow"),
            (
                "Chesed",
                4,
                "Deep violet",
                "Blue",
                "Deep purple",
                "Deep azure flecked yellow",
                "White",
            ),
            ("Geburah", 5, "Orange", "Scarlet red", "Bright scarlet", "Red flecked black", "Red"),
            (
                "Tiphareth",
                6,
                "Clear pink rose",
                "Yellow (gold)",
                "Rich salmon",
                "Gold amber",
                "White-red",
            ),
            (
                "Netzach",
                7,
                "Amber",
                "Emerald",
                "Bright yellow green",
                "Olive flecked gold",
                "Whitish-red",
            ),
            (
                "Hod",
                8,
                "Violet purple",
                "Orange",
                "Red-russet",
                "Yellow-brown flecked white",
                "Reddish-white",
            ),
            (
                "Yesod",
                9,
                "Indigo",
                "Violet",
                "Very dark purple",
                "Citrine flecked azure",
                "White-red-whitish-red-reddish-white",
            ),
            (
                "Malkuth",
                10,
                "Yellow",
                "Citrine, olive, russet, black",
                "Citrine, olive, russet, black flecked gold",
                "Black rayed yellow",
                "Light reflecting all colours",
            ),
        ]

        for name, number, king, queen, emperor, empress, sephirotic in sephera_colorscales:
            colorscale = Colorscale(
                name=name,
                number=number,
                king_scale=king,
                queen_scale=queen,
                emperor_scale=emperor,
                empress_scale=empress,
                sephirotic_color=sephirotic,
                type="Sephira",
            )
            cls._colorscales[number] = colorscale
            # Update the sephera in _sephera dict with colorscale
            if number in cls._sephera:
                seph = cls._sephera[number]
                # Create a new Sephera with the colorscale attached
                new_seph = Sephera(
                    number=seph.number,
                    name=seph.name,
                    hebrew_name=seph.hebrew_name,
                    meaning=seph.meaning,
                    archangel=seph.archangel,
                    order_of_angels=seph.order_of_angels,
                    mundane_chakra=seph.mundane_chakra,
                    element=seph.element,
                    planetary_ruler=seph.planetary_ruler,
                    tarot_trump=seph.tarot_trump,
                    colorscale=colorscale,
                )
                cls._sephera[number] = new_seph

        # 22 Paths color scales (numbers 11-32)
        path_colorscales = [
            (
                "Aleph",
                11,
                "Bright pale yellow",
                "Sky blue",
                "Blue emerald green",
                "Emerald flecked gold",
            ),
            ("Beth", 12, "Yellow", "Purple", "Grey", "Indigo rayed violet"),
            ("Gimel", 13, "Blue", "Silver", "Cold pale blue", "Silver rayed sky-blue"),
            (
                "Daleth",
                14,
                "Emerald green",
                "Sky blue",
                "Early spring green",
                "Bright rose or cerise rayed pale yellow",
            ),
            ("He", 15, "Scarlet", "Red", "Brilliant flame", "Glowing red"),
            ("Vau", 16, "Red orange", "Deep indigo", "Deep warm olive", "Rich brown"),
            (
                "Zain",
                17,
                "Orange",
                "Pale Mauve",
                "New yellow leather",
                "Reddish grey inclined to mauve",
            ),
            ("Cheth", 18, "Amber", "Maroon", "Rich bright russet", "Dark greenish brown"),
            ("Teth", 19, "Yellow, greenish", "Deep purple", "Grey", "Reddish amber"),
            ("Yod", 20, "Green, yellowish", "Slate grey", "Green grey", "Plum"),
            ("Kaph", 21, "Violet", "Blue", "Rich purple", "Bright blue rayed yellow"),
            ("Lamed", 22, "Emerald Green", "Blue", "Deep Blue-green", "Pale green"),
            ("Mem", 23, "Deep blue", "Sea-green", "Deep olive-green", "White flecked purple"),
            ("Nun", 24, "Green blue", "Dull brown", "Very dark brown", "Livid indigo brown"),
            ("Samekh", 25, "Blue", "Yellow", "Green", "Dark vivid blue"),
            ("Ayin", 26, "Indigo", "Black", "Blue black", "Cold dark grey, near black"),
            ("Pe", 27, "Scarlet", "Red", "Venetian red", "Bright red rayed azure or emerald"),
            (
                "Tzaddi",
                28,
                "Crimson (ultra violet)",
                "Sky blue",
                "Blueish mauve",
                "White tinged purple",
            ),
            (
                "Qoph",
                29,
                "Violet",
                "Buff flecked silver-white",
                "Light translucent pinksh brown",
                "Stone color",
            ),
            ("Resh", 30, "Orange", "Gold yellow", "Rich amber", "Amber rayed red"),
            (
                "Shin",
                31,
                "Glowing orange scarlet",
                "Vermilion",
                "Scarlet flecked gold",
                "Vermilion flecked crimson & emerald",
            ),
            ("Tau", 32, "Indigo", "Black", "Blue black", "Black rayed blue"),
        ]

        for name, number, king, queen, emperor, empress in path_colorscales:
            colorscale = Colorscale(
                name=f"Path of {name}",
                number=number,
                king_scale=king,
                queen_scale=queen,
                emperor_scale=emperor,
                empress_scale=empress,
                type="Path",
            )
            cls._colorscales[number] = colorscale
            # Update the path in _paths dict with colorscale (paths are loaded after this)
            # so we'll do this in _attach_colorscales_to_paths() after paths are loaded

    @classmethod
    def _load_periodic_table(cls) -> None:
        """Load Sephirothic periodic table with cross-correspondences."""
        specs = [
            {
                "number": 1,
                "name": "Unity",
                "element": "Spirit",
                "sephera_number": 1,
                "planet": "None",
                "tarot_trump": "0 - The Fool",
                "keywords": ["Potential", "Source", "Being"],
            },
            {
                "number": 2,
                "name": "Duality",
                "element": "Fire",
                "sephera_number": 2,
                "planet": "None",
                "tarot_trump": "I - The Magician",
                "keywords": ["Polarity", "Will", "Masculine"],
            },
            {
                "number": 3,
                "name": "Triad",
                "element": "Water",
                "sephera_number": 3,
                "planet": "None",
                "tarot_trump": "II - The High Priestess",
                "keywords": ["Understanding", "Receptivity", "Feminine"],
            },
            {
                "number": 4,
                "name": "Stability",
                "element": "Water",
                "sephera_number": 4,
                "planet": "Jupiter",
                "tarot_trump": "IV - The Emperor",
                "keywords": ["Foundation", "Order", "Justice"],
            },
            {
                "number": 5,
                "name": "Severity",
                "element": "Fire",
                "sephera_number": 5,
                "planet": "Mars",
                "tarot_trump": "V - The Hierophant",
                "keywords": ["Strength", "Power", "Discipline"],
            },
            {
                "number": 6,
                "name": "Equilibrium",
                "element": "Air",
                "sephera_number": 6,
                "planet": "Sun",
                "tarot_trump": "VI - The Lovers",
                "keywords": ["Balance", "Integration", "Self"],
            },
            {
                "number": 7,
                "name": "Desire",
                "element": "Water",
                "sephera_number": 7,
                "planet": "Venus",
                "tarot_trump": "VII - The Chariot",
                "keywords": ["Passion", "Victory", "Art"],
            },
            {
                "number": 8,
                "name": "Intellect",
                "element": "Air",
                "sephera_number": 8,
                "planet": "Mercury",
                "tarot_trump": "VIII - Strength",
                "keywords": ["Wisdom", "Communication", "Logic"],
            },
            {
                "number": 9,
                "name": "Foundation",
                "element": "Air",
                "sephera_number": 9,
                "planet": "Moon",
                "tarot_trump": "IX - The Hermit",
                "keywords": ["Dreams", "Subconscious", "Imagination"],
            },
            {
                "number": 10,
                "name": "Kingdom",
                "element": "Earth",
                "sephera_number": 10,
                "planet": "Earth",
                "tarot_trump": "X - Wheel of Fortune",
                "keywords": ["Matter", "Manifestation", "Completion"],
            },
        ]
        for spec in specs:
            entry_number = spec.get("number")
            name = spec.get("name")
            element = spec.get("element")
            planet = spec.get("planet")
            sephera_number = spec.get("sephera_number")
            if entry_number is None or not name:
                raise ValueError("Periodic table spec missing required fields")
            sephera = cls._sephera.get(sephera_number) if sephera_number else None
            element = cls._elements.get(element.lower()) if element else None
            planet = (
                cls._planets.get(planet.lower()) if planet and planet.lower() != "none" else None
            )
            number = cls._numbers.get(entry_number)
            color = number.color if number else None

            cls._periodic_table[entry_number] = PeriodicTable(
                number=entry_number,
                name=name,
                sephera=sephera,
                element=element,
                planet=planet,
                color=color,
                tarot_trump=spec.get("tarot_trump"),
                hebrew_letter=None,
                archangel=sephera.archangel if sephera else None,
                order_of_angels=sephera.order_of_angels if sephera else None,
                keywords=spec.get("keywords", []),
            )  # type: ignore[arg-type]

    @classmethod
    def _color(cls) -> None:
        """Load Crowley 777 colors for all 10 Sephiroth."""
        cls._colors = {
            1: Color(
                "White",
                "#FFFFFF",
                (255, 255, 255),
                "Kether",
                1,
                "Spirit",
                "Atziluth",
                "Pure being",
                ["0 - The Fool"],
            ),
            2: Color(
                "Grey",
                "#808080",
                (128, 128, 128),
                "Chokmah",
                2,
                "Fire",
                "Atziluth",
                "Pure creative force",
                ["I - The Magician"],
            ),
            3: Color(
                "Black",
                "#000000",
                (0, 0, 0),
                "Binah",
                3,
                "Water",
                "Atziluth",
                "Receptive understanding",
                ["II - The High Priestess"],
            ),
            4: Color(
                "Violet",
                "#EE82EE",
                (238, 130, 238),
                "Chesed",
                4,
                "Water",
                "Briah",
                "Mercy and expansion",
                ["IV - The Emperor"],
            ),
            5: Color(
                "Red Scarlet",
                "#FF0000",
                (255, 0, 0),
                "Gevurah",
                5,
                "Fire",
                "Briah",
                "Strength and severity",
                ["V - The Hierophant"],
            ),
            6: Color(
                "Gold",
                "#FFD700",
                (255, 215, 0),
                "Tiphareth",
                6,
                "Fire",
                "Yetzirah",
                "Beauty and harmony",
                ["VI - The Lovers", "XIX - The Sun"],
            ),
            7: Color(
                "Green Emerald",
                "#50C878",
                (80, 200, 120),
                "Netzach",
                7,
                "Water",
                "Yetzirah",
                "Victory and creativity",
                ["VII - The Chariot"],
            ),
            8: Color(
                "Yellow",
                "#FFFF00",
                (255, 255, 0),
                "Hod",
                8,
                "Air",
                "Yetzirah",
                "Intellect and communication",
                ["VIII - Strength"],
            ),
            9: Color(
                "Violet Purple",
                "#9400D3",
                (148, 0, 211),
                "Yesod",
                9,
                "Air",
                "Assiah",
                "Dreams and subconscious",
                ["XVII - The Star"],
            ),
            10: Color(
                "Russet Citrine Olive",
                "#8B4513",
                (139, 69, 19),
                "Malkuth",
                10,
                "Earth",
                "Assiah",
                "Material manifestation",
                ["XXI - The World"],
            ),
        }

    @classmethod
    def _load_numbers(cls) -> None:
        """Load numbers 1-9 with Kabbalistic attributes dynamically from Sephera."""
        cls._numbers = {}

        # Build Number objects dynamically from Sephera data (1-9 map to Sephera 1-9)
        for num in range(1, 10):
            sephera = cls._sephera.get(num)
            periodic = cls._periodic_table.get(num)
            color = cls._colors.get(num)

            if sephera:
                # Calculate compliment (1↔8, 2↔7, 3↔6, 4↔5, 9↔9)
                compliment = 9 - num if num != 9 else 9

                # Get element from Periodic Table
                element_name = periodic.element.name if periodic and periodic.element else "Spirit"

                cls._numbers[num] = Number(
                    value=num,
                    sephera=sephera.name,
                    element=element_name,
                    compliment=compliment,
                    color=color,
                )

    @classmethod
    def _load_ciphers(cls) -> None:
        """Load cipher patterns independent of alphabet association."""
        cipher_specs = [
            {
                "key": "hebrew_standard",
                "name": "Hebrew Standard Cipher",
                "pattern": [
                    1,
                    2,
                    3,
                    4,
                    5,
                    6,
                    7,
                    8,
                    9,
                    10,
                    20,
                    30,
                    40,
                    50,
                    60,
                    70,
                    80,
                    90,
                    100,
                    200,
                    300,
                    400,
                ],
                "default_alphabet": "hebrew",
            },
            {
                "key": "english_simple",
                "name": "English Simple Cipher",
                "pattern": list(range(1, 27)),
                "default_alphabet": "english",
            },
            {
                "key": "greek_isopsephy",
                "name": "Greek Isopsephy Cipher",
                "pattern": [
                    1,
                    2,
                    3,
                    4,
                    5,
                    6,
                    7,
                    8,
                    9,
                    10,
                    20,
                    30,
                    40,
                    50,
                    60,
                    70,
                    80,
                    90,
                    100,
                    200,
                    300,
                    400,
                    500,
                    600,
                    700,
                    800,
                ],
                "default_alphabet": "greek",
            },
            {
                "key": "kabbalah_three_mother",
                "name": "Kabbalistic Three Mother Cipher",
                "pattern": [1, 40, 300],
                "default_alphabet": "hebrew",
                "letter_subset": ["א", "מ", "ש"],
            },
            {
                "key": "reduction",
                "name": "Pythagorean Reduction Cipher",
                "pattern": [1, 2, 3, 4, 5, 6, 7, 8, 9],
                "default_alphabet": "english",
                "cycle": True,
            },
        ]
        cls._ciphers = {}
        for spec in cipher_specs:
            subset = spec.get("letter_subset")
            key = spec["key"].lower()
            cls._ciphers[key] = Cipher(
                name=spec["name"],
                key=key,
                pattern=spec["pattern"],
                cycle=spec.get("cycle", False),
                default_alphabet=spec.get("default_alphabet"),
                letter_subset=set(subset) if subset else None,
                description=spec.get("description", ""),
            )

    @classmethod
    def _load_alphabets(cls) -> None:
        """Load all alphabets (English, Greek, Hebrew)."""
        hebrew_specs = [
            ("א", "Aleph"),
            ("ב", "Beth"),
            ("ג", "Gimel"),
            ("ד", "Daleth"),
            ("ה", "Heh"),
            ("ו", "Vav"),
            ("ז", "Zayin"),
            ("ח", "Cheth"),
            ("ט", "Teth"),
            ("י", "Yod"),
            ("כ", "Kaph"),
            ("ל", "Lamed"),
            ("מ", "Mem"),
            ("נ", "Nun"),
            ("ס", "Samekh"),
            ("ע", "Ayin"),
            ("פ", "Pe"),
            ("צ", "Tzaddi"),
            ("ק", "Qoph"),
            ("ר", "Resh"),
            ("ש", "Shin"),
            ("ת", "Tav"),
        ]

        cls._alphabets = {
            "english": [
                EnglishAlphabet(chr(65 + i), i + 1, "Standard phonetic") for i in range(26)
            ],
            "greek": [
                GreekAlphabet(
                    chr(0x0391 + i) if i < 17 else chr(0x03A3 + i - 17), i + 1, f"Greek_{i+1}"
                )
                for i in range(24)
            ],
            "hebrew": [
                HebrewAlphabet(letter, idx + 1, name, f"Meaning_{idx + 1}")
                for idx, (letter, name) in enumerate(hebrew_specs)
            ],
        }

    @classmethod
    def _load_planets(cls) -> None:
        """Load planetary correspondences with references to numbers and colors."""
        specs = [
            {
                "name": "Sun",
                "symbol": "☉",
                "element": "Fire",
                "ruling_zodiac": ["Leo"],
                "numbers": [6],
                "letters": ["Resh"],
                "keywords": ["Vitality", "Consciousness", "Integration"],
                "description": "Centering principle that radiates will and creativity.",
            },
            {
                "name": "Moon",
                "symbol": "☾",
                "element": "Water",
                "ruling_zodiac": ["Cancer"],
                "numbers": [9],
                "letters": ["Gimel"],
                "keywords": ["Reflection", "Dreams", "Cycles"],
                "description": "Instinctual, reflective, and subconscious currents.",
            },
            {
                "name": "Mercury",
                "symbol": "☿",
                "element": "Air",
                "ruling_zodiac": ["Gemini", "Virgo"],
                "numbers": [8],
                "letters": ["Beth"],
                "keywords": ["Communication", "Analysis", "Movement"],
                "description": "Messenger force linking ideas, language, and commerce.",
            },
            {
                "name": "Venus",
                "symbol": "♀",
                "element": "Water",
                "ruling_zodiac": ["Taurus", "Libra"],
                "numbers": [7],
                "letters": ["Daleth"],
                "keywords": ["Harmony", "Beauty", "Attraction"],
                "description": "Creative cohesion through art, love, and receptivity.",
            },
            {
                "name": "Mars",
                "symbol": "♂",
                "element": "Fire",
                "ruling_zodiac": ["Aries", "Scorpio"],
                "numbers": [5],
                "letters": ["Pe"],
                "keywords": ["Assertion", "Courage", "Catalyst"],
                "description": "Force of action that tests limits and forges change.",
            },
            {
                "name": "Jupiter",
                "symbol": "♃",
                "element": "Air",
                "ruling_zodiac": ["Sagittarius", "Pisces"],
                "numbers": [4],
                "letters": ["Kaph"],
                "keywords": ["Expansion", "Wisdom", "Benevolence"],
                "description": "Amplifying principle that seeks growth and meaning.",
            },
            {
                "name": "Saturn",
                "symbol": "♄",
                "element": "Earth",
                "ruling_zodiac": ["Capricorn", "Aquarius"],
                "numbers": [3],
                "letters": ["Tav"],
                "keywords": ["Structure", "Discipline", "Time"],
                "description": "Form-giving boundary that crystallizes responsibility.",
            },
            {
                "name": "Earth",
                "symbol": "🜃",
                "element": "Earth",
                "ruling_zodiac": ["All"],
                "numbers": [10],
                "letters": ["Heh Final"],
                "keywords": ["Manifestation", "Matter", "Stability"],
                "description": "Physical plane of expression and culmination.",
            },
        ]
        cls._planets = {}
        for spec in specs:
            name = spec.get("name")
            if not name:
                raise ValueError("Planet spec missing 'name'")
            number_refs = spec.get("numbers", [])
            numbers: List[Number] = []
            for num in number_refs:
                number = cls._numbers.get(num)
                if number is not None:
                    numbers.append(number)
            color_key = number_refs[0] if number_refs else None
            color_ref = cls._colors.get(color_key) if color_key is not None else None
            cls._planets[name.lower()] = Planet(
                name=name,
                symbol=spec.get("symbol", ""),
                element=spec.get("element", ""),
                ruling_zodiac=spec.get("ruling_zodiac", []),
                associated_numbers=numbers,
                associated_letters=spec.get("letters", []),
                keywords=spec.get("keywords", []),
                color=color_ref,
                description=spec.get("description", ""),
            )

    @staticmethod
    def _card_for_moment(moment: datetime) -> Card:
        """Return a deterministic card for the provided datetime."""
        deck = Deck()
        minutes_since_year_start = (
            (moment.timetuple().tm_yday - 1) * 24 * 60 + moment.hour * 60 + moment.minute
        )
        index = minutes_since_year_start % len(deck.cards)
        return deck.cards[index]

    @classmethod
    def _load_weekdays(cls) -> None:
        """Load weekdays/weekends with planetary ties."""
        specs = [
            {
                "number": 1,
                "name": "Monday",
                "planet": "Moon",
                "is_weekend": False,
                "keywords": ["Reflection", "Cycles", "Dreams"],
            },
            {
                "number": 2,
                "name": "Tuesday",
                "planet": "Mars",
                "is_weekend": False,
                "keywords": ["Action", "Courage", "Heat"],
            },
            {
                "number": 3,
                "name": "Wednesday",
                "planet": "Mercury",
                "is_weekend": False,
                "keywords": ["Communication", "Commerce", "Adapt"],
            },
            {
                "number": 4,
                "name": "Thursday",
                "planet": "Jupiter",
                "is_weekend": False,
                "keywords": ["Expansion", "Teaching", "Grace"],
            },
            {
                "number": 5,
                "name": "Friday",
                "planet": "Venus",
                "is_weekend": False,
                "keywords": ["Love", "Beauty", "Harmony"],
            },
            {
                "number": 6,
                "name": "Saturday",
                "planet": "Saturn",
                "is_weekend": True,
                "keywords": ["Discipline", "Boundaries", "Structure"],
            },
            {
                "number": 7,
                "name": "Sunday",
                "planet": "Sun",
                "is_weekend": True,
                "keywords": ["Vitality", "Renewal", "Community"],
            },
        ]
        cls._weekdays = {}
        for spec in specs:
            name = spec.get("name")
            number = spec.get("number")
            planet = spec.get("planet")
            if not name or number is None or not planet:
                raise ValueError("Weekday spec missing required fields")
            cls._weekdays[name.lower()] = Weekday(
                number=number,
                name=name,
                planetary_correspondence=planet,
                is_weekend=spec.get("is_weekend", False),
                keywords=spec.get("keywords", []),
            )

    @classmethod
    def _load_clock_hours(cls) -> None:
        """Load a 24-hour clock table with AM/PM designations."""
        chaldean_order = ["Saturn", "Jupiter", "Mars", "Sun", "Venus", "Mercury", "Moon"]
        cls._clock_hours = {}
        for hour in range(24):
            period = "AM" if hour < 12 else "PM"
            hour_12 = hour % 12 or 12
            planet = chaldean_order[hour % len(chaldean_order)]
            emphasis_map = {
                "Saturn": "discipline",
                "Jupiter": "vision",
                "Mars": "courage",
                "Sun": "radiance",
                "Venus": "attraction",
                "Mercury": "intellect",
                "Moon": "intuition",
            }
            description = (
                f"Hour ruled by {planet} emphasizing {emphasis_map.get(planet, 'balance')}."
            )
            cls._clock_hours[hour] = ClockHour(
                hour_24=hour,
                hour_12=hour_12,
                period=period,
                planetary_ruler=planet,
                description=description,
            )

    @classmethod
    def _load_gods(cls) -> None:
        """Load a unified god registry spanning Egyptian, Greek, and Roman pantheons."""
        specs = [
            {
                "name": "Ptah",
                "culture": "Egyptian",
                "pantheon": "Memphite",
                "domains": ["Creation", "Craftsmanship", "Intelligence"],
                "epithets": ["Creator of All", "Father of the Gods"],
                "mythology": "Primordial creator who brought forth all existence through spoken word.",
                "sephera_numbers": [1],
                "elements": ["Spirit"],
            },
            {
                "name": "Nuith",
                "culture": "Egyptian",
                "pantheon": "Thelemic",
                "domains": ["Sky", "Infinity", "Wisdom"],
                "epithets": ["Star Goddess", "Infinite Mother"],
                "mythology": "Sky goddess representing boundless expansion and cosmic wisdom.",
                "sephera_numbers": [2],
                "elements": ["Air"],
            },
            {
                "name": "Maut",
                "culture": "Egyptian",
                "pantheon": "Theban",
                "domains": ["Understanding", "Matter", "Manifestation"],
                "epithets": ["Mother of All", "Cosmic Womb"],
                "mythology": "Great mother goddess embodying the receptive principle of manifestation.",
                "sephera_numbers": [3],
                "planets": ["Saturn"],
                "elements": ["Water"],
                "tarot_trumps": ["II - The Priestess"],
            },
            {
                "name": "Amoun",
                "culture": "Egyptian",
                "pantheon": "Theban",
                "domains": ["Mercy", "Providence", "Prosperity"],
                "epithets": ["The Hidden One", "King of Gods"],
                "mythology": "Hidden supreme force channeling mercy and abundance to all beings.",
                "sephera_numbers": [4],
                "planets": ["Jupiter"],
                "elements": ["Water"],
                "tarot_trumps": ["IV - The Emperor"],
            },
            {
                "name": "Horus",
                "culture": "Egyptian",
                "pantheon": "Solar",
                "domains": ["Strength", "War", "Justice"],
                "epithets": ["The Mighty One", "Avenger"],
                "mythology": "Warrior god embodying divine justice and forceful manifestation.",
                "sephera_numbers": [5],
                "planets": ["Mars"],
                "elements": ["Fire"],
                "tarot_trumps": ["XVI - The Tower"],
            },
            {
                "name": "Ra",
                "culture": "Egyptian",
                "pantheon": "Solar",
                "domains": ["Beauty", "Consciousness", "Solar Force"],
                "epithets": ["Sun God", "Divine Child", "Daily Renewal"],
                "mythology": "Solar deity representing the conscious self and daily renewal of spirit.",
                "sephera_numbers": [6],
                "planets": ["Sun"],
                "elements": ["Fire"],
                "tarot_trumps": ["XIX - The Sun"],
            },
            {
                "name": "Hathoor",
                "culture": "Egyptian",
                "pantheon": "Solar",
                "domains": ["Victory", "Love", "Passion"],
                "epithets": ["Lady of Love", "Mistress of Dance"],
                "mythology": "Goddess of love, joy, and victory through emotional connection.",
                "sephera_numbers": [7],
                "planets": ["Venus"],
                "elements": ["Water"],
                "tarot_trumps": ["III - The Empress"],
            },
            {
                "name": "Thoth",
                "culture": "Egyptian",
                "pantheon": "Hermopolitan",
                "domains": ["Splendor", "Intellect", "Writing"],
                "epithets": ["The Scribe", "Master of Wisdom"],
                "mythology": "God of wisdom, writing, and intellectual mastery of all knowledge.",
                "sephera_numbers": [8],
                "planets": ["Mercury"],
                "elements": ["Air"],
                "tarot_trumps": ["I - The Magus"],
                "path_numbers": [12],
            },
            {
                "name": "Shu",
                "culture": "Egyptian",
                "pantheon": "Heliopolitan",
                "domains": ["Foundation", "Air", "Clarity"],
                "epithets": ["The Lifter", "Wind God"],
                "mythology": "God of air and clarity, lifting consciousness between states of being.",
                "sephera_numbers": [9],
                "elements": ["Air"],
                "planets": ["Moon"],
                "path_numbers": [11],
            },
            {
                "name": "Seb",
                "culture": "Egyptian",
                "pantheon": "Heliopolitan",
                "domains": ["Kingdom", "Earth", "Manifestation"],
                "epithets": ["Earth God", "The Solid One"],
                "mythology": "God of the earth, providing solid foundation for all physical manifestation.",
                "sephera_numbers": [10],
                "planets": ["Earth"],
                "elements": ["Earth"],
                "path_numbers": [32],
            },
            {
                "name": "Apollo",
                "culture": "Greek",
                "pantheon": "Olympian",
                "domains": ["Prophecy", "Music", "Healing", "Light"],
                "epithets": ["Phoebus", "Far-shooter"],
                "planets": ["Sun"],
                "elements": ["Fire"],
                "description": "Solar consciousness integrating harmony and order per Liber 777.",
                "tarot_trumps": ["XIX - The Sun"],
            },
            {
                "name": "Artemis",
                "culture": "Greek",
                "pantheon": "Olympian",
                "domains": ["Cycles", "Protection", "Hunt", "Mysteries"],
                "epithets": ["Selene", "Torch-Bearer"],
                "planets": ["Moon"],
                "elements": ["Water"],
                "tarot_trumps": ["II - The Priestess"],
            },
            {
                "name": "Hermes",
                "culture": "Greek",
                "pantheon": "Olympian",
                "domains": ["Messages", "Alchemy", "Commerce", "Travel"],
                "epithets": ["Trismegistus", "Psychopomp"],
                "planets": ["Mercury"],
                "elements": ["Air"],
                "tarot_trumps": ["I - The Magus"],
            },
            {
                "name": "Aphrodite",
                "culture": "Greek",
                "pantheon": "Olympian",
                "domains": ["Attraction", "Beauty", "Artists", "Union"],
                "epithets": ["Pandemos", "Anadyomene"],
                "planets": ["Venus"],
                "elements": ["Water"],
                "tarot_trumps": ["III - The Empress"],
            },
            {
                "name": "Ares",
                "culture": "Greek",
                "pantheon": "Olympian",
                "domains": ["Conflict", "Courage", "Blood", "Guardianship"],
                "epithets": ["Enyalius"],
                "planets": ["Mars"],
                "elements": ["Fire"],
                "tarot_trumps": ["XVI - The Tower"],
            },
            {
                "name": "Zeus",
                "culture": "Greek",
                "pantheon": "Olympian",
                "domains": ["Thunder", "Law", "Kingship", "Abundance"],
                "epithets": ["Olympios", "Xenios"],
                "planets": ["Jupiter"],
                "elements": ["Air"],
                "tarot_trumps": ["X - Wheel of Fortune"],
            },
            {
                "name": "Cronus",
                "culture": "Greek",
                "pantheon": "Titan",
                "domains": ["Time", "Harvest", "Cycles", "Thresholds"],
                "epithets": ["Lord of the Boundaries"],
                "planets": ["Saturn"],
                "elements": ["Earth"],
                "tarot_trumps": ["XXI - The Universe"],
            },
            {
                "name": "Gaia",
                "culture": "Greek",
                "pantheon": "Primordial",
                "domains": ["Fertility", "Stability", "All Life"],
                "epithets": ["World Mother"],
                "planets": ["Earth"],
                "elements": ["Earth"],
                "tarot_trumps": ["XXI - The Universe"],
            },
            {
                "name": "Sol",
                "culture": "Roman",
                "pantheon": "Imperial",
                "domains": ["Sovereignty", "Vitality", "Clarity"],
                "epithets": ["Invictus"],
                "planets": ["Sun"],
                "elements": ["Fire"],
            },
            {
                "name": "Diana",
                "culture": "Roman",
                "pantheon": "Imperial",
                "domains": ["Moonlight", "Hunt", "Childbirth"],
                "epithets": ["Lucifera"],
                "planets": ["Moon"],
                "elements": ["Water"],
            },
            {
                "name": "Mercury",
                "culture": "Roman",
                "pantheon": "Imperial",
                "domains": ["Trade", "Negotiation", "Speed"],
                "epithets": ["Argeiphontes"],
                "planets": ["Mercury"],
                "elements": ["Air"],
            },
            {
                "name": "Venus",
                "culture": "Roman",
                "pantheon": "Imperial",
                "domains": ["Love", "Gardens", "Magnetism"],
                "epithets": ["Genetrix"],
                "planets": ["Venus"],
                "elements": ["Water"],
            },
            {
                "name": "Mars",
                "culture": "Roman",
                "pantheon": "Imperial",
                "domains": ["Military", "Discipline", "Defense"],
                "epithets": ["Gradivus"],
                "planets": ["Mars"],
                "elements": ["Fire"],
            },
            {
                "name": "Jupiter",
                "culture": "Roman",
                "pantheon": "Imperial",
                "domains": ["Oaths", "Weather", "Senate"],
                "epithets": ["Optimus Maximus"],
                "planets": ["Jupiter"],
                "elements": ["Air"],
            },
            {
                "name": "Saturn",
                "culture": "Roman",
                "pantheon": "Imperial",
                "domains": ["Agriculture", "Ages", "Structure"],
                "epithets": ["Senex"],
                "planets": ["Saturn"],
                "elements": ["Earth"],
            },
            {
                "name": "Terra",
                "culture": "Roman",
                "pantheon": "Imperial",
                "domains": ["Soil", "Abundance", "Protection"],
                "epithets": ["Mater"],
                "planets": ["Earth"],
                "elements": ["Earth"],
            },
        ]

        cls._gods = {}
        for spec in specs:
            name = spec.get("name")
            culture = spec.get("culture")
            pantheon = spec.get("pantheon", culture or "")
            if not name or not culture:
                continue
            sephera_numbers = spec.get("sephera_numbers", [])
            path_numbers = spec.get("path_numbers", [])
            planets = spec.get("planets", [])
            elements = list(spec.get("elements", []))
            zodiac_signs = list(spec.get("zodiac_signs", []))
            associated_planet = None
            associated_numbers: List[Number] = []
            if planets:
                planet = cls._planets.get(planets[0].lower())
                if planet:
                    associated_planet = planet
                    associated_numbers = list(planet.associated_numbers)
                    if not elements and planet.element:
                        elements = [planet.element]
                    if not zodiac_signs:
                        zodiac_signs = list(planet.ruling_zodiac)
            associated_element = None
            if elements:
                element_ref = cls._elements.get(elements[0].lower())
                if element_ref:
                    associated_element = element_ref
            god = God(
                name=name,
                culture=culture,
                pantheon=pantheon,
                domains=spec.get("domains", []),
                epithets=spec.get("epithets", []),
                mythology=spec.get("mythology", spec.get("description", "")),
                sephera_numbers=sephera_numbers,
                path_numbers=path_numbers,
                planets=planets,
                elements=elements,
                zodiac_signs=zodiac_signs,
                associated_planet=associated_planet,
                associated_element=associated_element,
                associated_numbers=associated_numbers,
                tarot_trumps=spec.get("tarot_trumps", []),
                keywords=spec.get("keywords", []),
                description=spec.get("description", spec.get("mythology", "")),
            )  # type: ignore[arg-type]
            cls._gods[name.lower()] = god

    @classmethod
    def _attach_colorscales_to_paths(cls) -> None:
        """Attach colorscales to paths after they are loaded."""
        for path_number, colorscale in list(cls._colorscales.items()):
            if path_number in cls._paths and colorscale.type == "Path":
                path = cls._paths[path_number]
                # Create a new Path with the colorscale
                cls._paths[path_number] = Path(
                    number=path.number,
                    hebrew_letter=path.hebrew_letter,
                    transliteration=path.transliteration,
                    tarot_trump=path.tarot_trump,
                    sephera_from=path.sephera_from,
                    sephera_to=path.sephera_to,
                    element=path.element,
                    planet=path.planet,
                    zodiac_sign=path.zodiac_sign,
                    colorscale=colorscale,
                    perfumes=path.perfumes,
                    gods=path.gods,
                    keywords=path.keywords,
                    description=path.description,
                )

    @classmethod
    def _attach_path_correspondences(cls) -> None:
        """Populate each Path container with its associated gods and perfumes."""
        if not cls._paths:
            return

        for path in cls._paths.values():
            path.perfumes.clear()
            path.gods.clear()

        for perfume in cls._perfumes.values():
            if perfume.path_number is None:
                continue
            path = cls._paths.get(perfume.path_number)
            if path is not None:
                path.add_perfume(perfume)

        god_entries = list(cls._gods.values())
        for path in cls._paths.values():
            sephera_numbers = [
                path.sephera_from.number if path.sephera_from else None,
                path.sephera_to.number if path.sephera_to else None,
            ]
            element_name = path.element.name if path.element else None
            planet_name = path.planet.name if path.planet else None
            zodiac_sign = path.zodiac_sign

            for god in god_entries:
                if path.number in god.path_numbers:
                    path.add_god(god)
                    continue
                if planet_name and any(planet_name.lower() == p.lower() for p in god.planets):
                    path.add_god(god)
                if element_name and any(element_name.lower() == e.lower() for e in god.elements):
                    path.add_god(god)
                if zodiac_sign and any(zodiac_sign.lower() == z.lower() for z in god.zodiac_signs):
                    path.add_god(god)
                if any(num is not None and num in god.sephera_numbers for num in sephera_numbers):
                    path.add_god(god)

    @classmethod
    def _load_perfumes(cls) -> None:
        """Load perfume/incense correspondences for Sephiroth and Paths."""
        specs = [
            # Sephiroth perfumes (Liber 777, Column XLII)
            {
                "name": "Ambergris",
                "sephera": 1,
                "element": None,
                "scent": "Warm, animalic, oceanic",
                "magical_uses": ["Consecration", "Elevation", "Crown work"],
            },
            {
                "name": "Musk",
                "sephera": 2,
                "element": None,
                "scent": "Dense, earthy, masculine",
                "magical_uses": ["Wisdom work", "Sexuality", "Primal force"],
            },
            {
                "name": "Myrrh",
                "sephera": 3,
                "element": None,
                "scent": "Smoky, bitter, resinous",
                "magical_uses": ["Understanding", "Purification", "Death work"],
            },
            {
                "name": "Cedar",
                "sephera": 4,
                "element": None,
                "scent": "Woody, warm, noble",
                "magical_uses": ["Mercy", "Benevolence", "Wealth magic"],
            },
            {
                "name": "Tobacco",
                "sephera": 5,
                "element": None,
                "scent": "Smoky, acrid, masculine",
                "magical_uses": ["Strength", "War", "Courage"],
            },
            {
                "name": "Olibanum",
                "sephera": 6,
                "element": None,
                "scent": "Resinous, sacred, solar",
                "magical_uses": ["Illumination", "Consciousness", "Self-realization"],
            },
            {
                "name": "Benzoin",
                "sephera": 7,
                "element": None,
                "scent": "Sweet, balsamic, warm",
                "magical_uses": ["Love", "Victory", "Passion"],
            },
            {
                "name": "Storax",
                "sephera": 8,
                "element": None,
                "scent": "Sweet, spicy, intellectual",
                "magical_uses": ["Communication", "Intellect", "Mercury work"],
            },
            {
                "name": "Jasmine",
                "sephera": 9,
                "element": None,
                "scent": "Floral, sweet, nocturnal",
                "magical_uses": ["Dreams", "Psychism", "Moon work"],
            },
            {
                "name": "Dittany of Crete",
                "sephera": 10,
                "element": None,
                "scent": "Herbaceous, earthy, grounding",
                "magical_uses": ["Manifestation", "Grounding", "Physical presence"],
            },
            # Path perfumes - Elements (Liber 777, Column XLII)
            {
                "name": "Galbanum",
                "path": 11,
                "element": "Air",
                "scent": "Green, fresh, airy",
                "magical_uses": ["Air element work", "Communication", "Mental clarity"],
            },
            {
                "name": "Onycha",
                "path": 23,
                "element": "Water",
                "scent": "Marine, salty, aquatic",
                "magical_uses": ["Water element", "Emotions", "Subconscious"],
            },
            {
                "name": "Olibanum",
                "path": 31,
                "element": "Fire",
                "scent": "Resinous, sacred fire",
                "magical_uses": ["Fire element", "Spiritual vision", "Illumination"],
            },
            {
                "name": "Storax",
                "path": 32,
                "element": "Earth",
                "scent": "Earthy, heavy, grounding",
                "magical_uses": ["Earth element", "Manifestation", "Stability"],
            },
            # Path perfumes - Planets (Liber 777, Column XLII)
            {
                "name": "Mastic",
                "path": 12,
                "planet": "Mercury",
                "scent": "Resinous, clean, mercurial",
                "magical_uses": ["Communication", "Commerce", "Quick wit"],
            },
            {
                "name": "Camphor",
                "path": 13,
                "planet": "Moon",
                "scent": "Sharp, cool, lunar",
                "magical_uses": ["Dreams", "Psychism", "Nurturing"],
            },
            {
                "name": "Sandalwood",
                "path": 14,
                "planet": "Venus",
                "scent": "Sweet, woody, sensual",
                "magical_uses": ["Love", "Beauty", "Attraction"],
            },
            {
                "name": "Saffron",
                "path": 21,
                "planet": "Jupiter",
                "scent": "Precious, warm, royal",
                "magical_uses": ["Prosperity", "Luck", "Expansion"],
            },
            {
                "name": "Pepper",
                "path": 27,
                "planet": "Mars",
                "scent": "Hot, pungent, aggressive",
                "magical_uses": ["Courage", "Action", "War"],
            },
            {
                "name": "Cinnamon",
                "path": 30,
                "planet": "Sun",
                "scent": "Warm, spicy, solar",
                "magical_uses": ["Success", "Vitality", "Solar power"],
            },
            {
                "name": "Assafoetida",
                "path": 32,
                "planet": "Saturn",
                "scent": "Foul, sulfurous, dark",
                "magical_uses": ["Banishing", "Binding", "Protection"],
            },
            # Path perfumes - Zodiac (sample from Liber 777, Column CLII-CLIV)
            {
                "name": "Dragon's Blood",
                "path": 15,
                "zodiac": "Aries",
                "scent": "Warm, resinous, sharp",
                "magical_uses": ["Martial energy", "Courage", "Victory"],
            },
            {
                "name": "Storax",
                "path": 16,
                "zodiac": "Taurus",
                "scent": "Sweet, earthy, grounding",
                "magical_uses": ["Stability", "Prosperity", "Earth connection"],
            },
            {
                "name": "Wormwood",
                "path": 17,
                "zodiac": "Gemini",
                "scent": "Bitter, green, intellectual",
                "magical_uses": ["Communication", "Travel", "Wit"],
            },
            {
                "name": "Onycha",
                "path": 18,
                "zodiac": "Cancer",
                "scent": "Marine, salty, intuitive",
                "magical_uses": ["Emotions", "Home", "Protection"],
            },
            {
                "name": "Olibanum",
                "path": 19,
                "zodiac": "Leo",
                "scent": "Resinous, solar, regal",
                "magical_uses": ["Confidence", "Creativity", "Leadership"],
            },
            {
                "name": "Narcissus",
                "path": 20,
                "zodiac": "Virgo",
                "scent": "Floral, sweet, discriminating",
                "magical_uses": ["Analysis", "Health", "Service"],
            },
            {
                "name": "Galbanum",
                "path": 22,
                "zodiac": "Libra",
                "scent": "Fresh, balanced, airy",
                "magical_uses": ["Balance", "Justice", "Harmony"],
            },
            {
                "name": "Siamese Benzoin",
                "path": 24,
                "zodiac": "Scorpio",
                "scent": "Sweet, dark, transformative",
                "magical_uses": ["Transformation", "Depth", "Power"],
            },
            {
                "name": "Lign-aloes",
                "path": 25,
                "zodiac": "Sagittarius",
                "scent": "Woody, spicy, expansive",
                "magical_uses": ["Expansion", "Prophecy", "Adventure"],
            },
            {
                "name": "Musk",
                "path": 26,
                "zodiac": "Capricorn",
                "scent": "Dense, earthy, grounding",
                "magical_uses": ["Ambition", "Authority", "Structure"],
            },
            {
                "name": "Galbanum",
                "path": 28,
                "zodiac": "Aquarius",
                "scent": "Fresh, airy, visionary",
                "magical_uses": ["Innovation", "Friendship", "Vision"],
            },
            {
                "name": "Ambergris",
                "path": 29,
                "zodiac": "Pisces",
                "scent": "Warm, oceanic, mystical",
                "magical_uses": ["Spirituality", "Compassion", "Mysticism"],
            },
        ]

        cls._perfumes = {}
        for spec in specs:
            perfume_name = spec.get("name", "")
            sephera_num = spec.get("sephera")
            path_num = spec.get("path")

            perfume = Perfume(
                name=perfume_name,
                scent_profile=spec.get("scent", ""),
                sephera_number=sephera_num,
                path_number=path_num,
                element=spec.get("element"),
                planet=spec.get("planet"),
                zodiac_sign=spec.get("zodiac"),
                keywords=[],
                magical_uses=spec.get("magical_uses", []),
                description=spec.get("scent", ""),
            )
            cls._perfumes[perfume_name.lower()] = perfume

    @classmethod
    def _load_paths(cls) -> None:
        """Load the 22 Paths with full Tree of Life correspondences."""
        # Path specifications mapping to Thelemapedia data
        path_specs = [
            {
                "num": 11,
                "hebrew": "Aleph",
                "trans": "Aleph",
                "trump": "0 - The Fool",
                "from": 1,
                "to": 2,
                "element": "Air",
                "planet": None,
                "zodiac": None,
            },
            {
                "num": 12,
                "hebrew": "Beth",
                "trans": "Beth",
                "trump": "I - The Magus",
                "from": 1,
                "to": 3,
                "element": None,
                "planet": "Mercury",
                "zodiac": None,
            },
            {
                "num": 13,
                "hebrew": "Gimel",
                "trans": "Gimel",
                "trump": "II - The Priestess",
                "from": 2,
                "to": 3,
                "element": None,
                "planet": "Moon",
                "zodiac": None,
            },
            {
                "num": 14,
                "hebrew": "Daleth",
                "trans": "Daleth",
                "trump": "III - The Empress",
                "from": 2,
                "to": 4,
                "element": None,
                "planet": "Venus",
                "zodiac": None,
            },
            {
                "num": 15,
                "hebrew": "Hé",
                "trans": "He",
                "trump": "IV - The Emperor",
                "from": 2,
                "to": 5,
                "element": None,
                "planet": None,
                "zodiac": "Aries",
            },
            {
                "num": 16,
                "hebrew": "Vau",
                "trans": "Vav",
                "trump": "V - The Hierophant",
                "from": 3,
                "to": 4,
                "element": None,
                "planet": None,
                "zodiac": "Taurus",
            },
            {
                "num": 17,
                "hebrew": "Zain",
                "trans": "Zayin",
                "trump": "VI - The Lovers",
                "from": 3,
                "to": 5,
                "element": None,
                "planet": None,
                "zodiac": "Gemini",
            },
            {
                "num": 18,
                "hebrew": "Cheth",
                "trans": "Chet",
                "trump": "VII - The Chariot",
                "from": 4,
                "to": 5,
                "element": None,
                "planet": None,
                "zodiac": "Cancer",
            },
            {
                "num": 19,
                "hebrew": "Teth",
                "trans": "Tet",
                "trump": "VIII - Strength",
                "from": 4,
                "to": 6,
                "element": None,
                "planet": None,
                "zodiac": "Leo",
            },
            {
                "num": 20,
                "hebrew": "Yod",
                "trans": "Yod",
                "trump": "IX - The Hermit",
                "from": 5,
                "to": 6,
                "element": None,
                "planet": None,
                "zodiac": "Virgo",
            },
            {
                "num": 21,
                "hebrew": "Kaph",
                "trans": "Kaph",
                "trump": "X - Wheel of Fortune",
                "from": 4,
                "to": 7,
                "element": None,
                "planet": "Jupiter",
                "zodiac": None,
            },
            {
                "num": 22,
                "hebrew": "Lamed",
                "trans": "Lamed",
                "trump": "XI - Justice",
                "from": 5,
                "to": 8,
                "element": None,
                "planet": None,
                "zodiac": "Libra",
            },
            {
                "num": 23,
                "hebrew": "Mem",
                "trans": "Mem",
                "trump": "XII - The Hanged Man",
                "from": 7,
                "to": 8,
                "element": "Water",
                "planet": None,
                "zodiac": None,
            },
            {
                "num": 24,
                "hebrew": "Nun",
                "trans": "Nun",
                "trump": "XIII - Death",
                "from": 7,
                "to": 9,
                "element": None,
                "planet": None,
                "zodiac": "Scorpio",
            },
            {
                "num": 25,
                "hebrew": "Samekh",
                "trans": "Samekh",
                "trump": "XIV - Temperance",
                "from": 6,
                "to": 9,
                "element": None,
                "planet": None,
                "zodiac": "Sagittarius",
            },
            {
                "num": 26,
                "hebrew": "Ayin",
                "trans": "Ayin",
                "trump": "XV - The Devil",
                "from": 8,
                "to": 9,
                "element": None,
                "planet": None,
                "zodiac": "Capricorn",
            },
            {
                "num": 27,
                "hebrew": "Pé",
                "trans": "Pe",
                "trump": "XVI - The Tower",
                "from": 5,
                "to": 8,
                "element": None,
                "planet": "Mars",
                "zodiac": None,
            },
            {
                "num": 28,
                "hebrew": "Tzaddi",
                "trans": "Tzade",
                "trump": "XVII - The Star",
                "from": 8,
                "to": 9,
                "element": None,
                "planet": None,
                "zodiac": "Aquarius",
            },
            {
                "num": 29,
                "hebrew": "Qoph",
                "trans": "Qof",
                "trump": "XVIII - The Moon",
                "from": 7,
                "to": 9,
                "element": None,
                "planet": None,
                "zodiac": "Pisces",
            },
            {
                "num": 30,
                "hebrew": "Resh",
                "trans": "Resh",
                "trump": "XIX - The Sun",
                "from": 6,
                "to": 9,
                "element": None,
                "planet": "Sun",
                "zodiac": None,
            },
            {
                "num": 31,
                "hebrew": "Shin",
                "trans": "Shin",
                "trump": "XX - The Aeon",
                "from": 9,
                "to": 10,
                "element": "Fire",
                "planet": None,
                "zodiac": None,
            },
            {
                "num": 32,
                "hebrew": "Tau",
                "trans": "Tau",
                "trump": "XXI - The Universe",
                "from": 10,
                "to": 1,
                "element": "Earth",
                "planet": None,
                "zodiac": None,
            },
        ]

        cls._paths = {}
        for spec in path_specs:
            path_num = spec["num"]
            sephera_from = cls._sephera.get(spec["from"])
            sephera_to = cls._sephera.get(spec["to"])
            element = cls._elements.get(spec["element"].lower()) if spec["element"] else None
            planet = cls._planets.get(spec["planet"].lower()) if spec["planet"] else None

            path = Path(
                number=path_num,
                hebrew_letter=spec["hebrew"],
                transliteration=spec["trans"],
                tarot_trump=spec["trump"],
                sephera_from=sephera_from,
                sephera_to=sephera_to,
                element=element,
                planet=planet,
                zodiac_sign=spec.get("zodiac"),
                description=f"Path {path_num}: {spec['trump']}",
            )
            cls._paths[path_num] = path

    @overload
    def number(self, value: int) -> Optional[Number]: ...

    @overload
    def number(self, value: None = ...) -> Dict[int, Number]: ...

    def number(self, value: Optional[int] = None) -> Union[Optional[Number], Dict[int, Number]]:
        """Return an individual Number or the full numerology table."""
        if value is None:
            return self._numbers.copy()
        return self._numbers.get(value)

    @overload
    def color(self, sephera_number: int) -> Optional[Color]: ...

    @overload
    def color(self, sephera_number: None = ...) -> Dict[int, Color]: ...

    def color(
        self, sephera_number: Optional[int] = None
    ) -> Union[Optional[Color], Dict[int, Color]]:
        """Return a single color correspondence or the entire map."""
        if sephera_number is None:
            return self._colors.copy()
        return self._colors.get(sephera_number)

    @overload
    def planet(self, name: str) -> Optional[Planet]: ...

    @overload
    def planet(self, name: None = ...) -> Dict[str, Planet]: ...

    def planet(self, name: Optional[str] = None) -> Union[Optional[Planet], Dict[str, Planet]]:
        """Return a planet entry or the full planetary registry."""
        if name is None:
            return self._planets.copy()
        return self._planets.get(name.lower())

    @overload
    def weekday(self, name: str) -> Optional[Weekday]: ...

    @overload
    def weekday(self, name: None = ...) -> Dict[str, Weekday]: ...

    def weekday(self, name: Optional[str] = None) -> Union[Optional[Weekday], Dict[str, Weekday]]:
        """Return a weekday correspondence or the entire set."""
        if name is None:
            return self._weekdays.copy()
        return self._weekdays.get(name.lower())

    @overload
    def clock_hour(self, hour_24: int) -> Optional[ClockHour]: ...

    @overload
    def clock_hour(self, hour_24: None = ...) -> Dict[int, ClockHour]: ...

    def clock_hour(
        self, hour_24: Optional[int] = None
    ) -> Union[Optional[ClockHour], Dict[int, ClockHour]]:
        """Return a planetary hour or the entire 24-hour map."""
        if hour_24 is None:
            return self._clock_hours.copy()
        return self._clock_hours.get(hour_24)

    @overload
    def sephera(self, number: int) -> Optional[Sephera]: ...

    @overload
    def sephera(self, number: None = ...) -> Dict[int, Sephera]: ...

    def sephera(self, number: Optional[int] = None) -> Union[Optional[Sephera], Dict[int, Sephera]]:
        """Return a Sephira or the entire Tree of Life mapping."""
        if number is None:
            return self._sephera.copy()
        return self._sephera.get(number)

    @overload
    def element(self, name: str) -> Optional[ElementType]: ...

    @overload
    def element(self, name: None = ...) -> Dict[str, ElementType]: ...

    def element(
        self, name: Optional[str] = None
    ) -> Union[Optional[ElementType], Dict[str, ElementType]]:
        """Return a single element or the registry of elements."""
        if name is None:
            return self._elements.copy()
        return self._elements.get(name.lower())

    @overload
    def periodic_entry(self, number: int) -> Optional[PeriodicTable]: ...

    @overload
    def periodic_entry(self, number: None = ...) -> Dict[int, PeriodicTable]: ...

    def periodic_entry(
        self, number: Optional[int] = None
    ) -> Union[Optional[PeriodicTable], Dict[int, PeriodicTable]]:
        """Return a periodic table correspondence or the entire dataset."""
        if number is None:
            return self._periodic_table.copy()
        return self._periodic_table.get(number)

    def color_by_number(self, number: int) -> Optional[Color]:
        """Get a Color by mapping a number through digital root."""
        dr = calculate_digital_root(number)
        return self._colors.get(dr)

    def number_by_digital_root(self, value: int) -> Optional[Number]:
        """Get a Number object using digital root calculation."""
        dr = calculate_digital_root(value)
        return self._numbers.get(dr)

    def digital_root(self, value: int) -> int:
        """Get the digital root of a value."""
        return calculate_digital_root(value)

    @overload
    def god(self, name: str, *, culture: Optional[str] = None) -> Optional[God]: ...

    @overload
    def god(self, name: None = ..., *, culture: Optional[str] = None) -> Dict[str, God]: ...

    def god(
        self, name: Optional[str] = None, *, culture: Optional[str] = None
    ) -> Union[Optional[God], Dict[str, God]]:
        """Return a god entry or the entire god registry, optionally filtered by culture."""
        if name is None:
            if culture is None:
                return self._gods.copy()
            # Return all gods of the specified culture
            return {
                k: v
                for k, v in self._gods.items()
                if v.culture and v.culture.lower() == culture.lower()
            }

        god_entry = self._gods.get(name.lower())
        if (
            god_entry
            and culture
            and god_entry.culture
            and god_entry.culture.lower() != culture.lower()
        ):
            return None
        return god_entry

    def gods_by_culture(self, culture: str) -> Dict[str, God]:
        """Get all gods associated with a specific culture."""
        return {
            k: v
            for k, v in self._gods.items()
            if v.culture and v.culture.lower() == culture.lower()
        }

    @overload
    def alphabet(
        self, name: str
    ) -> Optional["EnglishAlphabet | GreekAlphabet | HebrewAlphabet"]: ...

    @overload
    def alphabet(
        self, name: None = ...
    ) -> Dict[str, "EnglishAlphabet | GreekAlphabet | HebrewAlphabet"]: ...

    def alphabet(self, name: Optional[str] = None) -> Union[
        Optional["EnglishAlphabet | GreekAlphabet | HebrewAlphabet"],
        Dict[str, "EnglishAlphabet | GreekAlphabet | HebrewAlphabet"],
    ]:
        """Return an alphabet definition or all alphabets."""
        if name is None:
            return self._alphabets.copy()
        return self._alphabets.get(name.lower())

    @overload
    def letter(self, symbol: str) -> Optional["Letter"]: ...

    @overload
    def letter(self, symbol: None = ...) -> Dict[str, "Letter"]: ...

    def letter(
        self, symbol: Optional[str] = None
    ) -> Union[Optional["Letter"], Dict[str, "Letter"]]:
        """Return a letter entry or all letters."""
        if symbol is None:
            return self._letters.copy()
        return self._letters.get(symbol.upper())

    @overload
    def cipher(self, system_name: str) -> Optional[Cipher]: ...

    @overload
    def cipher(self, system_name: None = ...) -> Dict[str, Cipher]: ...

    def cipher(
        self, system_name: Optional[str] = None
    ) -> Union[Optional[Cipher], Dict[str, Cipher]]:
        """Return a cipher definition or the full cipher registry."""
        if system_name is None:
            return self._ciphers.copy()
        return self._ciphers.get(system_name.lower())

    @overload
    def perfume(self, name: str) -> Optional[Perfume]: ...

    @overload
    def perfume(self, name: None = ...) -> Dict[str, Perfume]: ...

    def perfume(self, name: Optional[str] = None) -> Union[Optional[Perfume], Dict[str, Perfume]]:
        """Return a perfume entry or the entire perfume catalog."""
        if name is None:
            return self._perfumes.copy()
        if not name:
            return None
        return self._perfumes.get(name.lower())

    def perfumes_by_sephera(self, sephera_number: int) -> List[Perfume]:
        """Get all perfumes associated with a Sephira."""
        return [p for p in self._perfumes.values() if p.sephera_number == sephera_number]

    def perfumes_by_path(self, path_number: int) -> List[Perfume]:
        """Get all perfumes associated with a Path."""
        return [p for p in self._perfumes.values() if p.path_number == path_number]

    def perfumes_by_element(self, element: str) -> List[Perfume]:
        """Get all perfumes associated with an element."""
        return [
            p for p in self._perfumes.values() if p.element and p.element.lower() == element.lower()
        ]

    def perfumes_by_zodiac(self, zodiac: str) -> List[Perfume]:
        """Get all perfumes associated with a zodiac sign."""
        return [
            p
            for p in self._perfumes.values()
            if p.zodiac_sign and p.zodiac_sign.lower() == zodiac.lower()
        ]

    @overload
    def path(self, number: int) -> Optional[Path]: ...

    @overload
    def path(self, number: None = ...) -> Dict[int, Path]: ...

    def path(self, number: Optional[int] = None) -> Union[Optional[Path], Dict[int, Path]]:
        """Return a path or the complete path registry."""
        if number is None:
            return self._paths.copy()
        if not 11 <= number <= 32:
            return None
        return self._paths.get(number)

    def paths_by_element(self, element: str) -> List[Path]:
        """Get all paths associated with an element."""
        return [
            p
            for p in self._paths.values()
            if p.element and p.element.name.lower() == element.lower()
        ]

    def paths_by_planet(self, planet: str) -> List[Path]:
        """Get all paths associated with a planet."""
        return [
            p for p in self._paths.values() if p.planet and p.planet.name.lower() == planet.lower()
        ]

    def paths_by_zodiac(self, zodiac: str) -> List[Path]:
        """Get all paths associated with a zodiac sign."""
        return [
            p
            for p in self._paths.values()
            if p.zodiac_sign and p.zodiac_sign.lower() == zodiac.lower()
        ]

    def temporal_correspondence(self, moment: Optional[datetime] = None) -> TemporalCorrespondence:
        """Combine zodiacal, planetary, and card data for a timestamp.

        Args:
            moment: Optional datetime to evaluate. Defaults to current local time.

        Returns:
            TemporalCorrespondence snapshot describing the aligned card, planet,
            number, and related metadata for the supplied moment.
        """

        moment = moment or datetime.now()
        weekday_name = moment.strftime("%A").lower()
        weekday = self._weekdays.get(weekday_name)
        clock_hour = self._clock_hours.get(moment.hour)

        planet: Optional[Planet] = None
        if clock_hour:
            planet = self._planets.get(clock_hour.planetary_ruler.lower())

        number = planet.associated_numbers[0] if planet and planet.associated_numbers else None
        color = number.color if number else None
        letters = list(planet.associated_letters) if planet else []
        card = self._card_for_moment(moment)

        hexagram_number = ((moment.timetuple().tm_yday - 1) % 64) + 1
        # Lazy import to avoid circular dependency
        from letter import hexagram as iching_hexagram

        hexagram_result = iching_hexagram.hexagram.filter(f"number:{hexagram_number}").first()
        hexagram = hexagram_result.data if hexagram_result else None

        return TemporalCorrespondence(
            timestamp=moment,
            weekday=weekday,
            clock_hour=clock_hour,
            card=card,
            planet=planet,
            number=number,
            color=color,
            letters=letters,
            hexagram=hexagram,
        )

    def word(self, text: str, *, alphabet: Optional[str] = None) -> WordCipherRequest:
        """Start a fluent cipher request for the given text."""
        if not text:
            raise ValueError("Text must be a non-empty string")
        return WordCipherRequest(self, text, alphabet)

    def _alphabet_letters(self, alphabet_name: str) -> List[str]:
        """Return the ordered letters for a named alphabet."""
        alphabet = self._alphabets.get(alphabet_name.lower())
        if not alphabet:
            raise ValueError(f"Alphabet '{alphabet_name}' is not available")
        letters: List[str] = []
        for entry in alphabet:
            letter = getattr(entry, "letter", None)
            if not letter:
                raise ValueError("Alphabet entries must expose a 'letter' attribute")
            letters.append(letter)
        return letters

    def _apply_cipher(
        self, *, text: str, cipher_name: str, alphabet_name: Optional[str]
    ) -> CipherResult:
        """Execute the named cipher against the provided text."""
        cipher = self.cipher(cipher_name)
        if not cipher:
            raise ValueError(f"Cipher '{cipher_name}' is not defined")
        target_alphabet = (alphabet_name or cipher.default_alphabet or "").lower()
        if not target_alphabet:
            raise ValueError("Alphabet must be provided when cipher has no default")
        letters = self._alphabet_letters(target_alphabet)
        values = tuple(cipher.encode(text=text, alphabet_letters=letters))
        return CipherResult(word=text, cipher=cipher, alphabet_name=target_alphabet, values=values)

    # Temporal query methods
    def month_info(self, month_num: int) -> Optional[Month]:
        """Return month metadata for the supplied number (1-12)."""
        if 1 <= month_num <= 12:
            month_data = [
                {"name": "January", "zodiac_start": "Capricorn", "zodiac_end": "Aquarius"},
                {"name": "February", "zodiac_start": "Aquarius", "zodiac_end": "Pisces"},
                {"name": "March", "zodiac_start": "Pisces", "zodiac_end": "Aries"},
                {"name": "April", "zodiac_start": "Aries", "zodiac_end": "Taurus"},
                {"name": "May", "zodiac_start": "Taurus", "zodiac_end": "Gemini"},
                {"name": "June", "zodiac_start": "Gemini", "zodiac_end": "Cancer"},
                {"name": "July", "zodiac_start": "Cancer", "zodiac_end": "Leo"},
                {"name": "August", "zodiac_start": "Leo", "zodiac_end": "Virgo"},
                {"name": "September", "zodiac_start": "Virgo", "zodiac_end": "Libra"},
                {"name": "October", "zodiac_start": "Libra", "zodiac_end": "Scorpio"},
                {"name": "November", "zodiac_start": "Scorpio", "zodiac_end": "Sagittarius"},
                {"name": "December", "zodiac_start": "Sagittarius", "zodiac_end": "Capricorn"},
            ]
            data = month_data[month_num - 1]
            name = data.get("name", "")
            zodiac_start = data.get("zodiac_start", "")
            zodiac_end = data.get("zodiac_end", "")
            return Month(
                number=month_num, name=name, zodiac_start=zodiac_start, zodiac_end=zodiac_end
            )
        return None

    def day_info(self, day_num: int) -> Optional[Day]:
        """Return day metadata for the supplied number (1-31)."""
        if 1 <= day_num <= 31:
            day_names = [
                "First",
                "Second",
                "Third",
                "Fourth",
                "Fifth",
                "Sixth",
                "Seventh",
                "Eighth",
                "Ninth",
                "Tenth",
                "Eleventh",
                "Twelfth",
                "Thirteenth",
                "Fourteenth",
                "Fifteenth",
                "Sixteenth",
                "Seventeenth",
                "Eighteenth",
                "Nineteenth",
                "Twentieth",
                "Twenty-first",
                "Twenty-second",
                "Twenty-third",
                "Twenty-fourth",
                "Twenty-fifth",
                "Twenty-sixth",
                "Twenty-seventh",
                "Twenty-eighth",
                "Twenty-ninth",
                "Thirtieth",
                "Thirty-first",
            ]
            planets = ["Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn"]
            planet = planets[(day_num - 1) % 7]
            return Day(number=day_num, name=day_names[day_num - 1], planetary_correspondence=planet)
        return None

    def month(self, month_num: int) -> "TemporalQuery":
        """Start a temporal query for a given month."""
        from ..deck import TemporalQuery

        return TemporalQuery(self, month_num=month_num)
