{"id":13579545,"url":"https://github.com/g-battaglia/kerykeion","last_synced_at":"2026-04-02T18:00:02.230Z","repository":{"id":39878916,"uuid":"284355091","full_name":"g-battaglia/kerykeion","owner":"g-battaglia","description":"Data-Driven Astrology  💫  Kerykeion is a Python library for astrology. It generates SVG charts and extracts detailed structured data for birth charts, synastry, transits, composite charts, and more.","archived":false,"fork":false,"pushed_at":"2026-03-28T14:24:27.000Z","size":54818,"stargazers_count":593,"open_issues_count":4,"forks_count":174,"subscribers_count":24,"default_branch":"main","last_synced_at":"2026-03-28T15:34:16.777Z","etag":null,"topics":["astrologer","astrology","astrology-calculator","astronomical-algorithms","birthchart","charts","composite-chart","data-driven","data-science","ephemeris","planets","python","svg","synastry","tranist-chart","transits","zodiac-algorithm","zodiac-sign","zodiacsign-calculator"],"latest_commit_sha":null,"homepage":"https://kerykeion.net","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/g-battaglia.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":null,"patreon":null,"open_collective":null,"ko_fi":"kerykeion","tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":null}},"created_at":"2020-08-01T23:21:51.000Z","updated_at":"2026-03-26T06:43:44.000Z","dependencies_parsed_at":"2026-01-19T14:09:07.943Z","dependency_job_id":null,"html_url":"https://github.com/g-battaglia/kerykeion","commit_stats":{"total_commits":335,"total_committers":14,"mean_commits":"23.928571428571427","dds":"0.33731343283582094","last_synced_commit":"9450df42e4b7bacba3d3062a88287314218ce492"},"previous_names":[],"tags_count":66,"template":false,"template_full_name":null,"purl":"pkg:github/g-battaglia/kerykeion","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/g-battaglia%2Fkerykeion","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/g-battaglia%2Fkerykeion/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/g-battaglia%2Fkerykeion/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/g-battaglia%2Fkerykeion/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/g-battaglia","download_url":"https://codeload.github.com/g-battaglia/kerykeion/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/g-battaglia%2Fkerykeion/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31312744,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T12:59:32.332Z","status":"ssl_error","status_checked_at":"2026-04-02T12:54:48.875Z","response_time":89,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["astrologer","astrology","astrology-calculator","astronomical-algorithms","birthchart","charts","composite-chart","data-driven","data-science","ephemeris","planets","python","svg","synastry","tranist-chart","transits","zodiac-algorithm","zodiac-sign","zodiacsign-calculator"],"created_at":"2024-08-01T15:01:40.391Z","updated_at":"2026-04-02T18:00:02.205Z","avatar_url":"https://github.com/g-battaglia.png","language":"Python","funding_links":["https://ko-fi.com/kerykeion"],"categories":["Python"],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eKerykeion\u003c/h1\u003e\n\n\u003cdiv align=\"center\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/stars/g-battaglia/kerykeion.svg?logo=github\" alt=\"stars\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/forks/g-battaglia/kerykeion.svg?logo=github\" alt=\"forks\"\u003e\n\u003c/div\u003e\n\u003cdiv align=\"center\"\u003e\n    \u003cimg src=\"https://static.pepy.tech/badge/kerykeion/month\" alt=\"PyPI Downloads\"\u003e\n    \u003cimg src=\"https://static.pepy.tech/badge/kerykeion/week\" alt=\"PyPI Downloads\"\u003e\n    \u003cimg src=\"https://static.pepy.tech/personalized-badge/kerykeion?period=total\u0026units=INTERNATIONAL_SYSTEM\u0026left_color=GREY\u0026right_color=BLUE\u0026left_text=downloads/total\" alt=\"PyPI Downloads\"\u003e\n\u003c/div\u003e\n\u003cdiv align=\"center\"\u003e\n    \u003cimg src=\"https://img.shields.io/pypi/v/kerykeion?label=pypi%20package\" alt=\"Package version\"\u003e\n    \u003cimg src=\"https://img.shields.io/pypi/pyversions/kerykeion.svg\" alt=\"Supported Python versions\"\u003e\n\u003c/div\u003e\n\u003cp align=\"center\"\u003e⭐ Like this project? Star it on GitHub and help it grow! ⭐\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/charts/classic_default_natal.svg\" width=\"540\" alt=\"John Lennon - Natal Chart\"\u003e\n\u003c/p\u003e\n\nKerykeion is a Python library for astrology. It computes planetary and house positions, detects aspects, and generates SVG charts, including birth, synastry, transit, and composite charts. You can also customize which planets to include in your calculations.\n\nThe main goal of this project is to offer a clean, data-driven approach to astrology, making it accessible and programmable.\n\nKerykeion also integrates seamlessly with LLM and AI applications.\n\n## **Web API**\n\nIf you want to use Kerykeion in a web application or for commercial or _closed-source_ purposes, you can try the dedicated web API:\n\n**[AstrologerAPI](https://rapidapi.com/gbattaglia/api/astrologer/pricing)**\n\nIt is [open source](https://github.com/g-battaglia/Astrologer-API) and directly supports this project.\n\n## Table of Contents\n\n- [**Web API**](#web-api)\n- [Table of Contents](#table-of-contents)\n- [Installation](#installation)\n- [Quick Start](#quick-start)\n- [Basic Usage](#basic-usage)\n- [Generate a SVG Chart](#generate-a-svg-chart)\n  - [Birth Chart](#birth-chart)\n  - [External Birth Chart](#external-birth-chart)\n  - [Synastry Chart](#synastry-chart)\n  - [Transit Chart](#transit-chart)\n  - [Solar Return Chart (Dual Wheel)](#solar-return-chart-dual-wheel)\n  - [Solar Return Chart (Single Wheel)](#solar-return-chart-single-wheel)\n  - [Lunar Return Chart](#lunar-return-chart)\n  - [Composite Chart](#composite-chart)\n- [Wheel Only Charts](#wheel-only-charts)\n  - [Birth Chart](#birth-chart-1)\n  - [Wheel Only Birth Chart (External)](#wheel-only-birth-chart-external)\n  - [Synastry Chart](#synastry-chart-1)\n  - [Change the Output Directory](#change-the-output-directory)\n  - [Change Language](#change-language)\n  - [Minified SVG](#minified-svg)\n  - [SVG without CSS Variables](#svg-without-css-variables)\n  - [Grid Only SVG](#grid-only-svg)\n- [Modern Chart Style](#modern-chart-style)\n  - [Modern Birth Chart](#modern-birth-chart)\n  - [Modern Synastry Chart](#modern-synastry-chart)\n  - [Modern Transit Chart](#modern-transit-chart)\n  - [Modern Wheel Only](#modern-wheel-only)\n- [Report Generator](#report-generator)\n  - [Quick Examples](#quick-examples)\n  - [Section Access](#section-access)\n- [AI Context Serializer](#ai-context-serializer)\n  - [Quick Example](#quick-example)\n- [Example: Retrieving Aspects](#example-retrieving-aspects)\n- [Relationship Score](#relationship-score)\n- [Element \\\u0026 Quality Distribution Strategies](#element--quality-distribution-strategies)\n- [Ayanamsa (Sidereal Modes)](#ayanamsa-sidereal-modes)\n- [House Systems](#house-systems)\n- [Perspective Type](#perspective-type)\n- [Themes](#themes)\n- [Alternative Initialization](#alternative-initialization)\n- [Lunar Nodes (Rahu \\\\\u0026 Ketu)](#lunar-nodes-rahu--ketu)\n- [Fixed Stars](#fixed-stars)\n- [JSON Support](#json-support)\n- [Moon Phase Details](#moon-phase-details)\n- [Documentation](#documentation)\n- [Projects built with Kerykeion](#projects-built-with-kerykeion)\n- [Development](#development)\n- [Integrating Kerykeion into Your Project](#integrating-kerykeion-into-your-project)\n- [License](#license)\n- [Contributing](#contributing)\n- [Citations](#citations)\n\n## Installation\n\nKerykeion requires **Python 3.9** or higher.\n\n```bash\npip3 install kerykeion\n```\n\nFor more installation options and environment setup, see the [Getting Started guide](https://www.kerykeion.net/content/docs/).\n\n## Quick Start\n\n```python\nfrom pathlib import Path\nfrom kerykeion import AstrologicalSubjectFactory\nfrom kerykeion.chart_data_factory import ChartDataFactory\nfrom kerykeion.charts.chart_drawer import ChartDrawer\n\nsubject = AstrologicalSubjectFactory.from_birth_data(\n    name=\"Example Person\",\n    year=1990, month=7, day=15,\n    hour=10, minute=30,\n    lng=12.4964,\n    lat=41.9028,\n    tz_str=\"Europe/Rome\",\n    online=False,\n)\n\nchart_data = ChartDataFactory.create_natal_chart_data(subject)\nchart_drawer = ChartDrawer(chart_data=chart_data)\n\noutput_dir = Path(\"charts_output\")\noutput_dir.mkdir(exist_ok=True)\n\nchart_drawer.save_svg(output_path=output_dir, filename=\"example-natal\")\nprint(\"Chart saved to\", (output_dir / \"example-natal.svg\").resolve())\n```\n\nThis script shows the recommended workflow:\n\n1. Create an `AstrologicalSubject` with explicit coordinates and timezone (offline mode).\n2. Build a `ChartDataModel` through `ChartDataFactory`.\n3. Render the SVG via `ChartDrawer`, saving it to a controlled folder (`charts_output`).\n\nUse the same pattern for synastry, composite, transit, or return charts by swapping the factory method.\n\n**📖 More examples: [kerykeion.net/examples](https://www.kerykeion.net/content/examples/)**\n\n## Basic Usage\n\nBelow is a simple example illustrating the creation of an astrological subject and retrieving astrological details:\n\n```python\nfrom kerykeion import AstrologicalSubjectFactory\n\n# Create an instance of the AstrologicalSubjectFactory class.\n# Arguments: Name, year, month, day, hour, minutes, city, nation\njohn = AstrologicalSubjectFactory.from_birth_data(\n    \"John Lennon\", 1940, 10, 9, 18, 30,\n    lng=-2.9833,\n    lat=53.4,\n    tz_str=\"Europe/London\",\n    online=False,\n)\n\n# Retrieve information about the Sun:\nprint(john.sun.model_dump_json())\n# \u003e {\"name\":\"Sun\",\"quality\":\"Cardinal\",\"element\":\"Air\",\"sign\":\"Lib\",\"sign_num\":6,\"position\":16.26789199474399,\"abs_pos\":196.267891994744,\"emoji\":\"♎️\",\"point_type\":\"AstrologicalPoint\",\"house\":\"Sixth_House\",\"retrograde\":false}\n\n# Retrieve information about the first house:\nprint(john.first_house.model_dump_json())\n# \u003e {\"name\":\"First_House\",\"quality\":\"Cardinal\",\"element\":\"Fire\",\"sign\":\"Ari\",\"sign_num\":0,\"position\":19.74676624176799,\"abs_pos\":19.74676624176799,\"emoji\":\"♈️\",\"point_type\":\"House\",\"house\":null,\"retrograde\":null}\n\n# Retrieve the element of the Moon sign:\nprint(john.moon.element)\n# \u003e 'Air'\n```\n\n\u003e **Working offline:** pass `online=False` and specify `lng`, `lat`, and `tz_str` as shown above.  \n\u003e **Working online:** set `online=True` and provide `city`, `nation`, and a valid GeoNames username. Register for free at [geonames.org](https://www.geonames.org/login). You can set the username via the `KERYKEION_GEONAMES_USERNAME` environment variable or the `geonames_username` parameter.\n\n**📖 Full factory documentation: [AstrologicalSubjectFactory](https://www.kerykeion.net/content/docs/astrological_subject_factory)**\n\n**To avoid GeoNames, provide longitude, latitude, and timezone and set `online=False`:**\n\n```python\njohn = AstrologicalSubjectFactory.from_birth_data(\n    \"John Lennon\", 1940, 10, 9, 18, 30,\n    city=\"Liverpool\",\n    nation=\"GB\",\n    lng=-2.9833,  # Longitude for Liverpool\n    lat=53.4000,  # Latitude for Liverpool\n    tz_str=\"Europe/London\",  # Timezone for Liverpool\n    online=False,\n)\n```\n\n## Generate a SVG Chart\n\nAll chart-rendering examples below create a local `charts_output/` folder so the tests can write without touching your home directory. Feel free to change the path when integrating into your own projects.\n\nTo generate a chart, use the `ChartDataFactory` to pre-compute chart data, then `ChartDrawer` to create the visualization. This two-step process ensures clean separation between astrological calculations and chart rendering.\n\n**📖 Chart generation docs: [Charts Documentation](https://www.kerykeion.net/content/docs/charts)**\n\n**Tip:**\nThe optimized way to open the generated SVG files is with a web browser (e.g., Chrome, Firefox).\nTo improve compatibility across different applications, you can use the `remove_css_variables` parameter when generating the SVG. This will inline all styles and eliminate CSS variables, resulting in an SVG that is more broadly supported.\n\n### Birth Chart\n\n```python\nfrom pathlib import Path\nfrom kerykeion import AstrologicalSubjectFactory\nfrom kerykeion.chart_data_factory import ChartDataFactory\nfrom kerykeion.charts.chart_drawer import ChartDrawer\n\n# Step 1: Create subject\njohn = AstrologicalSubjectFactory.from_birth_data(\n    \"John Lennon\", 1940, 10, 9, 18, 30,\n    lng=-2.9833,\n    lat=53.4,\n    tz_str=\"Europe/London\",\n    online=False,\n)\n\n# Step 2: Pre-compute chart data\nchart_data = ChartDataFactory.create_natal_chart_data(john)\n\n# Step 3: Create visualization\nbirth_chart_svg = ChartDrawer(chart_data=chart_data)\n\noutput_dir = Path(\"charts_output\")\noutput_dir.mkdir(exist_ok=True)\nbirth_chart_svg.save_svg(output_path=output_dir, filename=\"john-lennon-natal\")\n```\n\nThe SVG file is saved under `charts_output/john-lennon-natal.svg`.\n\n**📖 More birth chart examples: [Birth Chart Guide](https://www.kerykeion.net/content/examples/birth-chart)**\n\n![John Lennon Birth Chart](https://raw.githubusercontent.com/g-battaglia/kerykeion/main/tests/data/svg/John%20Lennon%20-%20Natal%20Chart.svg)\n\n### External Birth Chart\n\nAn \"external\" birth chart places the zodiac wheel on the outer ring, offering an alternative visualization style:\n\n```python\nfrom pathlib import Path\nfrom kerykeion import AstrologicalSubjectFactory\nfrom kerykeion.chart_data_factory import ChartDataFactory\nfrom kerykeion.charts.chart_drawer import ChartDrawer\n\n# Step 1: Create subject\nbirth_chart = AstrologicalSubjectFactory.from_birth_data(\n    \"John Lennon\", 1940, 10, 9, 18, 30,\n    lng=-2.9833,\n    lat=53.4,\n    tz_str=\"Europe/London\",\n    online=False,\n)\n\n# Step 2: Pre-compute chart data for external natal chart\nchart_data = ChartDataFactory.create_natal_chart_data(birth_chart)\n\n# Step 3: Create visualization with external_view=True\nbirth_chart_svg = ChartDrawer(chart_data=chart_data, external_view=True)\n\noutput_dir = Path(\"charts_output\")\noutput_dir.mkdir(exist_ok=True)\nbirth_chart_svg.save_svg(output_path=output_dir, filename=\"john-lennon-natal-external\")\n```\n\n![John Lennon External Birth Chart](https://raw.githubusercontent.com/g-battaglia/kerykeion/main/tests/data/svg/John%20Lennon%20-%20ExternalNatal%20-%20Natal%20Chart.svg)\n\n### Synastry Chart\n\nSynastry charts overlay two individuals' planetary positions to analyze relationship compatibility:\n\n```python\nfrom pathlib import Path\nfrom kerykeion import AstrologicalSubjectFactory\nfrom kerykeion.chart_data_factory import ChartDataFactory\nfrom kerykeion.charts.chart_drawer import ChartDrawer\n\n# Step 1: Create subjects\nfirst = AstrologicalSubjectFactory.from_birth_data(\n    \"John Lennon\", 1940, 10, 9, 18, 30,\n    lng=-2.9833,\n    lat=53.4,\n    tz_str=\"Europe/London\",\n    online=False,\n)\nsecond = AstrologicalSubjectFactory.from_birth_data(\n    \"Paul McCartney\", 1942, 6, 18, 15, 30,\n    lng=-2.9833,\n    lat=53.4,\n    tz_str=\"Europe/London\",\n    online=False,\n)\n\n# Step 2: Pre-compute synastry chart data\nchart_data = ChartDataFactory.create_synastry_chart_data(first, second)\n\n# Step 3: Create visualization\nsynastry_chart = ChartDrawer(chart_data=chart_data)\n\noutput_dir = Path(\"charts_output\")\noutput_dir.mkdir(exist_ok=True)\nsynastry_chart.save_svg(output_path=output_dir, filename=\"lennon-mccartney-synastry\")\n```\n\n**📖 Synastry chart guide: [Synastry Chart Examples](https://www.kerykeion.net/content/examples/synastry-chart)**\n\n![John Lennon and Paul McCartney Synastry](https://raw.githubusercontent.com/g-battaglia/kerykeion/main/tests/data/svg/John%20Lennon%20-%20Synastry%20Chart.svg)\n\n### Transit Chart\n\nTransit charts compare current planetary positions against a natal chart:\n\n```python\nfrom pathlib import Path\nfrom kerykeion import AstrologicalSubjectFactory\nfrom kerykeion.chart_data_factory import ChartDataFactory\nfrom kerykeion.charts.chart_drawer import ChartDrawer\n\n# Step 1: Create subjects\ntransit = AstrologicalSubjectFactory.from_birth_data(\n    \"Transit\", 2025, 6, 8, 8, 45,\n    lng=-84.3880,\n    lat=33.7490,\n    tz_str=\"America/New_York\",\n    online=False,\n)\nsubject = AstrologicalSubjectFactory.from_birth_data(\n    \"John Lennon\", 1940, 10, 9, 18, 30,\n    lng=-2.9833,\n    lat=53.4,\n    tz_str=\"Europe/London\",\n    online=False,\n)\n\n# Step 2: Pre-compute transit chart data\nchart_data = ChartDataFactory.create_transit_chart_data(subject, transit)\n\n# Step 3: Create visualization\ntransit_chart = ChartDrawer(chart_data=chart_data)\n\noutput_dir = Path(\"charts_output\")\noutput_dir.mkdir(exist_ok=True)\ntransit_chart.save_svg(output_path=output_dir, filename=\"john-lennon-transit\")\n```\n\n**📖 Transit chart guide: [Transit Chart Examples](https://www.kerykeion.net/content/examples/transit-chart)**\n\n![John Lennon Transit Chart](https://raw.githubusercontent.com/g-battaglia/kerykeion/main/tests/data/svg/John%20Lennon%20-%20Transit%20Chart.svg)\n\n### Solar Return Chart (Dual Wheel)\n\nSolar returns calculate the exact moment the Sun returns to its natal position each year:\n\n```python\nfrom pathlib import Path\nfrom kerykeion import AstrologicalSubjectFactory\nfrom kerykeion.planetary_return_factory import PlanetaryReturnFactory\nfrom kerykeion.chart_data_factory import ChartDataFactory\nfrom kerykeion.charts.chart_drawer import ChartDrawer\n\n# Step 1: Create natal subject\njohn = AstrologicalSubjectFactory.from_birth_data(\n    \"John Lennon\", 1940, 10, 9, 18, 30,\n    lng=-2.9833,\n    lat=53.4,\n    tz_str=\"Europe/London\",\n    online=False,\n)\n\n# Step 2: Calculate Solar Return subject (offline example with manual coordinates)\nreturn_factory = PlanetaryReturnFactory(\n    john,\n    lng=-2.9833,\n    lat=53.4000,\n    tz_str=\"Europe/London\",\n    online=False\n)\nsolar_return_subject = return_factory.next_return_from_date(1964, 10, 1, return_type=\"Solar\")\n\n# Step 3: Pre-compute return chart data (dual wheel: natal + solar return)\nchart_data = ChartDataFactory.create_return_chart_data(john, solar_return_subject)\n\n# Step 4: Create visualization\nsolar_return_chart = ChartDrawer(chart_data=chart_data)\n\noutput_dir = Path(\"charts_output\")\noutput_dir.mkdir(exist_ok=True)\nsolar_return_chart.save_svg(output_path=output_dir, filename=\"john-lennon-solar-return-dual\")\n```\n\n**📖 Return chart guide: [Dual Return Chart Examples](https://www.kerykeion.net/content/examples/dual-return-chart)**\n\n![John Lennon Solar Return Chart (Dual Wheel)](https://raw.githubusercontent.com/g-battaglia/kerykeion/main/tests/data/svg/John%20Lennon%20-%20DualReturnChart%20Chart%20-%20Solar%20Return.svg)\n\n### Solar Return Chart (Single Wheel)\n\n```python\nfrom pathlib import Path\nfrom kerykeion import AstrologicalSubjectFactory\nfrom kerykeion.planetary_return_factory import PlanetaryReturnFactory\nfrom kerykeion.chart_data_factory import ChartDataFactory\nfrom kerykeion.charts.chart_drawer import ChartDrawer\n\n# Step 1: Create natal subject\njohn = AstrologicalSubjectFactory.from_birth_data(\n    \"John Lennon\", 1940, 10, 9, 18, 30,\n    lng=-2.9833,\n    lat=53.4,\n    tz_str=\"Europe/London\",\n    online=False,\n)\n\n# Step 2: Calculate Solar Return subject (offline example with manual coordinates)\nreturn_factory = PlanetaryReturnFactory(\n    john,\n    lng=-2.9833,\n    lat=53.4000,\n    tz_str=\"Europe/London\",\n    online=False\n)\nsolar_return_subject = return_factory.next_return_from_date(1964, 10, 1, return_type=\"Solar\")\n\n# Step 3: Build a single-wheel return chart\nchart_data = ChartDataFactory.create_single_wheel_return_chart_data(solar_return_subject)\n\n# Step 4: Create visualization\nsingle_wheel_chart = ChartDrawer(chart_data=chart_data)\n\noutput_dir = Path(\"charts_output\")\noutput_dir.mkdir(exist_ok=True)\nsingle_wheel_chart.save_svg(output_path=output_dir, filename=\"john-lennon-solar-return-single\")\n```\n\n**📖 Planetary return factory docs: [PlanetaryReturnFactory](https://www.kerykeion.net/content/docs/planetary_return_factory)**\n\n![John Lennon Solar Return Chart (Single Wheel)](https://raw.githubusercontent.com/g-battaglia/kerykeion/main/tests/data/svg/John%20Lennon%20Solar%20Return%20-%20SingleReturnChart%20Chart.svg)\n\n### Lunar Return Chart\n\nLunar returns calculate when the Moon returns to its natal position (approximately monthly):\n\n```python\nfrom pathlib import Path\nfrom kerykeion import AstrologicalSubjectFactory\nfrom kerykeion.planetary_return_factory import PlanetaryReturnFactory\nfrom kerykeion.chart_data_factory import ChartDataFactory\nfrom kerykeion.charts.chart_drawer import ChartDrawer\n\n# Step 1: Create natal subject\njohn = AstrologicalSubjectFactory.from_birth_data(\n    \"John Lennon\", 1940, 10, 9, 18, 30,\n    lng=-2.9833,\n    lat=53.4,\n    tz_str=\"Europe/London\",\n    online=False,\n)\n\n# Step 2: Calculate Lunar Return subject\nreturn_factory = PlanetaryReturnFactory(\n    john,\n    lng=-2.9833,\n    lat=53.4000,\n    tz_str=\"Europe/London\",\n    online=False\n)\nlunar_return_subject = return_factory.next_return_from_date(1964, 1, 1, return_type=\"Lunar\")\n\n# Step 3: Build a dual wheel (natal + lunar return)\nlunar_return_chart_data = ChartDataFactory.create_return_chart_data(john, lunar_return_subject)\ndual_wheel_chart = ChartDrawer(chart_data=lunar_return_chart_data)\n\noutput_dir = Path(\"charts_output\")\noutput_dir.mkdir(exist_ok=True)\ndual_wheel_chart.save_svg(output_path=output_dir, filename=\"john-lennon-lunar-return-dual\")\n\n# Optional: create a single-wheel lunar return\nsingle_wheel_data = ChartDataFactory.create_single_wheel_return_chart_data(lunar_return_subject)\nsingle_wheel_chart = ChartDrawer(chart_data=single_wheel_data)\nsingle_wheel_chart.save_svg(output_path=output_dir, filename=\"john-lennon-lunar-return-single\")\n```\n\n![John Lennon Lunar Return Chart (Dual Wheel)](https://raw.githubusercontent.com/g-battaglia/kerykeion/main/tests/data/svg/John%20Lennon%20-%20DualReturnChart%20Chart%20-%20Lunar%20Return.svg)\n\n![John Lennon Lunar Return Chart (Single Wheel)](https://raw.githubusercontent.com/g-battaglia/kerykeion/main/tests/data/svg/John%20Lennon%20Lunar%20Return%20-%20SingleReturnChart%20Chart.svg)\n\n### Composite Chart\n\nComposite charts create a single chart from two individuals' midpoints to represent the relationship entity:\n\n```python\nfrom pathlib import Path\nfrom kerykeion import CompositeSubjectFactory, AstrologicalSubjectFactory\nfrom kerykeion.chart_data_factory import ChartDataFactory\nfrom kerykeion.charts.chart_drawer import ChartDrawer\n\n# Step 1: Create subjects (offline configuration)\nangelina = AstrologicalSubjectFactory.from_birth_data(\n    \"Angelina Jolie\", 1975, 6, 4, 9, 9,\n    lng=-118.2437,\n    lat=34.0522,\n    tz_str=\"America/Los_Angeles\",\n    online=False,\n)\n\nbrad = AstrologicalSubjectFactory.from_birth_data(\n    \"Brad Pitt\", 1963, 12, 18, 6, 31,\n    lng=-96.7069,\n    lat=35.3273,\n    tz_str=\"America/Chicago\",\n    online=False,\n)\n\n# Step 2: Create composite subject\nfactory = CompositeSubjectFactory(angelina, brad)\ncomposite_model = factory.get_midpoint_composite_subject_model()\n\n# Step 3: Pre-compute composite chart data\nchart_data = ChartDataFactory.create_composite_chart_data(composite_model)\n\n# Step 4: Create visualization\ncomposite_chart = ChartDrawer(chart_data=chart_data)\n\noutput_dir = Path(\"charts_output\")\noutput_dir.mkdir(exist_ok=True)\ncomposite_chart.save_svg(output_path=output_dir, filename=\"jolie-pitt-composite\")\n```\n\n**📖 Composite factory docs: [CompositeSubjectFactory](https://www.kerykeion.net/content/docs/composite_subject_factory)**\n\n![Angelina Jolie and Brad Pitt Composite Chart](https://raw.githubusercontent.com/g-battaglia/kerykeion/main/tests/data/svg/Angelina%20Jolie%20and%20Brad%20Pitt%20Composite%20Chart%20-%20Composite%20Chart.svg)\n\n## Wheel Only Charts\n\nFor _all_ the charts, you can generate a wheel-only chart by using the method `save_wheel_only_svg_file()`:\n\n**📖 Minimalist charts guide: [Wheel Only \u0026 Aspect Grid Charts](https://www.kerykeion.net/content/examples/minimalist-charts-and-aspect-table)**\n\n### Birth Chart\n\n```python\nfrom pathlib import Path\nfrom kerykeion import AstrologicalSubjectFactory\nfrom kerykeion.chart_data_factory import ChartDataFactory\nfrom kerykeion.charts.chart_drawer import ChartDrawer\n\n# Step 1: Create subject\nbirth_chart = AstrologicalSubjectFactory.from_birth_data(\n    \"John Lennon\", 1940, 10, 9, 18, 30,\n    lng=-2.9833,\n    lat=53.4,\n    tz_str=\"Europe/London\",\n    online=False,\n)\n\n# Step 2: Pre-compute chart data\nchart_data = ChartDataFactory.create_natal_chart_data(birth_chart)\n\n# Step 3: Create visualization\nbirth_chart_svg = ChartDrawer(chart_data=chart_data)\n\noutput_dir = Path(\"charts_output\")\noutput_dir.mkdir(exist_ok=True)\nbirth_chart_svg.save_wheel_only_svg_file(output_path=output_dir, filename=\"john-lennon-natal-wheel\")\n```\n\n![John Lennon — Natal Chart (Wheel Only)](https://raw.githubusercontent.com/g-battaglia/kerykeion/main/tests/data/svg/John%20Lennon%20-%20Wheel%20Only%20-%20Natal%20Chart%20-%20Wheel%20Only.svg)\n\n### Wheel Only Birth Chart (External)\n\n```python\nfrom pathlib import Path\nfrom kerykeion import AstrologicalSubjectFactory\nfrom kerykeion.chart_data_factory import ChartDataFactory\nfrom kerykeion.charts.chart_drawer import ChartDrawer\n\n# Step 1: Create subject\nbirth_chart = AstrologicalSubjectFactory.from_birth_data(\n    \"John Lennon\", 1940, 10, 9, 18, 30,\n    lng=-2.9833,\n    lat=53.4,\n    tz_str=\"Europe/London\",\n    online=False,\n)\n\n# Step 2: Pre-compute external natal chart data\nchart_data = ChartDataFactory.create_natal_chart_data(birth_chart)\n\n# Step 3: Create visualization (external wheel view)\nbirth_chart_svg = ChartDrawer(chart_data=chart_data, external_view=True)\n\noutput_dir = Path(\"charts_output\")\noutput_dir.mkdir(exist_ok=True)\nbirth_chart_svg.save_wheel_only_svg_file(output_path=output_dir, filename=\"john-lennon-natal-wheel-external\")\n```\n\n![John Lennon — Natal Chart (External Wheel Only)](https://raw.githubusercontent.com/g-battaglia/kerykeion/main/tests/data/svg/John%20Lennon%20-%20Wheel%20External%20Only%20-%20ExternalNatal%20Chart%20-%20Wheel%20Only.svg)\n\n### Synastry Chart\n\n```python\nfrom pathlib import Path\nfrom kerykeion import AstrologicalSubjectFactory\nfrom kerykeion.chart_data_factory import ChartDataFactory\nfrom kerykeion.charts.chart_drawer import ChartDrawer\n\n# Step 1: Create subjects\nfirst = AstrologicalSubjectFactory.from_birth_data(\n    \"John Lennon\", 1940, 10, 9, 18, 30,\n    lng=-2.9833,\n    lat=53.4,\n    tz_str=\"Europe/London\",\n    online=False,\n)\nsecond = AstrologicalSubjectFactory.from_birth_data(\n    \"Paul McCartney\", 1942, 6, 18, 15, 30,\n    lng=-2.9833,\n    lat=53.4,\n    tz_str=\"Europe/London\",\n    online=False,\n)\n\n# Step 2: Pre-compute synastry chart data\nchart_data = ChartDataFactory.create_synastry_chart_data(first, second)\n\n# Step 3: Create visualization\nsynastry_chart = ChartDrawer(chart_data=chart_data)\n\noutput_dir = Path(\"charts_output\")\noutput_dir.mkdir(exist_ok=True)\nsynastry_chart.save_wheel_only_svg_file(output_path=output_dir, filename=\"lennon-mccartney-synastry-wheel\")\n```\n\n![John Lennon and Paul McCartney Synastry](https://raw.githubusercontent.com/g-battaglia/kerykeion/main/tests/data/svg/John%20Lennon%20-%20Wheel%20Synastry%20Only%20-%20Synastry%20Chart%20-%20Wheel%20Only.svg)\n\n### Change the Output Directory\n\nTo save the SVG file in a custom location, specify the `output_path` parameter in `save_svg()`:\n\n```python\nfrom pathlib import Path\nfrom kerykeion import AstrologicalSubjectFactory\nfrom kerykeion.chart_data_factory import ChartDataFactory\nfrom kerykeion.charts.chart_drawer import ChartDrawer\n\n# Step 1: Create subjects\nfirst = AstrologicalSubjectFactory.from_birth_data(\n    \"John Lennon\", 1940, 10, 9, 18, 30,\n    lng=-2.9833,\n    lat=53.4,\n    tz_str=\"Europe/London\",\n    online=False,\n)\nsecond = AstrologicalSubjectFactory.from_birth_data(\n    \"Paul McCartney\", 1942, 6, 18, 15, 30,\n    lng=-2.9833,\n    lat=53.4,\n    tz_str=\"Europe/London\",\n    online=False,\n)\n\n# Step 2: Pre-compute synastry chart data\nchart_data = ChartDataFactory.create_synastry_chart_data(first, second)\n\n# Step 3: Create visualization with custom output directory\nsynastry_chart = ChartDrawer(chart_data=chart_data)\n\noutput_dir = Path(\"charts_output\")\noutput_dir.mkdir(exist_ok=True)\nsynastry_chart.save_svg(output_path=output_dir)\nprint(\"Saved to\", (output_dir / f\"{synastry_chart.first_obj.name} - Synastry Chart.svg\").resolve())\n```\n\n### Change Language\n\nYou can switch chart language by passing `chart_language` to the `ChartDrawer` class:\n\n```python\nfrom pathlib import Path\nfrom kerykeion import AstrologicalSubjectFactory\nfrom kerykeion.chart_data_factory import ChartDataFactory\nfrom kerykeion.charts.chart_drawer import ChartDrawer\n\n# Step 1: Create subject\nbirth_chart = AstrologicalSubjectFactory.from_birth_data(\n    \"John Lennon\", 1940, 10, 9, 18, 30,\n    lng=-2.9833,\n    lat=53.4,\n    tz_str=\"Europe/London\",\n    online=False,\n)\n\n# Step 2: Pre-compute chart data\nchart_data = ChartDataFactory.create_natal_chart_data(birth_chart)\n\n# Step 3: Create visualization with Italian language\nbirth_chart_svg = ChartDrawer(\n    chart_data=chart_data,\n    chart_language=\"IT\"  # Change to Italian\n)\n\noutput_dir = Path(\"charts_output\")\noutput_dir.mkdir(exist_ok=True)\nbirth_chart_svg.save_svg(output_path=output_dir, filename=\"john-lennon-natal-it\")\n```\n\nYou can also provide custom labels (or introduce a brand-new language) by passing\na dictionary to `language_pack`. Only the keys you supply are merged on top of the\nbuilt-in strings:\n\n```python\nfrom pathlib import Path\nfrom kerykeion import AstrologicalSubjectFactory\nfrom kerykeion.chart_data_factory import ChartDataFactory\nfrom kerykeion.charts.chart_drawer import ChartDrawer\n\nbirth_chart = AstrologicalSubjectFactory.from_birth_data(\n    \"John Lennon\", 1940, 10, 9, 18, 30,\n    lng=-2.9833,\n    lat=53.4,\n    tz_str=\"Europe/London\",\n    online=False,\n)\nchart_data = ChartDataFactory.create_natal_chart_data(birth_chart)\n\ncustom_labels = {\n    \"PT\": {\n        \"info\": \"Informações\",\n        \"celestial_points\": {\"Sun\": \"Sol\", \"Moon\": \"Lua\"},\n    }\n}\n\ncustom_chart = ChartDrawer(\n    chart_data=chart_data,\n    chart_language=\"PT\",\n    language_pack=custom_labels[\"PT\"],\n)\n\noutput_dir = Path(\"charts_output\")\noutput_dir.mkdir(exist_ok=True)\ncustom_chart.save_svg(output_path=output_dir, filename=\"john-lennon-natal-pt\")\n```\n\n**📖 Language configuration guide: [Chart Language Settings](https://www.kerykeion.net/content/examples/chart-language)**\n\nThe available languages are:\n\n- EN (English)\n- FR (French)\n- PT (Portuguese)\n- ES (Spanish)\n- TR (Turkish)\n- RU (Russian)\n- IT (Italian)\n- CN (Chinese)\n- DE (German)\n- HI (Hindi)\n\n### Minified SVG\n\nTo generate a minified SVG, set `minify=True` in the `save_svg()` method:\n\n```python\nfrom pathlib import Path\nfrom kerykeion import AstrologicalSubjectFactory\nfrom kerykeion.chart_data_factory import ChartDataFactory\nfrom kerykeion.charts.chart_drawer import ChartDrawer\n\n# Step 1: Create subject\nbirth_chart = AstrologicalSubjectFactory.from_birth_data(\n    \"John Lennon\", 1940, 10, 9, 18, 30,\n    lng=-2.9833,\n    lat=53.4,\n    tz_str=\"Europe/London\",\n    online=False,\n)\n\n# Step 2: Pre-compute chart data\nchart_data = ChartDataFactory.create_natal_chart_data(birth_chart)\n\n# Step 3: Create visualization\nbirth_chart_svg = ChartDrawer(chart_data=chart_data)\n\noutput_dir = Path(\"charts_output\")\noutput_dir.mkdir(exist_ok=True)\nbirth_chart_svg.save_svg(\n    output_path=output_dir,\n    filename=\"john-lennon-natal-minified\",\n    minify=True,\n)\n```\n\n### SVG without CSS Variables\n\nTo generate an SVG without CSS variables, set `remove_css_variables=True` in the `save_svg()` method:\n\n```python\nfrom pathlib import Path\nfrom kerykeion import AstrologicalSubjectFactory\nfrom kerykeion.chart_data_factory import ChartDataFactory\nfrom kerykeion.charts.chart_drawer import ChartDrawer\n\n# Step 1: Create subject\nbirth_chart = AstrologicalSubjectFactory.from_birth_data(\n    \"John Lennon\", 1940, 10, 9, 18, 30,\n    lng=-2.9833,\n    lat=53.4,\n    tz_str=\"Europe/London\",\n    online=False,\n)\n\n# Step 2: Pre-compute chart data\nchart_data = ChartDataFactory.create_natal_chart_data(birth_chart)\n\n# Step 3: Create visualization\nbirth_chart_svg = ChartDrawer(chart_data=chart_data)\n\noutput_dir = Path(\"charts_output\")\noutput_dir.mkdir(exist_ok=True)\nbirth_chart_svg.save_svg(\n    output_path=output_dir,\n    filename=\"john-lennon-natal-no-css-variables\",\n    remove_css_variables=True,\n)\n```\n\nThis will inline all styles and eliminate CSS variables, resulting in an SVG that is more broadly supported.\n\n### Grid Only SVG\n\nIt's possible to generate a grid-only SVG, useful for creating a custom layout. To do this, use the `save_aspect_grid_only_svg_file()` method:\n\n```python\nfrom pathlib import Path\nfrom kerykeion import AstrologicalSubjectFactory\nfrom kerykeion.chart_data_factory import ChartDataFactory\nfrom kerykeion.charts.chart_drawer import ChartDrawer\n\n# Step 1: Create subjects\nbirth_chart = AstrologicalSubjectFactory.from_birth_data(\n    \"John Lennon\", 1940, 10, 9, 18, 30,\n    lng=-2.9833,\n    lat=53.4,\n    tz_str=\"Europe/London\",\n    online=False,\n)\nsecond = AstrologicalSubjectFactory.from_birth_data(\n    \"Paul McCartney\", 1942, 6, 18, 15, 30,\n    lng=-2.9833,\n    lat=53.4,\n    tz_str=\"Europe/London\",\n    online=False,\n)\n\n# Step 2: Pre-compute synastry chart data\nchart_data = ChartDataFactory.create_synastry_chart_data(birth_chart, second)\n\n# Step 3: Create visualization with dark theme\naspect_grid_chart = ChartDrawer(chart_data=chart_data, theme=\"dark\")\n\noutput_dir = Path(\"charts_output\")\noutput_dir.mkdir(exist_ok=True)\naspect_grid_chart.save_aspect_grid_only_svg_file(output_path=output_dir, filename=\"lennon-mccartney-aspect-grid\")\n```\n\n![John Lennon — Aspect Grid](https://raw.githubusercontent.com/g-battaglia/kerykeion/main/tests/data/svg/John%20Lennon%20-%20Aspect%20Grid%20Only%20-%20Natal%20Chart%20-%20Aspect%20Grid%20Only.svg)\n\n## Modern Chart Style\n\nAll chart types support a **modern** concentric-ring layout as an alternative to the classic wheel. You can set the style at the instance level via `ChartDrawer(chart_data=..., style=\"modern\")` or per-render via `save_svg(style=\"modern\")`. The modern style works with all six themes.\n\nAvailable `style` values: `\"classic\"` (default) and `\"modern\"`.\n\n**Modern-only keyword arguments** (ignored by the classic style):\n\n| Parameter | Type | Default | Description |\n|---|---|---|---|\n| `show_zodiac_background_ring` | `bool` | `True` | Draw colored zodiac wedges as the outer zodiac annulus around the cusp ring |\n\n**Dual-chart keyword arguments** (Synastry, Transit, Composite, Dual Return):\n\n| Parameter | Type | Default | Description |\n|---|---|---|---|\n| `double_chart_aspect_grid_type` | `str` | `\"list\"` | Aspect grid layout: `\"list\"` (compact vertical list) or `\"table\"` (traditional cross-reference grid) |\n\n**Classic-only constructor arguments** (ignored by the modern style):\n\n| Parameter | Type | Default | Description |\n|---|---|---|---|\n| `show_degree_indicators` | `bool` | `True` | Show degree indicators on planets |\n| `show_aspect_icons` | `bool` | `True` | Show aspect icons on aspect lines |\n\n### Modern Birth Chart\n\n```python\nfrom pathlib import Path\nfrom kerykeion import AstrologicalSubjectFactory\nfrom kerykeion.chart_data_factory import ChartDataFactory\nfrom kerykeion.charts.chart_drawer import ChartDrawer\n\njohn = AstrologicalSubjectFactory.from_birth_data(\n    \"John Lennon\", 1940, 10, 9, 18, 30,\n    lng=-2.9833,\n    lat=53.4,\n    tz_str=\"Europe/London\",\n    online=False,\n)\n\nchart_data = ChartDataFactory.create_natal_chart_data(john)\nchart = ChartDrawer(chart_data=chart_data)\n\noutput_dir = Path(\"charts_output\")\noutput_dir.mkdir(exist_ok=True)\nchart.save_svg(output_path=output_dir, filename=\"john-lennon-modern\", style=\"modern\")\n```\n\n![John Lennon Modern Birth Chart](https://raw.githubusercontent.com/g-battaglia/kerykeion/main/tests/data/svg/John%20Lennon%20-%20Natal%20Chart%20-%20Modern.svg)\n\n### Modern Synastry Chart\n\n```python\nfrom pathlib import Path\nfrom kerykeion import AstrologicalSubjectFactory\nfrom kerykeion.chart_data_factory import ChartDataFactory\nfrom kerykeion.charts.chart_drawer import ChartDrawer\n\njohn = AstrologicalSubjectFactory.from_birth_data(\n    \"John Lennon\", 1940, 10, 9, 18, 30,\n    lng=-2.9833,\n    lat=53.4,\n    tz_str=\"Europe/London\",\n    online=False,\n)\nyoko = AstrologicalSubjectFactory.from_birth_data(\n    \"Yoko Ono\", 1933, 2, 18, 20, 30,\n    lng=139.6917,\n    lat=35.6895,\n    tz_str=\"Asia/Tokyo\",\n    online=False,\n)\n\nchart_data = ChartDataFactory.create_synastry_chart_data(john, yoko)\nchart = ChartDrawer(chart_data=chart_data)\n\noutput_dir = Path(\"charts_output\")\noutput_dir.mkdir(exist_ok=True)\nchart.save_svg(output_path=output_dir, filename=\"lennon-ono-synastry-modern\", style=\"modern\")\n```\n\n![John Lennon Modern Synastry Chart](https://raw.githubusercontent.com/g-battaglia/kerykeion/main/tests/data/svg/John%20Lennon%20-%20Synastry%20Chart%20-%20Modern.svg)\n\n### Modern Transit Chart\n\n```python\nfrom pathlib import Path\nfrom kerykeion import AstrologicalSubjectFactory\nfrom kerykeion.chart_data_factory import ChartDataFactory\nfrom kerykeion.charts.chart_drawer import ChartDrawer\n\njohn = AstrologicalSubjectFactory.from_birth_data(\n    \"John Lennon\", 1940, 10, 9, 18, 30,\n    lng=-2.9833,\n    lat=53.4,\n    tz_str=\"Europe/London\",\n    online=False,\n)\n\ntransit = AstrologicalSubjectFactory.from_birth_data(\n    \"Transit\", 2025, 3, 4, 12, 0,\n    lng=-2.9833,\n    lat=53.4,\n    tz_str=\"Europe/London\",\n    online=False,\n)\n\nchart_data = ChartDataFactory.create_transit_chart_data(john, transit)\nchart = ChartDrawer(chart_data=chart_data)\n\noutput_dir = Path(\"charts_output\")\noutput_dir.mkdir(exist_ok=True)\nchart.save_svg(output_path=output_dir, filename=\"lennon-transit-modern\", style=\"modern\")\n```\n\n![John Lennon Modern Transit Chart](https://raw.githubusercontent.com/g-battaglia/kerykeion/main/tests/data/svg/John%20Lennon%20-%20Transit%20Chart%20-%20Modern.svg)\n\n### Modern Wheel Only\n\n```python\nfrom pathlib import Path\nfrom kerykeion import AstrologicalSubjectFactory\nfrom kerykeion.chart_data_factory import ChartDataFactory\nfrom kerykeion.charts.chart_drawer import ChartDrawer\n\njohn = AstrologicalSubjectFactory.from_birth_data(\n    \"John Lennon\", 1940, 10, 9, 18, 30,\n    lng=-2.9833,\n    lat=53.4,\n    tz_str=\"Europe/London\",\n    online=False,\n)\n\nchart_data = ChartDataFactory.create_natal_chart_data(john)\nchart = ChartDrawer(chart_data=chart_data, theme=\"dark\")\n\noutput_dir = Path(\"charts_output\")\noutput_dir.mkdir(exist_ok=True)\nchart.save_wheel_only_svg_file(\n    output_path=output_dir,\n    filename=\"john-lennon-modern-wheel-dark\",\n    style=\"modern\",\n)\n```\n\n![John Lennon Modern Wheel Only](https://raw.githubusercontent.com/g-battaglia/kerykeion/main/tests/data/svg/John%20Lennon%20-%20Natal%20Chart%20-%20Modern%20Wheel%20Only.svg)\n\n**📖 Modern chart examples: [Modern Charts Guide](https://www.kerykeion.net/content/examples/modern-charts)**\n\n## Report Generator\n\n`ReportGenerator` mirrors the chart-type dispatch of `ChartDrawer`. It accepts raw `AstrologicalSubjectModel` instances as well as any `ChartDataModel` produced by `ChartDataFactory`—including natal, composite, synastry, transit, and planetary return charts—and renders the appropriate textual report automatically.\n\n**📖 Full report documentation: [Report Generator Guide](https://www.kerykeion.net/content/docs/report)**\n\n### Quick Examples\n\n```python\nfrom kerykeion import ReportGenerator, AstrologicalSubjectFactory, ChartDataFactory\n\n# Subject-only report\nsubject = AstrologicalSubjectFactory.from_birth_data(\n    \"Sample Natal\", 1990, 7, 21, 14, 45,\n    lng=12.4964,\n    lat=41.9028,\n    tz_str=\"Europe/Rome\",\n    online=False,\n)\nReportGenerator(subject).print_report(include_aspects=False)\n\n# Single-chart data (elements, qualities, aspects enabled)\nnatal_data = ChartDataFactory.create_natal_chart_data(subject)\nReportGenerator(natal_data).print_report(max_aspects=10)\n\n# Dual-chart data (synastry, transit, dual return, …)\npartner = AstrologicalSubjectFactory.from_birth_data(\n    \"Sample Partner\", 1992, 11, 5, 9, 30,\n    lng=12.4964,\n    lat=41.9028,\n    tz_str=\"Europe/Rome\",\n    online=False,\n)\nsynastry_data = ChartDataFactory.create_synastry_chart_data(subject, partner)\nReportGenerator(synastry_data).print_report(max_aspects=12)\n```\n\nEach report contains:\n\n- A chart-aware title summarising the subject(s) and chart type\n- Birth/event metadata and configuration settings\n- Celestial points with sign, position, **daily motion**, **declination**, retrograde flag, and house\n- House cusp tables for every subject involved\n- Lunar phase details when available\n- Element/quality distributions and active configuration summaries (for chart data)\n- Aspect listings tailored for single or dual charts, with symbols for type and movement\n- Dual-chart extras such as house comparisons and relationship scores (when provided by the data)\n\n### Section Access\n\nAll section helpers remain available for targeted output:\n\n```python\nfrom kerykeion import ReportGenerator, AstrologicalSubjectFactory, ChartDataFactory\n\nsubject = AstrologicalSubjectFactory.from_birth_data(\n    \"Sample Natal\", 1990, 7, 21, 14, 45,\n    lng=12.4964,\n    lat=41.9028,\n    tz_str=\"Europe/Rome\",\n    online=False,\n)\nnatal_data = ChartDataFactory.create_natal_chart_data(subject)\n\nreport = ReportGenerator(natal_data)\nsections = report.generate_report(max_aspects=5).split(\"\\n\\n\")\nfor section in sections[:3]:\n    print(section)\n```\n\n**📖 Report examples: [Report Examples](https://www.kerykeion.net/content/examples/report)**\n\n## AI Context Serializer\n\nThe `context_serializer` module transforms Kerykeion data models into precise, non-qualitative XML optimized for LLM consumption. It provides the essential \"ground truth\" data needed for AI agents to generate accurate astrological interpretations.\n\n**📖 Full context serializer docs: [Context Serializer Guide](https://www.kerykeion.net/content/docs/context_serializer)**\n\n### Quick Example\n\n```python\nfrom kerykeion import AstrologicalSubjectFactory, to_context\n\n# Create a subject\nsubject = AstrologicalSubjectFactory.from_birth_data(\n    \"John Doe\", 1990, 1, 1, 12, 0,\n    city=\"London\",\n    nation=\"GB\",\n    lng=-0.1278,\n    lat=51.5074,\n    tz_str=\"Europe/London\",\n    online=False,\n)\n\n# Generate AI-ready context\ncontext = to_context(subject)\nprint(context)\n```\n\n**Output:**\n\n```xml\n\u003cchart name=\"John Doe\"\u003e\n  \u003cbirth_data date=\"1990-01-01\" time=\"12:00\" city=\"London\" nation=\"GB\" ... /\u003e\n  \u003cconfig zodiac=\"Tropical\" house_system=\"Placidus\" perspective=\"Apparent Geocentric\" /\u003e\n  \u003cplanets\u003e\n    \u003cpoint name=\"Sun\" position=\"10.81\" sign=\"Capricorn\" element=\"Earth\" quality=\"Cardinal\" ... /\u003e\n    \u003cpoint name=\"Moon\" position=\"25.60\" sign=\"Aquarius\" element=\"Air\" quality=\"Fixed\" ... /\u003e\n    ...\n  \u003c/planets\u003e\n  \u003chouses\u003e...\u003c/houses\u003e\n  \u003clunar_phase name=\"Waning Gibbous\" phase=\"20\" degrees_between=\"254.32\" emoji=\"🌖\" /\u003e\n\u003c/chart\u003e\n```\n\n**Key Features:**\n\n- **XML Output:** Well-formed XML with semantic tags, proper escaping, and optional field omission.\n- **Standardized Output:** Consistent format for Natal, Synastry, Composite, and Return charts.\n- **Non-Qualitative:** Provides raw data (positions, aspects) without interpretive bias.\n- **Prompt-Ready:** Designed to be injected directly into system prompts.\n\n## Example: Retrieving Aspects\n\nKerykeion provides a unified `AspectsFactory` class for calculating astrological aspects within single charts or between two charts:\n\n```python\nfrom kerykeion import AspectsFactory, AstrologicalSubjectFactory\n\n# Create astrological subjects\njack = AstrologicalSubjectFactory.from_birth_data(\n    \"Jack\", 1990, 6, 15, 15, 15,\n    lng=12.4964,\n    lat=41.9028,\n    tz_str=\"Europe/Rome\",\n    online=False,\n)\njane = AstrologicalSubjectFactory.from_birth_data(\n    \"Jane\", 1991, 10, 25, 21, 0,\n    lng=12.4964,\n    lat=41.9028,\n    tz_str=\"Europe/Rome\",\n    online=False,\n)\n\n# For single chart aspects (natal, return, composite, etc.)\nsingle_chart_result = AspectsFactory.single_chart_aspects(jack)\nprint(f\"Found {len(single_chart_result.aspects)} aspects in Jack's chart\")\nprint(single_chart_result.aspects[0])\n\n# For dual chart aspects (synastry, transits, comparisons, etc.)\ndual_chart_result = AspectsFactory.dual_chart_aspects(jack, jane)\nprint(f\"Found {len(dual_chart_result.aspects)} aspects between Jack and Jane's charts\")\nprint(dual_chart_result.aspects[0])\n\n# Each AspectModel includes:\n# - p1_name, p2_name: Planet/point names\n# - p1_owner, p2_owner: Subject name string (e.g., \"Jack\", \"Jane\")\n# - aspect: Aspect type (conjunction, trine, square, etc.)\n# - orbit: Actual orb in degrees\n# - aspect_degrees: Exact degrees for the aspect (0, 60, 90, 120, 180, etc.)\n# - diff: Absolute angular difference between the two points\n# - p1_abs_pos, p2_abs_pos: Absolute ecliptic positions\n# - p1_speed, p2_speed: Daily speed of each point\n# - aspect_movement: \"Applying\", \"Separating\", or \"Static\"\n```\n\n**📖 Aspects documentation: [Aspects Factory Guide](https://www.kerykeion.net/content/docs/aspects)**\n\n**Advanced Usage with Custom Settings:**\n\n```python\n# You can also customize aspect calculations with custom orb settings\nfrom kerykeion.settings.config_constants import DEFAULT_ACTIVE_ASPECTS\n\n# Modify aspect settings if needed\ncustom_aspects = DEFAULT_ACTIVE_ASPECTS.copy()\n# ... modify as needed\n\n# The factory automatically uses the configured settings for orb calculations\n# and filters aspects based on relevance and orb thresholds\n```\n\n**📖 Configuration options: [Settings Documentation](https://www.kerykeion.net/content/docs/settings)**\n\n## Relationship Score\n\nKerykeion can calculate a relationship compatibility score based on synastry aspects, using the method of the Italian astrologer **Ciro Discepolo**:\n\n```python\nfrom kerykeion import AstrologicalSubjectFactory\nfrom kerykeion.relationship_score_factory import RelationshipScoreFactory\n\n# Create two subjects\nperson1 = AstrologicalSubjectFactory.from_birth_data(\n    \"Alice\", 1990, 3, 15, 14, 30,\n    lng=12.4964,\n    lat=41.9028,\n    tz_str=\"Europe/Rome\",\n    online=False,\n)\nperson2 = AstrologicalSubjectFactory.from_birth_data(\n    \"Bob\", 1988, 7, 22, 9, 0,\n    lng=12.4964,\n    lat=41.9028,\n    tz_str=\"Europe/Rome\",\n    online=False,\n)\n\n# Calculate relationship score\nscore_factory = RelationshipScoreFactory(person1, person2)\nresult = score_factory.get_relationship_score()\n\nprint(f\"Compatibility Score: {result.score_value}\")\nprint(f\"Description: {result.score_description}\")\n```\n\n**📖 Relationship score guide: [Relationship Score Examples](https://www.kerykeion.net/content/examples/relationship-score)**\n\n**📖 Factory documentation: [RelationshipScoreFactory](https://www.kerykeion.net/content/docs/relationship_score_factory)**\n\n## Element \u0026 Quality Distribution Strategies\n\n`ChartDataFactory` now offers two strategies for calculating element and modality totals. The default `\"weighted\"` mode leans on a curated map that emphasises core factors (for example `sun`, `moon`, and `ascendant` weight 2.0, angles such as `medium_coeli` 1.5, personal planets 1.5, social planets 1.0, outers 0.5, and minor bodies 0.3–0.8). Provide `distribution_method=\"pure_count\"` when you want every active point to contribute equally.\n\nYou can refine the weighting without rebuilding the dictionary: pass lowercase point names to `custom_distribution_weights` and use `\"__default__\"` to override the fallback value applied to entries that are not listed explicitly.\n\n```python\nfrom kerykeion import AstrologicalSubjectFactory, ChartDataFactory\n\nsubject = AstrologicalSubjectFactory.from_birth_data(\n    \"Sample\", 1986, 4, 12, 8, 45,\n    lng=11.3426,\n    lat=44.4949,\n    tz_str=\"Europe/Rome\",\n    online=False,\n)\n\n# Equal weighting: every active point counts once\npure_data = ChartDataFactory.create_natal_chart_data(\n    subject,\n    distribution_method=\"pure_count\",\n)\n\n# Custom emphasis: boost the Sun, soften everything else\nweighted_data = ChartDataFactory.create_natal_chart_data(\n    subject,\n    distribution_method=\"weighted\",\n    custom_distribution_weights={\n        \"sun\": 3.0,\n        \"__default__\": 0.75,\n    },\n)\n\nprint(pure_data.element_distribution.fire)\nprint(weighted_data.element_distribution.fire)\n```\n\nAll convenience helpers (`create_synastry_chart_data`, `create_transit_chart_data`, returns, and composites) forward the same keyword-only parameters, so you can keep a consistent weighting scheme across every chart type.\n\n**📖 Element/quality distribution guide: [Distribution Documentation](https://www.kerykeion.net/content/docs/element_quality_distribution)**\n\n## Ayanamsa (Sidereal Modes)\n\nBy default, the zodiac type is **Tropical**. To use **Sidereal**, specify the sidereal mode:\n\n```python\njohnny = AstrologicalSubjectFactory.from_birth_data(\n    \"Johnny Depp\", 1963, 6, 9, 0, 0,\n    lng=-87.1112,\n    lat=37.7719,\n    tz_str=\"America/Chicago\",\n    online=False,\n    zodiac_type=\"Sidereal\",\n    sidereal_mode=\"LAHIRI\"\n)\n\n# The ayanamsa offset (degrees) is available on sidereal charts:\nprint(johnny.ayanamsa_value)  # e.g. 23.85\n```\n\nKerykeion supports **47 named sidereal modes** plus a **USER** mode for custom ayanamsa definitions (48 total). Mode families include Indian/Vedic (Lahiri, Krishnamurti, Raman, Aryabhata, Suryasiddhanta, True Citra/Pushya/Revati, ...), Western sidereal (Fagan-Bradley, DeLuce, Hipparchos, ...), Babylonian (Kugler, Huber, Britton, ...), galactic alignment, and astronomical reference frames (J2000, J1900, B1950).\n\n**Custom ayanamsa (USER mode):**\n\n```python\ncustom = AstrologicalSubjectFactory.from_birth_data(\n    \"Custom Ayanamsa\", 2000, 1, 1, 0, 0,\n    lng=0.0, lat=51.5, tz_str=\"Etc/GMT\", online=False,\n    zodiac_type=\"Sidereal\",\n    sidereal_mode=\"USER\",\n    custom_ayanamsa_t0=2451545.0,      # J2000.0 reference epoch\n    custom_ayanamsa_ayan_t0=23.5,       # ayanamsa offset at epoch (degrees)\n)\n```\n\n**📖 Sidereal mode examples: [Sidereal Modes Guide](https://www.kerykeion.net/content/examples/sidereal-modes/)**\n\n**📖 Full list of supported sidereal modes: [SiderealMode Schema](https://www.kerykeion.net/content/docs/schemas#siderealmode)**\n\n## House Systems\n\nBy default, houses are calculated using **Placidus**. Configure a different house system as follows:\n\n```python\njohnny = AstrologicalSubjectFactory.from_birth_data(\n    \"Johnny Depp\", 1963, 6, 9, 0, 0,\n    lng=-87.1112,\n    lat=37.7719,\n    tz_str=\"America/Chicago\",\n    online=False,\n    houses_system_identifier=\"M\"\n)\n```\n\n**📖 House system examples: [House Systems Guide](https://www.kerykeion.net/content/examples/houses-systems/)**\n\n**📖 Full list of supported house systems: [HouseSystemIdentifier Schema](https://www.kerykeion.net/content/docs/schemas#housesystemidentifier)**\n\nSo far all the available houses system in the Swiss Ephemeris are supported but the Gauquelin Sectors.\n\n## Perspective Type\n\nBy default, Kerykeion uses the **Apparent Geocentric** perspective (the most standard in astrology). Other perspectives (e.g., **Heliocentric**) can be set this way:\n\n```python\njohnny = AstrologicalSubjectFactory.from_birth_data(\n    \"Johnny Depp\", 1963, 6, 9, 0, 0,\n    lng=-87.1112,\n    lat=37.7719,\n    tz_str=\"America/Chicago\",\n    online=False,\n    perspective_type=\"Heliocentric\"\n)\n```\n\n**📖 Perspective type examples: [Perspective Type Guide](https://www.kerykeion.net/content/examples/perspective-type/)**\n\n**📖 Full list of supported perspective types: [PerspectiveType Schema](https://www.kerykeion.net/content/docs/schemas#perspectivetype)**\n\n## Themes\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003cstrong\u003eClassic\u003c/strong\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003cstrong\u003eDark\u003c/strong\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003cstrong\u003eLight\u003c/strong\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003cstrong\u003eBlack \u0026 White\u003c/strong\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003cstrong\u003eClassic Style\u003c/strong\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003cimg src=\"docs/charts/classic_default_natal.svg\" width=\"220\" alt=\"Classic Natal Chart\"\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003cimg src=\"docs/charts/classic_dark_natal.svg\" width=\"220\" alt=\"Dark Natal Chart\"\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003cimg src=\"docs/charts/classic_light_natal.svg\" width=\"220\" alt=\"Light Natal Chart\"\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003cimg src=\"docs/charts/classic_black_and_white_natal.svg\" width=\"220\" alt=\"Black and White Natal Chart\"\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003cstrong\u003eModern Style\u003c/strong\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003cimg src=\"docs/charts/modern_classic_natal.svg\" width=\"220\" alt=\"Modern Classic Natal Chart\"\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003cimg src=\"docs/charts/modern_dark_natal.svg\" width=\"220\" alt=\"Modern Dark Natal Chart\"\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003cimg src=\"docs/charts/modern_light_natal.svg\" width=\"220\" alt=\"Modern Light Natal Chart\"\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003cimg src=\"docs/charts/modern_black_and_white_natal.svg\" width=\"220\" alt=\"Modern Black and White Natal Chart\"\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\nKerykeion provides several chart themes: **Classic** (default), **Dark**, **Light**, and **Black \u0026 White** (optimized for monochrome printing). Each is available in both **classic** and **modern** chart styles.\n\nEach theme offers a distinct visual style, allowing you to choose the one that best suits your preferences or presentation needs. If you prefer more control over the appearance, you can opt not to set any theme, making it easier to customize the chart by overriding the default CSS variables.\n\n**📖 Theming guide with all examples: [Theming Documentation](https://www.kerykeion.net/content/examples/theming)**\n\nThe Black \u0026 White theme renders glyphs, rings, and aspects in solid black on light backgrounds, designed for crisp B/W prints (PDF or paper) without sacrificing legibility.\n\nHere's an example of how to set the theme:\n\n```python\nfrom pathlib import Path\nfrom kerykeion import AstrologicalSubjectFactory\nfrom kerykeion.chart_data_factory import ChartDataFactory\nfrom kerykeion.charts.chart_drawer import ChartDrawer\n\n# Step 1: Create subject\ndark_theme_subject = AstrologicalSubjectFactory.from_birth_data(\n    \"John Lennon - Dark Theme\", 1940, 10, 9, 18, 30,\n    lng=-2.9833,\n    lat=53.4,\n    tz_str=\"Europe/London\",\n    online=False,\n)\n\n# Step 2: Pre-compute chart data\nchart_data = ChartDataFactory.create_natal_chart_data(dark_theme_subject)\n\n# Step 3: Create visualization with dark high contrast theme\ndark_theme_natal_chart = ChartDrawer(chart_data=chart_data, theme=\"dark-high-contrast\")\n\noutput_dir = Path(\"charts_output\")\noutput_dir.mkdir(exist_ok=True)\ndark_theme_natal_chart.save_svg(output_path=output_dir, filename=\"john-lennon-natal-dark-high-contrast\")\n```\n\n![John Lennon](https://www.kerykeion.net/img/showcase/John%20Lennon%20-%20Dark%20-%20Natal%20Chart.svg)\n\n## Alternative Initialization\n\nCreate an `AstrologicalSubjectModel` from a UTC ISO 8601 string:\n\n```python\nfrom kerykeion import AstrologicalSubjectFactory\n\nsubject = AstrologicalSubjectFactory.from_iso_utc_time(\n    name=\"Johnny Depp\",\n    iso_utc_time=\"1963-06-09T05:00:00Z\",\n    city=\"Owensboro\",\n    nation=\"US\",\n    lng=-87.1112,\n    lat=37.7719,\n    tz_str=\"America/Chicago\",\n    online=False,\n)\n\nprint(subject.iso_formatted_local_datetime)\n```\n\nIf you prefer automatic geocoding, set `online=True` and provide your GeoNames credentials via `geonames_username`.\n\n**📖 All initialization options: [AstrologicalSubjectFactory Documentation](https://www.kerykeion.net/content/docs/astrological_subject_factory)**\n\n## Lunar Nodes (Rahu \u0026 Ketu)\n\nKerykeion supports both **True** and **Mean** Lunar Nodes:\n\n- **True North Lunar Node**: `\"True_North_Lunar_Node\"`\n- **True South Lunar Node**: `\"True_South_Lunar_Node\"`\n- **Mean North Lunar Node**: `\"Mean_North_Lunar_Node\"`\n- **Mean South Lunar Node**: `\"Mean_South_Lunar_Node\"`\n\nBy default, only the **True** nodes are active in charts and aspect calculations. To include the Mean nodes (or customize which nodes appear), pass the `active_points` parameter to the `ChartDataFactory` methods.\n\n**📖 ChartDataFactory documentation: [ChartDataFactory Guide](https://www.kerykeion.net/content/docs/chart_data_factory)**\n\nExample:\n\n```python\nfrom pathlib import Path\nfrom kerykeion import AstrologicalSubjectFactory\nfrom kerykeion.chart_data_factory import ChartDataFactory\nfrom kerykeion.charts.chart_drawer import ChartDrawer\n\n# Step 1: Create subject\nsubject = AstrologicalSubjectFactory.from_birth_data(\n    \"John Lennon\", 1940, 10, 9, 18, 30,\n    lng=-2.9833,\n    lat=53.4,\n    tz_str=\"Europe/London\",\n    online=False,\n)\n\n# Step 2: Pre-compute chart data with custom active points including true nodes\nchart_data = ChartDataFactory.create_natal_chart_data(\n    subject,\n    active_points=[\n        \"Sun\",\n        \"Moon\",\n        \"Mercury\",\n        \"Venus\",\n        \"Mars\",\n        \"Jupiter\",\n        \"Saturn\",\n        \"Uranus\",\n        \"Neptune\",\n        \"Pluto\",\n        \"Mean_North_Lunar_Node\",\n        \"Mean_South_Lunar_Node\",\n        \"True_North_Lunar_Node\",\n        \"True_South_Lunar_Node\",\n        \"Ascendant\",\n        \"Medium_Coeli\",\n        \"Descendant\",\n        \"Imum_Coeli\"\n    ]\n)\n\n# Step 3: Create visualization\nchart = ChartDrawer(chart_data=chart_data)\n\noutput_dir = Path(\"charts_output\")\noutput_dir.mkdir(exist_ok=True)\nchart.save_svg(output_path=output_dir, filename=\"johnny-depp-custom-points\")\n```\n\n## Fixed Stars\n\nKerykeion includes **23 fixed stars** — the 2 original stars (Regulus, Spica) plus 21 new stars added in v5.12, completing all 15 Behenian stars of the medieval/Hermetic tradition plus 8 additional bright stars. The set includes the 4 Royal Stars of Persian/Hellenistic astrology (Regulus, Aldebaran, Antares, Fomalhaut). Each star provides ecliptic longitude, daily motion (`speed`), equatorial `declination`, and apparent visual `magnitude`.\n\nFixed stars are computed for every subject but are **inactive by default** in charts and aspect calculations. To include them, pass their names in `active_points`:\n\n```python\nfrom kerykeion import AstrologicalSubjectFactory\nfrom kerykeion.chart_data_factory import ChartDataFactory\nfrom kerykeion.settings.config_constants import DEFAULT_ACTIVE_POINTS\n\nsubject = AstrologicalSubjectFactory.from_birth_data(\n    \"John Lennon\", 1940, 10, 9, 18, 30,\n    lng=-2.9833, lat=53.4, tz_str=\"Europe/London\", online=False,\n)\n\n# Access fixed star data directly\nprint(subject.sirius.abs_pos)        # Ecliptic longitude\nprint(subject.sirius.magnitude)      # -1.44\nprint(subject.sirius.declination)    # Equatorial declination\n\n# Include fixed stars in chart rendering\nchart_data = ChartDataFactory.create_natal_chart_data(\n    subject,\n    active_points=list(DEFAULT_ACTIVE_POINTS) + [\n        \"Sirius\", \"Regulus\", \"Aldebaran\", \"Antares\", \"Fomalhaut\",\n    ],\n)\n```\n\nAvailable fixed stars: Regulus, Spica, Aldebaran, Antares, Sirius, Fomalhaut, Algol, Betelgeuse, Canopus, Procyon, Arcturus, Pollux, Deneb, Altair, Rigel, Achernar, Capella, Vega, Alcyone, Alphecca, Algorab, Deneb_Algedi, Alkaid.\n\n**📖 Full active points list: [Active Points Documentation](https://www.kerykeion.net/content/docs/active_points)**\n\n## JSON Support\n\nYou can serialize the astrological subject (the base data used throughout the library) to JSON:\n\n```python\nfrom kerykeion import AstrologicalSubjectFactory\n\njohnny = AstrologicalSubjectFactory.from_birth_data(\n    \"Johnny Depp\", 1963, 6, 9, 0, 0,\n    lng=-87.1112,\n    lat=37.7719,\n    tz_str=\"America/Chicago\",\n    online=False,\n)\n\nprint(johnny.model_dump_json(indent=2))\n```\n\n**📖 Data models and schemas: [Schemas Documentation](https://www.kerykeion.net/content/docs/schemas)**\n\n## Moon Phase Details\n\nThe `MoonPhaseDetailsFactory` generates a rich lunar phase context from any astrological subject — including illumination, upcoming major phases, next eclipses (solar and lunar), sunrise/sunset, and apparent solar position. All timings use Swiss Ephemeris for ~1 second precision.\n\n```python\nfrom kerykeion import AstrologicalSubjectFactory, MoonPhaseDetailsFactory, ReportGenerator\n\nsubject = AstrologicalSubjectFactory.from_birth_data(\n    \"Example\", 2025, 4, 1, 7, 51,\n    lng=-0.1276, lat=51.5074, tz_str=\"Europe/London\",\n    online=False,\n)\n\noverview = MoonPhaseDetailsFactory.from_subject(subject)\n\nprint(f\"Phase: {overview.moon.phase_name} {overview.moon.emoji}\")\nprint(f\"Illumination: {overview.moon.illumination}\")\nprint(f\"Stage: {overview.moon.stage}\")\n\nif overview.moon.detailed and overview.moon.detailed.upcoming_phases:\n    fm = overview.moon.detailed.upcoming_phases.full_moon\n    if fm and fm.next:\n        print(f\"Next Full Moon: {fm.next.datestamp}\")\n\n# Generate a formatted ASCII report\nReportGenerator(overview).print_report()\n```\n\n**Report output (truncated):**\n\n```text\n=====================================================\nMoon Phase Overview — Tue, 01 Apr 2025 06:51:00 +0000\n=====================================================\n\n+Moon Summary--+--------------------+\n| Field        | Value              |\n+--------------+--------------------+\n| Phase Name   | Waxing Crescent 🌒 |\n| Major Phase  | New Moon           |\n| Stage        | Waxing             |\n| Illumination | 8%                 |\n| Age (days)   | 3                  |\n| Lunar Cycle  | 9.571%             |\n| Sun Sign     | Ari                |\n| Moon Sign    | Gem                |\n+--------------+--------------------+\n\n+Illumination Details-------+\n| Field            | Value  |\n+------------------+--------+\n| Percentage       | 8.0%   |\n| Visible Fraction | 0.0837 |\n| Phase Angle      | 34.46° |\n+------------------+--------+\n\n+Upcoming Phases+---------------------------------+---------------------------------+\n| Phase         | Last                            | Next                            |\n+---------------+---------------------------------+---------------------------------+\n| New Moon      | Sun, 29 Mar 2025 10:57:49 +0000 | Mon, 28 Apr 2025 00:31:07 +0000 |\n| First Quarter | ...                             | ...                             |\n| Full Moon     | ...                             | ...                             |\n| Last Quarter  | ...                             | ...                             |\n+---------------+---------------------------------+---------------------------------+\n\n...\n```\n\nYou can also get the full model as JSON:\n\n```python\nprint(overview.model_dump_json(exclude_none=True, indent=2))\n```\n\n**JSON output (truncated):**\n\n```json\n{\n  \"timestamp\": 1743490260,\n  \"datestamp\": \"Tue, 01 Apr 2025 06:51:00 +0000\",\n  \"sun\": {\n    \"sunrise_timestamp\": \"06:35\",\n    \"sunset_timestamp\": \"19:34\",\n    \"solar_noon\": \"13:04\",\n    \"day_length\": \"12:59\",\n    \"next_solar_eclipse\": { \"type\": \"Partial Solar Eclipse\", \"...\": \"...\" }\n  },\n  \"moon\": {\n    \"phase_name\": \"Waxing Crescent\",\n    \"major_phase\": \"New Moon\",\n    \"stage\": \"waxing\",\n    \"illumination\": \"12%\",\n    \"emoji\": \"🌒\",\n    \"next_lunar_eclipse\": { \"type\": \"Total Lunar Eclipse\", \"...\": \"...\" },\n    \"detailed\": { \"upcoming_phases\": { \"...\": \"...\" }, \"illumination_details\": { \"...\": \"...\" } }\n  },\n  \"location\": { \"latitude\": \"51.5074\", \"longitude\": \"-0.1276\" }\n}\n```\n\n**📖 Full documentation: [Moon Phase Details Factory](https://www.kerykeion.net/content/docs/moon_phase_details_factory)**\n\n**📖 Examples: [Moon Phase Details Examples](https://www.kerykeion.net/content/examples/moon-phase-details)**\n\n\n## Documentation\n\n- **Main Website**: [kerykeion.net](https://www.kerykeion.net)\n- **Getting Started**: [kerykeion.net/docs](https://www.kerykeion.net/content/docs/)\n- **Examples Gallery**: [kerykeion.net/examples](https://www.kerykeion.net/content/examples/)\n- **API Reference**: [kerykeion.net/pydocs](https://www.kerykeion.net/pydocs/)\n- **Astrologer API Docs**: [kerykeion.net/astrologer-api](https://www.kerykeion.net/content/astrologer-api/)\n- **Migration Guide (v4 → v5)**: [Migration Guide](https://www.kerykeion.net/content/docs/migration)\n\n## Projects built with Kerykeion\n\n**[AstrologerStudio](https://www.astrologerstudio.com/)** is a cloud-based astrology app built on top of Kerykeion.\n\n## Development\n\nClone the repository or download the ZIP via the GitHub interface.\n\n```bash\ngit clone https://github.com/g-battaglia/kerykeion.git\ncd kerykeion\npip install -e \".[dev]\"\n```\n\n## Integrating Kerykeion into Your Project\n\nIf you would like to incorporate Kerykeion's astrological features into your application, please reach out via [email](mailto:kerykeion.astrology@gmail.com?subject=Integration%20Request). Whether you need custom features, support, or specialized consulting, I am happy to discuss potential collaborations.\n\nFor commercial or closed-source applications, consider using the paid [Astrologer API (RapidAPI plans \u0026 pricing)](https://www.kerykeion.net/astrologer-api/subscribe) which provides REST endpoints for all Kerykeion functionality.\n\n## License\n\nThis project is covered under the AGPL-3.0 License. For detailed information, please see the [LICENSE](LICENSE) file. If you have questions, feel free to contact me at [kerykeion.astrology@gmail.com](mailto:kerykeion.astrology@gmail.com?subject=Kerykeion).\n\nAs a rule of thumb, if you use this library in a project, you should open-source that project under a compatible license. Alternatively, if you wish to keep your source closed, consider using the paid [Astrologer API](https://www.kerykeion.net/astrologer-api/subscribe), which is AGPL-3.0 compliant and also helps support the project.\n\nSince the Astrologer API is an external third-party service, using it does _not_ require your code to be open-source.\n\n## Contributing\n\nContributions are welcome! Feel free to submit pull requests or report issues.\n\nBy submitting a contribution, you agree to assign the copyright of that contribution to the maintainer. The project stays openly available under the AGPL for everyone, while the re-licensing option helps sustain future development. Your authorship remains acknowledged in the commit history and release notes.\n\n## Citations\n\nIf using Kerykeion in published or academic work, please cite as follows:\n\n```\nBattaglia, G. (2025). Kerykeion: A Python Library for Astrological Calculations and Chart Generation.\nhttps://github.com/g-battaglia/kerykeion\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fg-battaglia%2Fkerykeion","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fg-battaglia%2Fkerykeion","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fg-battaglia%2Fkerykeion/lists"}