{
  "version": 3,
  "sources": ["../src/index.ts", "../src/components/angel-number-card.ts", "../src/utils/angel-sections.ts", "../src/utils/base-element.ts", "../src/i18n/localized-element.ts", "../src/generated/api-languages.ts", "../src/i18n/lang.ts", "../src/utils/string.ts", "../src/i18n/registry.ts", "../src/i18n/locale-controller.ts", "../src/utils/base-styles.ts", "../src/utils/key-guard.ts", "../src/utils/fetch-controller.ts", "../src/utils/interp-accordion.ts", "../src/utils/disclosure.ts", "../src/utils/markup-data.ts", "../src/components/angel-number-lookup.ts", "../src/components/arudha-padas.ts", "../src/tokens/index.ts", "../src/utils/frame.ts", "../src/utils/format.ts", "../src/components/ashtakavarga-grid.ts", "../src/utils/tablist.ts", "../src/components/aspects-table.ts", "../src/components/astrocartography-map.ts", "../src/utils/planet-color.ts", "../src/utils/world-map.ts", "../src/components/bhav-chalit-table.ts", "../src/utils/house-themes.ts", "../src/components/bhava-bala-table.ts", "../src/components/biorhythm-chart.ts", "../src/components/bodygraph.ts", "../src/utils/bodygraph-render.ts", "../src/utils/bodygraph-styles.ts", "../src/utils/hd-reading.ts", "../src/utils/localized.ts", "../src/components/chara-karakas.ts", "../src/components/choghadiya-grid.ts", "../src/components/compatibility-card.ts", "../src/components/crystal-card.ts", "../src/utils/css-color.ts", "../src/components/crystal-grid.ts", "../src/components/dasha-timeline.ts", "../src/components/data.ts", "../src/components/divisional-chart.ts", "../src/utils/kundli-render.ts", "../src/utils/degree.ts", "../src/utils/kundli-styles.ts", "../src/components/dosha-card.ts", "../src/components/dream-card.ts", "../src/components/dream-search.ts", "../src/components/endpoint-form.ts", "../src/utils/field-schema.ts", "../src/version.ts", "../src/components/ephemeris-table.ts", "../src/components/fixed-stars.ts", "../src/components/forecast-digest.ts", "../src/components/forecast-timeline.ts", "../src/components/gochara-table.ts", "../src/components/guna-milan.ts", "../src/components/hd-connection.ts", "../src/components/hd-penta.ts", "../src/utils/penta-render.ts", "../src/components/hd-type-card.ts", "../src/components/hd-variables.ts", "../src/components/heliacal-table.ts", "../src/components/hexagram.ts", "../src/components/hora-table.ts", "../src/components/horoscope-card.ts", "../src/components/kp-chart.ts", "../src/components/kp-planets-table.ts", "../src/components/kp-ruling-planets.ts", "../src/components/local-space-compass.ts", "../src/components/location-search.ts", "../src/utils/debounce.ts", "../src/components/moon-phase.ts", "../src/components/nakshatra-card.ts", "../src/components/natal-chart.ts", "../src/utils/aspect-line.ts", "../src/components/numerology-card.ts", "../src/components/panchang-table.ts", "../src/components/positions-table.ts", "../src/components/profection-card.ts", "../src/components/reference-card.ts", "../src/components/relocation-wheel.ts", "../src/components/shadbala-table.ts", "../src/components/synastry-chart.ts", "../src/components/tarot-card.ts", "../src/components/tarot-catalog.ts", "../src/components/tarot-spread.ts", "../src/components/transit-wheel.ts", "../src/components/transits-table.ts", "../src/components/upagraha-table.ts", "../src/components/vedic-aspects.ts", "../src/components/vedic-daily.ts", "../src/components/vedic-kundli.ts", "../src/components/vedic-planets-table.ts", "../src/components/western-planets-table.ts", "../src/components/yoga-list.ts", "../src/manifest.ts"],
  "sourcesContent": ["/**\n * @roxyapi/ui main entry. Importing this file registers every custom element.\n *\n * For tree-shake friendly usage, import per-component:\n *   import '@roxyapi/ui/components/natal-chart';\n */\n\n// Angel numbers\nexport { RoxyAngelNumberCard } from './components/angel-number-card.js';\nexport { RoxyAngelNumberLookup } from './components/angel-number-lookup.js';\nexport { RoxyArudhaPadas } from './components/arudha-padas.js';\nexport { RoxyAshtakavargaGrid } from './components/ashtakavarga-grid.js';\n// Western astrology\nexport { RoxyAspectsTable } from './components/aspects-table.js';\nexport { RoxyAstrocartographyMap } from './components/astrocartography-map.js';\nexport { RoxyBhavChalitTable } from './components/bhav-chalit-table.js';\nexport { RoxyBhavaBalaTable } from './components/bhava-bala-table.js';\n// Biorhythm\nexport { RoxyBiorhythmChart } from './components/biorhythm-chart.js';\n// Human Design\nexport { RoxyBodygraph } from './components/bodygraph.js';\nexport { RoxyCharaKarakas } from './components/chara-karakas.js';\nexport { RoxyChoghadiyaGrid } from './components/choghadiya-grid.js';\nexport { RoxyCompatibilityCard } from './components/compatibility-card.js';\n// Crystals\nexport { RoxyCrystalCard } from './components/crystal-card.js';\nexport { RoxyCrystalGrid } from './components/crystal-grid.js';\nexport { RoxyDashaTimeline } from './components/dasha-timeline.js';\n// Generic fallback first so it is always available for nested rendering\nexport { RoxyData } from './components/data.js';\nexport { RoxyDivisionalChart } from './components/divisional-chart.js';\nexport { RoxyDoshaCard } from './components/dosha-card.js';\n// Dreams\nexport { RoxyDreamCard } from './components/dream-card.js';\nexport { RoxyDreamSearch } from './components/dream-search.js';\n// Helpers\nexport { RoxyEndpointForm } from './components/endpoint-form.js';\nexport { RoxyEphemerisTable } from './components/ephemeris-table.js';\nexport { RoxyFixedStars } from './components/fixed-stars.js';\n// Forecast\nexport { RoxyForecastDigest } from './components/forecast-digest.js';\nexport { RoxyForecastTimeline } from './components/forecast-timeline.js';\nexport { RoxyGocharaTable } from './components/gochara-table.js';\nexport { RoxyGunaMilan } from './components/guna-milan.js';\n// Human Design\nexport { RoxyHdConnection } from './components/hd-connection.js';\nexport { RoxyHdPenta } from './components/hd-penta.js';\nexport { RoxyHdTypeCard } from './components/hd-type-card.js';\nexport { RoxyHdVariables } from './components/hd-variables.js';\nexport { RoxyHeliacalTable } from './components/heliacal-table.js';\n// I Ching\nexport { RoxyHexagram } from './components/hexagram.js';\nexport { RoxyHoraTable } from './components/hora-table.js';\nexport { RoxyHoroscopeCard } from './components/horoscope-card.js';\nexport { RoxyKpChart } from './components/kp-chart.js';\nexport { RoxyKpPlanetsTable } from './components/kp-planets-table.js';\nexport { RoxyKpRulingPlanets } from './components/kp-ruling-planets.js';\nexport { RoxyLocalSpaceCompass } from './components/local-space-compass.js';\nexport { RoxyLocationSearch } from './components/location-search.js';\nexport { RoxyMoonPhase } from './components/moon-phase.js';\nexport { RoxyNakshatraCard } from './components/nakshatra-card.js';\n// Western astrology\nexport { RoxyNatalChart } from './components/natal-chart.js';\n// Numerology\nexport { RoxyNumerologyCard } from './components/numerology-card.js';\nexport { RoxyPanchangTable } from './components/panchang-table.js';\nexport { RoxyPositionsTable } from './components/positions-table.js';\nexport { RoxyProfectionCard } from './components/profection-card.js';\nexport { RoxyReferenceCard } from './components/reference-card.js';\nexport { RoxyRelocationWheel } from './components/relocation-wheel.js';\nexport { RoxyShadbalaTable } from './components/shadbala-table.js';\nexport { RoxySynastryChart } from './components/synastry-chart.js';\n// Tarot\nexport { RoxyTarotCard } from './components/tarot-card.js';\nexport { RoxyTarotCatalog } from './components/tarot-catalog.js';\nexport { RoxyTarotSpread } from './components/tarot-spread.js';\nexport { RoxyTransitWheel } from './components/transit-wheel.js';\nexport { RoxyTransitsTable } from './components/transits-table.js';\nexport { RoxyUpagrahaTable } from './components/upagraha-table.js';\n// Vedic astrology\nexport { RoxyVedicAspects } from './components/vedic-aspects.js';\nexport { RoxyVedicDaily } from './components/vedic-daily.js';\nexport { RoxyVedicKundli } from './components/vedic-kundli.js';\nexport { RoxyVedicPlanetsTable } from './components/vedic-planets-table.js';\nexport { RoxyWesternPlanetsTable } from './components/western-planets-table.js';\nexport { RoxyYogaList } from './components/yoga-list.js';\n\n// SSR helpers for the server-rendered hydration path (Pattern 7). Safe writers\n// for the inline <script class=\"roxy-data\"> the MarkupDataController reads.\nexport {\n\troxyDataScript,\n\tserializeRoxyData,\n} from './utils/markup-data.js';\n\nimport { ROXY_COMPONENTS, type RoxyComponentSlug } from './manifest.js';\n\nexport {\n\tROXY_COMPONENTS,\n\ttype RoxyComponent,\n\ttype RoxyComponentSlug,\n} from './manifest.js';\nexport { ROXY_UI_VERSION } from './version.js';\n\n/** Slugs in declaration order. Kept for the auto-mount widgets script and downstream codegen. */\nexport const ROXY_UI_COMPONENTS: readonly RoxyComponentSlug[] =\n\tROXY_COMPONENTS.map((c) => c.slug) as RoxyComponentSlug[];\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport type { GetAngelNumberResponse } from '../types/index.js';\nimport { buildMeaningSections } from '../utils/angel-sections.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { interpAccordionStyles } from '../utils/interp-accordion.js';\n\n/**\n * Angel number card. Renders /angel-numbers/numbers/{number}: the number as a hero numeral, its title and core message, the pattern type / digit root / energy badges, keyword chips, the life-area interpretations (spiritual, love, career, money, twin flame) plus the biblical and shadow readings as an exclusive accordion, the affirmation, and the action steps.\n *\n * @remarks\n * The numeral, the pattern type, the digit root and the energy badge are computed from the sequence, so they are the card's data spine and `hide-readings` keeps every one of them. The core message, the keyword chips, the life-area accordion, the affirmation and the action steps are the reading and all go, which is the same line `roxy-angel-number-lookup` already draws on the same domain.\n *\n * The accordion draws the shared `interp-accordion` through `RoxyDataElement.renderInterpretation` rather than the bare `<details>` it once hand-rolled. That local copy predated the helper, so it neither honoured `hide-readings` nor answered to `::part(reading)`, and being plain `<details>` rather than `.interp-card` markup it was invisible to the e2e guard that caught the other five.\n */\n@customElement('roxy-angel-number-card')\nexport class RoxyAngelNumberCard extends RoxyDataElement<GetAngelNumberResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tinterpAccordionStyles,\n\t\tcss`.card{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.hero{align-items:center;gap:var(--roxy-space-md,1rem);display:flex}.numeral{flex-shrink:0}.hero>div:not(.numeral){min-width:0}@container (width<=24rem){.hero{justify-items:start;gap:var(--roxy-space-sm,.5rem);grid-template-columns:minmax(0,1fr);display:grid}}.numeral{font-size:3rem;line-height:1;font-weight:var(--roxy-weight-bold,600);color:var(--roxy-accent-ink,#b45309);font-variant-numeric:tabular-nums}.label{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.06em;margin:0}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.core{color:var(--roxy-fg,#0a0a0a);margin:0;line-height:1.6}.badges{gap:var(--roxy-space-xs,.25rem);flex-wrap:wrap;display:flex}.badge{align-items:center;gap:var(--roxy-space-xs,.25rem);border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 35%, transparent);color:var(--roxy-fg,#0a0a0a);text-transform:capitalize;padding:3px 10px;display:inline-flex}.badge.positive{background:color-mix(in srgb, var(--roxy-success,#16a34a) 16%, transparent);color:var(--roxy-success-fg,#166534)}.badge.cautionary{background:color-mix(in srgb, var(--roxy-danger,#dc2626) 16%, transparent);color:var(--roxy-danger-fg,#991b1b)}.badge.neutral{background:color-mix(in srgb, var(--roxy-info,#0284c7) 16%, transparent);color:var(--roxy-info-fg,#075985)}.chips{gap:var(--roxy-space-xs,.25rem);flex-wrap:wrap;display:flex}.chips span{background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 14%, transparent);border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);padding:2px 8px}.affirmation{background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 12%, transparent);border-left:3px solid var(--roxy-accent,#f59e0b);padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);border-radius:var(--roxy-radius-sm,4px);color:var(--roxy-fg,#0a0a0a);margin:0;font-style:italic}.steps h3{margin:0 0 var(--roxy-space-xs,.25rem);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.06em}.steps ul{padding-left:var(--roxy-space-md,1rem);font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a);gap:2px;margin:0;display:grid}`,\n\t];\n\n\tprotected renderData(d: GetAngelNumberResponse) {\n\t\tconst energy = (d.energy ?? '').toLowerCase();\n\t\tconst energyClass =\n\t\t\tenergy === 'positive' || energy === 'cautionary' || energy === 'neutral'\n\t\t\t\t? energy\n\t\t\t\t: '';\n\t\tconst keywords = d.keywords ?? [];\n\t\tconst steps = d.actionSteps ?? [];\n\n\t\treturn html`<article class=\"card\" part=\"card\" aria-label=\"${`${this.t('Angel number')} ${d.number ?? ''}`}\"><div class=\"hero\" part=\"header\">${d.number ? html`<div class=\"numeral\">${d.number}</div>` : nothing}<div><p class=\"label\">${this.t('Angel number')}</p>${d.title ? html`<h2 class=\"title\">${d.title}</h2>` : nothing}</div></div>${\n\t\t\t\t// The badges below classify the sequence; this is what it is said to say.\n\t\t\t\td.coreMessage && !this.hideReadings\n\t\t\t\t\t? html`<p class=\"core\">${d.coreMessage}</p>`\n\t\t\t\t\t: nothing\n\t\t\t}<div class=\"badges\" part=\"details\">${d.type ? html`<span class=\"badge\">${d.type}</span>` : nothing} ${typeof d.digitRoot === 'number' ? html`<span class=\"badge\">${this.t('Digit root')} ${d.digitRoot}</span>` : nothing} ${d.energy ? html`<span class=\"${`badge ${energyClass}`}\">${d.energy}</span>` : nothing}</div>${\n\t\t\t\tkeywords.length > 0 && !this.hideReadings\n\t\t\t\t\t? html`<div class=\"chips\">${keywords.map((k) => html`<span>${k}</span>`)}</div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${this.renderSections(d)} ${d.affirmation && !this.hideReadings ? html`<p class=\"affirmation\">${d.affirmation}</p>` : nothing} ${\n\t\t\t\t// Each step is a sentence of advice, so the list is prose under its own\n\t\t\t\t// heading and goes whole.\n\t\t\t\tsteps.length > 0 && !this.hideReadings\n\t\t\t\t\t? html`<div class=\"steps\" part=\"section action-steps\"><h3>${this.t('Action steps')}</h3><ul>${steps.map((s) => html`<li>${s}</li>`)}</ul></div>`\n\t\t\t\t\t: nothing\n\t\t\t}</article>`;\n\t}\n\n\tprivate renderSections(d: NonNullable<GetAngelNumberResponse>) {\n\t\treturn this.renderInterpretation(\n\t\t\tbuildMeaningSections(this.translator, d.meaning, d.biblical, d.shadow),\n\t\t\t'angel-meaning',\n\t\t\t'Reading',\n\t\t);\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-angel-number-card': RoxyAngelNumberCard;\n\t}\n}\n", "/**\n * Ordered angel-number interpretation sections, shared by the angel-number card and lookup components so the field list, labels, and display order live in one place instead of being duplicated per component.\n *\n * @remarks The life-area keys (`spiritual`, `love`, `career`, `money`, `twinFlame`) mirror the `meaning` object in the angel-numbers OpenAPI response; `biblical` and `shadow` are top-level siblings present only on a known meaning, not on the digit-root fallback. Any absent field is skipped, so the one builder serves a full known meaning and the leaner digit-root reading alike.\n */\n\nimport type { ChromeString } from '../i18n/chrome-strings.js';\nimport type { Translate } from './hd-reading.js';\n\nexport interface MeaningSection {\n\tlabel: string;\n\tbody: string;\n}\n\n/** Response field to the English SOURCE its heading is looked up by. `source`, not `label`, because nothing renders it directly: it is typed {@link ChromeString} so a heading no catalogue carries is a compile error rather than one English word inside translated chrome. */\nconst LIFE_AREA_SECTIONS: ReadonlyArray<{ key: string; source: ChromeString }> =\n\t[\n\t\t{ key: 'spiritual', source: 'Spiritual' },\n\t\t{ key: 'love', source: 'Love' },\n\t\t{ key: 'career', source: 'Career' },\n\t\t{ key: 'money', source: 'Money' },\n\t\t{ key: 'twinFlame', source: 'Twin flame' },\n\t];\n\n/**\n * Build the present-only interpretation sections in canonical order: the life areas first, then the optional `biblical` and `shadow` readings. An empty or missing field is omitted so the caller can render whatever the response actually carries.\n */\nexport function buildMeaningSections(\n\tt: Translate,\n\tmeaning: Record<string, string> | undefined,\n\tbiblical?: string,\n\tshadow?: string,\n): MeaningSection[] {\n\tconst sections: MeaningSection[] = [];\n\tif (meaning) {\n\t\tfor (const s of LIFE_AREA_SECTIONS) {\n\t\t\tconst body = meaning[s.key];\n\t\t\tif (body) sections.push({ label: t(s.source), body });\n\t\t}\n\t}\n\tif (biblical) sections.push({ label: t('Biblical'), body: biblical });\n\tif (shadow) sections.push({ label: t('Shadow'), body: shadow });\n\treturn sections;\n}\n", "import {\n\ttype ComplexAttributeConverter,\n\ttype CSSResultGroup,\n\thtml,\n\tnothing,\n\ttype PropertyValues,\n} from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport { ifDefined } from 'lit/directives/if-defined.js';\nimport type { ChromeString } from '../i18n/chrome-strings.js';\nimport { RoxyLocalizedElement } from '../i18n/localized-element.js';\nimport { baseStyles } from './base-styles.js';\nimport { buildRequest, FetchController } from './fetch-controller.js';\nimport {\n\ttype InterpSection,\n\trenderInterpAccordion,\n} from './interp-accordion.js';\nimport { MarkupDataController } from './markup-data.js';\n\n/**\n * Read the `submit-context` attribute into the object a proxied submit carries.\n *\n * @remarks\n * Hand-written rather than `type: Object`, which resolves malformed JSON to `null` in silence: a page whose context never left has nothing to look at, and the route it was meant for reports only that the value it expected is not there. One warning covers both refusals (unparseable, or parsed to something that is not an object) because both leave the request without a context, which is the fact worth reading.\n */\nconst submitContextConverter: ComplexAttributeConverter<\n\tRecord<string, unknown> | undefined\n> = {\n\tfromAttribute: (value) => {\n\t\tif (value == null) return undefined;\n\t\ttry {\n\t\t\tconst parsed: unknown = JSON.parse(value);\n\t\t\tif (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n\t\t\t\treturn parsed as Record<string, unknown>;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Both refusals share the warning below.\n\t\t}\n\t\tconsole.warn(\n\t\t\t'[roxy-ui] submit-context must be a JSON object; sending the request without it.',\n\t\t);\n\t\treturn undefined;\n\t},\n};\n\n/**\n * Shared base for every data-driven Roxy component. Consolidates the things every component used to repeat by hand and adds the self-contained, drop-in behavior: controlled-mode hydration, an opt-in self-fetch (form, request, loading, error, empty), the typed `data` slot, and the render switch. A subclass implements one method, {@link RoxyDataElement.renderData}.\n *\n * @remarks\n * Two render modes, one component, which is the whole drop-in contract:\n *\n * - Controlled: a server (WordPress, JSX SSR, static HTML) fetches the response and injects it as a direct-child `<script type=\"application/json\" class=\"roxy-data\">`. {@link MarkupDataController} hydrates `data` from it on connect. No key reaches the browser. This is the path the WordPress plugin drives, and it is unchanged by the base class.\n * - Uncontrolled: no data is injected, but a `data-endpoint` and a `pk_` `publishable-key` are set. The component renders its own `<roxy-endpoint-form>` (an internal detail, never placed by the consumer), and on submit fetches through {@link FetchController}, which refuses any non-publishable key. The result populates `data` and the component renders it.\n *\n * The JavaScript `data` property always wins over markup, so a host that assigns `el.data = …` after upgrade is authoritative.\n *\n * Self-fetch needs `<roxy-endpoint-form>` (and `<roxy-location-search>`) registered. The CDN bundle and the full `@roxyapi/ui` entry register everything; per-component ESM consumers that want self-fetch import the form component too.\n *\n * Two things every subclass inherits for the host page to shape the render from outside the shadow root, with no JavaScript: the `hide-readings` attribute ({@link RoxyDataElement.hideReadings}), and the `part` names on the base's own branches (`form`, `loading`, `error`, `empty`, `edit-bar`, `attribution`). A component adds `part` to its own structural blocks, drawn from the published part vocabulary.\n *\n * Translation is inherited rather than defined here: `t()`, `translator` and `effectiveLang()` live on {@link RoxyLocalizedElement}, which the two declared widgets extend as well, so the form a visitor fills and the card it produces read the same catalogue.\n *\n * @example\n * ```ts\n * @customElement('roxy-dream-card')\n * export class RoxyDreamCard extends RoxyDataElement<GetDreamSymbolResponse> {\n *   static styles = [baseStyles, css`.card { … }`];\n *   protected renderData(d: GetDreamSymbolResponse) {\n *     return html`<article class=\"card\">…</article>`;\n *   }\n * }\n * ```\n */\nexport abstract class RoxyDataElement<\n\tT = unknown,\n> extends RoxyLocalizedElement {\n\tstatic styles: CSSResultGroup = [baseStyles];\n\n\t/** Typed RoxyAPI response. Assigned in JavaScript, hydrated from the roxy-data island, or set by a self-fetch. */\n\t@property({ attribute: false })\n\tdata: T | null = null;\n\n\t/** Endpoint path for self-fetch, e.g. \"astrology/natal-chart\" or \"dreams/symbols/{id}\". Empty disables self-fetch (controlled mode only). */\n\t@property({ type: String, attribute: 'data-endpoint' })\n\tendpoint = '';\n\n\t/** HTTP method for self-fetch. */\n\t@property({ type: String })\n\tmethod: 'GET' | 'POST' = 'POST';\n\n\t/** Browser-safe publishable key (pk_) for self-fetch. A secret key is refused client-side. */\n\t@property({ type: String, attribute: 'publishable-key' })\n\tpublishableKey?: string;\n\n\t/** Override the API origin for self-hosted or proxied deployments. Absolute, or relative to the page for a same-origin route. */\n\t@property({ type: String, attribute: 'base-url' })\n\tbaseUrl?: string;\n\n\t/** Override the OpenAPI spec URL the self-fetch form introspects. */\n\t@property({ type: String, attribute: 'spec-url' })\n\tspecUrl?: string;\n\n\t/** Consumer backend route that proxies the request (holds the secret key). When set, self-fetch POSTs the request here instead of calling RoxyAPI directly, so no key reaches the browser. The server-rendered (WordPress) path. */\n\t@property({ type: String, attribute: 'submit-url' })\n\tsubmitUrl?: string;\n\n\t/**\n\t * Data the host page chooses, as a JSON object, sent to the {@link RoxyDataElement.submitUrl} route as `context` beside the request. The place a page attaches its own verification data to a proxied submission.\n\t *\n\t * @remarks\n\t * Shapeless by design and never read here: the component parses the attribute, hands the object to the request, and leaves every key of it to the page and the route that answers it. Nothing is sent when it is unset, when the JSON does not parse, and when it parses to anything but an object, so a route written before this existed goes on receiving exactly the request it always did.\n\t *\n\t * It rides the proxy path only. A direct call with a `publishable-key` sends what the endpoint declares, so setting this without a `submit-url` does nothing.\n\t *\n\t * @example\n\t * ```html\n\t * <roxy-natal-chart\n\t *   data-endpoint=\"astrology/natal-chart\"\n\t *   submit-url=\"/api/roxy/proxy\"\n\t *   submit-context='{\"token\":\"...\"}'\n\t * ></roxy-natal-chart>\n\t * ```\n\t */\n\t@property({ attribute: 'submit-context', converter: submitContextConverter })\n\tsubmitContext?: Record<string, unknown>;\n\n\t/**\n\t * Where the self-fetch form's city search sends its request, absolute or relative to the page.\n\t *\n\t * @remarks\n\t * The companion of {@link RoxyDataElement.submitUrl}, and it is separate because the two requests have different shapes: the proxy route takes one POSTed `{ path, method, body, query }`, while the city search is a GET the search issues on its own while a visitor types. A birth-data form makes both, so a page that routes its API traffic through its own server names that route here as well. It is forwarded to the internal form, which forwards it to the search; unset, the search keeps its own default.\n\t */\n\t@property({ type: String, attribute: 'location-url' })\n\tlocationUrl?: string;\n\n\t/** Persist the last self-fetch form values in sessionStorage (keyed by endpoint) and prefill the form on reload. */\n\t@property({ type: Boolean })\n\tremember = false;\n\n\t/**\n\t * Render the chart and the data, and omit the written interpretation. Off by default, so a component that does not set it is byte-identical to before.\n\t *\n\t * @remarks\n\t * The suppressed content is the prose an endpoint returns ABOUT the result: the interpretation accordion, the interpretive paragraphs, and the keyword chips that belong to them. Everything a practitioner reads the numbers off stays: wheels, tables, grids, legends, pills, counts and dates. The reading is dropped from the DOM rather than hidden with CSS, so a page that publishes the chart alone never ships the prose it is not showing.\n\t *\n\t * Set it when the page supplies its own words, which is the common case for a practitioner site that wants the graphic under its own writing.\n\t */\n\t@property({ type: Boolean, attribute: 'hide-readings', reflect: true })\n\thideReadings = false;\n\n\t/**\n\t * Remove named structural blocks from this component, as a comma-separated list of `part` names.\n\t *\n\t * @remarks\n\t * The sibling of {@link RoxyDataElement.hideReadings} and deliberately a different tool. `hide-readings` drops the PROSE an endpoint returns about a result; this drops a whole block whatever it contains, which is what a page wants when the block is measurement rather than writing. Hiding the chart-pattern list on a teaching page is the case it exists for: a T-square is data, so `hide-readings` leaves it standing on purpose.\n\t *\n\t * Any name in `components-catalog.json` works, on any component that publishes it, because the rule is generated from the name rather than from a list of components that opted in. A name no block carries costs nothing and hides nothing.\n\t *\n\t * **This hides with CSS, it does not remove from the DOM, and the difference is the reason both exist.** A part name is a rendering concern the base can act on generically; whether a block is prose is a question only the component can answer. So a page that must not SHIP the words uses `hide-readings`, and a page that just should not SHOW a block uses this.\n\t *\n\t * @example\n\t * ```html\n\t * <roxy-natal-chart hide-sections=\"patterns\"></roxy-natal-chart>\n\t * <roxy-natal-chart hide-sections=\"patterns, aspects\"></roxy-natal-chart>\n\t * ```\n\t */\n\t@property({ type: String, attribute: 'hide-sections', reflect: true })\n\thideSections = '';\n\n\t/** Override the self-fetch form's submit-button label. Empty derives one from the endpoint. */\n\t@property({ type: String, attribute: 'submit-label' })\n\tsubmitLabel?: string;\n\n\t/** Render a small \"Spiritual data by RoxyAPI\" credit under a self-fetch or auto-mount result. Off by default; any value except \"off\"/\"false\" enables it. The one-tag widgets script turns it on unless data-attribution=\"off\". Never shown in controlled mode. */\n\t@property({ type: String })\n\tattribution?: string;\n\n\t/** True while an uncontrolled self-fetch is in flight. Public so {@link FetchController} can drive it. */\n\t@state()\n\tloading = false;\n\n\t/** Message from a failed self-fetch, or null. Public so {@link FetchController} can drive it. */\n\t@state()\n\terror: string | null = null;\n\n\t/**\n\t * True once a self-fetch (not a consumer-assigned or island-hydrated `data`) produced the current result. It gates the interactive result affordances, so controlled mode never shows an Edit control or a sticky picker.\n\t */\n\t@state()\n\tprivate selfFetched = false;\n\n\t/** True when the visitor asked to edit a self-fetch result, so the form is restored over the result. */\n\t@state()\n\tprivate editing = false;\n\n\t/** True when the last self-fetch form was a single-enum picker, so it stays visible above the result and a change refetches. */\n\t@state()\n\tprivate sticky = false;\n\n\t/** Last submitted form values (nested per group), kept in memory to restore the form on Edit and to prefill the sticky picker. */\n\tprivate lastValues?: Record<string, unknown>;\n\n\tprivate fetcher: FetchController<T>;\n\n\tconstructor() {\n\t\tsuper();\n\t\t// Controlled mode: hydrate `data` from a direct-child roxy-data island when\n\t\t// no JS property was assigned. Keyless; the server already fetched.\n\t\tnew MarkupDataController<T>(this);\n\t\tthis.fetcher = new FetchController<T>(this);\n\t}\n\n\tprotected willUpdate(changed: PropertyValues): void {\n\t\tif (changed.has('publishableKey')) {\n\t\t\tthis.fetcher.publishableKey = this.publishableKey;\n\t\t}\n\t\tif (changed.has('baseUrl') && this.baseUrl) {\n\t\t\tthis.fetcher.baseUrl = this.baseUrl;\n\t\t}\n\t\tif (changed.has('submitUrl')) {\n\t\t\tthis.fetcher.submitUrl = this.submitUrl;\n\t\t}\n\t\tif (changed.has('submitContext')) {\n\t\t\tthis.fetcher.submitContext = this.submitContext;\n\t\t}\n\t}\n\n\trender(): unknown {\n\t\treturn html`${this.hiddenSectionsStyle()}${this.renderState()}`;\n\t}\n\n\t/**\n\t * The one rule set that acts on {@link RoxyDataElement.hideSections}, generated from the names rather than from a list of components.\n\t *\n\t * @remarks\n\t * `[part~=\"name\"]` is the selector because `part` is a space-separated list: the natal chart's aspect grid carries `section aspects table aspect-grid`, so a `~=` match reaches it by any one of its names while `=` would reach it by none. It is emitted INSIDE the shadow root, which is the only place a rule can see a `part` attribute on the element that declares it.\n\t *\n\t * Names are filtered to the same `[a-z][a-z0-9-]*` shape the catalog and the WordPress setting both use, so nothing a page passes can close the rule or inject a second one. An unknown name compiles to a selector that matches nothing, which is why there is no validation error and no list of legal names to keep in sync.\n\t *\n\t * A `<style>` element in the template rather than a static stylesheet, because static styles are evaluated once and shared across every instance, so two charts on one page could not hide different blocks. Lit supports per-instance style elements; the documented cost is a re-parse when the expression changes, which is irrelevant here because the value is set once from a page attribute and effectively never changes after that.\n\t *\n\t * **`!important` is load-bearing and is not a shortcut around specificity.** Lit puts static `styles` in `adoptedStyleSheets`, and the scoping spec applies those AFTER any `<style>` element inside the same shadow root, so an in-tree rule loses to the component's own stylesheet at equal specificity no matter how it is written. Measured: `patterns` is a bare `section` with no declared display and hid correctly without it, while `legend` carries `display:flex` from the static sheet and stayed visible. The WordPress site-wide rule needs no `!important` for the opposite reason, that an outer-tree `::part()` rule already outranks anything inside.\n\t */\n\tprivate hiddenSectionsStyle(): unknown {\n\t\tconst names = this.hideSections\n\t\t\t.split(',')\n\t\t\t.map((n) => n.trim().toLowerCase())\n\t\t\t.filter((n) => /^[a-z][a-z0-9-]*$/.test(n));\n\t\tif (names.length === 0) return nothing;\n\t\tconst rules = names\n\t\t\t.map((n) => `[part~=\"${n}\"]{display:none!important}`)\n\t\t\t.join('');\n\t\treturn html`<style>${rules}</style>`;\n\t}\n\n\t/** The state machine every render goes through, split out so {@link render} can pair it with the per-instance rules above. */\n\tprivate renderState(): unknown {\n\t\tif (this.loading) return this.renderLoading();\n\t\tif (this.error != null) return this.renderError(this.error);\n\t\tif (this.data != null) {\n\t\t\t// Controlled mode (consumer-assigned or island-hydrated data) renders the\n\t\t\t// result alone, byte-identical to before: no picker, no Edit. The one\n\t\t\t// exception is the widgets auto-mount attrs-complete path, which assigns\n\t\t\t// data directly AND sets `attribution`; a real controlled consumer never\n\t\t\t// sets the attribute, so this stays byte-identical for them.\n\t\t\tif (!this.selfFetched) {\n\t\t\t\treturn this.showAttribution()\n\t\t\t\t\t? html`${this.renderData(this.data)}${this.renderAttribution()}`\n\t\t\t\t\t: this.renderData(this.data);\n\t\t\t}\n\t\t\tif (this.editing) return this.renderForm();\n\t\t\treturn this.renderResult(this.data);\n\t\t}\n\t\treturn this.renderNoData();\n\t}\n\n\t/** Render the populated response. The single method every component implements. */\n\tprotected abstract renderData(data: T): unknown;\n\n\t/**\n\t * The interpretation accordion, gated by {@link RoxyDataElement.hideReadings}. Every component that shows a written reading routes through here instead of calling {@link renderInterpAccordion} directly.\n\t *\n\t * @remarks\n\t * The gate lives on the base rather than in each component because the property does, and a plain render function cannot read component state. One call site per accordion, one place that decides whether prose renders, and the section it emits carries the same `readings` part in every component.\n\t *\n\t * Prose that is NOT in an accordion (a summary paragraph, a per-item interpretation) is still the component's own to gate, since only the component knows which of its blocks are readings.\n\t *\n\t * @param heading - English source copy, translated here. Typed as {@link ChromeString} so the compiler, not a reviewer, rejects a heading no catalogue carries.\n\t */\n\tprotected renderInterpretation(\n\t\tsections: InterpSection[],\n\t\tname: string,\n\t\theading?: ChromeString,\n\t): unknown {\n\t\tif (this.hideReadings) return nothing;\n\t\t// Localized HERE rather than in the accordion: that is a plain render\n\t\t// function with no host, so it cannot read a language, and this is the one\n\t\t// call site every component routes through.\n\t\treturn renderInterpAccordion(sections, name, this.t(heading ?? 'Reading'));\n\t}\n\n\t/**\n\t * A self-fetch result plus its re-query affordance. A single-enum form keeps its picker above the result so a new selection refetches (a sign switch is a new reading); any other form gets a compact Edit control that restores the form with the previous values.\n\t */\n\tprotected renderResult(data: T): unknown {\n\t\tconst body = this.sticky\n\t\t\t? html`${this.renderForm()}${this.renderData(data)}`\n\t\t\t: html`<div class=\"roxy-edit-bar\" part=\"edit-bar\"><button type=\"button\" class=\"roxy-edit\" @click=\"${this.onEdit}\">${this.t('Edit query')}</button></div>${this.renderData(data)}`;\n\t\treturn this.showAttribution()\n\t\t\t? html`${body}${this.renderAttribution()}`\n\t\t\t: body;\n\t}\n\n\tprivate onEdit = () => {\n\t\tthis.editing = true;\n\t};\n\n\t/** True when the attribution credit renders: the attribute is present and not explicitly disabled. */\n\tprivate showAttribution(): boolean {\n\t\treturn (\n\t\t\tthis.attribution != null &&\n\t\t\tthis.attribution !== 'off' &&\n\t\t\tthis.attribution !== 'false'\n\t\t);\n\t}\n\n\t/** Small muted credit under a self-fetch or auto-mount result. Reached only from the result path, so controlled mode never renders it. */\n\tprotected renderAttribution(): unknown {\n\t\treturn html`<div class=\"roxy-attribution\" part=\"attribution\"><a href=\"https://roxyapi.com/?utm_source=widget&utm_medium=embed\" target=\"_blank\" rel=\"noopener\">${this.t('Spiritual data by RoxyAPI')}</a></div>`;\n\t}\n\n\t/** The data-absent branch: the self-fetch form when an endpoint is set, otherwise the empty state. */\n\tprotected renderNoData(): unknown {\n\t\treturn this.endpoint ? this.renderForm() : this.renderEmpty();\n\t}\n\n\t/** Internal self-fetch form. Reuses the introspecting `<roxy-endpoint-form>`; the consumer never places it. */\n\tprotected renderForm(): unknown {\n\t\treturn html`<roxy-endpoint-form part=\"form\" data-endpoint=\"${this.endpoint}\" method=\"${this.method}\" spec-url=\"${ifDefined(this.specUrl)}\" publishable-key=\"${ifDefined(this.publishableKey)}\" location-url=\"${ifDefined(this.locationUrl)}\" submit-label=\"${ifDefined(this.submitLabel)}\" lang=\"${ifDefined(this.effectiveLang())}\" .initialValues=\"${this.formInitialValues()}\" @roxy-submit=\"${this.onFormSubmit}\"></roxy-endpoint-form>`;\n\t}\n\n\t/** Prefill source for the form: the last submission (Edit / sticky refetch) wins over remembered storage. */\n\tprivate formInitialValues(): Record<string, unknown> | undefined {\n\t\treturn (\n\t\t\tthis.lastValues ?? (this.remember ? this.readRemembered() : undefined)\n\t\t);\n\t}\n\n\tprivate onFormSubmit = (e: Event) => {\n\t\tconst detail = (e as CustomEvent).detail as {\n\t\t\tvalues: Record<string, unknown>;\n\t\t\tqueryKeys?: string[];\n\t\t\tsticky?: boolean;\n\t\t};\n\t\tthis.lastValues = detail.values;\n\t\tthis.sticky = !!detail.sticky;\n\t\tthis.selfFetched = true;\n\t\tthis.editing = false;\n\t\tif (this.remember) this.writeRemembered(detail.values);\n\t\tvoid this.fetcher.run(\n\t\t\tbuildRequest(\n\t\t\t\tthis.endpoint,\n\t\t\t\tthis.method,\n\t\t\t\tdetail.values,\n\t\t\t\tdetail.queryKeys ?? [],\n\t\t\t),\n\t\t);\n\t};\n\n\t/** sessionStorage key for this component's remembered form values, scoped by endpoint. */\n\tprivate rememberKey(): string {\n\t\treturn `roxy-ui:form:${this.endpoint}`;\n\t}\n\n\tprivate readRemembered(): Record<string, unknown> | undefined {\n\t\ttry {\n\t\t\tconst raw = sessionStorage.getItem(this.rememberKey());\n\t\t\treturn raw ? (JSON.parse(raw) as Record<string, unknown>) : undefined;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tprivate writeRemembered(values: Record<string, unknown>): void {\n\t\ttry {\n\t\t\tsessionStorage.setItem(this.rememberKey(), JSON.stringify(values));\n\t\t} catch {\n\t\t\t// Private mode / disabled storage: remembering is best-effort, never fatal.\n\t\t}\n\t}\n\n\t/** Terminal empty state: one translated message for every component, addressable as `::part(empty)`. Override only where an empty state must DO something a message cannot. */\n\tprotected renderEmpty(): unknown {\n\t\treturn html`<div class=\"roxy-empty\" role=\"status\" part=\"empty\">${this.t('No data')}</div>`;\n\t}\n\n\t/** Loading placeholder shown during a self-fetch. */\n\tprotected renderLoading(): unknown {\n\t\treturn html`<div class=\"roxy-skeleton\" style=\"height:8rem\" role=\"status\" aria-label=\"${this.t('Loading')}\" part=\"loading\"></div>`;\n\t}\n\n\t/**\n\t * Error state shown when a self-fetch fails. Keeps the form in view so the request can be retried.\n\t *\n\t * The message goes through `t()` because ONE of the messages that reaches here is ours: `KEY_REFUSED_MESSAGE` (`utils/key-guard.ts`), which {@link FetchController} assigns when a site owner pastes a secret key into a browser page, and which `<roxy-location-search>` renders in its own shadow root from the same constant. Translating it in one place and not the other would put two languages on one page. Everything else that lands here is a wire fact (an HTTP status, a browser network error), misses the catalogue and renders unchanged.\n\t */\n\tprotected renderError(message: string): unknown {\n\t\tconst banner = html`<div class=\"roxy-error\" role=\"alert\" part=\"error\">${this.t(message)}</div>`;\n\t\treturn this.endpoint ? html`${banner}${this.renderForm()}` : banner;\n\t}\n}\n", "import { LitElement } from 'lit';\nimport { resolveLang } from './lang.js';\nimport { LocaleController } from './locale-controller.js';\nimport { translate } from './registry.js';\n\n/**\n * The base every element that writes WORDS extends: it resolves the page language and translates its own chrome.\n *\n * @remarks\n * This is deliberately smaller than {@link RoxyDataElement}, and the split is what makes the widgets translatable at all. `<roxy-endpoint-form>` and `<roxy-location-search>` are the two declared widgets: they fetch, they hold state, and they are NOT data components, so they cannot inherit the data base without dragging its controlled-mode hydration, its self-fetch controller and its own form rendering (which renders the form, so the form inheriting it would compose itself). They extended `LitElement` directly instead, which meant they had no `t()` and no {@link LocaleController}, so a Spanish page rendered a Spanish result card over an English form and neither widget re-rendered when a deferred locale payload landed.\n *\n * So the mechanism lives here ONCE and all three inherit it. There is no second translator, no second registry and no second way to resolve a language: adding one is how a catalogue entry and a call site drift apart.\n *\n * Two costs are why this is its own module rather than a second export of `utils/base-element.ts`. Every `dist/cdn/components/*.js` is a self-contained IIFE, so importing the data base into the city search would compile `FetchController`, `MarkupDataController` and the interpretation accordion into a bundle that uses none of them. And `utils/base-element.ts` imports THIS file, so putting the class there would close a cycle the moment a widget imported it.\n */\nexport abstract class RoxyLocalizedElement extends LitElement {\n\tconstructor() {\n\t\tsuper();\n\t\t// Re-render if a chrome-string catalogue lands after this element upgraded.\n\t\t// A host page loads the payload as its own script tag, so it can arrive\n\t\t// after first paint and there is nothing the site owner could do about it.\n\t\tnew LocaleController(this);\n\t}\n\n\t/**\n\t * The chrome string for this element's language, keyed by its own English source text.\n\t *\n\t * @remarks\n\t * Pass the English copy, never a key. With no catalogue registered the source string is returned unchanged, so an English page pays nothing and a language we do not ship degrades to English rather than to a visible key.\n\t *\n\t * Chrome only. A value the API returned (a planet, a sign, an aspect, a house system) is NOT translated here: it is rendered as the response carries it, so the words on the chart and the words in the response can never disagree. The same holds for a label derived from a spec field NAME through `humanize()`: a catalogue keyed on English source text cannot reach a string computed per response. A derived name is catalogued only where the set it comes from is CLOSED and small enough to enumerate against the spec, which is true of the nine form group names and of nothing else so far.\n\t *\n\t * @example\n\t * ```ts\n\t * this.t('Aspect grid');                        // 'Cuadricula de aspectos' under lang=\"es\"\n\t * this.t('{{count}} planets', { count: 12 });   // '12 planetas'\n\t * ```\n\t */\n\tprotected t(source: string, vars?: Record<string, string | number>): string {\n\t\treturn translate(this.effectiveLang(), source, vars);\n\t}\n\n\t/** {@link RoxyLocalizedElement.t} as a value, bound once, for a shared render helper that has no host of its own and therefore cannot resolve a page language (`utils/hd-reading.ts`). Named `translator` and not `translate` because `HTMLElement.translate` is a real DOM property and shadowing it with a protected member makes every `@customElement` decorator in the library fail to typecheck. */\n\tprotected readonly translator = (\n\t\tsource: string,\n\t\tvars?: Record<string, string | number>,\n\t): string => this.t(source, vars);\n\n\t/**\n\t * Site-owner language for this element: the `lang` attribute, the nearest ancestor carrying one, or the document. Protected so a component that composes another can forward it (a child inside this shadow root cannot reach the host page on its own).\n\t *\n\t * Full tag, region included. The truncation to what `?lang=` accepts happens where the request is built, in `<roxy-endpoint-form>`.\n\t */\n\tprotected effectiveLang(): string | undefined {\n\t\treturn resolveLang(this);\n\t}\n}\n", "// Generated by scripts/sync-bindings.ts (the `lang` query enum in specs/openapi.json). Do not edit.\n\n/**\n * Every language tag the API accepts on `?lang=`.\n *\n * @remarks\n * A self-fetch resolves the site language from the DOM, which means ANY value a\n * host page happens to carry can reach the query string. The API rejects an\n * unlisted tag with a 400, so a page in a language we do not translate would\n * stop working the moment the language chain was completed. Membership is\n * checked against this list before `lang` is sent, and a miss simply omits the\n * parameter, leaving the endpoint on its English default.\n */\nexport const API_LANGUAGES: readonly string[] = [\"de\",\"en\",\"es\",\"fr\",\"hi\",\"pt\",\"ru\",\"tr\"];\n", "/**\n * Where a component's language comes from, and what is safe to send to the API.\n *\n * @remarks\n * Two different answers, and conflating them is the whole reason this file is separate from the catalogue:\n *\n * - {@link resolveLang} answers \"what language is this PAGE in\", and keeps the full tag, because `es-AR` is a legitimate thing for a catalogue to want to differ on.\n * - {@link apiLang} answers \"what may go on the query string\", which is a narrower question with a hard edge: `?lang=es-AR` is a 400, `?lang=es` is a 200.\n *\n * The full tag is not a nicety the catalogue happens to tolerate: `utils/format.ts` hands it to `Intl`, which reads the region and writes `15 de ene de 1990` for `es-AR` where it writes `15 ene 1990` for `es`. Passing the API answer to a formatter demotes every regional visitor to the base language and nothing fails, so the two values are never interchangeable.\n */\n\nimport { API_LANGUAGES } from '../generated/api-languages.js';\n\n/**\n * The site-owner language for an element, resolved through the four-link chain the revamp design specifies.\n *\n * @remarks\n * 1. the element's own `lang` attribute (the explicit per-embed override),\n * 2. the nearest ancestor carrying `lang`, so one wrapper localizes a whole section,\n * 3. `document.documentElement.lang`, which is what makes this work on a real CMS with no markup change at all: WordPress emits `<html lang=\"es-AR\">` on every page of a Spanish site,\n * 4. nothing, which the callers read as English.\n *\n * All four links matter: with only the first, a component on a fully Spanish page renders English chrome and the only remedy is hand-editing every embed.\n *\n * `navigator.language` is deliberately NOT in the chain. It is the VISITOR's preference, not the site's: it fragments the API cache across every locale a visitor might carry and silently serves two people different content from the same URL.\n *\n * Link 2 uses `closest`, which stops at a shadow boundary. A component composed INSIDE another component's shadow root therefore cannot see the host page, which is why every composing component forwards `lang` to its children explicitly.\n */\nexport function resolveLang(el: HTMLElement): string | undefined {\n\t// Native accessor, never a Lit @property: declaring one would shadow\n\t// HTMLElement.lang and break the platform attribute for every consumer.\n\tif (el.lang) return el.lang;\n\tif (typeof document === 'undefined') return undefined;\n\tconst inherited = el.closest?.('[lang]')?.getAttribute('lang');\n\tif (inherited) return inherited;\n\treturn document.documentElement.lang || undefined;\n}\n\n/**\n * The value safe to put on `?lang=`, or `undefined` to leave the request on the API default.\n *\n * @remarks\n * Two narrowings, both of which are load-bearing the moment {@link resolveLang} starts reading `<html lang>`:\n *\n * - **The region is dropped.** A regional tag is a 400, so a Spanish WordPress site would go from English-but-working to broken the day the language chain was completed. `es-AR`, `es-419` and `ES` all send `es`.\n * - **An unsupported language is omitted, not sent.** The chain now surfaces whatever the host page carries, including languages the API does not translate, and an unlisted tag is the same 400. Omitting it degrades to English, which is the behaviour those pages already had.\n */\nexport function apiLang(el: HTMLElement): string | undefined {\n\tconst tag = resolveLang(el);\n\tif (!tag) return undefined;\n\tconst base = tag.toLowerCase().split('-')[0] as string;\n\treturn API_LANGUAGES.includes(base) ? base : undefined;\n}\n", "/**\n * Shared string helpers used across components. Single source of truth so the\n * same formatting rules apply to every key/label/title that surfaces in the\n * shadow tree.\n *\n *  - `lookupKey`: the ONE normalizer every lookup table in this library is keyed\n *    and read by (`tokens/index.ts` glyphs and abbreviations, `i18n/` chrome\n *    strings).\n *  - `capitalize`: title-cases the first character, lowercases the rest. A\n *    DISPLAY helper only; it is not a lookup key (see `lookupKey`).\n *  - `humanize`: turns an API key (`birth_date`, `birthDate`, `mahadasha-end`)\n *    into a label suitable for display (\"Birth date\", \"Mahadasha end\").\n */\n\n/**\n * Fold a name to the canonical key its lookup table is stored under: lower case, every run of spaces, underscores and hyphens collapsed to one space, trimmed.\n *\n * @remarks\n * This exists because a GUESSED key spelling is a defect class this repo has now shipped twice. The glyph tables were keyed off whatever {@link capitalize} happened to produce (`North node`), while the API returns `North Node`, so every call site that reached the table without first calling `capitalize` missed and fell through to a fabricated abbreviation. Aspects hit the same wall from the other side: `/aspects` returns `SEMI SEXTILE` where synastry returns `SEMI_SEXTILE`.\n *\n * One normalizer, applied when a table is DEFINED and again when it is READ, removes the whole class: no call site has to remember a convention, and no table has to guess a spelling. Use it for every new table keyed by an API-supplied name.\n *\n * @example\n * ```ts\n * lookupKey('North Node');    // 'north node'\n * lookupKey('SEMI_SEXTILE');  // 'semi sextile'\n * lookupKey('semi-sextile');  // 'semi sextile'\n * ```\n */\nexport function lookupKey(s: string): string {\n\treturn s\n\t\t.toLowerCase()\n\t\t.replace(/[\\s_-]+/g, ' ')\n\t\t.trim();\n}\n\nexport function capitalize(s: string): string {\n\tif (!s) return '';\n\treturn s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nexport function humanize(s: string): string {\n\treturn (\n\t\ts\n\t\t\t.replace(/[_-]+/g, ' ')\n\t\t\t.replace(/([a-z])([A-Z])/g, '$1 $2')\n\t\t\t// Split a trailing/embedded digit off a word so \"person1\" reads \"Person 1\"\n\t\t\t// and every group legend and field label spaces its ordinal.\n\t\t\t.replace(/([a-z])([0-9])/g, '$1 $2')\n\t\t\t.replace(/^\\w/, (c) => c.toUpperCase())\n\t);\n}\n", "/**\n * The chrome-string catalogue: one resolver reading one registry, so a translation can arrive from anywhere without a component changing.\n *\n * @remarks\n * **The key IS the canonical English string**, folded through {@link lookupKey}. There is no invented key vocabulary to keep in sync with the copy, which is the same rule the glyph tables in `tokens/index.ts` follow (they key off the exact value the API returns). Two consequences that are the whole point:\n *\n * - **English costs zero bytes.** A miss returns the source string, so the English build ships no catalogue at all, and a site that never loads a locale payload is byte-identical to one built before this file existed.\n * - **A missing translation degrades to English, never to a key.** The failure mode of a keyed catalogue is a raw `natal.tabs.wheel` on the page; here the worst case is the English word.\n *\n * **The registry lives on `globalThis`, not in module scope, because it has to.** Every `dist/cdn/components/*.js` is a self-contained IIFE with its own copy of this module, so a `Map` here would be invisible to the second component on the page. One global object, written by the locale payload and read by every bundle, is what makes `dist/cdn/locales/es.js` a single download that translates all of them.\n *\n * Load order is not guaranteed on a real host (WordPress enqueues scripts in an order the plugin does not fully control, and a payload may be deferred), so {@link onLocaleChange} lets an element that has already rendered in English re-render when its catalogue lands.\n */\n\nimport { lookupKey } from '../utils/string.js';\n\n/** Shared mutable state, addressed by a name rather than a module binding so every bundle on the page reaches the same object. */\ninterface LocaleRegistry {\n\t/** Language tag (lower case) to catalogue, keyed by {@link lookupKey} of the English source string. */\n\tcatalogs: Record<string, Record<string, string>>;\n\t/**\n\t * Language tag (lower case) to the API's field-label map: `fields` keyed by wire name,\n\t * `enums` keyed `{fieldName}.{value}`.\n\t *\n\t * A SEPARATE key space from `catalogs` on purpose. A chrome string is keyed by the English\n\t * text a component writes, which is a constant in our source; a field label is keyed by a\n\t * name that only the OpenAPI spec knows, computed per operation, so no catalogue keyed on\n\t * English source text can ever reach it. Same registry because the delivery problem is\n\t * identical (a payload that may land after first paint), so it reuses one global object and\n\t * one listener set rather than growing a second of each.\n\t */\n\tfieldLabels: Record<string, FieldLabels>;\n\t/** Language tags whose fetch is in flight or done, so N forms on a page cost ONE request. */\n\trequested: Set<string>;\n\t/** Elements waiting to re-render when a catalogue arrives. */\n\tlisteners: Set<() => void>;\n}\n\n/** The `/languages/field-labels` payload, minus the echoed `lang`. */\nexport interface FieldLabels {\n\tfields: Record<string, string>;\n\tenums: Record<string, string>;\n}\n\nconst GLOBAL_KEY = '__ROXY_UI_I18N__';\n\nfunction registry(): LocaleRegistry {\n\tconst holder = globalThis as unknown as Record<string, LocaleRegistry>;\n\tconst existing = holder[GLOBAL_KEY];\n\tif (existing) return existing;\n\tconst created: LocaleRegistry = {\n\t\tcatalogs: {},\n\t\tfieldLabels: {},\n\t\trequested: new Set(),\n\t\tlisteners: new Set(),\n\t};\n\tholder[GLOBAL_KEY] = created;\n\treturn created;\n}\n\n/**\n * Publish a catalogue for a language tag and wake every mounted element.\n *\n * @remarks\n * The catalogue is authored keyed by the English source string, exactly as it appears at the call site, and normalized here. That keeps the source file readable and diffable against the component that renders it, while the runtime lookup stays a single property read.\n *\n * This is the ONLY way into the registry, so a catalogue can later come from a static file, a server response, or a host page inlining a script tag, without a component knowing the difference.\n */\nexport function registerLocale(\n\tlang: string,\n\tcatalog: Record<string, string>,\n): void {\n\tconst reg = registry();\n\tconst normalized: Record<string, string> = {};\n\tfor (const [source, translated] of Object.entries(catalog)) {\n\t\tnormalized[lookupKey(source)] = translated;\n\t}\n\treg.catalogs[lang.toLowerCase()] = normalized;\n\tfor (const listener of reg.listeners) listener();\n}\n\n/**\n * Replace `{{name}}` placeholders. Copied from the API-side translator so both sides of the product interpolate identically; an unknown placeholder is left verbatim rather than blanked, so a typo in a catalogue is visible instead of silently eating the value.\n */\nexport function interpolate(\n\ttemplate: string,\n\tvars?: Record<string, string | number>,\n): string {\n\tif (!vars) return template;\n\treturn template.replace(/\\{\\{(\\w+)\\}\\}/g, (match, key: string) =>\n\t\tkey in vars ? String(vars[key] ?? '') : match,\n\t);\n}\n\n/**\n * The chrome string for `lang`, falling back to the English `source` it is keyed by.\n *\n * @remarks\n * A regional tag resolves against its base language, so `es-AR` and `es-419` both read the `es` catalogue and a site does not have to know which tags we happen to ship. An exact regional catalogue still wins if one is ever registered.\n */\nexport function translate(\n\tlang: string | undefined,\n\tsource: string,\n\tvars?: Record<string, string | number>,\n): string {\n\tif (!lang) return interpolate(source, vars);\n\tconst { catalogs } = registry();\n\tconst tag = lang.toLowerCase();\n\tconst catalog = catalogs[tag] ?? catalogs[tag.split('-')[0] as string];\n\treturn interpolate(catalog?.[lookupKey(source)] ?? source, vars);\n}\n\n/**\n * Publish a field-label map for a language tag and wake every mounted element.\n *\n * @remarks\n * Exported so a host that already has the payload (a server-rendered page, a test, a WordPress\n * plugin inlining it) can supply it without a network call, exactly as {@link registerLocale}\n * allows for chrome strings.\n */\nexport function registerFieldLabels(lang: string, labels: FieldLabels): void {\n\tconst reg = registry();\n\treg.fieldLabels[lang.toLowerCase()] = labels;\n\tfor (const listener of reg.listeners) listener();\n}\n\n/** The field-label map for `lang`, resolving a regional tag against its base language. */\nfunction labelsFor(lang: string | undefined): FieldLabels | undefined {\n\tif (!lang) return undefined;\n\tconst { fieldLabels } = registry();\n\tconst tag = lang.toLowerCase();\n\treturn fieldLabels[tag] ?? fieldLabels[tag.split('-')[0] as string];\n}\n\n/**\n * The label for a request field name, or `undefined` when nothing has been published.\n *\n * @remarks\n * Returning `undefined` rather than the name is deliberate: the caller owns the fallback, which is\n * `humanize()`, and that keeps this resolver honest about what it actually knows. A form renders\n * identically to before until a payload arrives.\n */\nexport function fieldLabel(\n\tlang: string | undefined,\n\tname: string,\n): string | undefined {\n\treturn labelsFor(lang)?.fields[name];\n}\n\n/** The label for one selectable option, keyed `{fieldName}.{value}` as the API publishes it. */\nexport function optionLabel(\n\tlang: string | undefined,\n\tfield: string,\n\tvalue: string,\n): string | undefined {\n\treturn labelsFor(lang)?.enums[`${field}.${value}`];\n}\n\n/** Subscribe to catalogue arrivals. Returns the unsubscribe, so an element can drop it on disconnect. */\nexport function onLocaleChange(listener: () => void): () => void {\n\tconst { listeners } = registry();\n\tlisteners.add(listener);\n\treturn () => {\n\t\tlisteners.delete(listener);\n\t};\n}\n", "import type { ReactiveController, ReactiveControllerHost } from 'lit';\nimport { onLocaleChange } from './registry.js';\n\n/**\n * Re-renders its host when a chrome-string catalogue arrives.\n *\n * @remarks\n * Without this the catalogue would have to be on the page BEFORE the elements upgrade, which is not something a component can require of a real host. WordPress orders enqueued scripts by dependency and a site owner can add `defer` to any of them; a CDN embed is two independent `<script src>` tags. Any of those can land the locale payload after the first paint, and the visible result would be a chart that renders in English on a Spanish page for no reason the site owner can see or fix.\n *\n * Mirrors `MarkupDataController`: constructed in {@link RoxyDataElement}'s constructor, subscribes on connect, drops the subscription on disconnect. Registration is idempotent and a re-register of the same language simply re-renders, so a payload loaded twice is harmless.\n */\nexport class LocaleController implements ReactiveController {\n\tprivate readonly host: ReactiveControllerHost;\n\tprivate unsubscribe?: () => void;\n\n\tconstructor(host: ReactiveControllerHost) {\n\t\tthis.host = host;\n\t\thost.addController(this);\n\t}\n\n\thostConnected(): void {\n\t\tthis.unsubscribe = onLocaleChange(() => this.host.requestUpdate());\n\t}\n\n\thostDisconnected(): void {\n\t\tthis.unsubscribe?.();\n\t\tthis.unsubscribe = undefined;\n\t}\n}\n", "import { css } from 'lit';\n\n/**\n * Shared host styles every component pulls in. Sets default font, color,\n * container query support, and the entry fade-in.\n */\nexport const baseStyles = css`:host{font-family:var(--roxy-font-sans,system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif);color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-base,1rem);line-height:var(--roxy-leading-normal,1.5);animation:roxy-fade-in var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,cubic-bezier(.4, 0, .2, 1)) both;background:0 0;display:block;container-type:inline-size}:host([bare]){--roxy-surface:transparent;--roxy-shadow-sm:none;--roxy-shadow-md:none}*,:before,:after{box-sizing:border-box}h1,h2,h3,h4{font-family:var(--roxy-font-display,var(--roxy-font-sans,system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif))}@keyframes roxy-fade-in{0%{opacity:0;transform:translateY(2px)}to{opacity:1;transform:translateY(0)}}@media (prefers-reduced-motion:reduce){:host{animation:none}}.roxy-skeleton{background:linear-gradient(90deg, var(--roxy-border,#e4e4e7) 0%, color-mix(in srgb, var(--roxy-border,#e4e4e7) 60%, transparent) 50%, var(--roxy-border,#e4e4e7) 100%);border-radius:var(--roxy-radius-md,8px);background-size:200% 100%;animation:1.4s ease-in-out infinite roxy-shimmer}@keyframes roxy-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}@media (prefers-reduced-motion:reduce){.roxy-skeleton{animation:none}}.roxy-sr-only{clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:0;padding:0;position:absolute;overflow:hidden}.roxy-empty{padding:var(--roxy-space-lg,1.5rem);color:var(--roxy-muted,#71717a);text-align:center;font-size:var(--roxy-text-sm,.875rem)}.roxy-error{gap:var(--roxy-space-sm,.5rem);padding:var(--roxy-space-lg,1.5rem);background:var(--roxy-surface,#fff);border:1px solid var(--roxy-danger,#dc2626);border-radius:var(--roxy-radius-md,8px);color:var(--roxy-danger-fg,#991b1b);font-size:var(--roxy-text-sm,.875rem);justify-items:start;display:grid}:host(:focus-within) .roxy-card{outline:2px solid var(--roxy-ring,#f59e0b66);outline-offset:2px}.roxy-attribution{margin-top:var(--roxy-space-sm,.5rem);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-align:right}.roxy-attribution a{color:inherit;text-decoration:none}.roxy-attribution a:hover{color:var(--roxy-accent-ink,#b45309);text-decoration:underline}.roxy-attribution a:focus-visible{outline:2px solid var(--roxy-ring,#f59e0b66);outline-offset:2px}.roxy-edit-bar{margin-bottom:var(--roxy-space-sm,.5rem);justify-content:flex-end;display:flex}.roxy-edit{padding:var(--roxy-space-xs,.25rem) var(--roxy-space-md,1rem);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);color:var(--roxy-fg,#0a0a0a);font-family:inherit;font-size:var(--roxy-text-sm,.875rem);cursor:pointer;background:0 0}.roxy-edit:hover{border-color:var(--roxy-accent,#f59e0b);color:var(--roxy-accent-ink,#b45309)}.roxy-edit:focus-visible{outline:2px solid var(--roxy-ring,#f59e0b66);outline-offset:2px}:host{font-variant-emoji:text}`;\n", "/**\n * The one place the \"publishable key only\" rule lives. Every client-side fetch boundary ({@link ../components/location-search.ts}, {@link ./fetch-controller.ts}) calls through here so a secret or legacy unprefixed key is refused identically: no network call, the same visible message, the same `roxy-validation-error` event. Centralising it is what stops the two paths drifting, which is exactly how `<roxy-location-search>` used to warn-then-fetch a secret key while the controller fail-closed.\n */\n\n/** Canonical refusal message. Both fetch boundaries surface this exact wording. */\nexport const KEY_REFUSED_MESSAGE =\n\t'Client-side components accept a pk_ publishable key only. Use a publishable key with an origin allowlist, or render server-side.';\n\n/** True when the key is a browser-safe `pk_` publishable key. */\nexport function isPublishableKey(key: string | undefined | null): boolean {\n\treturn !!key && key.startsWith('pk_');\n}\n\n/**\n * The fail-closed condition: a key is PRESENT and is NOT a `pk_` key. An absent key is not refused, so a keyless public call (a form-slotted city search with no key set) still proceeds; only a real secret/legacy token in the browser is stopped.\n */\nexport function keyIsRefused(key: string | undefined | null): boolean {\n\treturn !!key && !key.startsWith('pk_');\n}\n\n/**\n * Announce a refused key: dispatch the shared `roxy-validation-error` (bubbling, composed) and, when `warn` is set, log it once for the developer. The caller sets its own visible error state and skips the fetch; this only handles the parts both boundaries share.\n */\nexport function dispatchKeyRefusal(\n\thost: EventTarget,\n\topts: { warn?: boolean } = {},\n): void {\n\tif (opts.warn) console.warn(KEY_REFUSED_MESSAGE);\n\thost.dispatchEvent(\n\t\tnew CustomEvent('roxy-validation-error', {\n\t\t\tdetail: { reason: 'possible-secret-key', message: KEY_REFUSED_MESSAGE },\n\t\t\tbubbles: true,\n\t\t\tcomposed: true,\n\t\t}),\n\t);\n}\n", "import type { ReactiveController, ReactiveControllerHost } from 'lit';\nimport {\n\tdispatchKeyRefusal,\n\tKEY_REFUSED_MESSAGE,\n\tkeyIsRefused,\n} from './key-guard.js';\n\n/**\n * Host slots the controller drives. {@link RoxyDataElement} satisfies this, so the form mixin can attach a controller without the component wiring state by hand.\n *\n * @remarks\n * Intersected with `HTMLElement` because the controller dispatches events on the host. Lit's `ReactiveControllerHost` is the update-lifecycle contract only (`addController`, `requestUpdate`, `updateComplete`) and carries no DOM surface, even though every real host is a `LitElement` and therefore an `HTMLElement`. Declaring the DOM half is what the controller actually needs; it used to reach it through a `this.host as unknown as EventTarget` double cast instead, which asserted a capability the type never promised.\n */\ntype FetchHost<T> = ReactiveControllerHost &\n\tHTMLElement & {\n\t\tdata: T | null;\n\t\tloading: boolean;\n\t\terror: string | null;\n\t};\n\n/** Default RoxyAPI v2 origin. A component overrides it per instance via its `base-url` attribute. */\n/** The public API root. Exported so anything else that must reach the API (the field-label\n * fetch in `endpoint-form`) uses the SAME origin as every data request instead of a second copy. */\nexport const DEFAULT_BASE_URL = 'https://roxyapi.com/api/v2';\n\n/**\n * Turn a failed `Response` into the message to show: the API's own `{ error }` string when the body carries one, else the status line.\n *\n * @remarks\n * Exported so every client-side fetch boundary renders the same words for the same failure, the way {@link keyIsRefused} centralizes the key refusal. A boundary that discards the response body cannot tell a rejected request from an empty result, and renders the two identically.\n */\nexport async function readApiError(res: Response): Promise<string> {\n\ttry {\n\t\tconst body = (await res.json()) as { error?: string };\n\t\tif (body?.error) return body.error;\n\t} catch {\n\t\t// Non-JSON error body: fall through to the status line.\n\t}\n\treturn `Request failed (${res.status})`;\n}\n\n/** A single request the controller issues on the component's behalf. */\nexport interface RoxyRequest {\n\t/** Path under the API base, e.g. \"/dreams/symbols/water\" or \"/astrology/natal-chart\". */\n\tpath: string;\n\tmethod?: 'GET' | 'POST';\n\t/** JSON body for POST endpoints. */\n\tbody?: unknown;\n\t/** Query string parameters; nullish values are dropped. */\n\tquery?: Record<string, string | number | undefined>;\n}\n\n/**\n * Client-side fetch for uncontrolled (self-fetching) components: drives `host.data` / `host.loading` / `host.error` and cancels a stale request when a newer one starts or the host disconnects.\n *\n * @remarks\n * Security boundary. The only credential this ever sends is a `pk_` publishable key, which carries a server-side origin allowlist. A secret (`sk_`) or legacy unprefixed key is refused before any network call and surfaced as an error, so a server secret cannot leak into a browser request. This centralizes the guard that originated in `<roxy-location-search>` so every self-fetching component enforces it identically.\n *\n * Controlled-mode components never construct this. When a server injects the response as a `<script class=\"roxy-data\">` island, there is no key and no fetch, which is the path server-rendered consumers (WordPress, JSX SSR, static HTML) rely on.\n */\nexport class FetchController<T = unknown> implements ReactiveController {\n\tprivate readonly host: FetchHost<T>;\n\tprivate abort?: AbortController;\n\n\t/** Browser-safe publishable key. Set by the host from its `publishable-key` attribute. */\n\tpublishableKey?: string;\n\t/** API origin, overridable for self-hosted or proxied deployments. */\n\tbaseUrl = DEFAULT_BASE_URL;\n\t/**\n\t * Consumer backend route that holds the secret key. When set, the request is POSTed here as `{ path, method, body, query }` instead of called against RoxyAPI directly, so no key (publishable or secret) is sent from the browser. This is the canonical path for server-rendered hosts (WordPress); the backend proxies the request with its own `sk_` key and returns the JSON response.\n\t */\n\tsubmitUrl?: string;\n\t/**\n\t * Object the host page attaches to the proxied request, carried beside it as `context`. Set by the host from its `submit-context` attribute, and shapeless on purpose: this passes it through and reads no key of it, so what it holds is for the page and its own route to agree on.\n\t *\n\t * @remarks\n\t * Only the {@link FetchController.submitUrl} path can carry it, because that is the only body this code writes. A direct call sends the request the endpoint declares and nothing beside it.\n\t */\n\tsubmitContext?: Record<string, unknown>;\n\n\tconstructor(host: FetchHost<T>) {\n\t\tthis.host = host;\n\t\thost.addController(this);\n\t}\n\n\thostDisconnected() {\n\t\tthis.abort?.abort();\n\t\tthis.abort = undefined;\n\t}\n\n\t/**\n\t * Issue the request and resolve once `host.data` (success) or `host.error`\n\t * (failure) is set. A no-op return when a non-publishable key is present:\n\t * the error is already surfaced and nothing is sent.\n\t */\n\tasync run(req: RoxyRequest): Promise<void> {\n\t\t// submit-url proxy: no key leaves the browser; the consumer's backend holds it.\n\t\tif (!this.submitUrl && this.secretKeyRefused()) return;\n\t\tthis.abort?.abort();\n\t\tconst controller = new AbortController();\n\t\tthis.abort = controller;\n\t\tthis.host.loading = true;\n\t\tthis.host.error = null;\n\t\ttry {\n\t\t\tconst res = this.submitUrl\n\t\t\t\t? await fetch(this.submitUrl, {\n\t\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\tAccept: 'application/json',\n\t\t\t\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t\t\t},\n\t\t\t\t\t\tbody: this.proxyBody(req),\n\t\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t\t})\n\t\t\t\t: await this.callApi(req, controller.signal);\n\t\t\tif (!res.ok) throw new Error(await readApiError(res));\n\t\t\tconst json = (await res.json()) as T;\n\t\t\tif (controller.signal.aborted) return;\n\t\t\tthis.host.data = json;\n\t\t} catch (err) {\n\t\t\tif ((err as { name?: string })?.name === 'AbortError') return;\n\t\t\tthis.host.error = err instanceof Error ? err.message : String(err);\n\t\t} finally {\n\t\t\tif (this.abort === controller) this.abort = undefined;\n\t\t\tif (!controller.signal.aborted) this.host.loading = false;\n\t\t}\n\t}\n\n\t/**\n\t * The body of a proxied POST: the request, plus {@link FetchController.submitContext} under `context` when the host set one.\n\t *\n\t * @remarks\n\t * The context is appended rather than declared on {@link RoxyRequest}, so with none set the payload is the request object itself and every route already written against it keeps receiving the same four keys in the same order. A `null` or an empty object would be a fifth key such a route never agreed to read.\n\t */\n\tprivate proxyBody(req: RoxyRequest): string {\n\t\treturn JSON.stringify(\n\t\t\tthis.submitContext ? { ...req, context: this.submitContext } : req,\n\t\t);\n\t}\n\n\t/** Direct call against RoxyAPI with the publishable key (the no-backend path). */\n\tprivate callApi(req: RoxyRequest, signal: AbortSignal): Promise<Response> {\n\t\t// Resolved against the page, so a same-origin base (\"/api/roxy\") is as valid as an\n\t\t// absolute one. Every route a host page can name resolves the same way, and a bare\n\t\t// `new URL()` would reject the relative shape with an opaque \"Invalid URL\".\n\t\tconst url = new URL(`${this.baseUrl}${req.path}`, document.baseURI);\n\t\tfor (const [k, v] of Object.entries(req.query ?? {})) {\n\t\t\tif (v != null) url.searchParams.set(k, String(v));\n\t\t}\n\t\tconst headers: Record<string, string> = { Accept: 'application/json' };\n\t\tif (this.publishableKey) headers['X-API-Key'] = this.publishableKey;\n\t\tif (req.body != null) headers['Content-Type'] = 'application/json';\n\t\treturn fetch(url, {\n\t\t\tmethod: req.method ?? 'GET',\n\t\t\theaders,\n\t\t\tbody: req.body != null ? JSON.stringify(req.body) : undefined,\n\t\t\tsignal,\n\t\t});\n\t}\n\n\t/** True when a key is set and it is not a browser-safe `pk_` publishable key. Surfaces the error and refuses to send, via the shared {@link keyIsRefused} guard so every fetch boundary fail-closes identically. */\n\tprivate secretKeyRefused(): boolean {\n\t\tif (!keyIsRefused(this.publishableKey)) return false;\n\t\tthis.host.error = KEY_REFUSED_MESSAGE;\n\t\tdispatchKeyRefusal(this.host);\n\t\treturn true;\n\t}\n}\n\n/**\n * Translate flat form values into a {@link RoxyRequest} for an endpoint. The `{name}` segments in the endpoint template are substituted from the values and removed; the rest become the JSON body for POST or query parameters for GET. This is the spec-light request builder the base element uses to turn a `<roxy-endpoint-form>` submission into a self-fetch, so a component needs no per-endpoint glue.\n *\n * @param queryKeys - Names the spec declares as `in: query` for this operation, which `<roxy-endpoint-form>` reports on its `roxy-submit` event. A POST operation can still take query parameters (every localized endpoint takes `?lang=`), and sending one in the JSON body silently drops it, so those names are routed to the query string on POST as well as GET.\n */\nexport function buildRequest(\n\tendpoint: string,\n\tmethod: 'GET' | 'POST',\n\tvalues: Record<string, unknown>,\n\tqueryKeys: readonly string[] = [],\n): RoxyRequest {\n\tconst rest: Record<string, unknown> = { ...values };\n\tconst path = `/${endpoint.replace(/^\\//, '')}`.replace(\n\t\t/\\{([^}]+)\\}/g,\n\t\t(_match, name: string) => {\n\t\t\tconst v = rest[name];\n\t\t\tdelete rest[name];\n\t\t\treturn encodeURIComponent(String(v ?? ''));\n\t\t},\n\t);\n\tconst inQuery = new Set(queryKeys);\n\tconst query: Record<string, string | number | undefined> = {};\n\tconst body: Record<string, unknown> = {};\n\tfor (const [k, v] of Object.entries(rest)) {\n\t\tif (v === undefined || v === '') continue;\n\t\tif (method === 'GET' || inQuery.has(k)) query[k] = v as string | number;\n\t\telse body[k] = v;\n\t}\n\treturn { path, method, query, body: method === 'POST' ? body : undefined };\n}\n", "/**\n * The interpretation accordion: the one way this library shows a label, a short value, and a body of prose behind a disclosure.\n *\n * @remarks\n * Exclusive by construction (`<details name>`), so a card grows by at most one open section and never becomes a wall of text. Callers pass their own group `name` so two accordions on one page do not fight over which section is open.\n *\n * This lives here rather than in a component because the same accordion is the right shape wherever an endpoint returns a labelled reading: the Human Design bodygraph and type card use it for strategy, authority, and aura; `roxy-hexagram` uses it for the changing lines. A card that draws its own disclosure, because its summary carries more than a label and an aside, still renders the shared body and imports {@link readingDetailStyles} for it rather than restating those rules.\n *\n * **Call it through `RoxyDataElement.renderInterpretation`, never directly.** That method is where `hide-readings` is honoured, so a direct call renders prose a host asked to be left out. The section it emits carries `part=\"section readings\"` and each row `part=\"reading\"`, which is what makes the readings of EVERY component addressable under one name from outside the shadow root.\n */\n\nimport { css, html, nothing } from 'lit';\nimport type { ChromeString } from '../i18n/chrome-strings.js';\nimport { chevron } from './disclosure.js';\nimport type { Translate } from './hd-reading.js';\n\n/** One row: the label, the short value shown as the aside, and the interpretation body. `extra` renders under the body for a section that carries more than prose. */\nexport interface InterpSection {\n\tlabel: string;\n\taside?: string;\n\tbody: string;\n\textra?: unknown;\n}\n\n/**\n * Styles for the markup {@link renderReadingDetail} emits. Import this wherever that helper is called; {@link interpAccordionStyles} already embeds it, so an accordion caller needs only the one.\n *\n * @remarks\n * Separate from the accordion shell because the two are needed independently: a card can draw its own disclosure and still render the shared reading body, and hand-copying these rules is how the chip colour and spacing drifted apart across five components.\n *\n * No margin-top on the chip row. The body above already spaces its children, so a margin here would set the chips twice as far out as every other row.\n */\nexport const readingDetailStyles = css`.interp-keywords{flex-wrap:wrap;gap:.25rem;display:flex}.interp-keywords .kw{border-radius:var(--roxy-radius-full,9999px);background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 14%, transparent);color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-xs,.75rem);padding:1px 8px}`;\n\nexport const interpAccordionStyles = css`.block{border-top:1px solid var(--roxy-border,#e4e4e7);padding-top:var(--roxy-space-md,1rem)}.block h3{margin:0 0 var(--roxy-space-sm,.5rem);font-size:var(--roxy-text-sm,.875rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.06em}.interp-card{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);margin-bottom:var(--roxy-space-xs,.25rem)}.interp-card summary{cursor:pointer;color:var(--roxy-fg,#0a0a0a);align-items:center;gap:var(--roxy-space-sm,.5rem);font-weight:500;font-size:var(--roxy-text-sm,.875rem);display:flex}.interp-card summary:focus-visible{outline:2px solid var(--roxy-ring,#f59e0b66);outline-offset:2px;border-radius:var(--roxy-radius-sm,4px)}.interp-lead{flex-wrap:wrap;flex:auto;order:1;align-items:baseline;gap:.4rem;min-width:0;display:inline-flex}.interp-aside{flex-shrink:0;order:2;align-items:center;gap:.5rem;display:inline-flex}.roxy-chevron{order:3}.interp-aside small{color:var(--roxy-muted,#71717a);white-space:nowrap;font-weight:400}@container (width<=26rem){.interp-card summary{flex-wrap:wrap}.roxy-chevron{order:2}.interp-aside{flex-basis:100%;order:3;justify-content:flex-end}}.interp-body{margin-top:var(--roxy-space-sm,.5rem);gap:var(--roxy-space-sm,.5rem);color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-sm,.875rem);overflow-wrap:anywhere;line-height:1.6;display:grid}.interp-body p{margin:0}${readingDetailStyles}`;\n\n/** The keyword chips a reading carries, or nothing. One renderer because four components drew the identical row, and the markup has to match {@link readingDetailStyles}, which is the pairing that drifts when it is copied. */\nexport function renderKeywordChips(keywords: readonly string[] | undefined) {\n\tif (!keywords?.length) return nothing;\n\treturn html`<div class=\"interp-keywords\">${keywords.map((k) => html`<span class=\"kw\">${k}</span>`)}</div>`;\n}\n\n/** The prose an endpoint returns ABOUT one contact: a summary, up to three labelled lines, and the keyword chips under them. Every field is optional, so a narrower response renders fewer rows rather than empty ones. */\nexport interface ReadingDetail {\n\tsummary?: string;\n\timpact?: string;\n\ttiming?: string;\n\tguidance?: string;\n\tkeywords?: readonly string[];\n}\n\n/** Render one {@link ReadingDetail}. The three labels are copy, so this takes the translator, and they are typed {@link ChromeString} because the lookup is one indirection from the call: a scan of `t(...)` sites cannot follow that, and the compiler can. */\nexport function renderReadingDetail(\n\td: ReadingDetail | undefined,\n\tt: Translate,\n) {\n\tif (!d) return nothing;\n\tconst line = (label: ChromeString, value: string | undefined) =>\n\t\tvalue ? html`<p><strong>${t(label)}</strong> ${value}</p>` : nothing;\n\treturn html`${d.summary ? html`<p>${d.summary}</p>` : nothing} ${line('Impact:', d.impact)}${line('Timing:', d.timing)}${line('Guidance:', d.guidance)} ${renderKeywordChips(d.keywords)}`;\n}\n\n/**\n * Render the accordion. Sections with no body are dropped, so a caller can pass the full set and let a narrower response render only what it carries. `name` groups the `<details>` exclusively, so opening one closes the last.\n */\nexport function renderInterpAccordion(\n\tsections: InterpSection[],\n\tname: string,\n\theading: string,\n) {\n\tconst shown = sections.filter((s) => Boolean(s.body));\n\tif (shown.length === 0) return nothing;\n\n\treturn html`<section class=\"block\" part=\"section readings\"><h3>${heading}</h3>${shown.map(\n\t\t\t(\n\t\t\t\ts,\n\t\t\t\ti,\n\t\t\t) => html`<details class=\"interp-card\" part=\"reading\" name=\"${name}\" ?open=\"${i === 0}\"><summary><span class=\"interp-lead\">${s.label}</span> ${chevron()} ${s.aside ? html`<span class=\"interp-aside\"><small>${s.aside}</small></span>` : nothing}</summary><div class=\"interp-body\"><p>${s.body}</p>${s.extra ?? nothing}</div></details>`,\n\t\t)}</section>`;\n}\n", "import type { TemplateResult } from 'lit';\nimport { css, html } from 'lit';\n\n/**\n * Disclosure chevron for `<details>`/`<summary>` accordions. Replaces the\n * browser default triangle with a thin chevron that rotates when the host\n * `<details>` opens. Render it inside the `<summary>` and pair with\n * {@link disclosureStyles}.\n */\nexport function chevron(): TemplateResult {\n\treturn html`<svg class=\"roxy-chevron\" viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" aria-hidden=\"true\"><path d=\"M4 6l4 4 4-4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>`;\n}\n\n/**\n * Shared `<details>` accordion styling: hides the native disclosure triangle\n * and rotates the {@link chevron} 180 degrees when the accordion is open.\n * Import into any component that renders disclosure accordions so the chevron\n * looks and behaves identically.\n */\nexport const disclosureStyles = css`summary{list-style:none}summary::-webkit-details-marker{display:none}.roxy-chevron{aspect-ratio:auto;width:14px;height:14px;color:var(--roxy-muted,#71717a);transition:transform var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,ease);flex-shrink:0}details[open]>summary .roxy-chevron{transform:rotate(180deg)}@media (prefers-reduced-motion:reduce){.roxy-chevron{transition:none}}`;\n", "import type { ReactiveController, ReactiveControllerHost } from 'lit';\n\n/**\n * Marker class on the inline JSON script a server emits inside a component.\n *\n * @example\n * ```html\n * <roxy-natal-chart>\n *   <script type=\"application/json\" class=\"roxy-data\">{ ...response... }</script>\n * </roxy-natal-chart>\n * ```\n */\nconst ROXY_DATA_CLASS = 'roxy-data';\n\n/**\n * Serialize a RoxyAPI response for embedding inside an inline `<script type=\"application/json\" class=\"roxy-data\">`. This is the safe writer counterpart to {@link MarkupDataController}, the reader: server-rendered and cached consumers (WordPress, JSX SSR, static HTML) emit the script with this, and the component hydrates `data` from it on connect.\n *\n * @remarks\n * Use this instead of a bare `JSON.stringify`. A raw stringify of a response that contains the literal text `</script>` (common in long interpretation prose) closes the script element early, corrupting the page and creating an HTML-injection vector. This escapes the script-unsafe characters to their `\\uXXXX` JSON escapes, which `JSON.parse` restores to the original characters, so the value the component receives is identical to the response you passed.\n *\n * `<` is the load-bearing escape (it defeats `</script>` and `<!--`). `>` and `&` are escaped for defence in depth, and U+2028 / U+2029 because they are valid in JSON yet are line terminators in a `<script>` context and break some parsers. The escapes introduce only `\\uXXXX` sequences, so the replacements never feed each other and order is irrelevant.\n *\n * Pass the unwrapped RoxyAPI response, the same shape you would assign to `element.data`. Do not pass the SDK envelope (`{ data, error, request, response }`).\n *\n * @example\n * ```ts\n * import { serializeRoxyData } from '@roxyapi/ui';\n *\n * const { data } = await roxy.dreams.getDreamSymbol({ path: { id: 'water' } });\n * const html = `<roxy-dream-card><script type=\"application/json\" class=\"roxy-data\">${serializeRoxyData(data)}</script></roxy-dream-card>`;\n * ```\n */\nexport function serializeRoxyData(data: unknown): string {\n\treturn JSON.stringify(data)\n\t\t.replace(/</g, '\\\\u003c')\n\t\t.replace(/>/g, '\\\\u003e')\n\t\t.replace(/&/g, '\\\\u0026')\n\t\t.replace(/\\u2028/g, '\\\\u2028')\n\t\t.replace(/\\u2029/g, '\\\\u2029');\n}\n\n/**\n * Build the complete `<script type=\"application/json\" class=\"roxy-data\">…</script>` element a server nests inside a `roxy-*` component for the no-JavaScript hydration path. The payload is escaped via {@link serializeRoxyData}, so it is safe to drop straight into HTML output.\n *\n * @remarks\n * The element carries both `type=\"application/json\"` and `class=\"roxy-data\"` because {@link MarkupDataController} reads only a direct-child script that has both. Emit one of these inside the target component; the JavaScript `data` property still wins if it is later assigned.\n *\n * @example\n * ```ts\n * import { roxyDataScript } from '@roxyapi/ui';\n *\n * const { data } = await roxy.crystals.getCrystal({ path: { id: 'amethyst' } });\n * const html = `<roxy-crystal-grid>${roxyDataScript(data)}</roxy-crystal-grid>`;\n * ```\n */\nexport function roxyDataScript(data: unknown): string {\n\treturn `<script type=\"application/json\" class=\"${ROXY_DATA_CLASS}\">${serializeRoxyData(data)}</script>`;\n}\n\n/**\n * True when the element is a `<script type=\"application/json\">`. Uses tag name and attribute rather than `instanceof HTMLScriptElement` so the check holds in every DOM implementation, including server-rendered and hydration runtimes where the constructor global may be absent.\n */\nfunction isJsonScript(el: Element): boolean {\n\treturn (\n\t\tel.nodeName === 'SCRIPT' && el.getAttribute('type') === 'application/json'\n\t);\n}\n\n/**\n * Host shape the controller drives: any reactive element that exposes a public `data` slot. The controller only writes `data` when the host left it unset, so the JavaScript property path always wins.\n */\ninterface DataHost extends ReactiveControllerHost, HTMLElement {\n\tdata?: unknown;\n}\n\n/**\n * Reactive controller that lets a component hydrate its `data` from embedded markup when no `data` property was assigned in JavaScript.\n *\n * @remarks\n * The server-side and cached-render model: a backend renders the RoxyAPI response into a direct-child `<script type=\"application/json\" class=\"roxy-data\">` element, ships static HTML, and never runs per-element JavaScript to assign a property. On connect this controller reads that script, parses it, and feeds the result to the host. The JavaScript property path is untouched and authoritative: if `host.data` already holds a value when the host connects, the controller does nothing and the markup is ignored.\n *\n * Source resolution order on connect, first hit wins:\n *\n * 1. `host.data` already set in JavaScript -> leave it, read nothing.\n * 2. A direct-child `<script type=\"application/json\" class=\"roxy-data\">` -> parse and use. Direct-child only, so a nested component's own script is never read by an ancestor.\n *\n * Fetching from a URL is intentionally unsupported: that would require a browser-visible key and breaks the server-rendered, cached model these consumers rely on.\n *\n * Timing: {@link hostConnected} runs inside the host `connectedCallback`. For an element parsed from server HTML, its direct children are present by the time the custom element upgrades and connects, so the script is readable here. For an element created with `document.createElement` and connected before any child is appended, there is nothing to read and the property path is the only source, which is exactly the existing behavior.\n *\n * Failure is safe: malformed JSON or a missing script leaves `host.data` untouched, so the host renders its normal empty state.\n *\n * Reading the script never mutates it, and only the marked script is touched, so any sibling fallback markup a server nested inside the element (for no-JavaScript, AMP, or crawler rendering) is left in place.\n *\n * @example\n * ```ts\n * import { MarkupDataController } from '../utils/markup-data.js';\n *\n * export class RoxyExample extends LitElement {\n *   constructor() {\n *     super();\n *     new MarkupDataController(this);\n *   }\n *\n *   @property({ attribute: false })\n *   data: ExampleResponse | null = null;\n * }\n * ```\n */\nexport class MarkupDataController<T = unknown> implements ReactiveController {\n\tprivate readonly host: DataHost;\n\n\tconstructor(host: DataHost) {\n\t\tthis.host = host;\n\t\thost.addController(this);\n\t}\n\n\thostConnected() {\n\t\t// Property path wins. If the consumer (React, vanilla `.data =`, the\n\t\t// widgets script, an MCP agent) already set data, never look at markup.\n\t\tif (this.host.data != null) return;\n\n\t\tconst parsed = this.read();\n\t\tif (parsed === undefined) return;\n\n\t\tthis.host.data = parsed as T;\n\t\tthis.host.requestUpdate();\n\t}\n\n\t/**\n\t * Resolve the embedded payload. Returns `undefined` when there is nothing valid to read so the caller can leave `host.data` untouched.\n\t */\n\tprivate read(): T | undefined {\n\t\tconst inline = this.findInlineScript();\n\t\treturn inline ? this.parse(inline.textContent) : undefined;\n\t}\n\n\t/**\n\t * Direct-child `<script type=\"application/json\" class=\"roxy-data\">`. Scoped to immediate children so a nested data-driven component never has its script read by an ancestor, and so sibling fallback markup is ignored.\n\t */\n\tprivate findInlineScript(): Element | null {\n\t\tfor (const child of Array.from(this.host.children)) {\n\t\t\tif (isJsonScript(child) && child.classList.contains(ROXY_DATA_CLASS)) {\n\t\t\t\treturn child;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate parse(text: string | null): T | undefined {\n\t\tif (!text?.trim()) return undefined;\n\t\ttry {\n\t\t\treturn JSON.parse(text) as T;\n\t\t} catch {\n\t\t\t// Malformed embedded JSON: fail safe, leave the host empty state in\n\t\t\t// place rather than throwing during connect.\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport type { ChromeString } from '../i18n/chrome-strings.js';\nimport type { AnalyzeNumberSequenceResponse } from '../types/index.js';\nimport { buildMeaningSections } from '../utils/angel-sections.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { disclosureStyles } from '../utils/disclosure.js';\nimport { interpAccordionStyles } from '../utils/interp-accordion.js';\n\n/** Response energy classification to the English SOURCE its badge is looked up by. The three the spec defines; an unknown value falls back to the neutral wording rather than printing a machine token. */\nconst ENERGY_LABEL: Record<string, ChromeString> = {\n\tpositive: 'Positive energy',\n\tneutral: 'Neutral energy',\n\tcautionary: 'Cautionary energy',\n};\n\n/**\n * Angel number lookup card. Renders /angel-numbers/lookup: the analysed sequence with its pattern classification (type, digit count, unique digits, palindrome, repeating), the context note when the caller said where the number was seen, the known angel-number meaning when the sequence is in the database, and the foundational digit-root meaning that interprets any sequence. Built for synchronicity trackers where users enter arbitrary numbers.\n */\n@customElement('roxy-angel-number-lookup')\nexport class RoxyAngelNumberLookup extends RoxyDataElement<AnalyzeNumberSequenceResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tdisclosureStyles,\n\t\tinterpAccordionStyles,\n\t\tcss`.card{background:var(--roxy-surface,#fff);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.hero{align-items:center;gap:var(--roxy-space-md,1rem);display:flex}.numeral{flex-shrink:0}.hero>div:not(.numeral){min-width:0}@container (width<=24rem){.hero{justify-items:start;gap:var(--roxy-space-sm,.5rem);grid-template-columns:minmax(0,1fr);display:grid}}.numeral{font-size:3rem;line-height:1;font-weight:var(--roxy-weight-bold,600);color:var(--roxy-accent-ink,#b45309);font-variant-numeric:tabular-nums}.label{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.06em;margin:0}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.badges{gap:var(--roxy-space-xs,.25rem);flex-wrap:wrap;display:flex}.badge{border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 35%, transparent);color:var(--roxy-fg,#0a0a0a);text-transform:capitalize;align-items:center;padding:3px 10px;display:inline-flex}.badge.flag{background:color-mix(in srgb, var(--roxy-info,#0284c7) 16%, transparent);color:var(--roxy-info-fg,#075985)}.badge.energy-positive{background:color-mix(in srgb, var(--roxy-success,#16a34a) 18%, transparent)}.badge.energy-cautionary{background:color-mix(in srgb, var(--roxy-warning,#ea580c) 18%, transparent)}.context{gap:var(--roxy-space-xs,.25rem);padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);border-left:3px solid var(--roxy-accent,#f59e0b);background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 8%, transparent);border-radius:0 var(--roxy-radius-sm,4px) var(--roxy-radius-sm,4px) 0;display:grid}.steps{gap:var(--roxy-space-xs,.25rem);color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-sm,.875rem);margin:0;padding-left:1.1rem;line-height:1.6;display:grid}.section{border-top:1px solid var(--roxy-border,#e4e4e7);padding-top:var(--roxy-space-md,1rem);gap:var(--roxy-space-sm,.5rem);display:grid}.section>.label{margin:0}.section>h3{font-size:var(--roxy-text-base,1rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-fg,#0a0a0a);margin:0}.section p{color:var(--roxy-fg,#0a0a0a);line-height:1.6;font-size:var(--roxy-text-sm,.875rem);margin:0}.chips{gap:var(--roxy-space-xs,.25rem);flex-wrap:wrap;display:flex}.chips span{background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 14%, transparent);border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);padding:2px 8px}`,\n\t];\n\n\tprotected renderData(d: AnalyzeNumberSequenceResponse) {\n\t\tconst known = d.knownMeaning;\n\t\tconst root = d.digitRootMeaning;\n\t\tconst heading = known?.title ?? this.t('Number analysis');\n\t\tconst steps = known?.actionSteps ?? [];\n\n\t\treturn html`<article class=\"card\" part=\"card\" aria-label=\"${`Number ${d.number ?? ''}`}\"><div class=\"hero\" part=\"header\">${d.number ? html`<div class=\"numeral\">${d.number}</div>` : nothing}<div>${known?.title ? html`<p class=\"label\">${this.t('Number analysis')}</p>` : nothing}<h2 class=\"title\">${heading}</h2></div></div><div class=\"badges\" part=\"details\">${d.type ? html`<span class=\"badge\">${d.type}</span>` : nothing} ${typeof d.digits === 'number' ? html`<span class=\"badge\">${this.t('{{count}} digits', { count: d.digits })}</span>` : nothing} ${typeof d.uniqueDigits === 'number' ? html`<span class=\"badge\">${this.t('{{count}} unique', { count: d.uniqueDigits })}</span>` : nothing} ${typeof d.digitRoot === 'number' ? html`<span class=\"badge\">${this.t('Digit root {{n}}', { n: d.digitRoot })}</span>` : nothing} ${d.isPalindrome ? html`<span class=\"badge flag\">${this.t('Palindrome')}</span>` : nothing} ${d.isRepeating ? html`<span class=\"badge flag\">${this.t('Repeating')}</span>` : nothing} ${known?.energy ? html`<span class=\"${`badge energy-${known.energy}`}\">${this.t(ENERGY_LABEL[known.energy] ?? 'Neutral energy')}</span>` : nothing}</div>${\n\t\t\t\t// The caller's own note about where the number was seen, echoed back.\n\t\t\t\t// Not an interpretation, so it survives hide-readings.\n\t\t\t\td.contextNote\n\t\t\t\t\t? html`<div class=\"context\" part=\"section context\"><p class=\"label\">${this.t('Where you saw it')}</p><p>${d.contextNote}</p></div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\t// Both sections below are interpretation end to end: the core message,\n\t\t\t\t// the life-area accordion, the affirmation and the action steps. The\n\t\t\t\t// facts they would otherwise carry (the number, its digit root, the\n\t\t\t\t// title) are already in the hero and the badges, so the whole section\n\t\t\t\t// goes rather than leaving a heading over nothing.\n\t\t\t\tknown && !this.hideReadings\n\t\t\t\t\t? html`<div class=\"section\" part=\"section known-meaning\"><p class=\"label\">${this.t('Known angel number')}</p>${known.coreMessage ? html`<p>${known.coreMessage}</p>` : nothing} ${\n\t\t\t\t\t\t\tknown.keywords && known.keywords.length > 0\n\t\t\t\t\t\t\t\t? html`<div class=\"chips\">${known.keywords.map((k) => html`<span>${k}</span>`)}</div>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t} ${this.renderMeaning(known.meaning, 'lookup-known', known.biblical, known.shadow)} ${known.affirmation ? html`<p><em>${known.affirmation}</em></p>` : nothing} ${\n\t\t\t\t\t\t\tsteps.length > 0\n\t\t\t\t\t\t\t\t? html`<p class=\"label\">${this.t('What to do next')}</p><ol class=\"steps\">${steps.map((s) => html`<li>${s}</li>`)}</ol>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t}</div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\troot && !this.hideReadings\n\t\t\t\t\t? html`<div class=\"section\" part=\"section digit-root\"><p class=\"label\">${\n\t\t\t\t\t\t\troot.number\n\t\t\t\t\t\t\t\t? this.t('Foundational digit root ({{n}})', { n: root.number })\n\t\t\t\t\t\t\t\t: this.t('Foundational digit root')\n\t\t\t\t\t\t}</p>${root.title ? html`<h3>${root.title}</h3>` : nothing} ${root.coreMessage ? html`<p>${root.coreMessage}</p>` : nothing} ${!known ? this.renderRootMeaning(root) : nothing}</div>`\n\t\t\t\t\t: nothing\n\t\t\t}</article>`;\n\t}\n\n\tprivate renderMeaning(\n\t\tmeaning: Record<string, string> | undefined,\n\t\tname: string,\n\t\tbiblical?: string,\n\t\tshadow?: string,\n\t) {\n\t\treturn this.renderInterpretation(\n\t\t\tbuildMeaningSections(this.translator, meaning, biblical, shadow),\n\t\t\tname,\n\t\t\t'Reading',\n\t\t);\n\t}\n\n\t/**\n\t * Render the foundational digit-root reading for an unknown sequence: keyword chips, the full life-area accordion (including money), and the affirmation. The digit-root meaning carries no biblical or shadow field, so only the life areas are passed through.\n\t */\n\tprivate renderRootMeaning(\n\t\troot: NonNullable<AnalyzeNumberSequenceResponse['digitRootMeaning']>,\n\t) {\n\t\treturn html`${\n\t\t\troot.keywords && root.keywords.length > 0\n\t\t\t\t? html`<div class=\"chips\">${root.keywords.map((k) => html`<span>${k}</span>`)}</div>`\n\t\t\t\t: nothing\n\t\t}${this.renderMeaning(root.meaning, 'lookup-root')}${\n\t\t\troot.affirmation ? html`<p><em>${root.affirmation}</em></p>` : nothing\n\t\t}`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-angel-number-lookup': RoxyAngelNumberLookup;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport { signGlyph } from '../tokens/index.js';\nimport type { ArudhaResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { frameCaptionStyles, renderFrameCaption } from '../utils/frame.js';\n\ntype Pada = ArudhaResponse['padas'][number];\n\n/** The two padas lifted to the top of the response, and what each is read for. Both are marked in the table too, so the summary and the rows agree. */\nconst LEAD_IDS = new Set(['a1', 'a12']);\n\n/**\n * Arudha padas. Renders /vedic-astrology/arudha: the twelve Jaimini padas, each with the bhava it belongs to, the lord and the count that produced it, the sign it lands in, and what it is read for.\n *\n * @remarks\n * All twelve render, and two are marked. The Arudha Lagna (AL, the pada of the first house) is the one most readings start from, and the Upapada (UL, the pada of the twelfth) is the one a marriage reading turns on, so a table that shows only the AL is missing the second question it will be asked.\n *\n * The derivation is shown, not just the answer. Each row carries the bhava sign, its lord and the lord's sign, which is the count a reader checks by hand, plus a mark where the classical exception fired: a pada that fell in its own bhava or the seventh from it is moved to the tenth from there, and that step is the one implementations most often skip.\n */\n@customElement('roxy-arudha-padas')\nexport class RoxyArudhaPadas extends RoxyDataElement<ArudhaResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tframeCaptionStyles,\n\t\tcss`.wrap{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);background:var(--roxy-surface,#fff);box-shadow:var(--roxy-shadow-sm);overflow:hidden}.head{padding:var(--roxy-space-md,1rem);border-bottom:1px solid var(--roxy-border,#e4e4e7)}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.leads{gap:var(--roxy-space-md,1rem);padding:var(--roxy-space-md,1rem);border-bottom:1px solid var(--roxy-border,#e4e4e7);grid-template-columns:repeat(auto-fit,minmax(9rem,1fr));display:grid}.lead span{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);text-transform:uppercase;letter-spacing:.06em;display:block}.lead strong{font-size:var(--roxy-text-base,1rem);color:var(--roxy-fg,#0a0a0a)}.scroll{min-width:0;overflow-x:auto}table{border-collapse:collapse;width:100%;font-size:var(--roxy-text-sm,.875rem);min-width:700px}thead{background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 20%, transparent)}th,td{padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);text-align:left;white-space:nowrap}th{color:var(--roxy-muted,#71717a);font-weight:var(--roxy-weight-bold,600);text-transform:uppercase;font-size:var(--roxy-text-xs,.75rem);letter-spacing:.04em}tbody tr{border-top:1px solid var(--roxy-border,#e4e4e7)}tbody tr.lead-row{background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 10%, transparent)}td.pada{font-weight:var(--roxy-weight-bold,600);color:var(--roxy-fg,#0a0a0a)}.abbr{color:var(--roxy-muted,#71717a);font-weight:var(--roxy-weight-normal,400);margin-left:.4em}tbody tr.lead-row .abbr,tbody tr.lead-row .glyph{color:var(--roxy-accent-ink,#b45309)}.glyph{color:var(--roxy-muted,#71717a);margin-right:.4em}.num{font-variant-numeric:tabular-nums}.exception{font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-warning-fg,#b45309)}.meaning{white-space:normal;min-width:12rem}.meaning strong{font-weight:var(--roxy-weight-bold,600);display:block}.meaning p{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);line-height:var(--roxy-leading-normal,1.5);margin:0}.note{padding:0 var(--roxy-space-md,1rem) var(--roxy-space-md,1rem);color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);line-height:var(--roxy-leading-normal,1.5);margin:0}`,\n\t];\n\n\tprotected renderData(d: ArudhaResponse) {\n\t\tconst padas = d.padas ?? [];\n\t\tif (!padas.length) return this.renderEmpty();\n\t\tconst exceptions = padas.filter((p) => p.exceptionApplied).length;\n\n\t\treturn html`<div class=\"wrap\" part=\"card\" aria-label=\"${this.t('Arudha padas')}\"><header class=\"head\" part=\"header\"><h2 class=\"title\">${this.t('Arudha padas')}</h2>${renderFrameCaption(this.effectiveLang(), d.frame, this.translator)}</header><div class=\"leads\" part=\"details\"><div class=\"lead\"><span>${this.t('Lagna')}</span><strong>${d.lagnaRashi ?? ''}</strong></div><div class=\"lead\"><span>${this.t('Arudha Lagna')}</span><strong>${d.arudhaLagna ?? ''}</strong></div><div class=\"lead\"><span>${this.t('Upapada')}</span><strong>${d.upapada ?? ''}</strong></div></div><div class=\"scroll\" part=\"table\" tabindex=\"0\"><table role=\"table\"><caption class=\"roxy-sr-only\">${this.t(\n\t\t\t\t\t\t\t'The twelve Arudha padas: each pada with its bhava, the bhava sign and its lord, the sign the lord occupies, the sign the pada falls in, which house from the Lagna that is, whether the classical exception was applied, and what the pada is read for.',\n\t\t\t\t\t\t)}</caption><thead><tr><th scope=\"col\">${this.t('Pada')}</th><th scope=\"col\">${this.t('Bhava')}</th><th scope=\"col\">${this.t('Bhava rashi')}</th><th scope=\"col\">${this.t('Lord')}</th><th scope=\"col\">${this.t('Lord rashi')}</th><th scope=\"col\">${this.t('Pada rashi')}</th><th scope=\"col\">${this.t('From Lagna')}</th><th scope=\"col\">${this.t('Read for')}</th></tr></thead><tbody>${padas.map((p) => this.renderRow(p))}</tbody></table></div>${\n\t\t\t\texceptions > 0\n\t\t\t\t\t? html`<p class=\"note\" part=\"legend\"><span class=\"exception\">${this.t('Moved')}</span> ${this.t(\n\t\t\t\t\t\t\t'marks a pada that fell in its own bhava or the seventh from it and was moved to the tenth from there, as the classical rule requires. {{count}} of {{total}} padas here.',\n\t\t\t\t\t\t\t{ count: exceptions, total: padas.length },\n\t\t\t\t\t\t)}</p>`\n\t\t\t\t\t: nothing\n\t\t\t}</div>`;\n\t}\n\n\tprivate renderRow(p: Pada) {\n\t\tconst glyph = (sign?: string) => signGlyph(sign) ?? '';\n\t\tconst cell = (sign?: string) => {\n\t\t\tconst g = glyph(sign);\n\t\t\treturn html`${g ? html`<span class=\"glyph\">${g}</span>` : nothing}${sign ?? ''}`;\n\t\t};\n\t\treturn html`<tr class=\"${LEAD_IDS.has(p.id) ? 'lead-row' : ''}\"><td class=\"pada\">${p.name ?? ''}${p.abbreviation ? html`<span class=\"abbr\">${p.abbreviation}</span>` : nothing}</td><td class=\"num\">${p.house ?? ''}</td><td>${cell(p.bhavaRashi)}</td><td>${p.lord ?? ''}</td><td>${cell(p.lordRashi)}</td><td>${cell(p.rashi)} ${p.exceptionApplied ? html`<span class=\"exception\">${this.t('Moved')}</span>` : nothing}</td><td class=\"num\">${p.houseFromLagna ?? ''}</td><td class=\"meaning\"><strong>${p.meaning ?? ''}</strong> ${p.significations ? html`<p>${p.significations}</p>` : nothing}</td></tr>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-arudha-padas': RoxyArudhaPadas;\n\t}\n}\n", "/**\n * Symbol constants used across components. Single source of truth so chart\n * wheels, card headers, hexagram displays, and panchang tables stay visually\n * consistent.\n *\n * @remarks\n * **Every table here is keyed by {@link lookupKey} and read ONLY through the accessor beside it.** The raw records are deliberately not exported: a component that indexes a record directly has to guess the key convention, and a guessed spelling renders `No North Node R`, `So South Node R` and `Bl Black Moon Lilith` on a live chart. The API returns `North Node` with a space, while {@link capitalize} produces `North node`, so a call site that indexes without normalising misses the table.\n *\n * **A miss returns `undefined`, never a manufactured abbreviation.** A `?? name.slice(0, 2)` fallback renders a failed lookup as a plausible two-letter code that reads exactly like an intentional abbreviation, so nothing about the output says it is broken. A caller that can omit the glyph should omit it (`?? ''`); a caller that must show something falls back to the FULL name, which is visibly wrong at the size these slots are drawn.\n *\n * `tests/tokens.test.ts` resolves every body, sign and aspect name the committed spec can emit against these tables, so a vocabulary the API gains cannot ship unglyphed.\n */\n\nimport { lookupKey } from '../utils/string.js';\n\n/** Look one name up in a normalized table. The single read path for every map in this file. */\nconst resolve = (\n\ttable: Record<string, string>,\n\tname: string | undefined | null,\n): string | undefined => (name ? table[lookupKey(name)] : undefined);\n\nconst PLANET_GLYPH: Record<string, string> = {\n\tsun: '☉',\n\tmoon: '☽',\n\tmercury: '☿',\n\tvenus: '♀',\n\tearth: '♁',\n\tmars: '♂',\n\tjupiter: '♃',\n\tsaturn: '♄',\n\turanus: '♅',\n\tneptune: '♆',\n\tpluto: '♇',\n\trahu: '☊',\n\tketu: '☋',\n\tascendant: 'Asc',\n\tlagna: 'La',\n\t'north node': '☊',\n\t'south node': '☋',\n\tchiron: '⚷',\n\tlilith: '⚸',\n\t'black moon lilith': '⚸',\n};\n\n/** Astrological glyph for a planet or point, or `undefined` when the library has none for it. */\nexport const planetGlyph = (\n\tname: string | undefined | null,\n): string | undefined => resolve(PLANET_GLYPH, name);\n\nconst PLANET_ABBR: Record<string, string> = {\n\tsun: 'Su',\n\tmoon: 'Mo',\n\tmercury: 'Me',\n\tvenus: 'Ve',\n\tmars: 'Ma',\n\tjupiter: 'Ju',\n\tsaturn: 'Sa',\n\turanus: 'Ur',\n\tneptune: 'Ne',\n\tpluto: 'Pl',\n\trahu: 'Ra',\n\tketu: 'Ke',\n\tascendant: 'Asc',\n\tlagna: 'La',\n};\n\n/** Two-letter graha code for the tight cells of a North/South Indian kundli, or `undefined`. */\nexport const planetAbbr = (\n\tname: string | undefined | null,\n): string | undefined => resolve(PLANET_ABBR, name);\n\nconst SIGN_GLYPH: Record<string, string> = {\n\taries: '♈',\n\ttaurus: '♉',\n\tgemini: '♊',\n\tcancer: '♋',\n\tleo: '♌',\n\tvirgo: '♍',\n\tlibra: '♎',\n\tscorpio: '♏',\n\tsagittarius: '♐',\n\tcapricorn: '♑',\n\taquarius: '♒',\n\tpisces: '♓',\n};\n\n/** Zodiac glyph for a sign name in any case, or `undefined`. */\nexport const signGlyph = (\n\tname: string | undefined | null,\n): string | undefined => resolve(SIGN_GLYPH, name);\n\nconst SIGN_ABBR: Record<string, string> = {\n\taries: 'Ar',\n\ttaurus: 'Ta',\n\tgemini: 'Ge',\n\tcancer: 'Cn',\n\tleo: 'Le',\n\tvirgo: 'Vi',\n\tlibra: 'Li',\n\tscorpio: 'Sc',\n\tsagittarius: 'Sg',\n\tcapricorn: 'Cp',\n\taquarius: 'Aq',\n\tpisces: 'Pi',\n};\n\n/** Two-letter rashi code for kundli cells and grid headers, or `undefined`. */\nexport const signAbbr = (name: string | undefined | null): string | undefined =>\n\tresolve(SIGN_ABBR, name);\n\n/** The twelve signs in zodiacal order. Display values, not lookup keys: read them through {@link signGlyph} / {@link signAbbr}. */\nexport const SIGNS_ORDER = [\n\t'Aries',\n\t'Taurus',\n\t'Gemini',\n\t'Cancer',\n\t'Leo',\n\t'Virgo',\n\t'Libra',\n\t'Scorpio',\n\t'Sagittarius',\n\t'Capricorn',\n\t'Aquarius',\n\t'Pisces',\n] as const;\n\n/**\n * Glyph per aspect. Used by the natal, transit and synastry aspect tables.\n *\n * @remarks\n * These are the nine aspects Unicode itself defines as a set, in its Miscellaneous Symbols block note: `260C, 26BA, 2220, 26B9, 25A1, 25B3, 26BC, 26BB, 260D` for 0, 30, 45, 60, 90, 120, 135, 150 and 180 degrees. That is exactly the vocabulary the API returns, so every aspect resolves to a real glyph and none falls through to a truncated slug.\n *\n * Do not substitute the visually similar maths operators. Unicode lists `⊻` XOR and `⊼` NAND as cross-references FROM the real quincunx and semisextile glyphs, so they look like safe stand-ins and are easy to pair the wrong way round, which renders a 150 degree quincunx with the 30 degree semisextile symbol. An astrologer reads the glyph, not the tooltip.\n */\nconst ASPECT_SYMBOL: Record<string, string> = {\n\tconjunction: '☌',\n\tsemisextile: '⚺',\n\t'semi sextile': '⚺',\n\tsemisquare: '∠',\n\t'semi square': '∠',\n\tsextile: '⚹',\n\tsquare: '□',\n\ttrine: '△',\n\tsesquiquadrate: '⚼',\n\tquincunx: '⚻',\n\topposition: '☍',\n};\n\n/** Aspect glyph for any of the separator forms the endpoints use (`SEMI_SEXTILE`, `SEMI SEXTILE`, `semi-sextile`), or `undefined`. */\nexport const aspectSymbol = (\n\tname: string | undefined | null,\n): string | undefined => resolve(ASPECT_SYMBOL, name);\n\n/** Trigrams used by I Ching hexagrams. Eight trigrams compose 64 hexagrams. */\nconst TRIGRAM_GLYPH: Record<string, string> = {\n\theaven: '☰',\n\tlake: '☱',\n\tfire: '☲',\n\tthunder: '☳',\n\twind: '☴',\n\twater: '☵',\n\tmountain: '☶',\n\tearth: '☷',\n};\n\n/** Trigram glyph for a trigram name in any case, or `undefined`. */\nexport const trigramGlyph = (\n\tname: string | undefined | null,\n): string | undefined => resolve(TRIGRAM_GLYPH, name);\n\n/** Moon phase emoji set. Used by the moon phase card. */\nconst MOON_PHASE_EMOJI: Record<string, string> = {\n\t'new moon': '🌑',\n\t'waxing crescent': '🌒',\n\t'first quarter': '🌓',\n\t'waxing gibbous': '🌔',\n\t'full moon': '🌕',\n\t'waning gibbous': '🌖',\n\t'last quarter': '🌗',\n\t'waning crescent': '🌘',\n};\n\n/**\n * Emoji for a lunar phase, or `undefined`.\n *\n * @remarks\n * The live API suffixes most phase names (`Waxing Gibbous Moon`) and calls the third quarter `Third Quarter Moon`, while only new and full moon carry the suffix in the table. Both reconciliations live here rather than in the card, so any component reading a phase name resolves the same eight phases from the same four spellings.\n */\nexport const moonPhaseEmoji = (\n\tname: string | undefined | null,\n): string | undefined => {\n\tif (!name) return undefined;\n\tconst key = lookupKey(name);\n\tconst bare = key.replace(/ moon$/, '');\n\tconst alias = bare === 'third quarter' ? 'last quarter' : bare;\n\treturn MOON_PHASE_EMOJI[key] ?? MOON_PHASE_EMOJI[alias];\n};\n", "import type { TemplateResult } from 'lit';\nimport { css, html, nothing } from 'lit';\nimport { formatAyanamsa, formatNumber } from './format.js';\nimport type { Translate } from './hd-reading.js';\n\n/**\n * The sidereal frame a Vedic response was computed in.\n *\n * @remarks\n * Structural rather than imported from one response type, because fifteen different schemas carry the identical object and a component should not have to name whichever one it happens to render. Both fields are required by the schema; they are optional here only so a component can pass `d.frame` without proving the payload is complete first.\n */\nexport interface SiderealFrame {\n\tayanamsa?: string;\n\tayanamsaDegrees?: number;\n}\n\n/**\n * Shared styling for the frame caption. Include in a component's `static styles` alongside {@link renderFrameCaption}.\n */\nexport const frameCaptionStyles = css`.roxy-frame{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);line-height:var(--roxy-leading-normal,1.5);margin:0}.roxy-frame .roxy-frame-deg{font-variant-numeric:tabular-nums}`;\n\n/**\n * One line naming the sidereal frame a chart or strength table was cast in.\n *\n * @remarks\n * **This is provenance, not decoration, and leaving it out is a correctness problem rather than a cosmetic one.** Changing the ayanamsa moves every longitude by up to a degree, which is enough to put a graha in a different rashi. A chart rendered without saying which frame produced it therefore cannot be reconciled against any other calculator, and a reader who changed the frame by accident concludes the component is broken rather than that they are comparing two different skies. Echoing it is the whole reason the response carries it.\n *\n * Renders nothing when the payload has no frame, so it is safe to call unconditionally on a response type that predates the field.\n *\n * The label comes from {@link formatAyanamsa}, so `raman` reads \"B.V. Raman\" and `kp-newcomb` keeps its initialism, and a frame added upstream cannot arrive as a bare enum without a test failing.\n *\n * @example\n * ```ts\n * static styles = [baseStyles, frameCaptionStyles, css`...`];\n * // in renderData:\n * ${renderFrameCaption(this.effectiveLang(), d.frame, this.translator)}\n * ```\n */\nexport function renderFrameCaption(\n\tlocale: string | undefined,\n\tframe: SiderealFrame | undefined,\n\tt: Translate,\n): TemplateResult | typeof nothing {\n\tif (!frame?.ayanamsa) return nothing;\n\tconst label = formatAyanamsa(locale, frame.ayanamsa);\n\tif (!label) return nothing;\n\t// Two whole sentences rather than a stem plus a fragment, so each language\n\t// orders the frame and the offset the way its own grammar wants.\n\tconst degrees =\n\t\ttypeof frame.ayanamsaDegrees === 'number'\n\t\t\t? formatNumber(locale, frame.ayanamsaDegrees, 4)\n\t\t\t: undefined;\n\treturn html`<p class=\"roxy-frame\">${\n\t\t\tdegrees === undefined\n\t\t\t\t? t('Sidereal frame: {{frame}}', { frame: label })\n\t\t\t\t: t('Sidereal frame: {{frame}}, {{degrees}}° subtracted', {\n\t\t\t\t\t\tframe: label,\n\t\t\t\t\t\tdegrees,\n\t\t\t\t\t})\n\t\t}</p>`;\n}\n\n/** One frame in a response computed across several: the frame itself, the instant it was read at, and the sections it decided. */\nexport interface GoverningFrame extends SiderealFrame {\n\tat?: string;\n\tgoverns?: readonly string[];\n}\n\n/**\n * Every frame a multi-frame response was computed in, one line each.\n *\n * @remarks\n * Separate from {@link renderFrameCaption} rather than a widening of it, because the two answer different questions: one chart cast in one frame needs the frame NAMED, while a response assembled from several needs each frame tied to the part of the answer it decided. Collapsing these to a single caption would print one ayanamsa over sections two others produced, which is the provenance failure the caption exists to prevent.\n *\n * The label comes from {@link formatAyanamsa}, the same source the single-frame caption reads, so a frame added upstream reads the same in both.\n */\nexport function renderFrameProvenance(\n\tlocale: string | undefined,\n\tframes: Record<string, GoverningFrame | undefined> | undefined,\n\tt: Translate,\n): TemplateResult | typeof nothing {\n\tconst rows = Object.values(frames ?? {}).filter((f): f is GoverningFrame =>\n\t\tBoolean(f?.ayanamsa),\n\t);\n\tif (rows.length === 0) return nothing;\n\treturn html`<div class=\"roxy-frame\">${rows.map((f) => {\n\t\t\tconst label = formatAyanamsa(locale, f.ayanamsa);\n\t\t\tconst degrees =\n\t\t\t\ttypeof f.ayanamsaDegrees === 'number'\n\t\t\t\t\t? formatNumber(locale, f.ayanamsaDegrees, 4)\n\t\t\t\t\t: undefined;\n\t\t\tconst governs = f.governs?.length ? f.governs.join(', ') : '';\n\t\t\treturn html`<p class=\"roxy-frame\">${\n\t\t\t\t\tdegrees === undefined\n\t\t\t\t\t\t? t('Sidereal frame: {{frame}}', { frame: label })\n\t\t\t\t\t\t: t('Sidereal frame: {{frame}}, {{degrees}}° subtracted', {\n\t\t\t\t\t\t\t\tframe: label,\n\t\t\t\t\t\t\t\tdegrees,\n\t\t\t\t\t\t\t})\n\t\t\t\t}${governs ? t(', governs {{sections}}', { sections: governs }) : ''}</p>`;\n\t\t})}</div>`;\n}\n", "/**\n * Display formatters for ISO timestamps and floats coming back from the API.\n * Every helper returns \"\" for nullish or unparseable input so it falls out of\n * template literals cleanly.\n *\n * @remarks\n * **This file is the ONLY place in the library that may call `Intl` or a `toLocale*` method, and every helper that does takes the locale as its first argument, required.** Both halves are enforced by a source scan in `tests/i18n.test.ts`.\n *\n * The locale is the DISPLAY locale, the full tag `resolveLang` returns, region included. It is NOT the value that goes on `?lang=`: that one is truncated to a supported two-letter code because the API rejects anything else, and handing THAT to Intl would render every Argentine visitor's dates in Castilian conventions. `es-AR` is exactly the tag Intl wants.\n *\n * It is required rather than optional because the failure it prevents is an omission: `toLocaleDateString(undefined, ...)` means the locale of whoever is looking, so a Spanish page renders `Carta natal` above `Jan 15, 1990, 2:30 PM` and two visitors to one page see two different strings. An optional parameter defaults straight back to that. A missing argument is now a typecheck failure at every call site.\n */\n\nimport { capitalize, humanize } from './string.js';\n\nconst DATE_ONLY = /^\\d{4}-\\d{2}-\\d{2}$/;\nconst BARE_TIME = /^\\d{2}:\\d{2}(:\\d{2})?$/;\n/** An ISO datetime with NO timezone designator: a wall clock, not an instant. */\nconst NAIVE_DATETIME = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?)?$/;\n\n/** Canonical form per tag seen, so a page carrying an unparseable `lang` does not throw once per rendered date. */\nconst CANONICAL: Map<string, string> = new Map();\n\n/**\n * The locale list to hand Intl: the page's tag first, English behind it.\n *\n * @remarks\n * Two failure modes, both reachable from a `lang` attribute a site owner typed by hand and neither of which may reach a visitor:\n *\n * - **A structurally invalid tag throws.** `lang=\"english\"` or `lang=\"en_US\"` makes every `toLocale*` call raise a RangeError, which inside a render is a blank component rather than a wrong date. Canonicalizing behind a try/catch degrades it to English instead.\n * - **A valid-but-unknown tag falls back to the RUNTIME default**, which is the viewer's browser again, so the bug reappears for exactly the pages least likely to notice. Naming `en` as the second entry makes the fallback the page's, not the visitor's.\n *\n * No language signal at all resolves to English for the same reason the chrome catalogue does: a page that never declares a language is English as far as this library is concerned, and English chrome over German dates is the inconsistency this whole file exists to remove. `navigator.language` is deliberately absent from the chain here as it is in `resolveLang`: it is the VISITOR's preference, not the site's.\n */\nfunction intlLocales(locale: string | undefined): string[] {\n\tif (!locale) return ['en'];\n\tconst cached = CANONICAL.get(locale);\n\tif (cached) return [cached, 'en'];\n\tlet canonical = 'en';\n\ttry {\n\t\tcanonical = Intl.getCanonicalLocales(locale)[0] ?? 'en';\n\t} catch {\n\t\tcanonical = 'en';\n\t}\n\tCANONICAL.set(locale, canonical);\n\treturn [canonical, 'en'];\n}\n\n/**\n * Resolve an API timestamp to a Date plus the timezone it must be RENDERED in.\n *\n * @remarks\n * The API returns chart times naive (`2026-07-13T04:36:00`): that is 04:36 in the CHART's timezone, not UTC and not the viewer's. A wall clock must read the same for every viewer on earth, so hora at sunrise says 04:36 in Tokyo and in Chicago alike.\n *\n * Handing a naive string to `new Date()` makes the runtime interpret it in the VIEWER's zone, which is a silent corruption whenever that wall clock lands in the viewer's DST gap. In America/New_York `2026-03-08T02:30:00` does not exist, so the clock jumps and it renders as `3:30 AM`. Pinning the wall clock to UTC, which has no DST and therefore no gap, preserves the digits exactly while Intl still writes them in the PAGE locale conventions.\n *\n * An offset-bearing timestamp (`...Z`, `...+05:30`) IS a real instant, so it keeps the normal behaviour and converts to the viewer's local time.\n *\n * A DATE_ONLY value (`2026-08-01`) is the same wall clock with the time left off, and it is normalised HERE rather than at each call site because leaving it to the caller is a defect that formats correctly for whoever writes the code: the platform parses a bare date as UTC midnight, and Intl then renders that instant in the VIEWER's zone, so every viewer west of Greenwich reads the previous day. Three helpers pinned it inline and {@link formatDateGrain} did not, which nothing caught because its only caller passes full datetimes.\n *\n * Deliberately NOT exported. Exporting it lets a component needing a shape these helpers do not offer pin its own wall clock and call Intl itself, which is how a boundary date ends up hardcoded to one locale. A missing shape is added here, beside the locale handling, not reimplemented in a component.\n */\nfunction resolveDisplayDate(input: string): {\n\td: Date;\n\ttimeZone?: string;\n} {\n\tconst naive = BARE_TIME.test(input)\n\t\t? `1970-01-01T${input}`\n\t\t: DATE_ONLY.test(input)\n\t\t\t? `${input}T00:00:00`\n\t\t\t: NAIVE_DATETIME.test(input)\n\t\t\t\t? input\n\t\t\t\t: null;\n\treturn naive\n\t\t? { d: new Date(`${naive}Z`), timeZone: 'UTC' }\n\t\t: { d: new Date(input) };\n}\n\n/**\n * The clock, in the page locale: `2:30 PM` in English, `14:30` in German.\n *\n * @remarks\n * The hour cycle is the locale's, never ours. Pinning `hour12: true` puts an AM/PM clock on every page in Europe and Latin America while the embedded form beside it labels its own input \"formato de 24 horas\". CLDR already knows which convention each locale reads, including the ones that surprise you (`es` is 24-hour, `es-AR` is 12-hour with `p. m.`), so asserting a cycle here is asserting we know better than the locale data.\n */\nexport function formatTime(locale: string | undefined, input: unknown): string {\n\tif (typeof input !== 'string' || input.length === 0) return '';\n\tif (DATE_ONLY.test(input)) return '';\n\tconst { d, timeZone } = resolveDisplayDate(input);\n\tif (Number.isNaN(d.getTime())) return input;\n\treturn d.toLocaleTimeString(intlLocales(locale), {\n\t\thour: 'numeric',\n\t\tminute: '2-digit',\n\t\ttimeZone,\n\t});\n}\n\nexport function formatDate(locale: string | undefined, input: unknown): string {\n\tif (typeof input !== 'string' || input.length === 0) return '';\n\tconst { d, timeZone } = resolveDisplayDate(input);\n\tif (Number.isNaN(d.getTime())) return input;\n\treturn d.toLocaleDateString(intlLocales(locale), {\n\t\tmonth: 'short',\n\t\tday: 'numeric',\n\t\tyear: 'numeric',\n\t\ttimeZone,\n\t});\n}\n\nexport function formatTimeRange(\n\tlocale: string | undefined,\n\tt: { start?: string; end?: string } | undefined,\n): string {\n\tif (!t) return '';\n\tconst start = formatTime(locale, t.start);\n\tconst end = formatTime(locale, t.end);\n\tif (start && end) return `${start} - ${end}`;\n\treturn start || end || '';\n}\n\n/** Two dates as one label: `Jan 1, 2026 - Jan 7, 2026`. Falls back to whichever end parses when the other is absent. */\nexport function formatDateRange(\n\tlocale: string | undefined,\n\tstart: unknown,\n\tend: unknown,\n): string {\n\tconst a = formatDate(locale, start);\n\tconst b = formatDate(locale, end);\n\tif (a && b) return `${a} - ${b}`;\n\treturn a || b;\n}\n\n/** How much of a timestamp a label shows. `time` is the whole date plus the clock. */\nexport type DateGrain = 'year' | 'month' | 'day' | 'time';\n\n/**\n * A timestamp at a chosen granularity, for an axis or a bar end where the full date would not fit.\n *\n * @remarks\n * Every grain below `year` carries the year, because a period routinely straddles new year and a label reading `28 Dec - 3 Jan` says nothing about which side moved. `year` is sliced off the string rather than formatted, since a bare four-digit year is the same in every locale this library ships and running it through Intl would introduce era and numbering-system differences into an axis tick.\n */\nexport function formatDateGrain(\n\tlocale: string | undefined,\n\tinput: unknown,\n\tgrain: DateGrain,\n): string {\n\tif (typeof input !== 'string' || input.length === 0) return '';\n\tif (grain === 'year') {\n\t\tconst m = input.match(/^(\\d{4})/);\n\t\treturn m?.[1] ?? input;\n\t}\n\tconst { d, timeZone } = resolveDisplayDate(input);\n\tif (Number.isNaN(d.getTime())) return input;\n\treturn d.toLocaleString(intlLocales(locale), {\n\t\tday: grain === 'month' ? undefined : 'numeric',\n\t\tmonth: 'short',\n\t\tyear: 'numeric',\n\t\thour: grain === 'time' ? 'numeric' : undefined,\n\t\tminute: grain === 'time' ? '2-digit' : undefined,\n\t\ttimeZone,\n\t});\n}\n\n/**\n * A day inside a month already named elsewhere: `Aug 3`, `3 ago`, `3. Aug.`.\n *\n * @remarks\n * Deliberately WITHOUT the year, which is the one thing {@link formatDateGrain} will not do at any grain below `year`, and for a good reason there: a period can straddle new year and `28 Dec - 3 Jan` says nothing about which side moved. This shape exists for the opposite case, a payload whose own fields pin the month (a monthly ephemeris carries `year` and `month` at the top level and prints them in its header), where repeating the year on thirty-one rows and a dozen chips is noise rather than safety. Do not reach for it where the month is not already on screen.\n *\n * A date-only string is pinned to UTC like every other naive value, so the digits read the same for every viewer instead of rolling back a day west of Greenwich.\n */\nexport function formatMonthDay(\n\tlocale: string | undefined,\n\tinput: unknown,\n): string {\n\tif (typeof input !== 'string' || input.length === 0) return '';\n\tconst { d, timeZone } = resolveDisplayDate(input);\n\tif (Number.isNaN(d.getTime())) return input;\n\treturn d.toLocaleDateString(intlLocales(locale), {\n\t\tmonth: 'short',\n\t\tday: 'numeric',\n\t\ttimeZone,\n\t});\n}\n\n/**\n * A day of the month with its weekday: `1 Sat`, `sáb 1`, `Sa., 1.`.\n *\n * @remarks\n * The row label a printed ephemeris uses (`01 Sa`, `02 Su`), and it carries neither the month nor the year because the page header already names both. The weekday is not decoration: half of what a practitioner asks a monthly ephemeris is which DAY of the week a placement falls on, and deriving it from a bare number is exactly the arithmetic a table exists to save. Order and separator belong to the locale, which is why this is one Intl call rather than a join.\n *\n * A date-only string is pinned to UTC like every other naive value, so the weekday cannot roll backwards west of Greenwich.\n */\nexport function formatWeekdayDay(\n\tlocale: string | undefined,\n\tinput: unknown,\n): string {\n\tif (typeof input !== 'string' || input.length === 0) return '';\n\tconst { d, timeZone } = resolveDisplayDate(input);\n\tif (Number.isNaN(d.getTime())) return input;\n\treturn d.toLocaleDateString(intlLocales(locale), {\n\t\tweekday: 'short',\n\t\tday: 'numeric',\n\t\ttimeZone,\n\t});\n}\n\n/**\n * A calendar month and its year: `August 2026`, `agosto de 2026`, `August 2026`.\n *\n * @remarks\n * Takes the two NUMBERS an endpoint returns rather than a timestamp, because that is the shape a monthly payload echoes, and anchors them at UTC noon for the same reason {@link monthName} does: no timezone can roll the anchor into the neighbouring month. The connective belongs to the locale (`de` in Spanish, nothing in English), which is why this is one Intl call rather than a join of {@link monthName} and the year.\n */\nexport function formatMonthYear(\n\tlocale: string | undefined,\n\tyear: unknown,\n\tmonth: unknown,\n): string {\n\tif (typeof year !== 'number' || !Number.isFinite(year)) return '';\n\tif (typeof month !== 'number' || !Number.isInteger(month)) return '';\n\tif (month < 1 || month > 12) return '';\n\treturn new Date(Date.UTC(year, month - 1, 1, 12)).toLocaleDateString(\n\t\tintlLocales(locale),\n\t\t{ month: 'long', year: 'numeric', timeZone: 'UTC' },\n\t);\n}\n\n/**\n * Month name from a 1-based month number: `January`, `enero`, `janvier`.\n *\n * @remarks\n * Two components carried a byte-identical array of twelve English month names, which is both a duplicated table and a table of something the platform already knows in every language. Anchored to a UTC noon so no timezone can roll the date into the neighbouring month.\n */\nexport function monthName(locale: string | undefined, month: unknown): string {\n\tif (typeof month !== 'number' || !Number.isInteger(month)) return '';\n\tif (month < 1 || month > 12) return '';\n\treturn new Date(Date.UTC(2000, month - 1, 1, 12)).toLocaleDateString(\n\t\tintlLocales(locale),\n\t\t{ month: 'long', timeZone: 'UTC' },\n\t);\n}\n\n/**\n * A whole number with the page locale's digit grouping: `12,345` in English, `12.345` in Spanish.\n *\n * @remarks\n * Rounds rather than truncating, so a caller testing the magnitude (`Math.round(km) === 0`) and this label agree about which side of a boundary a value fell.\n */\nexport function formatInteger(\n\tlocale: string | undefined,\n\tvalue: unknown,\n): string {\n\tif (typeof value !== 'number' || !Number.isFinite(value)) return '';\n\treturn value.toLocaleString(intlLocales(locale), {\n\t\tmaximumFractionDigits: 0,\n\t});\n}\n\n/**\n * A number to at most `dp` decimals in the reader's notation: `2.5` in English, `2,5` in German.\n *\n * @remarks Trailing zeros drop on the fractional side only, so `100` at `dp = 0` stays `100`.\n *\n * @example\n * ```ts\n * formatNumber('en', 2.5, 1); // '2.5'\n * formatNumber('de', 2.5, 1); // '2,5'\n * ```\n */\nexport function formatNumber(\n\tlocale: string | undefined,\n\tvalue: unknown,\n\tdp = 1,\n): string {\n\tif (typeof value !== 'number' || !Number.isFinite(value)) return '';\n\treturn value.toLocaleString(intlLocales(locale), {\n\t\tmaximumFractionDigits: dp,\n\t});\n}\n\n/**\n * A 0-to-100 value as a percentage in the reader's notation, which places the sign: `82.5%` in English, `%82,5` in Turkish.\n *\n * @remarks `style: 'percent'` takes a fraction, so the value is divided first.\n */\nexport function formatPercent(\n\tlocale: string | undefined,\n\tvalue: unknown,\n\tdp = 1,\n): string {\n\tif (typeof value !== 'number' || !Number.isFinite(value)) return '';\n\treturn (value / 100).toLocaleString(intlLocales(locale), {\n\t\tstyle: 'percent',\n\t\tmaximumFractionDigits: dp,\n\t});\n}\n\n/**\n * CSS class name per aspect type. Used by natal and synastry chart aspect\n * lines so the same color encoding (harmonious vs challenging) applies in\n * both wheels. Keys are lowercase canonical names, values are CSS class\n * suffixes the chart components define in their `:host` styles.\n */\nexport const ASPECT_CLASS: Record<string, string> = {\n\tconjunction: 'aspect-conjunction',\n\tsextile: 'aspect-sextile',\n\tsquare: 'aspect-square',\n\ttrine: 'aspect-trine',\n\topposition: 'aspect-opposition',\n};\n\n/**\n * Normalize the `type` field on an aspect entry to a lowercase, hyphen-separated canonical name (`SEMI_SEXTILE` and `SEMI SEXTILE` both become `semi-sextile`). Accepts any aspect-shaped object so both natal and synastry inter-aspect entries can share this.\n *\n * @remarks\n * Spaces are folded as well as underscores because the endpoints do not agree on the separator: aspects returns `SEMI SEXTILE` where synastry returns `SEMI_SEXTILE`. Handling only the underscore left the space form failing every {@link ASPECT_CLASS} and `ASPECT_SYMBOL` lookup, so the same aspect rendered hyphenated and correctly coloured in one component and space-separated in the neutral fallback colour in another.\n */\nexport function normalizeAspect(a: { type?: string }): string {\n\treturn (a.type ?? '')\n\t\t.toLowerCase()\n\t\t.trim()\n\t\t.replace(/[\\s_]+/g, '-');\n}\n\n/**\n * Display label for an aspect: `SEMI_SEXTILE` -> `Semi-sextile`.\n *\n * The raw value is an API enum and the table column it lands in is read by practitioners, not parsers. {@link normalizeAspect} stays the lookup key for classes and glyphs; this is the human-facing form.\n */\nexport function formatAspectName(a: { type?: string }): string {\n\treturn capitalize(normalizeAspect(a));\n}\n\n/**\n * Display label for an ayanamsa identifier. `kp-newcomb` -> `KP Newcomb`.\n *\n * The raw values are API enums, and every one of them needs a human form that {@link humanize} cannot derive: KP is an initialism that must stay uppercase, and `raman` is a person, B.V. Raman, not a word. Anything unmapped degrades to a humanized slug rather than rendering the enum. That fallback is a safety net, not a licence to skip the map, since a humanized slug reads as a bare surname where a full name belongs. A test fails if the committed spec gains a frame this map lacks.\n */\nexport const AYANAMSA_LABEL: Record<string, string> = {\n\t'kp-newcomb': 'KP Newcomb',\n\t'kp-old': 'KP Old',\n\tlahiri: 'Lahiri',\n\traman: 'B.V. Raman',\n\n\tcustom: 'Custom',\n};\n\nexport function formatAyanamsa(\n\tlocale: string | undefined,\n\ttype: unknown,\n\tdegrees?: unknown,\n): string {\n\tconst label =\n\t\ttypeof type === 'string' ? (AYANAMSA_LABEL[type] ?? humanize(type)) : '';\n\tconst deg =\n\t\ttypeof degrees === 'number'\n\t\t\t? `${formatNumber(locale, degrees, 2)}\\u00b0`\n\t\t\t: '';\n\tif (label && deg) return `${label} (${deg})`;\n\treturn label || deg;\n}\n\n/**\n * Date plus time in one label: `Jan 15, 1990, 2:30 PM`.\n *\n * @remarks\n * Components were concatenating a raw date and a raw time (`1990-01-15 \\u00b7 14:30:00`) while siblings rendered the same instant through {@link formatDate}. One helper keeps every card reading the same way.\n *\n * Birth details arrive as two fields rather than one timestamp, so `time` may be passed separately; it is merged into a naive datetime, which {@link formatDate} and {@link formatTime} pin to UTC so the wall clock renders identically for every viewer.\n *\n * @example\n * ```ts\n * formatDateTime('en', '1990-01-15T14:30:00');    // 'Jan 15, 1990, 2:30 PM'\n * formatDateTime('en', '1990-01-15', '14:30:00'); // 'Jan 15, 1990, 2:30 PM'\n * formatDateTime('de', '1990-01-15T14:30:00');    // '15. Jan. 1990, 14:30'\n * formatDateTime('en', '1990-01-15');             // 'Jan 15, 1990'\n * ```\n */\nexport function formatDateTime(\n\tlocale: string | undefined,\n\tinput: unknown,\n\ttime?: unknown,\n): string {\n\tconst merged =\n\t\ttypeof input === 'string' &&\n\t\tDATE_ONLY.test(input) &&\n\t\ttypeof time === 'string' &&\n\t\tBARE_TIME.test(time)\n\t\t\t? `${input}T${time}`\n\t\t\t: input;\n\tconst date = formatDate(locale, merged);\n\tconst clock = formatTime(locale, merged);\n\tif (date && clock) return `${date}, ${clock}`;\n\treturn date || clock;\n}\n\n/**\n * The Sanskrit form of a name, but only when it actually differs from the English one.\n *\n * @remarks\n * Several endpoints pair an English `name` with a `sanskritName` that is sometimes the SAME string, because the tradition and the translation coincide (a nakshatra is `Ashwini` either way). Rendering the pair unconditionally then prints `Ashwini (Ashwini)`. That guard is the whole reason this exists: it is one comparison, it is easy to leave out, and leaving it out is invisible until a reader hits a name where the two agree.\n *\n * Presentation deliberately stays with the caller, since the same fact is drawn as a parenthetical in a table cell and as a middot-separated span on a chart heading.\n */\nexport function distinctSanskrit(\n\tname: unknown,\n\tsanskritName: unknown,\n): string | undefined {\n\tif (typeof sanskritName !== 'string' || !sanskritName) return undefined;\n\treturn sanskritName === name ? undefined : sanskritName;\n}\n\n/**\n * `English (Sanskrit)` for a value that carries both, falling back to whichever one exists.\n *\n * @example\n * ```ts\n * formatWithSanskrit('Tuesday', 'Mangalavara'); // 'Tuesday (Mangalavara)'\n * formatWithSanskrit('Ashwini', 'Ashwini');     // 'Ashwini'\n * ```\n */\nexport function formatWithSanskrit(\n\tname: unknown,\n\tsanskritName: unknown,\n): string {\n\tconst english = typeof name === 'string' ? name : '';\n\tconst sanskrit = distinctSanskrit(english, sanskritName);\n\tif (!english) return sanskrit ?? '';\n\treturn sanskrit ? `${english} (${sanskrit})` : english;\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement, state } from 'lit/decorators.js';\nimport type { ChromeString } from '../i18n/chrome-strings.js';\nimport { signGlyph } from '../tokens/index.js';\nimport type { AshtakavargaResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { frameCaptionStyles, renderFrameCaption } from '../utils/frame.js';\nimport { renderTablist, tablistStyles } from '../utils/tablist.js';\n\ntype Tab = 'sarva' | 'bhinna' | 'reduced' | 'pinda';\n\n/** Tab order, each id beside the English SOURCE its label is looked up by. */\nconst TABS: ReadonlyArray<{ id: Tab; source: ChromeString }> = [\n\t{ id: 'sarva', source: 'Sarvashtakavarga' },\n\t{ id: 'bhinna', source: 'Bhinnashtakavarga' },\n\t{ id: 'reduced', source: 'Reduced' },\n\t{ id: 'pinda', source: 'Shodhya Pinda' },\n];\n\ntype Signs = AshtakavargaResponse['signs'];\ntype BhinnaRow = AshtakavargaResponse['bhinnashtakavarga'][number];\ntype SarvaRow = AshtakavargaResponse['sarvashtakavarga'];\n\n/**\n * Ashtakavarga grid: Sarvashtakavarga, Bhinnashtakavarga, the Shodhana-reduced\n * grid, and Shodhya Pinda. Pass `data` from /vedic-astrology/ashtakavarga.\n *\n * @remarks\n * Bindus are benefic points, so MORE is better in every view here. The colour\n * ramp is therefore diverging, not a single hot hue: a weak count tints toward\n * --roxy-heat (which resolves to the danger token), the classical average stays\n * neutral, a strong count tints toward --roxy-success, and each tab states its\n * own scale in a legend. The previous single-hue ramp painted the best signs the\n * most alarming shade, the exact opposite of what the numbers mean.\n */\n@customElement('roxy-ashtakavarga-grid')\nexport class RoxyAshtakavargaGrid extends RoxyDataElement<AshtakavargaResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tframeCaptionStyles,\n\t\ttablistStyles,\n\t\tcss`.wrap{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.head{justify-content:space-between;align-items:baseline;gap:var(--roxy-space-md,1rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.subtitle{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem);margin:0}.overflow-scroll{-webkit-overflow-scrolling:touch;min-width:0;overflow-x:auto}table{border-collapse:collapse;width:100%;font-size:var(--roxy-text-sm,.875rem)}th,td{padding:var(--roxy-space-sm,.5rem);border-bottom:1px solid var(--roxy-border,#e4e4e7);text-align:center}th{color:var(--roxy-muted,#71717a);font-weight:var(--roxy-weight-bold,600);text-transform:uppercase;font-size:var(--roxy-text-xs,.75rem);letter-spacing:.06em}td:first-child,th:first-child{text-align:left}.glyph{margin-right:3px;font-size:1.1em;line-height:1}.planet-cell{white-space:nowrap;align-items:center;gap:4px;display:flex}.total-row td{font-weight:var(--roxy-weight-bold,600);border-top:2px solid var(--roxy-border,#e4e4e7);border-bottom:none}.bindu-cell{border-radius:var(--roxy-radius-sm,4px);font-weight:var(--roxy-weight-bold,600);font-variant-numeric:tabular-nums;min-width:2rem;color:var(--roxy-fg,currentColor)}.tier-1{background:color-mix(in srgb, var(--roxy-heat,#ef4444) 30%, transparent)}.tier-2{background:color-mix(in srgb, var(--roxy-heat,#ef4444) 17%, transparent)}.tier-3{background:color-mix(in srgb, var(--roxy-heat,#ef4444) 7%, transparent)}.tier-4{background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 45%, transparent)}.tier-5{background:color-mix(in srgb, var(--roxy-success,#16a34a) 16%, transparent)}.tier-6{background:color-mix(in srgb, var(--roxy-success,#16a34a) 32%, transparent)}.tier-7{background:color-mix(in srgb, var(--roxy-success,#16a34a) 50%, transparent)}.legend{align-items:center;gap:var(--roxy-space-xs,.25rem) var(--roxy-space-sm,.5rem);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);flex-wrap:wrap;display:flex}.legend-scale{gap:2px;display:flex}.legend-swatch{border-radius:2px;width:16px;height:10px}.legend-note{flex:12rem}.bhinna-table th:first-child,.bhinna-table td:first-child{min-width:5rem}@container (width<=480px){.bhinna-table th,.bhinna-table td{font-size:var(--roxy-text-xs,.75rem);padding:.3rem .35rem}.bhinna-table th:first-child,.bhinna-table td:first-child{min-width:3.5rem}.bindu-cell{min-width:1.5rem}}.overflow-scroll{-webkit-mask-image:linear-gradient(90deg,#0000 0,#000 .5rem calc(100% - 1rem),#0000 100%);mask-image:linear-gradient(90deg,#0000 0,#000 .5rem calc(100% - 1rem),#0000 100%)}`,\n\t];\n\n\t@state()\n\tactiveTab: Tab = 'sarva';\n\n\tprotected renderData(d: AshtakavargaResponse) {\n\t\tconst signs = d.signs ?? [];\n\n\t\treturn html`<div class=\"wrap\" part=\"card\" aria-label=\"${this.t('Ashtakavarga grid')}\"><div class=\"head\" part=\"header\"><h2 class=\"title\">${this.t('Ashtakavarga')}</h2>${\n\t\t\t\t\tsigns.length\n\t\t\t\t\t\t? html`<p class=\"subtitle\">${this.t('{{count}} signs', { count: signs.length })}</p>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t} ${renderFrameCaption(this.effectiveLang(), d.frame, this.translator)}</div>${renderTablist({\n\t\t\t\titems: TABS.map(({ id, source }) => ({ id, label: this.t(source) })),\n\t\t\t\tactive: this.activeTab,\n\t\t\t\tonSelect: (id) => {\n\t\t\t\t\tthis.activeTab = id;\n\t\t\t\t},\n\t\t\t\tlabel: this.t('Ashtakavarga views'),\n\t\t\t\tidPrefix: 'ashtakavarga',\n\t\t\t\tcontrols: true,\n\t\t\t})}<div id=\"ashtakavarga-panel-${this.activeTab}\" part=\"panel\" role=\"tabpanel\" aria-labelledby=\"ashtakavarga-tab-${this.activeTab}\">${\n\t\t\t\t\tthis.activeTab === 'sarva'\n\t\t\t\t\t\t? this.renderSarva(signs)\n\t\t\t\t\t\t: this.activeTab === 'bhinna'\n\t\t\t\t\t\t\t? this.renderBhinna(signs)\n\t\t\t\t\t\t\t: this.activeTab === 'reduced'\n\t\t\t\t\t\t\t\t? this.renderReduced(signs)\n\t\t\t\t\t\t\t\t: this.renderPinda()\n\t\t\t\t}</div></div>`;\n\t}\n\n\t/**\n\t * Bhinna bindus per planet per sign run 0..8 (one 0/1 contribution from each\n\t * of the 8 reference points). 4 is the mid, so it holds the neutral tier and\n\t * anything above it reads as support. The reduced grid uses the same 0..8\n\t * units, so it shares this scale.\n\t */\n\tprivate bhinnaTier(count: number): string {\n\t\tif (count <= 1) return 'tier-1';\n\t\tif (count <= 2) return 'tier-2';\n\t\tif (count <= 3) return 'tier-3';\n\t\tif (count <= 4) return 'tier-4';\n\t\tif (count <= 5) return 'tier-5';\n\t\tif (count <= 6) return 'tier-6';\n\t\treturn 'tier-7';\n\t}\n\n\t/**\n\t * Sarva bindus per sign are the column total across the 7 planets. The grand\n\t * total is always 337, so the classical average is 337/12 = 28.08 and the\n\t * bands used by readers are: under 25 weak, 25 to 29 average, 30 and above\n\t * strong, with the highest signs carrying transits best. The tiers follow\n\t * those bands rather than an even split, so 28 sits on the neutral tier.\n\t */\n\tprivate sarvaTier(count: number): string {\n\t\tif (count <= 20) return 'tier-1';\n\t\tif (count <= 24) return 'tier-2';\n\t\tif (count <= 27) return 'tier-3';\n\t\tif (count <= 29) return 'tier-4';\n\t\tif (count <= 32) return 'tier-5';\n\t\tif (count <= 36) return 'tier-6';\n\t\treturn 'tier-7';\n\t}\n\n\t/**\n\t * The scale, stated. Without this a reader has to guess whether a saturated\n\t * cell is good or bad, and in a bindu grid the answer is always \"good\".\n\t */\n\tprivate renderLegend(note: string) {\n\t\treturn html`<div class=\"legend\" part=\"legend\"><span>${this.t('Fewer bindus')}</span> <span class=\"legend-scale\" aria-hidden=\"true\">${[1, 2, 3, 4, 5, 6, 7].map(\n\t\t\t\t\t(t) => html`<span class=\"legend-swatch tier-${t}\"></span>`,\n\t\t\t\t)} </span><span>${this.t('More bindus')}</span> <span class=\"legend-note\">${note}</span></div>`;\n\t}\n\n\tprivate renderSarva(signs: Signs) {\n\t\tconst sav = this.data?.sarvashtakavarga;\n\t\tif (!sav)\n\t\t\treturn html`<p class=\"roxy-empty\">${this.t('No sarvashtakavarga data')}</p>`;\n\n\t\treturn html`<div class=\"overflow-scroll\" part=\"table\"><table><caption class=\"roxy-sr-only\">${this.t(\n\t\t\t\t\t\t\t'Sarvashtakavarga: each of the twelve signs and the bindus all planets contribute to it, with a grand total.',\n\t\t\t\t\t\t)}</caption><thead><tr><th scope=\"col\">${this.t('Sign')}</th><th scope=\"col\">${this.t('Bindus')}</th></tr></thead><tbody>${signs.map((sign, i) => {\n\t\t\t\t\t\t\tconst count = sav.bindus[i] ?? 0;\n\t\t\t\t\t\t\treturn html`<tr><td><div class=\"planet-cell\"><span class=\"glyph\" aria-hidden=\"true\">${signGlyph(sign) ?? ''}</span> ${sign}</div></td><td class=\"${`bindu-cell ${this.sarvaTier(count)}`}\">${count}</td></tr>`;\n\t\t\t\t\t\t})}</tbody><tfoot><tr class=\"total-row\"><td>${this.t('Total')}</td><td>${sav.total}</td></tr></tfoot></table></div>${this.renderLegend(\n\t\t\t\t'Under 25 is a weak sign, 25 to 29 average, 30 and above strong. The 12 signs always total 337.',\n\t\t\t)}`;\n\t}\n\n\tprivate renderBhinna(signs: Signs) {\n\t\tconst bhinna = this.data?.bhinnashtakavarga;\n\t\tif (!bhinna?.length)\n\t\t\treturn html`<p class=\"roxy-empty\">${this.t('No bhinnashtakavarga data')}</p>`;\n\n\t\treturn html`${this.renderBinduGrid(\n\t\t\tsigns,\n\t\t\tbhinna,\n\t\t\t'Bhinnashtakavarga: the bindus each planet scores in every one of the twelve signs, with a row total.',\n\t\t)} ${this.renderLegend(\n\t\t\t'Each planet scores 0 to 8 bindus per sign. 4 is the mid, 5 and above is strong support for that planet in that sign.',\n\t\t)}`;\n\t}\n\n\t/**\n\t * The Shodhana-purified grid (Trikona then Ekadipati), which is what Shodhya\n\t * Pinda is computed from. The column totals are the Reduced Sarvashtakavarga,\n\t * so they are shown as the total row rather than as a second table.\n\t */\n\tprivate renderReduced(signs: Signs) {\n\t\tconst reduced = this.data?.reducedBhinnashtakavarga;\n\t\tconst rsav = this.data?.reducedSarvashtakavarga;\n\t\tif (!reduced?.length && !rsav)\n\t\t\treturn html`<p class=\"roxy-empty\">${this.t('No reduced ashtakavarga data')}</p>`;\n\n\t\treturn html`${this.renderBinduGrid(\n\t\t\tsigns,\n\t\t\treduced ?? [],\n\t\t\t'Reduced Bhinnashtakavarga after Shodhana: the bindus each planet keeps in every one of the twelve signs, with a row total and a Reduced Sarvashtakavarga totals row.',\n\t\t\trsav,\n\t\t)} ${this.renderLegend(\n\t\t\t'Bindus left after Trikona and Ekadipati Shodhana, the input to Shodhya Pinda. The Reduced SAV row totals the seven planets only, so it does not include the Lagna row above it.',\n\t\t)}`;\n\t}\n\n\t/**\n\t * The planet-by-sign grid both the bhinna and the reduced tabs draw. The rows\n\t * are the 7 planets plus Lagna, but a Sarva total counts the 7 planets only\n\t * (which is why the classical SAV grand total is 337), so the totals row is\n\t * named for what it is rather than left to read as a column sum.\n\t */\n\tprivate renderBinduGrid(\n\t\tsigns: Signs,\n\t\trows: BhinnaRow[],\n\t\tcaption: string,\n\t\ttotals?: SarvaRow,\n\t) {\n\t\tif (!rows.length)\n\t\t\treturn html`<p class=\"roxy-empty\">${this.t('No bindu data')}</p>`;\n\t\treturn html`<div class=\"overflow-scroll\" part=\"table\"><table class=\"bhinna-table\"><caption class=\"roxy-sr-only\">${caption}</caption><thead><tr><th scope=\"col\">${this.t('Planet')}</th>${signs.map(\n\t\t\t\t\t\t\t(s) => html`<th scope=\"col\" title=\"${s}\">${signGlyph(s) ?? s}</th>`,\n\t\t\t\t\t\t)}<th scope=\"col\">${this.t('Total')}</th></tr></thead><tbody>${rows.map(\n\t\t\t\t\t\t(row) => html`<tr><td>${row.planet}</td>${row.bindus.map(\n\t\t\t\t\t\t\t(count) =>\n\t\t\t\t\t\t\t\thtml`<td class=\"${`bindu-cell ${this.bhinnaTier(count)}`}\">${count}</td>`,\n\t\t\t\t\t\t)}<td>${row.total}</td></tr>`,\n\t\t\t\t\t)}</tbody>${\n\t\t\t\t\ttotals\n\t\t\t\t\t\t? html`<tfoot><tr class=\"total-row\"><td>${this.t('Reduced SAV')}</td>${signs.map((_, i) => html`<td>${totals.bindus[i] ?? 0}</td>`)}<td>${totals.total}</td></tr></tfoot>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</table></div>`;\n\t}\n\n\tprivate renderPinda() {\n\t\tconst pinda = this.data?.shodhyaPinda;\n\t\tif (!pinda?.length)\n\t\t\treturn html`<p class=\"roxy-empty\">${this.t('No shodhya pinda data')}</p>`;\n\n\t\treturn html`<div class=\"overflow-scroll\" part=\"table\"><table><caption class=\"roxy-sr-only\">${this.t(\n\t\t\t\t\t\t'Shodhya Pinda: each planet with its Rashi Pinda, Graha Pinda and Shodhya Pinda strength scores.',\n\t\t\t\t\t)}</caption><thead><tr><th scope=\"col\">${this.t('Planet')}</th><th scope=\"col\">${this.t('Rashi Pinda')}</th><th scope=\"col\">${this.t('Graha Pinda')}</th><th scope=\"col\">${this.t('Shodhya Pinda')}</th></tr></thead><tbody>${pinda.map(\n\t\t\t\t\t\t(row) => html`<tr><td>${row.planet}</td><td>${row.rashiPinda}</td><td>${row.grahaPinda}</td><td>${row.shodhyaPinda}</td></tr>`,\n\t\t\t\t\t)}</tbody></table></div>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-ashtakavarga-grid': RoxyAshtakavargaGrid;\n\t}\n}\n", "import type { TemplateResult } from 'lit';\nimport { css, html, nothing } from 'lit';\n\n/** A single tab in a {@link renderTablist} strip. */\nexport interface TablistItem<T extends string = string> {\n\tid: T;\n\tlabel: string;\n}\n\n/**\n * Shared styling for every horizontal tab strip in the library (natal chart\n * views, transits, kundli styles). Keeping one rule set means the tabs look\n * identical everywhere and theme through the same --roxy-* tokens.\n */\nexport const tablistStyles = css`.roxy-tablist{border-bottom:2px solid var(--roxy-border,#e4e4e7);scrollbar-width:thin;gap:2px;min-width:0;display:flex;overflow-x:auto}.roxy-tab{padding:var(--roxy-space-xs,.25rem) var(--roxy-space-md,1rem);font-size:var(--roxy-text-sm,.875rem);cursor:pointer;color:var(--roxy-muted,#71717a);transition:color var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,ease);background:0 0;border:none;border-bottom:2px solid #0000;flex-shrink:0;margin-bottom:-2px;font-family:inherit}.roxy-tab[aria-selected=true]{color:var(--roxy-accent-ink,#b45309);border-bottom-color:var(--roxy-accent,#f59e0b);font-weight:var(--roxy-weight-bold,600)}.roxy-tab:hover:not([aria-selected=true]){color:var(--roxy-fg,#0a0a0a)}.roxy-tab:focus-visible{outline:2px solid var(--roxy-ring,#f59e0b66);outline-offset:-2px;border-radius:4px}`;\n\n/**\n * Render a WAI-ARIA tablist. The host component owns the active-tab state; this\n * helper draws the buttons, wires click plus Left/Right arrow navigation with a\n * roving tabindex, and moves focus to the newly selected tab. Pair with\n * {@link tablistStyles}.\n *\n * The strip carries `part=\"tablist\"` and each button `part=\"tab\"`, so a host page can restyle or hide the view switch from outside the shadow root without knowing which component drew it.\n *\n * Pass `controls: true` when each tab governs a sibling\n * `<div role=\"tabpanel\" id=\"${idPrefix}-panel-${id}\">` so the buttons advertise\n * `aria-controls`. The host renders only the ACTIVE panel; this helper emits an\n * empty hidden panel for each of the others so the `aria-controls` on every tab\n * resolves. Omit it for tablists that swap a single rendered view in place with\n * no separate panel element (the kundli style switch).\n *\n * @example\n * ```ts\n * renderTablist({\n *   items: [{ id: 'wheel', label: 'Wheel' }, { id: 'grid', label: 'Aspect grid' }],\n *   active: this.view,\n *   onSelect: (v) => { this.view = v; },\n *   label: 'Natal chart views',\n *   idPrefix: 'natal',\n *   controls: true,\n * })\n * ```\n */\nexport function renderTablist<T extends string>(opts: {\n\titems: ReadonlyArray<TablistItem<T>>;\n\tactive: T;\n\tonSelect: (id: T) => void;\n\tlabel: string;\n\tidPrefix: string;\n\tcontrols?: boolean;\n}): TemplateResult {\n\tconst { items, active, onSelect, label, idPrefix, controls = false } = opts;\n\tconst onKeyDown = (e: KeyboardEvent) => {\n\t\tif (e.key !== 'ArrowRight' && e.key !== 'ArrowLeft') return;\n\t\te.preventDefault();\n\t\tconst idx = items.findIndex((it) => it.id === active);\n\t\tif (idx === -1) return;\n\t\tconst delta = e.key === 'ArrowRight' ? 1 : -1;\n\t\tconst next = items[(idx + delta + items.length) % items.length];\n\t\tif (!next) return;\n\t\tonSelect(next.id);\n\t\tconst root = (e.currentTarget as HTMLElement).getRootNode() as\n\t\t\t| ShadowRoot\n\t\t\t| Document;\n\t\trequestAnimationFrame(() => {\n\t\t\troot\n\t\t\t\t.querySelector<HTMLButtonElement>(`#${idPrefix}-tab-${next.id}`)\n\t\t\t\t?.focus();\n\t\t});\n\t};\n\treturn html`<div class=\"roxy-tablist\" part=\"tablist\" role=\"tablist\" aria-label=\"${label}\" @keydown=\"${onKeyDown}\">${items.map(\n\t\t\t(it) => html`<button type=\"button\" class=\"roxy-tab\" part=\"tab\" role=\"tab\" id=\"${idPrefix}-tab-${it.id}\" aria-selected=\"${active === it.id ? 'true' : 'false'}\" aria-controls=\"${controls ? `${idPrefix}-panel-${it.id}` : nothing}\" tabindex=\"${active === it.id ? '0' : '-1'}\" @click=\"${() => onSelect(it.id)}\">${it.label}</button>`,\n\t\t)}</div>${\n\t\t/**\n\t\t * Stand-in panels for the tabs that are not selected.\n\t\t *\n\t\t * The APG requires `aria-controls` on EVERY tab, not just the active one, and it must resolve to a real `tabpanel`. Components render one panel at a time, so without these the other tabs either carried a dangling IDREF or, as here previously, dropped the attribute and lost the relationship entirely: a screen reader user could not tell what any inactive tab governed.\n\t\t *\n\t\t * Empty and `hidden`, so they cost two nodes and no layout, and the panel content stays lazy: a natal chart does not build its wheel, aspect grid and positions table all at once just to satisfy an IDREF. The host still renders the ACTIVE panel itself, at `${idPrefix}-panel-${active}`, which is why these skip it.\n\t\t */\n\t\tcontrols\n\t\t\t? items\n\t\t\t\t\t.filter((it) => it.id !== active)\n\t\t\t\t\t.map(\n\t\t\t\t\t\t(it) => html`<div role=\"tabpanel\" id=\"${idPrefix}-panel-${it.id}\" aria-labelledby=\"${idPrefix}-tab-${it.id}\" hidden></div>`,\n\t\t\t\t\t)\n\t\t\t: nothing\n\t}`;\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport { planetGlyph } from '../tokens/index.js';\nimport type {\n\tCalculateAspectsResponse,\n\tCalculateTransitAspectsResponse,\n\tDetectAspectPatternsResponse,\n} from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { chevron, disclosureStyles } from '../utils/disclosure.js';\nimport {\n\tformatAspectName,\n\tformatDate,\n\tformatNumber,\n\tformatPercent,\n} from '../utils/format.js';\nimport {\n\tinterpAccordionStyles,\n\trenderReadingDetail,\n} from '../utils/interp-accordion.js';\n\n/**\n * Natal + transit aspect grid with chart-pattern detection. Renders three endpoints that share an aspects/patterns shape: /astrology/aspects (natal aspects + patterns + a harmonious/challenging summary), /astrology/transit-aspects (transiting-to-natal aspects with timing guidance), and /astrology/aspect-patterns (patterns only: grand trines, t-squares, stelliums, yods, kites). Each aspect row shows the two bodies, the aspect type coloured by nature, its orb and strength, and an expandable interpretation; patterns render as labelled cards listing the planets they bind, apex first.\n */\ntype AspectsData =\n\t| CalculateAspectsResponse\n\t| CalculateTransitAspectsResponse\n\t| DetectAspectPatternsResponse;\n\ntype PatternEntry = NonNullable<\n\tDetectAspectPatternsResponse['patterns']\n>[number];\n\n/**\n * One line naming the orb budget and the body set a pattern run used, so a reader can reproduce the detection. Built as a string, not a template: interpolating the clauses in markup leaves a space before the comma.\n */\nfunction provenance(o: NonNullable<DetectAspectPatternsResponse['options']>) {\n\tconst orbs = o.strictOrbs ? 'strict' : 'industry-standard';\n\tconst bodies = o.include?.length\n\t\t? `including ${o.include.join(' and ')}`\n\t\t: 'classical bodies only';\n\treturn `Detected with ${orbs} orbs, ${bodies}.`;\n}\n\n/** Reading order for detected configurations: the rare, chart-defining figures first, then by tightness. Mirrors `roxy-natal-chart`, which renders the same `patterns` shape. */\nconst PATTERN_ORDER: Record<string, number> = {\n\tGRAND_CROSS: 0,\n\tGRAND_TRINE: 1,\n\tKITE: 2,\n\tT_SQUARE: 3,\n\tYOD: 4,\n\tMYSTIC_RECTANGLE: 5,\n\tSTELLIUM: 6,\n};\n\n@customElement('roxy-aspects-table')\nexport class RoxyAspectsTable extends RoxyDataElement<AspectsData> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tdisclosureStyles,\n\t\tinterpAccordionStyles,\n\t\tcss`.wrap{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.head{justify-content:space-between;align-items:baseline;gap:var(--roxy-space-md,1rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.subtitle{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem);margin:0}.summary-pills{gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.pill{padding:2px var(--roxy-space-sm,.5rem);border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);border:1px solid;align-items:center;gap:4px;display:inline-flex}.pill--muted{color:var(--roxy-fg,#0a0a0a);background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 60%, transparent)}.pill--success{color:var(--roxy-success-fg,#166534);background:color-mix(in srgb, var(--roxy-success,#16a34a) 10%, transparent)}.pill--danger{color:var(--roxy-danger-fg,#991b1b);background:color-mix(in srgb, var(--roxy-danger,#dc2626) 10%, transparent)}.section-label{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.06em;font-weight:var(--roxy-weight-bold,600);margin:0 0 var(--roxy-space-xs,.25rem) 0}.interp-card summary{flex-wrap:wrap;gap:.5em}.interp-card summary .meta{color:var(--roxy-muted,#71717a);font-weight:400;font-size:var(--roxy-text-xs,.75rem);font-variant-numeric:tabular-nums;margin-left:auto}.static-head{font-size:var(--roxy-text-sm,.875rem);flex-wrap:wrap;align-items:center;gap:.5em;font-weight:500;display:flex}.glyph{font-size:1.1em;line-height:1}.nature-badge{font-size:var(--roxy-text-xs,.75rem);text-transform:capitalize;border-radius:9999px;padding:1px 8px;font-weight:600;display:inline-block}.nature-badge.harmonious{background:color-mix(in srgb, var(--roxy-success,#16a34a) 12%, transparent);color:var(--roxy-success-fg,#166534)}.nature-badge.challenging{background:color-mix(in srgb, var(--roxy-danger,#dc2626) 12%, transparent);color:var(--roxy-danger-fg,#991b1b)}.nature-badge.neutral{background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 60%, transparent);color:var(--roxy-fg,#0a0a0a)}.pattern{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);margin-bottom:var(--roxy-space-xs,.25rem);gap:.35rem;display:grid}.pattern-head{flex-wrap:wrap;align-items:baseline;gap:.5rem;display:flex}.pattern-name{font-weight:var(--roxy-weight-bold,600)}.pattern-tag{border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 55%, transparent);color:var(--roxy-fg,#0a0a0a);text-transform:capitalize;padding:1px 8px}.pattern-planets{flex-wrap:wrap;gap:.25rem;display:flex}.planet-chip{border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 45%, transparent);color:var(--roxy-fg,#0a0a0a);align-items:baseline;gap:.3rem;padding:1px 8px;display:inline-flex}.planet-chip.apex{background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 20%, transparent);font-weight:var(--roxy-weight-bold,600)}.planet-chip .apex-tag{text-transform:uppercase;letter-spacing:.04em;font-size:.85em;font-weight:400}.pattern-tight{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);font-variant-numeric:tabular-nums;margin-left:auto}.pattern-interp{color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-sm,.875rem);margin:0;line-height:1.45}.strongest{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);font-size:var(--roxy-text-sm,.875rem);flex-wrap:wrap;align-items:baseline;gap:.4rem;display:flex}.strongest .label{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);text-transform:uppercase;letter-spacing:.06em;font-weight:var(--roxy-weight-bold,600)}.strongest .meta{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);font-variant-numeric:tabular-nums;margin-left:auto}.provenance{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);margin:0}`,\n\t];\n\n\tprotected renderData(d: AspectsData) {\n\t\tconst aspects = 'aspects' in d ? (d.aspects ?? []) : [];\n\t\tconst patterns = 'patterns' in d ? (d.patterns ?? []) : [];\n\t\tconst summary = 'summary' in d ? d.summary : undefined;\n\t\tif (aspects.length === 0 && patterns.length === 0)\n\t\t\treturn this.renderEmpty();\n\n\t\t// Natal aspects date the chart, transits date the sky.\n\t\tconst date = formatDate(\n\t\t\tthis.effectiveLang(),\n\t\t\t'transitDate' in d ? d.transitDate : 'date' in d ? d.date : '',\n\t\t);\n\t\tconst options = 'options' in d ? d.options : undefined;\n\n\t\treturn html`<div class=\"wrap\" part=\"card\" aria-label=\"${this.t('Aspects')}\"><div class=\"head\" part=\"header\"><h2 class=\"title\">${aspects.length === 0 ? this.t('Chart patterns') : this.t('Aspects')}</h2>${\n\t\t\t\t\taspects.length > 0 || date\n\t\t\t\t\t\t? html`<p class=\"subtitle\">${[\n\t\t\t\t\t\t\t\taspects.length > 0\n\t\t\t\t\t\t\t\t\t? `${aspects.length} aspect${aspects.length === 1 ? '' : 's'}`\n\t\t\t\t\t\t\t\t\t: '',\n\t\t\t\t\t\t\t\tdate,\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t\t\t\t.join(' · ')}</p>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</div>${summary ? this.renderSummary(summary) : nothing} ${summary && 'strongest' in summary && summary.strongest ? this.renderStrongest(summary.strongest) : nothing} ${\n\t\t\t\taspects.length > 0\n\t\t\t\t\t? html`<div role=\"list\" part=\"section aspects\" aria-label=\"${this.t('Aspect list')}\">${aspects.map((a, i) => this.renderAspect(a, i))}</div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\tpatterns.length > 0\n\t\t\t\t\t? html`<div part=\"section patterns\"><p class=\"section-label\">${this.t('Patterns')}</p>${[...patterns]\n\t\t\t\t\t\t\t.sort(\n\t\t\t\t\t\t\t\t(a, b) =>\n\t\t\t\t\t\t\t\t\t(PATTERN_ORDER[a.kind] ?? 9) - (PATTERN_ORDER[b.kind] ?? 9) ||\n\t\t\t\t\t\t\t\t\t(b.tightness ?? 0) - (a.tightness ?? 0),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.map((p) => this.renderPattern(p))}</div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${options ? html`<p class=\"provenance\">${provenance(options)}</p>` : nothing}</div>`;\n\t}\n\n\tprivate renderSummary(\n\t\ts:\n\t\t\t| NonNullable<CalculateAspectsResponse['summary']>\n\t\t\t| NonNullable<CalculateTransitAspectsResponse['summary']>,\n\t) {\n\t\tconst total = 'totalAspects' in s ? s.totalAspects : s.total;\n\t\t// byType is a map, not a list: render the pairs, never the object.\n\t\tconst byType = Object.entries(s.byType ?? {}).sort((a, b) => b[1] - a[1]);\n\t\treturn html`<div class=\"summary-pills\" part=\"details\" role=\"region\" aria-label=\"${this.t('Aspect summary')}\">${typeof total === 'number' ? html`<span class=\"pill pill--muted\">${this.t('Total')}: ${total}</span>` : nothing} <span class=\"pill pill--success\">${this.t('Harmonious')}: ${s.harmonious}</span> <span class=\"pill pill--danger\">${this.t('Challenging')}: ${s.challenging}</span> <span class=\"pill pill--muted\">${this.t('Neutral')}: ${s.neutral}</span> ${byType.map(\n\t\t\t\t([type, count]) =>\n\t\t\t\t\thtml`<span class=\"pill pill--muted\">${formatAspectName({ type })}: ${count}</span>`,\n\t\t\t)}</div>`;\n\t}\n\n\t/**\n\t * The tightest transit by orb: the one aspect the client is most likely to\n\t * feel today. Lifted out of the list so it is not buried among the rest.\n\t */\n\tprivate renderStrongest(\n\t\ts: NonNullable<\n\t\t\tNonNullable<CalculateTransitAspectsResponse['summary']>['strongest']\n\t\t>,\n\t) {\n\t\tconst g1 = planetGlyph(s.planet1) ?? '';\n\t\tconst g2 = planetGlyph(s.planet2) ?? '';\n\t\tconst nature = (s.interpretation ?? 'neutral').toLowerCase();\n\t\treturn html`<div class=\"strongest\" part=\"details strongest\"><span class=\"label\">${this.t('Strongest')}</span> <span aria-hidden=\"true\" class=\"glyph\">${g1}</span>${s.planet1} <span class=\"nature-badge ${nature}\">${formatAspectName(s)}</span> <span aria-hidden=\"true\" class=\"glyph\">${g2}</span>${s.planet2} <span class=\"meta\">${this.t(\n\t\t\t\t'{{status}} · orb {{orb}}° · str {{strength}}',\n\t\t\t\t{\n\t\t\t\t\tstatus: s.isApplying ? this.t('Applying') : this.t('Separating'),\n\t\t\t\t\torb: formatNumber(this.effectiveLang(), s.orb, 2),\n\t\t\t\t\tstrength: formatNumber(this.effectiveLang(), s.strength, 0),\n\t\t\t\t},\n\t\t\t)}</span></div>`;\n\t}\n\n\tprivate renderAspect(\n\t\ta:\n\t\t\t| NonNullable<CalculateAspectsResponse['aspects']>[number]\n\t\t\t| NonNullable<CalculateTransitAspectsResponse['aspects']>[number],\n\t\tidx: number,\n\t) {\n\t\tconst g1 = planetGlyph(a.planet1) ?? '';\n\t\tconst g2 = planetGlyph(a.planet2) ?? '';\n\t\tconst nature = (a.interpretation ?? 'neutral').toLowerCase();\n\t\tconst type = formatAspectName(a);\n\t\tconst status = a.isApplying ? this.t('Applying') : this.t('Separating');\n\t\t// Natal aspects carry `meaning` (static aspect lore); transits carry `transitInterpretation` (timed guidance).\n\t\tconst meaning = 'meaning' in a ? a.meaning : undefined;\n\t\tconst transit =\n\t\t\t'transitInterpretation' in a ? a.transitInterpretation : undefined;\n\t\tconst hasBody = Boolean(meaning || transit);\n\t\tconst header = html`<span aria-hidden=\"true\" class=\"glyph\">${g1}</span>${a.planet1} <span class=\"nature-badge ${nature}\">${type}</span> <span aria-hidden=\"true\" class=\"glyph\">${g2}</span>${a.planet2} <span class=\"meta\">${this.t(\n\t\t\t\t'{{status}} · orb {{orb}}° · str {{strength}}',\n\t\t\t\t{\n\t\t\t\t\tstatus,\n\t\t\t\t\torb: formatNumber(this.effectiveLang(), a.orb, 2),\n\t\t\t\t\tstrength: formatNumber(this.effectiveLang(), a.strength, 0),\n\t\t\t\t},\n\t\t\t)}</span>`;\n\t\t// The header is the aspect itself (both bodies, the type, applying or\n\t\t// separating, orb and strength) and is never a reading, so an aspect the\n\t\t// API sent no meaning for already renders flat. Hiding the readings reuses\n\t\t// that shape rather than dropping the row.\n\t\tif (!hasBody || this.hideReadings) {\n\t\t\treturn html`<div class=\"interp-card\" part=\"reading\" role=\"listitem\"><div class=\"static-head\">${header}</div></div>`;\n\t\t}\n\t\treturn html`<details class=\"interp-card\" part=\"reading\" role=\"listitem\" name=\"aspects\" ?open=\"${idx === 0}\"><summary>${header}${chevron()}</summary><div class=\"interp-body\">${meaning ? this.renderMeaning(meaning) : nothing} ${transit ? this.renderTransit(transit) : nothing}</div></details>`;\n\t}\n\n\tprivate renderMeaning(\n\t\tm: NonNullable<\n\t\t\tNonNullable<CalculateAspectsResponse['aspects']>[number]['meaning']\n\t\t>,\n\t) {\n\t\tconst text = m.description?.short ?? m.description?.long;\n\t\treturn renderReadingDetail(\n\t\t\t{ summary: text, keywords: m.keywords },\n\t\t\tthis.translator,\n\t\t);\n\t}\n\n\tprivate renderTransit(\n\t\tt: NonNullable<\n\t\t\tNonNullable<\n\t\t\t\tCalculateTransitAspectsResponse['aspects']\n\t\t\t>[number]['transitInterpretation']\n\t\t>,\n\t) {\n\t\treturn renderReadingDetail(t, this.translator);\n\t}\n\n\t/**\n\t * One detected configuration. The apex (the focal planet a Kite, T-Square, or Yod discharges through) leads the chip row and is called out, since a T-Square read without its apex is not a reading. `element` and `modality` only appear on the kinds whose meaning pivots on them, so they are tags rather than columns.\n\t */\n\tprivate renderPattern(p: PatternEntry) {\n\t\tconst planets = p.planets ?? [];\n\t\tconst ordered = p.apex\n\t\t\t? [...planets].sort((a, b) => Number(b === p.apex) - Number(a === p.apex))\n\t\t\t: planets;\n\t\treturn html`<div class=\"pattern\" part=\"pattern\"><div class=\"pattern-head\"><span class=\"pattern-name\">${p.name ?? p.kind ?? 'Pattern'}</span> ${p.element ? html`<span class=\"pattern-tag\">${p.element}</span>` : nothing} ${p.modality ? html`<span class=\"pattern-tag\">${p.modality}</span>` : nothing} ${\n\t\t\t\t\tp.dissociate\n\t\t\t\t\t\t? html`<span class=\"pattern-tag\" title=\"${this.t('Out of sign: one or more planets sit outside the pattern element or modality, so the theme holds but runs weaker.')}\">${this.t('Dissociate')}</span>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t} ${typeof p.tightness === 'number' ? html`<span class=\"pattern-tight\">${this.t('{{percent}} tight', { percent: formatPercent(this.effectiveLang(), p.tightness, 0) })}</span>` : nothing}</div>${\n\t\t\t\tordered.length\n\t\t\t\t\t? html`<div class=\"pattern-planets\">${ordered.map((name) => {\n\t\t\t\t\t\t\tconst glyph = planetGlyph(name);\n\t\t\t\t\t\t\tconst isApex = Boolean(p.apex) && name === p.apex;\n\t\t\t\t\t\t\treturn html`<span class=\"${isApex ? 'planet-chip apex' : 'planet-chip'}\">${glyph ? html`<span aria-hidden=\"true\">${glyph}</span>` : nothing}${name}${isApex ? html`<span class=\"apex-tag\">${this.t('apex')}</span>` : nothing}</span>`;\n\t\t\t\t\t\t})}</div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\t// The figure, its planets and its tightness are the finding; the\n\t\t\t\t// paragraph is the reading of it. Same split as roxy-natal-chart, which\n\t\t\t\t// renders the same `patterns` shape.\n\t\t\t\tp.interpretation && !this.hideReadings\n\t\t\t\t\t? html`<p class=\"pattern-interp\">${p.interpretation}</p>`\n\t\t\t\t\t: nothing\n\t\t\t}</div>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-aspects-table': RoxyAspectsTable;\n\t}\n}\n", "import { css, html, nothing, svg } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport type { ChromeString } from '../i18n/chrome-strings.js';\nimport { planetGlyph } from '../tokens/index.js';\nimport type { AstrocartographyResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { chevron, disclosureStyles } from '../utils/disclosure.js';\nimport { formatDateTime } from '../utils/format.js';\nimport { interpAccordionStyles } from '../utils/interp-accordion.js';\nimport { planetColor } from '../utils/planet-color.js';\nimport { WORLD_LAND_PATH } from '../utils/world-map.js';\n\ntype LineSet = AstrocartographyResponse['lines'][number];\ntype GeoPoint = LineSet['ascendant']['points'][number];\n\n// Equirectangular (plate carree) projection in a 360x180 unit canvas: one unit\n// per degree. The SVG scales to the container width; the 2:1 aspect ratio holds.\nconst W = 360;\nconst H = 180;\nconst lonToX = (lon: number): number => lon + 180;\nconst latToY = (lat: number): number => 90 - lat;\n\n// Reference parallels (degrees). Tropics and polar circles use the current mean\n// obliquity; drawn as dashed guides so the curved rising/setting lines read\n// against real climate bands, not just a bare grid.\nconst TROPIC = 23.44;\nconst POLAR = 66.56;\n\nconst formatLon = (lon: number): string =>\n\tlon === 0 ? '0' : `${Math.abs(lon)}°${lon > 0 ? 'E' : 'W'}`;\nconst formatLat = (lat: number): string =>\n\tlat === 0 ? '0' : `${Math.abs(lat)}°${lat > 0 ? 'N' : 'S'}`;\n\n/**\n * Split a rising/setting line into screen polylines, breaking the path wherever\n * consecutive samples jump more than 180 degrees of longitude. Without the\n * split, a line that crosses the antimeridian draws a stray horizontal streak\n * straight across the whole map.\n */\nfunction toSegments(points: GeoPoint[]): string[] {\n\tconst segments: string[][] = [];\n\tlet current: string[] = [];\n\tlet prevLon: number | null = null;\n\tfor (const p of points) {\n\t\tif (prevLon !== null && Math.abs(p.longitude - prevLon) > 180) {\n\t\t\tif (current.length) segments.push(current);\n\t\t\tcurrent = [];\n\t\t}\n\t\tcurrent.push(`${lonToX(p.longitude)},${latToY(p.latitude)}`);\n\t\tprevLon = p.longitude;\n\t}\n\tif (current.length) segments.push(current);\n\treturn segments.filter((s) => s.length > 1).map((s) => s.join(' '));\n}\n\n/** Response angle key to the English SOURCE its label is looked up by. The same four tokens the natal wheel draws, so one chart cannot call an axis `AC` while the other calls it `ASC`. */\nconst ANGLE_LABEL: Record<string, ChromeString> = {\n\tmc: 'MC',\n\tic: 'IC',\n\tascendant: 'ASC',\n\tdescendant: 'DSC',\n};\n\n/**\n * Astrocartography (relocation) world map. Plots the four planetary lines for\n * every body from a /astrology/astrocartography response over a labeled\n * graticule: MC and IC as straight meridians, the Ascendant and Descendant as\n * latitude-sampled curves. Color is per body and theme-token driven; solid\n * lines are the Ascendant and Midheaven, dashed are the Descendant and IC.\n */\n@customElement('roxy-astrocartography-map')\nexport class RoxyAstrocartographyMap extends RoxyDataElement<AstrocartographyResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tdisclosureStyles,\n\t\tinterpAccordionStyles,\n\t\tcss`.wrap{background:var(--roxy-surface,#fff);width:100%;color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-primary,#0f172a);margin:0}.meta{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem)}svg{border-radius:var(--roxy-radius-sm,4px);width:100%;height:auto;display:block}.map-frame{fill:color-mix(in srgb, var(--roxy-border,#e4e4e7) 12%, transparent);stroke:var(--roxy-border,#e4e4e7);stroke-width:.8px}.land{fill:var(--roxy-secondary,#475569);opacity:.13}.grat{stroke:var(--roxy-border,#e4e4e7);stroke-width:.4px;fill:none}.grat-axis{stroke:var(--roxy-muted,#71717a);stroke-width:.6px;opacity:.6}.grat-ref{stroke:var(--roxy-secondary,#475569);stroke-width:.4px;stroke-dasharray:2 2;opacity:.5;fill:none}.axis-label{fill:var(--roxy-muted,#71717a);font-size:5px;font-family:var(--roxy-font-sans)}.acg-line{fill:none;stroke-width:1px;opacity:.95}.acg-line.dashed{stroke-dasharray:4 2.5}.acg-glyph{font-size:8px;font-family:var(--roxy-font-sans);font-weight:600}.birthplace{fill:var(--roxy-fg,#0a0a0a);font-size:9px}.legend{gap:var(--roxy-space-xs,.25rem) var(--roxy-space-md,1rem);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);flex-wrap:wrap;display:flex}.legend-item{align-items:center;gap:.3rem;display:inline-flex}.legend-swatch{border-top-style:solid;border-top-width:2px;width:14px;height:0}.legend-note{width:100%;color:var(--roxy-muted,#71717a)}.summary{color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-sm,.875rem);margin:0}.interp-dot{border-radius:50%;flex-shrink:0;align-self:center;width:10px;height:10px}.interp-line{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a);margin:0}.interp-line .code{color:var(--roxy-accent-ink,#b45309);margin-right:.4rem;font-weight:600}`,\n\t];\n\n\tprotected renderData(data: AstrocartographyResponse) {\n\t\tconst lines = data.lines ?? [];\n\t\tconst bd = data.birthDetails;\n\t\treturn html`<div class=\"wrap\" part=\"card\"><header part=\"header\"><h2 class=\"title\">${this.t('Astrocartography')}</h2>${\n\t\t\t\t\tbd\n\t\t\t\t\t\t? html`<div class=\"meta\">${formatDateTime(this.effectiveLang(), bd.date, bd.time)} · ${formatLat(Math.round(bd.latitude))} ${formatLon(Math.round(bd.longitude))}</div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</header>${this.renderMap(lines, bd)} ${this.renderLegend(lines)} ${\n\t\t\t\t// The map and its key are the deliverable here; the summary is prose\n\t\t\t\t// about what the map shows.\n\t\t\t\tdata.summary && !this.hideReadings\n\t\t\t\t\t? html`<p class=\"summary\">${data.summary}</p>`\n\t\t\t\t\t: nothing\n\t\t\t} ${this.renderInterpretations(lines)}</div>`;\n\t}\n\n\tprivate renderMap(\n\t\tlines: LineSet[],\n\t\tbd: AstrocartographyResponse['birthDetails'],\n\t) {\n\t\treturn html`<svg viewBox=\"0 0 ${W} ${H}\" part=\"chart\" role=\"img\" aria-label=\"${this.t('World map of planetary astrocartography lines')}\"><title>${this.t('Astrocartography world map')}</title><desc>${this.t(\n\t\t\t\t\t'Equirectangular world map. Each body has a Midheaven and Imum Coeli meridian and a curved Ascendant and Descendant line, colored per body.',\n\t\t\t\t)}</desc><rect class=\"map-frame\" x=\"0\" y=\"0\" width=\"${W}\" height=\"${H}\"/><path class=\"land\" d=\"${WORLD_LAND_PATH}\" fill-rule=\"evenodd\"/>${this.renderGraticule()} ${lines.map((l, i) => this.renderBodyLines(l, i))} ${\n\t\t\t\tbd\n\t\t\t\t\t? svg`<text class=\"birthplace\" x=${lonToX(bd.longitude)} y=${latToY(bd.latitude)} text-anchor=\"middle\" dominant-baseline=\"central\"><title>${this.t('Birth location')}</title>★</text>`\n\t\t\t\t\t: nothing\n\t\t\t}</svg>`;\n\t}\n\n\tprivate renderGraticule() {\n\t\tconst meridians = [];\n\t\tfor (let lon = -150; lon <= 150; lon += 30) {\n\t\t\tconst x = lonToX(lon);\n\t\t\tconst axis = lon === 0;\n\t\t\tmeridians.push(\n\t\t\t\tsvg`<line class=${axis ? 'grat-axis' : 'grat'} x1=${x} y1=\"0\" x2=${x} y2=${H} />`,\n\t\t\t);\n\t\t\tmeridians.push(\n\t\t\t\tsvg`<text class=\"axis-label\" x=${x} y=${H - 2} text-anchor=\"middle\">${formatLon(lon)}</text>`,\n\t\t\t);\n\t\t}\n\t\tconst parallels = [];\n\t\tfor (let lat = -60; lat <= 60; lat += 30) {\n\t\t\tconst y = latToY(lat);\n\t\t\tconst axis = lat === 0;\n\t\t\tparallels.push(\n\t\t\t\tsvg`<line class=${axis ? 'grat-axis' : 'grat'} x1=\"0\" y1=${y} x2=${W} y2=${y} />`,\n\t\t\t);\n\t\t\tparallels.push(\n\t\t\t\tsvg`<text class=\"axis-label\" x=\"2\" y=${y - 1}>${formatLat(lat)}</text>`,\n\t\t\t);\n\t\t}\n\t\t// Tropics and polar circles as dashed climate-band references.\n\t\tconst refs = [TROPIC, -TROPIC, POLAR, -POLAR].map(\n\t\t\t(lat) =>\n\t\t\t\tsvg`<line class=\"grat-ref\" x1=\"0\" y1=${latToY(lat)} x2=${W} y2=${latToY(lat)} />`,\n\t\t);\n\t\treturn svg`${meridians}${parallels}${refs}`;\n\t}\n\n\tprivate renderBodyLines(line: LineSet, index: number) {\n\t\tconst color = planetColor(line.planet, index);\n\t\t// Response symbol first, then the shared table, then the full name. Never a\n\t\t// truncation: `North Node.slice(0, 2)` labelled a whole map line \"No\".\n\t\tconst glyph = line.symbol || planetGlyph(line.planet) || line.planet;\n\t\tconst items = [\n\t\t\tthis.renderMeridian(\n\t\t\t\tline.mc.longitude,\n\t\t\t\tcolor,\n\t\t\t\tglyph,\n\t\t\t\tline.planet,\n\t\t\t\t'mc',\n\t\t\t\tfalse,\n\t\t\t),\n\t\t\tthis.renderMeridian(\n\t\t\t\tline.ic.longitude,\n\t\t\t\tcolor,\n\t\t\t\tglyph,\n\t\t\t\tline.planet,\n\t\t\t\t'ic',\n\t\t\t\ttrue,\n\t\t\t),\n\t\t\tthis.renderCurve(\n\t\t\t\tline.ascendant.points,\n\t\t\t\tcolor,\n\t\t\t\tglyph,\n\t\t\t\tline.planet,\n\t\t\t\t'ascendant',\n\t\t\t\tfalse,\n\t\t\t),\n\t\t\tthis.renderCurve(\n\t\t\t\tline.descendant.points,\n\t\t\t\tcolor,\n\t\t\t\tglyph,\n\t\t\t\tline.planet,\n\t\t\t\t'descendant',\n\t\t\t\ttrue,\n\t\t\t),\n\t\t];\n\t\treturn svg`${items}`;\n\t}\n\n\tprivate renderMeridian(\n\t\tlon: number,\n\t\tcolor: string,\n\t\tglyph: string,\n\t\tplanet: string,\n\t\tangle: string,\n\t\tdashed: boolean,\n\t) {\n\t\tconst x = lonToX(lon);\n\t\t// MC label rides the top edge, IC the bottom, so the two meridians of one\n\t\t// body never stack their glyphs at the same point.\n\t\tconst labelY = angle === 'ic' ? H - 7 : 9;\n\t\treturn svg`<g>\n\t\t\t<line class=${`acg-line${dashed ? ' dashed' : ''}`} stroke=${color} x1=${x} y1=\"0\" x2=${x} y2=${H}><title>${this.t('{{planet}} {{angle}} line', { planet, angle: this.t(ANGLE_LABEL[angle] ?? 'MC') })}</title></line>\n\t\t\t<text class=\"acg-glyph\" fill=${color} x=${x} y=${labelY} text-anchor=\"middle\" dominant-baseline=\"central\">${glyph}</text>\n\t\t</g>`;\n\t}\n\n\tprivate renderCurve(\n\t\tpoints: GeoPoint[],\n\t\tcolor: string,\n\t\tglyph: string,\n\t\tplanet: string,\n\t\tangle: string,\n\t\tdashed: boolean,\n\t) {\n\t\tconst segments = toSegments(points ?? []);\n\t\tif (segments.length === 0) return nothing;\n\t\t// Label at the sample nearest the equator, the most visible band.\n\t\tconst anchor = (points ?? []).reduce(\n\t\t\t(best, p) => (Math.abs(p.latitude) < Math.abs(best.latitude) ? p : best),\n\t\t\tpoints[0] ?? { latitude: 0, longitude: 0 },\n\t\t);\n\t\treturn svg`<g>\n\t\t\t${segments.map(\n\t\t\t\t(pts) =>\n\t\t\t\t\tsvg`<polyline class=${`acg-line${dashed ? ' dashed' : ''}`} stroke=${color} points=${pts}><title>${this.t('{{planet}} {{angle}} line', { planet, angle: this.t(ANGLE_LABEL[angle] ?? 'MC') })}</title></polyline>`,\n\t\t\t)}\n\t\t\t<text class=\"acg-glyph\" fill=${color} x=${lonToX(anchor.longitude)} y=${latToY(anchor.latitude)} text-anchor=\"middle\" dominant-baseline=\"central\">${glyph}</text>\n\t\t</g>`;\n\t}\n\n\tprivate renderLegend(lines: LineSet[]) {\n\t\tif (lines.length === 0) return nothing;\n\t\treturn html`<div class=\"legend\" part=\"legend\">${lines.map((l, i) => {\n\t\t\t\tconst color = planetColor(l.planet, i);\n\t\t\t\treturn html`<span class=\"legend-item\"><span class=\"legend-swatch\" style=\"${`border-top-color: ${color}`}\"></span> ${l.symbol ? html`${l.symbol}` : nothing}${l.planet}</span>`;\n\t\t\t})} <span class=\"legend-note\">${this.t('Solid lines are the Ascendant and Midheaven, dashed are the Descendant and IC.')}</span></div>`;\n\t}\n\n\t/**\n\t * The written read of each planetary line. Prose end to end, and the lines it\n\t * describes are already drawn and keyed on the map above, so `hide-readings`\n\t * takes the section whole.\n\t */\n\tprivate renderInterpretations(lines: LineSet[]) {\n\t\tif (lines.length === 0 || this.hideReadings) return nothing;\n\t\treturn html`<section class=\"block\" part=\"section readings\"><h3>${this.t('Planetary lines')}</h3>${lines.map((l, i) => {\n\t\t\t\tconst color = planetColor(l.planet, i);\n\t\t\t\tconst rows: Array<[string, string]> = [\n\t\t\t\t\t['MC', l.mc.interpretation],\n\t\t\t\t\t['IC', l.ic.interpretation],\n\t\t\t\t\t['AC', l.ascendant.interpretation],\n\t\t\t\t\t['DC', l.descendant.interpretation],\n\t\t\t\t];\n\t\t\t\treturn html`<details class=\"interp-card\" part=\"reading\" name=\"acg-lines\" ?open=\"${i === 0}\"><summary><span class=\"interp-lead\"><span class=\"interp-dot\" style=\"${`background: ${color}`}\"></span> ${l.symbol ? html`${l.symbol}` : nothing}${l.planet} </span>${chevron()}</summary><div class=\"interp-body\">${rows\n\t\t\t\t\t\t\t.filter(([, text]) => text)\n\t\t\t\t\t\t\t.map(\n\t\t\t\t\t\t\t\t([code, text]) =>\n\t\t\t\t\t\t\t\t\thtml`<p class=\"interp-line\"><span class=\"code\">${code}</span>${text}</p>`,\n\t\t\t\t\t\t\t)}</div></details>`;\n\t\t\t})}</section>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-astrocartography-map': RoxyAstrocartographyMap;\n\t}\n}\n", "import { capitalize } from './string.js';\n\n/**\n * Stable, theme-token stroke color per celestial body, for the multi-line map components (astrocartography, local space) where every body needs a distinct line that still follows the host theme.\n *\n * @remarks\n * Values are `var(--roxy-*)` references, so light and dark both resolve through the design tokens and a host override repaints every line with no rebuild. Classical bodies map to a fixed semantic token; any body the map omits (localized names, asteroids, the lunar nodes, Chiron, Lilith) falls back to a deterministic cycle keyed by its index in the response, so colors stay stable across re-renders and do not collide within the common 14-body set. Strokes and glyph fills are non-text chart marks, so the brighter base tokens are used directly rather than the WCAG-AA text variants.\n */\nconst PLANET_COLOR: Record<string, string> = {\n\tSun: 'var(--roxy-accent)',\n\tMoon: 'var(--roxy-secondary)',\n\tMercury: 'var(--roxy-info)',\n\tVenus: 'var(--roxy-success)',\n\tMars: 'var(--roxy-danger)',\n\tJupiter: 'var(--roxy-warning)',\n\tSaturn: 'var(--roxy-muted)',\n\tUranus: 'var(--roxy-info-fg)',\n\tNeptune: 'var(--roxy-success-fg)',\n\tPluto: 'var(--roxy-danger-fg)',\n};\n\n/** Deterministic fallback palette for bodies outside {@link PLANET_COLOR} (nodes, Chiron, Lilith, asteroids, or localized names). Keyed by response index so a given body keeps one color. */\nconst CYCLE = [\n\t'var(--roxy-accent)',\n\t'var(--roxy-info)',\n\t'var(--roxy-success)',\n\t'var(--roxy-danger)',\n\t'var(--roxy-warning)',\n\t'var(--roxy-secondary)',\n\t'var(--roxy-info-fg)',\n\t'var(--roxy-success-fg)',\n\t'var(--roxy-danger-fg)',\n\t'var(--roxy-accent-ink)',\n\t'var(--roxy-warning-fg)',\n\t'var(--roxy-primary)',\n];\n\n/** Theme-token color for a body, by name with an index fallback. {@link capitalize} matches the canonical TitleCase keys; unmapped or localized names cycle by `index`. */\nexport function planetColor(name: string, index = 0): string {\n\treturn (\n\t\tPLANET_COLOR[capitalize(name)] ??\n\t\tCYCLE[index % CYCLE.length] ??\n\t\t'var(--roxy-accent)'\n\t);\n}\n", "/**\n * Simplified Natural Earth 110m land outline (public domain, no attribution required) projected into the astrocartography 360x180 plate-carree space: x = lon + 180, y = 90 - lat. It therefore aligns pixel for pixel with the graticule and the planetary lines drawn in that same space, with no nested-SVG transform.\n *\n * @remarks\n * Integer-degree precision and a tiny-island cull keep it light (about 9 KB gzipped) so it ships inside the per-component bundle as a faint background fill; the colored lines stay the focus. Use `fill-rule: evenodd` so inland holes render. Regenerate from `ne_110m_land.geojson` if the projection or precision changes.\n */\nexport const WORLD_LAND_PATH =\n\t'M120 170L120 171L118 171L116 171L114 171L114 170L116 170L118 170L119 170L120 170ZM21 169L19 170L18 169L17 169L16 169L17 168L19 168L20 169L21 169ZM135 168L136 168L137 169L137 170L135 170L133 171L132 171L130 171L127 171L126 171L126 170L128 170L129 170L130 169L131 168L132 168L133 168L135 168ZM59 164L60 164L61 163L61 164L60 164L58 164L57 164L58 163L59 164ZM54 163L56 164L55 164L54 164L53 163L54 163ZM81 162L82 162L83 162L84 163L83 162L82 162L81 162L79 163L78 162L80 162L81 162ZM112 161L111 162L110 162L109 163L108 162L107 162L106 162L105 162L106 161L107 161L108 161L108 160L109 159L110 159L111 160L111 161L112 161ZM121 154L120 154L119 154L119 155L118 155L117 155L117 156L118 156L117 156L116 157L115 157L114 158L115 158L115 159L116 159L117 159L117 160L118 160L118 161L119 162L119 163L119 164L118 164L117 165L116 165L114 166L113 166L112 166L110 166L109 167L108 167L106 167L104 167L103 167L105 167L106 168L105 168L104 168L102 168L102 169L103 170L105 170L107 170L109 171L110 171L112 171L114 171L117 172L118 172L120 172L121 173L122 173L123 173L125 173L126 172L128 172L130 172L133 172L135 172L137 172L138 172L139 171L142 171L144 171L146 171L148 171L150 171L151 170L150 170L150 169L148 169L146 169L144 169L144 168L145 168L146 168L148 168L149 167L150 167L151 167L152 166L154 166L155 166L156 166L158 166L159 166L160 166L161 165L162 165L163 165L164 164L165 164L164 164L164 163L165 163L166 163L167 163L168 162L169 162L170 161L171 161L171 162L173 162L173 161L174 161L176 161L177 161L178 161L179 161L180 162L181 161L182 161L183 161L184 161L185 161L186 160L187 160L188 160L190 160L191 161L192 161L192 160L193 160L195 160L196 160L197 160L198 160L199 160L200 160L201 160L202 160L203 161L204 161L205 160L206 160L207 160L208 160L209 160L210 160L211 160L212 160L213 159L214 159L215 159L216 159L217 159L218 160L219 160L220 160L220 159L221 159L222 159L223 158L224 158L225 158L226 158L227 158L228 157L229 157L230 157L231 157L232 156L233 156L234 156L235 156L236 156L237 156L237 157L238 157L239 157L240 157L241 158L242 158L243 158L244 157L245 158L246 158L247 158L248 158L249 158L250 159L250 160L249 160L248 160L248 161L249 161L248 161L248 162L249 162L250 162L251 162L252 162L252 161L253 161L253 160L254 160L256 160L257 160L258 159L259 158L260 158L261 158L262 157L263 157L264 157L265 157L266 157L267 157L268 156L269 157L270 157L271 157L272 157L273 157L274 157L275 157L276 157L277 157L278 157L279 157L280 157L281 157L282 156L283 156L284 156L285 156L286 157L287 157L288 157L289 157L290 157L291 156L292 156L293 156L294 156L295 156L296 157L297 157L299 157L300 157L301 157L302 157L303 156L304 157L305 157L306 157L307 157L308 157L309 157L310 157L311 156L312 156L313 156L314 156L315 156L315 155L316 156L317 157L319 157L320 157L321 157L322 157L323 157L324 157L325 157L326 157L326 158L327 158L328 158L329 158L330 159L331 159L333 159L334 159L335 159L336 159L337 159L338 159L339 160L340 160L341 160L342 161L343 161L344 161L345 161L346 161L347 161L348 161L349 161L351 161L351 162L350 163L349 164L348 164L347 164L346 164L346 165L345 165L344 165L344 166L343 167L344 167L344 168L345 168L347 168L347 169L345 169L344 169L342 169L341 170L340 171L341 171L342 172L344 172L345 173L347 173L349 173L349 174L352 174L353 174L356 174L358 174L360 175L360 180L0 180L0 175L1 174L3 174L4 174L6 175L7 174L10 174L11 174L13 175L16 175L18 175L22 175L25 175L29 175L31 176L34 175L37 175L33 175L30 174L29 174L26 174L27 173L27 172L25 172L25 171L23 171L26 171L28 171L29 171L31 171L33 171L34 170L33 170L32 170L30 169L28 169L27 169L25 169L24 169L23 168L22 168L22 167L23 167L25 167L26 167L27 167L29 167L30 167L31 167L32 167L34 166L34 165L35 165L36 166L37 165L38 165L40 165L41 165L42 165L44 165L45 164L46 164L48 164L49 164L50 164L52 164L53 164L55 165L56 164L57 164L59 165L60 164L61 164L63 164L64 164L65 164L66 164L67 164L68 165L69 164L70 165L71 165L72 165L74 165L75 165L77 165L78 165L79 165L80 165L79 165L79 164L77 164L77 163L76 163L77 163L78 163L80 163L81 163L82 163L82 164L84 164L85 163L86 163L88 163L89 163L90 163L91 163L92 163L93 163L94 163L95 163L96 164L97 164L99 164L99 163L100 163L101 164L102 163L103 164L104 164L105 164L106 164L107 163L108 163L110 163L111 163L112 163L113 162L112 161L112 160L111 160L112 159L113 158L112 158L112 157L113 157L114 156L115 156L116 155L117 155L118 155L119 154L120 154L121 154L121 153L122 153L123 154L122 154L121 154ZM112 144L114 144L115 145L114 145L113 145L112 146L111 145L110 145L109 145L108 144L107 144L105 143L106 143L108 144L109 144L110 143L111 143L112 143L112 144ZM121 141L122 142L121 142L120 142L119 142L120 141L121 142L121 141ZM250 140L249 140L249 139L250 139L251 139L250 140ZM325 131L326 131L327 131L328 131L328 132L328 133L327 134L326 134L325 133L325 132L325 131ZM353 131L354 131L354 132L353 133L353 134L352 134L351 134L351 135L351 136L350 136L349 137L348 137L348 136L347 136L347 135L348 134L349 134L350 134L351 133L352 132L352 131L353 130L353 131ZM355 126L355 127L356 127L356 128L357 128L358 128L359 128L358 129L357 129L357 130L357 131L356 131L355 132L355 131L355 130L354 130L354 129L355 129L355 128L355 127L354 127L354 126L353 125L353 124L354 125L355 126ZM347 112L346 112L345 112L345 111L344 110L345 110L345 111L346 111L347 112ZM358 107L359 108L358 108L357 108L358 107L358 108L358 107ZM359 107L359 106L360 106L360 107L359 107ZM230 104L230 105L230 106L230 105L230 106L230 107L229 107L229 108L229 109L229 110L228 112L228 114L227 115L226 115L225 116L225 115L224 115L224 114L223 113L223 112L223 111L224 111L224 110L224 109L224 108L224 107L224 106L225 106L226 106L227 105L228 105L228 104L229 103L229 102L230 102L230 103L230 104ZM324 104L324 105L325 104L325 105L325 106L326 107L326 108L326 109L327 109L328 110L329 110L329 111L330 112L330 113L331 112L331 113L332 114L333 115L333 116L333 117L334 118L334 119L333 119L333 120L333 121L333 122L332 123L331 124L331 125L330 126L330 127L329 128L328 128L327 128L327 129L326 129L325 129L325 128L324 128L324 129L323 129L322 128L321 128L320 127L320 126L319 126L318 126L318 125L318 124L318 125L317 125L318 124L318 123L317 124L316 124L316 125L315 124L315 123L314 123L313 122L312 122L311 121L310 122L308 122L307 122L306 122L305 123L304 123L304 124L303 124L302 124L301 124L300 124L299 125L299 124L299 125L298 125L297 125L296 124L295 124L296 123L296 122L295 121L295 120L295 119L294 118L294 117L293 117L293 116L294 117L293 116L294 116L294 115L293 114L294 114L294 113L294 112L294 113L295 112L295 111L296 111L297 111L298 110L299 110L300 110L301 110L301 109L302 109L302 108L302 107L303 106L303 107L304 107L304 106L305 105L306 105L306 104L307 104L308 104L308 105L309 105L310 105L309 104L310 104L310 103L311 103L311 102L312 102L313 102L312 101L313 101L314 102L315 102L316 102L317 102L317 103L316 103L316 104L315 105L316 105L316 106L317 106L318 106L318 107L319 107L320 108L321 107L321 106L322 105L322 104L322 103L322 102L322 101L323 101L323 102L324 103L324 104ZM301 100L300 100L299 100L300 99L300 100L301 100ZM341 100L340 100L340 99L341 100ZM342 100L341 99L341 98L341 99L342 100ZM304 100L303 100L304 100L304 99L305 99L306 98L307 98L307 99L306 99L305 99L304 100ZM298 98L299 98L299 99L298 99L297 99L297 98L298 98ZM303 98L303 99L301 99L300 99L300 98L301 98L301 99L302 98L303 98ZM340 98L340 99L339 98L338 97L339 98L340 98ZM289 97L291 97L291 96L293 97L293 98L294 98L296 98L295 99L293 98L292 98L291 98L289 98L288 98L286 97L285 97L286 96L287 96L288 96L289 97ZM315 96L314 97L314 96L314 95L315 96ZM336 97L335 97L335 96L335 95L335 96L336 96L336 97ZM332 95L331 96L330 96L329 96L328 96L328 95L329 96L330 96L330 95L330 96L331 95L332 95L332 94L332 95ZM307 93L307 94L306 94L306 93L307 93ZM310 93L311 94L310 93L309 93L308 93L309 93L310 93ZM333 94L333 95L333 94L332 94L332 93L331 93L332 93L333 94ZM314 91L314 93L315 93L316 92L317 92L318 92L319 92L320 92L321 93L323 93L325 94L326 95L328 96L328 97L327 97L328 98L329 99L329 100L330 100L331 100L331 101L330 101L330 100L329 100L328 100L327 99L326 98L325 98L324 98L323 98L323 99L322 99L321 99L320 98L319 98L318 98L319 97L318 96L318 95L316 95L315 94L314 94L313 94L313 93L312 93L313 92L314 92L312 92L311 91L312 91L312 90L314 91ZM305 89L304 90L303 90L301 90L300 90L300 91L301 91L303 91L302 92L302 93L302 94L303 95L303 96L302 95L303 94L302 95L301 95L302 94L301 94L301 93L300 93L300 94L300 96L299 95L300 94L299 93L299 92L299 91L300 90L300 89L301 89L302 89L303 89L304 89L305 88L305 89ZM309 89L309 90L308 90L308 91L308 90L307 89L308 88L309 88L309 89ZM286 96L285 96L284 95L283 94L282 94L281 93L281 92L280 91L279 90L279 89L279 88L278 88L277 87L276 86L275 85L276 85L277 85L278 86L279 86L280 87L281 88L282 88L282 89L283 89L284 90L283 91L284 91L285 92L286 92L286 93L286 94L286 96ZM298 88L299 89L298 89L297 90L298 91L297 91L297 92L296 94L295 94L294 93L293 93L292 93L291 93L290 93L290 92L290 91L289 90L289 89L290 88L291 88L291 87L292 87L293 87L294 86L294 85L295 85L296 84L297 83L298 84L299 85L298 85L299 86L298 86L297 87L298 88ZM306 82L307 83L306 84L306 83L305 83L306 84L305 84L304 84L304 83L304 82L303 83L302 83L302 82L303 82L303 81L304 82L305 81L305 80L306 81L306 82ZM261 84L260 84L260 83L260 82L260 80L261 81L262 82L262 84L261 84ZM304 80L303 81L302 80L303 80L303 79L303 80L304 79L304 80ZM299 81L297 82L298 81L298 80L299 80L300 79L299 80L299 81ZM302 78L303 78L303 79L302 80L302 79L302 78ZM306 78L306 79L305 79L305 80L305 79L304 79L305 79L305 78L304 77L305 77L306 78ZM302 77L301 78L301 77L300 77L301 77L302 77ZM301 71L302 72L303 73L302 74L302 75L302 76L303 76L304 76L304 77L303 77L303 76L303 77L302 76L301 76L301 75L301 76L300 75L300 74L300 72L301 71ZM114 72L113 72L113 71L114 71L114 72ZM103 72L102 72L102 71L102 72L103 72L104 72L103 72ZM107 70L108 70L109 70L110 70L110 71L111 71L112 71L111 72L110 72L109 72L108 72L107 72L106 72L106 71L107 71L107 72L108 71L107 71L107 70ZM290 71L289 72L289 71L289 70L290 70L291 70L291 71L290 71ZM24 71L24 70L25 70L25 71L24 71ZM100 67L101 68L102 67L102 68L103 68L103 69L104 69L105 69L106 70L105 70L104 70L102 70L103 70L103 69L102 69L101 68L100 68L99 68L98 68L98 67L97 67L97 68L96 68L95 68L96 68L96 67L97 67L98 67L99 67L100 67ZM102 66L102 65L102 66ZM301 67L301 68L300 67L300 66L301 65L302 65L302 66L301 67ZM315 56L314 57L314 56L313 57L312 57L313 56L314 56L315 56ZM215 54L214 55L213 55L212 55L213 55L214 55L215 54ZM204 54L204 55L205 55L206 55L205 55L204 55L204 54ZM196 52L195 53L194 53L192 52L193 52L194 52L195 52L196 52ZM189 49L190 49L190 51L189 51L188 51L188 50L188 49L189 49ZM321 53L321 54L320 55L319 55L317 55L316 57L315 56L315 55L313 56L312 56L311 56L312 57L311 59L310 59L310 58L310 57L309 57L310 56L311 56L312 55L313 55L315 54L316 54L317 53L319 52L320 51L320 49L321 49L322 50L322 51L321 52L321 53ZM190 48L189 49L189 48L189 47L190 48ZM324 46L325 46L326 47L324 47L323 48L322 47L321 48L320 48L320 47L321 47L322 45L322 44L323 45L324 46ZM116 43L117 44L118 44L117 44L116 44L116 43ZM118 41L116 41L115 40L116 40L117 40L118 41ZM56 41L56 42L54 41L53 40L52 40L52 39L53 39L53 40L54 40L55 40L55 41L56 41ZM124 39L123 40L124 40L125 40L124 40L125 41L126 40L127 41L126 41L127 41L127 42L127 43L126 43L126 42L125 43L124 43L125 43L124 42L123 42L121 42L121 41L122 41L123 39L124 38L125 38L124 39ZM47 36L48 36L48 37L49 38L48 38L48 37L47 37L47 36ZM324 39L325 41L323 41L323 42L324 43L324 44L323 43L322 44L322 43L322 42L322 41L322 40L322 39L322 38L322 37L323 36L322 36L323 36L323 37L323 38L324 39ZM173 38L171 38L170 38L171 37L170 36L172 35L173 35L174 35L174 36L174 37L173 38ZM193 34L192 35L191 35L191 34L192 34L193 34ZM27 33L26 33L25 33L26 32L27 32L28 32L27 33ZM177 31L176 32L177 32L178 32L178 33L177 34L178 34L179 35L180 36L180 37L182 37L182 38L181 38L181 39L179 39L178 39L177 39L176 40L175 40L174 40L176 39L177 39L175 38L176 38L175 37L177 37L177 36L176 35L175 35L175 34L174 35L174 34L174 33L174 32L175 31L176 31L177 31ZM14 30L13 30L14 30ZM98 27L97 28L96 28L97 27L98 27ZM8 26L9 26L10 26L10 27L11 27L10 27L9 27L8 27L8 26ZM95 24L95 25L96 25L97 25L98 26L99 26L100 26L99 27L97 26L96 26L94 27L94 26L93 26L94 26L94 25L94 24L95 24ZM165 24L166 25L165 26L162 26L161 27L160 26L157 26L158 26L156 25L158 25L156 24L158 24L159 24L161 24L162 24L164 23L165 24ZM104 23L103 23L103 22L104 22L105 22L105 23L104 23ZM5 23L6 24L5 23L8 23L10 24L9 24L7 25L7 26L6 26L5 25L4 25L3 24L2 25L1 24L0 24L1 25L0 25L0 21L2 22L5 23ZM84 21L82 21L80 21L81 20L82 20L83 20L84 21ZM360 19L359 19L360 18L360 19ZM1 19L0 19L0 18L1 18L2 19L1 19ZM89 21L89 22L91 21L92 21L92 22L93 23L94 22L94 21L94 20L96 20L97 20L99 21L98 22L99 22L99 23L97 24L95 24L94 23L94 24L93 25L92 26L90 26L89 26L89 27L88 27L87 28L86 29L85 30L85 31L87 31L87 32L88 33L89 33L91 33L92 34L93 34L94 34L95 35L97 35L98 35L98 36L98 37L99 38L100 39L101 38L101 37L101 36L100 35L102 35L103 34L103 33L103 32L101 31L103 30L102 29L102 28L103 27L104 28L105 28L106 28L107 28L108 28L109 29L110 29L110 30L111 31L112 31L112 32L114 31L115 30L116 31L117 32L119 33L118 34L120 34L120 35L122 35L123 35L123 36L124 36L124 37L124 38L123 39L121 39L120 40L118 40L116 40L115 40L114 40L113 40L111 41L110 42L109 43L110 43L111 42L113 41L115 41L116 41L115 42L115 43L116 44L117 44L118 44L119 43L120 44L119 45L117 45L116 46L115 46L114 46L116 45L114 45L113 45L112 46L111 46L110 46L109 47L109 48L110 48L109 49L108 49L107 49L106 49L108 49L107 49L106 49L106 50L105 51L104 51L105 51L105 52L104 53L104 52L104 51L103 51L104 52L103 52L104 52L104 53L104 54L104 55L103 55L102 56L101 56L101 57L100 57L99 58L99 59L99 60L99 61L99 62L100 63L100 64L100 65L99 65L99 64L98 64L98 63L97 63L97 62L97 61L96 60L95 60L94 60L92 60L91 60L90 60L91 60L91 61L90 61L89 61L88 60L87 60L86 60L85 61L84 61L83 62L83 63L83 64L82 65L82 66L82 67L82 68L83 69L83 70L84 71L85 71L86 72L87 71L88 71L89 71L89 70L90 69L91 69L92 69L93 68L93 69L93 70L92 70L93 71L92 71L92 72L92 71L92 72L92 73L91 74L92 74L93 74L94 74L95 74L96 74L96 75L97 75L97 76L96 76L96 77L97 77L97 78L96 78L96 79L97 80L98 81L99 81L100 81L100 80L101 80L101 81L102 81L103 81L104 81L104 80L105 79L106 79L107 79L107 78L108 78L109 78L108 79L108 80L108 81L109 81L109 80L109 79L110 79L110 78L110 79L111 79L112 79L113 79L114 79L114 80L115 80L116 80L116 79L117 79L118 79L117 80L118 80L119 81L120 81L120 82L121 82L122 83L122 84L123 84L124 84L125 84L126 84L127 85L128 85L128 86L129 86L129 88L130 88L130 89L129 90L130 90L131 90L131 91L132 91L133 91L135 92L136 92L135 93L137 92L139 93L140 93L141 94L143 95L144 95L145 95L145 97L145 99L144 100L143 101L142 102L142 103L141 103L141 104L141 106L141 107L141 108L140 108L140 110L139 111L139 112L138 112L138 113L137 113L135 113L135 114L134 114L132 115L132 116L131 117L132 117L131 118L131 119L130 119L129 121L128 122L127 123L127 124L126 124L125 125L124 125L123 124L122 124L123 125L123 126L123 127L122 128L121 129L119 129L118 129L118 130L118 131L117 131L116 131L115 131L115 132L116 132L117 133L116 133L115 133L115 135L114 135L113 135L113 136L112 136L113 137L114 137L114 138L113 139L112 140L111 140L111 141L111 142L112 142L111 142L110 143L109 143L109 144L107 144L106 143L105 142L105 141L105 140L104 139L105 138L106 137L104 137L105 136L106 134L107 134L107 132L106 133L106 132L106 130L107 129L106 128L106 127L107 127L107 126L108 124L109 122L108 121L109 120L109 119L109 118L109 116L110 114L110 111L110 110L110 108L109 108L109 107L107 106L105 105L104 105L104 104L103 102L102 100L101 98L100 97L99 97L99 96L99 95L99 94L100 93L100 92L100 93L99 92L99 91L100 90L100 89L101 89L101 88L102 87L103 86L103 85L102 84L103 84L103 83L102 83L102 82L101 81L100 81L100 82L100 83L99 83L99 82L98 82L97 82L96 82L96 81L95 80L94 80L94 79L93 78L92 77L93 77L92 77L91 77L90 76L89 76L88 76L88 75L87 74L86 74L85 74L84 74L83 74L82 74L81 73L80 73L79 73L78 72L76 72L76 71L75 71L75 70L74 70L75 69L74 69L75 69L74 68L74 67L73 66L72 65L71 64L70 63L69 62L68 62L68 61L67 60L67 59L66 58L65 58L65 59L65 60L66 60L66 61L67 61L67 62L68 62L68 63L69 64L69 65L69 66L70 66L71 67L70 67L69 66L68 66L68 65L68 64L67 64L67 63L66 63L65 62L66 62L66 61L65 61L64 60L64 59L63 58L63 57L62 56L61 56L60 56L59 55L58 54L57 52L56 51L56 50L56 49L56 48L55 47L56 46L56 44L56 43L56 42L55 42L57 42L57 43L58 43L58 42L57 41L55 40L54 40L53 39L52 38L51 37L51 36L49 36L49 35L48 35L48 34L46 33L46 32L45 32L43 32L42 31L40 30L39 30L37 30L36 30L34 30L33 29L32 29L32 30L31 30L30 30L29 31L28 31L28 30L29 29L30 29L29 29L28 29L27 30L26 31L27 31L26 32L25 32L24 33L23 33L22 34L20 34L19 35L18 35L17 35L15 36L15 35L16 35L17 35L18 34L19 34L20 34L21 33L22 33L22 32L23 31L22 31L21 31L21 32L20 31L19 31L18 31L18 30L17 30L16 30L15 30L15 29L14 28L15 27L16 27L17 27L18 26L18 27L19 26L18 26L19 25L18 25L17 26L16 25L15 26L14 25L13 25L12 24L13 24L16 23L16 24L18 24L18 23L16 23L16 22L15 22L13 22L14 21L16 21L17 21L17 20L18 20L19 20L21 19L22 19L23 19L25 19L26 19L28 19L29 20L30 19L32 20L34 20L35 20L36 20L38 20L39 20L41 21L42 21L43 21L44 21L46 20L47 20L49 20L50 20L51 20L52 20L53 20L54 21L56 20L56 21L57 20L59 20L60 21L62 21L64 21L65 21L66 22L65 22L67 22L69 22L70 22L71 23L72 22L71 22L72 21L73 21L74 21L75 21L76 22L77 22L79 22L80 22L82 22L81 22L82 21L84 22L84 23L85 22L86 21L85 20L84 20L84 19L85 18L86 18L87 19L88 20L89 21ZM66 17L65 17L68 17L69 18L70 17L71 17L72 18L72 17L73 17L75 17L75 18L76 19L77 20L79 20L77 20L78 21L76 21L74 21L73 21L71 21L68 21L67 21L66 21L65 21L64 21L63 20L65 20L66 20L68 20L66 19L64 19L62 19L64 19L62 19L61 18L62 17L65 17L66 17ZM76 17L75 17L73 17L73 16L75 16L76 17ZM104 17L103 17L102 17L101 17L100 17L99 17L99 16L100 16L102 16L104 17ZM93 17L94 17L95 17L98 16L99 17L99 18L101 18L102 17L104 18L106 18L106 19L108 18L109 19L111 19L112 20L113 21L111 21L114 22L115 22L117 23L118 23L118 24L116 25L115 25L113 24L112 24L113 25L114 25L115 26L115 27L114 27L111 26L113 27L114 28L111 28L109 27L108 27L108 26L107 26L105 25L105 26L102 26L101 25L102 25L104 25L106 25L106 24L107 23L107 22L105 21L103 21L104 21L103 20L102 20L101 20L99 20L95 20L93 20L91 20L90 19L92 19L90 19L90 18L91 17L92 16L94 16L93 17ZM80 16L81 16L83 16L83 17L82 17L83 17L83 18L82 19L81 19L80 18L78 17L80 17L78 17L80 16ZM324 17L322 17L320 17L321 16L322 16L323 17L324 17ZM87 17L86 18L85 18L84 17L85 16L88 16L89 16L88 17L87 17ZM60 19L57 19L56 19L54 18L55 17L56 16L55 16L58 16L60 16L62 16L63 16L64 17L63 17L61 17L60 18L60 19ZM331 15L330 15L328 15L326 15L328 15L331 15ZM86 15L84 15L83 15L84 15L85 14L86 15ZM325 14L324 15L321 15L319 15L317 15L318 14L319 14L321 14L325 14ZM82 13L82 14L82 15L80 15L79 15L79 14L77 14L79 14L80 13L81 13L82 13ZM72 14L73 14L74 14L74 15L70 15L68 16L66 16L66 15L68 15L64 15L62 15L64 14L65 14L67 14L69 14L71 15L70 14L70 13L71 13L72 14ZM238 19L237 19L234 19L233 19L232 19L231 18L232 18L232 17L234 16L236 15L238 14L241 14L244 14L246 13L248 13L249 13L248 14L245 14L242 15L238 16L237 17L235 18L236 18L238 19ZM85 13L86 13L88 13L89 14L90 14L91 14L92 14L94 15L95 14L97 14L99 14L100 15L98 16L97 15L94 16L92 16L90 15L88 15L87 15L87 14L86 14L84 14L83 13L85 13ZM64 12L64 13L63 13L62 14L60 14L59 14L57 14L59 13L61 12L62 13L64 12ZM287 13L287 14L288 13L291 13L293 14L294 14L294 15L293 15L290 16L289 16L291 16L292 16L293 16L294 17L294 16L296 16L299 16L299 17L303 17L303 16L305 16L307 16L309 17L309 18L308 18L310 19L311 19L312 18L314 19L316 18L317 19L318 18L320 19L319 18L320 17L330 18L333 19L337 19L339 19L340 20L341 21L342 20L344 20L346 21L348 20L350 21L351 21L350 20L354 20L356 20L359 21L360 21L360 25L359 25L357 25L358 26L359 27L359 28L357 27L355 28L354 28L352 29L351 30L350 30L349 29L346 30L345 30L344 30L343 31L342 32L343 32L343 34L342 34L342 35L340 36L340 37L339 37L338 38L337 39L336 38L336 37L335 35L336 33L337 33L337 32L338 32L340 31L342 30L344 29L344 27L343 28L340 29L339 28L337 29L334 30L335 31L333 31L331 31L331 30L330 30L329 31L325 31L322 31L319 33L315 35L317 35L317 36L318 36L319 36L320 36L321 37L321 38L321 39L321 40L320 42L319 43L318 44L317 45L316 46L315 47L314 47L313 47L312 47L311 47L311 48L310 48L310 49L309 49L309 50L308 50L308 51L307 51L308 51L309 53L309 54L309 55L308 55L307 56L306 56L306 55L307 54L306 53L307 53L306 52L305 52L305 51L305 50L304 50L303 50L302 51L301 51L302 51L301 50L302 50L302 49L301 49L300 50L299 51L298 51L298 52L299 52L299 53L300 53L301 52L302 53L303 53L301 53L301 54L300 54L299 55L300 56L301 57L301 58L302 58L302 59L301 59L302 60L302 61L302 62L301 62L300 63L300 64L299 65L297 66L296 67L295 67L294 68L294 67L293 68L292 68L291 69L290 70L290 69L289 68L288 68L287 69L286 70L286 71L286 72L287 73L288 74L289 75L289 77L289 78L288 79L287 80L286 80L285 81L285 80L284 80L283 79L283 78L282 77L281 77L280 77L280 78L279 79L279 80L279 81L280 81L280 82L280 83L281 83L282 83L282 84L283 84L283 85L283 86L283 87L284 87L284 88L284 89L283 88L281 87L281 86L281 85L280 85L280 84L280 83L279 82L278 82L278 81L279 80L278 79L279 79L278 78L279 77L278 76L278 75L278 74L277 73L277 74L275 74L274 74L275 73L274 72L274 71L274 70L273 70L272 69L272 68L271 67L270 67L271 68L270 68L269 68L268 68L267 69L266 70L265 71L264 72L263 72L262 73L262 74L261 74L260 74L260 75L260 76L260 77L260 78L260 80L259 80L259 81L258 81L258 82L257 81L256 80L256 79L255 78L255 77L255 76L254 75L254 74L253 72L253 71L253 70L253 69L251 69L250 69L249 68L250 68L249 67L248 66L247 66L247 65L246 65L245 65L243 65L241 65L240 65L239 64L237 64L237 63L236 63L235 64L233 63L232 62L231 61L230 60L229 60L228 60L228 61L229 62L229 63L230 63L230 64L231 65L231 64L232 64L232 65L231 65L232 66L233 66L234 66L235 65L236 64L236 65L237 66L238 66L239 66L239 67L240 67L240 68L239 68L239 69L238 70L238 71L237 71L237 72L236 72L235 72L235 73L234 73L233 73L232 74L231 75L230 75L229 76L228 76L227 76L227 77L226 77L225 77L224 77L223 77L223 76L223 75L223 74L223 73L222 73L222 72L221 71L220 70L219 69L219 68L219 67L218 66L217 66L217 65L217 64L216 63L215 62L215 61L215 60L215 61L214 62L213 62L212 60L213 61L213 62L214 64L215 65L216 66L215 66L216 67L217 68L217 69L217 70L217 71L218 72L219 73L219 74L220 75L221 76L222 76L222 77L223 77L223 78L223 79L224 79L224 80L225 80L226 79L227 79L228 79L229 79L230 78L231 78L231 79L231 80L231 81L230 82L229 83L229 85L228 86L227 87L226 88L224 89L223 90L222 91L222 92L221 92L220 93L220 94L219 95L219 96L219 97L219 98L220 99L220 100L220 101L220 102L221 103L221 104L221 105L220 105L220 106L219 107L217 108L216 109L215 110L215 111L215 112L216 112L216 113L215 114L216 114L215 114L214 115L213 115L213 116L213 117L212 118L212 119L211 119L211 120L210 121L209 122L208 123L207 123L206 124L205 124L204 124L203 124L202 124L201 124L200 125L199 124L198 124L198 123L198 122L198 121L197 120L196 119L196 118L195 117L195 116L195 115L194 114L194 113L194 112L193 111L193 110L193 109L192 108L192 107L192 106L192 105L192 104L193 104L193 103L193 102L194 102L194 101L193 100L193 99L193 98L193 97L192 96L192 95L191 94L190 93L189 92L189 91L189 90L189 89L190 88L190 87L189 86L188 86L189 85L187 86L186 86L185 85L185 84L184 84L183 84L182 84L181 84L179 85L178 85L177 85L176 85L175 85L174 85L173 85L172 86L171 85L170 84L169 84L169 83L168 83L167 82L167 81L166 81L166 80L165 80L165 79L164 79L164 78L163 78L163 77L163 76L162 75L163 75L163 74L164 74L163 73L164 73L164 72L164 71L164 70L163 69L163 68L164 67L164 66L165 66L165 65L165 64L166 64L166 63L167 62L168 62L169 61L170 61L170 60L170 59L171 58L171 57L172 56L173 56L174 55L174 54L175 54L175 55L176 55L177 55L178 55L179 54L180 54L181 54L181 53L183 53L185 53L186 53L187 53L188 53L190 53L191 53L191 54L191 55L190 56L191 56L191 57L193 57L194 57L195 58L196 59L197 59L198 59L199 60L200 59L200 58L201 57L202 57L203 57L203 58L204 58L205 58L206 58L207 59L208 59L209 59L210 59L211 58L212 59L213 59L214 59L215 58L214 58L215 58L215 57L215 56L216 55L216 54L216 53L215 53L214 54L213 54L212 53L211 53L210 54L209 53L208 53L207 52L206 52L207 51L206 51L207 50L209 50L209 49L211 49L212 48L214 48L215 48L217 49L218 49L220 49L222 48L221 47L220 47L219 46L218 45L217 45L218 44L218 43L219 43L218 43L217 43L216 43L215 44L216 45L217 45L216 45L215 45L214 46L213 45L214 45L212 45L213 44L214 44L213 44L212 44L212 43L211 43L210 44L210 45L209 45L209 46L208 47L208 48L209 49L208 49L207 49L206 50L206 49L205 49L204 49L204 50L203 50L203 51L204 51L204 52L203 52L203 53L203 54L202 54L202 53L201 52L201 51L200 51L200 50L199 50L199 49L200 48L199 48L198 48L198 47L197 47L196 46L195 46L195 45L194 45L194 44L193 44L192 45L193 46L194 46L194 47L195 48L196 48L197 49L198 49L198 50L197 50L196 50L197 51L196 52L196 51L196 50L195 50L195 49L194 49L193 49L192 48L191 48L191 47L190 46L189 46L188 46L187 46L187 47L185 47L183 47L183 48L182 49L181 49L180 50L180 51L180 52L179 52L179 53L178 53L177 53L176 53L175 54L174 54L173 53L172 53L171 53L171 52L170 51L171 51L171 50L171 49L171 48L171 47L172 46L173 46L175 46L176 47L178 47L179 46L179 44L178 43L177 42L176 42L175 41L177 41L178 41L178 40L179 41L181 40L182 39L183 39L184 38L185 37L186 36L187 37L187 36L188 36L189 36L189 35L188 34L188 33L189 33L190 33L191 32L191 33L190 33L191 34L190 34L190 35L191 36L192 36L193 36L194 36L195 36L196 35L198 35L199 35L199 36L200 36L200 35L201 35L201 34L201 33L202 33L203 32L203 33L204 33L204 32L203 31L205 31L206 30L207 31L208 31L209 30L208 29L206 30L204 30L203 30L202 30L201 29L202 28L201 27L202 27L202 26L205 25L205 24L204 24L202 24L201 25L201 26L200 26L198 27L197 29L198 29L199 30L198 31L197 31L196 33L196 34L195 34L194 35L193 35L193 34L192 33L191 31L190 31L188 32L187 32L186 31L185 30L185 28L186 27L189 27L191 26L192 24L195 22L196 21L199 20L201 20L203 20L205 19L206 19L208 19L211 20L210 20L211 20L212 20L214 21L217 21L220 22L221 23L220 24L218 24L214 23L213 23L215 24L215 26L216 26L217 26L217 25L220 25L222 24L223 24L224 24L225 23L224 23L224 22L223 21L226 22L227 22L226 22L226 23L228 23L228 22L230 22L234 21L233 22L235 22L237 22L239 21L240 22L241 21L240 20L241 20L244 20L245 21L249 22L249 21L248 21L247 21L247 20L247 19L249 18L249 17L250 17L253 17L253 18L252 19L253 20L253 21L254 22L253 22L251 24L252 24L253 23L254 23L255 22L254 22L255 21L254 21L254 20L254 19L253 19L255 18L255 17L256 18L255 19L256 19L256 18L258 18L260 18L262 18L261 17L261 16L262 16L265 16L267 16L266 16L267 15L268 15L270 14L273 14L276 14L277 14L279 14L281 14L281 13L282 13L284 12L286 13L285 13L287 13ZM229 49L230 49L230 50L229 51L229 52L230 53L231 53L232 53L234 53L234 52L234 51L233 51L233 50L233 49L234 49L235 49L234 48L233 48L233 49L233 48L232 48L233 48L233 47L231 47L231 46L230 46L230 45L231 45L232 45L233 45L233 44L233 43L232 43L231 43L230 43L229 44L228 44L227 45L228 46L227 47L229 48L229 49ZM86 12L86 13L84 12L86 12ZM70 12L68 13L66 12L67 12L69 12L70 12ZM205 12L202 13L201 12L203 12L205 12ZM70 11L69 12L67 12L67 11L68 11L69 11L70 11ZM84 12L83 12L82 12L81 12L81 11L83 11L84 12ZM80 12L79 12L77 12L75 12L76 11L75 11L76 11L79 11L80 12ZM285 12L279 12L281 11L282 11L283 11L285 11L285 12ZM198 10L202 11L199 11L198 12L197 13L196 13L194 13L195 12L193 12L191 11L190 10L193 10L194 10L195 10L196 10L197 10L198 10ZM205 10L207 10L206 10L203 11L200 10L198 10L197 10L200 9L202 10L203 9L205 10ZM231 9L230 10L229 10L228 10L227 10L227 9L225 9L227 9L228 9L229 9L230 9L232 9L231 9ZM280 11L278 11L275 11L273 11L273 10L271 10L274 9L276 9L278 9L280 10L280 11ZM93 10L94 11L93 11L91 12L89 12L87 12L86 11L87 11L85 11L84 10L83 10L84 9L85 9L86 9L85 9L88 9L89 9L91 9L92 10L93 10ZM112 7L114 7L116 7L118 7L118 8L116 8L113 8L112 8L115 8L112 9L111 9L109 10L107 10L106 11L103 11L104 11L105 11L104 12L102 12L100 13L102 13L99 14L97 14L94 14L92 14L91 14L90 13L92 13L92 12L95 12L94 12L92 12L93 11L95 11L93 10L96 10L97 10L98 10L96 9L92 9L91 9L90 9L89 8L88 8L90 8L91 8L93 8L95 7L96 7L97 8L98 7L99 7L101 7L104 7L107 7L109 7L112 7ZM153 6L159 7L157 8L153 8L148 8L149 8L152 8L155 8L157 8L158 8L157 9L159 8L164 8L167 8L168 9L164 9L163 10L160 10L162 10L161 11L160 11L160 12L162 13L160 13L158 13L160 14L160 15L159 15L161 16L158 16L160 16L159 17L158 17L156 17L158 17L158 18L156 17L155 18L157 18L158 19L156 20L156 19L154 19L155 19L154 20L156 20L158 20L155 21L152 22L149 22L148 22L147 22L146 23L144 24L143 24L142 24L140 25L139 25L139 26L139 27L137 27L138 28L137 29L137 30L135 30L134 29L132 29L131 29L130 28L128 26L128 25L126 24L127 23L126 23L127 22L129 21L129 20L128 20L127 21L125 20L126 19L127 19L129 19L127 19L126 18L125 19L124 18L125 17L124 16L123 15L121 15L121 14L119 14L117 14L114 14L111 14L110 14L109 13L111 13L113 13L109 12L107 12L111 11L114 11L115 10L112 10L113 9L116 9L118 9L117 8L120 8L123 8L126 8L127 8L130 8L132 8L133 8L135 8L133 8L133 7L137 7L140 7L141 6L145 6L153 6Z';\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport { planetGlyph } from '../tokens/index.js';\nimport type { BhavChalitResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { formatNumber } from '../utils/format.js';\nimport { frameCaptionStyles, renderFrameCaption } from '../utils/frame.js';\nimport { houseWords } from '../utils/house-themes.js';\n\ntype Graha = BhavChalitResponse['grahas'][number];\ntype Bhava = BhavChalitResponse['bhavas'][number];\n\n/**\n * Bhav Chalit: which house each graha actually falls in once the bhavas are cut at their real cusps rather than at sign boundaries.\n *\n * @remarks\n * **The reading is `moved`, and everything else is supporting detail.** A Bhav Chalit chart is opened to answer one question: does any graha change house between the whole-sign Rashi chart and the unequal Sripati cusps? So the count leads, the grahas that moved are called out with both placements, and the bhava spans sit underneath as the evidence.\n *\n * **Zero moved is a normal, meaningful result and must read as one.** It means the two charts agree, not that the request failed or that data is missing, so the empty case gets a sentence saying exactly that rather than an empty list.\n *\n * Bhava spans are rarely 30 degrees. The Ascendant and Midheaven are only 90 degrees apart at the equator and diverge sharply with latitude, so unequal spans are the expected result and the width is shown per bhava rather than assumed.\n *\n * House meanings come from `houseThemes` through the shared `houseWords` helper; this component holds no table of house significations.\n */\n@customElement('roxy-bhav-chalit-table')\nexport class RoxyBhavChalitTable extends RoxyDataElement<BhavChalitResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tframeCaptionStyles,\n\t\tcss`.wrap{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.head{gap:var(--roxy-space-xs,.25rem);display:grid}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.lede{font-size:var(--roxy-text-sm,.875rem);line-height:var(--roxy-leading-normal,1.5);margin:0}.moved-list{gap:var(--roxy-space-xs,.25rem);display:grid}.moved-row{align-items:baseline;gap:var(--roxy-space-sm,.5rem);font-size:var(--roxy-text-sm,.875rem);padding:var(--roxy-space-sm,.5rem);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-sm,4px);background:color-mix(in srgb, var(--roxy-warning,#f59e0b) 8%, transparent);flex-wrap:wrap;display:flex}.graha{font-weight:var(--roxy-weight-bold,600)}.glyph{color:var(--roxy-muted,#71717a);margin-right:.35em}.shift{font-variant-numeric:tabular-nums;color:var(--roxy-fg,#0a0a0a)}.themes{color:var(--roxy-secondary,#475569);font-size:var(--roxy-text-xs,.75rem)}table{border-collapse:collapse;width:100%;font-size:var(--roxy-text-sm,.875rem)}.scroll{-webkit-overflow-scrolling:touch;min-width:0;overflow-x:auto}th,td{padding:var(--roxy-space-sm,.5rem);border-bottom:1px solid var(--roxy-border,#e4e4e7);text-align:left;white-space:nowrap}th{color:var(--roxy-muted,#71717a);font-weight:var(--roxy-weight-bold,600);text-transform:uppercase;font-size:var(--roxy-text-xs,.75rem);letter-spacing:.06em}td.num{font-variant-numeric:tabular-nums}td.occupants{white-space:normal;color:var(--roxy-secondary,#475569)}summary{cursor:pointer;color:var(--roxy-accent-ink,#b45309);font-size:var(--roxy-text-sm,.875rem);font-weight:500}`,\n\t];\n\n\tprotected renderData(d: BhavChalitResponse) {\n\t\tconst grahas = d.grahas ?? [];\n\t\tconst bhavas = d.bhavas ?? [];\n\t\tif (!grahas.length && !bhavas.length) return this.renderEmpty();\n\n\t\tconst moved = grahas.filter((g) => g.moved);\n\t\tconst count = d.movedCount ?? moved.length;\n\n\t\treturn html`<div class=\"wrap\" part=\"card\" aria-label=\"${this.t('Bhav Chalit')}\"><header class=\"head\" part=\"header\"><h2 class=\"title\">${this.t('Bhav Chalit')}</h2><p class=\"lede\">${\n\t\t\t\t\t\tcount === 0\n\t\t\t\t\t\t\t? this.t(\n\t\t\t\t\t\t\t\t\t'No graha changes house. The Rashi chart and the Chalit chart agree, which is a normal result rather than a missing reading.',\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t: this.t(\n\t\t\t\t\t\t\t\t\t'{{count}} of {{total}} grahas change house between the Rashi chart and the unequal Sripati cusps.',\n\t\t\t\t\t\t\t\t\t{ count, total: grahas.length },\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t}</p>${renderFrameCaption(this.effectiveLang(), d.frame, this.translator)}</header>${\n\t\t\t\tmoved.length\n\t\t\t\t\t? html`<div class=\"moved-list\" part=\"table moved\">${moved.map((g) => this.renderMoved(g, d.houseThemes))}</div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\tbhavas.length\n\t\t\t\t\t? html`<details part=\"details\"><summary>${this.t('Bhava cusps and occupants')}</summary><div class=\"scroll\"><table part=\"table\"><thead><tr><th>${this.t('Bhava')}</th><th>${this.t('Rashi')}</th><th>${this.t('Start')}</th><th>${this.t('Madhya')}</th><th>${this.t('End')}</th><th>${this.t('Span')}</th><th>${this.t('Grahas')}</th></tr></thead><tbody>${bhavas.map((b) => this.renderBhavaRow(b))}</tbody></table></div></details>`\n\t\t\t\t\t: nothing\n\t\t\t}</div>`;\n\t}\n\n\tprivate renderMoved(g: Graha, themes: BhavChalitResponse['houseThemes']) {\n\t\tconst glyph = planetGlyph(g.graha) ?? '';\n\t\tconst words =\n\t\t\ttypeof g.bhava === 'number' ? houseWords([g.bhava], themes) : '';\n\t\treturn html`<div class=\"moved-row\"><span class=\"graha\">${glyph ? html`<span class=\"glyph\">${glyph}</span>` : nothing}${g.graha} </span><span class=\"shift\">${this.t('house {{from}} in the Rashi chart, house {{to}} here', {\n\t\t\t\t\tfrom: g.rashiHouse ?? '',\n\t\t\t\t\tto: g.bhava ?? '',\n\t\t\t\t})} </span>${words ? html`<span class=\"themes\">${words}</span>` : nothing}</div>`;\n\t}\n\n\tprivate renderBhavaRow(b: Bhava) {\n\t\tconst occupants = (b.grahas ?? []).join(', ');\n\t\treturn html`<tr><td class=\"num\">${b.house}</td><td>${b.rashi}</td><td class=\"num\">${formatNumber(this.effectiveLang(), b.start, 2)}°</td><td class=\"num\">${formatNumber(this.effectiveLang(), b.madhya, 2)}°</td><td class=\"num\">${formatNumber(this.effectiveLang(), b.end, 2)}°</td><td class=\"num\">${formatNumber(this.effectiveLang(), b.span, 2)}°</td><td class=\"occupants\">${occupants || '—'}</td></tr>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-bhav-chalit-table': RoxyBhavChalitTable;\n\t}\n}\n", "import type { GetCurrentDashaResponse } from '../types/index.js';\n\n/**\n * The `houseThemes` map every themed Vedic response carries: house number as a string key, significations ordered primary first. Present on the six Vimshottari dasha routes, `kp/chart`, `kp/cusps`, `kp/ruling-planets` (only when birth data was sent, hence the `undefined` arm) and `kp/ruling-planets-interval` (once at the response root, never per interval).\n *\n * @remarks\n * The `focus` query parameter chooses which vocabulary the API fills this map with (`general` significations, or the `finance` reading of the same twelve bhavas) and `lang` translates it, so the caller has already chosen both by the time a component sees the response. A component therefore reads this map and holds no table of house meanings of its own.\n */\nexport type HouseThemes = GetCurrentDashaResponse['houseThemes'] | undefined;\n\n/**\n * One primary keyword per house, \"gains, enemies\" for houses 11 and 6. Turns a bare house list (a dasha period's significators, a KP planet's `signifies`) into a phrase that fits on one line beside it.\n *\n * The words come from the response map and nowhere else, so a Hindi response renders Hindi houses and a `focus=finance` request renders the money reading. Only the FIRST theme per house is used: these call sites carry up to eight houses, and a full list per house would wrap past its row.\n *\n * Returns an empty string when the map is absent (the request did not ask for it) or no house is signified, so a caller can render nothing without a second guard.\n */\nexport function houseWords(\n\thouses: readonly number[] | undefined,\n\tthemes: HouseThemes,\n): string {\n\tif (!themes || !houses?.length) return '';\n\treturn houses\n\t\t.map((h) => themes[String(h)]?.[0])\n\t\t.filter(Boolean)\n\t\t.join(', ');\n}\n\n/** Every keyword for ONE house, \"self, body, vitality\". For a per-house row (a KP cusp, a house-wise significator) where the row IS the house and there is room for the whole set. */\nexport function houseThemeLine(\n\thouse: number | undefined,\n\tthemes: HouseThemes,\n): string {\n\tif (!themes || typeof house !== 'number') return '';\n\treturn (themes[String(house)] ?? []).join(', ');\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport type { ChromeString } from '../i18n/chrome-strings.js';\nimport type { BhavaBalaResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { formatNumber } from '../utils/format.js';\nimport { frameCaptionStyles, renderFrameCaption } from '../utils/frame.js';\nimport { houseWords } from '../utils/house-themes.js';\n\ntype Bhava = BhavaBalaResponse['bhavas'][number];\n\n/**\n * The three components that sum to the total, in the order the tradition states them. Each is a FIELD on the response, so this list names what to read and never holds a value.\n */\nconst COMPONENTS: ReadonlyArray<{\n\tkey: keyof Bhava;\n\tsource: ChromeString;\n\tcolor: string;\n}> = [\n\t{\n\t\tkey: 'bhavadhipatiBala',\n\t\tsource: 'Bhavadhipati',\n\t\tcolor: 'var(--roxy-accent, #f59e0b)',\n\t},\n\t{ key: 'digBala', source: 'Dig', color: 'var(--roxy-info, #0284c7)' },\n\t{\n\t\tkey: 'drishtiBala',\n\t\tsource: 'Drishti',\n\t\tcolor: 'var(--roxy-success, #16a34a)',\n\t},\n];\n\n/**\n * Bhava Bala: the strength of each of the twelve houses, ranked, with the three classical components that make up the total.\n *\n * @remarks\n * Read in RUPAS, not virupas, because rupas are the conventional unit a practitioner compares against and 60 virupas make one rupa. The virupa total is kept beside it since that is what the ranking is computed on.\n *\n * **The bar is proportional to the total across all twelve houses, never scaled to each row's own maximum.** Scaling per row would make the weakest bhava look as full as the strongest, which inverts the one thing this table exists to show. Same rule as the Shadbala bars.\n *\n * House meanings come from `houseThemes` on the response and are read through the shared `houseWords` helper, so this component holds no table of house significations of its own. The API has already chosen the vocabulary through `focus` and the language through `lang` by the time the response arrives.\n */\n@customElement('roxy-bhava-bala-table')\nexport class RoxyBhavaBalaTable extends RoxyDataElement<BhavaBalaResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tframeCaptionStyles,\n\t\tcss`.wrap{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.head{gap:var(--roxy-space-xs,.25rem);display:grid}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.subtitle{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem);margin:0}.rows{gap:var(--roxy-space-sm,.5rem);display:grid}.row{grid-template-columns:minmax(0,1fr);gap:2px;display:grid}.row-top{align-items:baseline;gap:var(--roxy-space-sm,.5rem);font-size:var(--roxy-text-sm,.875rem);flex-wrap:wrap;display:flex}.house{font-weight:var(--roxy-weight-bold,600);min-width:4.5rem}.rashi{color:var(--roxy-secondary,#475569)}.lord{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem)}.total{font-variant-numeric:tabular-nums;font-weight:var(--roxy-weight-bold,600);margin-left:auto}.rank{font-size:var(--roxy-text-xs,.75rem);border-radius:var(--roxy-radius-sm,4px);border:1px solid var(--roxy-border,#e4e4e7);font-variant-numeric:tabular-nums;padding:.05rem .4rem}.bar{border-radius:var(--roxy-radius-full,9999px);background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 55%, transparent);height:10px;display:flex;overflow:hidden}.seg{height:100%}.themes{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);line-height:var(--roxy-leading-normal,1.5);margin:0}.legend{gap:var(--roxy-space-xs,.25rem) var(--roxy-space-md,1rem);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);flex-wrap:wrap;display:flex}.legend-row{align-items:center;gap:4px;display:flex}.swatch{border-radius:2px;width:14px;height:9px}`,\n\t];\n\n\tprotected renderData(d: BhavaBalaResponse) {\n\t\tconst bhavas = d.bhavas ?? [];\n\t\tif (!bhavas.length) return this.renderEmpty();\n\n\t\t// One scale for every row, taken from the strongest bhava, so the bars\n\t\t// compare against each other rather than each against itself.\n\t\tconst peak = Math.max(...bhavas.map((b) => b.totalVirupas ?? 0), 1);\n\n\t\treturn html`<div class=\"wrap\" part=\"card\" aria-label=\"${this.t('Bhava Bala')}\"><header class=\"head\" part=\"header\"><h2 class=\"title\">${this.t('Bhava Bala')}</h2><p class=\"subtitle\">${\n\t\t\t\t\t\td.houseSystem\n\t\t\t\t\t\t\t? this.t(\n\t\t\t\t\t\t\t\t\t'Twelve houses ranked by strength on the {{system}} frame',\n\t\t\t\t\t\t\t\t\t{ system: d.houseSystem },\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t: this.t('Twelve houses ranked by strength')\n\t\t\t\t\t}</p>${renderFrameCaption(this.effectiveLang(), d.frame, this.translator)}</header><div class=\"rows\" part=\"table\" role=\"list\">${bhavas.map((b) => this.renderBhava(b, peak, d.houseThemes))}</div><div class=\"legend\" part=\"legend\" aria-label=\"${this.t('Component legend')}\">${COMPONENTS.map(\n\t\t\t\t\t(c) => html`<div class=\"legend-row\"><span class=\"swatch\" style=\"background:${c.color}\" aria-hidden=\"true\"></span> ${this.t('{{component}} Bala', { component: this.t(c.source) })}</div>`,\n\t\t\t\t)}</div></div>`;\n\t}\n\n\tprivate renderBhava(\n\t\tb: Bhava,\n\t\tpeak: number,\n\t\tthemes: BhavaBalaResponse['houseThemes'],\n\t) {\n\t\tconst words =\n\t\t\ttypeof b.house === 'number' ? houseWords([b.house], themes) : '';\n\n\t\treturn html`<article class=\"row\" role=\"listitem\"><div class=\"row-top\"><span class=\"house\">${this.t('House {{n}}', { n: b.house })}</span> <span class=\"rashi\">${b.rashi}</span> ${b.lord ? html`<span class=\"lord\">${this.t('lord {{graha}}', { graha: b.lord })}</span>` : nothing} <span class=\"total\">${this.t('{{value}} rupas', { value: formatNumber(this.effectiveLang(), b.totalRupas, 2) })} ${\n\t\t\t\t\t\ttypeof b.rank === 'number'\n\t\t\t\t\t\t\t? html`<span class=\"rank\">#${b.rank}</span>`\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t}</span></div><div class=\"bar\" role=\"img\" aria-label=\"${this.t('Bhava Bala {{value}} virupas', { value: formatNumber(this.effectiveLang(), b.totalVirupas, 1) })}\">${COMPONENTS.map((c) => {\n\t\t\t\t\tconst v = Math.max(0, (b[c.key] as number | undefined) ?? 0);\n\t\t\t\t\treturn v > 0\n\t\t\t\t\t\t? html`<span class=\"seg\" style=\"width:${(v / peak) * 100}%;background:${c.color}\" title=\"${this.t('{{component}} {{value}} virupas', { component: this.t(c.source), value: formatNumber(this.effectiveLang(), v, 1) })}\"></span>`\n\t\t\t\t\t\t: nothing;\n\t\t\t\t})}</div>${words ? html`<p class=\"themes\">${words}</p>` : nothing}</article>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-bhava-bala-table': RoxyBhavaBalaTable;\n\t}\n}\n", "import { css, html, nothing, svg } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport type { ChromeString } from '../i18n/chrome-strings.js';\nimport type {\n\tGetCriticalDaysResponse,\n\tGetDailyBiorhythmResponse,\n\tGetForecastResponse,\n} from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { disclosureStyles } from '../utils/disclosure.js';\nimport { formatDate, formatDateRange, formatPercent } from '../utils/format.js';\nimport {\n\ttype InterpSection,\n\tinterpAccordionStyles,\n} from '../utils/interp-accordion.js';\nimport { humanize } from '../utils/string.js';\n\ntype BiorhythmData =\n\t| GetDailyBiorhythmResponse\n\t| GetForecastResponse\n\t| GetCriticalDaysResponse;\n\nconst CYCLE_COLOR: Record<string, string> = {\n\tphysical: '#dc2626',\n\temotional: '#0284c7',\n\tintellectual: '#16a34a',\n\tintuitive: '#a855f7',\n\taesthetic: '#f59e0b',\n\tawareness: '#ec4899',\n\tspiritual: '#14b8a6',\n\tpassion: '#ef4444',\n\tmastery: '#6366f1',\n\twisdom: '#475569',\n};\n\n/** The cycles a forecast day carries, in the order they are plotted and keyed in the legend. */\nconst FORECAST_CYCLES = [\n\t'physical',\n\t'emotional',\n\t'intellectual',\n\t'intuitive',\n] as const;\n\n/** Response key to the English SOURCE its legend entry is looked up by. The key indexes the payload and stays lower case; the label a reader sees does not. */\nconst CYCLE_LABEL: Record<(typeof FORECAST_CYCLES)[number], ChromeString> = {\n\tphysical: 'Physical',\n\temotional: 'Emotional',\n\tintellectual: 'Intellectual',\n\tintuitive: 'Intuitive',\n};\n\n/**\n * Biorhythm chart. Renders /biorhythm/{daily,forecast,critical-days}.\n */\n@customElement('roxy-biorhythm-chart')\nexport class RoxyBiorhythmChart extends RoxyDataElement<BiorhythmData> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tdisclosureStyles,\n\t\tinterpAccordionStyles,\n\t\tcss`.wrap{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.head{justify-content:space-between;align-items:center;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.head-meta{align-items:center;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.energy{font-variant-numeric:tabular-nums;color:var(--roxy-accent-ink,#b45309);font-weight:var(--roxy-weight-bold,600)}.phase{background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 16%, transparent);color:var(--roxy-fg,#0a0a0a);border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);padding:2px 10px}.spotlight{gap:var(--roxy-space-xs,.25rem);padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);border-left:3px solid var(--roxy-accent,#f59e0b);background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 22%, transparent);border-radius:0 var(--roxy-radius-sm,4px) var(--roxy-radius-sm,4px) 0;display:grid}.spotlight .label{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.06em;margin:0}.spotlight .lead{align-items:baseline;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.spotlight strong{text-transform:capitalize}.spotlight p{font-size:var(--roxy-text-sm,.875rem);margin:0;line-height:1.6}.bars{gap:var(--roxy-space-xs,.25rem);display:grid}.bar{gap:var(--roxy-space-sm,.5rem);font-size:var(--roxy-text-sm,.875rem);grid-template-columns:8rem 1fr 3.5rem;align-items:center;display:grid}.track{background:var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-full,9999px);height:14px;position:relative;overflow:hidden}.fill{height:100%;transition:width var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,cubic-bezier(.4, 0, .2, 1));display:block}.value{font-variant-numeric:tabular-nums;text-align:right;color:var(--roxy-muted,#71717a)}.advice{color:var(--roxy-fg,#0a0a0a);margin:0}svg{width:100%;max-width:var(--roxy-chart-max-width,600px);height:auto;display:block}.legend{gap:var(--roxy-space-md,1rem);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-fg,#0a0a0a);flex-wrap:wrap;display:flex}.key{text-transform:capitalize;align-items:center;gap:.35rem;display:inline-flex}.dot{border-radius:var(--roxy-radius-full,9999px);width:.6rem;height:.6rem}.dot.critical{background:var(--roxy-danger,#dc2626)}.stats{gap:var(--roxy-space-sm,.5rem);grid-template-columns:repeat(auto-fit,minmax(7rem,1fr));margin:0;display:grid}.stat{gap:2px;display:grid}.stat dt{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.05em}.stat dd{font-size:var(--roxy-text-sm,.875rem);font-weight:var(--roxy-weight-bold,600);font-variant-numeric:tabular-nums;margin:0}.crit-note{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a);background:color-mix(in srgb, var(--roxy-danger,#dc2626) 10%, transparent);border-radius:var(--roxy-radius-sm,4px);padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);margin:0}.crit-meta{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);margin:0}`,\n\t];\n\n\t@property({ type: String, reflect: true })\n\tmode: 'daily' | 'forecast' | 'critical-days' = 'daily';\n\n\tprotected renderData(d: BiorhythmData) {\n\t\tif (this.mode === 'critical-days' && 'criticalDays' in d) {\n\t\t\treturn this.renderCritical(d as GetCriticalDaysResponse);\n\t\t}\n\t\tif (this.mode === 'forecast' && 'days' in d) {\n\t\t\treturn this.renderForecast(d as GetForecastResponse);\n\t\t}\n\t\treturn this.renderDaily(d as GetDailyBiorhythmResponse);\n\t}\n\n\tprivate renderDaily(d: GetDailyBiorhythmResponse) {\n\t\tconst raw = d.quickRead ?? {};\n\t\tconst entries = Object.entries(raw).map(([cycle, value]) => {\n\t\t\tconst v = typeof value === 'number' ? value : 0;\n\t\t\tconst normalized = Math.abs(v) > 1 ? v / 100 : v;\n\t\t\treturn [cycle, normalized] as const;\n\t\t});\n\t\tconst spot = d.spotlight;\n\n\t\treturn html`<section class=\"wrap\" part=\"card\" aria-label=\"${this.t('Daily biorhythm')}\"><header class=\"head\" part=\"header\"><h2 class=\"title\">${this.t('Biorhythm')}</h2><div class=\"head-meta\">${d.overallPhase ? html`<span class=\"phase\">${humanize(d.overallPhase)}</span>` : nothing} ${\n\t\t\t\t\t\ttypeof d.energyRating === 'number'\n\t\t\t\t\t\t\t? html`<span class=\"energy\">${this.t('Energy {{value}}/10', { value: d.energyRating })}</span>`\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t}</div></header>${\n\t\t\t\tspot\n\t\t\t\t\t? html`<div class=\"spotlight\" part=\"details spotlight\" style=\"${`border-left-color: ${CYCLE_COLOR[spot.cycle] ?? 'var(--roxy-accent, #f59e0b)'}`}\"><p class=\"label\">${this.t('Spotlight cycle')}</p><div class=\"lead\"><strong>${humanize(spot.cycle)}</strong> ${typeof spot.value === 'number' ? html`<span class=\"energy\">${formatPercent(this.effectiveLang(), spot.value, 0)}</span>` : nothing} ${spot.phase ? html`<span class=\"phase\">${humanize(spot.phase)}</span>` : nothing}</div>${\n\t\t\t\t\t\t\t// The cycle, its value and its phase are the reading's subject;\n\t\t\t\t\t\t\t// the message is the reading.\n\t\t\t\t\t\t\tspot.message && !this.hideReadings\n\t\t\t\t\t\t\t\t? html`<p>${spot.message}</p>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t}</div>`\n\t\t\t\t\t: nothing\n\t\t\t}<div class=\"bars\" part=\"chart bars\" role=\"list\">${entries.map(([cycle, v]) => {\n\t\t\t\t\tconst pct = ((v + 1) / 2) * 100; // -1..1 -> 0..100\n\t\t\t\t\tconst color = CYCLE_COLOR[cycle] ?? 'var(--roxy-accent, #f59e0b)';\n\t\t\t\t\treturn html`<div class=\"bar\" role=\"listitem\"><span style=\"text-transform:capitalize\">${cycle}</span> <span class=\"track\"><span class=\"fill\" style=\"width:${pct}%;background:${color}\"></span> </span><span class=\"value\">${formatPercent(this.effectiveLang(), v * 100, 0)}</span></div>`;\n\t\t\t\t})}</div>${\n\t\t\t\tthis.hideReadings\n\t\t\t\t\t? nothing\n\t\t\t\t\t: html`${d.dailyMessage ? html`<p class=\"advice\">${d.dailyMessage}</p>` : nothing} ${d.advice ? html`<p class=\"advice\">${d.advice}</p>` : nothing}`\n\t\t\t}</section>`;\n\t}\n\n\tprivate renderForecast(d: GetForecastResponse) {\n\t\tconst days = d.days ?? [];\n\t\tif (days.length === 0)\n\t\t\treturn html`<div class=\"roxy-empty\" role=\"status\">${this.t('No forecast')}</div>`;\n\t\tconst w = 600;\n\t\tconst h = 160;\n\t\tconst xStep = w / Math.max(days.length - 1, 1);\n\t\tconst s = d.summary;\n\n\t\treturn html`<section class=\"wrap\" part=\"card\" aria-label=\"${this.t('Biorhythm forecast')}\"><header class=\"head\" part=\"header\"><h2 class=\"title\">${this.t('Forecast')}</h2><span class=\"energy\">${formatDateRange(this.effectiveLang(), d.startDate, d.endDate)}</span></header><svg viewBox=\"0 0 ${w} ${h}\" part=\"chart\" role=\"img\" aria-label=\"${this.t('Biorhythm cycle lines across the forecast window')}\"><title>${this.t('Biorhythm forecast')}</title><line x1=\"0\" y1=\"${h / 2}\" x2=\"${w}\" y2=\"${h / 2}\" stroke=\"var(--roxy-border, #e4e4e7)\" stroke-width=\"1\"/>${FORECAST_CYCLES.map((cycle) => {\n\t\t\t\t\tconst points = days\n\t\t\t\t\t\t.map((day, i) => {\n\t\t\t\t\t\t\tconst v = day[cycle] ?? 0;\n\t\t\t\t\t\t\tconst x = i * xStep;\n\t\t\t\t\t\t\tconst y = h / 2 - (v / 100) * (h / 2 - 8);\n\t\t\t\t\t\t\t// Path coordinates, not copy: a decimal comma here IS the\n\t\t\t\t\t\t\t// separator and the polyline collapses.\n\t\t\t\t\t\t\treturn `${x.toFixed(2)},${y.toFixed(2)}`;\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.join(' ');\n\t\t\t\t\tconst color = CYCLE_COLOR[cycle] ?? '#475569';\n\t\t\t\t\treturn svg`<polyline points=${points} fill=\"none\" stroke=${color} stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />`;\n\t\t\t\t})} ${days.map((day, i) =>\n\t\t\t\t\t// A critical day is a zero crossing, so it sits on the midline by\n\t\t\t\t\t// definition: mark it there rather than hiding it in the curves.\n\t\t\t\t\tday.isCritical\n\t\t\t\t\t\t? svg`<circle cx=${(i * xStep).toFixed(2)} cy=${h / 2} r=\"3\" fill=\"var(--roxy-danger, #dc2626)\"><title>${[\n\t\t\t\t\t\t\t\tformatDate(this.effectiveLang(), day.date),\n\t\t\t\t\t\t\t\tday.criticalCycles?.length\n\t\t\t\t\t\t\t\t\t? `${day.criticalCycles.join(', ')} critical`\n\t\t\t\t\t\t\t\t\t: 'critical',\n\t\t\t\t\t\t\t\ttypeof day.energyRating === 'number'\n\t\t\t\t\t\t\t\t\t? `energy ${day.energyRating}/10`\n\t\t\t\t\t\t\t\t\t: '',\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t\t\t\t.join(' · ')}</title></circle>`\n\t\t\t\t\t\t: nothing,\n\t\t\t\t)}</svg><div class=\"legend\" part=\"legend\">${FORECAST_CYCLES.map(\n\t\t\t\t\t(cycle) => html`<span class=\"key\"><span class=\"dot\" style=\"${`background: ${CYCLE_COLOR[cycle]}`}\"></span>${this.t(CYCLE_LABEL[cycle])}</span>`,\n\t\t\t\t)} <span class=\"key\"><span class=\"dot critical\"></span>${this.t('critical day')}</span></div>${\n\t\t\t\ts\n\t\t\t\t\t? html`<dl class=\"stats\" part=\"details\">${this.stat(this.t('Best day'), formatDate(this.effectiveLang(), s.bestDay))} ${this.stat(this.t('Worst day'), formatDate(this.effectiveLang(), s.worstDay))} ${this.stat(\n\t\t\t\t\t\t\tthis.t('Average energy'),\n\t\t\t\t\t\t\ttypeof s.averageEnergy === 'number'\n\t\t\t\t\t\t\t\t? `${s.averageEnergy}/10`\n\t\t\t\t\t\t\t\t: '',\n\t\t\t\t\t\t)} ${this.stat(\n\t\t\t\t\t\t\tthis.t('Critical days'),\n\t\t\t\t\t\t\ttypeof s.criticalDayCount === 'number'\n\t\t\t\t\t\t\t\t? `${s.criticalDayCount}`\n\t\t\t\t\t\t\t\t: '',\n\t\t\t\t\t\t)}</dl>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\ts?.periodAdvice && !this.hideReadings\n\t\t\t\t\t? html`<p class=\"advice\">${s.periodAdvice}</p>`\n\t\t\t\t\t: nothing\n\t\t\t}</section>`;\n\t}\n\n\tprivate renderCritical(d: GetCriticalDaysResponse) {\n\t\tconst days = d.criticalDays ?? [];\n\t\tconst doubles = d.doubleCriticalDays ?? [];\n\t\tconst sections: InterpSection[] = days.map((day) => ({\n\t\t\tlabel: formatDate(this.effectiveLang(), day.date) || day.date,\n\t\t\taside: [day.cycle, day.severity].filter(Boolean).join(' · '),\n\t\t\tbody: day.advisory ?? '',\n\t\t\textra: html`<p class=\"crit-meta\">${[\n\t\t\t\t\tday.direction ? `${day.direction} through zero` : '',\n\t\t\t\t\ttypeof day.period === 'number' ? `${day.period} day cycle` : '',\n\t\t\t\t]\n\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t.join(' · ')}</p>`,\n\t\t}));\n\n\t\treturn html`<section class=\"wrap\" part=\"card\" aria-label=\"${this.t('Critical days')}\"><header class=\"head\" part=\"header\"><h2 class=\"title\">${this.t('Critical days')}</h2><span class=\"energy\">${formatDateRange(this.effectiveLang(), d.startDate, d.endDate)}</span></header><dl class=\"stats\" part=\"details\">${this.stat(this.t('Events'), typeof d.totalCriticalDays === 'number' ? `${d.totalCriticalDays}` : '')} ${this.stat(this.t('Double days'), doubles.length ? `${doubles.length}` : '0')} ${this.stat(\n\t\t\t\t\tthis.t('Triple day'),\n\t\t\t\t\td.tripleCriticalDay\n\t\t\t\t\t\t? formatDate(this.effectiveLang(), d.tripleCriticalDay)\n\t\t\t\t\t\t: 'None in range',\n\t\t\t\t)}</dl>${\n\t\t\t\tdoubles.length > 0\n\t\t\t\t\t? html`<p class=\"crit-note\">${this.t('Two or more cycles cross zero on {{dates}}. Take extra care on these dates.', { dates: doubles.map((x) => formatDate(this.effectiveLang(), x) || x).join(', ') })}</p>`\n\t\t\t\t\t: nothing\n\t\t\t} ${this.renderInterpretation(sections, 'biorhythm-critical', 'Advisories')}</section>`;\n\t}\n\n\tprivate stat(label: string, value: string) {\n\t\tif (!value) return nothing;\n\t\treturn html`<div class=\"stat\"><dt>${label}</dt><dd>${value}</dd></div>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-biorhythm-chart': RoxyBiorhythmChart;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement, state } from 'lit/decorators.js';\nimport { planetGlyph } from '../tokens/index.js';\nimport type { GenerateBodygraphResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport {\n\tBODYGRAPH_VIEWBOX,\n\ttype BodygraphCenterId,\n\tCENTER_GEOMETRY,\n\ttype CenterColor,\n\ttype GateSources,\n\trenderBodygraphSvg,\n} from '../utils/bodygraph-render.js';\nimport { bodygraphChartStyles } from '../utils/bodygraph-styles.js';\nimport { chevron, disclosureStyles } from '../utils/disclosure.js';\nimport {\n\thdReadingStyles,\n\ttype ReadingSection,\n\trenderHdFacts,\n\trenderHdKeynotes,\n\trenderHdThemes,\n\tsideWord,\n} from '../utils/hd-reading.js';\nimport { interpAccordionStyles } from '../utils/interp-accordion.js';\nimport { display } from '../utils/localized.js';\nimport { renderTablist, tablistStyles } from '../utils/tablist.js';\n\ntype Bodygraph = GenerateBodygraphResponse;\ntype GateActivation = Bodygraph['gates'][number];\ntype CenterEntry = Bodygraph['centers'][number];\ntype ChannelEntry = Bodygraph['channels'][number];\n\n/**\n * The order the color key reads in, which is the order the centers themselves are colored in from the crown down: the two identity centers, the mental awareness center, the pressure and expression group, then the two life-force motors.\n *\n * @remarks\n * The four rows and the names inside them are DERIVED from {@link CENTER_GEOMETRY} and the response rather than written out, so a legend can never claim a color for a center the chart draws in another one, and the names in the key are the same words the accordion below prints.\n */\nconst LEGEND_COLORS: readonly CenterColor[] = ['gold', 'green', 'brown', 'red'];\n\n/** The activations on each gate, in response order. One gate carries several whenever both sides reach it or two bodies share it, which is the case the chart draws as a split circle. */\nfunction groupByGate(gates: GateActivation[]): Map<number, GateActivation[]> {\n\tconst byGate = new Map<number, GateActivation[]>();\n\tfor (const g of gates) {\n\t\tif (g.gate == null) continue;\n\t\tconst bucket = byGate.get(g.gate);\n\t\tif (bucket) bucket.push(g);\n\t\telse byGate.set(g.gate, [g]);\n\t}\n\treturn byGate;\n}\n\n/**\n * Human Design bodygraph. Pass `data` from /human-design/bodygraph. Renders the nine centers in their canonical positions and shapes, filled when defined and surface-filled and outlined when open, the 36 channels as wiring between gates, and ALL 64 gates: outlined where nothing landed, filled where an activation did, and split down the middle where both sides did.\n *\n * @remarks\n * The response carries a full interpretation, not just labels, so the card is laid out in four passes from identity down to detail. The chart takes the full card width at every size and identity sits under it, never beside it, because a bodygraph is half again taller than it is wide and is read as a whole. Identity is always visible: the type, strategy, authority, profile, and definition tiles, the type description as the lead paragraph, the incarnation cross, and the signature and not-self themes. Everything below is progressive disclosure through the shared exclusive-accordion pattern, so only one body of prose is ever open at a time and the card never becomes a wall of text: the reading (strategy, authority, profile, definition, aura, cross), the defined channels grouped by circuit, the nine centers, and the 26 gate activations split by chart side.\n *\n * A center returns `notSelfQuestion` whatever its state, but the question describes the conditioning of an OPEN center, so it is rendered only when the center is open. `theme` already tracks the defined or open state and is always shown.\n *\n * **Every name on this card is read twice.** A translated response echoes the display value beside the canonical one (`nameLocalized` next to `name`, `planetLocalized` next to `planet`) and keeps the canonical one English in every language, so the reader gets `display(...)` and the machine keeps the English: `planetGlyph` is keyed on `planet`, the defined-center Set and the channel groups are keyed on `id` and `circuit`, and the tab state is the wire value `personality` or `design`. Two fields have no localized partner and print as sent, each with a note at its site: `incarnationCross.name` and `ichingHexagram.english`.\n *\n * **Personality is the dark half of a bodygraph and Design the red half, and the binary the reading rests on is drawn, not just labelled.** It comes from `gates[].side`, so nothing extra is requested: a gate circle takes the colour of the side or sides that activated it, and each half of a channel takes the colour of the gate at its own end, which is why a channel joining a Personality gate to a Design gate is half dark and half red. Literal black cannot survive a dark theme, so Personality follows `--roxy-fg` and inverts with it.\n *\n * The chart is theme-driven through `--roxy-*` custom properties on `:host`, so it adopts the host palette in light and dark without runtime color probing.\n *\n * `hide-readings` leaves the chart, the fact tiles, the incarnation cross, the themes and the legend, and drops the reading accordion together with the channels, centers and activations sections. Those three exist to hold the prose behind each disclosure, and the wiring they describe is already drawn in the chart above them: the gate circles, the lit channels, and the filled centers.\n *\n * Centre names live in each shape's `<title>`, which is the hover tooltip and the accessible name. No margin labels: they need leader lines across the drawing and no printed bodygraph carries either.\n */\n@customElement('roxy-bodygraph')\nexport class RoxyBodygraph extends RoxyDataElement<Bodygraph> {\n\t/** Which chart side the activations panel is showing. View state, not configuration: the response always carries both sides. */\n\t@state()\n\tprivate side: 'personality' | 'design' = 'personality';\n\n\tstatic styles = [\n\t\tbaseStyles,\n\t\ttablistStyles,\n\t\tdisclosureStyles,\n\t\tinterpAccordionStyles,\n\t\thdReadingStyles,\n\t\tbodygraphChartStyles,\n\t\tcss`.wrap{background:var(--roxy-surface,#fff);width:100%;color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.head{justify-content:space-between;align-items:baseline;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.type-line{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem)}.layout{gap:var(--roxy-space-lg,1.5rem);grid-template-columns:minmax(0,1fr);align-items:start;display:grid}.wrap{--src-left:var(--roxy-danger,#dc2626);--src-right:var(--roxy-fg,#0a0a0a)}.summary{gap:var(--roxy-space-md,1rem);display:grid}.cross{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a);border-left:2px solid var(--roxy-accent,#f59e0b);padding-left:var(--roxy-space-sm,.5rem);margin:0}.cross .gates{color:var(--roxy-muted,#71717a);font-variant-numeric:tabular-nums}.legend-row{color:var(--roxy-muted,#71717a);flex-basis:100%}.legend-row .swatch:not(:first-child){margin-left:var(--roxy-space-md,1rem)}.legend .swatch.defined{border-color:color-mix(in srgb, var(--roxy-fg,#0a0a0a) 45%, transparent)}.group{margin-bottom:var(--roxy-space-md,1rem)}.group:last-child{margin-bottom:0}.group-head{font-size:var(--roxy-text-sm,.875rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-fg,#0a0a0a);margin:0}.group-note{margin:0 0 var(--roxy-space-sm,.5rem);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);line-height:1.6}.footnote{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);margin:0;line-height:1.6}.chip{border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 45%, transparent);color:var(--roxy-fg,#0a0a0a);padding:1px 8px;display:inline-block}.chip--on{background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 18%, transparent)}.chips{flex-wrap:wrap;gap:.25rem;display:inline-flex}.gate-id,.chan-gates{font-variant-numeric:tabular-nums;font-weight:var(--roxy-weight-bold,600);color:var(--roxy-accent-ink,#b45309)}.glyph{color:var(--roxy-accent-ink,#b45309)}.side-note{margin:0 0 var(--roxy-space-sm,.5rem);padding-top:var(--roxy-space-sm,.5rem);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);line-height:1.6}`,\n\t];\n\n\tprotected renderData(d: Bodygraph) {\n\t\tconst centers = d.centers ?? [];\n\t\tconst definedCenters = new Set<BodygraphCenterId>(\n\t\t\tcenters.filter((c) => c.defined).map((c) => c.id as BodygraphCenterId),\n\t\t);\n\t\tconst byGate = groupByGate(d.gates ?? []);\n\t\t// Design is the left half of a split circle and Personality the right, which\n\t\t// is the column order a printed chart prints them in.\n\t\tconst gateSources = new Map<number, GateSources>(\n\t\t\t[...byGate].map(([gate, list]) => [\n\t\t\t\tgate,\n\t\t\t\t{\n\t\t\t\t\tleft: list.some((g) => g.side === 'design'),\n\t\t\t\t\tright: list.some((g) => g.side === 'personality'),\n\t\t\t\t},\n\t\t\t]),\n\t\t);\n\t\tconst gateTitles = this.buildGateTitles(byGate);\n\t\t// The chart reads the same names the accordion does, so one card cannot name a\n\t\t// centre two ways. The set above stays keyed on `id`, the machine value, which\n\t\t// is `solar-plexus` in every language.\n\t\tconst centerNames = new Map<BodygraphCenterId, string>(\n\t\t\tcenters.map((c) => [c.id as BodygraphCenterId, display(c, 'name')]),\n\t\t);\n\n\t\treturn html`<div class=\"wrap\" part=\"card\"><header class=\"head\" part=\"header\"><h2 class=\"title\">${this.t('Bodygraph')}</h2>${\n\t\t\t\t\t// One text node, not two: the markup minifier collapses the leading\n\t\t\t\t\t// space of an adjacent template and the separator would lose it.\n\t\t\t\t\td.type || d.profile\n\t\t\t\t\t\t? html`<div class=\"type-line\">${[\n\t\t\t\t\t\t\t\tdisplay(d, 'type'),\n\t\t\t\t\t\t\t\td.profile\n\t\t\t\t\t\t\t\t\t? this.t('Profile {{profile}}', { profile: d.profile })\n\t\t\t\t\t\t\t\t\t: '',\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t\t\t\t.join(' · ')}</div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</header><div class=\"layout\" part=\"layout\"><svg class=\"chart\" part=\"chart\" viewBox=\"${BODYGRAPH_VIEWBOX}\" preserveAspectRatio=\"xMidYMid meet\" role=\"img\" aria-label=\"${this.t(\n\t\t\t\t\t\t'Human Design bodygraph with nine centers, channels, and activated gates overlaid on a human silhouette',\n\t\t\t\t\t)}\"><title>${this.t('Human Design bodygraph')}</title><desc>${this.t(\n\t\t\t\t\t\t\t'Nine energy centers in their canonical positions over a human silhouette, each filled with its traditional color when defined and outlined when open, wired by channels between activated gates.',\n\t\t\t\t\t\t)}</desc>${renderBodygraphSvg({\n\t\t\t\t\t\tdefinedCenters,\n\t\t\t\t\t\tgateSources,\n\t\t\t\t\t\tgateTitles,\n\t\t\t\t\t\tcenterNames,\n\t\t\t\t\t\tstateWords: { defined: this.t('Defined'), open: this.t('Open') },\n\t\t\t\t\t})}</svg> ${this.renderSummary(d, centerNames)}</div>${this.renderReading(d)} ${this.renderChannels(d.channels ?? [], centerNames)} ${this.renderCenters(centers)} ${this.renderActivations(d)}</div>`;\n\t}\n\n\t/** The hover and accessible text for one gate circle, naming every activation on it rather than one. */\n\tprivate buildGateTitles(\n\t\tbyGate: ReadonlyMap<number, GateActivation[]>,\n\t): Map<number, string> {\n\t\tconst titles = new Map<number, string>();\n\t\tfor (const [gate, list] of byGate) {\n\t\t\t// The line belongs to the activation, not the gate: two bodies on one gate\n\t\t\t// routinely land on different lines, so each carries its own `gate.line` the\n\t\t\t// way a printed planet column prints it.\n\t\t\tconst bodies = list.map((g) =>\n\t\t\t\t[\n\t\t\t\t\tthis.gateGlyph(g),\n\t\t\t\t\t`${gate}${g.line != null ? `.${g.line}` : ''}`,\n\t\t\t\t\tsideWord(g.side, this.translator),\n\t\t\t\t]\n\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t.join(' '),\n\t\t\t);\n\t\t\ttitles.set(\n\t\t\t\tgate,\n\t\t\t\t[\n\t\t\t\t\tthis.t('Gate {{gate}}', { gate }),\n\t\t\t\t\tdisplay(list[0], 'gateName'),\n\t\t\t\t\t...bodies,\n\t\t\t\t]\n\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t.join(' · '),\n\t\t\t);\n\t\t}\n\t\treturn titles;\n\t}\n\n\t/** Monochrome planet glyph for an activation, or its body name when the wheel has no glyph for it. The lookup is keyed on the canonical English name, which the API keeps English in every language for exactly this; the fallback TEXT is the reader's. */\n\tprivate gateGlyph(g: GateActivation): string {\n\t\tif (!g.planet) return '';\n\t\treturn planetGlyph(g.planet) ?? display(g, 'planet');\n\t}\n\n\tprivate renderSummary(\n\t\td: Bodygraph,\n\t\tcenterNames: ReadonlyMap<BodygraphCenterId, string>,\n\t) {\n\t\tconst ic = d.incarnationCross;\n\t\treturn html`<div class=\"summary\" part=\"details\">${renderHdFacts([\n\t\t\t\t{ label: this.t('Type'), value: display(d, 'type') },\n\t\t\t\t{ label: this.t('Strategy'), value: display(d, 'strategy') },\n\t\t\t\t{ label: this.t('Authority'), value: display(d, 'authority') },\n\t\t\t\t{ label: this.t('Profile'), value: d.profile },\n\t\t\t\t{ label: this.t('Definition'), value: display(d, 'definition') },\n\t\t\t])} ${\n\t\t\t\t// The tiles name the type; this paragraph explains it. The cross NAME\n\t\t\t\t// and its gates below are chart facts and stay.\n\t\t\t\td.typeDescription && !this.hideReadings\n\t\t\t\t\t? html`<p class=\"lead\">${d.typeDescription}</p>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\t// `incarnationCross.name` is the one piece of vocabulary on this card the\n\t\t\t\t// API does NOT localize (`Left Angle Cross of the Clarion` comes back\n\t\t\t\t// English in every language), so it prints as sent. Do not translate it\n\t\t\t\t// here from `angleLocalized` plus the gates: that is a second translation\n\t\t\t\t// of the same fact and it can disagree with the reading below.\n\t\t\t\tic?.name\n\t\t\t\t\t? html`<p class=\"cross\">${ic.name} ${\n\t\t\t\t\t\t\tic.gates?.length\n\t\t\t\t\t\t\t\t? html`<span class=\"gates\">(${ic.gates.join(', ')})</span>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t}</p>`\n\t\t\t\t\t: nothing\n\t\t\t} ${renderHdThemes(\n\t\t\t\td.signature ? display(d, 'signature') : undefined,\n\t\t\t\td.notSelf ? display(d, 'notSelf') : undefined,\n\t\t\t\tthis.translator,\n\t\t\t)} ${this.renderLegend(centerNames)}</div>`;\n\t}\n\n\t/**\n\t * The chart key: what an activation colour means, then what a centre colour means.\n\t *\n\t * @remarks\n\t * The centre rows are built from the chart geometry and named with the response\n\t * vocabulary, so they can never claim a colour the chart does not draw and never\n\t * name a centre in a different language from the accordion. The activation row\n\t * leads because it is the half a reader cannot guess.\n\t */\n\tprivate renderLegend(centerNames: ReadonlyMap<BodygraphCenterId, string>) {\n\t\tconst named = LEGEND_COLORS.map(\n\t\t\t(color) =>\n\t\t\t\t[\n\t\t\t\t\tcolor,\n\t\t\t\t\tCENTER_GEOMETRY.filter((c) => c.color === color)\n\t\t\t\t\t\t.map((c) => centerNames.get(c.id) || c.label)\n\t\t\t\t\t\t.join(', '),\n\t\t\t\t] as const,\n\t\t);\n\t\treturn html`<div class=\"legend\" part=\"legend\"><span class=\"legend-row\"><span class=\"swatch source src-right\"></span>${this.t('Personality')} <span class=\"swatch source src-left\"></span>${this.t('Design')} </span><span class=\"legend-row\">${this.t('Center colors when defined. Open centers are outlined.')}</span> ${named.map(\n\t\t\t\t([color, names]) =>\n\t\t\t\t\thtml`<span><span class=\"swatch bg-${color} defined\"></span>${names}</span>`,\n\t\t\t)} <span><span class=\"swatch\"></span>${this.t('Open center')}</span></div>`;\n\t}\n\n\t/**\n\t * The mechanics of the design, in the order a reader needs them: how to engage (strategy), how to decide (authority), the role played (profile), how the definition hangs together, the aura, and the life theme of the incarnation cross. The cross name and gates already sit beside the chart, so only its description is repeated here.\n\t */\n\tprivate renderReading(d: Bodygraph) {\n\t\tconst ic = d.incarnationCross;\n\t\tconst sections: ReadingSection[] = [\n\t\t\t{\n\t\t\t\tlabel: this.t('Strategy'),\n\t\t\t\taside: display(d, 'strategy'),\n\t\t\t\tbody: d.strategyDescription,\n\t\t\t},\n\t\t\t{\n\t\t\t\tlabel: this.t('Authority'),\n\t\t\t\taside: display(d, 'authority'),\n\t\t\t\tbody: d.authorityDescription,\n\t\t\t},\n\t\t\t{\n\t\t\t\tlabel: this.t('Profile'),\n\t\t\t\taside: d.profile,\n\t\t\t\tbody: d.profileDescription,\n\t\t\t\textra: renderHdKeynotes(d.profileKeynotes, this.translator),\n\t\t\t},\n\t\t\t{\n\t\t\t\tlabel: this.t('Definition'),\n\t\t\t\taside: display(d, 'definition'),\n\t\t\t\tbody: d.definitionDescription,\n\t\t\t},\n\t\t\t{ label: this.t('Aura'), body: d.aura },\n\t\t\t{\n\t\t\t\tlabel: this.t('Incarnation cross'),\n\t\t\t\taside: ic ? display(ic, 'angle') : undefined,\n\t\t\t\tbody: ic?.description ?? '',\n\t\t\t},\n\t\t];\n\t\treturn this.renderInterpretation(sections, 'hd-reading');\n\t}\n\n\t/**\n\t * Defined channels, grouped by circuit. The circuit description is the same\n\t * text for every channel that belongs to it, so it is lifted to the group\n\t * intro instead of repeating inside each row. Groups keep response order, which\n\t * is gate order; no circuit ranking is invented.\n\t *\n\t * @remarks\n\t * The two centres a channel joins are what it DOES, so each row names them as\n\t * chips beside its gates. They read the same names the chart and the centres\n\t * section use, and being names rather than a sentence they need no wording of\n\t * their own in any language.\n\t */\n\tprivate renderChannels(\n\t\tchannels: ChannelEntry[],\n\t\tcenterNames: ReadonlyMap<BodygraphCenterId, string>,\n\t) {\n\t\tif (channels.length === 0 || this.hideReadings) return nothing;\n\t\tconst groups = new Map<string, ChannelEntry[]>();\n\t\tfor (const c of channels) {\n\t\t\tconst key = c.circuit ?? '';\n\t\t\tconst bucket = groups.get(key);\n\t\t\tif (bucket) bucket.push(c);\n\t\t\telse groups.set(key, [c]);\n\t\t}\n\t\t// One accordion group spans every circuit, so the open row is the first\n\t\t// channel overall, not the first of each circuit.\n\t\tlet index = 0;\n\n\t\treturn html`<section class=\"block\" part=\"section channels\"><h3>${this.t('Defined channels ({{count}})', { count: channels.length })}</h3>${[...groups].map(\n\t\t\t\t([circuit, list]) => html`<div class=\"group\">${\n\t\t\t\t\t\tcircuit\n\t\t\t\t\t\t\t? html`<p class=\"group-head\">${this.t('{{circuit}} circuit', {\n\t\t\t\t\t\t\t\t\tcircuit: display(list[0], 'circuit'),\n\t\t\t\t\t\t\t\t})}</p>`\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t} ${\n\t\t\t\t\t\tlist[0]?.circuitDescription\n\t\t\t\t\t\t\t? html`<p class=\"group-note\">${list[0].circuitDescription}</p>`\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t} ${list.map(\n\t\t\t\t\t\t(\n\t\t\t\t\t\t\tc,\n\t\t\t\t\t\t) => html`<details class=\"interp-card\" part=\"reading\" name=\"hd-channel\" ?open=\"${index++ === 0}\"><summary><span class=\"interp-lead\"><span class=\"chan-gates\">${c.gateA}-${c.gateB}</span> <span>${display(c, 'name')}</span> </span>${chevron()} <span class=\"interp-aside\"><span class=\"chips\">${(c.centers ?? []).map(\n\t\t\t\t\t\t\t\t\t\t\t(id) =>\n\t\t\t\t\t\t\t\t\t\t\t\thtml`<span class=\"chip\">${centerNames.get(id as BodygraphCenterId) || id}</span>`,\n\t\t\t\t\t\t\t\t\t\t)}</span></span></summary><div class=\"interp-body\">${c.description ? html`<p>${c.description}</p>` : nothing}</div></details>`,\n\t\t\t\t\t)}</div>`,\n\t\t\t)}</section>`;\n\t}\n\n\t/**\n\t * The nine centers. `theme` already reflects whether the center came back\n\t * defined or open, so it always shows. `notSelfQuestion` is written for the\n\t * OPEN state (\"the open Spleen clings to...\"), so showing it on a defined\n\t * center would state the opposite of the chart; it is rendered for open\n\t * centers only.\n\t */\n\tprivate renderCenters(centers: CenterEntry[]) {\n\t\tif (centers.length === 0 || this.hideReadings) return nothing;\n\t\tconst definedCount = centers.filter((c) => c.defined).length;\n\t\treturn html`<section class=\"block\" part=\"section centers\"><h3>${this.t('Centers ({{defined}} defined, {{open}} open)', {\n\t\t\t\tdefined: definedCount,\n\t\t\t\topen: centers.length - definedCount,\n\t\t\t})}</h3>${centers.map(\n\t\t\t\t(\n\t\t\t\t\tc,\n\t\t\t\t\ti,\n\t\t\t\t) => html`<details class=\"interp-card\" part=\"reading\" name=\"hd-center\" ?open=\"${i === 0}\"><summary><span class=\"interp-lead\">${display(c, 'name')}</span> ${chevron()} <span class=\"interp-aside\"><span class=\"chips\"><span class=\"chip ${c.defined ? 'chip--on' : ''}\">${c.defined ? this.t('Defined') : this.t('Open')}</span> ${c.motor ? html`<span class=\"chip\">${this.t('Motor')}</span>` : nothing} ${c.awareness ? html`<span class=\"chip\">${this.t('Awareness')}</span>` : nothing}</span></span></summary><div class=\"interp-body\">${c.theme ? html`<p>${c.theme}</p>` : nothing} ${\n\t\t\t\t\t\t\t!c.defined && c.notSelfQuestion\n\t\t\t\t\t\t\t\t? html`<dl class=\"keynotes\"><dt>${this.t('Not-self question')}</dt><dd>${c.notSelfQuestion}</dd></dl>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t} ${c.biology ? html`<p class=\"footnote\">${this.t('Biology')}. ${c.biology}</p>` : nothing} ${\n\t\t\t\t\t\t\tc.gates?.length\n\t\t\t\t\t\t\t\t? html`<p class=\"footnote\">${this.t('Gates {{gates}}', { gates: c.gates.join(', ') })}</p>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t}</div></details>`,\n\t\t\t)}</section>`;\n\t}\n\n\t/**\n\t * The 26 activations, split by chart side. Personality is the conscious side\n\t * printed in black, design the unconscious side printed in red, and the\n\t * response describes each once at the top level, so the side description\n\t * becomes the panel intro rather than an orphan glossary entry. Splitting the\n\t * list in two also halves what a reader scans: 13 rows per side, in the\n\t * canonical planet order the response returns.\n\t */\n\tprivate renderActivations(d: Bodygraph) {\n\t\tconst gates = d.gates ?? [];\n\t\tif (gates.length === 0 || this.hideReadings) return nothing;\n\n\t\tconst personality = gates.filter((g) => g.side === 'personality');\n\t\tconst design = gates.filter((g) => g.side === 'design');\n\t\t// A response with only one side (or an unrecognized side value) still\n\t\t// renders: fall back to the flat list and drop the tabs.\n\t\tconst split = personality.length > 0 && design.length > 0;\n\t\tconst side = this.side;\n\t\tconst shown = split ? (side === 'design' ? design : personality) : gates;\n\t\tconst sideNote = split ? d.sides?.[side] : undefined;\n\n\t\treturn html`<section class=\"block\" part=\"section activations\"><h3>${this.t('Activations ({{count}})', { count: gates.length })}</h3>${\n\t\t\t\tsplit\n\t\t\t\t\t? renderTablist({\n\t\t\t\t\t\t\titems: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tid: 'personality' as const,\n\t\t\t\t\t\t\t\t\tlabel: `${this.t('Personality')} (${personality.length})`,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tid: 'design' as const,\n\t\t\t\t\t\t\t\t\tlabel: `${this.t('Design')} (${design.length})`,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tactive: side,\n\t\t\t\t\t\t\tonSelect: (v) => {\n\t\t\t\t\t\t\t\tthis.side = v;\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tlabel: this.t('Chart sides'),\n\t\t\t\t\t\t\tidPrefix: 'hd',\n\t\t\t\t\t\t\tcontrols: true,\n\t\t\t\t\t\t})\n\t\t\t\t\t: nothing\n\t\t\t}<div id=\"${split ? `hd-panel-${side}` : nothing}\" part=\"panel\" role=\"${split ? 'tabpanel' : nothing}\" aria-labelledby=\"${split ? `hd-tab-${side}` : nothing}\">${sideNote ? html`<p class=\"side-note\">${sideNote}</p>` : nothing} ${shown.map((g, i) => this.renderGate(g, i === 0))}</div></section>`;\n\t}\n\n\tprivate renderGate(g: GateActivation, open: boolean) {\n\t\tconst glyph = this.gateGlyph(g);\n\t\tconst body = display(g, 'planet');\n\t\tconst hex = g.ichingHexagram;\n\t\treturn html`<details class=\"interp-card\" part=\"reading\" name=\"hd-gate\" ?open=\"${open}\"><summary><span class=\"interp-lead\">${glyph ? html`<span class=\"glyph\" aria-hidden=\"true\">${glyph}</span>` : nothing} <span class=\"gate-id\">${g.gate}${g.line != null ? `.${g.line}` : ''}</span> <span>${display(g, 'gateName')}</span> </span>${chevron()} ${body ? html`<span class=\"interp-aside\"><small>${body}</small></span>` : nothing}</summary><div class=\"interp-body\">${g.gateDescription ? html`<p>${g.gateDescription}</p>` : nothing} ${\n\t\t\t\t\tg.lineMeaning\n\t\t\t\t\t\t? html`<dl class=\"keynotes\"><dt>${this.t('Line {{line}}', { line: g.line ?? '' })}</dt><dd>${g.lineMeaning}</dd></dl>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t} ${\n\t\t\t\t\tg.planetDescription\n\t\t\t\t\t\t? html`<p class=\"footnote\">${body}. ${g.planetDescription}</p>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t} ${\n\t\t\t\t\t// The hexagram field is literally named `english` and the API returns\n\t\t\t\t\t// it English in every language, so it prints as sent rather than being\n\t\t\t\t\t// looked up somewhere else.\n\t\t\t\t\thex?.number\n\t\t\t\t\t\t? html`<p class=\"footnote\">${this.t('I Ching hexagram {{number}}', { number: hex.number })}${hex.english ? `, ${hex.english}` : ''}</p>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</div></details>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-bodygraph': RoxyBodygraph;\n\t}\n}\n", "import type { TemplateResult } from 'lit';\nimport { nothing, svg } from 'lit';\n\n/**\n * Fixed geometry and renderer for the Human Design bodygraph: nine centers, 36 channels, all 64 gates, over a front-facing silhouette. Only which centers are defined and which gates are activated changes per chart.\n *\n * @remarks\n * Points are authored in a 0 to 100 grid and mapped into {@link BODYGRAPH_VIEWBOX} by {@link g}, so the chart and the silhouette share one space. The window is tight to the drawing rather than square, because every unused grid unit costs gate-number size. `tests/bodygraph.test.ts` holds the layout to the canonical shapes, orientations and row order.\n */\n\nexport type BodygraphCenterId =\n\t| 'head'\n\t| 'ajna'\n\t| 'throat'\n\t| 'g'\n\t| 'heart'\n\t| 'sacral'\n\t| 'solar-plexus'\n\t| 'spleen'\n\t| 'root';\n\ninterface Point {\n\tx: number;\n\ty: number;\n}\n\n/** Which of the two sources activated a gate; both splits the circle. The renderer names neither, so one card reads them as the two sides of a person and another as two people. */\nexport interface GateSources {\n\tleft: boolean;\n\tright: boolean;\n}\n\n/** One center's drawable geometry, plus the English fallback name for its accessible title when the response carries none. */\ninterface CenterGeometry {\n\tid: BodygraphCenterId;\n\tlabel: string;\n\tcolor: CenterColor;\n\tpoints: Point[];\n\t/** Where the centre name sits, when the shape's middle is taken by a gate number. Defaults to the centroid. */\n\tlabelAt?: Point;\n}\n\n/** Traditional center color group: gold for Head and G, green for Ajna, red for the Heart and Sacral motors, brown for the rest. A defined center is filled with it; an open one takes the card surface so the wiring passes behind. */\nexport type CenterColor = 'gold' | 'green' | 'red' | 'brown';\n\n/** The grid window the viewBox covers, with nothing to spare: margin here is space the drawing does not get. Symmetric about {@link AXIS}, and the topmost element is the Head apex rather than the silhouette. */\nconst X_MIN = 14;\nconst X_MAX = 86;\nconst Y_MIN = 0;\nconst Y_MAX = 102;\n/** Grid units to viewBox units. One number sets the resolution of every coordinate below. */\nconst UNIT = 6;\nconst VIEW_W = (X_MAX - X_MIN) * UNIT;\nconst VIEW_H = (Y_MAX - Y_MIN) * UNIT;\n\n/** Map a normalized grid point into viewBox units. */\nfunction g(x: number, y: number): Point {\n\treturn { x: (x - X_MIN) * UNIT, y: (y - Y_MIN) * UNIT };\n}\n\n/** Chart horizontal center, the axis of symmetry for the body and the side centers. */\nconst AXIS = 50;\n\n/** Reflect a normalized grid x across {@link AXIS}. */\nconst mirrorX = (x: number): number => 2 * AXIS - x;\n\n/** Gate circle and number size, in grid units. Bounded by the closest pair on the chart (Throat 12 and 45, 2.3 apart) and by fitting two digits inside the circle. */\nexport const GATE_RADIUS = 1.02 * UNIT;\nexport const GATE_FONT_SIZE = 1.35 * UNIT;\n/** The centre name behind the numbers. Smaller than a gate number so it reads as ground rather than as another value. */\nexport const CENTER_NAME_FONT_SIZE = 1.15 * UNIT;\n\n/** The three x offsets from {@link AXIS} a central-column gate sits on: the inner triples, the outer column of the square centers, and the G's wider waist pair. Named so they cannot drift apart one edit at a time. */\nconst COL = 2.87;\nconst EDGE = 4.4;\nconst G_EDGE = 5.65;\n\n/** Gate positions per center, in canonical reading order. Symmetry is structural rather than typed: the central column is authored as offsets from {@link AXIS}, and the Solar Plexus is left empty and filled by reflecting the Spleen in {@link buildGatePoints}. */\nconst GATES_BY_CENTER: Record<\n\tBodygraphCenterId,\n\tRecord<number, [number, number]>\n> = {\n\thead: { 64: [AXIS - COL, 11.2], 61: [AXIS, 11.2], 63: [AXIS + COL, 11.2] },\n\tajna: {\n\t\t47: [AXIS - COL, 18.0],\n\t\t24: [AXIS, 18.0],\n\t\t4: [AXIS + COL, 18.0],\n\t\t17: [AXIS - COL, 20.6],\n\t\t11: [AXIS + COL, 20.6],\n\t\t43: [AXIS, 25.3],\n\t},\n\tthroat: {\n\t\t62: [AXIS - COL, 32.4],\n\t\t23: [AXIS, 32.4],\n\t\t56: [AXIS + COL, 32.4],\n\t\t16: [AXIS - EDGE, 34.7],\n\t\t35: [AXIS + EDGE, 34.7],\n\t\t20: [AXIS - EDGE, 37.3],\n\t\t12: [AXIS + EDGE, 37.3],\n\t\t45: [AXIS + EDGE, 39.6],\n\t\t31: [AXIS - COL, 41.9],\n\t\t8: [AXIS, 41.9],\n\t\t33: [AXIS + COL, 41.9],\n\t},\n\tg: {\n\t\t1: [AXIS, 47.7],\n\t\t7: [AXIS - COL, 50.3],\n\t\t13: [AXIS + COL, 50.3],\n\t\t10: [AXIS - G_EDGE, 53.4],\n\t\t25: [AXIS + G_EDGE, 53.4],\n\t\t15: [AXIS - COL, 56.8],\n\t\t46: [AXIS + COL, 56.8],\n\t\t2: [AXIS, 59.2],\n\t},\n\theart: {\n\t\t21: [63.9, 59.5],\n\t\t51: [62.1, 61.3],\n\t\t26: [60.1, 63.3],\n\t\t40: [66.6, 63.3],\n\t},\n\tspleen: {\n\t\t48: [20.0, 70.8],\n\t\t57: [22.5, 72.4],\n\t\t44: [25.0, 73.8],\n\t\t50: [27.7, 75.3],\n\t\t32: [25.0, 76.6],\n\t\t28: [22.5, 78.0],\n\t\t18: [20.0, 79.5],\n\t},\n\tsacral: {\n\t\t5: [AXIS - COL, 72.7],\n\t\t14: [AXIS, 72.7],\n\t\t29: [AXIS + COL, 72.7],\n\t\t34: [AXIS - EDGE, 75.4],\n\t\t27: [AXIS - EDGE, 79.0],\n\t\t59: [AXIS + EDGE, 79.0],\n\t\t42: [AXIS - COL, 81.7],\n\t\t3: [AXIS, 81.7],\n\t\t9: [AXIS + COL, 81.7],\n\t},\n\t'solar-plexus': {},\n\troot: {\n\t\t53: [AXIS - COL, 90.2],\n\t\t60: [AXIS, 90.2],\n\t\t52: [AXIS + COL, 90.2],\n\t\t54: [AXIS - EDGE, 92.6],\n\t\t19: [AXIS + EDGE, 92.6],\n\t\t38: [AXIS - EDGE, 95.3],\n\t\t39: [AXIS + EDGE, 95.3],\n\t\t58: [AXIS - EDGE, 98.1],\n\t\t41: [AXIS + EDGE, 98.1],\n\t},\n};\n\n/** Spleen gate to the Solar Plexus gate at its mirror position (base-top, ..., apex, ..., base-bottom). */\nconst SPLEEN_TO_SOLAR_PLEXUS: Record<number, number> = {\n\t48: 36,\n\t57: 22,\n\t44: 37,\n\t50: 6,\n\t32: 49,\n\t28: 55,\n\t18: 30,\n};\n\n/** Gate anchors and the gate-to-center index, filling the Solar Plexus by reflecting the Spleen so the two can never drift apart. */\nfunction buildGatePoints(): {\n\tpoints: Record<number, Point>;\n\tcenterOf: Record<number, BodygraphCenterId>;\n} {\n\tconst points: Record<number, Point> = {};\n\tconst centerOf: Record<number, BodygraphCenterId> = {};\n\tfor (const [spleenGate, [x, y]] of Object.entries(GATES_BY_CENTER.spleen)) {\n\t\tGATES_BY_CENTER['solar-plexus'][\n\t\t\tSPLEEN_TO_SOLAR_PLEXUS[Number(spleenGate)]\n\t\t] = [mirrorX(x), y];\n\t}\n\tfor (const [center, gates] of Object.entries(GATES_BY_CENTER) as Array<\n\t\t[BodygraphCenterId, Record<number, [number, number]>]\n\t>) {\n\t\tfor (const [gate, [x, y]] of Object.entries(gates)) {\n\t\t\tpoints[Number(gate)] = g(x, y);\n\t\t\tcenterOf[Number(gate)] = center;\n\t\t}\n\t}\n\treturn { points, centerOf };\n}\n\n/**\n * The viewBox-space gate anchors ({@link GATE_POINTS}) and the gate to center\n * index ({@link GATE_CENTER}). Exported so the geometry tests can assert the\n * layout invariants (side-center mirror symmetry, central-column balance, gates\n * inside their centers, circles that never overlap) without rendering.\n */\nexport const { points: GATE_POINTS, centerOf: GATE_CENTER } = buildGatePoints();\n\n/** Horizontal axis of symmetry in viewBox units, the reflection axis for geometry tests. */\nexport const CHART_AXIS_X = VIEW_W / 2;\n\n/** Build a polygon from normalized grid corner pairs, mapping each through {@link g}. */\nfunction shape(corners: ReadonlyArray<readonly [number, number]>): Point[] {\n\treturn corners.map(([x, y]) => g(x, y));\n}\n\n/** A square center, centered on {@link AXIS}, spanning the given half-width and y range. */\nfunction squareShape(halfWidth: number, top: number, bottom: number): Point[] {\n\treturn shape([\n\t\t[AXIS - halfWidth, top],\n\t\t[AXIS + halfWidth, top],\n\t\t[AXIS + halfWidth, bottom],\n\t\t[AXIS - halfWidth, bottom],\n\t]);\n}\n\n/** Measured half-width of the three square centers (Throat, Sacral, Root), which share one width. */\nconst SQUARE_HALF = 5.9;\n\n/** The Spleen triangle (base on the far-left edge, apex pointing right toward center). */\nconst SPLEEN_SHAPE: ReadonlyArray<readonly [number, number]> = [\n\t[18.3, 68.3],\n\t[18.3, 82.0],\n\t[30.3, 75.15],\n];\n\n/**\n * Center shapes in canonical orientation and color. Central-column centers are\n * built centered on {@link AXIS}; the Solar Plexus shape is the Spleen reflected\n * across the axis, so the side centers stay exact mirrors. The Heart is the\n * deliberate off-axis exception and points UP, not right.\n */\nexport const CENTER_GEOMETRY: readonly CenterGeometry[] = [\n\t{\n\t\tid: 'head',\n\t\tlabel: 'Head',\n\t\tcolor: 'gold',\n\t\tpoints: shape([\n\t\t\t[43.55, 12.5],\n\t\t\t[56.45, 12.5],\n\t\t\t[50.0, 1.7],\n\t\t]),\n\t},\n\t{\n\t\tid: 'ajna',\n\t\tlabel: 'Ajna',\n\t\tcolor: 'green',\n\t\tpoints: shape([\n\t\t\t[43.55, 16.5],\n\t\t\t[56.45, 16.5],\n\t\t\t[50.0, 27.7],\n\t\t]),\n\t},\n\t{\n\t\tid: 'throat',\n\t\tlabel: 'Throat',\n\t\tcolor: 'brown',\n\t\tpoints: squareShape(SQUARE_HALF, 31.0, 43.3),\n\t},\n\t{\n\t\tid: 'g',\n\t\tlabel: 'G',\n\t\tcolor: 'gold',\n\t\tpoints: shape([\n\t\t\t[50.0, 45.8],\n\t\t\t[57.7, 53.6],\n\t\t\t[50.0, 61.4],\n\t\t\t[42.3, 53.6],\n\t\t]),\n\t},\n\t{\n\t\tid: 'heart',\n\t\tlabel: 'Heart',\n\t\tcolor: 'red',\n\t\tpoints: shape([\n\t\t\t[56.65, 65.23],\n\t\t\t[64.2, 56.74],\n\t\t\t[69.75, 65.23],\n\t\t]),\n\t},\n\t{\n\t\tid: 'spleen',\n\t\tlabel: 'Spleen',\n\t\tcolor: 'brown',\n\t\tpoints: shape(SPLEEN_SHAPE),\n\t},\n\t{\n\t\tid: 'sacral',\n\t\tlabel: 'Sacral',\n\t\tcolor: 'red',\n\t\tpoints: squareShape(SQUARE_HALF, 71.3, 83.1),\n\t},\n\t{\n\t\tid: 'solar-plexus',\n\t\tlabel: 'Solar Plexus',\n\t\tcolor: 'brown',\n\t\tpoints: shape(\n\t\t\tSPLEEN_SHAPE.map(([x, y]) => [mirrorX(x), y] as [number, number]),\n\t\t),\n\t},\n\t{\n\t\tid: 'root',\n\t\tlabel: 'Root',\n\t\tcolor: 'brown',\n\t\tpoints: squareShape(SQUARE_HALF, 88.5, 99.8),\n\t},\n];\n\n/**\n * The 36 channels as ordered gate pairs. This is the canonical Human Design\n * channel set; a channel is active only when both of its gates are activated,\n * which the live response reports in its `channels` array. The static list lets\n * the renderer draw every channel as an inactive connector and overlay the\n * activated halves, so an open bodygraph still shows its full wiring skeleton.\n */\nexport const CHANNEL_PAIRS: ReadonlyArray<readonly [number, number]> = [\n\t[64, 47],\n\t[61, 24],\n\t[63, 4],\n\t[17, 62],\n\t[11, 56],\n\t[43, 23],\n\t[16, 48],\n\t[20, 34],\n\t[20, 10],\n\t[7, 31],\n\t[1, 8],\n\t[13, 33],\n\t[21, 45],\n\t[12, 22],\n\t[35, 36],\n\t[57, 20],\n\t[15, 5],\n\t[2, 14],\n\t[46, 29],\n\t[34, 10],\n\t[10, 57],\n\t[25, 51],\n\t[27, 50],\n\t[57, 34],\n\t[26, 44],\n\t[18, 58],\n\t[28, 38],\n\t[32, 54],\n\t[3, 60],\n\t[9, 52],\n\t[42, 53],\n\t[59, 6],\n\t[19, 49],\n\t[39, 55],\n\t[41, 30],\n\t[37, 40],\n];\n\n/**\n * Front-facing standing figure behind the chart, mirror-symmetric about {@link\n * AXIS} and authored in the same grid as the centers so the two scale together:\n * a rounded head holding the Head and Ajna, a short neck at the Throat, shoulders\n * sloping to arms whose span frames the Spleen and Solar Plexus, then hips ending\n * just below the Root. The right half is built from cubic beziers and reflected,\n * so the figure is exactly symmetric. Drawn first; the centers paint over it,\n * which is what makes an open center read as a cut-out rather than a tint.\n */\nconst BODY_PATH = buildBodyPath();\n\nfunction buildBodyPath(): string {\n\tconst m = mirrorX;\n\t// Right-side outline in grid units (x, y) from the crown down to the\n\t// pelvis-right corner: a start point, then triples of (ctrl1, ctrl2, end).\n\tconst r: Array<[number, number]> = [\n\t\t[50, 3], // crown apex (start), below the Head apex so the triangle reads above it\n\t\t[58, 3], // crown round (ctrl)\n\t\t[60, 10], // head side (ctrl)\n\t\t[57, 17.5], // brow, the head holds Head + Ajna (end)\n\t\t[56.5, 20.5], // cheek (ctrl)\n\t\t[53.5, 23.5], // jaw (ctrl)\n\t\t[51.5, 27], // neck right, narrowed to the Ajna apex (end)\n\t\t[54, 28], // neck base (ctrl)\n\t\t[64, 30], // trapezius slope (ctrl)\n\t\t[79, 35], // shoulder / deltoid, the widest point (end)\n\t\t[83, 42], // upper arm (ctrl)\n\t\t[84, 58], // outer arm, frames the side centers (ctrl)\n\t\t[83, 74], // arm past Spleen / Solar Plexus (end)\n\t\t[83, 86], // waist, held wide enough to contain the side-center bases (ctrl)\n\t\t[76, 93], // hip (ctrl)\n\t\t[68, 97], // hip (end)\n\t\t[64, 99], // toward the pelvis (ctrl)\n\t\t[62, 100.5], // pelvis (ctrl)\n\t\t[60, 100.5], // pelvis-right corner (end)\n\t];\n\tconst segs: string[] = [`M ${pt(r[0])}`];\n\t// Walk the right side as cubic beziers, three points per segment.\n\tfor (let i = 1; i + 2 < r.length; i += 3) {\n\t\tsegs.push(`C ${pt(r[i])} ${pt(r[i + 1])} ${pt(r[i + 2])}`);\n\t}\n\t// Flat pelvis hem across to the mirrored corner.\n\tsegs.push(`L ${ptm(r[r.length - 1], m)}`);\n\t// Mirror the right walk back up the left side to the crown.\n\tfor (let i = r.length - 3; i >= 1; i -= 3) {\n\t\tsegs.push(`C ${ptm(r[i + 1], m)} ${ptm(r[i], m)} ${ptm(r[i - 1], m)}`);\n\t}\n\tsegs.push('Z');\n\treturn segs.join(' ');\n}\n\nfunction pt([x, y]: [number, number]): string {\n\tconst p = g(x, y);\n\treturn `${p.x} ${p.y}`;\n}\n\nfunction ptm([x, y]: [number, number], m: (x: number) => number): string {\n\tconst p = g(m(x), y);\n\treturn `${p.x} ${p.y}`;\n}\n\nfunction polygonPoints(pts: Point[]): string {\n\treturn pts.map((p) => `${p.x},${p.y}`).join(' ');\n}\n\n/** The class for a source pair, or `both` when the mark has to carry two colours. */\nfunction sourceClass(s: GateSources): 'src-left' | 'src-right' | 'both' {\n\tif (s.left && s.right) return 'both';\n\treturn s.left ? 'src-left' : 'src-right';\n}\n\n/** Render the body silhouette behind the chart. */\nfunction renderBody(): TemplateResult {\n\treturn svg`<path class=\"bg-body\" d=${BODY_PATH} />`;\n}\n\n/**\n * Render every channel as a pair of halves meeting at the midpoint, so one\n * function draws both states a bodygraph distinguishes: both gates activated\n * reads as one continuous bar, a single activated gate as that gate hanging\n * halfway out of its center. Each half takes the colour of the gate at ITS end,\n * so a channel carrying one Personality gate and one Design gate is half dark and\n * half red rather than forced to one of the two.\n *\n * @remarks\n * A `pd` half is drawn twice, a solid Design line under a dashed Personality one,\n * for the striped bar a gate both sides activated takes. The inactive connector\n * is drawn first so the full 36-channel skeleton stays visible on an open chart.\n */\nfunction renderChannels(\n\tgateSources: ReadonlyMap<number, GateSources>,\n): TemplateResult[] {\n\tconst lines: TemplateResult[] = [];\n\tfor (const [a, b] of CHANNEL_PAIRS) {\n\t\tconst pa = GATE_POINTS[a];\n\t\tconst pb = GATE_POINTS[b];\n\t\tif (!pa || !pb) continue;\n\t\tlines.push(\n\t\t\tsvg`<line class=\"bg-channel\" x1=${pa.x} y1=${pa.y} x2=${pb.x} y2=${pb.y} />`,\n\t\t);\n\t\tconst mid = { x: (pa.x + pb.x) / 2, y: (pa.y + pb.y) / 2 };\n\t\tfor (const [gate, from] of [\n\t\t\t[a, pa],\n\t\t\t[b, pb],\n\t\t] as const) {\n\t\t\tconst sources = gateSources.get(gate);\n\t\t\tif (!sources) continue;\n\t\t\tconst cls = sourceClass(sources);\n\t\t\tlines.push(\n\t\t\t\tsvg`<line class=\"bg-half ${cls === 'both' ? 'src-left' : cls}\" x1=${from.x} y1=${from.y} x2=${mid.x} y2=${mid.y} />`,\n\t\t\t);\n\t\t\tif (cls === 'both') {\n\t\t\t\tlines.push(\n\t\t\t\t\tsvg`<line class=\"bg-half src-right stripe\" x1=${from.x} y1=${from.y} x2=${mid.x} y2=${mid.y} />`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\treturn lines;\n}\n\n/**\n * Render the nine center shapes: filled with their semantic color when defined,\n * filled with the card surface and outlined when open so the wiring and the\n * silhouette pass behind them. Each carries a `<title>` naming it in the reader's\n * language, which is both the hover tooltip and the accessible name.\n */\nfunction renderCenters(\n\tdefined: Set<BodygraphCenterId>,\n\tnames: ReadonlyMap<BodygraphCenterId, string> | undefined,\n\tstateWords: { defined: string; open: string },\n): TemplateResult[] {\n\treturn CENTER_GEOMETRY.flatMap((c) => {\n\t\tconst isDefined = defined.has(c.id);\n\t\tconst cls = `bg-center bg-${c.color}${isDefined ? ' defined' : ''}`;\n\t\tconst label = names?.get(c.id) || c.label;\n\t\tconst at = c.labelAt ?? centroid(c.points);\n\t\treturn [\n\t\t\tsvg`<polygon class=${cls} points=${polygonPoints(c.points)}><title>${label}: ${isDefined ? stateWords.defined : stateWords.open}</title></polygon>`,\n\t\t\t// Drawn with the shapes, so the gate numbers paint over it. Hidden from\n\t\t\t// the accessibility tree because the polygon `<title>` above already\n\t\t\t// names this centre, and announcing it twice is worse than once.\n\t\t\trenderCenterName(label, at, isDefined, c.points),\n\t\t];\n\t});\n}\n\n/**\n * The centre name as ground behind the gate numbers.\n *\n * @remarks\n * A multi-word name stacks one word per line rather than running past the shape: the two side triangles taper to a point, so `Solar Plexus` on one line overruns its own outline while `SOLAR` over `PLEXUS` sits inside it. Splitting on whitespace is one rule for every language rather than a width measured per label, and a name that is one long word in some language simply stays one line.\n */\nfunction renderCenterName(\n\tlabel: string,\n\tat: Point,\n\tisDefined: boolean,\n\tpoints: readonly Point[],\n): TemplateResult {\n\tconst words = label.split(/\\s+/).filter(Boolean);\n\tconst longest = words.reduce((n, w) => Math.max(n, w.length), 0);\n\t// Shrink to the room the shape actually has at this height rather than\n\t// trusting one size to fit every name: the side centres taper to a point, and\n\t// a translated name is a different length in every language.\n\tconst room = widthAt(points, at.y) * 0.86;\n\tconst size = Math.min(\n\t\tCENTER_NAME_FONT_SIZE,\n\t\troom / Math.max(1, longest * UPPERCASE_EM),\n\t);\n\tconst step = size * 1.05;\n\tconst top = at.y - (step * (words.length - 1)) / 2;\n\treturn svg`<text class=\"bg-center-name ${isDefined ? 'defined' : ''}\" style=${`font-size:${size}px`} x=${at.x} y=${at.y} text-anchor=\"middle\" dominant-baseline=\"central\" aria-hidden=\"true\">${words.map(\n\t\t(w, i) => svg`<tspan x=${at.x} y=${top + i * step}>${w}</tspan>`,\n\t)}</text>`;\n}\n\n/** Advance width of one uppercase character at the label weight, as a fraction of the font size. Approximate on purpose: it only has to keep a name inside its own outline, and it is applied with room to spare. */\nconst UPPERCASE_EM = 0.66;\n\n/** Horizontal span of a convex polygon at height `y`: how much room a line of text has there. */\nfunction widthAt(points: readonly Point[], y: number): number {\n\tconst xs: number[] = [];\n\tfor (let i = 0; i < points.length; i++) {\n\t\tconst a = points[i] as Point;\n\t\tconst b = points[(i + 1) % points.length] as Point;\n\t\tif (a.y === b.y) continue;\n\t\tconst lo = Math.min(a.y, b.y);\n\t\tconst hi = Math.max(a.y, b.y);\n\t\tif (y < lo || y > hi) continue;\n\t\txs.push(a.x + ((y - a.y) / (b.y - a.y)) * (b.x - a.x));\n\t}\n\tif (xs.length < 2) return 0;\n\treturn Math.max(...xs) - Math.min(...xs);\n}\n\n/** Average of a shape's vertices: the visual middle of every centre this chart draws, all of which are convex. */\nfunction centroid(points: readonly Point[]): Point {\n\tconst n = points.length || 1;\n\treturn {\n\t\tx: points.reduce((t, p) => t + p.x, 0) / n,\n\t\ty: points.reduce((t, p) => t + p.y, 0) / n,\n\t};\n}\n\n/** A half-disc starting at the top of the circle and sweeping to the bottom. `sweep` 1 is the right half. */\nfunction halfDisc(p: Point, sweep: 0 | 1): string {\n\treturn `M ${p.x} ${p.y - GATE_RADIUS} A ${GATE_RADIUS} ${GATE_RADIUS} 0 0 ${sweep} ${p.x} ${p.y + GATE_RADIUS} Z`;\n}\n\n/**\n * Render all 64 gate numbers at their fixed points: an unactivated gate is a named\n * position on the chart, not an absence, so it draws as an outlined circle with a\n * muted number while an activated one is a filled disc with a knocked-out number.\n * A gate both sources activated is split down the middle.\n */\nfunction renderGates(\n\tgateSources: ReadonlyMap<number, GateSources>,\n\ttitles: ReadonlyMap<number, string>,\n): TemplateResult[] {\n\tconst out: TemplateResult[] = [];\n\tfor (const [gate, p] of Object.entries(GATE_POINTS)) {\n\t\tconst num = Number(gate);\n\t\tconst sources = gateSources.get(num);\n\t\tconst cls = sources ? sourceClass(sources) : '';\n\t\tconst title = titles.get(num);\n\t\tout.push(svg`<g class=\"bg-gate-node\">\n\t\t\t${\n\t\t\t\tcls === 'both'\n\t\t\t\t\t? [\n\t\t\t\t\t\t\tsvg`<path class=\"bg-gate-dot on src-left\" d=${halfDisc(p, 0)} />`,\n\t\t\t\t\t\t\tsvg`<path class=\"bg-gate-dot on src-right\" d=${halfDisc(p, 1)} />`,\n\t\t\t\t\t\t]\n\t\t\t\t\t: svg`<circle class=\"bg-gate-dot ${cls && `on ${cls}`}\" cx=${p.x} cy=${p.y} r=${GATE_RADIUS} />`\n\t\t\t}\n\t\t\t<text class=\"bg-gate ${sources ? 'on' : ''}\" x=${p.x} y=${p.y} text-anchor=\"middle\" dominant-baseline=\"central\">${num}</text>\n\t\t\t${title ? svg`<title>${title}</title>` : nothing}\n\t\t</g>`);\n\t}\n\treturn out;\n}\n\nexport interface BodygraphRenderInput {\n\tdefinedCenters: Set<BodygraphCenterId>;\n\t/** Which source(s) activated each gate; absent means unactivated. The only wiring input, because a channel is defined exactly when both of its gates are. */\n\tgateSources: ReadonlyMap<number, GateSources>;\n\tgateTitles: ReadonlyMap<number, string>;\n\t/** What to name each center in its `<title>`, keyed by center id. The response is the authority for its own vocabulary, so this carries `centers[].nameLocalized` where the API sent one and `centers[].name` otherwise; an id with no entry keeps the chart's own English label. */\n\tcenterNames?: ReadonlyMap<BodygraphCenterId, string>;\n\t/** The two words a center shape states its state with in its tooltip. Supplied by the caller because only a component can reach a chrome catalogue. */\n\tstateWords?: { defined: string; open: string };\n}\n\nexport const BODYGRAPH_VIEWBOX = `0 0 ${VIEW_W} ${VIEW_H}`;\n\n/**\n * Render the full bodygraph SVG inner content for the given live state. The\n * caller wraps it in an `<svg>` with {@link BODYGRAPH_VIEWBOX} and applies its\n * own theming CSS. Draw order: body silhouette under channels under centers\n * under gates, so the body is the backdrop, the wiring runs behind the shapes,\n * and every gate circle sits on top of whatever it lands on.\n */\nexport function renderBodygraphSvg(\n\tinput: BodygraphRenderInput,\n): TemplateResult {\n\treturn svg`\n\t\t${renderBody()}\n\t\t${renderChannels(input.gateSources)}\n\t\t${renderCenters(\n\t\t\tinput.definedCenters,\n\t\t\tinput.centerNames,\n\t\t\tinput.stateWords ?? { defined: 'defined', open: 'open' },\n\t\t)}\n\t\t${renderGates(input.gateSources, input.gateTitles)}\n\t`;\n}\n", "import { css } from 'lit';\nimport { CENTER_NAME_FONT_SIZE, GATE_FONT_SIZE } from './bodygraph-render.js';\n\n/**\n * The stylesheet for anything `renderBodygraphSvg` draws, so a second chart on the same geometry cannot fork the first one's appearance.\n *\n * @remarks\n * The renderer marks each gate and channel half `src-left` or `src-right` and names neither, so a card supplies the meaning by declaring those two colours on its own root and nothing else. Each must clear 4.5:1 against the ink knocked out of it and 3:1 against the card surface, in both themes. A card still owns its `.wrap`, its layout and its legend copy.\n */\nexport const bodygraphChartStyles = css`.chart{width:100%;max-width:var(--roxy-chart-max-width,30rem);height:auto;margin:0 auto;display:block}@container (width<=26rem){.chart{width:calc(100% + 2 * var(--roxy-space-lg,1.5rem));margin-inline:calc(-1 * var(--roxy-space-lg,1.5rem))}}.bg-body{fill:color-mix(in srgb, var(--roxy-secondary,#475569) 8%, transparent);stroke:var(--roxy-border,#e4e4e7);stroke-width:1px}.bg-channel{stroke:var(--roxy-border,#e4e4e7);stroke-width:5px;stroke-linecap:round}.bg-half{stroke-width:5px;stroke-linecap:round}.bg-half.stripe{stroke-linecap:butt;stroke-dasharray:7 7}.bg-center{fill:var(--roxy-surface,#fff);stroke:var(--roxy-secondary,#475569);stroke-width:1.8px}.bg-center.defined{stroke:color-mix(in srgb, var(--roxy-fg,#0a0a0a) 45%, transparent)}:host{--center-gold:#e0a200;--center-green:#2f8f00;--center-red:#c41f1f;--center-brown:#76502f}.bg-center.bg-gold.defined,.legend .swatch.bg-gold{fill:var(--center-gold);background:var(--center-gold)}.bg-center.bg-green.defined,.legend .swatch.bg-green{fill:var(--center-green);background:var(--center-green)}.bg-center.bg-red.defined,.legend .swatch.bg-red{fill:var(--center-red);background:var(--center-red)}.bg-center.bg-brown.defined,.legend .swatch.bg-brown{fill:var(--center-brown);background:var(--center-brown)}.bg-gate-dot{fill:var(--roxy-surface,#fff);stroke:var(--roxy-border,#e4e4e7);stroke-width:1px}.bg-gate-dot.on{stroke:none}.bg-center-name{fill:var(--roxy-muted,#71717a);opacity:.5;font-size:${CENTER_NAME_FONT_SIZE}px;letter-spacing:.08em;text-transform:uppercase;pointer-events:none;font-weight:600}.bg-center-name.defined{fill:var(--roxy-surface,#fff);opacity:.62}.bg-gate{fill:var(--roxy-muted,#71717a);font-size:${GATE_FONT_SIZE}px;font-weight:600;font-family:var(--roxy-font-sans)}.bg-gate.on{fill:var(--src-ink,var(--roxy-surface,#fff))}.legend{gap:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);flex-wrap:wrap;display:flex}.legend .swatch{vertical-align:middle;border:1px solid var(--roxy-secondary,#475569);border-radius:2px;width:10px;height:10px;margin-right:4px;display:inline-block}.bg-half.src-left{stroke:var(--src-left)}.bg-half.src-right{stroke:var(--src-right)}.bg-gate-dot.on.src-left{fill:var(--src-left)}.bg-gate-dot.on.src-right{fill:var(--src-right)}.legend .swatch.src-left{background:var(--src-left)}.legend .swatch.src-right{background:var(--src-right)}.legend .swatch.source{border-radius:var(--roxy-radius-full,9999px);border-color:#0000}`;\n", "/**\n * The Human Design reading surface: the fact tiles, the lead paragraph, the signature and not-self pills, and the profile keynotes.\n *\n * @remarks\n * `/human-design/type` returns a strict subset of the `/human-design/bodygraph` interpretation fields (type, aura, strategy, authority, their descriptions, signature, not-self, profile), so `roxy-bodygraph` and `roxy-hd-type-card` would otherwise render the same markup and carry the same CSS twice. Both import from here instead: the bodygraph wraps the chart around it, the type card is the reading on its own.\n *\n * The disclosure accordion itself is NOT here. It lives in `utils/interp-accordion.ts`, because the same accordion serves any endpoint that returns a labelled reading (`roxy-hexagram` uses it for the changing lines), and both HD components draw it through `RoxyDataElement.renderInterpretation` so `hide-readings` is honoured. Import `interpAccordionStyles` alongside `hdReadingStyles`.\n */\n\nimport { css, html, nothing } from 'lit';\nimport type { InterpSection } from './interp-accordion.js';\n\n/** One row of the reading accordion. Alias of the shared {@link InterpSection} so HD call sites keep reading naturally. */\nexport type ReadingSection = InterpSection;\n\n/**\n * A component's own {@link RoxyDataElement.t}, threaded in so a shared render helper can name its rows.\n *\n * @remarks\n * These helpers are plain functions with no host, so they cannot resolve a page language on their own, and a label they hardcode is one English word inside otherwise translated chrome. Taking the translator as an argument keeps each English source literal in the file that renders it, which is what `tests/i18n.test.ts` scans (it walks `utils/` as well as `components/`), so a string added here still cannot ship without a catalogue entry. That scanner reads comments too, so do not write a sample call in one.\n */\nexport type Translate = (\n\tsource: string,\n\tvars?: Record<string, string | number>,\n) => string;\n\n/** A labelled value tile. A fact with no value is dropped, so a narrower response renders fewer tiles rather than empty ones. */\nexport interface Fact {\n\tlabel: string;\n\tvalue?: string;\n}\n\n/** The two profile lines with the keynote of each. Normalized: the bodygraph nests these under `profileKeynotes`, `/human-design/profile` returns them flat, and both map onto this. */\nexport interface ProfileKeynotes {\n\tpersonality?: string;\n\tpersonalityLine?: number;\n\tdesign?: string;\n\tdesignLine?: number;\n}\n\nexport const hdReadingStyles = css`.facts{gap:var(--roxy-space-sm,.5rem);grid-template-columns:repeat(auto-fit,minmax(8rem,1fr));display:grid}.fact{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);background:var(--roxy-surface,#fff)}.fact span{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);text-transform:uppercase;letter-spacing:.06em;display:block}.fact strong{font-size:var(--roxy-text-base,1rem);color:var(--roxy-fg,#0a0a0a)}.lead{color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-sm,.875rem);margin:0;line-height:1.6}.themes{gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.pill{border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);padding:2px 10px}.pill--good{background:color-mix(in srgb, var(--roxy-success,#16a34a) 16%, transparent);color:var(--roxy-success-fg,#166534)}.pill--shadow{background:color-mix(in srgb, var(--roxy-danger,#dc2626) 16%, transparent);color:var(--roxy-danger-fg,#991b1b)}.keynotes{gap:var(--roxy-space-xs,.25rem);margin:0;display:grid}.keynotes dt{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.05em}.keynotes dd{margin:0}`;\n\n/**\n * The chart side as a word a reader sees.\n *\n * @remarks\n * Personality is the conscious side calculated at birth, design the unconscious side 88 degrees of solar arc before it, and the wire carries them as the machine enum `personality` and `design` with no localized partner. The component is what turns an enum into a word, so this is chrome and comes from the catalogue: the same two words label the bodygraph activation tabs, name the side inside a chart tooltip, and sit under a Variables arrow. An unrecognized value is passed through rather than guessed at.\n */\nexport function sideWord(side: string | undefined, t: Translate): string {\n\tif (side === 'design') return t('Design');\n\tif (side === 'personality') return t('Personality');\n\treturn side ?? '';\n}\n\n/** The labelled value tiles. Values the response does not carry are dropped rather than rendered empty. */\nexport function renderHdFacts(facts: Fact[]) {\n\tconst shown = facts.filter((f) => Boolean(f.value));\n\tif (shown.length === 0) return nothing;\n\treturn html`<div class=\"facts\" part=\"facts\">${shown.map(\n\t\t\t(f) => html`<div class=\"fact\" part=\"fact\"><span>${f.label}</span> <strong>${f.value}</strong></div>`,\n\t\t)}</div>`;\n}\n\n/**\n * The signature and not-self pills: the feeling of being in alignment, and the one that signals being out of it.\n *\n * @remarks\n * Both values arrive already resolved to what a reader should see, because the response carries `signatureLocalized` and `notSelfLocalized` beside the canonical English pair and only the caller knows which half it is holding. The two words around them are this card's own and go through `t`.\n */\nexport function renderHdThemes(\n\tsignature: string | undefined,\n\tnotSelf: string | undefined,\n\tt: Translate,\n) {\n\tif (!signature && !notSelf) return nothing;\n\treturn html`<div class=\"themes\" part=\"themes\">${signature ? html`<span class=\"pill pill--good\">${t('Signature: {{value}}', { value: signature })}</span>` : nothing} ${notSelf ? html`<span class=\"pill pill--shadow\">${t('Not-self: {{value}}', { value: notSelf })}</span>` : nothing}</div>`;\n}\n\n/** The two profile lines, each with the keynote of the line. Personality first: it is the conscious line and the one the profile is read from. */\nexport function renderHdKeynotes(k: ProfileKeynotes | undefined, t: Translate) {\n\tif (!k?.personality && !k?.design) return nothing;\n\treturn html`<dl class=\"keynotes\" part=\"keynotes\">${\n\t\t\tk.personality\n\t\t\t\t? html`<dt>${t('Line {{line}} · Personality', { line: k.personalityLine ?? '' })}</dt><dd>${k.personality}</dd>`\n\t\t\t\t: nothing\n\t\t} ${\n\t\t\tk.design\n\t\t\t\t? html`<dt>${t('Line {{line}} · Design', { line: k.designLine ?? '' })}</dt><dd>${k.design}</dd>`\n\t\t\t\t: nothing\n\t\t}</dl>`;\n}\n", "/**\n * The value a READER sees, for a response field the API returns twice.\n *\n * @remarks\n * Several endpoints now echo a display translation beside the canonical value: `nameLocalized` next to `name`, `signLocalized` next to `sign`, `typeLocalized` next to `type`. **The canonical field is deliberately always English, whatever `?lang=` says, so it stays safe to compare against in code**, and the localized one is deliberately absent from an English response.\n *\n * That split is the whole contract, and inverting it breaks charts silently rather than loudly: `planetGlyph`, `signGlyph`, `aspectSymbol`, `ASPECT_CLASS`, `SIGNS_ORDER.indexOf`, every `Map` key and every CSS class fragment is keyed on the English name, so pointing one of them at `nameLocalized` resolves to nothing on a translated page and to the same value on an English one, which is exactly the shape of defect that passes every gate (lesson 24). **The lookup stays on the canonical value; only the printed text node, the `title` and the `aria-label` move.**\n *\n * These two helpers exist so that rule lives in one place rather than as a `??` at every call site, and so an English response, where the localized field simply is not there, degrades by construction instead of by remembering. Both take the KEY of the canonical field and derive its partner, so a mistyped key or a field that is not a display string is a compile error rather than a silent empty string.\n */\n\nimport { fieldLabel, optionLabel } from '../i18n/registry.js';\nimport { humanize } from './string.js';\n\n/**\n * The label for a field NAME, which is a different problem from a field VALUE.\n *\n * @remarks\n * A catalogue keyed on English source text cannot reach a name computed per response, so these read the published field-label payload instead and fall back to {@link humanize}, which is exactly what every caller printed before one existed. A name the payload does not carry therefore renders as it always did rather than as a gap.\n *\n * Here rather than in each component because three of them need the identical pairing: the form that asks for a field, and the two generic renderers that label what came back.\n */\nexport function displayField(lang: string | undefined, name: string): string {\n\treturn fieldLabel(lang, name) ?? humanize(name);\n}\n\n/** The label for one selectable VALUE of a field, same fallback rule. */\nexport function displayOption(\n\tlang: string | undefined,\n\tfield: string,\n\tvalue: string,\n): string {\n\treturn optionLabel(lang, field, value) ?? humanize(value);\n}\n\n/** The one naming convention every helper here derives from. Ratified by the API and spreading to more operations, so it is matched as a convention and never as a list of field names. */\nconst LOCALIZED = 'Localized';\n\n/** A response row carrying a canonical English `K` plus the optional localized partner the API writes beside it. */\nexport type Localizable<K extends string> = Partial<\n\tRecord<K | `${K}Localized`, string>\n>;\n\n/** The same pairing where the value is a LIST of names, e.g. `retrogradePlanets`. */\nexport type LocalizableList<K extends string> = Partial<\n\tRecord<K | `${K}Localized`, string[]>\n>;\n\n/**\n * The text to print for `key`: the localized value when the response carried one, else the English one.\n *\n * @param english - What to print in the English case INSTEAD of the raw field, for a canonical value that is an API enum with a display form of its own (`type` is `SEMI_SEXTILE` on the wire and `Semi-sextile` on screen). Omit it wherever the canonical value is already the readable one.\n *\n * @example\n * ```ts\n * display(planet, 'name');                            // 'Sol' on ?lang=es, 'Sun' on English\n * planetGlyph(planet.name) ?? display(planet, 'name'); // the lookup stays English, the fallback TEXT does not\n * display(aspect, 'type', formatAspectName(aspect));   // 'Trígono', else 'Trine' and never 'TRINE'\n * ```\n */\nexport function display<\n\tT extends Localizable<K>,\n\tK extends Extract<keyof T, string>,\n>(row: T | null | undefined, key: K, english?: string): string {\n\tconst rec = row as Record<string, unknown> | null | undefined;\n\tconst localized = rec?.[`${key}${LOCALIZED}`];\n\tif (typeof localized === 'string' && localized !== '') return localized;\n\tif (english !== undefined) return english;\n\tconst canonical = rec?.[key];\n\treturn typeof canonical === 'string' ? canonical : '';\n}\n\n/**\n * A list field as `{ value, label }` pairs: the English `value` every lookup is keyed on, and the `label` a reader sees.\n *\n * @remarks\n * The localized array is INDEX-ALIGNED with the canonical one, which is the only thing that makes the pairing possible, so it is zipped by position and neither side is ever sorted, filtered or deduped on its own. A list that is short or absent falls back per item, so a partial translation still resolves every glyph.\n */\nexport function displayList<\n\tT extends LocalizableList<K>,\n\tK extends Extract<keyof T, string>,\n>(row: T | null | undefined, key: K): Array<{ value: string; label: string }> {\n\tconst rec = row as Record<string, unknown> | null | undefined;\n\tconst canonical = rec?.[key];\n\tif (!Array.isArray(canonical)) return [];\n\tconst localized = rec?.[`${key}${LOCALIZED}`];\n\tconst labels = Array.isArray(localized) ? localized : [];\n\treturn canonical.map((value, i) => {\n\t\tconst label = labels[i];\n\t\treturn {\n\t\t\tvalue: String(value),\n\t\t\tlabel: typeof label === 'string' && label !== '' ? label : String(value),\n\t\t};\n\t});\n}\n\n/**\n * Fold every `XLocalized` field into its `X` partner: the reader's value under the canonical NAME, and the duplicate key gone.\n *\n * @remarks\n * For a renderer that has no idea what its fields MEAN and derives its columns from `Object.keys(row)`, which is how `<roxy-data>` started drawing `Planet | Planet Localized | Longitude | Sign | Sign Localized` the day the API began sending both. Two extra columns, headed in English, carrying the same fact twice, on a page whose owner changed nothing. That is a regression rather than a missing feature, so it is repaired at the key layer every render path already funnels through rather than at each of them.\n *\n * Three rules, and each has a way of going wrong:\n *\n * - **A `*Localized` key with no canonical partner stays put.** Suppressing on the name alone deletes data the response carries and nothing else is rendering.\n * - **The canonical key keeps its POSITION**, so a translated response and an English one lay out identically, column for column.\n * - **A row with nothing to fold is returned as the same object**, so the English path is not merely equivalent but untouched.\n *\n * The English value is dropped rather than shown beside its translation because the two are the same fact, and the caller here is generic: it cannot know that `sign` is the value a lookup would be keyed on. A component that needs both halves reads them itself with {@link display}.\n */\nexport function foldLocalized<V>(row: Record<string, V>): Record<string, V> {\n\tlet folded: Record<string, V> | undefined;\n\tfor (const key of Object.keys(row)) {\n\t\tif (!key.endsWith(LOCALIZED)) continue;\n\t\tconst base = key.slice(0, -LOCALIZED.length);\n\t\tif (base === '' || !(base in row)) continue;\n\t\tfolded ??= { ...row };\n\t\tconst value = row[key];\n\t\tif (value !== undefined && value !== null && value !== '')\n\t\t\tfolded[base] = value;\n\t\tdelete folded[key];\n\t}\n\treturn folded ?? row;\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport { planetGlyph, signGlyph } from '../tokens/index.js';\nimport type { CharaKarakaResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { formatNumber } from '../utils/format.js';\nimport { frameCaptionStyles, renderFrameCaption } from '../utils/frame.js';\n\ntype Karaka = CharaKarakaResponse['karakas'][number];\n\n/** What each scheme is, stated on the card rather than left implicit. Read from the response `scheme`, which the API echoes for exactly this reason. */\nconst SCHEMES: Record<string, string> = {\n\teight:\n\t\t'Eight karaka scheme, Rahu included and ranked from the end of its sign.',\n\tseven:\n\t\t'Seven karaka scheme, Rahu excluded and the Pitrikaraka office not assigned.',\n};\n\n/**\n * Chara Karakas. Renders /vedic-astrology/chara-karakas: the movable significators of Jaimini astrology, the offices ranked by how far each graha has advanced into its sign, Atmakaraka first.\n *\n * @remarks\n * The scheme is shown, never hidden. The seven and eight karaka schemes name a DIFFERENT Atmakaraka on the same chart, so a reader who follows one school has to be able to see at a glance which one produced the ranking in front of them. The value is the one the response echoes, so the label can never disagree with the ranking beside it.\n *\n * Rahu is flagged where it appears. It advances backward through a sign, so it is ranked on thirty degrees minus its position, and the response carries both figures: the degree a chart displays and the degree that earned the office. Showing only one of them makes the order look wrong.\n */\n@customElement('roxy-chara-karakas')\nexport class RoxyCharaKarakas extends RoxyDataElement<CharaKarakaResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tframeCaptionStyles,\n\t\tcss`.wrap{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);background:var(--roxy-surface,#fff);box-shadow:var(--roxy-shadow-sm);overflow:hidden}.head{padding:var(--roxy-space-md,1rem);border-bottom:1px solid var(--roxy-border,#e4e4e7);gap:var(--roxy-space-xs,.25rem);display:grid}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.scheme{align-items:baseline;gap:var(--roxy-space-sm,.5rem);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);line-height:var(--roxy-leading-normal,1.5);flex-wrap:wrap;display:flex}.scheme-chip{font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);text-transform:uppercase;letter-spacing:.05em;background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 16%, transparent);color:var(--roxy-accent-ink,#b45309);border-radius:999px;padding:.15em .6em}.leads{gap:var(--roxy-space-md,1rem);padding:var(--roxy-space-md,1rem);border-bottom:1px solid var(--roxy-border,#e4e4e7);grid-template-columns:repeat(auto-fit,minmax(9rem,1fr));display:grid}.lead span{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);text-transform:uppercase;letter-spacing:.06em;display:block}.lead strong{font-size:var(--roxy-text-base,1rem);color:var(--roxy-fg,#0a0a0a)}.scroll{min-width:0;overflow-x:auto}table{border-collapse:collapse;width:100%;font-size:var(--roxy-text-sm,.875rem);min-width:640px}thead{background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 20%, transparent)}th,td{padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);text-align:left;white-space:nowrap}th{color:var(--roxy-muted,#71717a);font-weight:var(--roxy-weight-bold,600);text-transform:uppercase;font-size:var(--roxy-text-xs,.75rem);letter-spacing:.04em}tbody tr{border-top:1px solid var(--roxy-border,#e4e4e7)}tbody tr.ak{background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 10%, transparent)}td.office{font-weight:var(--roxy-weight-bold,600);color:var(--roxy-fg,#0a0a0a)}.abbr{color:var(--roxy-muted,#71717a);font-weight:var(--roxy-weight-normal,400);margin-left:.4em}tbody tr.ak .abbr{color:var(--roxy-accent-ink,#b45309)}.glyph{color:var(--roxy-muted,#71717a);margin-right:.4em}tbody tr.ak .glyph{color:var(--roxy-accent-ink,#b45309)}.num{font-variant-numeric:tabular-nums}.reversed{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);display:block}.meaning{white-space:normal;min-width:12rem}.meaning strong{font-weight:var(--roxy-weight-bold,600);display:block}.meaning p{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);line-height:var(--roxy-leading-normal,1.5);margin:0}`,\n\t];\n\n\tprotected renderData(d: CharaKarakaResponse) {\n\t\tconst karakas = d.karakas ?? [];\n\t\tif (!karakas.length) return this.renderEmpty();\n\n\t\treturn html`<div class=\"wrap\" part=\"card\" aria-label=\"${this.t('Chara karakas')}\"><header class=\"head\" part=\"header\"><h2 class=\"title\">${this.t('Chara karakas')}</h2><p class=\"scheme\"><span class=\"scheme-chip\">${d.scheme ?? ''}</span> ${d.scheme ? (SCHEMES[d.scheme] ?? '') : ''}</p>${renderFrameCaption(this.effectiveLang(), d.frame, this.translator)}</header><div class=\"leads\" part=\"details\"><div class=\"lead\"><span>${this.t('Atmakaraka')}</span><strong>${d.atmakaraka ?? ''}</strong></div><div class=\"lead\"><span>${this.t('Darakaraka')}</span><strong>${d.darakaraka ?? ''}</strong></div></div><div class=\"scroll\" part=\"table\" tabindex=\"0\"><table role=\"table\"><caption class=\"roxy-sr-only\">${this.t(\n\t\t\t\t\t\t\t'Chara karakas in descending rank: each office, the graha holding it, its rashi, the degree it holds, the degree that earned the office, and what the office is read for.',\n\t\t\t\t\t\t)}</caption><thead><tr><th scope=\"col\">${this.t('Office')}</th><th scope=\"col\">${this.t('Graha')}</th><th scope=\"col\">${this.t('Rashi')}</th><th scope=\"col\">${this.t('Degree')}</th><th scope=\"col\">${this.t('Ranked on')}</th><th scope=\"col\">${this.t('Read for')}</th></tr></thead><tbody>${karakas.map((k, i) => this.renderRow(k, i === 0))}</tbody></table></div></div>`;\n\t}\n\n\tprivate renderRow(k: Karaka, isFirst: boolean) {\n\t\tconst glyph = planetGlyph(k.graha) ?? '';\n\t\tconst sGlyph = signGlyph(k.rashi) ?? '';\n\t\treturn html`<tr class=\"${isFirst ? 'ak' : ''}\"><td class=\"office\">${k.name ?? ''}${k.abbreviation ? html`<span class=\"abbr\">${k.abbreviation}</span>` : nothing}</td><td>${glyph ? html`<span class=\"glyph\">${glyph}</span>` : nothing}${k.graha ?? ''}</td><td>${sGlyph ? html`<span class=\"glyph\">${sGlyph}</span>` : nothing}${k.rashi ?? ''}</td><td class=\"num\">${formatNumber(this.effectiveLang(), k.degreeInRashi, 2)}</td><td class=\"num\">${formatNumber(this.effectiveLang(), k.rankingDegree, 2)} ${k.isReversed ? html`<span class=\"reversed\">${this.t('measured from the end of the sign')}</span>` : nothing}</td><td class=\"meaning\"><strong>${k.meaning ?? ''}</strong> ${k.significations ? html`<p>${k.significations}</p>` : nothing}</td></tr>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-chara-karakas': RoxyCharaKarakas;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport { planetGlyph } from '../tokens/index.js';\nimport type { GetChoghadiyaResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { formatDate, formatTimeRange } from '../utils/format.js';\n\ntype ChoghadiyaPeriod = GetChoghadiyaResponse['dayChoghadiya'][number];\n\n/**\n * Choghadiya muhurta grid. Accepts a GetChoghadiyaResponse and renders\n * 8 daytime and 8 nighttime muhurta tiles in a two-column responsive layout.\n * Good periods are highlighted in green, Bad periods in red.\n */\n@customElement('roxy-choghadiya-grid')\nexport class RoxyChoghadiyaGrid extends RoxyDataElement<GetChoghadiyaResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.wrap{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.header{gap:var(--roxy-space-xs,.25rem);display:grid}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.subtitle{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-muted,#71717a);margin:0}.cho-grid{gap:var(--roxy-space-md,1rem);grid-template-columns:1fr;display:grid}@media (width>=720px){.cho-grid{grid-template-columns:1fr 1fr}}.period-col{gap:var(--roxy-space-xs,.25rem);display:grid}.period-heading{font-size:var(--roxy-text-base,1rem);font-weight:var(--roxy-weight-bold,600);margin:0 0 var(--roxy-space-xs,.25rem);color:var(--roxy-fg,#0a0a0a)}.cho-tile{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:.25em .75em;padding:.55em .85em;display:grid}.cho-tile.good{background:color-mix(in srgb, var(--roxy-success,#22c55e) 18%, transparent);border-color:color-mix(in srgb, var(--roxy-success,#22c55e) 45%, transparent);color:var(--roxy-fg,#0a0a0a)}.cho-tile.bad{background:color-mix(in srgb, var(--roxy-danger,#ef4444) 18%, transparent);border-color:color-mix(in srgb, var(--roxy-danger,#ef4444) 45%, transparent);color:var(--roxy-fg,#0a0a0a)}.cho-tile.neutral{color:var(--roxy-fg,#0a0a0a);background:0 0}.cho-tile.now{outline:2px solid var(--roxy-accent,#f59e0b);outline-offset:1px;box-shadow:0 0 0 4px color-mix(in srgb, var(--roxy-accent,#f59e0b) 18%, transparent)}.now-badge{font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-accent-ink,#b45309);text-transform:uppercase;letter-spacing:.06em;margin-left:.4em;display:inline-block}.tile-name{font-size:var(--roxy-text-base,1rem);font-weight:var(--roxy-weight-bold,600);overflow-wrap:anywhere;grid-column:1;min-width:0}.tile-time{font-size:var(--roxy-text-xs,.75rem);opacity:.8;white-space:nowrap;text-align:right;grid-area:1/2/3;align-self:center}.tile-lord{font-size:var(--roxy-text-sm,.875rem);opacity:.85;grid-column:1;align-items:center;gap:.25em;display:flex}`,\n\t];\n\n\t/**\n\t * True when the current wall-clock time falls inside this period. Both\n\t * `start` and `end` are ISO 8601 with timezone, so the comparison is\n\t * timezone-aware via the host's `Date` parsing.\n\t */\n\tprivate isCurrent(period: ChoghadiyaPeriod): boolean {\n\t\tconst now = Date.now();\n\t\tconst start = Date.parse(period.start);\n\t\tconst end = Date.parse(period.end);\n\t\tif (Number.isNaN(start) || Number.isNaN(end)) return false;\n\t\treturn now >= start && now < end;\n\t}\n\n\tprivate renderTile(period: ChoghadiyaPeriod) {\n\t\tconst effectClass =\n\t\t\tperiod.effect === 'Good'\n\t\t\t\t? 'good'\n\t\t\t\t: period.effect === 'Bad'\n\t\t\t\t\t? 'bad'\n\t\t\t\t\t: 'neutral';\n\t\tconst current = this.isCurrent(period);\n\t\tconst lordGlyph = planetGlyph(period.lord) ?? '';\n\t\tconst timeRange = formatTimeRange(this.effectiveLang(), period);\n\t\treturn html`<div class=\"cho-tile ${effectClass}${current ? ' now' : ''}\" role=\"listitem\" aria-current=\"${current ? 'time' : 'false'}\"><span class=\"tile-name\">${period.name}${current ? html`<span class=\"now-badge\">${this.t('Now')}</span>` : nothing} </span><span class=\"tile-time\" aria-label=\"${this.t('Time range')}\">${timeRange}</span> <span class=\"tile-lord\">${lordGlyph ? html`<span aria-hidden=\"true\">${lordGlyph}</span>` : nothing} ${period.lord}</span></div>`;\n\t}\n\n\tprotected renderData(d: GetChoghadiyaResponse) {\n\t\tconst { date, dayChoghadiya, nightChoghadiya } = d;\n\n\t\treturn html`<div class=\"wrap\" part=\"card\"><div class=\"header\" part=\"header\"><h2 class=\"title\">${this.t('Choghadiya')}</h2>${date ? html`<p class=\"subtitle\">${formatDate(this.effectiveLang(), date)}</p>` : nothing}</div><div class=\"cho-grid\" part=\"table\"><section class=\"period-col\" aria-label=\"${this.t('Day muhurta periods')}\"><h3 class=\"period-heading\">${this.t('Day')}</h3><div role=\"list\" aria-label=\"${this.t('Daytime choghadiya')}\">${\n\t\t\t\t\t\t\tdayChoghadiya && dayChoghadiya.length > 0\n\t\t\t\t\t\t\t\t? dayChoghadiya.map((p) => this.renderTile(p))\n\t\t\t\t\t\t\t\t: html`<p class=\"roxy-empty\" role=\"status\">${this.t('No daytime periods')}</p>`\n\t\t\t\t\t\t}</div></section><section class=\"period-col\" aria-label=\"${this.t('Night muhurta periods')}\"><h3 class=\"period-heading\">${this.t('Night')}</h3><div role=\"list\" aria-label=\"${this.t('Nighttime choghadiya')}\">${\n\t\t\t\t\t\t\tnightChoghadiya && nightChoghadiya.length > 0\n\t\t\t\t\t\t\t\t? nightChoghadiya.map((p) => this.renderTile(p))\n\t\t\t\t\t\t\t\t: html`<p class=\"roxy-empty\" role=\"status\">${this.t('No nighttime periods')}</p>`\n\t\t\t\t\t\t}</div></section></div></div>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-choghadiya-grid': RoxyChoghadiyaGrid;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { planetGlyph, signGlyph } from '../tokens/index.js';\nimport type {\n\tCalculateBioCompatibilityResponse,\n\tCalculateCompatibilityResponse,\n\tCalculateNumCompatibilityResponse,\n} from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { disclosureStyles } from '../utils/disclosure.js';\nimport {\n\tformatNumber,\n\tformatPercent,\n\tnormalizeAspect,\n} from '../utils/format.js';\nimport {\n\ttype InterpSection,\n\tinterpAccordionStyles,\n} from '../utils/interp-accordion.js';\nimport { capitalize, humanize } from '../utils/string.js';\n\ntype CompatibilityData =\n\t| CalculateCompatibilityResponse\n\t| CalculateNumCompatibilityResponse\n\t| CalculateBioCompatibilityResponse;\n\ntype AstroCompat = CalculateCompatibilityResponse;\n\n/** The four relationship planets, in the order a synastry reading works them: identity, feeling, love, desire. */\nconst RELATIONSHIP_PLANETS = ['sun', 'moon', 'venus', 'mars'] as const;\nconst ELEMENTS = ['fire', 'earth', 'air', 'water'] as const;\n\n/**\n * Cross-domain compatibility card. Renders /astrology/compatibility-score,\n * /numerology/compatibility, or /biorhythm/compatibility responses.\n */\n@customElement('roxy-compatibility-card')\nexport class RoxyCompatibilityCard extends RoxyDataElement<CompatibilityData> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tdisclosureStyles,\n\t\tinterpAccordionStyles,\n\t\tcss`.card{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.head{align-items:center;gap:var(--roxy-space-md,1rem);grid-template-columns:1fr auto;display:grid}.head h2{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);text-transform:capitalize;margin:0}.score{font-variant-numeric:tabular-nums;font-size:2rem;font-weight:var(--roxy-weight-bold,600);color:var(--roxy-accent-ink,#b45309);line-height:1}.rating{color:var(--roxy-secondary,#475569);font-size:var(--roxy-text-sm,.875rem)}.bar-row{gap:var(--roxy-space-sm,.5rem);font-size:var(--roxy-text-sm,.875rem);grid-template-columns:8rem 1fr 3.5rem;align-items:center;display:grid}.bar{background:var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-full,9999px);height:8px;overflow:hidden}.bar>span{background:var(--roxy-accent,#f59e0b);height:100%;transition:width var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,cubic-bezier(.4, 0, .2, 1));display:block}.bar-row>span:last-child{font-variant-numeric:tabular-nums;color:var(--roxy-muted,#71717a);text-align:right}.archetype{color:var(--roxy-accent-ink,#b45309);font-weight:var(--roxy-weight-bold,600)}.lists{gap:var(--roxy-space-md,1rem);grid-template-columns:repeat(auto-fit,minmax(12rem,1fr));display:grid}.lists h3{margin:0 0 var(--roxy-space-xs,.25rem);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.06em}.lists ul{padding-left:var(--roxy-space-md,1rem);margin:0}.lead{color:var(--roxy-fg,#0a0a0a);margin:0;font-weight:500}p.body{color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-sm,.875rem);margin:0;line-height:1.6}.pills{gap:var(--roxy-space-xs,.25rem);flex-wrap:wrap;display:flex}.pill{border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 60%, transparent);color:var(--roxy-fg,#0a0a0a);padding:2px 8px}.pill--success{background:color-mix(in srgb, var(--roxy-success,#16a34a) 12%, transparent);color:var(--roxy-success-fg,#166534)}.pill--danger{background:color-mix(in srgb, var(--roxy-danger,#dc2626) 12%, transparent);color:var(--roxy-danger-fg,#991b1b)}.elements{gap:var(--roxy-space-sm,.5rem);display:grid}.el-row{align-items:center;gap:var(--roxy-space-sm,.5rem);font-size:var(--roxy-text-sm,.875rem);grid-template-columns:4rem 1fr 1fr;display:grid}.el-row .el-name{text-transform:capitalize;color:var(--roxy-fg,#0a0a0a)}.el-row.head span{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);text-transform:uppercase;letter-spacing:.06em;font-weight:var(--roxy-weight-bold,600)}.el-row.head span:nth-child(2){text-align:right}.el-bar{font-variant-numeric:tabular-nums;color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);align-items:center;gap:.4rem;display:flex}.el-bar.p1{flex-direction:row-reverse}.el-bar .track{background:var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-full,9999px);flex:auto;height:8px;display:flex;overflow:hidden}.el-bar.p1 .track{justify-content:flex-end}.el-bar .track>span{background:var(--roxy-accent,#f59e0b);height:100%;display:block}.el-bar.p2 .track>span{background:var(--roxy-info,#0284c7)}.el-row.shared .el-name{font-weight:var(--roxy-weight-bold,600)}.shared-note{margin:var(--roxy-space-sm,.5rem) 0 0;font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a)}.key-aspects{padding-left:var(--roxy-space-md,1rem);font-size:var(--roxy-text-sm,.875rem);margin:0}`,\n\t];\n\n\t@property({ type: String, reflect: true })\n\tmode: 'astrology' | 'numerology' | 'biorhythm' = 'astrology';\n\n\tprivate getBreakdown(): Record<string, number> {\n\t\tconst d = this.data;\n\t\tif (!d) return {};\n\t\tif ('categories' in d && d.categories) {\n\t\t\tconst out: Record<string, number> = {};\n\t\t\tfor (const [k, v] of Object.entries(d.categories)) {\n\t\t\t\tif (typeof v === 'number' && Number.isFinite(v)) out[k] = v;\n\t\t\t}\n\t\t\treturn out;\n\t\t}\n\t\treturn {};\n\t}\n\n\tprotected renderData(d: CompatibilityData) {\n\t\tconst score = d.overallScore;\n\t\tconst breakdown = this.getBreakdown();\n\t\tconst rating =\n\t\t\t'rating' in d\n\t\t\t\t? (d as CalculateNumCompatibilityResponse).rating\n\t\t\t\t: undefined;\n\t\tconst archetype =\n\t\t\t'archetype' in d\n\t\t\t\t? (d as CalculateCompatibilityResponse).archetype\n\t\t\t\t: undefined;\n\t\tconst advice =\n\t\t\t'advice' in d\n\t\t\t\t? (d as CalculateNumCompatibilityResponse).advice\n\t\t\t\t: undefined;\n\t\tconst summary =\n\t\t\t'summary' in d\n\t\t\t\t? (d as CalculateCompatibilityResponse).summary\n\t\t\t\t: undefined;\n\t\tconst interpretation =\n\t\t\t'interpretation' in d\n\t\t\t\t? (d as CalculateCompatibilityResponse).interpretation\n\t\t\t\t: undefined;\n\t\tconst strengths = 'strengths' in d ? d.strengths : undefined;\n\t\tconst challenges = 'challenges' in d ? d.challenges : undefined;\n\t\tconst keyAspects =\n\t\t\t'keyAspects' in d\n\t\t\t\t? (d as CalculateCompatibilityResponse).keyAspects\n\t\t\t\t: undefined;\n\n\t\tconst readings = !this.hideReadings;\n\n\t\treturn html`<article class=\"card\" part=\"card\" aria-label=\"${`Compatibility (${this.mode})`}\"><div class=\"head\" part=\"header\"><h2>${humanize(`${this.mode} compatibility`)}</h2><div>${\n\t\t\t\t\t\ttypeof score === 'number'\n\t\t\t\t\t\t\t? html`<div class=\"score\">${Math.round(score)}</div>`\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t} ${rating ? html`<div class=\"rating\">${rating}</div>` : nothing}</div></div>${\n\t\t\t\tObject.keys(breakdown).length > 0\n\t\t\t\t\t? html`<div role=\"list\" part=\"details breakdown\">${Object.entries(breakdown).map(\n\t\t\t\t\t\t\t([k, v]) => html`<div class=\"bar-row\" role=\"listitem\"><span style=\"text-transform:capitalize\">${k}</span> <span class=\"bar\"><span style=\"width:${Math.max(0, Math.min(100, v))}%\"></span></span> <span>${Math.round(v)}</span></div>`,\n\t\t\t\t\t\t)}</div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\t// The archetype LABEL is the classification the score resolved to and\n\t\t\t\t// stays; only the sentence explaining it is a reading.\n\t\t\t\tarchetype\n\t\t\t\t\t? html`<p><span class=\"archetype\">${archetype.label}</span> ${archetype.description && readings ? html`· ${archetype.description}` : nothing}</p>`\n\t\t\t\t\t: nothing\n\t\t\t} ${summary && readings ? html`<p class=\"lead\">${summary}</p>` : nothing} ${interpretation && readings ? html`<p class=\"body\">${interpretation}</p>` : nothing} ${advice && readings ? html`<p class=\"body\">${advice}</p>` : nothing} ${this.renderAspectBreakdown()} ${this.renderSignCompatibility()} ${this.renderElementBalance()} ${this.renderSubScores()} ${\n\t\t\t\t// Each entry is a sentence about the pair, so the two lists are prose\n\t\t\t\t// laid out as bullets rather than data.\n\t\t\t\treadings &&\n\t\t\t\t((strengths?.length ?? 0) > 0 || (challenges?.length ?? 0) > 0)\n\t\t\t\t\t? html`<div class=\"lists\" part=\"section strengths-challenges\">${\n\t\t\t\t\t\t\tstrengths?.length\n\t\t\t\t\t\t\t\t? html`<div><h3>${this.t('Strengths')}</h3><ul>${strengths.map((s) => html`<li>${s}</li>`)}</ul></div>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t} ${\n\t\t\t\t\t\t\tchallenges?.length\n\t\t\t\t\t\t\t\t? html`<div><h3>${this.t('Challenges')}</h3><ul>${challenges.map((s) => html`<li>${s}</li>`)}</ul></div>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t}</div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\tkeyAspects?.length\n\t\t\t\t\t? html`<div class=\"lists\" part=\"section key-aspects\"><div><h3>${this.t('Key aspects')}</h3><ul class=\"key-aspects\">${keyAspects.slice(0, 6).map((a) => html`<li>${formatAspect(this.effectiveLang(), a, readings)}</li>`)}</ul></div></div>`\n\t\t\t\t\t: nothing\n\t\t\t}</article>`;\n\t}\n\n\t/** Contact balance behind the score. Astrology only. */\n\tprivate renderAspectBreakdown() {\n\t\tconst b = this.astro()?.aspectBreakdown;\n\t\tif (!b) return nothing;\n\t\treturn html`<div class=\"pills\" part=\"details aspect-breakdown\" role=\"region\" aria-label=\"${this.t('Aspect breakdown')}\">${typeof b.total === 'number' ? html`<span class=\"pill\">${this.t('Total')}: ${b.total}</span>` : nothing} <span class=\"pill pill--success\">${this.t('Harmonious')}: ${b.harmonious}</span> <span class=\"pill pill--danger\">${this.t('Challenging')}: ${b.challenging}</span> <span class=\"pill\">${this.t('Neutral')}: ${b.neutral}</span></div>`;\n\t}\n\n\t/**\n\t * Sign-by-sign reading for the four relationship planets. The aside pairs the two signs (with degrees from `persons`, which would otherwise be dropped) so the header answers \"which signs\" before the body answers \"and so what\".\n\t */\n\tprivate renderSignCompatibility() {\n\t\tconst d = this.astro();\n\t\tconst sc = d?.signCompatibility;\n\t\tif (!sc) return nothing;\n\t\tconst persons = d?.persons;\n\t\tconst sections = RELATIONSHIP_PLANETS.map(\n\t\t\t(planet): InterpSection | null => {\n\t\t\t\tconst pair = sc[planet];\n\t\t\t\tif (!pair) return null;\n\t\t\t\tconst at = (p: 'person1' | 'person2'): string => {\n\t\t\t\t\tconst sign = capitalize(\n\t\t\t\t\t\t(p === 'person1' ? pair.person1Sign : pair.person2Sign) ?? '',\n\t\t\t\t\t);\n\t\t\t\t\tconst deg = persons?.[p]?.[planet]?.degree;\n\t\t\t\t\tconst glyph = signGlyph(sign) ?? '';\n\t\t\t\t\tconst degree = typeof deg === 'number' ? ` ${Math.round(deg)}°` : '';\n\t\t\t\t\treturn `${glyph} ${sign}${degree}`.trim();\n\t\t\t\t};\n\t\t\t\treturn {\n\t\t\t\t\tlabel: `${planetGlyph(planet) ?? ''} ${capitalize(planet)}`.trim(),\n\t\t\t\t\taside: `${at('person1')} and ${at('person2')}`,\n\t\t\t\t\tbody: pair.description ?? '',\n\t\t\t\t};\n\t\t\t},\n\t\t).filter((s): s is InterpSection => s !== null);\n\t\treturn this.renderInterpretation(\n\t\t\tsections,\n\t\t\t'compat-signs',\n\t\t\t'Sign compatibility',\n\t\t);\n\t}\n\n\t/**\n\t * Elemental weight of both charts, mirrored around the element name so a shared emphasis reads as a matched pair. The shared element (or its absence) is the headline the API computes, so it is stated rather than left for the reader to spot.\n\t */\n\tprivate renderElementBalance() {\n\t\tconst eb = this.astro()?.elementBalance;\n\t\tif (!eb) return nothing;\n\t\tconst p1 = eb.person1;\n\t\tconst p2 = eb.person2;\n\t\tconst max = Math.max(\n\t\t\t1,\n\t\t\t...ELEMENTS.map((e) => Math.max(p1?.[e] ?? 0, p2?.[e] ?? 0)),\n\t\t);\n\t\tconst shared = eb.sharedElement?.toLowerCase();\n\t\treturn html`<section class=\"block\" part=\"section element-balance\"><h3>${this.t('Element balance')}</h3><div class=\"elements\"><div class=\"el-row head\"><span></span> <span>${this.t('Person 1')}</span> <span>${this.t('Person 2')}</span></div>${ELEMENTS.map((el) => {\n\t\t\t\t\tconst a = p1?.[el] ?? 0;\n\t\t\t\t\tconst b = p2?.[el] ?? 0;\n\t\t\t\t\treturn html`<div class=\"${el === shared ? 'el-row shared' : 'el-row'}\"><span class=\"el-name\">${el}</span> <span class=\"el-bar p1\"><span>${a}</span> <span class=\"track\"><span style=\"width:${(a / max) * 100}%\"></span></span> </span><span class=\"el-bar p2\"><span class=\"track\"><span style=\"width:${(b / max) * 100}%\"></span></span> <span>${b}</span></span></div>`;\n\t\t\t\t})}</div>${\n\t\t\t\t// The mirrored bars are the balance; this sentence is what it means.\n\t\t\t\teb.description && !this.hideReadings\n\t\t\t\t\t? html`<p class=\"shared-note\">${eb.description}</p>`\n\t\t\t\t\t: nothing\n\t\t\t}</section>`;\n\t}\n\n\t/**\n\t * Per-cycle readings the other two domains carry instead of astrology categories: the numerology core numbers (Life Path, Expression, Soul Urge) and the biorhythm cycle alignments. Both are labelled score-plus-prose, which is exactly what the interpretation accordion is for.\n\t */\n\tprivate renderSubScores() {\n\t\tconst d = this.data;\n\t\tif (!d) return nothing;\n\t\tconst sections: InterpSection[] = [];\n\t\tif ('lifePath' in d) {\n\t\t\tconst cores: Array<[string, typeof d.lifePath]> = [\n\t\t\t\t['Life Path', d.lifePath],\n\t\t\t\t['Expression', d.expression],\n\t\t\t\t['Soul Urge', d.soulUrge],\n\t\t\t];\n\t\t\tfor (const [label, core] of cores) {\n\t\t\t\tif (!core) continue;\n\t\t\t\tsections.push({\n\t\t\t\t\tlabel,\n\t\t\t\t\taside: `${core.person1} and ${core.person2} · ${formatPercent(this.effectiveLang(), core.compatibility, 0)}`,\n\t\t\t\t\tbody: core.description ?? '',\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tif ('cycles' in d && d.cycles) {\n\t\t\tfor (const [name, cycle] of Object.entries(d.cycles)) {\n\t\t\t\tif (!cycle) continue;\n\t\t\t\tsections.push({\n\t\t\t\t\tlabel: capitalize(name),\n\t\t\t\t\taside: `${formatPercent(this.effectiveLang(), cycle.alignment, 0)} in step · ${humanize(cycle.phase ?? '')}`,\n\t\t\t\t\tbody: cycle.description ?? '',\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn this.renderInterpretation(\n\t\t\tsections,\n\t\t\t'compat-sub-scores',\n\t\t\t'Breakdown',\n\t\t);\n\t}\n\n\t/** The astrology response, or undefined when the card is showing another domain. `signCompatibility` is unique to it, so it is the discriminator. */\n\tprivate astro(): AstroCompat | undefined {\n\t\tconst d = this.data;\n\t\treturn d && 'signCompatibility' in d ? (d as AstroCompat) : undefined;\n\t}\n}\n\ntype KeyAspect = CalculateCompatibilityResponse extends {\n\tkeyAspects: Array<infer T>;\n}\n\t? T\n\t: never;\n\n/** \"Sun trine Moon (orb 2.4°)\", with the written reading of the contact appended unless the host asked for the data alone. */\nfunction formatAspect(\n\tlocale: string | undefined,\n\ta: KeyAspect,\n\twithReading: boolean,\n): string {\n\t// Lowercase on purpose: this lands mid-sentence as \"Sun trine Moon\".\n\tconst aspect = normalizeAspect(a);\n\tconst orb =\n\t\ttypeof a.orb === 'number'\n\t\t\t? ` (orb ${formatNumber(locale, a.orb, 1)}°)`\n\t\t\t: '';\n\tconst head = [a.planet1, aspect, a.planet2].filter(Boolean).join(' ');\n\treturn a.description && withReading\n\t\t? `${head}${orb} · ${a.description}`\n\t\t: `${head}${orb}`;\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-compatibility-card': RoxyCompatibilityCard;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport type { GetCrystalResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { cssColor } from '../utils/css-color.js';\nimport { monthName } from '../utils/format.js';\n\n/**\n * Single-crystal detail card. Renders /crystals/{id}: the stone's photo, description, and full metaphysical profile (spiritual / emotional / physical meaning, governing chakras, zodiac signs, planet, elements, colours, Mohs hardness, numerical vibration, birthstone month), plus its affirmation and the crystals it pairs with. This is the detail view; roxy-crystal-grid is the gallery.\n *\n * @remarks\n * The card is half mineral record and half reading, and the split is clean.\n * `hide-readings` keeps the photo, the name, the whole attribute grid (planet,\n * Mohs hardness, numerical vibration, birthstone month, chakras, zodiac signs,\n * elements and the colour swatches) and the crystals it pairs with, which is a\n * catalogue relation rather than a claim about the stone. It drops the\n * description, the spiritual, emotional and physical meanings, the keyword chips\n * that belong to them, and the affirmation. The meaning block is three headed\n * paragraphs and nothing else, so it goes whole.\n */\n@customElement('roxy-crystal-card')\nexport class RoxyCrystalCard extends RoxyDataElement<GetCrystalResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.wrap{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.hero{gap:var(--roxy-space-md,1rem);align-items:flex-start;display:flex}.photo{border-radius:var(--roxy-radius-md,8px);object-fit:cover;background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 35%, transparent);flex:none;width:96px;height:96px}.title{margin:0 0 var(--roxy-space-xs,.25rem) 0;font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600)}.desc{line-height:1.55;font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a);margin:0}.attrs{gap:var(--roxy-space-sm,.5rem);grid-template-columns:repeat(auto-fit,minmax(7rem,1fr));display:grid}.attr{gap:2px;display:grid}.attr dt{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.05em}.attr dd{font-size:var(--roxy-text-sm,.875rem);margin:0;font-weight:500}.colors{flex-wrap:wrap;gap:.4rem;display:flex}.color{font-size:var(--roxy-text-xs,.75rem);text-transform:capitalize;align-items:center;gap:.3rem;display:inline-flex}.dot{border-radius:var(--roxy-radius-full,9999px);border:1px solid color-mix(in srgb, var(--roxy-fg,#0a0a0a) 18%, transparent);width:.7rem;height:.7rem}.meaning h3{margin:0 0 var(--roxy-space-xs,.25rem) 0;font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.05em}.meaning p{margin:0 0 var(--roxy-space-sm,.5rem) 0;font-size:var(--roxy-text-sm,.875rem);line-height:1.55}.chips{flex-wrap:wrap;gap:.3rem;display:flex}.chip{border-radius:var(--roxy-radius-full,9999px);background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 14%, transparent);color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-xs,.75rem);text-transform:capitalize;padding:1px 8px}.section-label{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.06em;font-weight:var(--roxy-weight-bold,600);margin:0 0 var(--roxy-space-xs,.25rem) 0}.affirmation{padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);border-left:3px solid var(--roxy-accent,#f59e0b);background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 8%, transparent);font-style:italic;font-size:var(--roxy-text-sm,.875rem);border-radius:0 var(--roxy-radius-sm,4px) var(--roxy-radius-sm,4px) 0;margin:0}`,\n\t];\n\n\tprotected renderData(d: GetCrystalResponse) {\n\t\tconst colors = d.colors ?? [];\n\t\tconst keywords = d.keywords ?? [];\n\t\tconst pairs = d.pairsWith ?? [];\n\t\tconst month = monthName(this.effectiveLang(), d.birthMonth) || undefined;\n\n\t\treturn html`<article class=\"wrap\" part=\"card\" aria-label=\"${d.name ?? 'Crystal'}\"><div class=\"hero\" part=\"header\">${d.imageUrl ? html`<img class=\"photo\" part=\"chart\" src=\"${d.imageUrl}\" alt=\"${d.name ?? 'Crystal'}\" loading=\"lazy\">` : nothing}<div><h2 class=\"title\">${d.name}</h2>${\n\t\t\t\t\t\t// The grid below is the stone; this paragraph is what it is said to do.\n\t\t\t\t\t\td.description && !this.hideReadings\n\t\t\t\t\t\t\t? html`<p class=\"desc\">${d.description}</p>`\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t}</div></div><dl class=\"attrs\" part=\"details\">${this.attr(this.t('Planet'), d.planet)} ${this.attr(this.t('Hardness'), typeof d.hardness === 'number' ? `${d.hardness} Mohs` : undefined)} ${this.attr(this.t('Vibration'), d.numericalVibration)} ${this.attr(this.t('Birthstone'), month)} ${this.list(this.t('Chakras'), d.chakras)} ${this.list(this.t('Zodiac'), d.zodiacSigns)} ${this.list(this.t('Elements'), d.elements)} ${\n\t\t\t\t\tcolors.length\n\t\t\t\t\t\t? html`<div class=\"attr\"><dt>${this.t('Colors')}</dt><dd><div class=\"colors\">${colors.map((c) => html`<span class=\"color\"><span class=\"dot\" style=\"${`background:${cssColor(c)}`}\"></span>${c}</span>`)}</div></dd></div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</dl>${this.renderMeaning(d.meaning)} ${\n\t\t\t\tkeywords.length && !this.hideReadings\n\t\t\t\t\t? html`<div part=\"section keywords\"><p class=\"section-label\">${this.t('Keywords')}</p><div class=\"chips\">${keywords.map((k) => html`<span class=\"chip\">${k}</span>`)}</div></div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${d.affirmation && !this.hideReadings ? html`<p class=\"affirmation\">${d.affirmation}</p>` : nothing} ${\n\t\t\t\tpairs.length\n\t\t\t\t\t? html`<div part=\"section pairs-with\"><p class=\"section-label\">${this.t('Pairs with')}</p><div class=\"chips\">${pairs.map((p) => html`<span class=\"chip\">${String(p).replace(/-/g, ' ')}</span>`)}</div></div>`\n\t\t\t\t\t: nothing\n\t\t\t}</article>`;\n\t}\n\n\tprivate attr(label: string, value: string | number | null | undefined) {\n\t\tif (value === undefined || value === null || value === '') return nothing;\n\t\treturn html`<div class=\"attr\"><dt>${label}</dt><dd>${value}</dd></div>`;\n\t}\n\n\tprivate list(label: string, values: readonly string[] | null | undefined) {\n\t\tif (!values?.length) return nothing;\n\t\treturn html`<div class=\"attr\"><dt>${label}</dt><dd>${values.join(', ')}</dd></div>`;\n\t}\n\n\tprivate renderMeaning(m: GetCrystalResponse['meaning'] | undefined) {\n\t\t// Three headed paragraphs and nothing else, so the block goes whole rather\n\t\t// than leaving Spiritual, Emotional and Physical over nothing.\n\t\tif (!m || this.hideReadings) return nothing;\n\t\tconst rows: Array<[string, string | null | undefined]> = [\n\t\t\t[this.t('Spiritual'), m.spiritual],\n\t\t\t[this.t('Emotional'), m.emotional],\n\t\t\t[this.t('Physical'), m.physical],\n\t\t];\n\t\tconst present = rows.filter(([, v]) => Boolean(v));\n\t\tif (present.length === 0) return nothing;\n\t\treturn html`<div class=\"meaning\" part=\"section meaning\">${present.map(([label, text]) => html`<h3>${label}</h3><p>${text}</p>`)}</div>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-crystal-card': RoxyCrystalCard;\n\t}\n}\n", "/**\n * Resolve a crystal colour keyword to a paintable CSS colour.\n *\n * @remarks\n * Two thirds of the 92 colour values the crystals API serves are COMPOUNDS (\"blue-green\", \"pale brass-yellow\", \"deep brownish red\"), and a compound is never a valid CSS colour, so painting the raw string leaves the swatch blank. Lowercasing and stripping spaces is not enough either, since that turns \"apple green\" into `applegreen` and blanks just the same. One resolver serves both crystal components.\n */\n\n/**\n * Colour words the crystals API returns that CSS cannot resolve, mapped to their closest CSS value. Also collapses the `-ish` and plural forms onto their base word, so \"bluish\" and \"browns\" resolve like \"blue\" and \"brown\".\n */\nconst COLOR_WORDS: Record<string, string> = {\n\tapple: '#8db600',\n\tazure: '#007fff',\n\tblackish: 'black',\n\tbluish: 'blue',\n\tbottle: '#006a4e',\n\tbrass: '#b5a642',\n\tbrick: '#b22222',\n\tbrownish: 'brown',\n\tbrowns: 'brown',\n\tburgundy: '#800020',\n\tclear: '#eef2f6',\n\tcolorless: '#eef2f6',\n\tcolourless: '#eef2f6',\n\tcream: '#fffdd0',\n\tcreamy: '#fffdd0',\n\temerald: '#50c878',\n\tgolden: 'gold',\n\tgreenish: 'green',\n\tlilac: '#c8a2c8',\n\tmauve: '#e0b0ff',\n\tpeach: 'peachpuff',\n\tpinkish: 'pink',\n\treddish: 'red',\n\trose: '#f4a6b7',\n\troyal: 'royalblue',\n\tsilvery: 'silver',\n\tsky: 'skyblue',\n\tyellowish: 'yellow',\n};\n\n/** CSS named colours the crystals vocabulary uses verbatim. A word outside this set and {@link COLOR_WORDS} is not a colour, so it is dropped rather than passed through: one unresolvable token would otherwise invalidate the whole color-mix and blank the swatch. */\nconst CSS_COLORS = new Set([\n\t'beige',\n\t'black',\n\t'blue',\n\t'brown',\n\t'crimson',\n\t'cyan',\n\t'gold',\n\t'gray',\n\t'green',\n\t'grey',\n\t'indigo',\n\t'lavender',\n\t'magenta',\n\t'maroon',\n\t'navy',\n\t'olive',\n\t'orange',\n\t'pink',\n\t'purple',\n\t'red',\n\t'salmon',\n\t'silver',\n\t'tan',\n\t'teal',\n\t'turquoise',\n\t'violet',\n\t'white',\n\t'yellow',\n]);\n\n/** Qualifiers that shade a base colour instead of naming one: the mix partner, and how much of the base survives the mix. */\nconst COLOR_QUALIFIERS: Record<string, [partner: string, keepPct: number]> = {\n\tbright: ['white', 85],\n\tdark: ['black', 60],\n\tdeep: ['black', 70],\n\tfaintly: ['white', 30],\n\tlight: ['white', 55],\n\tpale: ['white', 45],\n\trich: ['black', 80],\n};\n\n/**\n * Resolve a crystal colour keyword to a paintable CSS colour.\n *\n * @remarks\n * Two thirds of the 92 colour values the crystals API serves are compounds (\"blue-green\", \"pale brass-yellow\", \"deep brownish red\"), and a compound is never a CSS colour, so painting the raw string leaves the swatch blank. Split on spaces and hyphens instead: qualifiers ({@link COLOR_QUALIFIERS}) shade, the first two colour words mix, and anything unrecognised is dropped. A value with no colour word at all (\"banded\") falls back to the border token rather than painting nothing.\n *\n *\n * @example\n * ```ts\n * cssColor('blue-green');   // color-mix(in srgb, blue, green)\n * cssColor('pale yellow');  // color-mix(in srgb, yellow 45%, white)\n * ```\n */\nexport function cssColor(name: string): string {\n\tconst bases: string[] = [];\n\tconst qualifiers: Array<[string, number]> = [];\n\tfor (const word of name.toLowerCase().split(/[\\s-]+/)) {\n\t\tconst qualifier = COLOR_QUALIFIERS[word];\n\t\tif (qualifier) {\n\t\t\tqualifiers.push(qualifier);\n\t\t\tcontinue;\n\t\t}\n\t\tconst base = COLOR_WORDS[word] ?? (CSS_COLORS.has(word) ? word : '');\n\t\tif (base) bases.push(base);\n\t}\n\n\tconst [first, second] = bases;\n\tif (!first) return 'var(--roxy-border, #e4e4e7)';\n\tlet color = second ? `color-mix(in srgb, ${first}, ${second})` : first;\n\tfor (const [partner, keepPct] of qualifiers) {\n\t\tcolor = `color-mix(in srgb, ${color} ${keepPct}%, ${partner})`;\n\t}\n\treturn color;\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport type {\n\tGetBirthstonesResponse,\n\tGetCrystalsByChakraResponse,\n\tGetCrystalsByElementResponse,\n\tGetCrystalsByZodiacResponse,\n\tListCrystalsResponse,\n\tSearchCrystalsResponse,\n} from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { cssColor } from '../utils/css-color.js';\nimport { monthName } from '../utils/format.js';\n\n/**\n * Any crystal list response that carries a `crystals` summary array. Every crystals endpoint that returns more than one stone shares the `{ name, id, imageUrl, colors }` item shape, so one grid renders them all.\n */\ntype CrystalGridData =\n\t| ListCrystalsResponse\n\t| GetCrystalsByChakraResponse\n\t| GetCrystalsByElementResponse\n\t| GetCrystalsByZodiacResponse\n\t| GetBirthstonesResponse\n\t| SearchCrystalsResponse;\n\n/**\n * Crystal grid. Renders any crystals list response (/crystals, /crystals/chakra/{chakra}, /crystals/element/{element}, /crystals/zodiac/{sign}, /crystals/birthstone/{month}, /crystals/search) as a responsive gallery of crystal tiles with photo, name, and colour swatches. The heading is derived from the response filter (chakra, element, zodiac sign, or birth month) or set explicitly via the `heading` attribute.\n */\n@customElement('roxy-crystal-grid')\nexport class RoxyCrystalGrid extends RoxyDataElement<CrystalGridData> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.wrap{gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.head{justify-content:space-between;align-items:baseline;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-fg,#0a0a0a);margin:0}.count{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem)}.grid{gap:var(--roxy-space-md,1rem);grid-template-columns:repeat(auto-fill,minmax(8rem,1fr));margin:0;padding:0;list-style:none;display:grid}.tile{gap:var(--roxy-space-xs,.25rem);background:var(--roxy-surface,#fff);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-sm,.5rem);box-shadow:var(--roxy-shadow-sm);display:grid}.photo{aspect-ratio:1;border-radius:var(--roxy-radius-sm,4px);object-fit:cover;background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 35%, transparent);width:100%}.name{font-size:var(--roxy-text-sm,.875rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-fg,#0a0a0a);margin:0}.colors{flex-wrap:wrap;gap:4px;display:flex}.swatch{border-radius:var(--roxy-radius-full,9999px);border:1px solid color-mix(in srgb, var(--roxy-fg,#0a0a0a) 18%, transparent);width:10px;height:10px}`,\n\t];\n\n\t/**\n\t * Override the auto-derived grid heading. Empty by default, in which case the heading comes from the response filter (chakra, element, zodiac, or birth month) or falls back to \"Crystals\".\n\t */\n\t@property({ type: String, reflect: true })\n\theading = '';\n\n\tprotected renderData(d: CrystalGridData) {\n\t\tconst crystals = d.crystals ?? [];\n\t\tif (crystals.length === 0) return this.renderEmpty();\n\n\t\tconst title = this.heading || this.deriveHeading(d);\n\t\tconst total =\n\t\t\t'total' in d && typeof d.total === 'number' ? d.total : crystals.length;\n\n\t\treturn html`<section class=\"wrap\" part=\"card\" aria-label=\"${title}\"><header class=\"head\" part=\"header\"><h2 class=\"title\">${title}</h2><span class=\"count\">${total} ${total === 1 ? 'crystal' : 'crystals'}</span></header><ul class=\"grid\" part=\"section crystals\">${crystals.map(\n\t\t\t\t\t(c) => html`<li class=\"tile\">${\n\t\t\t\t\t\t\tc.imageUrl\n\t\t\t\t\t\t\t\t? html`<img class=\"photo\" src=\"${c.imageUrl}\" alt=\"${c.name ?? 'Crystal'}\" loading=\"lazy\">`\n\t\t\t\t\t\t\t\t: html`<div class=\"photo\" aria-hidden=\"true\"></div>`\n\t\t\t\t\t\t}<p class=\"name\">${c.name}</p>${\n\t\t\t\t\t\t\tc.colors && c.colors.length > 0\n\t\t\t\t\t\t\t\t? html`<div class=\"colors\" aria-label=\"${`Colours: ${c.colors.join(', ')}`}\">${c.colors.map((col) => html`<span class=\"swatch\" style=\"${`background:${cssColor(col)}`}\" title=\"${col}\"></span>`)}</div>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t}</li>`,\n\t\t\t\t)}</ul></section>`;\n\t}\n\n\tprivate deriveHeading(d: CrystalGridData): string {\n\t\tif ('chakra' in d && d.chakra)\n\t\t\treturn this.t('{{chakra}} chakra crystals', { chakra: d.chakra });\n\t\tif ('element' in d && d.element)\n\t\t\treturn this.t('{{element}} element crystals', { element: d.element });\n\t\tif ('sign' in d && d.sign)\n\t\t\treturn this.t('Crystals for {{sign}}', { sign: d.sign });\n\t\tif ('month' in d && typeof d.month === 'number')\n\t\t\treturn this.t('{{month}} birthstones', {\n\t\t\t\tmonth: monthName(this.effectiveLang(), d.month),\n\t\t\t});\n\t\treturn this.t('Crystals');\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-crystal-grid': RoxyCrystalGrid;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport type { ChromeString } from '../i18n/chrome-strings.js';\nimport type {\n\tGetCurrentDashaResponse,\n\tGetMajorDashasResponse,\n\tGetPranaDashasResponse,\n\tGetPratyantardashasResponse,\n\tGetSookshmaDashasResponse,\n\tGetSubDashasResponse,\n} from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { disclosureStyles } from '../utils/disclosure.js';\nimport {\n\ttype DateGrain,\n\tformatAyanamsa,\n\tformatDate,\n\tformatDateGrain,\n\tformatDateTime,\n\tformatNumber,\n} from '../utils/format.js';\nimport { type HouseThemes, houseWords } from '../utils/house-themes.js';\nimport {\n\ttype InterpSection,\n\tinterpAccordionStyles,\n} from '../utils/interp-accordion.js';\nimport { renderTablist, tablistStyles } from '../utils/tablist.js';\n\ntype DashaData =\n\t| GetCurrentDashaResponse\n\t| GetMajorDashasResponse\n\t| GetSubDashasResponse\n\t| GetPratyantardashasResponse\n\t| GetSookshmaDashasResponse\n\t| GetPranaDashasResponse;\n\ntype DashaPeriod = GetMajorDashasResponse['mahadashas'][number];\ntype Remaining = GetCurrentDashaResponse['remainingInMahadasha'];\n\n/**\n * The Vimshottari level a listed period sits at, so no view ever labels an\n * antardasha a mahadasha.\n *\n * Read from the RESPONSE, not from the `period` attribute: all three drill-down\n * routes are `period=\"sub\"`, and only the payload says whether the list is\n * antardashas, pratyantardashas or sookshma dashas.\n */\nfunction levelOf(d: DashaData): ChromeString {\n\t// Deepest level first, matching parentOf. No response carries two of these\n\t// keys today, so the order is not yet load-bearing; it is the order that stays\n\t// correct when a level is added, since a shallower key that a deeper payload\n\t// also carried would otherwise win the chain and mislabel it.\n\tif ('pranaDashas' in d) return 'Prana';\n\tif ('sookshmaDashas' in d) return 'Sookshma';\n\tif ('pratyantardashas' in d) return 'Pratyantardasha';\n\tif ('antardashas' in d) return 'Antardasha';\n\treturn 'Mahadasha';\n}\n\n/** The parent period a drill-down response hangs off, with the level it sits at. */\nfunction parentOf(\n\td: DashaData,\n): { source: ChromeString; period: DashaPeriod } | undefined {\n\tif ('sookshmaPeriod' in d && d.sookshmaPeriod)\n\t\treturn { source: 'Sookshma', period: d.sookshmaPeriod };\n\tif ('pratyantardashaPeriod' in d && d.pratyantardashaPeriod)\n\t\treturn { source: 'Pratyantardasha', period: d.pratyantardashaPeriod };\n\tif ('antardashaPeriod' in d && d.antardashaPeriod)\n\t\treturn { source: 'Antardasha', period: d.antardashaPeriod };\n\tif ('mahadashaPeriod' in d && d.mahadashaPeriod)\n\t\treturn { source: 'Mahadasha', period: d.mahadashaPeriod };\n\treturn undefined;\n}\n\n/** \"8y 11m 5d\", the form a dasha reader expects for a remaining balance. */\nfunction formatBalance(b: Remaining | undefined): string {\n\tif (!b) return '';\n\tconst parts: string[] = [];\n\tif (b.years) parts.push(`${b.years}y`);\n\tif (b.months) parts.push(`${b.months}m`);\n\tif (b.days) parts.push(`${b.days}d`);\n\treturn parts.length ? parts.join(' ') : '0d';\n}\n\n/**\n * The houses a period lord acts on, as words: `[2, 7, 8]` reads \"wealth, marriage, longevity\".\n *\n * `strongHouses` is preferred over `signifiedHouses`: it is the grade A and B subset a KP reading acts on, two or three houses rather than up to eight, which is what keeps this to one line beside a bar. Empty unless the request asked for significators, since both the map and the house lists are absent otherwise.\n */\nfunction periodHouseWords(\n\tsig: DashaPeriod['significators'],\n\tthemes: HouseThemes,\n): string {\n\treturn houseWords(\n\t\tsig?.strongHouses?.length ? sig.strongHouses : sig?.signifiedHouses,\n\t\tthemes,\n\t);\n}\n\n/**\n * \"Jun 17, 2018 to Jun 17, 2035\" for the accordion aside.\n *\n * @remarks\n * A prana period, and a deep sookshma, opens and closes on the same calendar day, so the date alone printed \"Aug 8, 2026 to Aug 8, 2026\" and named no span at all. The clock is added only when the two ends collapse, so every longer period keeps the shorter form.\n */\nfunction formatSpan(\n\tlocale: string | undefined,\n\tp: { startDate?: string; endDate?: string },\n): string {\n\tconst start = p.startDate ? formatDate(locale, p.startDate) : '';\n\tconst end = p.endDate ? formatDate(locale, p.endDate) : '';\n\tif (start && start === end) {\n\t\treturn `${formatDateTime(locale, p.startDate)} to ${formatDateTime(locale, p.endDate)}`;\n\t}\n\tif (start && end) return `${start} to ${end}`;\n\treturn start || end;\n}\n\n/**\n * Dasha timeline. Renders /vedic-astrology/dasha/{current,major,sub/{...}}.\n *\n * @remarks\n * Default mode shows all five active levels, mahadasha through prana, with the\n * reading each one carries. `period=\"major\"` draws the full 120-year\n * Vimshottari timeline; `period=\"sub\"` draws the antardashas inside one\n * mahadasha and states which mahadasha they sit in, since an antardasha means\n * nothing without its parent lord.\n *\n * When the request asked for significators, each period also names the houses\n * its lord acts on, in words taken from the response rather than from any table\n * held here, so the words arrive in the language the caller requested.\n */\n@customElement('roxy-dasha-timeline')\nexport class RoxyDashaTimeline extends RoxyDataElement<DashaData> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tdisclosureStyles,\n\t\tinterpAccordionStyles,\n\t\ttablistStyles,\n\t\tcss`.wrap{gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.head{justify-content:space-between;align-items:center;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.nakshatra{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem)}.current{gap:var(--roxy-space-md,1rem);background:var(--roxy-surface,#fff);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-md,1rem);box-shadow:var(--roxy-shadow-sm);grid-template-columns:repeat(auto-fit,minmax(10rem,1fr));display:grid}.current div span:first-child{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);text-transform:uppercase;letter-spacing:.06em;display:block}.current div strong{font-size:var(--roxy-text-base,1rem);color:var(--roxy-fg,#0a0a0a)}.current div small{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);display:block}.parent{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-muted,#71717a);border-left:2px solid var(--roxy-border,#e4e4e7);padding-left:var(--roxy-space-sm,.5rem);margin:0}.timeline{gap:var(--roxy-space-xs,.25rem);display:grid}.bar{gap:var(--roxy-space-sm,.5rem);font-size:var(--roxy-text-sm,.875rem);grid-template-columns:5rem 1fr 8rem;align-items:center;row-gap:.15rem;display:grid}.bar.now strong{color:var(--roxy-accent-ink,#b45309)}.now-badge{font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-accent-ink,#b45309);text-transform:uppercase;letter-spacing:.06em;margin-left:.4em;display:inline-block}.bar-track{background:var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-full,9999px);height:14px;position:relative;overflow:hidden}.bar-fill{background:var(--roxy-accent,#f59e0b);opacity:.45;height:100%;transition:width var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,cubic-bezier(.4, 0, .2, 1));display:block}.bar-now .bar-fill{opacity:1}.bar-progress{background:var(--roxy-accent-ink,#b45309);width:2px;box-shadow:0 0 0 2px color-mix(in srgb, var(--roxy-accent,#f59e0b) 35%, transparent);border-radius:2px;position:absolute;top:-2px;bottom:-2px}.dates{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);font-variant-numeric:tabular-nums;text-align:right}.bar.fine{grid-template-columns:5rem 1fr}.bar.fine .dates{text-align:left;grid-column:1/-1;min-width:0}.bar .houses{min-width:0;color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);grid-column:1/-1}[role=tabpanel]{padding-top:var(--roxy-space-md,1rem)}.frame{gap:var(--roxy-space-sm,.5rem);grid-template-columns:repeat(auto-fit,minmax(12rem,1fr));margin:0;display:grid}.frame dt{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);text-transform:uppercase;letter-spacing:.06em}.frame dd{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a);font-variant-numeric:tabular-nums;margin:0}.block{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);background:var(--roxy-surface,#fff);padding:var(--roxy-space-md,1rem)}`,\n\t];\n\n\t/**\n\t * Which dasha endpoint fed this element. `sub`, `antara`, `sookshma` and\n\t * `prana` are the four drill-down levels and render identically; they stay\n\t * distinct because each maps one-to-one onto its endpoint binding, which is\n\t * what lets a widget pick a level. The heading and the level LABELS come from\n\t * the payload, not from here, so a host that cannot set attributes still\n\t * renders correctly.\n\t */\n\t@property({ type: String, reflect: true })\n\tperiod: 'current' | 'major' | 'sub' | 'antara' | 'sookshma' | 'prana' =\n\t\t'current';\n\n\t/** Which panel is showing. Reset guarded in renderData when the data changes shape. */\n\t@state()\n\tprivate view: 'timeline' | 'readings' | 'frame' = 'timeline';\n\n\tprotected renderData(d: DashaData) {\n\t\tconst periods = this.collectPeriods(d);\n\t\tconst maxYears = periods.length\n\t\t\t? Math.max(...periods.map((p) => p.durationYears))\n\t\t\t: 0;\n\t\t// Grain comes from the SHORTEST bar, not the longest: the column has to\n\t\t// resolve every row, and the set is never uniform. A sookshma set whose\n\t\t// longest member is 73 days picked month grain, and its 8 day members\n\t\t// then printed both ends as the same month: `Saturn Jan 1990 - Jan 1990`.\n\t\tconst grain = grainFor(\n\t\t\tperiods.length ? Math.min(...periods.map((p) => p.durationYears)) : 0,\n\t\t);\n\t\t// Empty when readings are hidden, which drops the tab as well as the panel:\n\t\t// a \"Readings (0)\" tab onto a blank panel is worse than no tab.\n\t\tconst readings = this.hideReadings ? [] : this.readings(d, periods);\n\t\tconst frame = this.frameRows(d);\n\n\t\t// One panel per kind of question a reader asks: when do the periods run,\n\t\t// what do they mean, and what chart produced them. Without the split the\n\t\t// card became a single column of periods, prose and provenance, and the\n\t\t// dates (the reason anyone opens it) were pushed below the fold.\n\t\tconst tabs = [\n\t\t\t{ id: 'timeline' as const, label: this.t('Timeline') },\n\t\t\t...(readings.length\n\t\t\t\t? [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tid: 'readings' as const,\n\t\t\t\t\t\t\tlabel: this.t('Readings ({{count}})', { count: readings.length }),\n\t\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t: []),\n\t\t\t// \"Chart details\", never \"Chart\": this panel is the sidereal frame the\n\t\t\t// dates came from, not a wheel. A reader who clicks a tab labelled Chart\n\t\t\t// expects to see one drawn.\n\t\t\t...(frame.length\n\t\t\t\t? [{ id: 'frame' as const, label: this.t('Chart details') }]\n\t\t\t\t: []),\n\t\t];\n\t\t// A tab that no longer exists (data changed under a selection) must not\n\t\t// blank the card, so fall back to the first.\n\t\tconst view = tabs.some((t) => t.id === this.view) ? this.view : 'timeline';\n\n\t\treturn html`<div class=\"wrap\" part=\"card\" aria-label=\"${this.t('Dasha timeline')}\"><header class=\"head\" part=\"header\"><h2 class=\"title\">${this.heading(d)}</h2>${this.renderLordChain(d)}</header>${this.renderParentMahadasha(d)} ${\n\t\t\t\ttabs.length > 1\n\t\t\t\t\t? renderTablist({\n\t\t\t\t\t\t\titems: tabs,\n\t\t\t\t\t\t\tactive: view,\n\t\t\t\t\t\t\tonSelect: (v) => {\n\t\t\t\t\t\t\t\tthis.view = v;\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tlabel: this.t('Dasha views'),\n\t\t\t\t\t\t\tidPrefix: 'roxy-dasha',\n\t\t\t\t\t\t\tcontrols: true,\n\t\t\t\t\t\t})\n\t\t\t\t\t: nothing\n\t\t\t}<div id=\"roxy-dasha-panel-${view}\" part=\"panel\" role=\"tabpanel\" tabindex=\"0\" aria-labelledby=\"${tabs.length > 1 ? `roxy-dasha-tab-${view}` : nothing}\">${\n\t\t\t\t\tview === 'timeline'\n\t\t\t\t\t\t? html`${this.period === 'current' ? this.renderCurrent(d) : nothing} ${\n\t\t\t\t\t\t\t\tperiods.length > 0\n\t\t\t\t\t\t\t\t\t? html`<div class=\"timeline\" part=\"chart timeline\" role=\"list\">${periods.map((p) => this.renderBar(p, maxYears, grain, d.houseThemes))}</div>`\n\t\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t\t}`\n\t\t\t\t\t\t: nothing\n\t\t\t\t} ${view === 'readings' ? this.renderReadings(readings) : nothing} ${view === 'frame' ? this.renderFrame(frame) : nothing}</div></div>`;\n\t}\n\n\t/**\n\t * The full lord chain a drill-down sits under, e.g. Saturn > Venus > Rahu.\n\t *\n\t * The response carries every ancestor lord on each period, and naming only the\n\t * immediate parent leaves a reader at the sookshma level unable to tell which\n\t * branch of the chart they are looking at.\n\t */\n\tprivate renderLordChain(d: DashaData) {\n\t\tconst chain = [\n\t\t\t'mahadashaLord' in d ? d.mahadashaLord : undefined,\n\t\t\t'antardashaLord' in d ? d.antardashaLord : undefined,\n\t\t\t'pratyantardashaLord' in d ? d.pratyantardashaLord : undefined,\n\t\t\t'sookshmaLord' in d ? d.sookshmaLord : undefined,\n\t\t].filter(Boolean);\n\t\tif (chain.length > 1) {\n\t\t\treturn html`<div class=\"nakshatra\">${chain.join(' \\u203a ')}</div>`;\n\t\t}\n\t\tif ('nakshatraName' in d && d.nakshatraName) {\n\t\t\treturn html`<div class=\"nakshatra\">${\n\t\t\t\t\t'nakshatraLord' in d && d.nakshatraLord\n\t\t\t\t\t\t? this.t('Moon nakshatra: {{name}} (lord {{lord}})', {\n\t\t\t\t\t\t\t\tname: d.nakshatraName,\n\t\t\t\t\t\t\t\tlord: d.nakshatraLord,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t: this.t('Moon nakshatra: {{name}}', { name: d.nakshatraName })\n\t\t\t\t}</div>`;\n\t\t}\n\t\treturn nothing;\n\t}\n\n\t/**\n\t * Provenance rows: the chart every date in this response was derived from.\n\t *\n\t * These are the fields that let a reader reconcile our dates against another\n\t * calculator. The sidereal frame is the one that actually explains a\n\t * disagreement, since Lahiri and the KP variants move every boundary by weeks,\n\t * so it is shown rather than left in the JSON.\n\t */\n\tprivate frameRows(d: DashaData): Array<[string, string]> {\n\t\tconst rows: Array<[string, string]> = [];\n\t\tif ('nakshatraName' in d && d.nakshatraName) {\n\t\t\tconst n =\n\t\t\t\t'moonNakshatra' in d && d.moonNakshatra\n\t\t\t\t\t? ` (${d.moonNakshatra} of 27)`\n\t\t\t\t\t: '';\n\t\t\trows.push(['Moon nakshatra', `${d.nakshatraName}${n}`]);\n\t\t}\n\t\tif ('nakshatraLord' in d && d.nakshatraLord) {\n\t\t\trows.push(['Nakshatra lord', d.nakshatraLord]);\n\t\t}\n\t\tif ('moonLongitude' in d && typeof d.moonLongitude === 'number') {\n\t\t\trows.push([\n\t\t\t\t'Moon longitude',\n\t\t\t\t`${formatNumber(this.effectiveLang(), d.moonLongitude, 3)}\\u00b0 sidereal`,\n\t\t\t]);\n\t\t}\n\t\tif ('ayanamsaType' in d && d.ayanamsaType) {\n\t\t\trows.push([\n\t\t\t\t'Ayanamsa',\n\t\t\t\tformatAyanamsa(\n\t\t\t\t\tthis.effectiveLang(),\n\t\t\t\t\td.ayanamsaType,\n\t\t\t\t\t'ayanamsa' in d ? d.ayanamsa : undefined,\n\t\t\t\t),\n\t\t\t]);\n\t\t}\n\t\tif ('birthDashaBalance' in d && d.birthDashaBalance) {\n\t\t\tconst lord =\n\t\t\t\t'nakshatraLord' in d && d.nakshatraLord ? `${d.nakshatraLord} ` : '';\n\t\t\trows.push([\n\t\t\t\t'Balance at birth',\n\t\t\t\t`${formatBalance(d.birthDashaBalance)} of the opening ${lord}mahadasha`,\n\t\t\t]);\n\t\t}\n\t\tif ('totalYears' in d && typeof d.totalYears === 'number') {\n\t\t\trows.push(['Cycle length', `${d.totalYears} years`]);\n\t\t}\n\t\treturn rows;\n\t}\n\n\tprivate renderFrame(rows: Array<[string, string]>) {\n\t\treturn html`<dl class=\"frame\" part=\"details frame\">${rows.map(([label, value]) => html`<div><dt>${label}</dt><dd>${value}</dd></div>`)}</dl>`;\n\t}\n\n\t/**\n\t * Heading, decided by the PAYLOAD first and the `period` attribute only as a\n\t * tie-break.\n\t *\n\t * Not every host can set the attribute. The WordPress plugin maps an\n\t * operationId to a bare component tag with no attrs, so every dasha shortcode\n\t * arrives with the default `period=\"current\"`; keying off the attribute alone\n\t * titled an antardasha list \"Active dashas\" there. A drill-down response is\n\t * self-identifying (it carries a parent period), so the markup does not need\n\t * to be told.\n\t */\n\t/** The accordion for this view. Singular or plural by count, decided here rather than inside the template so the two sources are ordinary arguments the compiler checks. */\n\tprivate renderReadings(readings: InterpSection[]) {\n\t\treturn this.renderInterpretation(\n\t\t\treadings,\n\t\t\t'roxy-dasha',\n\t\t\treadings.length === 1 ? 'Reading' : 'Readings',\n\t\t);\n\t}\n\n\tprivate heading(d: DashaData): string {\n\t\tconst parent = parentOf(d);\n\t\tif (parent) {\n\t\t\t// One whole sentence: an English plural formed by appending an s is a\n\t\t\t// letter no other language adds in the same place.\n\t\t\treturn this.t('{{level}} periods in {{planet}} {{parent}}', {\n\t\t\t\tlevel: this.t(levelOf(d)),\n\t\t\t\tplanet: parent.period.planet,\n\t\t\t\tparent: this.t(parent.source),\n\t\t\t});\n\t\t}\n\t\tif ('mahadashas' in d) return this.t('Vimshottari Mahadasha');\n\t\tif ('mahadasha' in d) return this.t('Active dashas');\n\t\treturn this.period === 'major'\n\t\t\t? this.t('Vimshottari Mahadasha')\n\t\t\t: this.t('Active dashas');\n\t}\n\n\t/**\n\t * Drill-down modes list sub-periods inside one parent, so name the parent and\n\t * its span. A sub-period means nothing without its parent lord.\n\t *\n\t * `nominalStartDate` is present only when birth cut the parent short, which is\n\t * also why such a list can hold fewer than nine rows: the sub-periods that\n\t * finished before the native was born are not part of the chart. Saying so\n\t * turns a surprising short list into an explained one.\n\t */\n\tprivate renderParentMahadasha(d: DashaData) {\n\t\tconst parent = parentOf(d);\n\t\tif (!parent) return nothing;\n\t\tconst p = parent.period;\n\t\tconst span = formatSpan(this.effectiveLang(), p);\n\t\tconst began = p.nominalStartDate\n\t\t\t? formatDate(this.effectiveLang(), p.nominalStartDate)\n\t\t\t: '';\n\t\treturn html`<p class=\"parent\">${this.t('Inside the {{planet}} {{level}}{{span}}{{duration}}.', {\n\t\t\t\tplanet: p.planet,\n\t\t\t\tlevel: this.t(parent.source),\n\t\t\t\tspan: span ? `, ${span}` : '',\n\t\t\t\tduration:\n\t\t\t\t\ttypeof p.durationYears === 'number'\n\t\t\t\t\t\t? ` (${formatDuration(this.effectiveLang(), p.durationYears)})`\n\t\t\t\t\t\t: '',\n\t\t\t})} ${\n\t\t\t\tbegan\n\t\t\t\t\t? html`<br>${this.t(\n\t\t\t\t\t\t\t'It began {{date}}, before birth, so only the sub-periods running after the birth date are listed.',\n\t\t\t\t\t\t\t{ date: began },\n\t\t\t\t\t\t)}`\n\t\t\t\t\t: nothing\n\t\t\t}</p>`;\n\t}\n\n\t/**\n\t * Every reading the response carries, behind one exclusive accordion. Current\n\t * mode has five (mahadasha, antardasha, pratyantardasha, sookshma, prana);\n\t * drill-down modes lead with the parent period reading, then the sub-period\n\t * running now; major mode shows the mahadasha running now.\n\t */\n\tprivate readings(d: DashaData, periods: DashaPeriod[]): InterpSection[] {\n\t\tconst sections: InterpSection[] = [];\n\n\t\tconst parent = parentOf(d);\n\t\tif (parent?.period.interpretation) {\n\t\t\tsections.push({\n\t\t\t\tlabel: this.t('{{planet}} {{level}}', {\n\t\t\t\t\tplanet: parent.period.planet,\n\t\t\t\t\tlevel: this.t(parent.source),\n\t\t\t\t}),\n\t\t\t\taside: formatSpan(this.effectiveLang(), parent.period),\n\t\t\t\tbody: parent.period.interpretation,\n\t\t\t});\n\t\t}\n\n\t\tif ('mahadasha' in d) {\n\t\t\tconst levels = [\n\t\t\t\t['Mahadasha', d.mahadasha, d.remainingInMahadasha],\n\t\t\t\t['Antardasha', d.antardasha, d.remainingInAntardasha],\n\t\t\t\t['Pratyantardasha', d.pratyantardasha, d.remainingInPratyantardasha],\n\t\t\t\t['Sookshma', d.sookshmaDasha, d.remainingInSookshma],\n\t\t\t\t['Prana', d.pranaDasha, d.remainingInPrana],\n\t\t\t] as const;\n\t\t\tfor (const [label, period, remaining] of levels) {\n\t\t\t\tif (!period?.interpretation) continue;\n\t\t\t\tconst left = formatBalance(remaining);\n\t\t\t\tsections.push({\n\t\t\t\t\tlabel: this.t('{{planet}} {{level}}', {\n\t\t\t\t\t\tplanet: period.planet,\n\t\t\t\t\t\tlevel: this.t(label),\n\t\t\t\t\t}),\n\t\t\t\t\taside: left\n\t\t\t\t\t\t? this.t('{{balance}} left', { balance: left })\n\t\t\t\t\t\t: formatSpan(this.effectiveLang(), period),\n\t\t\t\t\tbody: period.interpretation,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tconst active = periods.find((p) => this.isCurrent(p));\n\t\tif (active?.interpretation) {\n\t\t\tsections.push({\n\t\t\t\tlabel: `${active.planet} ${levelOf(d)}`,\n\t\t\t\taside: formatSpan(this.effectiveLang(), active),\n\t\t\t\tbody: active.interpretation,\n\t\t\t});\n\t\t}\n\n\t\treturn sections;\n\t}\n\n\tprivate renderCurrent(d: DashaData) {\n\t\tif (!('mahadasha' in d)) return nothing;\n\t\tconst levels = [\n\t\t\t['Mahadasha', d.mahadasha, d.remainingInMahadasha],\n\t\t\t['Antardasha', d.antardasha, d.remainingInAntardasha],\n\t\t\t['Pratyantardasha', d.pratyantardasha, d.remainingInPratyantardasha],\n\t\t\t['Sookshma', d.sookshmaDasha, d.remainingInSookshma],\n\t\t\t['Prana', d.pranaDasha, d.remainingInPrana],\n\t\t] as const;\n\t\treturn html`<div class=\"current\" part=\"details current\">${levels.map(([label, period, remaining]) => {\n\t\t\t\tif (!period) return nothing;\n\t\t\t\tconst left = formatBalance(remaining);\n\t\t\t\tconst houses = periodHouseWords(period.significators, d.houseThemes);\n\t\t\t\treturn html`<div><span>${this.t(label)}</span> <strong>${period.planet}</strong> ${left ? html`<small>${this.t('{{balance}} left', { balance: left })}</small>` : nothing} ${houses ? html`<small class=\"houses\">${this.t('Signifies {{houses}}', { houses })}</small>` : nothing}</div>`;\n\t\t\t})}</div>`;\n\t}\n\n\tprivate collectPeriods(d: DashaData): DashaPeriod[] {\n\t\tif ('mahadashas' in d && d.mahadashas?.length) return d.mahadashas;\n\t\tif ('antardashas' in d && d.antardashas?.length) return d.antardashas;\n\t\tif ('pratyantardashas' in d && d.pratyantardashas?.length)\n\t\t\treturn d.pratyantardashas;\n\t\tif ('sookshmaDashas' in d && d.sookshmaDashas?.length)\n\t\t\treturn d.sookshmaDashas;\n\t\tif ('pranaDashas' in d && d.pranaDashas?.length) return d.pranaDashas;\n\t\treturn [];\n\t}\n\n\t/** True when the current wall-clock time falls between the period's start and end. */\n\tprivate isCurrent(p: DashaPeriod): boolean {\n\t\tif (!p.startDate || !p.endDate) return false;\n\t\tconst now = Date.now();\n\t\tconst start = Date.parse(p.startDate);\n\t\tconst end = Date.parse(p.endDate);\n\t\tif (Number.isNaN(start) || Number.isNaN(end)) return false;\n\t\treturn now >= start && now < end;\n\t}\n\n\t/**\n\t * Fractional progress (0..1) through a period at the current time. Used to\n\t * draw a vertical \"now\" marker inside the active bar. Returns -1 outside the\n\t * period so the caller can skip the marker.\n\t */\n\tprivate progressIn(p: DashaPeriod): number {\n\t\tif (!p.startDate || !p.endDate) return -1;\n\t\tconst start = Date.parse(p.startDate);\n\t\tconst end = Date.parse(p.endDate);\n\t\tconst now = Date.now();\n\t\tif (\n\t\t\tNumber.isNaN(start) ||\n\t\t\tNumber.isNaN(end) ||\n\t\t\tnow < start ||\n\t\t\tnow >= end ||\n\t\t\tend <= start\n\t\t) {\n\t\t\treturn -1;\n\t\t}\n\t\treturn (now - start) / (end - start);\n\t}\n\n\tprivate renderBar(\n\t\tp: DashaPeriod,\n\t\tmax: number,\n\t\tgrain: DateGrain,\n\t\tthemes: HouseThemes,\n\t) {\n\t\tconst years = p.durationYears;\n\t\tconst width = max > 0 ? (years / max) * 100 : 0;\n\t\tconst current = this.isCurrent(p);\n\t\tconst progress = current ? this.progressIn(p) : -1;\n\t\tconst trackClass = current ? 'bar-track bar-now' : 'bar-track';\n\t\tconst houses = periodHouseWords(p.significators, themes);\n\t\tconst cls = ['bar', current ? 'now' : '', grain === 'time' ? 'fine' : '']\n\t\t\t.filter(Boolean)\n\t\t\t.join(' ');\n\t\treturn html`<div class=\"${cls}\" role=\"listitem\" aria-current=\"${current ? 'time' : 'false'}\"><span><strong>${p.planet}</strong>${current ? html`<span class=\"now-badge\">${this.t('Now')}</span>` : nothing} </span><span class=\"${trackClass}\"><span class=\"bar-fill\" style=\"width:${width}%\"></span> ${\n\t\t\t\t\tprogress >= 0\n\t\t\t\t\t\t? html`<span class=\"bar-progress\" style=\"left:${progress * width}%\" aria-hidden=\"true\"></span>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t} </span><span class=\"dates\">${p.startDate ? formatDateGrain(this.effectiveLang(), p.startDate, grain) : ''} ${p.endDate ? html`- ${formatDateGrain(this.effectiveLang(), p.endDate, grain)}` : ''} </span>${houses ? html`<span class=\"houses\">${this.t('Signifies {{houses}}', { houses })}</span>` : nothing}</div>`;\n\t}\n}\n\nconst DAYS_PER_YEAR = 365.25;\n\n/**\n * A period length in the largest unit that still carries information.\n *\n * @remarks\n * Every level was printed in years, which is right for a mahadasha and useless below it: a sookshma read \"0.5 years\" where a reader wants days, and a prana read \"0 years\" because rounding a period shorter than seven hours to one decimal place of a year is zero. The unit has to follow the level.\n */\nfunction formatDuration(locale: string | undefined, years: number): string {\n\tif (years >= 1) return `${formatNumber(locale, years, 1)} years`;\n\tconst days = years * DAYS_PER_YEAR;\n\tif (days >= 1)\n\t\treturn `${formatNumber(locale, days, days >= 10 ? 0 : 1)} days`;\n\tconst hours = days * 24;\n\tif (hours >= 1) return `${formatNumber(locale, hours, 1)} hours`;\n\treturn `${formatNumber(locale, hours * 60, 0)} minutes`;\n}\n\n/**\n * How precise the bar date column has to be, chosen from the SHORTEST period in\n * the set.\n *\n * A Mahadasha spans years, so a bare year reads cleanly and keeps the column\n * narrow. A Sookshma spans days: printed as a year, every one of the nine rows\n * reads \"1990 - 1990\" and the column carries no information at exactly the level\n * a reader opened the drill-down to see. A Prana runs minutes to days, so it\n * needs the clock for the same reason: at day grain the deepest sets print both\n * ends of a row as the same date.\n */\nfunction grainFor(minYears: number): DateGrain {\n\tif (minYears >= 2) return 'year';\n\tif (minYears >= 60 / DAYS_PER_YEAR) return 'month';\n\tif (minYears >= 1 / DAYS_PER_YEAR) return 'day';\n\treturn 'time';\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-dasha-timeline': RoxyDashaTimeline;\n\t}\n}\n", "import { css, html, nothing, type TemplateResult } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { ifDefined } from 'lit/directives/if-defined.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { formatDate, formatNumber, formatTime } from '../utils/format.js';\nimport { displayField, foldLocalized } from '../utils/localized.js';\nimport { humanize } from '../utils/string.js';\n\n/**\n * Generic fallback renderer. Accepts ANY OpenAPI response shape and renders\n * it via field-name heuristics so future spec additions render reasonably\n * without hand-wired components.\n *\n * Heuristic order:\n *   1. Primitive (string, number, boolean) -> single line.\n *   2. Array of primitives -> chip list.\n *   3. Array of objects with shared keys -> table.\n *   4. Object with title-like field -> card with key/value rows; object and\n *      object-array values promote to full-width sections below the rows so\n *      nested tables keep the whole container width.\n *   5. Otherwise -> definition list of all keys.\n *\n * Scalar display rules (shared by rows, chips, and table cells): numbers\n * round to 2 decimals, booleans read Yes/No, ISO dates and datetimes format\n * for the locale, SCREAMING_SNAKE enums humanize, http(s) strings link out.\n *\n * Every word this component writes goes through `this.t()`, which is not\n * optional politeness: `suppress()` already folds the API's localized values\n * into their canonical columns, so before the catalogue landed a Spanish page\n * read `Sol` and `Piscis` under `Yes`, `No` and `31 rows` in English. What a\n * catalogue CANNOT reach is the column headings, which come from the wire field\n * name through `humanize()` and are therefore computed per response; those stay\n * English on both this path and the PHP one, which at least keeps them\n * consistent with each other. The PHP twin has none of these strings yet, so\n * the JS and no-JS paths currently disagree on the chrome as well as the fold\n * (see lesson 6 for why that matters).\n *\n * When a schema declares an `x-roxy-ui` hint, a future dispatcher can opt\n * into a hand-tuned component instead of this fallback.\n */\n\ntype Json = string | number | boolean | null | Json[] | { [key: string]: Json };\ntype Scalar = string | number | boolean;\n\nconst TITLE_KEYS = ['title', 'name', 'label', 'heading', 'overview', 'summary'];\n/** A title is a label, not a paragraph. `overview`/`summary` are only title candidates when a response carries no real name, and a whole paragraph promoted to an <h3> is worse than no title at all. */\nconst MAX_TITLE_LEN = 80;\nconst IMAGE_KEYS = ['imageUrl', 'image', 'icon', 'symbol'];\nconst SKIP_KEYS = ['imageUrl', 'image']; // rendered separately, not in body rows\nconst QUOTE_KEYS = ['affirmation', 'mantra', 'motto', 'quote'];\n\n/**\n * Response fields that are engineer-facing noise on a reading card, and the rules for hiding them.\n *\n * @remarks\n * This mirrors `SUPPRESS_*` in the WordPress plugin's `src/Support/GenericRenderer.php`. Both render the SAME API responses for the same visitor: `roxy-data` when JavaScript runs, the PHP renderer when it does not. If the two disagree the page visibly changes content on hydration, so they must be kept in sync.\n *\n * Without this, a real `/numerology/life-path` card printed \"Calculation: Month: 6, Day: 15 -> 1+5 = 6, Year: 1990 -> ...\", \"Type: Single\", and a list endpoint printed \"Total: 78, Limit: 3, Offset: 0\". That is how the number was derived and how the API paginates: it is not the reading the visitor asked for.\n */\nconst SUPPRESS_ALWAYS = new Set(['seed']);\n/** Suppressed only when the object carries a title: an untitled record still needs its identifier. */\nconst SUPPRESS_NAMED = new Set(['id', 'slug', 'key']);\nconst SUPPRESS_NOISE = new Set([\n\t'calculation',\n\t'calculations',\n\t'type', // schema discriminator (\"single\", \"general\"): indexes a polymorphic shape, means nothing on a card\n\t'position', // pinnacle/challenge index: already carried by the row order\n\t'count',\n\t'totalcount',\n\t'total',\n\t'limit',\n\t'offset',\n\t'page',\n\t'pagesize',\n\t'perpage',\n]);\n\n/** Sections with more keys than this, and tables with more rows than this, collapse into `<details>`. */\nconst DETAILS_KEYS = 8;\n/** Depth at which a section folds shut by default rather than rendering open. */\nconst DEEP_DEPTH = 1;\nconst DETAILS_ROWS = 12;\n\nconst normKey = (k: string): string => k.toLowerCase().replace(/[_-]/g, '');\n\n/**\n * `hasKarmicDebt` / `is_master`: a true reads better as a badge than as the row \"Has karmic debt: Yes\".\n *\n * @remarks\n * Presentation only. The PHP twin also DROPS the false case as \"silence, not data\", and this deliberately does not follow it there: a generic renderer must never lose a fact the response carries, and `isRetrograde: false` (a planet in direct motion) is a real reading, not an absence. Suppression is reserved for fields that are provably not part of the reading at all, which is what {@link SUPPRESS_NOISE} enumerates.\n *\n * Matching on the camel boundary rejects `island` / `issue` / `history`, which a bare `startsWith('is')` would swallow. The PHP twin tests `strpos($key, 'has_')` against a camelCase key, so its badge branch never actually fires; it hard-suppresses the three keys it knew about instead.\n */\nconst isBadgeKey = (k: string): boolean => /^(has|is)([A-Z_]|$)/.test(k);\n\nconst ISO_DATE =\n\t/^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?)?(Z|[+-]\\d{2}:\\d{2})?)?$/;\nconst ENUM_STRING = /^[A-Z0-9]+(_[A-Z0-9]+)+$/;\nconst LINK_STRING = /^https?:\\/\\//;\n\n// Hard cap on recursion. Real RoxyAPI responses nest at most 5-6 deep; anything\n// deeper is either a circular reference (which would otherwise infinite-loop)\n// or a payload too rich for the generic fallback to render usefully. The\n// recursion is otherwise safe: <roxy-data> is registered globally by its\n// `@customElement` decorator on import, so the nested template resolves to\n// this same class without a separate import.\nconst MAX_DEPTH = 6;\n\nfunction isPrimitive(value: Json | undefined): value is Scalar | null {\n\treturn (\n\t\tvalue === null || ['string', 'number', 'boolean'].includes(typeof value)\n\t);\n}\n\n/** Object or object-bearing array: needs full width, never fits a dl value cell. */\nfunction isComplex(value: Json): boolean {\n\treturn (\n\t\tvalue !== null &&\n\t\ttypeof value === 'object' &&\n\t\t!(Array.isArray(value) && value.every(isPrimitive))\n\t);\n}\n\n/**\n * True when a value would render as nothing: `[]`, `{}`, or an object whose every value is itself empty.\n *\n * @remarks\n * An empty object still counts as {@link isComplex}, so it is promoted to a full-width section and draws a heading over a blank body. This is the generic fallback renderer for endpoints with no bespoke component, so it receives whatever shape the API returns and a heading with nothing under it is the most visible way it can look broken.\n *\n * Recursive because emptiness nests: `{ breakdown: { western: [], vedic: [] } }` has keys at the top level and still renders as nothing.\n */\nfunction isEmptyValue(value: Json): boolean {\n\tif (value === null || value === undefined) return true;\n\tif (Array.isArray(value)) return value.length === 0;\n\tif (typeof value !== 'object') return false;\n\tconst values = Object.values(value as Record<string, Json>);\n\treturn values.length === 0 || values.every(isEmptyValue);\n}\n\n@customElement('roxy-data')\nexport class RoxyData extends RoxyDataElement<Json> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.roxy-card{background:var(--roxy-surface,#fff);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-md,1rem);box-shadow:var(--roxy-shadow-sm)}.roxy-card a{color:var(--roxy-accent-ink,#b45309)}.roxy-title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0 0 var(--roxy-space-sm,.5rem) 0;color:var(--roxy-primary,#0f172a);letter-spacing:var(--roxy-tracking-tight)}.roxy-summary{color:var(--roxy-secondary,#475569);margin:0 0 var(--roxy-space-md,1rem) 0;font-size:var(--roxy-text-sm,.875rem)}dl.roxy-rows{grid-template-columns:minmax(8ch, min(30%, max-content)) minmax(0, 1fr);gap:var(--roxy-space-xs,.25rem) var(--roxy-space-md,1rem);margin:0;display:grid}dl.roxy-rows dt{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem);text-transform:capitalize}dl.roxy-rows dd{color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-sm,.875rem);word-break:break-word;margin:0}ul.roxy-chips{gap:var(--roxy-space-xs,.25rem);flex-wrap:wrap;margin:0;padding:0;list-style:none;display:flex}ul.roxy-chips li{background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 14%, transparent);color:var(--roxy-fg,#0a0a0a);border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);padding:2px 8px}.roxy-table-wrap{min-width:0;overflow-x:auto}table.roxy-table{border-collapse:collapse;width:100%;font-size:var(--roxy-text-sm,.875rem)}table.roxy-table th,table.roxy-table td{border-bottom:1px solid var(--roxy-border,#e4e4e7);padding:var(--roxy-space-sm,.5rem);text-align:left;text-transform:none;word-break:normal;overflow-wrap:normal}table.roxy-table th{color:var(--roxy-muted,#71717a);font-weight:var(--roxy-weight-bold,600);text-transform:capitalize;font-size:var(--roxy-text-xs,.75rem);letter-spacing:.04em;white-space:nowrap}table.roxy-table th.col-tight,table.roxy-table td.col-tight{white-space:nowrap;width:1%}table.roxy-table th.col-wide,table.roxy-table td.col-wide{width:auto}.roxy-image{border-radius:var(--roxy-radius-md,8px);max-width:100%;height:auto;margin-bottom:var(--roxy-space-md,1rem)}.roxy-section{margin-top:var(--roxy-space-md,1rem)}.roxy-section h4{font-size:var(--roxy-text-sm,.875rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-secondary,#475569);margin:0 0 var(--roxy-space-xs,.25rem) 0;text-transform:capitalize}blockquote.roxy-quote{margin:var(--roxy-space-md,1rem) 0;padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);border-left:3px solid var(--roxy-accent,#f59e0b);background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 8%, transparent);color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-sm,.875rem);font-style:italic}.roxy-badge{border-radius:var(--roxy-radius-full,9999px);background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 18%, transparent);color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);padding:1px 8px;display:inline-block}details.roxy-section,details.roxy-table-details{margin-top:var(--roxy-space-md,1rem)}details.roxy-section>summary,details.roxy-table-details>summary{cursor:pointer;font-size:var(--roxy-text-sm,.875rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-secondary,#475569);text-transform:capitalize}details.roxy-section>summary:focus-visible,details.roxy-table-details>summary:focus-visible{outline:2px solid var(--roxy-ring,#f59e0b66);outline-offset:2px;border-radius:4px}`,\n\t];\n\n\t/**\n\t * Internal recursion depth. Nested <roxy-data> instances inherit this from the parent and increment to guard against circular references in the input. Not part of the public API; do not set from consumer code.\n\t */\n\t@property({ attribute: false })\n\tdepth = 0;\n\n\tprotected renderData(data: Json) {\n\t\tif (this.depth >= MAX_DEPTH) {\n\t\t\treturn html`<div class=\"roxy-empty\" role=\"status\">${this.t('Nested data omitted')}</div>`;\n\t\t}\n\t\treturn html`<div class=\"roxy-card\" part=\"card\" aria-label=\"${this.t('Generic data display')}\">${this.renderValue(data)}</div>`;\n\t}\n\n\tprivate renderValue(value: Json): TemplateResult | typeof nothing {\n\t\tif (value === null || value === undefined) return nothing;\n\t\tif (isPrimitive(value)) return html`<p>${this.scalarTemplate(value)}</p>`;\n\t\tif (Array.isArray(value)) return this.renderArray(value);\n\t\treturn this.renderObject(value as Record<string, Json>);\n\t}\n\n\tprivate renderArray(arr: Json[]): TemplateResult {\n\t\tif (arr.length === 0) {\n\t\t\treturn html`<div class=\"roxy-empty\" role=\"status\">${this.t('Empty list')}</div>`;\n\t\t}\n\t\tif (arr.every(isPrimitive)) {\n\t\t\treturn this.renderChips(arr as (Scalar | null)[]);\n\t\t}\n\t\tconst allObjects = arr.every(\n\t\t\t(v) => v !== null && typeof v === 'object' && !Array.isArray(v),\n\t\t);\n\t\tif (allObjects) return this.renderTable(arr as Record<string, Json>[]);\n\t\treturn html`<ol>${arr.map((v) => html`<li>${this.renderValue(v)}</li>`)}</ol>`;\n\t}\n\n\tprivate renderChips(arr: (Scalar | null)[]): TemplateResult {\n\t\treturn html`<ul class=\"roxy-chips\">${arr\n\t\t\t\t.filter((v): v is Scalar => v !== null)\n\t\t\t\t.map((v) => html`<li>${this.formatScalar(v)}</li>`)}</ul>`;\n\t}\n\n\tprivate renderTable(rows: Record<string, Json>[]): TemplateResult {\n\t\tconst clean = rows.map((r) => this.suppress(r));\n\t\tconst keys = this.collectKeys(clean);\n\n\t\t/**\n\t\t * A column holding nested data needs the width; a column holding a short scalar does not.\n\t\t *\n\t\t * @remarks\n\t\t * Browsers lay an auto table out by content, so a `date` column of ten characters and a `positions` column holding a whole nested table settle at roughly half the width each, and the nested one is the one that ends up cramped. Marking each column by what it actually CONTAINS lets the scalar columns collapse to their content (`width: 1%` is the standard way to say \"as narrow as your content allows\") and the nested column absorb everything left over. No fixed percentage: the split follows the data.\n\t\t *\n\t\t * Only worth doing when the row is mixed. If every column is scalar, or every column is nested, there is nothing to bias toward and the browser's own distribution is already right.\n\t\t */\n\t\tconst complex = new Set(\n\t\t\tkeys.filter((k) => clean.some((r) => isComplex(r[k] ?? null))),\n\t\t);\n\t\tconst mixed = complex.size > 0 && complex.size < keys.length;\n\t\tconst colClass = (k: string) =>\n\t\t\tmixed ? (complex.has(k) ? 'col-wide' : 'col-tight') : '';\n\n\t\tconst table = html`<div class=\"roxy-table-wrap\" part=\"table\" role=\"group\" aria-label=\"${this.t('Data table')}\" tabindex=\"0\"><table class=\"roxy-table\" role=\"table\"><thead><tr>${keys.map(\n\t\t\t\t\t\t\t(k) =>\n\t\t\t\t\t\t\t\thtml`<th class=\"${colClass(k)}\">${displayField(this.effectiveLang(), k)}</th>`,\n\t\t\t\t\t\t)}</tr></thead><tbody>${clean.map(\n\t\t\t\t\t\t(row) => html`<tr>${keys.map(\n\t\t\t\t\t\t\t\t(k) =>\n\t\t\t\t\t\t\t\t\thtml`<td class=\"${colClass(k)}\">${this.renderCell(row[k], k)}</td>`,\n\t\t\t\t\t\t\t)}</tr>`,\n\t\t\t\t\t)}</tbody></table></div>`;\n\t\t// A 78-row angel-number list or a 27-row nakshatra table is a scroll trap\n\t\t// inline. Past the threshold it folds away behind its own row count.\n\t\tif (clean.length > DETAILS_ROWS) {\n\t\t\treturn html`<details class=\"roxy-table-details\"><summary>${this.t('{{count}} rows', { count: clean.length })}</summary>${table}</details>`;\n\t\t}\n\t\treturn table;\n\t}\n\n\t/**\n\t * Drop the fields that are noise on a reading card, and fold each localized twin into the field it translates. See {@link SUPPRESS_NOISE} and {@link foldLocalized}.\n\t *\n\t * @remarks\n\t * The single funnel for BOTH render paths, the table and the object, which is why the fold belongs here: it is one call and no column-building or row-building code has to know the convention exists. Folding runs FIRST, so a field this then suppresses takes its translation with it rather than leaving `Type Localized` standing where `Type` was hidden.\n\t */\n\tprivate suppress(obj: Record<string, Json>): Record<string, Json> {\n\t\tconst folded = foldLocalized(obj);\n\t\tconst titled = TITLE_KEYS.some((k) => typeof folded[k] === 'string');\n\t\treturn Object.fromEntries(\n\t\t\tObject.entries(folded).filter(([k]) => {\n\t\t\t\tconst n = normKey(k);\n\t\t\t\tif (SUPPRESS_ALWAYS.has(n)) return false;\n\t\t\t\tif (titled && SUPPRESS_NAMED.has(n)) return false;\n\t\t\t\treturn !SUPPRESS_NOISE.has(n);\n\t\t\t}),\n\t\t);\n\t}\n\n\tprivate renderObject(input: Record<string, Json>): TemplateResult {\n\t\tconst obj = this.suppress(input);\n\t\tconst titleKey = TITLE_KEYS.find(\n\t\t\t(k) =>\n\t\t\t\ttypeof obj[k] === 'string' &&\n\t\t\t\t(obj[k] as string).length <= MAX_TITLE_LEN,\n\t\t);\n\t\tconst imageKey = IMAGE_KEYS.find(\n\t\t\t(k) =>\n\t\t\t\ttypeof obj[k] === 'string' && (obj[k] as string).startsWith('http'),\n\t\t);\n\t\tconst summaryKey =\n\t\t\ttitleKey !== 'summary' && typeof obj.summary === 'string'\n\t\t\t\t? 'summary'\n\t\t\t\t: null;\n\t\tconst quoteKey = QUOTE_KEYS.find((k) => typeof obj[k] === 'string');\n\t\tconst entries = Object.entries(obj).filter(\n\t\t\t([k, v]) =>\n\t\t\t\tk !== titleKey &&\n\t\t\t\tk !== summaryKey &&\n\t\t\t\tk !== quoteKey &&\n\t\t\t\t!SKIP_KEYS.includes(k) &&\n\t\t\t\t!isEmptyValue(v),\n\t\t);\n\t\t// Scalars and primitive arrays fit the two-column rows; objects and\n\t\t// object arrays promote to full-width sections so nested tables are\n\t\t// not squeezed into the value column.\n\t\tconst rows = entries.filter(([, v]) => !isComplex(v));\n\t\tconst sections = entries.filter(([, v]) => isComplex(v));\n\n\t\treturn html`${\n\t\t\t\timageKey\n\t\t\t\t\t? html`<img class=\"roxy-image\" src=\"${String(obj[imageKey])}\" alt=\"${titleKey ? String(obj[titleKey]) : this.t('illustration')}\" loading=\"lazy\">`\n\t\t\t\t\t: nothing\n\t\t\t} ${titleKey ? html`<h3 class=\"roxy-title\" part=\"header\">${obj[titleKey]}</h3>` : nothing} ${summaryKey ? html`<p class=\"roxy-summary\">${obj[summaryKey]}</p>` : nothing} ${quoteKey ? html`<blockquote class=\"roxy-quote\">${obj[quoteKey]}</blockquote>` : nothing} ${\n\t\t\t\trows.length > 0\n\t\t\t\t\t? html`<dl class=\"roxy-rows\" part=\"details\">${rows.map(\n\t\t\t\t\t\t\t([k, v]) => html`<dt>${displayField(this.effectiveLang(), k)}</dt><dd>${this.renderField(v, k)}</dd>`,\n\t\t\t\t\t\t)}</dl>`\n\t\t\t\t\t: nothing\n\t\t\t} ${sections.map(([k, v]) => this.renderSection(k, v))}`;\n\t}\n\n\t/**\n\t * A named block of nested data. Collapses into `<details>` once it carries more than {@link DETAILS_KEYS} keys, so one fat object (a 27-nakshatra map, a 12-house table) cannot bury the rest of the card under a wall of rows.\n\t *\n\t * @remarks\n\t * Deliberately carries no `part`. The heading here IS a response key, so the only name this block could take is computed, and `roxy-reference-card` is the one component allowed to derive a part at runtime (`catalog.test.ts` pins it as the only one). A bare `part=\"section\"` is the other tempting option and is worse: the vocabulary pairs `section` with a specific name precisely so `::part(section patterns)` addresses ONE block, and a token that lands identically on every key of every response addresses none of them.\n\t */\n\tprivate renderSection(key: string, value: Json): TemplateResult {\n\t\tconst size =\n\t\t\tvalue !== null && typeof value === 'object'\n\t\t\t\t? Array.isArray(value)\n\t\t\t\t\t? value.length\n\t\t\t\t\t: Object.keys(value).length\n\t\t\t\t: 0;\n\t\tconst body = this.renderField(value, key);\n\t\tconst heading = displayField(this.effectiveLang(), key);\n\t\t// Past depth 1 a section folds shut by default. A real /numerology/chart response\n\t\t// rendered every level open and grew to a 29,933px-tall element, about thirty\n\t\t// screens of correct but unusable output. The reader opens what they want.\n\t\tif (size > DETAILS_KEYS || this.depth >= DEEP_DEPTH) {\n\t\t\treturn html`<details class=\"roxy-section\" ?open=\"${this.depth < DEEP_DEPTH}\"><summary>${heading}</summary>${body}</details>`;\n\t\t}\n\t\treturn html`<div class=\"roxy-section\"><h4>${heading}</h4>${body}</div>`;\n\t}\n\n\tprivate renderField(value: Json, key?: string): TemplateResult | string {\n\t\tif (value === null || value === undefined) return '';\n\t\tif (value === true && key !== undefined && isBadgeKey(key)) {\n\t\t\treturn html`<span class=\"roxy-badge\">${this.t('Yes')}</span>`;\n\t\t}\n\t\tif (isPrimitive(value)) return html`${this.scalarTemplate(value)}`;\n\t\tif (Array.isArray(value) && value.every(isPrimitive)) {\n\t\t\treturn this.renderChips(value as (Scalar | null)[]);\n\t\t}\n\t\t// `bare`: a nested card inside a card compounds padding and border at every\n\t\t// level, and by depth 3 the innermost object was rendering into a fraction of the\n\t\t// width with prose wrapping to two words a line.\n\t\treturn html`<roxy-data bare lang=\"${ifDefined(this.effectiveLang())}\" .data=\"${value}\" .depth=\"${this.depth + 1}\"></roxy-data>`;\n\t}\n\n\tprivate renderCell(\n\t\tvalue: Json | undefined,\n\t\tkey?: string,\n\t): TemplateResult | string {\n\t\tif (value === null || value === undefined) return '';\n\t\tif (value === true && key !== undefined && isBadgeKey(key)) {\n\t\t\treturn html`<span class=\"roxy-badge\">${this.t('Yes')}</span>`;\n\t\t}\n\t\tif (isPrimitive(value)) return this.scalarTemplate(value);\n\t\tif (Array.isArray(value) && value.every(isPrimitive)) {\n\t\t\treturn (value as (Scalar | null)[])\n\t\t\t\t.filter((v): v is Scalar => v !== null)\n\t\t\t\t.map((v) => this.formatScalar(v))\n\t\t\t\t.join(', ');\n\t\t}\n\t\t// Nested object or object array inside a row: recurse instead of\n\t\t// stringifying to [object Object]. `bare` drops the card surface so\n\t\t// the cell does not paint a card inside a cell.\n\t\treturn html`<roxy-data bare lang=\"${ifDefined(this.effectiveLang())}\" .data=\"${value}\" .depth=\"${this.depth + 1}\"></roxy-data>`;\n\t}\n\n\tprivate scalarTemplate(value: Scalar): TemplateResult | string {\n\t\tif (typeof value === 'string' && LINK_STRING.test(value)) {\n\t\t\treturn html`<a href=\"${value}\" target=\"_blank\" rel=\"noopener noreferrer\">${value}</a>`;\n\t\t}\n\t\treturn this.formatScalar(value);\n\t}\n\n\tprivate formatScalar(value: Scalar): string {\n\t\tif (typeof value === 'number') {\n\t\t\treturn formatNumber(this.effectiveLang(), value, 2) || String(value);\n\t\t}\n\t\tif (typeof value === 'boolean') return value ? this.t('Yes') : this.t('No');\n\t\tif (ISO_DATE.test(value)) {\n\t\t\tconst time = formatTime(this.effectiveLang(), value);\n\t\t\tconst date = formatDate(this.effectiveLang(), value);\n\t\t\treturn time ? `${date}, ${time}` : date;\n\t\t}\n\t\tif (ENUM_STRING.test(value)) return humanize(value.toLowerCase());\n\t\treturn value;\n\t}\n\n\tprivate collectKeys(rows: Record<string, Json>[]): string[] {\n\t\tconst seen = new Set<string>();\n\t\tfor (const row of rows) {\n\t\t\tfor (const k of Object.keys(row)) seen.add(k);\n\t\t}\n\t\treturn Array.from(seen);\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-data': RoxyData;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { planetGlyph } from '../tokens/index.js';\nimport type { DivisionalChartResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { distinctSanskrit } from '../utils/format.js';\nimport { frameCaptionStyles, renderFrameCaption } from '../utils/frame.js';\nimport {\n\ttype ChartStyle,\n\ttype KundliViewModel,\n\trenderKundliCaptions,\n\trenderKundliStyleTablist,\n\trenderKundliSvg,\n\ttoKundliViewModel,\n} from '../utils/kundli-render.js';\nimport { kundliStyles } from '../utils/kundli-styles.js';\nimport { tablistStyles } from '../utils/tablist.js';\n\n/**\n * Divisional chart renderer (D2-D60). Accepts a DivisionalChartResponse and\n * renders the same South / North / East kundli grid as the birth chart, plus\n * division metadata and Vargottama planet pills. A visible tablist lets the\n * end user switch styles at runtime. The varga response carries a graha-keyed\n * `chart.meta` map (no per-rashi buckets), so houses are bucketed from that\n * map.\n */\n@customElement('roxy-divisional-chart')\nexport class RoxyDivisionalChart extends RoxyDataElement<DivisionalChartResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tframeCaptionStyles,\n\t\tkundliStyles,\n\t\ttablistStyles,\n\t\tcss`.division-meta{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-muted,#71717a);margin:0}.significance{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-muted,#71717a);border-left:2px solid var(--roxy-border,#e4e4e7);padding-left:var(--roxy-space-sm,.5rem);margin:0}.vargottama-row{gap:var(--roxy-space-xs,.25rem);flex-wrap:wrap;align-items:center;display:flex}.vargottama-label{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-muted,#71717a);margin-right:var(--roxy-space-xs,.25rem);font-weight:500}.vargottama-pill{font-size:var(--roxy-text-sm,.875rem);background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 22%, transparent);color:var(--roxy-fg,#0a0a0a);border:1px solid color-mix(in srgb, var(--roxy-accent,#f59e0b) 45%, transparent);border-radius:999px;align-items:center;gap:.2em;padding:.15em .6em;font-weight:600;display:inline-flex}`,\n\t];\n\n\t@property({ type: String, reflect: true, attribute: 'chart-style' })\n\tchartStyle: ChartStyle = 'north';\n\n\tprivate setStyle = (next: ChartStyle) => {\n\t\tthis.chartStyle = next;\n\t};\n\n\tprivate viewModel(): KundliViewModel | null {\n\t\tif (!this.data?.chart?.meta) return null;\n\t\tconst { division } = this.data;\n\t\tconst label = `D${division.number} ${division.name}`;\n\t\treturn toKundliViewModel(this.data.chart.meta, { divisionLabel: label });\n\t}\n\n\tprotected renderData(d: DivisionalChartResponse) {\n\t\tconst vm = this.viewModel();\n\t\tif (!vm) return this.renderEmpty();\n\n\t\tconst { division, vargottama } = d;\n\n\t\treturn html`<div class=\"wrap\" part=\"card\"><div class=\"header\" part=\"header\"><div><h2 class=\"title\">D${division.number} ${division.name} ${\n\t\t\t\t\t\t\tdistinctSanskrit(division.name, division.sanskritName)\n\t\t\t\t\t\t\t\t? html`<span class=\"division-meta\">· ${division.sanskritName}</span>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t}</h2>${\n\t\t\t\t\t\tdivision.significance\n\t\t\t\t\t\t\t? html`<p class=\"significance\">${division.significance}</p>`\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t} ${renderFrameCaption(this.effectiveLang(), d.frame, this.translator)} ${renderKundliCaptions(vm, this.translator)}</div>${renderKundliStyleTablist(this.chartStyle, this.setStyle, this.translator)}</div><div id=\"kundli-panel-${this.chartStyle}\" part=\"panel\" role=\"tabpanel\" tabindex=\"0\" aria-labelledby=\"kundli-tab-${this.chartStyle}\"><svg viewBox=\"0 0 400 400\" preserveAspectRatio=\"xMidYMid meet\" part=\"chart\" role=\"img\" aria-label=\"${this.t('{{chart}} divisional chart with twelve sign houses', { chart: `D${division.number} ${division.name}` })}\"><title>D${division.number} ${division.name}</title>${renderKundliSvg(vm, this.chartStyle, this.translator)}</svg></div>${\n\t\t\t\tvargottama && vargottama.length > 0\n\t\t\t\t\t? html`<div class=\"vargottama-row\" part=\"details\" role=\"list\" aria-label=\"${this.t('Vargottama planets')}\"><span class=\"vargottama-label\">${this.t('Vargottama')}:</span> ${vargottama.map(\n\t\t\t\t\t\t\t(planet) =>\n\t\t\t\t\t\t\t\thtml`<span class=\"vargottama-pill\" role=\"listitem\">${planetGlyph(planet) ?? ''} ${planet}</span>`,\n\t\t\t\t\t\t)}</div>`\n\t\t\t\t\t: nothing\n\t\t\t}</div>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-divisional-chart': RoxyDivisionalChart;\n\t}\n}\n", "import type { TemplateResult } from 'lit';\nimport { html, nothing, svg } from 'lit';\nimport type { ChromeString } from '../i18n/chrome-strings.js';\nimport { planetAbbr, SIGNS_ORDER, signAbbr } from '../tokens/index.js';\nimport { longitudeToSignPosition } from './degree.js';\nimport type { Translate } from './hd-reading.js';\nimport { capitalize } from './string.js';\nimport { renderTablist } from './tablist.js';\n\n/**\n * Canonical viewBox geometry for every kundli style. The chart is drawn into a\n * 360-unit square centred in a 400-unit viewBox, leaving a 20-unit gutter for\n * outer labels. All coordinates below are derived from these constants; change\n * them and every cell relocates correctly.\n *\n * @remarks SVG is vector-only and scales without raster loss, so the chart\n * remains crisp from a phone screen to a wall projector. Hosts size the chart\n * by setting `width` on the surrounding container; the SVG keeps a 1:1 aspect\n * ratio via the viewBox.\n */\nconst VIEW_BOX = 400;\nconst MARGIN = 20;\nconst INNER = VIEW_BOX - 2 * MARGIN; // 360\nconst CENTRE = VIEW_BOX / 2; // 200\n\n/**\n * Lowercase rashi key (`\"aries\"`) to canonical title-cased sign name (`\"Aries\"`).\n * Bridges API lowercase rashi strings to the SIGNS_ORDER tokens used everywhere\n * else in the render.\n */\nconst RASHI_TO_SIGN: Record<string, string> = Object.fromEntries(\n\tSIGNS_ORDER.map((s) => [s.toLowerCase(), s] as const),\n);\n\n/**\n * A graha placed inside a kundli cell. Render-only view model fed from a\n * `meta` map on the API response. Carries enough detail to draw a compact\n * in-cell label (abbreviation, whole degree, retrograde mark) and a rich SVG\n * `<title>` tooltip (exact position, nakshatra, pada, avastha).\n */\nexport interface PlacedGraha {\n\tgraha: string;\n\tlongitude?: number;\n\tnakshatra?: { name?: string; pada?: number; lord?: string };\n\tisRetrograde?: boolean;\n\tawastha?: string;\n\t/** Deeptadi avastha, the nine dispositional states. Rendered as the response sends it, like {@link PlacedGraha.awastha}. */\n\tdeeptadi?: string;\n\t/** Within its combustion orb of the Sun (astangata). */\n\tcombust?: boolean;\n\t/** Graha yuddha: the opponent within one degree, and which of the pair won. */\n\twar?: { opponent: string; winner?: string };\n}\n\n/** The two chart-wide state lists a D1 response carries beside `meta`. Divisional charts have neither, so both are optional. */\nexport interface GrahaStates {\n\tcombustion?: ReadonlyArray<{ planet?: string }>;\n\tplanetaryWar?: ReadonlyArray<{\n\t\tplanet1?: string;\n\t\tplanet2?: string;\n\t\twinner?: string;\n\t}>;\n}\n\n/**\n * Unified view model used by every kundli style. Caller passes a graha-keyed\n * `meta` map (from `/vedic-astrology/birth-chart`, `/divisional-chart`, or\n * `/navamsa`) through {@link toKundliViewModel} to produce this shape.\n *\n * `placements` is keyed by lowercase rashi name (`\"aries\"`, `\"taurus\"`, ...)\n * so the sign-fixed styles can index directly. The Lagna entry is not\n * counted as a planet; it only flags the ascendant cell.\n */\nexport interface KundliViewModel {\n\tlagnaSign: string;\n\tplacements: Record<string, PlacedGraha[]>;\n\tdivisionLabel?: string;\n}\n\n/**\n * Kundli regional styles. Sign-fixed (south, east) and house-fixed (north).\n * Exposed so consumers can type their own `chart-style` attribute reflection.\n */\nexport type ChartStyle = 'south' | 'north' | 'east';\n\n/** Tab order for the style switcher, each id beside the English source its label is looked up by. One ordered list rather than an order and a lookup, so a style cannot exist in one and not the other, and `source` is typed {@link ChromeString} so the compiler rejects a tab no catalogue carries. */\nconst CHART_STYLES: ReadonlyArray<{ id: ChartStyle; source: ChromeString }> = [\n\t{ id: 'north', source: 'North' },\n\t{ id: 'south', source: 'South' },\n\t{ id: 'east', source: 'East' },\n];\n\n/** Superscript state marks on a graha label. A cell is small, so each state that changes how a graha is READ gets one character and the tooltip carries the words. */\nconst RETRO_MARK = 'ʳ';\nconst COMBUST_MARK = 'ᶜ';\nconst WAR_MARK = 'ʷ';\n\n/** Position of a sign in the zodiac order, or -1. Takes a plain string because it reads API values, and widens the literal tuple to match so the lookup is a plain `indexOf`. */\nfunction signIndex(sign: string): number {\n\treturn (SIGNS_ORDER as readonly string[]).indexOf(sign);\n}\n\n/**\n * True when the placed graha's longitude maps to a sign other than the cell\n * it occupies. The API preserves the D1 sidereal longitude on every chart, so\n * inside a D2..D60 cell that longitude refers to the D1 sign, not the cell's\n * divisional sign. In that case the degree-within-sign is not meaningful and\n * must be hidden from the in-cell label.\n */\nfunction isDivisionalPlacement(p: PlacedGraha, cellSign: string): boolean {\n\tif (typeof p.longitude !== 'number' || !Number.isFinite(p.longitude)) {\n\t\treturn false;\n\t}\n\treturn (\n\t\tlongitudeToSignPosition(p.longitude).sign.toLowerCase() !==\n\t\tcellSign.toLowerCase()\n\t);\n}\n\n/**\n * Compact in-cell graha label: abbreviation, optional whole-degree, retrograde\n * mark. The degree is shown only when the longitude actually maps to the cell\n * the graha is rendered in (the D1 case); divisional placements show the\n * abbreviation alone since the API longitude refers to D1, not the divisional\n * sign. A body the library has no abbreviation for falls back to its full\n * name: the cell is the only place that name appears, so it must stay\n * visibly wrong rather than a fabricated two-letter code.\n */\nfunction grahaLabel(p: PlacedGraha, cellSign: string): string {\n\tconst abbr = planetAbbr(p.graha) ?? p.graha;\n\tconst retro = `${p.isRetrograde ? RETRO_MARK : ''}${p.combust ? COMBUST_MARK : ''}${p.war ? WAR_MARK : ''}`;\n\tif (\n\t\ttypeof p.longitude !== 'number' ||\n\t\t!Number.isFinite(p.longitude) ||\n\t\tisDivisionalPlacement(p, cellSign)\n\t) {\n\t\treturn `${abbr}${retro}`;\n\t}\n\tconst { degree } = longitudeToSignPosition(p.longitude);\n\treturn `${abbr} ${degree}°${retro}`;\n}\n\n/**\n * Full-detail tooltip surfaced via the SVG `<title>` for each planet label.\n * Includes planet name, the divisional placement (when the longitude does not\n * match the cell, the cell's rashi is preferred), the exact D1 longitude as\n * the original reference, nakshatra and pada, avastha, and the retrograde\n * flag. Surfaces on hover or long-press without crowding the cell.\n */\nfunction grahaTitle(p: PlacedGraha, cellSign: string, t: Translate): string {\n\tconst parts: string[] = [capitalize(p.graha)];\n\tconst divisional = isDivisionalPlacement(p, cellSign);\n\tif (divisional) {\n\t\tparts.push(t('in {{sign}}', { sign: cellSign }));\n\t}\n\tif (typeof p.longitude === 'number' && Number.isFinite(p.longitude)) {\n\t\tconst sp = longitudeToSignPosition(p.longitude);\n\t\tconst minute = String(sp.minute).padStart(2, '0');\n\t\tparts.push(\n\t\t\tdivisional\n\t\t\t\t? `D1: ${sp.degree}°${minute}' ${sp.sign}`\n\t\t\t\t: `${sp.degree}°${minute}' ${sp.sign}`,\n\t\t);\n\t}\n\tif (p.nakshatra?.name) {\n\t\tconst pada = p.nakshatra.pada\n\t\t\t? ` ${t('pada {{n}}', { n: p.nakshatra.pada })}`\n\t\t\t: '';\n\t\tparts.push(`${p.nakshatra.name}${pada}`);\n\t}\n\t// The nakshatra lord is what a dasha is read from, so a chart that names the\n\t// nakshatra and withholds its lord stops one step short of the reading.\n\tif (p.nakshatra?.lord) {\n\t\tparts.push(t('nakshatra lord {{graha}}', { graha: p.nakshatra.lord }));\n\t}\n\tif (p.awastha) parts.push(p.awastha);\n\tif (p.deeptadi) parts.push(p.deeptadi);\n\tif (p.combust) parts.push(t('combust'));\n\tif (p.war) {\n\t\tparts.push(\n\t\t\tp.war.winner\n\t\t\t\t? t('planetary war with {{graha}}, won by {{winner}}', {\n\t\t\t\t\t\tgraha: p.war.opponent,\n\t\t\t\t\t\twinner: p.war.winner,\n\t\t\t\t\t})\n\t\t\t\t: t('planetary war with {{graha}}', { graha: p.war.opponent }),\n\t\t);\n\t}\n\tif (p.isRetrograde) parts.push(t('retrograde'));\n\treturn parts.join(' · ');\n}\n\n/**\n * Render a vertically centred stack of planet labels at `(cx, baseY)`, one\n * line per planet, with an SVG `<title>` per line carrying the full tooltip.\n * The stack auto-centres on `baseY` regardless of count so a 1-planet cell\n * and a 5-planet cell both look intentional.\n */\nfunction renderPlanetStack(\n\tplanets: PlacedGraha[],\n\tcellSign: string,\n\tcx: number,\n\tbaseY: number,\n\tlineHeight: number,\n\tt: Translate,\n): TemplateResult[] {\n\tconst startY = baseY - ((planets.length - 1) * lineHeight) / 2;\n\treturn planets.map((p, j) => {\n\t\tconst yPos = startY + j * lineHeight;\n\t\treturn svg`<text class=\"planet-text\" x=${cx} y=${yPos} text-anchor=\"middle\" dominant-baseline=\"central\">${grahaLabel(\n\t\t\tp,\n\t\t\tcellSign,\n\t\t)}<title>${grahaTitle(p, cellSign, t)}</title></text>`;\n\t});\n}\n\n/**\n * Bucket a graha-keyed `meta` map (D1 birth chart or D2..D60 divisional\n * chart) into the unified {@link KundliViewModel} the renderer consumes. The\n * Lagna entry is recognised by `graha === 'Lagna'` (or key `\"Lagna\"`) and\n * sets `lagnaSign`; it is not bucketed as a placed planet.\n *\n * @param meta - Graha-keyed map; missing rashi entries are skipped.\n * @param options - `divisionLabel` titles the chart centre. `lagnaOverride` is a rashi/sign name (case-insensitive, e.g. `\"cancer\"`) replacing the `meta.Lagna`-derived ascendant, which drives the Chandra Lagna and other reference-point views: the `meta` of a `/birth-chart` response always carries the Janma Lagna as its `Lagna` key, so this is the only way to pivot the houses without a second request, and it is ignored when it does not resolve to a known sign. `states` folds the chart-wide combustion and graha-yuddha lists onto the grahas they name, because both arrive beside `meta` rather than inside it and a cell can only mark what its own entry carries.\n */\nexport function toKundliViewModel(\n\tmeta: Record<\n\t\tstring,\n\t\t{\n\t\t\tgraha?: string;\n\t\t\trashi?: string;\n\t\t\tlongitude?: number;\n\t\t\tnakshatra?: { name?: string; pada?: number; lord?: string };\n\t\t\tisRetrograde?: boolean;\n\t\t\tawastha?: string;\n\t\t\tdeeptadi?: string;\n\t\t}\n\t>,\n\toptions: {\n\t\tdivisionLabel?: string;\n\t\tlagnaOverride?: string;\n\t\tstates?: GrahaStates;\n\t} = {},\n): KundliViewModel {\n\tconst { divisionLabel, lagnaOverride, states } = options;\n\tconst combust = new Set(\n\t\t(states?.combustion ?? []).map((c) => (c.planet ?? '').toLowerCase()),\n\t);\n\tconst wars = new Map<string, { opponent: string; winner?: string }>();\n\tfor (const w of states?.planetaryWar ?? []) {\n\t\tif (!w.planet1 || !w.planet2) continue;\n\t\twars.set(w.planet1.toLowerCase(), {\n\t\t\topponent: w.planet2,\n\t\t\twinner: w.winner,\n\t\t});\n\t\twars.set(w.planet2.toLowerCase(), {\n\t\t\topponent: w.planet1,\n\t\t\twinner: w.winner,\n\t\t});\n\t}\n\tconst placements: Record<string, PlacedGraha[]> = {};\n\tfor (const sign of SIGNS_ORDER) placements[sign.toLowerCase()] = [];\n\tconst override = lagnaOverride\n\t\t? (RASHI_TO_SIGN[lagnaOverride.toLowerCase()] ?? '')\n\t\t: '';\n\tlet lagnaSign = override;\n\tfor (const [name, pos] of Object.entries(meta ?? {})) {\n\t\tconst rashiKey = (pos?.rashi ?? '').toLowerCase();\n\t\tif (name === 'Lagna' || pos?.graha === 'Lagna') {\n\t\t\t// An explicit override pins the ascendant; otherwise the Janma Lagna\n\t\t\t// from meta is the reference point.\n\t\t\tif (!override) lagnaSign = RASHI_TO_SIGN[rashiKey] ?? '';\n\t\t\tcontinue;\n\t\t}\n\t\tif (!rashiKey || !(rashiKey in placements)) continue;\n\t\tconst graha = pos.graha ?? name;\n\t\tconst key = graha.toLowerCase();\n\t\tplacements[rashiKey]?.push({\n\t\t\tgraha,\n\t\t\tlongitude: pos.longitude,\n\t\t\tnakshatra: pos.nakshatra,\n\t\t\tisRetrograde: pos.isRetrograde,\n\t\t\tawastha: pos.awastha,\n\t\t\tdeeptadi: pos.deeptadi,\n\t\t\tcombust: combust.has(key) || undefined,\n\t\t\twar: wars.get(key),\n\t\t});\n\t}\n\treturn { lagnaSign, placements, divisionLabel };\n}\n\n// ---------------------------------------------------------------------------\n// South Indian: 4x4 grid with central 2x2 hollow. Signs are FIXED to cells\n// (Pisces top-left corner, clockwise); houses rotate from the Lagna cell.\n// ---------------------------------------------------------------------------\n\nconst SOUTH_CELL = INNER / 4; // 90\n\n/**\n * Sign-to-cell column/row in the South Indian fixed-sign grid. Pisces sits in\n * the top-left corner and the remaining signs proceed clockwise around the\n * 12 perimeter cells. (col, row) origin is the chart top-left.\n */\nconst SOUTH_CELL_GRID: Record<string, { col: number; row: number }> = {\n\tPisces: { col: 0, row: 0 },\n\tAries: { col: 1, row: 0 },\n\tTaurus: { col: 2, row: 0 },\n\tGemini: { col: 3, row: 0 },\n\tCancer: { col: 3, row: 1 },\n\tLeo: { col: 3, row: 2 },\n\tVirgo: { col: 3, row: 3 },\n\tLibra: { col: 2, row: 3 },\n\tScorpio: { col: 1, row: 3 },\n\tSagittarius: { col: 0, row: 3 },\n\tCapricorn: { col: 0, row: 2 },\n\tAquarius: { col: 0, row: 1 },\n};\n\nfunction southCellRect(sign: string): {\n\tx: number;\n\ty: number;\n\tw: number;\n\th: number;\n} {\n\tconst g = SOUTH_CELL_GRID[sign] ?? { col: 0, row: 0 };\n\treturn {\n\t\tx: MARGIN + g.col * SOUTH_CELL,\n\t\ty: MARGIN + g.row * SOUTH_CELL,\n\t\tw: SOUTH_CELL,\n\t\th: SOUTH_CELL,\n\t};\n}\n\n/**\n * South Indian frame: outer square, the two full-span grid lines, and the\n * partial inner lines that bound the central 2x2 hollow on each edge.\n */\nfunction renderSouthFrame(divisionLabel?: string): TemplateResult {\n\tconst a = MARGIN;\n\tconst b = MARGIN + SOUTH_CELL; // 110\n\tconst c = MARGIN + 2 * SOUTH_CELL; // 200\n\tconst d = MARGIN + 3 * SOUTH_CELL; // 290\n\tconst e = VIEW_BOX - MARGIN; // 380\n\treturn svg`\n\t\t<rect class=\"line\" x=${a} y=${a} width=${INNER} height=${INNER} stroke-width=\"1.5\" fill=\"none\" />\n\t\t<line class=\"line\" x1=${a} y1=${b} x2=${e} y2=${b} stroke-width=\"1\" />\n\t\t<line class=\"line\" x1=${a} y1=${d} x2=${e} y2=${d} stroke-width=\"1\" />\n\t\t<line class=\"line\" x1=${b} y1=${a} x2=${b} y2=${e} stroke-width=\"1\" />\n\t\t<line class=\"line\" x1=${d} y1=${a} x2=${d} y2=${e} stroke-width=\"1\" />\n\t\t<line class=\"line\" x1=${a} y1=${c} x2=${b} y2=${c} stroke-width=\"1\" />\n\t\t<line class=\"line\" x1=${d} y1=${c} x2=${e} y2=${c} stroke-width=\"1\" />\n\t\t<line class=\"line\" x1=${c} y1=${a} x2=${c} y2=${b} stroke-width=\"1\" />\n\t\t<line class=\"line\" x1=${c} y1=${d} x2=${c} y2=${e} stroke-width=\"1\" />\n\t\t${\n\t\t\tdivisionLabel\n\t\t\t\t? svg`<text class=\"centre-label\" x=${CENTRE} y=${CENTRE} text-anchor=\"middle\" dominant-baseline=\"central\">${divisionLabel}</text>`\n\t\t\t\t: nothing\n\t\t}\n\t`;\n}\n\n/**\n * House number for a given sign relative to a Lagna sign. House 1 is the\n * Lagna cell; subsequent houses follow the zodiac in order. Returns 0 when\n * the Lagna sign is unknown so the caller can skip rendering the badge.\n */\nfunction houseNumberInSign(sign: string, lagnaSign: string): number {\n\tconst lagnaIdx = signIndex(lagnaSign);\n\tconst signIdx = signIndex(sign);\n\tif (lagnaIdx === -1 || signIdx === -1) return 0;\n\treturn ((signIdx - lagnaIdx + 12) % 12) + 1;\n}\n\nfunction renderSouthCell(\n\tsign: string,\n\tplanets: PlacedGraha[],\n\tisLagna: boolean,\n\thouseNum: number,\n\tt: Translate,\n): TemplateResult {\n\tconst r = southCellRect(sign);\n\tconst cx = r.x + r.w / 2;\n\tconst cy = r.y + r.h / 2;\n\tconst sAbbr = signAbbr(sign) ?? sign;\n\t// Inset the Lagna diagonal so it does not collide with the chart frame on\n\t// corner cells (Pisces, Gemini, Virgo, Sagittarius) or with the sign label\n\t// in the top-left of every cell.\n\tconst slashInset = 14;\n\treturn svg`\n\t\t<g class=${isLagna ? 'cell lagna' : 'cell'}>\n\t\t\t${\n\t\t\t\tisLagna\n\t\t\t\t\t? svg`\n\t\t\t\t\t\t<rect class=\"lagna-bg\" x=${r.x} y=${r.y} width=${r.w} height=${r.h} />\n\t\t\t\t\t\t<line class=\"lagna-slash\" x1=${r.x + r.w - slashInset} y1=${r.y + slashInset} x2=${r.x + slashInset} y2=${r.y + r.h - slashInset} stroke-width=\"1.2\" />\n\t\t\t\t\t`\n\t\t\t\t\t: nothing\n\t\t\t}\n\t\t\t<text class=\"sign-text\" x=${r.x + 6} y=${r.y + 12} text-anchor=\"start\" dominant-baseline=\"central\">${sAbbr}</text>\n\t\t\t${\n\t\t\t\thouseNum > 0\n\t\t\t\t\t? svg`<text class=\"house-num\" x=${r.x + r.w - 6} y=${r.y + 12} text-anchor=\"end\" dominant-baseline=\"central\">${houseNum}</text>`\n\t\t\t\t\t: nothing\n\t\t\t}\n\t\t\t${\n\t\t\t\tisLagna\n\t\t\t\t\t? svg`<text class=\"lagna-marker\" x=${cx} y=${r.y + 26} text-anchor=\"middle\" dominant-baseline=\"central\">${t('ASC')}</text>`\n\t\t\t\t\t: nothing\n\t\t\t}\n\t\t\t${planets.length ? renderPlanetStack(planets, sign, cx, cy + 4, 14, t) : nothing}\n\t\t</g>\n\t`;\n}\n\nfunction renderSouthSvg(vm: KundliViewModel, t: Translate): TemplateResult {\n\tconst lagnaKey = vm.lagnaSign.toLowerCase();\n\treturn svg`\n\t\t${renderSouthFrame(vm.divisionLabel)}\n\t\t${SIGNS_ORDER.map((sign) =>\n\t\t\trenderSouthCell(\n\t\t\t\tsign,\n\t\t\t\tvm.placements[sign.toLowerCase()] ?? [],\n\t\t\t\tsign.toLowerCase() === lagnaKey,\n\t\t\t\thouseNumberInSign(sign, vm.lagnaSign),\n\t\t\t\tt,\n\t\t\t),\n\t\t)}\n\t`;\n}\n\n// ---------------------------------------------------------------------------\n// North Indian: outer square + inscribed midpoint diamond + both outer\n// diagonals. 12 cells: 4 cardinal diamonds + 8 corner triangles. Houses are\n// FIXED (H1 always top-centre); signs rotate from the Lagna sign.\n// ---------------------------------------------------------------------------\n\nconst NORTH_VERTICES = {\n\ttl: { x: MARGIN, y: MARGIN },\n\ttr: { x: VIEW_BOX - MARGIN, y: MARGIN },\n\tbr: { x: VIEW_BOX - MARGIN, y: VIEW_BOX - MARGIN },\n\tbl: { x: MARGIN, y: VIEW_BOX - MARGIN },\n\ttop: { x: CENTRE, y: MARGIN },\n\tright: { x: VIEW_BOX - MARGIN, y: CENTRE },\n\tbottom: { x: CENTRE, y: VIEW_BOX - MARGIN },\n\tleft: { x: MARGIN, y: CENTRE },\n\ttlMid: { x: CENTRE - INNER / 4, y: CENTRE - INNER / 4 },\n\ttrMid: { x: CENTRE + INNER / 4, y: CENTRE - INNER / 4 },\n\tbrMid: { x: CENTRE + INNER / 4, y: CENTRE + INNER / 4 },\n\tblMid: { x: CENTRE - INNER / 4, y: CENTRE + INNER / 4 },\n} as const;\n\n/**\n * Centroid (geometric mean) of an arbitrary set of polygon vertices. Used by\n * every cell that needs a label-anchor point; defining it once keeps the\n * North diamond and East triangle math identical.\n */\nfunction centroidOf(pts: Array<{ x: number; y: number }>): {\n\tx: number;\n\ty: number;\n} {\n\tconst x = pts.reduce((s, p) => s + p.x, 0) / pts.length;\n\tconst y = pts.reduce((s, p) => s + p.y, 0) / pts.length;\n\treturn { x, y };\n}\n\n/**\n * House centres for the North Indian diamond. Numbered 1..12 counter-clockwise\n * from the top diamond (H1 is always the ascendant cell). Centroids derived\n * from the canonical geometry above; do not edit by eye, recompute if you\n * change `VIEW_BOX` or `MARGIN`.\n */\nconst NORTH_HOUSE_CENTERS: Record<number, { x: number; y: number }> = {\n\t1: { x: CENTRE, y: NORTH_VERTICES.tlMid.y },\n\t2: centroidOf([NORTH_VERTICES.tl, NORTH_VERTICES.top, NORTH_VERTICES.tlMid]),\n\t3: centroidOf([NORTH_VERTICES.tl, NORTH_VERTICES.left, NORTH_VERTICES.tlMid]),\n\t4: { x: NORTH_VERTICES.tlMid.x, y: CENTRE },\n\t5: centroidOf([NORTH_VERTICES.bl, NORTH_VERTICES.left, NORTH_VERTICES.blMid]),\n\t6: centroidOf([\n\t\tNORTH_VERTICES.bl,\n\t\tNORTH_VERTICES.bottom,\n\t\tNORTH_VERTICES.blMid,\n\t]),\n\t7: { x: CENTRE, y: NORTH_VERTICES.blMid.y },\n\t8: centroidOf([\n\t\tNORTH_VERTICES.br,\n\t\tNORTH_VERTICES.bottom,\n\t\tNORTH_VERTICES.brMid,\n\t]),\n\t9: centroidOf([\n\t\tNORTH_VERTICES.br,\n\t\tNORTH_VERTICES.right,\n\t\tNORTH_VERTICES.brMid,\n\t]),\n\t10: { x: NORTH_VERTICES.brMid.x, y: CENTRE },\n\t11: centroidOf([\n\t\tNORTH_VERTICES.tr,\n\t\tNORTH_VERTICES.right,\n\t\tNORTH_VERTICES.trMid,\n\t]),\n\t12: centroidOf([NORTH_VERTICES.tr, NORTH_VERTICES.top, NORTH_VERTICES.trMid]),\n};\n\nfunction renderNorthFrame(divisionLabel?: string): TemplateResult {\n\tconst { tl, tr, br, bl, top, right, bottom, left } = NORTH_VERTICES;\n\treturn svg`\n\t\t<rect class=\"line\" x=${tl.x} y=${tl.y} width=${INNER} height=${INNER} stroke-width=\"1.5\" fill=\"none\" />\n\t\t<polygon class=\"line\" points=\"${top.x},${top.y} ${right.x},${right.y} ${bottom.x},${bottom.y} ${left.x},${left.y}\" stroke-width=\"1\" fill=\"none\" />\n\t\t<line class=\"line\" x1=${tl.x} y1=${tl.y} x2=${br.x} y2=${br.y} stroke-width=\"1\" />\n\t\t<line class=\"line\" x1=${tr.x} y1=${tr.y} x2=${bl.x} y2=${bl.y} stroke-width=\"1\" />\n\t\t${\n\t\t\tdivisionLabel\n\t\t\t\t? svg`<text class=\"centre-label\" x=${CENTRE} y=${CENTRE} text-anchor=\"middle\" dominant-baseline=\"central\">${divisionLabel}</text>`\n\t\t\t\t: nothing\n\t\t}\n\t`;\n}\n\nfunction renderNorthCell(\n\thouseNum: number,\n\trashiNum: number,\n\tsign: string,\n\tplanets: PlacedGraha[],\n\tisLagna: boolean,\n\tt: Translate,\n): TemplateResult {\n\tconst c = NORTH_HOUSE_CENTERS[houseNum];\n\tif (!c) return svg``;\n\t// Tight cells (H2/3/5/6/8/9/11/12 corner triangles) clip the rasi number\n\t// when it sits too high above the centroid. Clamp the upward offset based\n\t// on the cell's distance from the chart vertical centre so the label\n\t// always stays comfortably inside its triangle or diamond.\n\tconst rashiOffsetY = Math.min(14, Math.abs(c.y - CENTRE) * 0.45 + 6);\n\tconst ascOffsetY = rashiOffsetY + 12;\n\t// North cells carry only a rasi number by convention. The ascendant also\n\t// names its sign so the reader can see which sign rises without translating\n\t// the number; other cells stay number-only.\n\tconst rashiLabel = isLagna\n\t\t? `${rashiNum} · ${signAbbr(sign) ?? sign}`\n\t\t: `${rashiNum}`;\n\treturn svg`\n\t\t<g class=${isLagna ? 'cell lagna' : 'cell'}>\n\t\t\t<text class=\"rashi-num\" x=${c.x} y=${c.y - rashiOffsetY} text-anchor=\"middle\" dominant-baseline=\"central\">${rashiLabel}</text>\n\t\t\t${\n\t\t\t\tisLagna\n\t\t\t\t\t? svg`<text class=\"lagna-marker\" x=${c.x} y=${c.y - ascOffsetY} text-anchor=\"middle\" dominant-baseline=\"central\">${t('ASC')}</text>`\n\t\t\t\t\t: nothing\n\t\t\t}\n\t\t\t${planets.length ? renderPlanetStack(planets, sign, c.x, c.y + 8, 12, t) : nothing}\n\t\t</g>\n\t`;\n}\n\nfunction renderNorthSvg(vm: KundliViewModel, t: Translate): TemplateResult {\n\treturn svg`\n\t\t${renderNorthFrame(vm.divisionLabel)}\n\t\t${SIGNS_ORDER.map((sign, i) => {\n\t\t\t// Walking the signs rather than the houses answers both questions the\n\t\t\t// cell needs from the one house count the sign-fixed styles already use:\n\t\t\t// which house this sign falls in, and therefore which rashi that house\n\t\t\t// carries. Cells are positioned by house, so emission order is free.\n\t\t\tconst houseNum = houseNumberInSign(sign, vm.lagnaSign);\n\t\t\treturn renderNorthCell(\n\t\t\t\thouseNum,\n\t\t\t\ti + 1,\n\t\t\t\tsign,\n\t\t\t\tvm.placements[sign.toLowerCase()] ?? [],\n\t\t\t\thouseNum === 1,\n\t\t\t\tt,\n\t\t\t);\n\t\t})}\n\t`;\n}\n\n// ---------------------------------------------------------------------------\n// East Indian (Bengali / Maithili): 3x3 underlying grid, 4 edge rectangles +\n// 4 corner cells each split by a diagonal from the outer chart corner to the\n// inner corner of the centre cell. Aries fixed top-centre; signs proceed\n// counter-clockwise. Houses rotate from the Lagna.\n// ---------------------------------------------------------------------------\n\nconst EAST_CELL = INNER / 3; // 120\n\ninterface EastCell {\n\t/** Vertices of the cell polygon, in viewBox units. */\n\tpoints: Array<{ x: number; y: number }>;\n\t/** Visual centroid for label placement. */\n\tcentroid: { x: number; y: number };\n}\n\nfunction eastCells(): Record<string, EastCell> {\n\tconst a = MARGIN; // 20\n\tconst b = MARGIN + EAST_CELL; // 140\n\tconst c = MARGIN + 2 * EAST_CELL; // 260\n\tconst d = VIEW_BOX - MARGIN; // 380\n\tconst aries = [\n\t\t{ x: b, y: a },\n\t\t{ x: c, y: a },\n\t\t{ x: c, y: b },\n\t\t{ x: b, y: b },\n\t];\n\tconst cancer = [\n\t\t{ x: a, y: b },\n\t\t{ x: b, y: b },\n\t\t{ x: b, y: c },\n\t\t{ x: a, y: c },\n\t];\n\tconst libra = [\n\t\t{ x: b, y: c },\n\t\t{ x: c, y: c },\n\t\t{ x: c, y: d },\n\t\t{ x: b, y: d },\n\t];\n\tconst capricorn = [\n\t\t{ x: c, y: b },\n\t\t{ x: d, y: b },\n\t\t{ x: d, y: c },\n\t\t{ x: c, y: c },\n\t];\n\tconst taurus = [\n\t\t{ x: a, y: a },\n\t\t{ x: b, y: a },\n\t\t{ x: b, y: b },\n\t];\n\tconst gemini = [\n\t\t{ x: a, y: a },\n\t\t{ x: b, y: b },\n\t\t{ x: a, y: b },\n\t];\n\tconst leo = [\n\t\t{ x: a, y: c },\n\t\t{ x: b, y: c },\n\t\t{ x: a, y: d },\n\t];\n\tconst virgo = [\n\t\t{ x: b, y: c },\n\t\t{ x: b, y: d },\n\t\t{ x: a, y: d },\n\t];\n\tconst scorpio = [\n\t\t{ x: c, y: c },\n\t\t{ x: c, y: d },\n\t\t{ x: d, y: d },\n\t];\n\tconst sagittarius = [\n\t\t{ x: c, y: c },\n\t\t{ x: d, y: d },\n\t\t{ x: d, y: c },\n\t];\n\tconst aquarius = [\n\t\t{ x: d, y: a },\n\t\t{ x: d, y: b },\n\t\t{ x: c, y: b },\n\t];\n\tconst pisces = [\n\t\t{ x: c, y: a },\n\t\t{ x: d, y: a },\n\t\t{ x: c, y: b },\n\t];\n\tconst polys = {\n\t\tAries: aries,\n\t\tTaurus: taurus,\n\t\tGemini: gemini,\n\t\tCancer: cancer,\n\t\tLeo: leo,\n\t\tVirgo: virgo,\n\t\tLibra: libra,\n\t\tScorpio: scorpio,\n\t\tSagittarius: sagittarius,\n\t\tCapricorn: capricorn,\n\t\tAquarius: aquarius,\n\t\tPisces: pisces,\n\t} as const;\n\tconst out: Record<string, EastCell> = {};\n\tfor (const [sign, points] of Object.entries(polys)) {\n\t\tout[sign] = { points: [...points], centroid: centroidOf(points) };\n\t}\n\treturn out;\n}\n\nconst EAST_CELLS = eastCells();\n\nfunction renderEastFrame(divisionLabel?: string): TemplateResult {\n\tconst a = MARGIN;\n\tconst b = MARGIN + EAST_CELL;\n\tconst c = MARGIN + 2 * EAST_CELL;\n\tconst d = VIEW_BOX - MARGIN;\n\treturn svg`\n\t\t<rect class=\"line\" x=${a} y=${a} width=${INNER} height=${INNER} stroke-width=\"1.5\" fill=\"none\" />\n\t\t<line class=\"line\" x1=${a} y1=${b} x2=${b} y2=${b} stroke-width=\"1\" />\n\t\t<line class=\"line\" x1=${c} y1=${b} x2=${d} y2=${b} stroke-width=\"1\" />\n\t\t<line class=\"line\" x1=${a} y1=${c} x2=${b} y2=${c} stroke-width=\"1\" />\n\t\t<line class=\"line\" x1=${c} y1=${c} x2=${d} y2=${c} stroke-width=\"1\" />\n\t\t<line class=\"line\" x1=${b} y1=${a} x2=${b} y2=${b} stroke-width=\"1\" />\n\t\t<line class=\"line\" x1=${b} y1=${c} x2=${b} y2=${d} stroke-width=\"1\" />\n\t\t<line class=\"line\" x1=${c} y1=${a} x2=${c} y2=${b} stroke-width=\"1\" />\n\t\t<line class=\"line\" x1=${c} y1=${c} x2=${c} y2=${d} stroke-width=\"1\" />\n\t\t<line class=\"line\" x1=${a} y1=${a} x2=${b} y2=${b} stroke-width=\"1\" />\n\t\t<line class=\"line\" x1=${d} y1=${a} x2=${c} y2=${b} stroke-width=\"1\" />\n\t\t<line class=\"line\" x1=${d} y1=${d} x2=${c} y2=${c} stroke-width=\"1\" />\n\t\t<line class=\"line\" x1=${a} y1=${d} x2=${b} y2=${c} stroke-width=\"1\" />\n\t\t${\n\t\t\tdivisionLabel\n\t\t\t\t? svg`<text class=\"centre-label\" x=${CENTRE} y=${CENTRE} text-anchor=\"middle\" dominant-baseline=\"central\">${divisionLabel}</text>`\n\t\t\t\t: nothing\n\t\t}\n\t`;\n}\n\nfunction renderEastCell(\n\tsign: string,\n\tplanets: PlacedGraha[],\n\tisLagna: boolean,\n\thouseNum: number,\n\tt: Translate,\n): TemplateResult {\n\tconst cell = EAST_CELLS[sign];\n\tif (!cell) return svg``;\n\tconst { centroid: cen, points } = cell;\n\tconst sAbbr = signAbbr(sign) ?? sign;\n\tconst polyPoints = points.map((p) => `${p.x},${p.y}`).join(' ');\n\treturn svg`\n\t\t<g class=${isLagna ? 'cell lagna' : 'cell'}>\n\t\t\t${\n\t\t\t\tisLagna\n\t\t\t\t\t? svg`<polygon class=\"lagna-bg\" points=${polyPoints} />`\n\t\t\t\t\t: nothing\n\t\t\t}\n\t\t\t<text class=\"sign-text\" x=${cen.x} y=${cen.y - 16} text-anchor=\"middle\" dominant-baseline=\"central\">${sAbbr}</text>\n\t\t\t${\n\t\t\t\thouseNum > 0\n\t\t\t\t\t? svg`<text class=\"house-num\" x=${cen.x + 18} y=${cen.y - 16} text-anchor=\"start\" dominant-baseline=\"central\">${houseNum}</text>`\n\t\t\t\t\t: nothing\n\t\t\t}\n\t\t\t${\n\t\t\t\tisLagna\n\t\t\t\t\t? svg`<text class=\"lagna-marker\" x=${cen.x} y=${cen.y - 30} text-anchor=\"middle\" dominant-baseline=\"central\">${t('ASC')}</text>`\n\t\t\t\t\t: nothing\n\t\t\t}\n\t\t\t${planets.length ? renderPlanetStack(planets, sign, cen.x, cen.y + 4, 12, t) : nothing}\n\t\t</g>\n\t`;\n}\n\nfunction renderEastSvg(vm: KundliViewModel, t: Translate): TemplateResult {\n\tconst lagnaKey = vm.lagnaSign.toLowerCase();\n\treturn svg`\n\t\t${renderEastFrame(vm.divisionLabel)}\n\t\t${SIGNS_ORDER.map((sign) =>\n\t\t\trenderEastCell(\n\t\t\t\tsign,\n\t\t\t\tvm.placements[sign.toLowerCase()] ?? [],\n\t\t\t\tsign.toLowerCase() === lagnaKey,\n\t\t\t\thouseNumberInSign(sign, vm.lagnaSign),\n\t\t\t\tt,\n\t\t\t),\n\t\t)}\n\t`;\n}\n\n// ---------------------------------------------------------------------------\n// Public entry point\n// ---------------------------------------------------------------------------\n\n/** True when the chart carries an ascendant the house-fixed north layout can be anchored to. The one place that question is answered, so the renderer and the caller showing {@link renderMissingLagnaNote} can never disagree about it. */\nexport function hasLagna(vm: KundliViewModel): boolean {\n\treturn signIndex(vm.lagnaSign) !== -1;\n}\n\n/** Key for each mark, listing only the ones this chart actually uses so a legend never explains a symbol nobody can see. */\nfunction renderGrahaMarkLegend(\n\tvm: KundliViewModel,\n\tt: Translate,\n): TemplateResult | typeof nothing {\n\tconst placed = Object.values(vm.placements).flat();\n\tconst keys: string[] = [];\n\tif (placed.some((p) => p.isRetrograde)) {\n\t\tkeys.push(`${RETRO_MARK} ${t('retrograde')}`);\n\t}\n\tif (placed.some((p) => p.combust)) {\n\t\tkeys.push(`${COMBUST_MARK} ${t('combust')}`);\n\t}\n\tif (placed.some((p) => p.war)) {\n\t\tkeys.push(`${WAR_MARK} ${t('planetary war')}`);\n\t}\n\tif (keys.length === 0) return nothing;\n\treturn html`<p class=\"roxy-frame\">${keys.join(' · ')}</p>`;\n}\n\n/** One line saying a chart arrived with no ascendant, so houses go unnumbered and the sign-fixed layout is drawn. */\nfunction renderMissingLagnaNote(t: Translate): TemplateResult {\n\treturn html`<p class=\"roxy-frame\">${t('No ascendant in this chart, so the houses are not numbered.')}</p>`;\n}\n\n/**\n * Every caption that belongs under a kundli: why the layout changed when no ascendant arrived, and the key to the state marks the cells carry.\n *\n * @remarks\n * One entry point rather than two, because both callers need the identical set and a caller that rendered only one of them would explain the marks on a chart whose layout had silently changed, or the reverse. Uses the shared frame-caption class, so a component already showing the sidereal frame needs no extra styles.\n */\nexport function renderKundliCaptions(\n\tvm: KundliViewModel,\n\tt: Translate,\n): TemplateResult[] {\n\tconst out: TemplateResult[] = [];\n\tif (!hasLagna(vm)) out.push(renderMissingLagnaNote(t));\n\tconst legend = renderGrahaMarkLegend(vm, t);\n\tif (legend !== nothing) out.push(legend);\n\treturn out;\n}\n\n/**\n * Render the kundli body for the requested style. Returns the SVG inner\n * content; the caller wraps it in an `<svg>` element with the canonical\n * viewBox `0 0 400 400` and applies its own theming CSS.\n */\nexport function renderKundliSvg(\n\tvm: KundliViewModel,\n\tstyle: ChartStyle,\n\tt: Translate,\n): TemplateResult {\n\t// The north layout is house-fixed: every cell IS a house and the rashi it\n\t// carries is counted from the Lagna. With no ascendant there is nothing to\n\t// anchor that rotation to, and assuming one draws a complete, plausible chart\n\t// of a different nativity. The sign-fixed layout needs no ascendant, so it is\n\t// drawn instead and {@link renderMissingLagnaNote} says why.\n\tif (style === 'north' && !hasLagna(vm)) return renderSouthSvg(vm, t);\n\tswitch (style) {\n\t\tcase 'north':\n\t\t\treturn renderNorthSvg(vm, t);\n\t\tcase 'east':\n\t\t\treturn renderEastSvg(vm, t);\n\t\tdefault:\n\t\t\treturn renderSouthSvg(vm, t);\n\t}\n}\n\n/**\n * Render a WAI-ARIA-compliant tablist that lets the end user switch between\n * South / North / East kundli styles at runtime. The hosting component owns\n * the `chartStyle` state; this helper renders the buttons and wires the\n * arrow-key navigation plus click handler.\n *\n * @param active - The currently selected style.\n * @param setStyle - Callback the host component uses to update its state.\n */\nexport function renderKundliStyleTablist(\n\tactive: ChartStyle,\n\tsetStyle: (next: ChartStyle) => void,\n\tt: Translate,\n): TemplateResult {\n\treturn renderTablist({\n\t\titems: CHART_STYLES.map(({ id, source }) => ({ id, label: t(source) })),\n\t\tactive,\n\t\tonSelect: setStyle,\n\t\tlabel: t('Kundli style'),\n\t\tidPrefix: 'kundli',\n\t\t// The chart SVG below is the panel these tabs swap. Callers MUST wrap it in\n\t\t// `<div role=\"tabpanel\" id=\"kundli-panel-${style}\">`, otherwise the buttons\n\t\t// claim role=tab while governing nothing, which is what a screen reader\n\t\t// announces as a broken tab set.\n\t\tcontrols: true,\n\t});\n}\n", "/**\n * Math helpers for converting raw ecliptic longitude decimals into the\n * sign / degree / minute / second triplet used across chart components.\n */\n\nimport { SIGNS_ORDER } from '../tokens/index.js';\n\nexport interface SignPosition {\n\tsign: string;\n\tsignIndex: number;\n\tdegree: number;\n\tminute: number;\n\tsecond: number;\n}\n\n/**\n * Wrap longitude into [0, 360) so negative or out-of-range values still\n * resolve to a real sign. Robust to wonky upstream data.\n */\nexport function normalizeLongitude(lon: number): number {\n\tconst wrapped = lon % 360;\n\treturn wrapped < 0 ? wrapped + 360 : wrapped;\n}\n\n/**\n * Convert decimal ecliptic longitude (0-360) into sign/degree/minute/second.\n * Used by every chart wheel and aspect table.\n */\nexport function longitudeToSignPosition(longitude: number): SignPosition {\n\tconst lon = normalizeLongitude(longitude);\n\tconst signIndex = Math.floor(lon / 30) % 12;\n\tconst within = lon % 30;\n\tconst degree = Math.floor(within);\n\tconst minuteFloat = (within - degree) * 60;\n\tconst minute = Math.floor(minuteFloat);\n\tconst second = Math.round((minuteFloat - minute) * 60);\n\treturn {\n\t\tsign: SIGNS_ORDER[signIndex] ?? 'Aries',\n\t\tsignIndex,\n\t\tdegree,\n\t\tminute,\n\t\tsecond,\n\t};\n}\n\n/**\n * Compact display string like `12° Leo 34'`, for a chart label.\n *\n * @param signLabel - The sign NAME to print. Pass the one the response carried whenever there is one: the degree and minute here are arithmetic on the longitude and always correct, but the sign is a name, and deriving it from a local table means the label can disagree with the sign printed beside it the moment a response answers in another language. Omitted, it falls back to the derived English name, which is right only while the caller has nothing better.\n */\nexport function formatSignPosition(\n\tlongitude: number,\n\tsignLabel?: string,\n): string {\n\tconst { sign, degree, minute } = longitudeToSignPosition(longitude);\n\treturn `${degree}° ${signLabel || sign} ${String(minute).padStart(2, '0')}'`;\n}\n\n/**\n * A within-sign decimal degree (0-30) split into whole degrees and minutes, with the rounding carry already applied.\n *\n * @remarks\n * Separate from {@link formatDegreeInSign} because a printed ephemeris interleaves the sign BETWEEN the two halves (`09♌56`, the form every published ephemeris has used for a century) rather than putting the sign beside a finished `9°56'`. Rounding 59.6 minutes up has to roll the degree with it, and that carry is the part a caller gets wrong, so it lives here once and both forms read it.\n */\nexport function splitDegreeInSign(deg: number): {\n\tdegree: number;\n\tminute: number;\n} {\n\tlet degree = Math.floor(deg);\n\tlet minute = Math.round((deg - degree) * 60);\n\tif (minute === 60) {\n\t\tminute = 0;\n\t\tdegree += 1;\n\t}\n\treturn { degree, minute };\n}\n\n/** Format a within-sign decimal degree (0-30) as degree-and-minute, e.g. 17.99 to \"17°59'\". The reference-grade form astrologers read when the sign is already known (asteroids, lots, directed points, fixed stars). */\nexport function formatDegreeInSign(deg: number): string {\n\tconst { degree, minute } = splitDegreeInSign(deg);\n\treturn `${degree}°${String(minute).padStart(2, '0')}'`;\n}\n\n/**\n * The point diametrically opposite a longitude (e.g. Descendant from\n * Ascendant, IC from MC). Exact derivation, always 180 degrees away.\n */\nexport function oppositePoint(longitude: number): number {\n\treturn normalizeLongitude(longitude + 180);\n}\n\n/**\n * Midpoint of the forward arc from `start` to `end` (both ecliptic\n * longitudes). Handles the 360/0 wrap, so a house spanning 350 to 20 degrees\n * yields a midpoint of 5, not 185. Used to place house numbers between two\n * cusps regardless of how unequal the house is.\n */\nexport function arcMidpoint(start: number, end: number): number {\n\tconst s = normalizeLongitude(start);\n\tlet span = normalizeLongitude(end) - s;\n\tif (span < 0) span += 360;\n\treturn normalizeLongitude(s + span / 2);\n}\n\n/** A body placed on a wheel: where it really is, and where its glyph had to be drawn to stay legible. */\nexport interface FannedPoint<T> {\n\titem: T;\n\t/** The true ecliptic longitude. Where a leader line points, and the only value a reader should take as the position. */\n\tlongitude: number;\n\t/** Where the glyph is drawn, pushed forward only as far as it takes to clear its neighbour. Equal to {@link FannedPoint.longitude} whenever nothing was in the way. */\n\tdisplayLongitude: number;\n}\n\n/**\n * Spread a cluster of bodies apart along a wheel so every glyph stays readable,\n * without moving any of them off its real position in the data.\n *\n * @remarks\n * Conjunctions inside a few degrees are the norm, not the exception: a\n * Sun-Mercury-Venus cluster or a stack of outer planets will pile four glyphs on\n * top of each other and print their degree labels over one another. Sorting by\n * longitude and pushing each later member forward until it clears\n * `minSeparation` is the conventional fix in professional chart software, and it\n * is why {@link FannedPoint} carries BOTH longitudes: the caller draws the glyph\n * at the display value and a leader line back to the true one, so nothing about\n * the drawing claims a position the response did not give.\n *\n * `minSeparation` is an ANGLE, so it depends on the radius the caller is drawing\n * at: the same glyph needs more degrees of arc on a small ring than a large one.\n *\n * If a cluster runs past 360 degrees the whole set slides back by the overshoot,\n * which keeps the stack anchored near its real longitudes instead of wrapping\n * one member around to the far side of the wheel.\n */\nexport function fanOut<T>(\n\titems: readonly T[],\n\tlongitudeOf: (item: T) => number,\n\tminSeparation: number,\n): FannedPoint<T>[] {\n\tconst placed: FannedPoint<T>[] = items\n\t\t.filter((item) => Number.isFinite(longitudeOf(item)))\n\t\t.map((item) => {\n\t\t\tconst longitude = normalizeLongitude(longitudeOf(item));\n\t\t\treturn { item, longitude, displayLongitude: longitude };\n\t\t})\n\t\t.sort((a, b) => a.longitude - b.longitude);\n\n\tfor (let i = 1; i < placed.length; i++) {\n\t\tconst prev = placed[i - 1];\n\t\tconst cur = placed[i];\n\t\tif (!prev || !cur) continue;\n\t\tconst wanted = prev.displayLongitude + minSeparation;\n\t\tif (cur.displayLongitude < wanted) cur.displayLongitude = wanted;\n\t}\n\n\tconst last = placed[placed.length - 1];\n\tif (last && last.displayLongitude > 360) {\n\t\tconst shift = last.displayLongitude - 360;\n\t\tfor (const p of placed) p.displayLongitude -= shift;\n\t}\n\treturn placed;\n}\n\n/**\n * Degrees of arc a mark of `width` user units occupies at `radius`, which is the\n * separation {@link fanOut} needs to keep two of them from touching. One\n * expression instead of a magic number per ring, so an inner ring automatically\n * asks for more degrees than an outer one.\n */\nexport function arcSeparation(width: number, radius: number): number {\n\treturn radius > 0 ? (width / radius) * (180 / Math.PI) : 0;\n}\n\n/** Polar to cartesian for SVG wheel positioning. Angle in degrees, 0 at 3 o'clock. */\nexport function polarToCartesian(\n\tcx: number,\n\tcy: number,\n\tradius: number,\n\tangleDeg: number,\n): { x: number; y: number } {\n\tconst angleRad = (angleDeg * Math.PI) / 180;\n\treturn {\n\t\tx: cx + radius * Math.cos(angleRad),\n\t\ty: cy + radius * Math.sin(angleRad),\n\t};\n}\n", "import { css } from 'lit';\n\n/**\n * Shared CSS for every kundli renderer (`<roxy-vedic-kundli>`,\n * `<roxy-divisional-chart>`, and any future `<roxy-navamsa-chart>`). Centralises\n * the SVG layout (responsive viewBox + aspect ratio), the line/text classes,\n * and the Lagna highlight so the three components stay visually identical.\n *\n * @remarks Font sizes are written in viewBox user units (the chart is 400×400\n * inside a 1:1 aspect-ratio container), so they scale linearly from a 320px\n * phone surface to a wall projector without raster loss. The Lagna palette\n * tracks `--roxy-accent` so host themes flow through unchanged.\n */\nexport const kundliStyles = css`.wrap{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);display:grid}.header{justify-content:space-between;align-items:center;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}svg{width:100%;max-width:var(--roxy-chart-max-width,560px);aspect-ratio:1;height:auto;margin:0 auto;display:block}.line{fill:#0000;stroke:var(--roxy-border,#d4d4d8)}.sign-text{fill:var(--roxy-muted,#71717a);font-size:11px;font-weight:500;font-family:var(--roxy-font-sans);text-transform:uppercase;letter-spacing:.04em}.rashi-num{fill:var(--roxy-muted,#71717a);font-size:12px;font-weight:500;font-family:var(--roxy-font-sans)}.house-num{fill:var(--roxy-accent-ink,#b45309);font-size:11px;font-weight:600;font-family:var(--roxy-font-sans);opacity:.85}.planet-text{fill:var(--roxy-fg,#0a0a0a);font-size:13px;font-weight:600;font-family:var(--roxy-font-sans)}.centre-label{fill:var(--roxy-muted,#71717a);font-size:14px;font-weight:600;font-family:var(--roxy-font-sans);letter-spacing:.02em}.lagna-marker{fill:var(--roxy-accent-ink,#b45309);font-size:10px;font-weight:700;font-family:var(--roxy-font-sans);letter-spacing:.08em;text-transform:uppercase}.lagna-bg{fill:color-mix(in srgb, var(--roxy-accent,#f59e0b) 14%, transparent)}.lagna-slash{stroke:var(--roxy-accent,#f59e0b);stroke-linecap:round;opacity:.7}`;\n", "import { css, html, nothing } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport type {\n\tKalsarpaResponse,\n\tManglikResponse,\n\tSadhesatiResponse,\n} from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { frameCaptionStyles, renderFrameCaption } from '../utils/frame.js';\n\ntype DoshaData = ManglikResponse | KalsarpaResponse | SadhesatiResponse;\n\nconst DOSHA_LABELS: Record<string, string> = {\n\tmanglik: 'Mangal Dosha',\n\tkalsarpa: 'Kaal Sarp Dosha',\n\tsadhesati: 'Sade Sati',\n};\n\n/**\n * Dosha presence card. Renders /vedic-astrology/dosha/{manglik,kalsarpa,sadhesati}.\n * Visual severity indicator + remedies + scoped effects.\n *\n * @remarks\n * The verdict is the data and the counsel is the reading. `hide-readings` keeps the present/absent badge, the subtype (which for Sade Sati is the phase, the whole answer), the severity meter and the frame caption, and drops the description, the scoped effects, the remedies and the exceptions. Each of those three sections is prose end to end, so it goes whole rather than leaving a heading over nothing.\n */\n@customElement('roxy-dosha-card')\nexport class RoxyDoshaCard extends RoxyDataElement<DoshaData> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tframeCaptionStyles,\n\t\tcss`.card{background:var(--roxy-surface,#fff);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.head{justify-content:space-between;align-items:center;gap:var(--roxy-space-md,1rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);text-transform:capitalize;margin:0}.subtype{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a);margin:0}.subtype .subtype-label{color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.06em;font-size:var(--roxy-text-xs,.75rem);margin-right:.4rem}.badge{align-items:center;gap:var(--roxy-space-xs,.25rem);border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);text-transform:uppercase;letter-spacing:.06em;padding:4px 10px;display:inline-flex}.badge.absent{background:color-mix(in srgb, var(--roxy-success,#16a34a) 16%, transparent);color:var(--roxy-success-fg,#166534)}.badge.present{background:color-mix(in srgb, var(--roxy-danger,#dc2626) 16%, transparent);color:var(--roxy-danger-fg,#991b1b)}.severity-bar{background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 30%, transparent);border-radius:4px;width:100%;height:8px;position:relative;overflow:hidden}.severity-fill{height:100%;transition:width var(--roxy-motion-duration,.2s) ease-out;border-radius:4px;display:block}@media (prefers-reduced-motion:reduce){.severity-fill{transition:none}}.description{color:var(--roxy-fg,#0a0a0a);margin:0}h3{margin:0 0 var(--roxy-space-xs,.25rem);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.06em}ul{padding-left:var(--roxy-space-md,1rem);font-size:var(--roxy-text-sm,.875rem);margin:0}.effects{gap:var(--roxy-space-md,1rem);grid-template-columns:repeat(auto-fit,minmax(12rem,1fr));display:grid}.effects p{font-size:var(--roxy-text-sm,.875rem);margin:0}`,\n\t];\n\n\t@property({ type: String, reflect: true })\n\ttype: 'manglik' | 'kalsarpa' | 'sadhesati' | string = 'manglik';\n\n\tprotected renderData(d: DoshaData) {\n\t\tconst present = !!d.present;\n\t\tconst label = DOSHA_LABELS[this.type] ?? this.type;\n\t\tconst sevLower = (d.severity ?? '').toLowerCase();\n\t\tconst tier =\n\t\t\tsevLower === 'severe'\n\t\t\t\t? 3\n\t\t\t\t: sevLower === 'moderate'\n\t\t\t\t\t? 2\n\t\t\t\t\t: sevLower === 'mild'\n\t\t\t\t\t\t? 1\n\t\t\t\t\t\t: 0;\n\t\tconst pct = tier * 33;\n\t\t// A present dosha is never \"good news\": keep the ramp in the warning/danger\n\t\t// family so a Mild severity does not paint green and read as benign. The bar\n\t\t// width already conveys the tier.\n\t\tconst barColor =\n\t\t\ttier === 3\n\t\t\t\t? 'var(--roxy-danger)'\n\t\t\t\t: tier >= 1\n\t\t\t\t\t? 'var(--roxy-warning)'\n\t\t\t\t\t: 'transparent';\n\n\t\treturn html`<article class=\"card\" part=\"card\" aria-label=\"${label}\"><header class=\"head\" part=\"header\"><h2 class=\"title\">${label}</h2><span class=\"${`badge ${present ? 'present' : 'absent'}`}\">${present ? this.t('Present') : this.t('Absent')} </span>${renderFrameCaption(this.effectiveLang(), d.frame, this.translator)}</header>${\n\t\t\t\t'type' in d && d.type\n\t\t\t\t\t? html`<p class=\"subtype\"><span class=\"subtype-label\">${this.type === 'sadhesati' ? this.t('Current phase') : this.t('Type')}</span>${d.type}</p>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\td.severity\n\t\t\t\t\t? html`<div class=\"severity-bar\" part=\"details\" role=\"meter\" aria-valuemin=\"0\" aria-valuemax=\"3\" aria-valuenow=\"${tier}\" aria-label=\"${this.t('Severity')} ${d.severity}\"><span class=\"severity-fill\" style=\"width:${pct}%;background:${barColor}\"></span></div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\t// The badge above states the verdict; this paragraph is the read of it.\n\t\t\t\td.description && !this.hideReadings\n\t\t\t\t\t? html`<p class=\"description\">${d.description}</p>`\n\t\t\t\t\t: nothing\n\t\t\t} ${this.renderEffects(d)} ${\n\t\t\t\t// Remedies and exceptions are prescriptive sentences laid out as bullets,\n\t\t\t\t// so each list goes with its own heading rather than leaving one behind.\n\t\t\t\td.remedies && d.remedies.length > 0 && !this.hideReadings\n\t\t\t\t\t? html`<div part=\"section remedies\"><h3>${this.t('Remedies')}</h3><ul>${d.remedies.map((r) => html`<li>${r}</li>`)}</ul></div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\t'exceptions' in d &&\n\t\t\t\td.exceptions &&\n\t\t\t\td.exceptions.length > 0 &&\n\t\t\t\t!this.hideReadings\n\t\t\t\t\t? html`<div part=\"section exceptions\"><h3>${this.t('Exceptions')}</h3><ul>${d.exceptions.map((r) => html`<li>${r}</li>`)}</ul></div>`\n\t\t\t\t\t: nothing\n\t\t\t}</article>`;\n\t}\n\n\tprivate renderEffects(d: DoshaData) {\n\t\t// Every effect is a paragraph about a life area, so the block is prose end to\n\t\t// end and goes whole under hide-readings.\n\t\tif (!d.effects || this.hideReadings) return nothing;\n\t\t// Effects mix flat string fields (marriage, career...) with a nested map\n\t\t// (Sadhesati effects.phases: { Rising, Peak, Setting }). Render both; the\n\t\t// a string-only filter would drop the phase-specific effects, which\n\t\t// are the substance of a Sade Sati reading.\n\t\tconst sections: unknown[] = [];\n\t\tfor (const [key, value] of Object.entries(\n\t\t\td.effects as Record<string, unknown>,\n\t\t)) {\n\t\t\tif (typeof value === 'string' && value.length > 0) {\n\t\t\t\tsections.push(html`<div><h3>${key}</h3><p>${value}</p></div>`);\n\t\t\t} else if (value && typeof value === 'object') {\n\t\t\t\tfor (const [nestedKey, nestedValue] of Object.entries(\n\t\t\t\t\tvalue as Record<string, unknown>,\n\t\t\t\t)) {\n\t\t\t\t\tif (typeof nestedValue === 'string' && nestedValue.length > 0) {\n\t\t\t\t\t\tsections.push(\n\t\t\t\t\t\t\thtml`<div><h3>${nestedKey}</h3><p>${nestedValue}</p></div>`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (sections.length === 0) return nothing;\n\t\treturn html`<div class=\"effects\" part=\"section effects\">${sections}</div>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-dosha-card': RoxyDoshaCard;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport type { GetDreamSymbolResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\n\n/**\n * Dream symbol card. Renders /dreams/symbols/{id}: the symbol name as a heading, the full psychological interpretation as the body, and the dictionary letter as a chip for alphabetical context.\n *\n * @remarks\n * **`hide-readings` is a deliberate no-op here, and it is the only card in the library that is.** The response is `{id, name, letter, meaning}`: three of those four fields are the lookup key itself, and `meaning` is the entire card. Honouring the attribute would leave a dictionary heading with nothing under it, which is not a chart without a report, it is an empty card. Rendering nothing at all was the other option and is worse, because a site-wide `hide-readings` would then delete the component from the page rather than trim it. Documented in README.md and AGENTS.md so an integrator learns this without testing for it, and pinned by `components.test.ts`.\n */\n@customElement('roxy-dream-card')\nexport class RoxyDreamCard extends RoxyDataElement<GetDreamSymbolResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.card{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.head{align-items:center;gap:var(--roxy-space-md,1rem);display:flex}.letter{border-radius:var(--roxy-radius-full,9999px);background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 16%, transparent);width:2.5rem;height:2.5rem;color:var(--roxy-accent-ink,#b45309);font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);text-transform:uppercase;font-variant-numeric:tabular-nums;flex:none;justify-content:center;align-items:center;display:inline-flex}.label{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.06em;margin:0}.name{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-fg,#0a0a0a);margin:0}.meaning{color:var(--roxy-fg,#0a0a0a);margin:0;line-height:1.6}`,\n\t];\n\n\tprotected renderData(d: GetDreamSymbolResponse) {\n\t\treturn html`<article class=\"card\" part=\"card\" aria-label=\"${d.name ?? this.t('Dream symbol')}\"><header class=\"head\" part=\"header\">${d.letter ? html`<span class=\"letter\" aria-hidden=\"true\">${d.letter}</span>` : nothing}<div><p class=\"label\">${this.t('Dream symbol')}</p>${d.name ? html`<h2 class=\"name\">${d.name}</h2>` : nothing}</div></header>${d.meaning ? html`<p class=\"meaning\">${d.meaning}</p>` : nothing}</article>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-dream-card': RoxyDreamCard;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport type { SearchDreamSymbolsResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\n\ntype DreamSymbol = NonNullable<SearchDreamSymbolsResponse['symbols']>[number];\n\n/**\n * Dream-symbol search results. Renders /dreams/symbols (the `q` search): the matched symbols as selectable tiles. In self-fetch mode the base renders the search input and this lists the matches. Selecting a result emits a `roxy-symbol-select` CustomEvent ({ id, name, letter }) that bubbles and is composed, so a host pairs it with a roxy-dream-card to show the full meaning. This is the dreams form-mode analog of roxy-location-search.\n */\n@customElement('roxy-dream-search')\nexport class RoxyDreamSearch extends RoxyDataElement<SearchDreamSymbolsResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.wrap{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.head{justify-content:space-between;align-items:baseline;gap:var(--roxy-space-md,1rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.count{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem)}.grid{gap:var(--roxy-space-sm,.5rem);grid-template-columns:repeat(auto-fill,minmax(8rem,1fr));margin:0;padding:0;list-style:none;display:grid}.result{align-items:center;gap:var(--roxy-space-sm,.5rem);text-align:left;width:100%;font:inherit;color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);cursor:pointer;background:0 0;transition:border-color .12s,background .12s;display:flex}.result:hover,.result:focus-visible{border-color:var(--roxy-accent,#f59e0b);background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 8%, transparent);outline:none}.letter{border-radius:var(--roxy-radius-full,9999px);background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 16%, transparent);width:1.6rem;height:1.6rem;color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);text-transform:uppercase;flex:none;place-items:center;display:grid}.name{font-size:var(--roxy-text-sm,.875rem);font-weight:500}`,\n\t];\n\n\tprotected renderData(d: SearchDreamSymbolsResponse) {\n\t\tconst symbols = d.symbols ?? [];\n\t\tif (symbols.length === 0) return this.renderEmpty();\n\t\tconst total = typeof d.total === 'number' ? d.total : symbols.length;\n\n\t\treturn html`<section class=\"wrap\" part=\"card\" aria-label=\"${this.t('Dream symbols')}\"><header class=\"head\" part=\"header\"><h2 class=\"title\">${this.t('Dream symbols')}</h2><span class=\"count\">${this.t('{{count}} matches', { count: total })}</span></header><ul class=\"grid\" part=\"section symbols\">${symbols.map(\n\t\t\t\t\t(s) => html`<li><button type=\"button\" class=\"result\" @click=\"${() => this.select(s)}\">${s.letter ? html`<span class=\"letter\" aria-hidden=\"true\">${s.letter}</span>` : nothing} <span class=\"name\">${s.name}</span></button></li>`,\n\t\t\t\t)}</ul></section>`;\n\t}\n\n\t/** Emit the chosen symbol so a host can load its detail (e.g. into a roxy-dream-card). */\n\tprivate select(s: DreamSymbol) {\n\t\tthis.dispatchEvent(\n\t\t\tnew CustomEvent('roxy-symbol-select', {\n\t\t\t\tdetail: { id: s.id, name: s.name, letter: s.letter },\n\t\t\t\tbubbles: true,\n\t\t\t\tcomposed: true,\n\t\t\t}),\n\t\t);\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-dream-search': RoxyDreamSearch;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { ifDefined } from 'lit/directives/if-defined.js';\nimport { apiLang } from '../i18n/lang.js';\nimport { RoxyLocalizedElement } from '../i18n/localized-element.js';\nimport { signGlyph } from '../tokens/index.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { chevron, disclosureStyles } from '../utils/disclosure.js';\nimport {\n\tbuildFormModel,\n\tderiveSubmitLabel,\n\ttype FieldDef,\n\ttype FormModel,\n\tisZodiacEnum,\n\tLOCATION_PAIR,\n\tLOCATION_TRIO,\n\ttype OpenApiDoc,\n\ttype OperationSchema,\n\tsliceFileName,\n} from '../utils/field-schema.js';\nimport { displayField, displayOption } from '../utils/localized.js';\nimport { humanize } from '../utils/string.js';\nimport { ROXY_UI_VERSION } from '../version.js';\n\n/** Production spec, fetched when no slice is available and no explicit `spec-url` is set. */\nconst PROD_SPEC_URL = 'https://roxyapi.com/api/v2/openapi.json';\n\n/** Version-pinned jsDelivr base for the digested per-operation schema slices, mirroring the wrapper CDN pinning so a slice matches the bundle it ships beside. */\nconst SLICE_BASE = `https://cdn.jsdelivr.net/npm/@roxyapi/ui@${ROXY_UI_VERSION}/dist/schemas`;\n\nconst specCache = new Map<string, Promise<OpenApiDoc>>();\n\n/**\n * A spec fetch that came back with a non-2xx status, carrying the STATUS rather than a sentence.\n *\n * @remarks\n * The visible wording is composed by the element, in the element's language, and never here: {@link loadSpec} shares one cached promise across every form on the page, so a message built at throw time would freeze the first mounter's language onto every later one. Same split as everywhere else in this library, the machine value travels and the display copy is made where the reader is.\n */\nclass SpecHttpError extends Error {\n\tconstructor(readonly status: number) {\n\t\tsuper(`HTTP ${status}`);\n\t}\n}\n\n/** The endpoint is absent from the document that was loaded. Carries the two parts the message names, for the reason {@link SpecHttpError} gives. */\nclass SpecMissingOperationError extends Error {\n\tconstructor(\n\t\treadonly method: string,\n\t\treadonly path: string,\n\t) {\n\t\tsuper(`Endpoint ${method} ${path} not found in OpenAPI spec`);\n\t}\n}\n\n/**\n * Drop every cached spec promise.\n\n * @remarks\n * Test-only, and deliberately NOT re-exported from the package entry. The cache is module state shared by every test file in a single `bun test` process, and it only self-evicts on rejection, so one file that resolves a spec leaves a warm entry that silently satisfies a later file assertion about fetching. That made slice-fallback coverage pass or fail purely on file order.\n\n * @internal\n */\nexport function resetSpecCache(): void {\n\tspecCache.clear();\n}\n\nasync function loadSpec(url: string): Promise<OpenApiDoc> {\n\tlet pending = specCache.get(url);\n\tif (!pending) {\n\t\tpending = fetch(url)\n\t\t\t.then(async (res) => {\n\t\t\t\tif (!res.ok) throw new SpecHttpError(res.status);\n\t\t\t\treturn (await res.json()) as OpenApiDoc;\n\t\t\t})\n\t\t\t.catch((err) => {\n\t\t\t\t// Evict the rejected promise BEFORE rethrowing so subsequent\n\t\t\t\t// callers (the user clicking Retry, a remount) hit the network\n\t\t\t\t// again instead of replaying the cached failure forever.\n\t\t\t\tspecCache.delete(url);\n\t\t\t\tthrow err;\n\t\t\t});\n\t\tspecCache.set(url, pending);\n\t}\n\treturn pending;\n}\n\n/** A fresh seed for a hidden `seed` field, so each submit is a new draw unless the caller pinned one. */\nfunction randomSeed(): string {\n\treturn (\n\t\tglobalThis.crypto?.randomUUID?.() ?? Math.random().toString(36).slice(2)\n\t);\n}\n\n/** Parse an array-field text value into a real array: JSON when it parses to one, else comma-separated tokens. */\nfunction parseArrayValue(raw: string): unknown {\n\tconst trimmed = raw.trim();\n\tif (!trimmed) return [];\n\ttry {\n\t\tconst parsed = JSON.parse(trimmed);\n\t\tif (Array.isArray(parsed)) return parsed;\n\t} catch {\n\t\t// Not JSON: fall through to comma splitting.\n\t}\n\treturn trimmed\n\t\t.split(',')\n\t\t.map((s) => s.trim())\n\t\t.filter(Boolean);\n}\n\n/**\n * Schema-driven form. Pass `endpoint` (e.g. \"vedic-astrology/birth-chart\"). The form digests the operation's request schema into a spec-derived input for each parameter (a zodiac tile grid, a date or time input, a city search, a toggle) with progressive disclosure, and emits a `roxy-submit` CustomEvent carrying the validated payload. The caller decides what to do (call the SDK, render a chart, navigate).\n *\n * @remarks\n * Schema resolution order: an explicit `spec-url` fetches that full spec and digests it (unchanged, the demo path); otherwise the form tries a small version-pinned per-operation slice from the CDN, and on any miss falls back to fetching the production spec. Each input kind is chosen purely from the parameter shape (see {@link ../utils/field-schema.ts}), so a new endpoint gets a working, on-brand form with no per-endpoint code.\n *\n * The visitor-facing `lang` parameter is never rendered: a site owner sets the element `lang` attribute, and the form routes it to the query string on submit. Optional parameters collapse under one Advanced disclosure; a form whose only required field is an enum submits on selection.\n *\n * **Two languages are in play here and they are different answers.** {@link RoxyLocalizedElement.effectiveLang} is the DISPLAY tag, region included, and it is what every `t()` call and the city search read. {@link RoxyEndpointForm.requestLang} is the WIRE value, region stripped and unsupported languages omitted, and it is what reaches `?lang=`. Swapping them is silent in both directions: the request one demotes every regional visitor, and the display one is a 400.\n *\n * **Field labels and option text ARE translated now, and they arrive over the network rather than from a catalogue.** They are keyed by the WIRE NAME the spec uses, not by the English text, because that English text is itself computed here by `humanize()`: there is no constant for a source-keyed catalogue to key on, which is why the chrome catalogue could never reach them. {@link RoxyEndpointForm.fieldText} and {@link RoxyEndpointForm.optionText} read a map baked into the per-language payload at build time and published through the same registry the chrome strings use. It rides the `locales/{lang}.js` download a translated page already makes, so it costs no extra request, needs no network at render time, and cannot flash English before correcting itself.\n *\n * **`humanize()` remains, as the fallback, and that is what makes this safe.** The payload is cosmetic and never awaited: a form draws immediately, an offline page or a blocked request renders exactly what it rendered before labels existed, and a language with no translation for a given key falls back to English server-side. There is no state in which a missing label breaks a form. Operation SUMMARIES and field DESCRIPTIONS are deliberately NOT in that map: a description is developer-facing reference prose, most of it collapsed behind a disclosure, and the widget already states the same contract language-neutrally through its input type and bounds. GROUP names stay catalogued rather than fetched, because the spec has nine of them: see {@link RoxyEndpointForm.groupName}.\n */\n@customElement('roxy-endpoint-form')\nexport class RoxyEndpointForm extends RoxyLocalizedElement {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tdisclosureStyles,\n\t\tcss`form{gap:var(--roxy-space-md,1rem);background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);display:grid}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.fields{align-items:start;gap:var(--roxy-space-md,1rem);grid-template-columns:repeat(auto-fit,minmax(12rem,1fr));display:grid}.fields:empty{display:none}.person-group{background:var(--roxy-surface,#fff);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-md,1rem);gap:var(--roxy-space-md,1rem);min-inline-size:0;margin:0;display:grid}.person-group legend{padding:0 var(--roxy-space-xs,.25rem);font-weight:var(--roxy-weight-bold,600)}.field{gap:var(--roxy-space-xs,.25rem);flex-direction:column;min-width:0;display:flex}.tiles-field{grid-column:1/-1}label,.label{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-secondary,#475569)}.req{color:var(--roxy-danger-fg,#991b1b);margin-left:4px}input,select{box-sizing:border-box;width:100%;padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);font-size:var(--roxy-text-base,1rem);color:var(--roxy-fg,#0a0a0a);background:var(--roxy-bg,#fff);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);font-family:inherit}input:focus,select:focus{outline:2px solid var(--roxy-ring,#f59e0b66);outline-offset:2px;border-color:var(--roxy-accent-ink,#b45309)}.help{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem)}.help-details>summary{cursor:pointer;color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);align-items:center;gap:4px;display:flex}.help-details .help-lead{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.help-details .help-full{margin-top:var(--roxy-space-xs,.25rem);display:block}.location-block{gap:var(--roxy-space-xs,.25rem);grid-column:1/-1;display:grid}.tiles{gap:var(--roxy-space-sm,.5rem);grid-template-columns:repeat(auto-fit,minmax(5.5rem,1fr));display:grid}.tile{min-height:44px;padding:var(--roxy-space-sm,.5rem);background:var(--roxy-bg,#fff);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);color:var(--roxy-fg,#0a0a0a);font-family:inherit;font-size:var(--roxy-text-sm,.875rem);cursor:pointer;transition:border-color var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,ease), background-color var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,ease);flex-direction:column;justify-content:center;align-items:center;gap:2px;display:flex}.tile:hover{border-color:var(--roxy-accent,#f59e0b)}.tile[aria-checked=true]{border-color:var(--roxy-accent,#f59e0b);background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 12%, transparent);color:var(--roxy-accent-ink,#b45309);font-weight:var(--roxy-weight-bold,600)}.tile:focus-visible{outline:2px solid var(--roxy-ring,#f59e0b66);outline-offset:2px}.tile-glyph{font-size:1.35em;line-height:1}.toggle-row{align-items:center;gap:var(--roxy-space-sm,.5rem);display:flex}.toggle{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-full,9999px);background:var(--roxy-border,#e4e4e7);cursor:pointer;width:44px;height:26px;transition:background-color var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,ease);flex-shrink:0;padding:0;position:relative}.toggle[aria-checked=true]{background:var(--roxy-accent,#f59e0b);border-color:var(--roxy-accent,#f59e0b)}.toggle .knob{background:var(--roxy-bg,#fff);width:20px;height:20px;transition:transform var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,ease);border-radius:50%;position:absolute;top:2px;left:2px}.toggle[aria-checked=true] .knob{transform:translate(18px)}.toggle:focus-visible{outline:2px solid var(--roxy-ring,#f59e0b66);outline-offset:2px}.toggle-label{cursor:pointer}@media (prefers-reduced-motion:reduce){.tile,.toggle,.toggle .knob{transition:none}}.advanced{border-top:1px solid var(--roxy-border,#e4e4e7);padding-top:var(--roxy-space-md,1rem)}.advanced>summary{cursor:pointer;font-size:var(--roxy-text-sm,.875rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-fg,#0a0a0a);align-items:center;gap:6px;display:flex}.advanced[open]>summary{margin-bottom:var(--roxy-space-md,1rem)}.advanced .fields,.advanced .person-group{margin-top:var(--roxy-space-md,1rem)}.validation-error{gap:var(--roxy-space-xs,.25rem);padding:var(--roxy-space-md,1rem);background:color-mix(in srgb, var(--roxy-danger,#dc2626) 8%, transparent);border:1px solid var(--roxy-danger,#dc2626);border-radius:var(--roxy-radius-md,8px);color:var(--roxy-danger-fg,#991b1b);font-size:var(--roxy-text-sm,.875rem);display:grid}button.submit{background:var(--roxy-accent-ink,#b45309);color:var(--roxy-bg,#fff);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-sm,.5rem) var(--roxy-space-lg,1.5rem);font-size:var(--roxy-text-base,1rem);font-weight:var(--roxy-weight-bold,600);cursor:pointer;transition:transform var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,cubic-bezier(.4, 0, .2, 1));border:0;justify-self:start}button.submit:hover{transform:scale(1.02)}button.submit:focus-visible{outline:2px solid var(--roxy-ring,#f59e0b66);outline-offset:2px}@media (prefers-reduced-motion:reduce){button.submit{transition:none}}.spec-error{gap:var(--roxy-space-md,1rem);background:var(--roxy-surface,#fff);border:1px solid var(--roxy-danger,#dc2626);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);color:var(--roxy-danger-fg,#991b1b);font-size:var(--roxy-text-sm,.875rem);justify-items:start;display:grid}`,\n\t];\n\n\t@property({ type: String, attribute: 'data-endpoint' })\n\tendpoint = 'vedic-astrology/birth-chart';\n\n\t@property({ type: String })\n\tmethod: 'GET' | 'POST' = 'POST';\n\n\t/** Explicit OpenAPI spec URL. When set, the form fetches and digests that full spec (the demo path). When empty, it resolves a version-pinned slice first, then the production spec. */\n\t@property({ type: String, attribute: 'spec-url' })\n\tspecUrl = '';\n\n\t/** Override the submit-button label. Empty derives an outcome-first label from the endpoint. */\n\t@property({ type: String, attribute: 'submit-label' })\n\tsubmitLabel = '';\n\n\t/** Browser-safe publishable key, forwarded to the slotted city search so the natal or synastry form can geocode. */\n\t@property({ type: String, attribute: 'publishable-key' })\n\tpublishableKey?: string;\n\n\t/** Where the slotted city search sends its request, absolute or relative to the page. Set it when the page routes its API traffic through its own server, so the one request this form issues on its own reaches that server too. Unset, the search keeps its own default. */\n\t@property({ type: String, attribute: 'location-url' })\n\tlocationUrl?: string;\n\n\t/** Prefill values, keyed by field name (nested per group). Used by `remember` mode and to restore the previous submission. JS property only. */\n\t@property({ attribute: false })\n\tinitialValues?: Record<string, unknown>;\n\n\t@state()\n\tprivate fields: FieldDef[] = [];\n\n\t@state()\n\tprivate formTitle = '';\n\n\t@state()\n\tprivate hasLang = false;\n\n\t@state()\n\tprivate values: Record<string, unknown> = {};\n\n\t@state()\n\tprivate loaded = false;\n\n\t@state()\n\tprivate specError: string | null = null;\n\n\t@state()\n\tprivate validationErrors: string[] = [];\n\n\tconnectedCallback(): void {\n\t\tsuper.connectedCallback();\n\t\tvoid this.loadSchema();\n\t}\n\n\tprivate async loadSchema() {\n\t\tthis.specError = null;\n\t\ttry {\n\t\t\tthis.applyModel(await this.resolveModel());\n\t\t} catch (err) {\n\t\t\t// The banner reads in the page language; the event keeps the canonical\n\t\t\t// English, because a listener is code and a developer console is not a\n\t\t\t// localized surface.\n\t\t\tthis.specError = this.specErrorMessage(err);\n\t\t\tthis.loaded = true;\n\t\t\tthis.dispatchEvent(\n\t\t\t\tnew CustomEvent('roxy-spec-error', {\n\t\t\t\t\tdetail: {\n\t\t\t\t\t\turl: this.specUrl || PROD_SPEC_URL,\n\t\t\t\t\t\tmessage: err instanceof Error ? err.message : String(err),\n\t\t\t\t\t},\n\t\t\t\t\tbubbles: true,\n\t\t\t\t\tcomposed: true,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t}\n\n\t/** The failure sentence a visitor reads. Composed here rather than at the throw, so the shared spec-cache promise cannot freeze one form's language onto another. A message from anywhere else (a browser network error) is a wire fact and passes through untranslated. */\n\tprivate specErrorMessage(err: unknown): string {\n\t\tif (err instanceof SpecHttpError)\n\t\t\treturn this.t('HTTP error {{status}}', { status: err.status });\n\t\tif (err instanceof SpecMissingOperationError)\n\t\t\treturn this.t('Endpoint {{method}} {{path}} not found in OpenAPI spec', {\n\t\t\t\tmethod: err.method,\n\t\t\t\tpath: err.path,\n\t\t\t});\n\t\treturn err instanceof Error ? err.message : String(err);\n\t}\n\n\t/** Resolve the form model: explicit spec-url wins; else a version-pinned slice; else the production spec. */\n\tprivate async resolveModel(): Promise<FormModel> {\n\t\tif (this.specUrl) return this.modelFromSpec(this.specUrl);\n\t\tconst slice = await this.tryLoadSlice();\n\t\treturn slice ?? this.modelFromSpec(PROD_SPEC_URL);\n\t}\n\n\t/** Fetch and digest one operation from a full OpenAPI document. */\n\tprivate async modelFromSpec(url: string): Promise<FormModel> {\n\t\tconst spec = await loadSpec(url);\n\t\tconst path = `/${this.endpoint.replace(/^\\//, '')}`;\n\t\tconst op = spec.paths?.[path]?.[this.method.toLowerCase()] as\n\t\t\t| OperationSchema\n\t\t\t| undefined;\n\t\tif (!op) throw new SpecMissingOperationError(this.method, path);\n\t\treturn buildFormModel(op, spec.components?.schemas ?? {}, this.endpoint);\n\t}\n\n\t/** Try the small precomputed slice; return null on any miss so the caller falls back to the full spec. */\n\tprivate async tryLoadSlice(): Promise<FormModel | null> {\n\t\ttry {\n\t\t\tconst res = await fetch(\n\t\t\t\t`${SLICE_BASE}/${sliceFileName(this.method, this.endpoint)}`,\n\t\t\t);\n\t\t\tif (!res.ok) return null;\n\t\t\treturn (await res.json()) as FormModel;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate applyModel(model: FormModel) {\n\t\tthis.fields = model.fields;\n\t\tthis.formTitle = model.title;\n\t\tthis.hasLang = model.hasLang;\n\t\t// Pre-fill: schema defaults first, then any remembered/initial values (only\n\t\t// for fields this endpoint has) so a stale stored payload from another\n\t\t// endpoint cannot inject unknown keys. Remembered values are nested per\n\t\t// group, so read them by group.\n\t\tconst init: Record<string, unknown> = {};\n\t\tfor (const f of model.fields) {\n\t\t\tif (f.default !== undefined) init[f.key] = f.default;\n\t\t\tconst remembered = f.group\n\t\t\t\t? (\n\t\t\t\t\t\tthis.initialValues?.[f.group] as Record<string, unknown> | undefined\n\t\t\t\t\t)?.[f.name]\n\t\t\t\t: this.initialValues?.[f.name];\n\t\t\tif (remembered !== undefined) init[f.key] = remembered;\n\t\t}\n\t\tthis.values = init;\n\t\tthis.loaded = true;\n\t}\n\n\tprivate retryLoadSchema = () => {\n\t\tthis.loaded = false;\n\t\tthis.specError = null;\n\t\tvoid this.loadSchema();\n\t};\n\n\tprivate setValue(name: string, value: unknown) {\n\t\tthis.values = { ...this.values, [name]: value };\n\t}\n\n\t/** The field's render role: a hidden autogenerated seed, a hidden defaulted limit/offset, a suppressed location coordinate, or a normally rendered input. */\n\tprivate roleOf(\n\t\tf: FieldDef,\n\t): 'seed' | 'hidden-default' | 'location' | 'normal' {\n\t\tif (f.name === 'seed') return 'seed';\n\t\tif ((f.name === 'limit' || f.name === 'offset') && !f.required)\n\t\t\treturn 'hidden-default';\n\t\tif (\n\t\t\t(LOCATION_TRIO as readonly string[]).includes(f.name) &&\n\t\t\tthis.groupHasLocation(f.group)\n\t\t)\n\t\t\treturn 'location';\n\t\t// A flat timezone in a group with no coordinates of its own is still suppressed when a\n\t\t// location group has claimed it, otherwise the prefixed shape shows a raw decimal-hours\n\t\t// box beside two city searches that already know the answer.\n\t\tif (\n\t\t\tf.name === 'timezone' &&\n\t\t\tthis.locationGroups().some((g) => this.timezoneFieldFor(g)?.key === f.key)\n\t\t)\n\t\t\treturn 'location';\n\t\treturn 'normal';\n\t}\n\n\t/** True when the field renders its own input (as opposed to being hidden or replaced by the city search). */\n\tprivate isRendered(f: FieldDef): boolean {\n\t\treturn this.roleOf(f) === 'normal';\n\t}\n\n\t/** Ordered, de-duplicated group keys (undefined flat group plus each named object). */\n\tprivate groupKeys(): (string | undefined)[] {\n\t\tconst keys: (string | undefined)[] = [];\n\t\tfor (const f of this.fields)\n\t\t\tif (!keys.includes(f.group)) keys.push(f.group);\n\t\treturn keys;\n\t}\n\n\t/** True when the fields in `group` (or the flat top level) carry a latitude+longitude pair, so a location-search can autofill them. Timezone is NOT required here, see {@link LOCATION_PAIR}. */\n\tprivate groupHasLocation(group?: string): boolean {\n\t\tconst inGroup = this.fields.filter((f) => f.group === group);\n\t\treturn LOCATION_PAIR.every((n) => inGroup.some((f) => f.name === n));\n\t}\n\n\t/**\n\t * Location groups in field order. Order is load-bearing: when a request carries coordinate\n\t * groups but only ONE unprefixed top-level `timezone`, the FIRST group owns it, because that\n\t * timezone belongs to the primary moment (`generateRelocationChart` has one `timezone` and it is\n\t * the birth timezone). Without an owner both city boxes would write the same key and the second\n\t * pick would silently overwrite the first with the wrong offset.\n\t */\n\tprivate locationGroups(): (string | undefined)[] {\n\t\treturn this.groupKeys().filter((g) => this.groupHasLocation(g));\n\t}\n\n\t/**\n\t * The timezone field a group's city search should fill, or undefined when there is none.\n\t *\n\t * Prefers a timezone inside the group (the nested `person1`/`person2` shape has its own). Falls\n\t * back to an unowned flat `timezone` for the first location group only, which is what makes the\n\t * prefixed shape work without leaving a decimal-hours box for a visitor to guess at.\n\t */\n\tprivate timezoneFieldFor(group?: string): FieldDef | undefined {\n\t\tconst own = this.fields.find(\n\t\t\t(f) => f.group === group && f.name === 'timezone',\n\t\t);\n\t\tif (own) return own;\n\t\tif (this.locationGroups()[0] !== group) return undefined;\n\t\treturn this.fields.find(\n\t\t\t(f) => f.group === undefined && f.name === 'timezone',\n\t\t);\n\t}\n\n\t/**\n\t * True when the location block must show a required mark, i.e. ANY member of the trio is required. The block is a single city-search input that fills all three, so if even one is required (e.g. bodygraph requires `timezone` while `latitude`/`longitude` are optional) the input is required and `collectMissing` blocks submit without it. Requiring ALL three understated that: the asterisk went missing on a block the form still enforced, which reads as optional to a non-technical embedder.\n\t */\n\tprivate locationRequired(group?: string): boolean {\n\t\tconst inGroup = this.fields.filter((f) => f.group === group);\n\t\treturn LOCATION_TRIO.some((n) =>\n\t\t\tinGroup.some((f) => f.name === n && f.required),\n\t\t);\n\t}\n\n\t/** A named group is a required input when any of its leaf fields is required or it carries a location. */\n\tprivate groupIsRequired(group: string): boolean {\n\t\treturn (\n\t\t\tthis.groupHasLocation(group) ||\n\t\t\tthis.fields.some((f) => f.group === group && f.required)\n\t\t);\n\t}\n\n\t/**\n\t * The single visible required enum field, when the whole form reduces to exactly one: no location block, no named group, one required rendered enum. This is what turns the horoscope form into a tap-to-load sign grid; returns null otherwise.\n\t */\n\tprivate get singleEnumField(): FieldDef | null {\n\t\tif (this.groupKeys().some((g) => this.groupHasLocation(g))) return null;\n\t\tif (this.groupKeys().some((g) => g !== undefined)) return null;\n\t\tconst req = this.fields.filter((f) => f.required && this.isRendered(f));\n\t\tconst only = req[0];\n\t\tif (req.length !== 1 || !only) return null;\n\t\treturn only.kind === 'tiles' || only.kind === 'select' ? only : null;\n\t}\n\n\t/** The submit-button label. A caller-supplied one is theirs and is printed verbatim; the derived verb is ours, so it goes through the catalogue. {@link deriveSubmitLabel} returns the canonical English verb, which IS the catalogue key. */\n\tprivate effectiveSubmitLabel(): string {\n\t\treturn this.submitLabel || this.t(deriveSubmitLabel(this.endpoint));\n\t}\n\n\t/**\n\t * The value this form may put on `?lang=`, or undefined to leave the request on the API default.\n\t *\n\t * @remarks\n\t * NOT the display language, and the name says so: `effectiveLang()` on the base class is the full display tag every `t()` reads. `?lang=es-AR` is a 400 and `?lang=es` is a 200, and the language resolves from `<html lang>` rather than only from an attribute a developer typed, so the unfiltered tag reaching the query string would break every regional and every untranslated locale. {@link apiLang} does both narrowings; this method exists so the submit path reads as one call.\n\t */\n\tprivate requestLang(): string | undefined {\n\t\treturn apiLang(this);\n\t}\n\n\t/**\n\t * The words this form writes over a request field, and the options under it.\n\t *\n\t * @remarks\n\t * `humanize()` is the FALLBACK, not the default, and it stays because it has to: the labels\n\t * arrive over the network and a form must draw before they land, on an offline page, and when\n\t * the request fails. So the worst case is exactly what this form rendered before labels\n\t * existed, and the best case is a translated one, with no state in between where it breaks.\n\t *\n\t * Keyed by WIRE NAME rather than by the English text, because the English text is itself\n\t * computed here by `humanize()`; there is no constant to key on. Same reason the label map is\n\t * a separate key space from the chrome catalogue.\n\t */\n\tprivate fieldText(name: string): string {\n\t\treturn displayField(this.requestLang(), name);\n\t}\n\n\t/** The option text under one field, falling back the same way. */\n\tprivate optionText(field: string, value: string): string {\n\t\treturn displayOption(this.requestLang(), field, value);\n\t}\n\n\t/**\n\t * Location-select handler bound to a group: fills that group's coordinate keys plus whichever\n\t * timezone field it owns.\n\t *\n\t * Keys are LOOKED UP from the model rather than built as `group.name`. That assumption held only\n\t * while every group came from object nesting; a prefixed group stores under the original wire\n\t * name (`birthLatitude`), so constructing `birth.latitude` would write a key the request builder\n\t * never reads and the coordinates would silently stay empty.\n\t */\n\tprivate onLocationFor(group?: string) {\n\t\treturn (e: Event) => {\n\t\t\tconst detail = (e as CustomEvent).detail as {\n\t\t\t\tlatitude?: number;\n\t\t\t\tlongitude?: number;\n\t\t\t\ttimezone?: string;\n\t\t\t\tutcOffset?: number;\n\t\t\t};\n\t\t\tif (!detail) return;\n\t\t\tconst keyOf = (name: string) =>\n\t\t\t\tthis.fields.find((f) => f.group === group && f.name === name)?.key;\n\t\t\tconst next: Record<string, unknown> = { ...this.values };\n\t\t\tconst lat = keyOf('latitude');\n\t\t\tconst lon = keyOf('longitude');\n\t\t\tif (lat) next[lat] = detail.latitude;\n\t\t\tif (lon) next[lon] = detail.longitude;\n\t\t\tconst tz = this.timezoneFieldFor(group);\n\t\t\tif (tz) next[tz.key] = detail.timezone ?? detail.utcOffset;\n\t\t\tthis.values = next;\n\t\t};\n\t}\n\n\tprivate selectTile(f: FieldDef, value: string) {\n\t\tthis.setValue(f.key, value);\n\t\tif (this.singleEnumField?.key === f.key) this.submit();\n\t}\n\n\t/** Roving-tabindex arrow-key navigation for a tile radiogroup, modeled on the shared tablist pattern (selection follows focus). */\n\tprivate onTilesKeyDown(f: FieldDef) {\n\t\treturn (e: KeyboardEvent) => {\n\t\t\tconst opts = f.enum ?? [];\n\t\t\tif (opts.length === 0) return;\n\t\t\tconst cur = opts.indexOf(this.values[f.key] as string);\n\t\t\tlet next: number;\n\t\t\tif (e.key === 'Home') next = 0;\n\t\t\telse if (e.key === 'End') next = opts.length - 1;\n\t\t\telse if (e.key === 'ArrowRight' || e.key === 'ArrowDown')\n\t\t\t\tnext = cur === -1 ? 0 : (cur + 1) % opts.length;\n\t\t\telse if (e.key === 'ArrowLeft' || e.key === 'ArrowUp')\n\t\t\t\tnext =\n\t\t\t\t\tcur === -1 ? opts.length - 1 : (cur - 1 + opts.length) % opts.length;\n\t\t\telse return;\n\t\t\te.preventDefault();\n\t\t\tconst value = opts[next];\n\t\t\tif (value === undefined) return;\n\t\t\tthis.selectTile(f, value);\n\t\t\tconst root = e.currentTarget as HTMLElement;\n\t\t\trequestAnimationFrame(() =>\n\t\t\t\troot.querySelector<HTMLButtonElement>(`[data-tile='${next}']`)?.focus(),\n\t\t\t);\n\t\t};\n\t}\n\n\tprivate onSubmit = (e: Event) => {\n\t\te.preventDefault();\n\t\tthis.submit();\n\t};\n\n\t/** Validate, then emit `roxy-submit` with the reconstructed payload, the spec query keys, and whether the form was a single-enum sticky picker. */\n\tprivate submit() {\n\t\tconst missing = this.collectMissing();\n\t\tif (missing.keys.length > 0) {\n\t\t\tthis.validationErrors = missing.labels;\n\t\t\tthis.dispatchEvent(\n\t\t\t\tnew CustomEvent('roxy-validation-error', {\n\t\t\t\t\tdetail: { missing: missing.keys },\n\t\t\t\t\tbubbles: true,\n\t\t\t\t\tcomposed: true,\n\t\t\t\t}),\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tthis.validationErrors = [];\n\t\tconst out: Record<string, unknown> = {};\n\t\tfor (const f of this.fields) {\n\t\t\tlet v = this.values[f.key];\n\t\t\t// A hidden seed autogenerates per submit unless a value was remembered.\n\t\t\tif (f.name === 'seed' && (v === undefined || v === '')) v = randomSeed();\n\t\t\tif (v === undefined || v === '') continue;\n\t\t\tif (f.kind === 'array' && typeof v === 'string') v = parseArrayValue(v);\n\t\t\tif (f.group) {\n\t\t\t\tconst g = (out[f.group] as Record<string, unknown> | undefined) ?? {};\n\t\t\t\tg[f.name] = v;\n\t\t\t\tout[f.group] = g;\n\t\t\t} else {\n\t\t\t\tout[f.name] = v;\n\t\t\t}\n\t\t}\n\t\t// The spec knows which parameters belong in the query string; the listener\n\t\t// does not. Report them (including a site-owner lang) so a POST with a query\n\t\t// parameter reaches the API correctly instead of being dropped in the body.\n\t\tconst queryKeys = this.fields.filter((f) => f.inQuery).map((f) => f.name);\n\t\tconst lang = this.requestLang();\n\t\tif (this.hasLang && lang) {\n\t\t\tout.lang = lang;\n\t\t\tqueryKeys.push('lang');\n\t\t}\n\t\tthis.dispatchEvent(\n\t\t\tnew CustomEvent('roxy-submit', {\n\t\t\t\tdetail: {\n\t\t\t\t\tendpoint: this.endpoint,\n\t\t\t\t\tvalues: out,\n\t\t\t\t\tqueryKeys,\n\t\t\t\t\tsticky: this.singleEnumField != null,\n\t\t\t\t},\n\t\t\t\tbubbles: true,\n\t\t\t\tcomposed: true,\n\t\t\t}),\n\t\t);\n\t}\n\n\t/** Required fields left empty, with the location trio collapsed to one entry per group. Seed and hidden defaults never block. */\n\tprivate collectMissing(): { keys: string[]; labels: string[] } {\n\t\tconst keys: string[] = [];\n\t\tconst labels: string[] = [];\n\t\tconst locGroups = new Set<string | undefined>();\n\t\tfor (const f of this.fields) {\n\t\t\tif (!f.required) continue;\n\t\t\tconst isLocation = this.roleOf(f) === 'location';\n\t\t\tif (!this.isRendered(f) && !isLocation) continue;\n\t\t\tconst v = this.values[f.key];\n\t\t\tif (v !== undefined && v !== '') continue;\n\t\t\tkeys.push(f.key);\n\t\t\tif (isLocation) {\n\t\t\t\tlocGroups.add(f.group);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// The group half translates and the field half cannot, which is not a\n\t\t\t// half-measure: this names a block the visitor has to go back to, so the word\n\t\t\t// in the message has to be the word printed on the fieldset legend. A message\n\t\t\t// saying `Person 1` over a legend reading `Persona 1` points at nothing.\n\t\t\tlabels.push(\n\t\t\t\tf.group\n\t\t\t\t\t? `${this.groupName(f.group)} ${this.fieldText(f.name)}`\n\t\t\t\t\t: this.fieldText(f.name),\n\t\t\t);\n\t\t}\n\t\tfor (const g of locGroups) {\n\t\t\tlabels.push(this.locationLabel(g));\n\t\t}\n\t\treturn { keys, labels };\n\t}\n\n\tprivate reqMark(f: FieldDef) {\n\t\treturn f.required\n\t\t\t? html`<span class=\"req\" aria-hidden=\"true\">*</span>`\n\t\t\t: nothing;\n\t}\n\n\t/** A description under a field: rendered inline when short, collapsed behind a disclosure showing its first line when long. */\n\tprivate description(f: FieldDef) {\n\t\tif (!f.description) return nothing;\n\t\tif (f.description.length <= 120) {\n\t\t\treturn html`<small class=\"help\">${f.description}</small>`;\n\t\t}\n\t\tconst lead = f.description.split('. ')[0] ?? f.description;\n\t\treturn html`<details class=\"help-details\"><summary><span class=\"help-lead\">${lead}</span>${chevron()}</summary><small class=\"help help-full\">${f.description}</small></details>`;\n\t}\n\n\tprivate renderTiles(f: FieldDef) {\n\t\tconst labelId = `roxy-form-${f.key}-label`;\n\t\tconst zodiac = !!f.enum && isZodiacEnum(f.enum);\n\t\tconst opts = f.enum ?? [];\n\t\tconst cur = opts.indexOf(this.values[f.key] as string);\n\t\tconst active = cur === -1 ? 0 : cur;\n\t\treturn html`<div class=\"field tiles-field\"><span class=\"label\" id=\"${labelId}\">${this.fieldText(f.name)}${this.reqMark(f)}</span><div class=\"tiles\" role=\"radiogroup\" aria-labelledby=\"${labelId}\" @keydown=\"${this.onTilesKeyDown(f)}\">${opts.map((opt, i) => {\n\t\t\t\t\tconst selected = this.values[f.key] === opt;\n\t\t\t\t\tconst glyph = zodiac ? signGlyph(opt) : undefined;\n\t\t\t\t\treturn html`<button type=\"button\" class=\"tile\" role=\"radio\" data-tile=\"${i}\" aria-checked=\"${selected ? 'true' : 'false'}\" tabindex=\"${i === active ? '0' : '-1'}\" @click=\"${() => this.selectTile(f, opt)}\">${\n\t\t\t\t\t\t\tglyph\n\t\t\t\t\t\t\t\t? html`<span class=\"tile-glyph\" aria-hidden=\"true\">${glyph}</span>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t} <span class=\"tile-label\">${this.optionText(f.name, opt)}</span></button>`;\n\t\t\t\t})}</div>${this.description(f)}</div>`;\n\t}\n\n\tprivate renderSelect(f: FieldDef) {\n\t\tconst id = `roxy-form-${f.key}`;\n\t\treturn html`<div part=\"field\" class=\"field\"><label for=\"${id}\">${this.fieldText(f.name)}${this.reqMark(f)}</label> <select id=\"${id}\" ?required=\"${f.required}\" @change=\"${(e: Event) =>\n\t\t\t\t\tthis.setValue(f.key, (e.target as HTMLSelectElement).value)}\"><option value=\"\">${this.t('Choose')}</option>${(f.enum ?? []).map(\n\t\t\t\t\t(\n\t\t\t\t\t\topt,\n\t\t\t\t\t) => html`<option value=\"${opt}\" ?selected=\"${this.values[f.key] === opt}\">${this.optionText(f.name, opt)}</option>`,\n\t\t\t\t)}</select> ${this.description(f)}</div>`;\n\t}\n\n\tprivate renderToggle(f: FieldDef) {\n\t\tconst id = `roxy-form-${f.key}`;\n\t\tconst checked = this.values[f.key] === true;\n\t\treturn html`<div part=\"field\" class=\"field\"><div class=\"toggle-row\"><button type=\"button\" id=\"${id}\" class=\"toggle\" role=\"switch\" aria-checked=\"${checked ? 'true' : 'false'}\" @click=\"${() => this.setValue(f.key, !checked)}\"><span class=\"knob\"></span></button> <label class=\"toggle-label\" for=\"${id}\">${this.fieldText(f.name)}${this.reqMark(f)}</label></div>${this.description(f)}</div>`;\n\t}\n\n\tprivate renderInput(f: FieldDef) {\n\t\tconst id = `roxy-form-${f.key}`;\n\t\tconst type =\n\t\t\tf.kind === 'datetime'\n\t\t\t\t? 'datetime-local'\n\t\t\t\t: f.kind === 'number'\n\t\t\t\t\t? 'number'\n\t\t\t\t\t: f.kind;\n\t\tconst placeholder =\n\t\t\tf.kind === 'array'\n\t\t\t\t? this.t('Comma separated')\n\t\t\t\t: f.example != null\n\t\t\t\t\t? String(f.example)\n\t\t\t\t\t: '';\n\t\treturn html`<div part=\"field\" class=\"field\"><label for=\"${id}\">${this.fieldText(f.name)}${this.reqMark(f)}</label> <input id=\"${id}\" type=\"${f.kind === 'array' ? 'text' : type}\" ?required=\"${f.required}\" min=\"${ifDefined(f.min)}\" max=\"${ifDefined(f.max)}\" step=\"${f.kind === 'number' ? 'any' : nothing}\" placeholder=\"${placeholder || nothing}\" .value=\"${(this.values[f.key] ?? '') as string}\" @input=\"${(e: Event) =>\n\t\t\t\t\tthis.setValue(\n\t\t\t\t\t\tf.key,\n\t\t\t\t\t\tthis.coerce(f.kind, (e.target as HTMLInputElement).value),\n\t\t\t\t\t)}\"> ${this.description(f)}</div>`;\n\t}\n\n\tprivate renderField(f: FieldDef) {\n\t\tswitch (f.kind) {\n\t\t\tcase 'tiles':\n\t\t\t\treturn this.renderTiles(f);\n\t\t\tcase 'select':\n\t\t\t\treturn this.renderSelect(f);\n\t\t\tcase 'toggle':\n\t\t\t\treturn this.renderToggle(f);\n\t\t\tdefault:\n\t\t\t\treturn this.renderInput(f);\n\t\t}\n\t}\n\n\t/**\n\t * The fields this group's city search actually fills, named in the help text.\n\t *\n\t * Not the hardcoded \"latitude, longitude, timezone\" it used to say: a group does not always own a\n\t * timezone. `generateRelocationChart` has one top-level `timezone` that belongs to the birth\n\t * moment, so the relocation block fills coordinates only, and promising a timezone there would be\n\t * a visible lie on the one form that made this method necessary.\n\t */\n\tprivate locationFillList(group?: string): string {\n\t\tconst names = [...LOCATION_PAIR] as string[];\n\t\tif (this.timezoneFieldFor(group)) names.push('timezone');\n\t\treturn names.join(', ');\n\t}\n\n\t/**\n\t * The name of one request group, translated, falling back to the humanized English.\n\t *\n\t * @remarks\n\t * A group name is the one derived label that is also ENUMERABLE. A field label is `humanize()` over one of 909 spec field names, but a group is `humanize()` over the names an object-valued property or a coordinate prefix can take, and the whole spec has nine across 176 operations. So each is a catalogue entry, and the English token that used to sit inside translated prose (`Local de Natal Chart`, `Место (Birth Data)`) is gone. A tenth group appearing in the spec still renders: `t()` returns its source string on a miss, so it degrades to the humanized English rather than to a blank or a key.\n\t *\n\t * **The lookup folds case, and that is what makes `natalChart` work.** `humanize` produces `Natal Chart` while the catalogue carries `Natal chart` for the card heading, and {@link lookupKey} folds both to one key on write and on read, so this reuses the shipped translation. Adding the capitalized twin instead would SILENTLY OVERWRITE that heading in every locale rather than reading as a duplicate, which is why `i18n/chrome-strings.ts` carries no entry for it.\n\t */\n\tprivate groupName(group: string): string {\n\t\treturn this.t(humanize(group));\n\t}\n\n\t/**\n\t * The name of one location block, and of the same block in a validation message, which is why it is a method rather than two literals.\n\t *\n\t * The placeholder carries the group rather than the form concatenating a name in front of a translated noun, so a translator owns the word order: `Person 1 location` is English syntax, and Turkish suffixes the head noun, Hindi takes a genitive, Russian a parenthetical and German a colon. The four shapes are deliberately different and normalizing them breaks three languages.\n\t */\n\tprivate locationLabel(group?: string): string {\n\t\treturn group\n\t\t\t? this.t('{{group}} location', { group: this.groupName(group) })\n\t\t\t: this.t('Birth location');\n\t}\n\n\t/**\n\t * One city search standing in for a group's raw coordinates.\n\t *\n\t * `lang` is forwarded EXPLICITLY and that line is load-bearing: the city search lives inside this shadow root, and the `closest('[lang]')` link of the resolution chain stops at a shadow boundary, so without it the dropdown renders its own empty state and its refusal message in English on a fully translated page. Every composing component carries the same obligation.\n\t *\n\t * `endpoint` is forwarded for the same structural reason as the key. This search is the one request the form issues on its own, it sits inside this shadow root where a host page cannot reach it, and it is the only part of a birth-data form that talks to the API before submit. So a page that routes its API traffic through its own server states that route once, on the form, and it lands here. An unset {@link RoxyEndpointForm.locationUrl} omits the attribute entirely, which leaves the search on its own default.\n\t */\n\tprivate locationBlock(group?: string) {\n\t\treturn html`<div class=\"location-block\"><label>${this.locationLabel(group)}${\n\t\t\t\t\tthis.locationRequired(group)\n\t\t\t\t\t\t? html`<span class=\"req\" aria-hidden=\"true\">*</span>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</label><roxy-location-search endpoint=\"${ifDefined(this.locationUrl || undefined)}\" publishable-key=\"${ifDefined(this.publishableKey)}\" lang=\"${ifDefined(this.effectiveLang())}\" @roxy-location-select=\"${this.onLocationFor(group)}\" placeholder=\"${\n\t\t\t\t\tgroup\n\t\t\t\t\t\t? this.t('{{group}} city', { group: this.groupName(group) })\n\t\t\t\t\t\t: this.t('City of birth')\n\t\t\t\t}\"></roxy-location-search><small class=\"help\">${this.t('Fills {{fields}}. Pick a city to autofill.', {\n\t\t\t\t\tfields: this.locationFillList(group),\n\t\t\t\t})}</small></div>`;\n\t}\n\n\tprivate groupCard(group: string) {\n\t\tconst fields = this.fields.filter(\n\t\t\t(f) => f.group === group && this.isRendered(f),\n\t\t);\n\t\treturn html`<fieldset part=\"group\" class=\"person-group\"><legend>${this.groupName(group)}</legend>${this.groupHasLocation(group) ? this.locationBlock(group) : nothing}<div class=\"fields\">${fields.map((f) => this.renderField(f))}</div></fieldset>`;\n\t}\n\n\trender() {\n\t\tif (!this.loaded) {\n\t\t\treturn html`<form><div class=\"roxy-skeleton\" style=\"height:8rem\"></div></form>`;\n\t\t}\n\t\tif (this.specError) {\n\t\t\treturn html`<div class=\"spec-error\" role=\"alert\">${this.t('Schema load failed: {{message}}', { message: this.specError })} <button type=\"button\" class=\"submit\" @click=\"${this.retryLoadSchema}\">${this.t('Retry')}</button></div>`;\n\t\t}\n\n\t\tconst flat = this.fields.filter((f) => !f.group && this.isRendered(f));\n\t\tconst flatReq = flat.filter((f) => f.required);\n\t\tconst flatOpt = flat.filter((f) => !f.required);\n\t\tconst named = this.groupKeys().filter((g): g is string => g !== undefined);\n\t\tconst reqGroups = named.filter((g) => this.groupIsRequired(g));\n\t\tconst optGroups = named.filter((g) => !this.groupIsRequired(g));\n\t\tconst hasAdvanced = flatOpt.length > 0 || optGroups.length > 0;\n\n\t\treturn html`<form @submit=\"${this.onSubmit}\"><h2 part=\"title\" class=\"title\">${this.formTitle}</h2>${\n\t\t\t\tthis.validationErrors.length > 0\n\t\t\t\t\t? html`<div part=\"validation-error\" class=\"validation-error\" role=\"alert\"><strong>${this.t('Please complete:')}</strong> ${this.validationErrors.join(', ')}</div>`\n\t\t\t\t\t: nothing\n\t\t\t}<div class=\"fields\">${flatReq.map((f) => this.renderField(f))}</div>${this.groupHasLocation(undefined) ? this.locationBlock(undefined) : nothing} ${reqGroups.map((g) => this.groupCard(g))} ${\n\t\t\t\thasAdvanced\n\t\t\t\t\t? html`<details part=\"advanced\" class=\"advanced\"><summary>${this.t('Advanced')}${chevron()}</summary><div class=\"fields\">${flatOpt.map((f) => this.renderField(f))}</div>${optGroups.map((g) => this.groupCard(g))}</details>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\tthis.singleEnumField\n\t\t\t\t\t? nothing\n\t\t\t\t\t: html`<button part=\"submit\" class=\"submit\" type=\"submit\">${this.effectiveSubmitLabel()}</button>`\n\t\t\t}</form>`;\n\t}\n\n\tprivate coerce(kind: string, v: string): unknown {\n\t\tif (v === '') return undefined;\n\t\tif (kind === 'number') {\n\t\t\tconst n = Number(v);\n\t\t\treturn Number.isFinite(n) ? n : undefined;\n\t\t}\n\t\treturn v;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-endpoint-form': RoxyEndpointForm;\n\t}\n}\n", "/**\n * Pure, framework-free schema layer shared by the self-fetch form. It turns one OpenAPI operation into a digested list of {@link FieldDef}s whose {@link InputKind} decides which input widget renders, with zero Lit or DOM dependency so the build-time slice generator ({@link ../../../scripts/build-schemas.ts}) and the taxonomy-coverage test consume the exact same classification the browser does.\n *\n * @remarks\n * The registry is by SHAPE, never by name: {@link classifyInput} inspects `enum` / `type` / `format` only, so a new API parameter gets a working widget with no code change, and the taxonomy gate fails loudly (via a `null` return) the day a genuinely new shape appears. Name-driven behaviour (suppressing `lang`, hiding `seed` / `limit` / `offset`, replacing the latitude+longitude+timezone trio with a city search) is a separate render-time concern layered on top by the form, never encoded here.\n */\n\nimport { SIGNS_ORDER } from '../tokens/index.js';\nimport { humanize } from './string.js';\n\n/** A JSON `$ref` node, before resolution against `components.schemas`. */\nexport interface OpenApiSchemaRef {\n\t$ref?: string;\n}\n\n/** The subset of an OpenAPI 3 schema object the form reads. */\nexport interface OpenApiSchema extends OpenApiSchemaRef {\n\t/** A plain name in 3.0, or an ARRAY in 3.1 where nullability is spelled `['number', 'null']`. Read it through {@link scalarType}, never compare it directly. */\n\ttype?: string | string[];\n\tformat?: string;\n\tdescription?: string;\n\tenum?: string[];\n\tdefault?: unknown;\n\tminimum?: number;\n\tmaximum?: number;\n\tproperties?: Record<string, OpenApiSchema>;\n\trequired?: string[];\n\titems?: OpenApiSchema;\n\texample?: unknown;\n\tanyOf?: OpenApiSchema[];\n\toneOf?: OpenApiSchema[];\n}\n\n/** One operation's request shape, as read from `spec.paths[path][method]`. */\nexport interface OperationSchema {\n\tsummary?: string;\n\trequestBody?: {\n\t\tcontent?: Record<string, { schema?: OpenApiSchema | OpenApiSchemaRef }>;\n\t};\n\tparameters?: Array<{\n\t\tname: string;\n\t\tin: string;\n\t\trequired?: boolean;\n\t\tschema?: OpenApiSchema;\n\t}>;\n}\n\n/** The minimal OpenAPI document surface the form introspects. */\nexport interface OpenApiDoc {\n\tpaths?: Record<string, Record<string, unknown>>;\n\tcomponents?: { schemas?: Record<string, OpenApiSchema> };\n}\n\n/** The fully typed spec document the build-time scripts read from the committed `specs/openapi.json`: every path item value is an {@link OperationSchema}. The runtime form keeps the looser {@link OpenApiDoc} because a live spec fetch is untrusted input it narrows per operation. */\nexport interface SpecDoc {\n\tpaths: Record<string, Record<string, OperationSchema>>;\n\tcomponents?: { schemas?: Record<string, OpenApiSchema> };\n}\n\n/**\n * The widget kind a parameter resolves to. This is the closed set the taxonomy gate enforces: every request parameter in the spec MUST classify to one of these, or {@link classifyInput} returns `null` and the gate fails, forcing a conscious decision when the API grows a new shape.\n */\nexport type InputKind =\n\t| 'tiles'\n\t| 'select'\n\t| 'toggle'\n\t| 'date'\n\t| 'time'\n\t| 'datetime'\n\t| 'number'\n\t| 'text'\n\t| 'array';\n\n/** A single input the form renders, digested from one schema property or parameter. */\nexport interface FieldDef {\n\t/** Unique storage + input key: the property name, or \"group.name\" for a nested object field. */\n\tkey: string;\n\t/** Schema property name (the label source), e.g. \"date\". */\n\tname: string;\n\t/** Parent object key for a nested schema (person1/person2); undefined for a flat field. */\n\tgroup?: string;\n\t/** True when the spec declares the field as `in: query`, so it belongs in the query string even on a POST. */\n\tinQuery?: boolean;\n\t/** The resolved widget kind. */\n\tkind: InputKind;\n\trequired: boolean;\n\tdescription?: string;\n\tenum?: string[];\n\tmin?: number;\n\tmax?: number;\n\tdefault?: unknown;\n\t/** Spec `example`, used as the placeholder for a text input. */\n\texample?: unknown;\n}\n\n/** The digested form model for one operation. The build-time slice is exactly this shape. */\nexport interface FormModel {\n\t/** Concise heading derived from the operation summary, falling back to the path. */\n\ttitle: string;\n\t/** Every rendered field. The `lang` query parameter is intentionally excluded (routed via the element `lang` attribute instead). */\n\tfields: FieldDef[];\n\t/** True when the operation carries a `lang` query parameter, so the form knows to route an effective language to the query string on submit. */\n\thasLang: boolean;\n}\n\n/** At most this many enum options render as a tile/chip picker; above it a filterable select is used instead. */\nexport const TILE_MAX = 12;\n\n/** The latitude+longitude+timezone trio the form suppresses in favour of a city search. Centralised so the render path and tests agree. */\nexport const LOCATION_TRIO = ['latitude', 'longitude', 'timezone'] as const;\n\n/**\n * The coordinate pair that DEFINES a location group. `timezone` is deliberately absent.\n *\n * @remarks\n * A group qualifies for a city search on latitude+longitude alone, because a timezone is not always\n * part of the group that owns the coordinates: `generateRelocationChart` carries two coordinate\n * pairs and exactly ONE top-level `timezone`, which is the BIRTH timezone (relocating does not move\n * the birth moment), so the relocation pair correctly has no timezone of its own. Requiring all\n * three would leave that operation rendering raw number inputs, which is the bug this exists to\n * prevent.\n */\nexport const LOCATION_PAIR = ['latitude', 'longitude'] as const;\n\n/**\n * Split a flat coordinate property into its group prefix and canonical leaf name, or `null` when the\n * name is not a prefixed coordinate.\n *\n * @remarks\n * **Two shapes carry two locations in one request and this handles the second one.** Most\n * multi-location operations nest per person (`person1`/`person2`, `personA`/`personB`), so object\n * nesting alone already groups them. `generateRelocationChart` is the only operation in the spec\n * that instead puts both pairs at the TOP level and distinguishes them by a name prefix\n * (`birthLatitude` / `relocationLatitude`). Keying the grouping off the prefix as well as off the\n * nesting means both shapes converge on the same group machinery, and a future `partnerLatitude`\n * needs no code change.\n *\n * Matching is deliberately restricted to the coordinate pair. A prefix is NOT harvested from any\n * other field name, so `birthDate` stays an ordinary field in the flat group and does not invent a\n * phantom `birth` group with a lone date in it.\n */\nexport function splitCoordinateName(\n\tname: string,\n): { group: string; leaf: (typeof LOCATION_PAIR)[number] } | null {\n\tconst m = name.match(/^(.+?)(Latitude|Longitude)$/);\n\tif (!m) return null;\n\treturn {\n\t\tgroup: m[1],\n\t\tleaf: m[2].toLowerCase() as (typeof LOCATION_PAIR)[number],\n\t};\n}\n\n/** Canonical lowercase zodiac set, derived from {@link SIGNS_ORDER} so sign detection and the glyph map can never disagree. */\nconst ZODIAC_LOWER = SIGNS_ORDER.map((s) => s.toLowerCase());\n\n/** Resolve a `$ref` (one hop is all the spec uses) against the schema map; pass non-refs through. */\nexport function resolveSchema(\n\tschema: OpenApiSchema | OpenApiSchemaRef | undefined,\n\tall: Record<string, OpenApiSchema>,\n): OpenApiSchema | undefined {\n\tif (!schema) return undefined;\n\tif ('$ref' in schema && schema.$ref) {\n\t\tconst name = schema.$ref.split('/').pop();\n\t\treturn name ? all[name] : undefined;\n\t}\n\treturn schema as OpenApiSchema;\n}\n\n/**\n * Collapse an `anyOf`/`oneOf` union to the single member that decides the widget: an enum member wins (so `houseSystem` as enum-or-string renders as a picker), then any concrete-typed member (so timezone as number-or-string resolves to a real kind), else the first member. Non-union schemas pass through untouched.\n */\nfunction representative(s: OpenApiSchema): OpenApiSchema {\n\tconst union = s.anyOf ?? s.oneOf;\n\tif (!union || union.length === 0) return s;\n\treturn (\n\t\tunion.find((m) => Array.isArray(m.enum)) ??\n\t\tunion.find((m) => scalarType(m) && scalarType(m) !== 'null') ??\n\t\tunion[0] ??\n\t\ts\n\t);\n}\n\n/**\n * Map one resolved schema to its {@link InputKind}, or `null` when the shape is genuinely unhandled. The form treats `null` as a plain text fallback (never crashes); the taxonomy gate treats it as a failure (forces a decision). Object schemas are never passed here: an object with `properties` expands into a group of leaf fields upstream, so it has no leaf kind of its own.\n */\n/**\n * The single scalar type name a schema resolves to, across both OpenAPI spellings.\n *\n * @remarks\n * OpenAPI 3.1 expresses a nullable scalar as `type: ['number', 'null']` where 3.0 wrote `type: 'number', nullable: true`. Every direct `type === 'number'` comparison silently stops matching the day the served document switches, and the field then classifies as UNHANDLED rather than as a number, which is how a whole form degrades to text boxes without one test noticing. The `null` member carries no input information, so the remaining single member IS the type.\n */\nexport function scalarType(s: OpenApiSchema): string | undefined {\n\tif (typeof s.type === 'string') return s.type;\n\tif (!Array.isArray(s.type)) return undefined;\n\tconst real = s.type.filter((t) => t !== 'null');\n\treturn real.length === 1 ? real[0] : undefined;\n}\n\nexport function classifyInput(schema: OpenApiSchema): InputKind | null {\n\tconst s = representative(schema);\n\tconst type = scalarType(s);\n\tif (Array.isArray(s.enum))\n\t\treturn s.enum.length <= TILE_MAX ? 'tiles' : 'select';\n\tif (type === 'boolean') return 'toggle';\n\tif (s.format === 'date') return 'date';\n\tif (s.format === 'time') return 'time';\n\tif (s.format === 'date-time') return 'datetime';\n\tif (type === 'integer' || type === 'number') return 'number';\n\tif (type === 'string') return 'text';\n\tif (type === 'array') return 'array';\n\treturn null;\n}\n\n/** True when an enum's values are exactly the twelve zodiac signs (case-insensitive), so the tiles can carry sign glyphs. */\nexport function isZodiacEnum(values: readonly string[]): boolean {\n\tif (values.length !== 12) return false;\n\tconst lower = new Set(values.map((v) => v.toLowerCase()));\n\treturn ZODIAC_LOWER.every((s) => lower.has(s));\n}\n\n/** One digested leaf field from a resolved schema. Shared by the flat and grouped construction paths so params and body props carry identical metadata (min/max included). */\nfunction toField(\n\tname: string,\n\tresolved: OpenApiSchema,\n\topts: { key: string; group?: string; required: boolean; inQuery?: boolean },\n): FieldDef {\n\tconst rep = representative(resolved);\n\treturn {\n\t\tkey: opts.key,\n\t\tname,\n\t\tgroup: opts.group,\n\t\tinQuery: opts.inQuery,\n\t\tkind: classifyInput(resolved) ?? 'text',\n\t\trequired: opts.required,\n\t\tdescription: rep.description ?? resolved.description,\n\t\tenum: rep.enum,\n\t\tmin: rep.minimum,\n\t\tmax: rep.maximum,\n\t\tdefault: resolved.default ?? rep.default,\n\t\texample: resolved.example ?? rep.example,\n\t};\n}\n\n/**\n * Digest one operation into the {@link FormModel} the form renders. This is the single construction path: body properties (nested objects expand to `group.sub` keys) and path+query parameters flow through {@link toField} identically, so a query integer keeps its min/max the same as a body integer. The `lang` query parameter is dropped from `fields` and surfaced as {@link FormModel.hasLang} instead.\n */\nexport function buildFormModel(\n\top: OperationSchema,\n\tschemas: Record<string, OpenApiSchema>,\n\tendpoint: string,\n): FormModel {\n\tconst fields: FieldDef[] = [];\n\tlet hasLang = false;\n\n\tconst bodyRef = op.requestBody?.content?.['application/json']?.schema;\n\tconst bodySchema = resolveSchema(bodyRef, schemas);\n\tif (bodySchema?.properties) {\n\t\tconst required = new Set(bodySchema.required ?? []);\n\t\tfor (const [name, sub] of Object.entries(bodySchema.properties)) {\n\t\t\tconst resolved = resolveSchema(sub, schemas) ?? {};\n\t\t\tif (resolved.type === 'object' && resolved.properties) {\n\t\t\t\tconst subRequired = new Set(resolved.required ?? []);\n\t\t\t\tfor (const [subName, subSchema] of Object.entries(\n\t\t\t\t\tresolved.properties,\n\t\t\t\t)) {\n\t\t\t\t\tconst r = resolveSchema(subSchema, schemas) ?? {};\n\t\t\t\t\tfields.push(\n\t\t\t\t\t\ttoField(subName, r, {\n\t\t\t\t\t\t\tkey: `${name}.${subName}`,\n\t\t\t\t\t\t\tgroup: name,\n\t\t\t\t\t\t\trequired: required.has(name) && subRequired.has(subName),\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// A prefixed coordinate joins a group named after its prefix and reports the\n\t\t\t\t// canonical leaf name, so the trio logic and the city search match it unchanged.\n\t\t\t\t// `key` stays the ORIGINAL property name because it is the storage and wire\n\t\t\t\t// identity: rewriting it to `birth.latitude` would serialise a body the API\n\t\t\t\t// rejects.\n\t\t\t\tconst coord = splitCoordinateName(name);\n\t\t\t\tfields.push(\n\t\t\t\t\tcoord\n\t\t\t\t\t\t? toField(coord.leaf, resolved, {\n\t\t\t\t\t\t\t\tkey: name,\n\t\t\t\t\t\t\t\tgroup: coord.group,\n\t\t\t\t\t\t\t\trequired: required.has(name),\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t: toField(name, resolved, {\n\t\t\t\t\t\t\t\tkey: name,\n\t\t\t\t\t\t\t\trequired: required.has(name),\n\t\t\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (const param of op.parameters ?? []) {\n\t\tif (param.in !== 'path' && param.in !== 'query') continue;\n\t\t// lang is site-owner chrome, not a visitor field: route it through the\n\t\t// element `lang` attribute, never render it in the form.\n\t\tif (param.name === 'lang' && param.in === 'query') {\n\t\t\thasLang = true;\n\t\t\tcontinue;\n\t\t}\n\t\tconst resolved = resolveSchema(param.schema, schemas) ?? {};\n\t\tfields.push(\n\t\t\ttoField(param.name, resolved, {\n\t\t\t\tkey: param.name,\n\t\t\t\trequired: !!param.required,\n\t\t\t\tinQuery: param.in === 'query',\n\t\t\t}),\n\t\t);\n\t}\n\n\treturn { title: deriveTitle(op.summary, endpoint), fields, hasLang };\n}\n\n/**\n * Concise form heading. Prefers the operation summary (spec-authored), taking the clause before the first \" - \" so \"Daily horoscope by zodiac sign - Transit-based forecast\" reads as \"Daily horoscope by zodiac sign\", and falls back to the humanized last path segment.\n */\nexport function deriveTitle(\n\tsummary: string | undefined,\n\tendpoint: string,\n): string {\n\tconst lead = summary?.split(' - ')[0]?.trim();\n\tif (lead) return lead;\n\treturn humanize(endpoint.split('/').pop() ?? '');\n}\n\n/**\n * Outcome-first submit-button label keyed off the endpoint intent. Chart and reading endpoints \"Generate\", divination endpoints \"Cast\", comparison endpoints \"Compare\", and lookup/reading GETs \"Get reading\". A generic verb beats a bare \"Submit\" on a widget a visitor never set up.\n *\n * Returns the CANONICAL English verb, which is also its catalogue key: the form translates the result rather than this function doing it, because this module is request-context-free and has no element to resolve a page language from. All four verbs live in `i18n/chrome-strings.ts`, and `tests/i18n.test.ts` runs this function over every operation in the committed spec so a fifth verb cannot be added without a catalogue entry.\n */\nexport function deriveSubmitLabel(endpoint: string): string {\n\tconst e = endpoint.toLowerCase();\n\tif (/compat|synastry|guna|connection|penta|composite/.test(e))\n\t\treturn 'Compare';\n\tif (/\\bcast\\b|\\/draw|iching|hexagram|tarot/.test(e)) return 'Cast';\n\tif (/horoscope|dream|angel|nakshatra|crystal|meaning|reference/.test(e))\n\t\treturn 'Get reading';\n\treturn 'Generate';\n}\n\n/** Deterministic slice filename for one operation, shared by the generator and the runtime loader so both address the same artifact. */\nexport function sliceFileName(method: string, endpoint: string): string {\n\tconst path = endpoint\n\t\t.replace(/^\\//, '')\n\t\t.replace(/[{}]/g, '')\n\t\t.replace(/\\//g, '-');\n\treturn `${method.toLowerCase()}--${path}.json`;\n}\n", "// Generated by scripts/sync-version.ts. Do not edit.\nexport const ROXY_UI_VERSION = '0.33.0';\n", "import { css, html, nothing } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { planetGlyph, SIGNS_ORDER, signGlyph } from '../tokens/index.js';\nimport type {\n\tGetMonthlyEphemerisResponse,\n\tGetMonthlyTropicalEphemerisResponse,\n} from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { splitDegreeInSign } from '../utils/degree.js';\nimport {\n\tformatMonthDay,\n\tformatMonthYear,\n\tformatWeekdayDay,\n} from '../utils/format.js';\nimport { display } from '../utils/localized.js';\n\n/**\n * The sample instant every position in both responses is calculated for, printed as a literal rather than a translated phrase.\n *\n * @remarks\n * An ephemeris is unusable without its time basis, which is why a published one always declares it in the header, as a clock literal rather than a sentence. A clock literal is the same in every language, so this needs no catalogue entry and cannot drift from one. Both endpoint descriptions state noon UTC and the returned longitudes agree with that basis.\n */\nconst SAMPLE_INSTANT = '12:00 UTC';\n\n/**\n * The two monthly-ephemeris responses this table renders. Structurally\n * identical: the Western one carries fourteen tropical bodies, the Vedic one\n * the nine sidereal Navagraha, and neither says which frame it is in, so the\n * component never claims one (see the class docblock).\n */\ntype EphemerisResponse =\n\t| GetMonthlyTropicalEphemerisResponse\n\t| GetMonthlyEphemerisResponse;\n\n/** One body on one day, straight off the response. */\ntype Position = EphemerisResponse['days'][number]['positions'][number];\n\n/** A contiguous stretch of sampled days on which a body is retrograde, clipped to the month. */\ninterface RetrogradeRun {\n\tstart: string;\n\tend: string;\n}\n\n/** A body crossing into a new sign, dated by the first day the sample shows it there. */\ninterface Ingress {\n\tdate: string;\n\t/** Canonical English sign: the glyph lookup keys on this. */\n\tsign: string;\n\t/** The sign name a reader sees. */\n\tsignLabel: string;\n}\n\n/** One body read along the whole month: where it began, where it ended, and everything that changed in between. */\ninterface BodyTrack {\n\t/** Canonical English body name. Every glyph lookup and every column key uses this. */\n\tname: string;\n\t/** The body name a reader sees. */\n\tlabel: string;\n\tfirst: Position;\n\tlast: Position;\n\tingresses: Ingress[];\n\tretrogrades: RetrogradeRun[];\n\t/** Dates on which this body changed sign, so the daily grid can mark the same cells the chips name. */\n\tingressDates: Set<string>;\n\t/** Dates on which this body was seen to change direction, which a printed ephemeris marks in the column rather than only listing below it. */\n\tstationDates: Set<string>;\n}\n\n/** One day of the grid: the date plus one cell per body, aligned to {@link ViewModel.bodies}. */\ninterface DayRow {\n\tdate: string;\n\tcells: Array<Position | undefined>;\n}\n\n/** One line of the sign key: the glyph the table prints and the name it stands for. */\ninterface SignKeyEntry {\n\t/** Canonical English sign, the glyph lookup key. */\n\tsign: string;\n\t/** The sign name a reader sees, in the response language. */\n\tlabel: string;\n\tglyph: string;\n}\n\ninterface ViewModel {\n\tyear: number;\n\tmonth: number;\n\tbodies: BodyTrack[];\n\trows: DayRow[];\n\tsigns: SignKeyEntry[];\n}\n\n/**\n * Monthly ephemeris: every body read across a whole month, as the reference\n * table a practitioner works from plus the two things a month view exists to\n * answer.\n *\n * @remarks\n * Renders `POST /astrology/planets/monthly` (fourteen tropical Western bodies)\n * and `POST /vedic-astrology/planetary-positions/monthly` (the nine sidereal\n * Navagraha). One response shape, so one component; nothing selects between\n * them, because the payload is the only honest discriminator and the layout is\n * the same either way.\n *\n * **The grid follows the published ephemeris, which is one of the most\n * convention-bound artifacts in astrology and where a deviation reads as\n * amateurish on sight.** Days run DOWN the rows with the weekday beside\n * the day number, bodies run ACROSS the columns headed by their glyphs, a cell\n * is `9♌16` with the sign glyph BETWEEN the degree and the minutes rather than a\n * decimal longitude, retrograde is marked in the cell, and the sign changes and\n * direction changes are listed separately beside the grid, which is what those\n * publications call Ingresses and Stations. One deliberate deviation, and it is\n * additive: a printed ephemeris prints the sign glyph only on the row where it\n * changes, because a reader has the whole page in view. A scrolling table has no\n * such anchor, so every cell carries its glyph and the ingress cell is tinted\n * instead, which makes the same day easier to find rather than harder.\n *\n * **Two blocks, both always in the page, and the order is the reading order.**\n * A month of daily positions is 31 rows by up to 14 bodies, and dumping that\n * alone answers \"where was Mars on the 12th\" while burying the questions a\n * practitioner actually opens an ephemeris for: when did a body change sign,\n * and when did it turn retrograde. So the derived answer leads. Each body gets\n * one row carrying its position on the first and last sampled day, a chip per\n * sign it entered with the date, and a chip per retrograde stretch. This is also\n * how the published tables separate the Moon, which changes sign every two or\n * three days and would otherwise swamp a single date-ordered ingress list: one\n * row per body puts its thirteen crossings on their own line. Below it, the full\n * daily grid, so every chip can be checked against the day it came from. Neither\n * block is behind a tab or a disclosure, and the grid scrolls inside its own box\n * so a phone never scrolls the page sideways.\n *\n * **Ingresses and retrograde windows are DERIVED from consecutive days, and the\n * grain of the answer is the grain of the sample.** The API returns one\n * position per day at noon UTC, so an ingress is dated to the first day the\n * body is shown in the new sign and a retrograde run to the first and last days\n * it is shown retrograde. The true crossing and the true station fall somewhere\n * in the preceding 24 hours; a month page cannot say where, and pretending to a\n * time the response does not carry would be the worse error. A run that reaches\n * either edge of the month is clipped there rather than extrapolated, so\n * `Retrograde Aug 1 - Aug 31` means what it says: retrograde on every day this\n * month.\n *\n * **No coordinate-system badge, deliberately.** The Western endpoint is always\n * tropical, but the Vedic one takes `coordinateSystem` as a REQUEST parameter\n * (`sidereal` by default, `tropical` on request) and echoes nothing about it in\n * the response, and neither response carries a `frame`. A card cannot label a\n * zodiac it was not told, and a wrong frame label on an ephemeris is worse than\n * none, so the roster of bodies is left to say what it says.\n *\n * `hide-readings` is a no-op here: neither endpoint returns a word of\n * interpretation, so there is nothing to take away.\n */\n@customElement('roxy-ephemeris-table')\nexport class RoxyEphemerisTable extends RoxyDataElement<EphemerisResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`:host{overflow-x:clip}.wrap{background:var(--roxy-surface,#fff);width:100%;color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}header{justify-content:space-between;align-items:baseline;gap:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-primary,#0f172a);margin:0}.month{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem);gap:var(--roxy-space-xs,.25rem) var(--roxy-space-sm,.5rem);flex-wrap:wrap;margin:0;display:flex}.basis{font-variant-numeric:tabular-nums}.wrap>*{min-width:0}h3{margin:0 0 var(--roxy-space-xs,.25rem);font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);text-transform:uppercase;letter-spacing:.06em;color:var(--roxy-muted,#71717a)}.signkey{flex-wrap:wrap;gap:1px 0;margin:0;padding:0;list-style:none;display:flex}.signkey-item{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);white-space:nowrap;align-items:baseline;gap:.3rem;padding:2px 10px 2px 0;display:inline-flex}.signkey-glyph{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a)}.tracks{background:var(--roxy-border,#e4e4e7);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);grid-template-columns:minmax(0,1fr);gap:1px;display:grid;overflow:hidden}.track{align-items:center;gap:var(--roxy-space-xs,.25rem) var(--roxy-space-sm,.5rem);padding:6px var(--roxy-space-sm,.5rem);background:var(--roxy-surface,#fff);font-size:var(--roxy-text-sm,.875rem);flex-wrap:wrap;display:flex}.body-name{align-items:baseline;gap:.35rem;min-width:8.5rem;font-weight:500;display:inline-flex}.body-name .glyph{color:var(--roxy-accent-ink,#b45309);font-size:1.1em}.span{color:var(--roxy-secondary,#475569);font-variant-numeric:tabular-nums;white-space:nowrap;align-items:baseline;gap:.3rem;display:inline-flex}.arrow{color:var(--roxy-muted,#71717a)}.chip-date{color:var(--roxy-secondary,#475569)}.chips{gap:var(--roxy-space-xs,.25rem);flex-wrap:wrap;margin-left:auto;display:flex}.chip{border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);white-space:nowrap;align-items:baseline;gap:.25rem;padding:1px 8px;display:inline-flex}.chip--ingress{background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 14%, transparent);color:var(--roxy-fg,#0a0a0a)}.chip--retro{background:color-mix(in srgb, var(--roxy-danger,#dc2626) 10%, transparent);color:var(--roxy-danger-fg,#991b1b);font-weight:var(--roxy-weight-bold,600)}.scroll:focus-visible{outline:2px solid var(--roxy-ring,#f59e0b);outline-offset:2px}.scroll{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);-webkit-overflow-scrolling:touch;min-width:0;overflow-x:auto}table{border-collapse:collapse;font-size:var(--roxy-text-xs,.75rem)}th,td{text-align:left;border-bottom:1px solid var(--roxy-border,#e4e4e7);white-space:nowrap;padding:4px 8px}thead th{color:var(--roxy-muted,#71717a);font-weight:var(--roxy-weight-bold,600);background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 20%, transparent)}thead th .glyph{color:var(--roxy-fg,#0a0a0a);font-size:1.15em}th.day{z-index:1;background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);font-variant-numeric:tabular-nums;border-right:1px solid var(--roxy-border,#e4e4e7);font-weight:500;position:sticky;left:0}thead th.day{z-index:2;background:var(--roxy-surface,#fff)}td{font-variant-numeric:tabular-nums}.sg{color:var(--roxy-secondary,#475569);font-variant-numeric:normal;margin:0 .15em}.retro-mark{font-weight:var(--roxy-weight-bold,600);margin-left:.25em}td.retro{background:color-mix(in srgb, var(--roxy-danger,#dc2626) 10%, transparent);color:var(--roxy-danger-fg,#991b1b)}td.retro .sg{color:inherit}td.ingress{box-shadow:inset 3px 0 var(--roxy-accent-ink,#b45309);font-weight:var(--roxy-weight-bold,600)}td.station{border-top:2px solid var(--roxy-danger-fg,#991b1b);font-weight:var(--roxy-weight-bold,600)}`,\n\t];\n\n\t/** Override the card heading. Empty keeps the default, which is translated like every other chrome string. */\n\t@property({ type: String })\n\theading = 'Ephemeris';\n\n\tprotected renderData(data: EphemerisResponse) {\n\t\tif (!data.days?.length) return this.renderEmpty();\n\t\tconst vm = this.toViewModel(data);\n\t\tconst locale = this.effectiveLang();\n\t\treturn html`<div class=\"wrap\" part=\"card\"><header part=\"header\"><h2 class=\"title\">${this.t(this.heading)}</h2><p class=\"month\"><span>${formatMonthYear(locale, vm.year, vm.month)}</span><span class=\"basis\">${SAMPLE_INSTANT}</span></p></header>${this.renderSignKey(vm.signs)}<section part=\"section changes\"><h3>${this.t('Sign changes and retrograde periods')}</h3><div class=\"tracks\" role=\"list\">${vm.bodies.map((b) => this.renderTrack(b))}</div></section><section part=\"section daily\"><h3>${this.t('Daily positions')}</h3><div class=\"scroll\" part=\"table\" aria-label=\"${this.t('Daily positions')}\" tabindex=\"0\">${this.renderGrid(vm)}</div></section></div>`;\n\t}\n\n\t/**\n\t * The glyph-to-name key, above the table it explains.\n\t *\n\t * @remarks\n\t * Every position in this component prints its sign as a glyph, which is how a\n\t * printed ephemeris prints one and is unreadable to somebody still learning\n\t * them. The name rides on each cell's `title`, and a title is a hover: it does\n\t * not exist on touch and it cannot be read down a column. A key that names all\n\t * of them once is the form the page actually needed.\n\t *\n\t * `part=\"section legend\"` rather than a name of its own, because a page hiding\n\t * legends should hide this one too and the vocabulary is shared across every\n\t * component.\n\t */\n\tprivate renderSignKey(signs: SignKeyEntry[]) {\n\t\tif (signs.length === 0) return nothing;\n\t\treturn html`<section part=\"section legend\"><h3>${this.t('Signs in this month')}</h3><ul class=\"signkey\" role=\"list\">${signs.map(\n\t\t\t\t\t(s) => html`<li class=\"signkey-item\"><span class=\"signkey-glyph\" aria-hidden=\"true\">${s.glyph}</span>${s.label}</li>`,\n\t\t\t\t)}</ul></section>`;\n\t}\n\n\t/** One body across the month: where it started, where it ended, and every change in between. */\n\tprivate renderTrack(b: BodyTrack) {\n\t\tconst glyph = planetGlyph(b.name);\n\t\treturn html`<div class=\"track\" role=\"listitem\"><span class=\"body-name\">${glyph ? html`<span class=\"glyph\" aria-hidden=\"true\">${glyph}</span>` : nothing}${b.label} </span><span class=\"span\">${this.renderPosition(b.first)}<span class=\"arrow\" aria-hidden=\"true\">→</span>${this.renderPosition(b.last)} </span>${\n\t\t\t\tb.ingresses.length || b.retrogrades.length\n\t\t\t\t\t? html`<span class=\"chips\">${b.ingresses.map((i) => this.renderIngressChip(i))} ${b.retrogrades.map((r) => this.renderRetrogradeChip(r))}</span>`\n\t\t\t\t\t: nothing\n\t\t\t}</div>`;\n\t}\n\n\t/**\n\t * A longitude the way a published ephemeris prints one: `9♌16`, degree then\n\t * sign then minutes. The sign name rides on the `title` for a reader who does\n\t * not read glyphs, and it is the localized half.\n\t */\n\tprivate renderPosition(p: Position) {\n\t\tconst { degree, minute } = splitDegreeInSign(p.degreeInSign);\n\t\tconst g = signGlyph(p.sign);\n\t\tconst label = display(p, 'sign');\n\t\treturn html`<span class=\"pos\" title=\"${label}\">${degree}${\n\t\t\t\tg\n\t\t\t\t\t? html`<span class=\"sg\" aria-hidden=\"true\">${g}</span>`\n\t\t\t\t\t: html`<span class=\"sg\">${label}</span>`\n\t\t\t}${String(minute).padStart(2, '0')}</span>`;\n\t}\n\n\tprivate renderIngressChip(i: Ingress) {\n\t\tconst g = signGlyph(i.sign);\n\t\tconst date = formatMonthDay(this.effectiveLang(), i.date);\n\t\treturn html`<span class=\"chip chip--ingress\" aria-label=\"${this.t('Enters {{sign}} on {{date}}', { sign: i.signLabel, date })}\"><span aria-hidden=\"true\">→</span>${g ? html`<span aria-hidden=\"true\">${g}</span>` : nothing}${i.signLabel} <span class=\"chip-date\">${date}</span></span>`;\n\t}\n\n\tprivate renderRetrogradeChip(r: RetrogradeRun) {\n\t\tconst locale = this.effectiveLang();\n\t\tconst start = formatMonthDay(locale, r.start);\n\t\tconst end = formatMonthDay(locale, r.end);\n\t\tconst range = start === end ? start : `${start} - ${end}`;\n\t\treturn html`<span class=\"chip chip--retro\"><span aria-hidden=\"true\">℞</span>${this.t('Retrograde {{range}}', { range })}</span>`;\n\t}\n\n\t/** The reference ephemeris: one row per day, one column per body, in the order the response lists them. */\n\tprivate renderGrid(vm: ViewModel) {\n\t\tconst locale = this.effectiveLang();\n\t\treturn html`<table><caption class=\"roxy-sr-only\">${this.t('Every body with its position on each day of the month, as a zodiac sign and a degree.')}</caption><thead><tr><th scope=\"col\" class=\"day\">${this.t('Date')}</th>${vm.bodies.map((b) => {\n\t\t\t\t\t\tconst glyph = planetGlyph(b.name);\n\t\t\t\t\t\treturn html`<th scope=\"col\" title=\"${b.label}\">${\n\t\t\t\t\t\t\t\tglyph\n\t\t\t\t\t\t\t\t\t? html`<span class=\"glyph\" aria-hidden=\"true\">${glyph}</span><span class=\"roxy-sr-only\">${b.label}</span>`\n\t\t\t\t\t\t\t\t\t: b.label\n\t\t\t\t\t\t\t}</th>`;\n\t\t\t\t\t})}</tr></thead><tbody>${vm.rows.map(\n\t\t\t\t\t(row) => html`<tr><th scope=\"row\" class=\"day\">${formatWeekdayDay(locale, row.date)}</th>${row.cells.map((cell, i) => this.renderCell(cell, vm.bodies[i], row.date))}</tr>`,\n\t\t\t\t)}</tbody></table>`;\n\t}\n\n\tprivate renderCell(\n\t\tcell: Position | undefined,\n\t\tbody: BodyTrack | undefined,\n\t\tdate: string,\n\t) {\n\t\tif (!cell) return html`<td>—</td>`;\n\t\tconst classes = [\n\t\t\tcell.isRetrograde ? 'retro' : '',\n\t\t\tbody?.ingressDates.has(date) ? 'ingress' : '',\n\t\t\tbody?.stationDates.has(date) ? 'station' : '',\n\t\t]\n\t\t\t.filter(Boolean)\n\t\t\t.join(' ');\n\t\treturn html`<td class=\"${classes}\">${this.renderPosition(cell)}${\n\t\t\t\tcell.isRetrograde\n\t\t\t\t\t? html`<span class=\"retro-mark\" aria-label=\"${this.t('retrograde')}\">℞</span>`\n\t\t\t\t\t: nothing\n\t\t\t}</td>`;\n\t}\n\n\t/**\n\t * Walk the month once, keying every body on its canonical English name.\n\t *\n\t * @remarks\n\t * The column order is the order the response lists bodies on its first day,\n\t * which is the order each domain reads them in (Sun through Pluto then the\n\t * nodes, Chiron and Black Moon Lilith for the Western set; the Navagraha in\n\t * vara order for the Vedic one). A body that only appears on a later day is\n\t * appended rather than dropped, and a body missing from a given day leaves\n\t * that one cell empty instead of shifting the row.\n\t */\n\tprivate toViewModel(data: EphemerisResponse): ViewModel {\n\t\tconst order: string[] = [];\n\t\tconst series = new Map<string, Array<{ date: string; p: Position }>>();\n\t\tfor (const day of data.days) {\n\t\t\tfor (const p of day.positions) {\n\t\t\t\tlet samples = series.get(p.planet);\n\t\t\t\tif (!samples) {\n\t\t\t\t\tsamples = [];\n\t\t\t\t\tseries.set(p.planet, samples);\n\t\t\t\t\torder.push(p.planet);\n\t\t\t\t}\n\t\t\t\tsamples.push({ date: day.date, p });\n\t\t\t}\n\t\t}\n\n\t\tconst bodies: BodyTrack[] = [];\n\t\tfor (const name of order) {\n\t\t\tconst samples = series.get(name);\n\t\t\tconst first = samples?.[0];\n\t\t\tconst last = samples?.[samples.length - 1];\n\t\t\tif (!samples || !first || !last) continue;\n\t\t\tconst ingresses: Ingress[] = [];\n\t\t\tconst retrogrades: RetrogradeRun[] = [];\n\t\t\tconst ingressDates = new Set<string>();\n\t\t\tconst stationDates = new Set<string>();\n\t\t\tfor (let i = 0; i < samples.length; i++) {\n\t\t\t\tconst cur = samples[i];\n\t\t\t\tconst prev = samples[i - 1];\n\t\t\t\tif (!cur) continue;\n\t\t\t\tif (prev && cur.p.sign !== prev.p.sign) {\n\t\t\t\t\tingresses.push({\n\t\t\t\t\t\tdate: cur.date,\n\t\t\t\t\t\tsign: cur.p.sign,\n\t\t\t\t\t\tsignLabel: display(cur.p, 'sign'),\n\t\t\t\t\t});\n\t\t\t\t\tingressDates.add(cur.date);\n\t\t\t\t}\n\t\t\t\t// A direction change is only observable against a previous day, so a\n\t\t\t\t// body that is already retrograde on the first of the month stationed\n\t\t\t\t// before this page begins and is not marked as stationing on it.\n\t\t\t\tif (prev && cur.p.isRetrograde !== prev.p.isRetrograde)\n\t\t\t\t\tstationDates.add(cur.date);\n\t\t\t\tif (!cur.p.isRetrograde) continue;\n\t\t\t\t// Extend the run in progress only when yesterday was retrograde too, so\n\t\t\t\t// a body that stations twice in one month gets two chips rather than one\n\t\t\t\t// spanning the direct stretch between them.\n\t\t\t\tconst open = retrogrades[retrogrades.length - 1];\n\t\t\t\tif (open && prev?.p.isRetrograde) open.end = cur.date;\n\t\t\t\telse retrogrades.push({ start: cur.date, end: cur.date });\n\t\t\t}\n\t\t\tbodies.push({\n\t\t\t\tname,\n\t\t\t\tlabel: display(last.p, 'planet'),\n\t\t\t\tfirst: first.p,\n\t\t\t\tlast: last.p,\n\t\t\t\tingresses,\n\t\t\t\tretrogrades,\n\t\t\t\tingressDates,\n\t\t\t\tstationDates,\n\t\t\t});\n\t\t}\n\n\t\tconst rows: DayRow[] = data.days.map((day) => {\n\t\t\tconst byName = new Map(day.positions.map((p) => [p.planet, p]));\n\t\t\treturn { date: day.date, cells: bodies.map((b) => byName.get(b.name)) };\n\t\t});\n\t\treturn {\n\t\t\tyear: data.year,\n\t\t\tmonth: data.month,\n\t\t\tbodies,\n\t\t\trows,\n\t\t\tsigns: this.toSignKey(data),\n\t\t};\n\t}\n\n\t/**\n\t * The signs this month's table actually prints, in zodiacal order.\n\t *\n\t * @remarks\n\t * Read from the response rather than from the twelve-sign constant, so the key\n\t * explains the glyphs ON THIS PAGE and nothing else. In practice the Moon\n\t * crosses every sign in a month, so a full month lists all twelve anyway; a\n\t * partial range lists only what it contains, which is the point.\n\t *\n\t * The label is the API's own localized sign name, taken the same way every\n\t * other sign name in this component is taken. Translating it here instead\n\t * would put a second vocabulary on the page and let the key disagree with the\n\t * cells it explains.\n\t *\n\t * Ordered by {@link SIGNS_ORDER} because first-seen order is the order the\n\t * bodies happen to sit in on the first of the month, which is meaningless to a\n\t * reader learning the glyphs.\n\t */\n\tprivate toSignKey(data: EphemerisResponse): SignKeyEntry[] {\n\t\tconst labels = new Map<string, string>();\n\t\tfor (const day of data.days) {\n\t\t\tfor (const p of day.positions) {\n\t\t\t\tconst key = String(p.sign ?? '').toLowerCase();\n\t\t\t\tif (key && !labels.has(key)) labels.set(key, display(p, 'sign'));\n\t\t\t}\n\t\t}\n\t\tconst out: SignKeyEntry[] = [];\n\t\tfor (const sign of SIGNS_ORDER) {\n\t\t\tconst label = labels.get(sign.toLowerCase());\n\t\t\tconst glyph = signGlyph(sign);\n\t\t\tif (label && glyph) out.push({ sign, label, glyph });\n\t\t}\n\t\treturn out;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-ephemeris-table': RoxyEphemerisTable;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport { signGlyph } from '../tokens/index.js';\nimport type { FixedStarsResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { formatDegreeInSign } from '../utils/degree.js';\nimport { chevron, disclosureStyles } from '../utils/disclosure.js';\nimport { formatNumber } from '../utils/format.js';\nimport { interpAccordionStyles } from '../utils/interp-accordion.js';\n\ntype Star = FixedStarsResponse['stars'][number];\n\n/**\n * Fixed stars table. Leads with the high-value view from a\n * /astrology/fixed-stars response: every star-to-natal-point conjunction sorted\n * tightest first, each with its reading. The full precessed star catalog\n * (position, magnitude, traditional nature, keywords) sits in a secondary\n * disclosure so the contacts stay front and center.\n */\n@customElement('roxy-fixed-stars')\nexport class RoxyFixedStars extends RoxyDataElement<FixedStarsResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tdisclosureStyles,\n\t\tinterpAccordionStyles,\n\t\tcss`.wrap{background:var(--roxy-surface,#fff);width:100%;color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}header{align-items:baseline;gap:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-primary,#0f172a);margin:0}.badge{border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 14%, transparent);color:var(--roxy-fg,#0a0a0a);padding:2px 8px}.badge b{color:var(--roxy-accent-ink,#b45309);font-weight:600}.summary{color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-sm,.875rem);margin:0}.empty-note{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem);margin:0}.subhead{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.06em;margin:0 0 var(--roxy-space-sm,.5rem);font-weight:600}.interp-lead .point{color:var(--roxy-accent-ink,#b45309);font-weight:var(--roxy-weight-bold,600)}.interp-aside small{font-variant-numeric:tabular-nums}.catalog summary{cursor:pointer;color:var(--roxy-fg,#0a0a0a);align-items:center;gap:.5rem;font-weight:600;display:flex}.scroll{min-width:0;margin-top:var(--roxy-space-sm,.5rem);overflow-x:auto}table{border-collapse:collapse;width:100%;font-size:var(--roxy-text-sm,.875rem)}th,td{text-align:left;border-bottom:1px solid var(--roxy-border,#e4e4e7);white-space:nowrap;vertical-align:top;padding:6px 10px}th{color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.04em;font-weight:600;font-size:var(--roxy-text-xs,.75rem)}td.num{text-align:right;font-variant-numeric:tabular-nums}.sg{color:var(--roxy-secondary,#475569);margin-right:.3rem}.kw{white-space:normal;flex-wrap:wrap;gap:.2rem;max-width:18rem;display:flex}.kw span{border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 45%, transparent);color:var(--roxy-fg,#0a0a0a);padding:0 6px}`,\n\t];\n\n\tprotected renderData(data: FixedStarsResponse) {\n\t\tconst conjunctions = data.conjunctions ?? [];\n\t\tconst stars = data.stars ?? [];\n\t\treturn html`<div class=\"wrap\" part=\"card\"><header part=\"header\"><h2 class=\"title\">${this.t('Fixed stars')}</h2>${\n\t\t\t\t\ttypeof data.orb === 'number'\n\t\t\t\t\t\t? html`<span class=\"badge\"><b>${this.t('Orb')}</b> ${formatNumber(this.effectiveLang(), data.orb, 1)}°</span>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</header>${\n\t\t\t\t// The contacts and the catalog below are positional data; this is the\n\t\t\t\t// written read of them.\n\t\t\t\tdata.summary && !this.hideReadings\n\t\t\t\t\t? html`<p class=\"summary\">${data.summary}</p>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\tconjunctions.length\n\t\t\t\t\t? html`<section part=\"section conjunctions\"><p class=\"subhead\">${this.t('Conjunctions to the chart')}</p>${conjunctions.map(\n\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\tc,\n\t\t\t\t\t\t\t\ti,\n\t\t\t\t\t\t\t) => html`<details class=\"interp-card\" part=\"reading\" name=\"fixed-star-contacts\" ?open=\"${i === 0}\"><summary><span class=\"interp-lead\">${this.t('{{point}} conjunct {{star}}', { point: c.point, star: c.star })}</span> ${chevron()} <span class=\"interp-aside\"><small>${this.t('orb {{value}}°', { value: formatNumber(this.effectiveLang(), c.orb, 2) })}</small></span></summary>${\n\t\t\t\t\t\t\t\t\t// A contact the API sent no reading for already renders as a\n\t\t\t\t\t\t\t\t\t// bodyless card, so hiding the readings reuses that shape and\n\t\t\t\t\t\t\t\t\t// keeps every star, point and orb on screen.\n\t\t\t\t\t\t\t\t\tc.interpretation && !this.hideReadings\n\t\t\t\t\t\t\t\t\t\t? html`<div class=\"interp-body\"><p>${c.interpretation}</p></div>`\n\t\t\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t\t\t}</details>`,\n\t\t\t\t\t\t)}</section>`\n\t\t\t\t\t: html`<p class=\"empty-note\">${this.t('No star sits within the orb of a natal point.')}</p>`\n\t\t\t} ${stars.length ? this.renderCatalog(stars) : nothing}</div>`;\n\t}\n\n\tprivate renderCatalog(stars: Star[]) {\n\t\treturn html`<details class=\"catalog\"><summary>${chevron()} ${this.t('Star catalog ({{count}})', { count: stars.length })}</summary><div class=\"scroll\"><table><caption class=\"subhead\">${this.t('Precessed positions for the chart date')}</caption><thead><tr><th scope=\"col\">${this.t('Star')}</th><th scope=\"col\">${this.t('Position')}</th><th scope=\"col\" class=\"num\">${this.t('Mag')}</th><th scope=\"col\">${this.t('Nature')}</th><th scope=\"col\">${this.t('Keywords')}</th></tr></thead><tbody>${stars.map((s) => {\n\t\t\t\t\t\t\tconst g = signGlyph(s.sign);\n\t\t\t\t\t\t\treturn html`<tr><td>${s.name}</td><td>${g ? html`<span class=\"sg\">${g}</span>` : nothing}${formatDegreeInSign(s.degree)} ${s.sign}</td><td class=\"num\">${formatNumber(this.effectiveLang(), s.magnitude, 1)}</td><td>${s.nature}</td><td><div class=\"kw\">${(s.keywords ?? []).map((k) => html`<span>${k}</span>`)}</div></td></tr>`;\n\t\t\t\t\t\t})}</tbody></table></div></details>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-fixed-stars': RoxyFixedStars;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport type { GenerateDigestResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { formatDate, formatNumber } from '../utils/format.js';\nimport { humanize } from '../utils/string.js';\n\ntype DigestWindow = NonNullable<GenerateDigestResponse['windows']>[number];\ntype DigestEvent = NonNullable<DigestWindow['top']>[number];\n\n/**\n * Forecast digest: the rolled-up reading across the next 24 hours, 7, 30, and 90 days. Renders /forecast/digest. Each window shows how many events fall in it, a per-domain breakdown (western, vedic, biorhythm), and the few highest-significance events with a significance bar coloured by domain. Use it as the at-a-glance period summary that pairs with the day-by-day roxy-forecast-timeline.\n *\n * @remarks\n * What a digest is for survives `hide-readings` intact: the windows, their event counts, the per-domain breakdown, every event date and the significance ranking. Only the written `description` goes, and each row falls back to the component's existing type label rather than emptying out. This is the one accepted cost here, and it is deliberate: unlike the timeline, a digest row carries no structured headline beside the sentence, so hiding the prose costs the bodies involved. The counts above it, which is what the card is read for, do not move.\n */\n@customElement('roxy-forecast-digest')\nexport class RoxyForecastDigest extends RoxyDataElement<GenerateDigestResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.wrap{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.head{justify-content:space-between;align-items:baseline;gap:var(--roxy-space-md,1rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.subtitle{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem);margin:0}.window{gap:var(--roxy-space-sm,.5rem);padding-top:var(--roxy-space-md,1rem);border-top:1px solid var(--roxy-border,#e4e4e7);display:grid}.window:first-of-type{border-top:none;padding-top:0}.window-head{flex-wrap:wrap;align-items:baseline;gap:.5rem;display:flex}.window-label{font-weight:var(--roxy-weight-bold,600)}.window-count{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);font-variant-numeric:tabular-nums;margin-left:auto}.domains{flex-wrap:wrap;gap:.4rem;display:flex}.domain-chip{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);align-items:center;gap:.35rem;display:inline-flex}.swatch{border-radius:2px;width:.65rem;height:.65rem;display:inline-block}.sw-western{background:var(--roxy-accent,#f59e0b)}.sw-vedic{background:var(--roxy-info,#2563eb)}.sw-biorhythm{background:var(--roxy-success,#16a34a)}.event{grid-template-columns:auto 1fr;align-items:baseline;gap:.25rem .6rem;display:grid}.event-date{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);font-variant-numeric:tabular-nums;white-space:nowrap}.event-desc{font-size:var(--roxy-text-sm,.875rem);line-height:1.4}.sig{background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 60%, transparent);border-radius:2px;grid-column:2;height:4px;overflow:hidden}.sig-fill{border-radius:2px;height:100%;display:block}.sig-fill.western{background:var(--roxy-accent,#f59e0b)}.sig-fill.vedic{background:var(--roxy-info,#2563eb)}.sig-fill.biorhythm{background:var(--roxy-success,#16a34a)}.quiet{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem);font-style:italic}`,\n\t];\n\n\tprotected renderData(d: GenerateDigestResponse) {\n\t\tconst windows = d.windows ?? [];\n\t\tif (windows.length === 0) return this.renderEmpty();\n\t\tconst range = [\n\t\t\tformatDate(this.effectiveLang(), d.startDate),\n\t\t\tformatDate(this.effectiveLang(), d.endDate),\n\t\t]\n\t\t\t.filter(Boolean)\n\t\t\t.join(' – ');\n\n\t\treturn html`<div class=\"wrap\" part=\"card\" aria-label=\"${this.t('Forecast digest')}\"><div class=\"head\" part=\"header\"><h2 class=\"title\">${this.t('Forecast digest')}</h2>${range ? html`<p class=\"subtitle\">${range}</p>` : nothing}</div>${windows.map((w) => this.renderWindow(w))}</div>`;\n\t}\n\n\tprivate windowLabel(days: number | undefined): string {\n\t\tif (days === 1) return 'Next 24 hours';\n\t\treturn typeof days === 'number' ? `Next ${days} days` : 'Window';\n\t}\n\n\tprivate renderWindow(w: DigestWindow) {\n\t\t// The window is a ranked summary, so the strongest event must lead. The\n\t\t// payload order is by domain and then by date within it, which put a\n\t\t// significance 30 event above a significance 90 one and made the bars\n\t\t// read as noise. Sorted copy: never mutate the array we were handed.\n\t\tconst top = [...(w.top ?? [])].sort(\n\t\t\t(a, b) => (b.significance ?? 0) - (a.significance ?? 0),\n\t\t);\n\t\tconst byDomain = w.byDomain ?? {};\n\t\tconst domains = Object.entries(byDomain) as Array<[string, number]>;\n\t\treturn html`<section class=\"window\" part=\"section window\"><div class=\"window-head\" part=\"details\"><span class=\"window-label\">${this.windowLabel(w.days)}</span> <span class=\"window-count\">${this.t('{{count}} events', { count: w.count ?? 0 })}</span></div>${\n\t\t\t\tdomains.length > 0\n\t\t\t\t\t? html`<div class=\"domains\" part=\"legend\">${domains.map(\n\t\t\t\t\t\t\t([dom, n]) => html`<span class=\"domain-chip\"><span class=\"swatch sw-${dom}\"></span>${humanize(dom)} ${n}</span>`,\n\t\t\t\t\t\t)}</div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\ttop.length > 0\n\t\t\t\t\t? html`<div role=\"list\">${top.map((e) => this.renderEvent(e))}</div>`\n\t\t\t\t\t: html`<p class=\"quiet\">${this.t('No notable events.')}</p>`\n\t\t\t}</section>`;\n\t}\n\n\tprivate renderEvent(e: DigestEvent) {\n\t\tconst sig = typeof e.significance === 'number' ? e.significance : 0;\n\t\t// Hiding the readings reuses the shape a description-less event already\n\t\t// renders in, rather than inventing a second headline builder or leaving the\n\t\t// row blank.\n\t\tconst label =\n\t\t\tthis.hideReadings || e.description == null\n\t\t\t\t? humanize(e.type ?? '')\n\t\t\t\t: e.description;\n\t\treturn html`<div class=\"event\" role=\"listitem\"><span class=\"event-date\">${formatDate(this.effectiveLang(), e.date)}</span> <span class=\"event-desc\">${label}</span> <span class=\"sig\" role=\"img\" aria-label=\"${this.t('significance {{value}} of 100', { value: formatNumber(this.effectiveLang(), sig, 0) })}\"><span class=\"sig-fill ${e.domain}\" style=\"width:${Math.max(0, Math.min(100, sig))}%\"></span></span></div>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-forecast-digest': RoxyForecastDigest;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport { aspectSymbol } from '../tokens/index.js';\nimport type {\n\tFindSignificantDatesResponse,\n\tForecastTransitsResponse,\n\tGenerateTimelineResponse,\n} from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport {\n\tASPECT_CLASS,\n\tformatAspectName,\n\tformatDate,\n\tformatNumber,\n\tnormalizeAspect,\n} from '../utils/format.js';\nimport { capitalize, humanize } from '../utils/string.js';\n\n/** Timeline, significant-dates, and forecast-transits all return the same `{ events, startDate, endDate, birthData, count }` shape. */\ntype ForecastTimelineData =\n\t| GenerateTimelineResponse\n\t| FindSignificantDatesResponse\n\t| ForecastTransitsResponse;\n\ntype ForecastEvent = GenerateTimelineResponse['events'][number];\ntype ForecastDomain = ForecastEvent['domain'];\n\n/**\n * Display label per forecast domain. Keyed by the spec `domain` union so a new\n * domain in the spec surfaces as a typecheck failure here rather than an\n * unlabeled swatch.\n */\nconst DOMAIN_LABEL: Record<ForecastDomain, string> = {\n\twestern: 'Western',\n\tvedic: 'Vedic',\n\tbiorhythm: 'Biorhythm',\n};\n\nconst DOMAIN_ORDER: readonly ForecastDomain[] = [\n\t'western',\n\t'vedic',\n\t'biorhythm',\n];\n\n/**\n * Cross-domain forecast timeline. Pass `data` from /forecast/timeline. Events\n * are grouped by calendar date down a vertical axis, each colored by its domain\n * and weighted by significance (0-100), showing the event type, body, target,\n * aspect, and description. The visual language matches the dasha timeline: a\n * left rail of dates, a significance bar per event, and a colored marker for\n * the domain.\n *\n * Theming flows through `--roxy-*` custom properties on `:host`.\n *\n * @remarks\n * `hide-readings` drops only `description`. Every event keeps its date, its domain marker, its significance bar and the headline built from the structured `body`, `aspect`, `target` and `orb`, so nothing factual is lost: the sentence restates that headline in prose, which is exactly the report a page supplying its own words does not want repeated under every row.\n */\n@customElement('roxy-forecast-timeline')\nexport class RoxyForecastTimeline extends RoxyDataElement<ForecastTimelineData> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.wrap{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.head{justify-content:space-between;align-items:baseline;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.range{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem);font-variant-numeric:tabular-nums}.legend{gap:var(--roxy-space-md,1rem);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);flex-wrap:wrap;display:flex}.legend .swatch{vertical-align:middle;border-radius:50%;width:10px;height:10px;margin-right:4px;display:inline-block}.swatch-western{background:var(--roxy-accent,#f59e0b)}.swatch-vedic{background:var(--roxy-info,#2563eb)}.swatch-biorhythm{background:var(--roxy-success,#16a34a)}.day{gap:var(--roxy-space-md,1rem);padding-block:var(--roxy-space-sm,.5rem);border-top:1px solid var(--roxy-border,#e4e4e7);grid-template-columns:4.5rem 1fr;display:grid}.day:first-of-type{border-top:none}.day-date{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);font-variant-numeric:tabular-nums;padding-top:2px}.events{gap:var(--roxy-space-sm,.5rem);display:grid}.event{gap:var(--roxy-space-sm,.5rem);grid-template-columns:.6rem 1fr;align-items:start;display:grid}.dot{border-radius:50%;width:.6rem;height:.6rem;margin-top:4px}.dot-western{background:var(--roxy-accent,#f59e0b)}.dot-vedic{background:var(--roxy-info,#2563eb)}.dot-biorhythm{background:var(--roxy-success,#16a34a)}.event-body{min-width:0}.event-line{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a)}.event-line .aspect-trine,.event-line .aspect-sextile{color:var(--roxy-success-fg,#166534)}.event-line .aspect-square,.event-line .aspect-opposition{color:var(--roxy-danger-fg,#991b1b)}.event-line .aspect-conjunction{color:var(--roxy-accent-ink,#b45309)}.event-line .kind{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);text-transform:uppercase;letter-spacing:.04em;margin-left:.35em}.event-desc{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);margin:2px 0 0}.sig{align-items:center;gap:.4rem;margin-top:4px;display:flex}.sig-track{background:var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-full,9999px);flex:1;max-width:8rem;height:4px;overflow:hidden}.sig-fill{border-radius:var(--roxy-radius-full,9999px);height:100%;display:block}.sig-fill-western{background:var(--roxy-accent,#f59e0b)}.sig-fill-vedic{background:var(--roxy-info,#2563eb)}.sig-fill-biorhythm{background:var(--roxy-success,#16a34a)}.sig-val{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);font-variant-numeric:tabular-nums}`,\n\t];\n\n\tprotected renderData(d: ForecastTimelineData) {\n\t\tconst events = d.events ?? [];\n\t\tconst grouped = this.groupByDate(events);\n\t\tconst present = DOMAIN_ORDER.filter((dom) =>\n\t\t\tevents.some((e) => e.domain === dom),\n\t\t);\n\n\t\treturn html`<div class=\"wrap\" part=\"card\" aria-label=\"${this.t('Forecast timeline')}\"><header class=\"head\" part=\"header\"><h2 class=\"title\">${this.t('Forecast timeline')}</h2>${\n\t\t\t\t\td.startDate && d.endDate\n\t\t\t\t\t\t? html`<div class=\"range\">${formatDate(this.effectiveLang(), d.startDate)} - ${formatDate(this.effectiveLang(), d.endDate)} · ${this.t('{{count}} events', { count: d.count ?? events.length })}</div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</header>${\n\t\t\t\tpresent.length\n\t\t\t\t\t? html`<div class=\"legend\" part=\"legend\">${present.map(\n\t\t\t\t\t\t\t(dom) =>\n\t\t\t\t\t\t\t\thtml`<span><span class=\"swatch swatch-${dom}\"></span>${DOMAIN_LABEL[dom]}</span>`,\n\t\t\t\t\t\t)}</div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\tgrouped.length\n\t\t\t\t\t? html`<div class=\"days\" part=\"section events\" role=\"list\">${grouped.map(([date, dayEvents]) => this.renderDay(date, dayEvents))}</div>`\n\t\t\t\t\t: html`<p class=\"roxy-empty\" role=\"status\">${this.t('No events in this window')}</p>`\n\t\t\t}</div>`;\n\t}\n\n\t/** Group events by their calendar date, preserving response order. */\n\tprivate groupByDate(\n\t\tevents: ForecastEvent[],\n\t): Array<[string, ForecastEvent[]]> {\n\t\tconst map = new Map<string, ForecastEvent[]>();\n\t\tfor (const e of events) {\n\t\t\tconst key = e.date ?? '';\n\t\t\tconst list = map.get(key) ?? [];\n\t\t\tlist.push(e);\n\t\t\tmap.set(key, list);\n\t\t}\n\t\treturn [...map.entries()];\n\t}\n\n\tprivate renderDay(date: string, events: ForecastEvent[]) {\n\t\treturn html`<div class=\"day\" role=\"listitem\"><div class=\"day-date\">${formatDate(this.effectiveLang(), date)}</div><div class=\"events\">${events.map((e) => this.renderEvent(e))}</div></div>`;\n\t}\n\n\tprivate renderEvent(e: ForecastEvent) {\n\t\tconst sig = typeof e.significance === 'number' ? e.significance : 0;\n\t\tconst width = Math.max(0, Math.min(100, sig));\n\t\treturn html`<div class=\"event\"><span class=\"dot dot-${e.domain}\" aria-hidden=\"true\"></span><div class=\"event-body\"><div class=\"event-line\">${this.renderHeadline(e)}</div>${\n\t\t\t\t\t// The headline above already carries the bodies, the aspect and the orb\n\t\t\t\t\t// from the structured fields, so this sentence is the written read of a\n\t\t\t\t\t// row that stays complete without it.\n\t\t\t\t\te.description && !this.hideReadings\n\t\t\t\t\t\t? html`<p class=\"event-desc\">${e.description}</p>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}<div class=\"sig\" title=\"${this.t('significance {{value}} of 100', { value: width })}\"><span class=\"sig-track\"><span class=\"sig-fill sig-fill-${e.domain}\" style=\"width:${width}%\"></span> </span><span class=\"sig-val\">${width}</span></div></div></div>`;\n\t}\n\n\t/**\n\t * Compact event headline built from the spec fields. Transit aspects read\n\t * \"body aspect target\"; ingresses, stations, eclipses, dasha changes, and\n\t * critical days fall back to \"body\" plus the qualifier the spec carries for\n\t * that type (station direction, eclipse kind). The aspect symbol is colored\n\t * by nature, matching the chart aspect encoding.\n\t */\n\tprivate renderHeadline(e: ForecastEvent) {\n\t\tconst body = e.body ? capitalize(e.body) : '';\n\t\tconst target = e.target ? capitalize(e.target) : '';\n\t\tconst aspect = normalizeAspect({ type: e.aspect });\n\t\tconst aspectClass = ASPECT_CLASS[aspect] ?? '';\n\t\tconst aspectSym = aspect\n\t\t\t? (aspectSymbol(e.aspect) ?? formatAspectName({ type: e.aspect }))\n\t\t\t: '';\n\t\tconst orb =\n\t\t\ttypeof e.orb === 'number'\n\t\t\t\t? formatNumber(this.effectiveLang(), e.orb, 1)\n\t\t\t\t: '';\n\t\tconst qualifier = this.typeQualifier(e);\n\n\t\tif (aspect && target) {\n\t\t\treturn html`<strong>${body}</strong> <span class=\"${aspectClass}\">${aspectSym}</span> <strong>${target}</strong>${orb ? html`<span class=\"kind\">${this.t('orb {{value}}°', { value: orb })}</span>` : nothing}`;\n\t\t}\n\t\treturn html`<strong>${body || humanize(e.type ?? '')}</strong>${\n\t\t\tqualifier ? html`<span class=\"kind\">${qualifier}</span>` : nothing\n\t\t}`;\n\t}\n\n\t/** Type-specific qualifier text from the optional spec fields. */\n\tprivate typeQualifier(e: ForecastEvent): string {\n\t\tif (e.type === 'sign-ingress' && e.target)\n\t\t\treturn `enters ${capitalize(e.target)}`;\n\t\tif (e.type === 'retrograde-station' && e.station) return e.station;\n\t\tif (e.type === 'eclipse')\n\t\t\treturn [e.kind, 'eclipse'].filter(Boolean).join(' ');\n\t\tif (e.type === 'critical-day') return 'critical day';\n\t\tif (e.type === 'dasha-change' && e.target)\n\t\t\treturn `dasha ${capitalize(e.target)}`;\n\t\treturn humanize(e.type ?? '');\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-forecast-timeline': RoxyForecastTimeline;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport { planetGlyph } from '../tokens/index.js';\nimport type { CalculateTransitResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport {\n\tformatDegreeInSign,\n\tlongitudeToSignPosition,\n} from '../utils/degree.js';\nimport {\n\tformatAspectName,\n\tformatDateTime,\n\tformatNumber,\n} from '../utils/format.js';\nimport { frameCaptionStyles, renderFrameCaption } from '../utils/frame.js';\n\ntype Transiting = CalculateTransitResponse['transitingPlanets'][number];\ntype Kaksha = Transiting['kaksha'];\n\n/** Kakshas per sign. Fixed by the definition, not by our data: each sign divides into eight stretches of 3 degrees 45 minutes. */\nconst KAKSHA_COUNT = 8;\n\n/**\n * Gochara: where each graha is transiting now, which natal house it falls in, and the Ashtakavarga-qualified verdict for the exact stretch it occupies.\n *\n * @remarks\n * This renders `POST /vedic-astrology/transit`, the SINGULAR Vedic operation, which is a different response from the Western `calculateTransits` that {@link RoxyTransitsTable} renders. The two are not interchangeable: the Western one carries `transitPlanets` with speed and retrograde flags, this one carries `natalHouse`, `aspectsToNatal` and `kaksha`.\n *\n * **Gochara is reckoned from the natal Moon: the house from Janma Rashi leads each row and the house from the Lagna sits beside it**, because a transit chart drawn over the birth chart shows the Lagna reading and a reader comparing the two has to see which number is which.\n *\n * **Drishti and the degree-based aspects are both listed and each is labelled**, since Parashari jyotish has no sextile, square or trine and a Vedic card printing only the Western vocabulary would teach a reading the tradition does not make.\n *\n * **Kaksha is drawn as a POSITION WITHIN THE SIGN, never as a verdict chip.** The sign says where a graha is; the kaksha says whether the sub-four-degree stretch it currently occupies is one its own Bhinnashtakavarga supports. What a practitioner reads off it is how long until the verdict turns, and a single chip throws that away: a graha two thirds through an unsupported kaksha is a different situation from one that just entered it. The eight-segment bar answers both at a glance.\n *\n * The eight kaksha lords run in a fixed order from the start of every sign, and this component deliberately holds NO copy of that order. Only the CURRENT kaksha is labelled, from the `lord` the response carries. A local table would be a second source of truth for data the API owns, and it would sit in a public repo.\n *\n * **`bindu` is null for Rahu and Ketu and must render blank, never as an unfavourable verdict.** The nodes have no Bhinnashtakavarga of their own, so there is no bindu to give; treating the absence as a negative would invent a reading the tradition does not make. Since the OpenAPI 3.1 nullability fix the type is `boolean | null`, so a `bindu ? a : b` no longer typechecks its way past the distinction.\n *\n * `hide-readings` takes the Key transits section and nothing else. Every graha row stays whole, including the kaksha line: that sentence is a readout of `number`, `lord`, `startDegree`, `endDegree` and `binduCount`, so it is the calculation written out rather than a reading of it. The Key transits section is the reverse, a list of `description` sentences whose one datum, the natal house, is already on the graha row above, so it goes whole rather than leaving its heading behind.\n */\n@customElement('roxy-gochara-table')\nexport class RoxyGocharaTable extends RoxyDataElement<CalculateTransitResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tframeCaptionStyles,\n\t\tcss`.wrap{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);box-shadow:var(--roxy-shadow-sm);grid-template-columns:minmax(0,1fr);display:grid;overflow:hidden}.head{padding:var(--roxy-space-md,1rem);border-bottom:1px solid var(--roxy-border,#e4e4e7);gap:var(--roxy-space-xs,.25rem);display:grid}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.sub{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);line-height:var(--roxy-leading-normal,1.5);margin:0}.row{gap:var(--roxy-space-xs,.25rem);padding:var(--roxy-space-md,1rem);border-top:1px solid var(--roxy-border,#e4e4e7);grid-template-columns:minmax(0,1fr);display:grid}.row-top{align-items:baseline;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.graha{font-weight:var(--roxy-weight-bold,600)}.glyph{color:var(--roxy-muted,#71717a);margin-right:.35em}.pos{color:var(--roxy-secondary,#475569);font-size:var(--roxy-text-sm,.875rem);font-variant-numeric:tabular-nums}.house{font-size:var(--roxy-text-xs,.75rem);border-radius:var(--roxy-radius-sm,4px);border:1px solid var(--roxy-border,#e4e4e7);padding:.1rem .45rem}.house.moon{border-color:var(--roxy-accent,#f59e0b)}.house.lagna{color:var(--roxy-muted,#71717a)}.contact-label{font-weight:var(--roxy-weight-bold,600);margin-right:.35em}.kaksha-bar{grid-template-columns:repeat(8,1fr);gap:2px;height:10px;margin:.15rem 0;display:grid}.seg{background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 60%, transparent);border-radius:2px}.seg.here{background:color-mix(in srgb, var(--roxy-muted,#71717a) 45%, transparent)}.seg.here.yes{background:color-mix(in srgb, var(--roxy-success,#16a34a) 55%, transparent)}.seg.here.no{background:color-mix(in srgb, var(--roxy-warning,#f59e0b) 55%, transparent)}.meta{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);line-height:var(--roxy-leading-normal,1.5);margin:0}.num{font-variant-numeric:tabular-nums}.aspects{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-secondary,#475569);line-height:var(--roxy-leading-normal,1.5);margin:0}.key{padding:var(--roxy-space-md,1rem);border-top:2px solid var(--roxy-border,#e4e4e7);gap:var(--roxy-space-sm,.5rem);display:grid}.key-title{font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);text-transform:uppercase;letter-spacing:.06em;color:var(--roxy-muted,#71717a);margin:0}.key-item{font-size:var(--roxy-text-sm,.875rem);line-height:var(--roxy-leading-normal,1.5);margin:0}`,\n\t];\n\n\tprotected renderData(d: CalculateTransitResponse) {\n\t\tconst planets = d.transitingPlanets ?? [];\n\t\tif (!planets.length) return this.renderEmpty();\n\t\tconst key = d.keyTransits ?? [];\n\t\t// Janma Rashi, named so the house numbers below are readable without a\n\t\t// second request. The response says the reference is the Moon entry.\n\t\tconst moonSign = d.natalPlanets?.find((p) => p.name === 'Moon')?.sign;\n\n\t\treturn html`<div class=\"wrap\" part=\"card\" aria-label=\"${this.t('Gochara transits')}\"><header class=\"head\" part=\"header\"><h2 class=\"title\">${this.t('Gochara')}</h2><p class=\"sub\">${this.t(\n\t\t\t\t\t\t'Where each graha transits at {{when}}, read against the natal chart of {{birth}}.',\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\twhen: formatDateTime(this.effectiveLang(), d.transitDatetime),\n\t\t\t\t\t\t\tbirth: formatDateTime(this.effectiveLang(), d.birthDatetime),\n\t\t\t\t\t\t},\n\t\t\t\t\t)}</p>${\n\t\t\t\t\tmoonSign\n\t\t\t\t\t\t? html`<p class=\"sub\">${this.t('Gochara houses are counted from the natal Moon in {{sign}}.', { sign: moonSign })}</p>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t} ${renderFrameCaption(this.effectiveLang(), d.frame, this.translator)}</header><div part=\"table\">${planets.map((p) => this.renderPlanet(p))}</div>${\n\t\t\t\tkey.length && !this.hideReadings\n\t\t\t\t\t? html`<section class=\"key\" part=\"section key-transits\"><h3 class=\"key-title\">${this.t('Key transits')}</h3>${key.map((k) => html`<p class=\"key-item\">${k.description}</p>`)}</section>`\n\t\t\t\t\t: nothing\n\t\t\t}</div>`;\n\t}\n\n\tprivate renderPlanet(p: Transiting) {\n\t\tconst glyph = planetGlyph(p.name) ?? '';\n\t\tconst pos =\n\t\t\ttypeof p.longitude === 'number'\n\t\t\t\t? longitudeToSignPosition(p.longitude)\n\t\t\t\t: undefined;\n\n\t\treturn html`<article class=\"row\"><div class=\"row-top\"><span class=\"graha\">${glyph ? html`<span class=\"glyph\">${glyph}</span>` : nothing}${p.name} </span><span class=\"pos\">${p.sign}${pos ? html`${formatDegreeInSign(pos.degree)}` : nothing} </span>${\n\t\t\t\t\ttypeof p.houseFromMoon === 'number'\n\t\t\t\t\t\t? html`<span class=\"house moon\">${this.t('house {{n}} from the Moon', { n: p.houseFromMoon })}</span>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t} ${\n\t\t\t\t\ttypeof p.natalHouse === 'number'\n\t\t\t\t\t\t? html`<span class=\"house lagna\">${this.t('house {{n}} from the Lagna', { n: p.natalHouse })}</span>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</div>${this.renderKaksha(p.kaksha)} ${this.renderContacts(this.t('Drishti'), p.drishtiToNatal)} ${this.renderContacts(this.t('Aspects'), p.aspectsToNatal)}</article>`;\n\t}\n\n\t/** One labelled contact list. Drishti and the degree-based aspects carry the same three fields, so both read through here and the label is what tells them apart. */\n\tprivate renderContacts(\n\t\tlabel: string,\n\t\titems:\n\t\t\t| readonly { natalPlanet: string; aspectType: string; orb: number }[]\n\t\t\t| undefined,\n\t) {\n\t\tif (!items?.length) return nothing;\n\t\treturn html`<p class=\"aspects\"><span class=\"contact-label\">${label}</span> ${items\n\t\t\t\t.map((a) =>\n\t\t\t\t\ttypeof a.orb === 'number'\n\t\t\t\t\t\t? this.t('{{aspect}} natal {{planet}} ({{orb}}°)', {\n\t\t\t\t\t\t\t\taspect: formatAspectName({ type: a.aspectType }),\n\t\t\t\t\t\t\t\tplanet: a.natalPlanet,\n\t\t\t\t\t\t\t\torb: formatNumber(this.effectiveLang(), a.orb, 1),\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t: this.t('{{aspect}} natal {{planet}}', {\n\t\t\t\t\t\t\t\taspect: formatAspectName({ type: a.aspectType }),\n\t\t\t\t\t\t\t\tplanet: a.natalPlanet,\n\t\t\t\t\t\t\t}),\n\t\t\t\t)\n\t\t\t\t.join(' · ')}</p>`;\n\t}\n\n\t/**\n\t * The eight-segment kaksha bar plus its one-line reading.\n\t *\n\t * @remarks\n\t * `bindu` has three states and each renders differently: `true` supports the transit, `false` does not, and `null` means the graha has no Bhinnashtakavarga at all, which is Rahu and Ketu. The null case gets the neutral segment and NO verdict sentence, the same way the planets table leaves the avastha cells blank for the nodes rather than printing a zero.\n\t */\n\tprivate renderKaksha(k: Kaksha | undefined) {\n\t\tif (!k || typeof k.number !== 'number') return nothing;\n\t\tconst here = k.number;\n\t\tconst verdict = k.bindu === true ? 'yes' : k.bindu === false ? 'no' : '';\n\n\t\tconst total = KAKSHA_COUNT;\n\t\tconst head =\n\t\t\tk.lord &&\n\t\t\ttypeof k.startDegree === 'number' &&\n\t\t\ttypeof k.endDegree === 'number'\n\t\t\t\t? this.t(\n\t\t\t\t\t\t'Kaksha {{n}} of {{total}}, ruled by {{graha}}, spanning {{start}}° to {{end}}° of the sign',\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tn: here,\n\t\t\t\t\t\t\ttotal,\n\t\t\t\t\t\t\tgraha: k.lord,\n\t\t\t\t\t\t\tstart: formatNumber(this.effectiveLang(), k.startDegree, 2),\n\t\t\t\t\t\t\tend: formatNumber(this.effectiveLang(), k.endDegree, 2),\n\t\t\t\t\t\t},\n\t\t\t\t\t)\n\t\t\t\t: k.lord\n\t\t\t\t\t? this.t('Kaksha {{n}} of {{total}}, ruled by {{graha}}', {\n\t\t\t\t\t\t\tn: here,\n\t\t\t\t\t\t\ttotal,\n\t\t\t\t\t\t\tgraha: k.lord,\n\t\t\t\t\t\t})\n\t\t\t\t\t: typeof k.startDegree === 'number' && typeof k.endDegree === 'number'\n\t\t\t\t\t\t? this.t(\n\t\t\t\t\t\t\t\t'Kaksha {{n}} of {{total}}, spanning {{start}}° to {{end}}° of the sign',\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tn: here,\n\t\t\t\t\t\t\t\t\ttotal,\n\t\t\t\t\t\t\t\t\tstart: formatNumber(this.effectiveLang(), k.startDegree, 2),\n\t\t\t\t\t\t\t\t\tend: formatNumber(this.effectiveLang(), k.endDegree, 2),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t: this.t('Kaksha {{n}} of {{total}}', { n: here, total });\n\n\t\tconst gave =\n\t\t\tk.bindu === null || k.bindu === undefined\n\t\t\t\t? ''\n\t\t\t\t: typeof k.binduCount === 'number'\n\t\t\t\t\t? k.bindu\n\t\t\t\t\t\t? this.t(\n\t\t\t\t\t\t\t\t'this kaksha lord gave bindu, {{count}} of {{total}} in this sign',\n\t\t\t\t\t\t\t\t{ count: k.binduCount, total },\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t: this.t(\n\t\t\t\t\t\t\t\t'this kaksha lord gave no bindu, {{count}} of {{total}} in this sign',\n\t\t\t\t\t\t\t\t{ count: k.binduCount, total },\n\t\t\t\t\t\t\t)\n\t\t\t\t\t: k.bindu\n\t\t\t\t\t\t? this.t('this kaksha lord gave bindu')\n\t\t\t\t\t\t: this.t('this kaksha lord gave no bindu');\n\n\t\treturn html`<div class=\"kaksha-bar\" part=\"chart\" role=\"img\" aria-label=\"${this.t(\n\t\t\t\t\t'Kaksha {{n}} of {{total}} within the current sign',\n\t\t\t\t\t{\n\t\t\t\t\t\tn: here,\n\t\t\t\t\t\ttotal,\n\t\t\t\t\t},\n\t\t\t\t)}\">${Array.from(\n\t\t\t\t\t{ length: KAKSHA_COUNT },\n\t\t\t\t\t(_, i) =>\n\t\t\t\t\t\thtml`<span class=\"seg ${i + 1 === here ? `here ${verdict}` : ''}\"></span>`,\n\t\t\t\t)}</div><p class=\"meta\">${gave ? `${head} · ${gave}` : head}</p>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-gochara-table': RoxyGocharaTable;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport type { CompatibilityResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { formatNumber, formatPercent } from '../utils/format.js';\nimport { frameCaptionStyles, renderFrameCaption } from '../utils/frame.js';\n\n/**\n * The eight kootas and the points each carries, in the classical order the API\n * returns them. Used only as a fallback when a payload omits `maxScore`; the\n * spec marks it required. The names are the canonical ones (Vashya, Graha\n * Maitri), not the shortened forms.\n */\nconst KOOTA_MAX: Record<string, number> = {\n\tvarna: 1,\n\tvashya: 2,\n\ttara: 3,\n\tyoni: 4,\n\t'graha maitri': 5,\n\tgana: 6,\n\tbhakoot: 7,\n\tnadi: 8,\n};\n\n/**\n * 36-point Ashtakoota score card. Renders /vedic-astrology/compatibility.\n *\n * @remarks\n * Each koota row carries what it actually evaluates and how each person\n * classifies under it (Varna: Shudra against Shudra, Yoni: Sheep against Horse),\n * because the point total alone tells a couple nothing about WHY a koota scored\n * as it did.\n *\n * `hide-readings` drops exactly one block, the recommendation line. The koota\n * descriptions stay: each states what its category evaluates and reads the same\n * for every couple, so it is a column gloss on the table rather than a reading of\n * this match, the same way `houseThemes` labels a house list. The score, the\n * ring, the compatible verdict, the eight-row breakdown and the dosha and\n * cancellation chips are all data and all survive.\n */\n@customElement('roxy-guna-milan')\nexport class RoxyGunaMilan extends RoxyDataElement<CompatibilityResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tframeCaptionStyles,\n\t\tcss`.card{background:var(--roxy-surface,#fff);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.card>*{min-width:0}.score-header{align-items:center;gap:1rem;display:flex}.score-info{flex:1;min-width:0}.table-scroll{-webkit-overflow-scrolling:touch;min-width:0;overflow-x:auto}.score-bar{align-items:center;gap:var(--roxy-space-md,1rem);grid-template-columns:1fr auto;display:grid}.total{font-size:2.25rem;font-weight:var(--roxy-weight-bold,600);color:var(--roxy-accent-ink,#b45309);font-variant-numeric:tabular-nums;line-height:1}.over{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-base,1rem)}.verdict-wrap{justify-items:end;gap:var(--roxy-space-xs,.25rem);text-align:right;display:grid}.verdict{border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);padding:1px 10px}.verdict.yes{background:color-mix(in srgb, var(--roxy-success,#16a34a) 16%, transparent);color:var(--roxy-success-fg,#166534)}.verdict.no{background:color-mix(in srgb, var(--roxy-danger,#dc2626) 16%, transparent);color:var(--roxy-danger-fg,#991b1b)}.recommendation{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-secondary,#475569)}.score-ring{flex-shrink:0;width:120px;height:120px}.score-ring svg{width:100%;height:100%}.score-ring .ring-text{fill:var(--roxy-fg,#0a0a0a);font-size:22px;font-weight:700;font-family:var(--roxy-font-sans)}.score-ring .ring-max{fill:var(--roxy-muted,#71717a);font-size:10px;font-family:var(--roxy-font-sans)}table{border-collapse:collapse;width:100%;font-size:var(--roxy-text-sm,.875rem)}th,td{padding:var(--roxy-space-sm,.5rem);border-bottom:1px solid var(--roxy-border,#e4e4e7);text-align:left}th{color:var(--roxy-muted,#71717a);font-weight:var(--roxy-weight-bold,600);text-transform:uppercase;font-size:var(--roxy-text-xs,.75rem);letter-spacing:.06em}td small{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);margin-top:2px;line-height:1.5;display:block}td.classification{color:var(--roxy-secondary,#475569);white-space:nowrap}th.score{text-align:right}td.score{text-align:right;font-variant-numeric:tabular-nums;color:var(--roxy-fg,#0a0a0a);font-weight:var(--roxy-weight-bold,600)}td.bar-cell{width:22%}@container (width<=32rem){.bar-col{display:none}}@container (width<=26rem){.score-header{flex-direction:column-reverse;align-items:stretch}.score-ring{align-self:center}.score-ring .ring-text,.score-ring .ring-max{display:none}.score-bar{gap:var(--roxy-space-xs,.25rem);grid-template-columns:1fr}.verdict-wrap{text-align:left;justify-items:start}}.mini-bar{background:var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-full,9999px);height:8px;overflow:hidden}.mini-bar>span{background:var(--roxy-accent,#f59e0b);height:100%;transition:width var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,cubic-bezier(.4, 0, .2, 1));display:block}.tags{gap:var(--roxy-space-xs,.25rem);flex-wrap:wrap;display:flex}.tags span{border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);padding:2px 8px}.tags .dosha{background:color-mix(in srgb, var(--roxy-danger,#dc2626) 16%, transparent);color:var(--roxy-danger-fg,#991b1b)}.tags .cancel{background:color-mix(in srgb, var(--roxy-success,#16a34a) 18%, transparent);color:var(--roxy-success-fg,#166534)}`,\n\t];\n\n\tprotected renderData(d: CompatibilityResponse) {\n\t\tconst breakdown = (d.breakdown ?? []).filter(\n\t\t\t(b) => b?.category !== undefined,\n\t\t);\n\n\t\tconst score = d.total ?? 0;\n\t\tconst max = d.maxScore ?? 36;\n\t\tconst pct = (score / max) * 100;\n\t\tconst trackColor =\n\t\t\t'color-mix(in srgb, var(--roxy-border) 50%, transparent)';\n\t\tconst fillColor =\n\t\t\tpct >= 70\n\t\t\t\t? 'var(--roxy-success)'\n\t\t\t\t: pct >= 50\n\t\t\t\t\t? 'var(--roxy-warning)'\n\t\t\t\t\t: 'var(--roxy-danger)';\n\t\t// SVG circle with r=45: circumference = 2 * pi * 45 = 282.74\n\t\t// dasharray segments = pct * 2.827, (100 - pct) * 2.827\n\t\tconst dashFill = pct * 2.827;\n\t\tconst dashGap = (100 - pct) * 2.827;\n\n\t\treturn html`<article class=\"card\" part=\"card\" aria-label=\"${this.t('Guna Milan score')}\"><div class=\"score-header\" part=\"header\"><div class=\"score-info\"><div class=\"score-bar\" part=\"details\"><div><span class=\"total\">${formatNumber(this.effectiveLang(), d.total, 1)}</span> <span class=\"over\">/ ${d.maxScore}</span> ${\n\t\t\t\t\t\t\t\ttypeof d.percentage === 'number'\n\t\t\t\t\t\t\t\t\t? html`<small style=\"margin-left:.5rem;color:var(--roxy-muted)\">${formatPercent(this.effectiveLang(), d.percentage, 1)}</small>`\n\t\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t\t}</div><div class=\"verdict-wrap\">${\n\t\t\t\t\t\t\t\ttypeof d.isCompatible === 'boolean'\n\t\t\t\t\t\t\t\t\t? html`<span class=\"verdict ${d.isCompatible ? 'yes' : 'no'}\">${d.isCompatible ? this.t('Compatible') : this.t('Not compatible')}</span>`\n\t\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t\t} ${\n\t\t\t\t\t\t\t\t// The compatible verdict beside it and the dosha chips below are\n\t\t\t\t\t\t\t\t// the data; this line is the counsel drawn from them.\n\t\t\t\t\t\t\t\td.recommendation && !this.hideReadings\n\t\t\t\t\t\t\t\t\t? html`<span class=\"recommendation\">${d.recommendation}</span>`\n\t\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t\t}</div></div></div><div class=\"score-ring\" part=\"chart\" role=\"meter\" aria-label=\"${this.t('Guna Milan score')}\" aria-valuemin=\"0\" aria-valuemax=\"36\" aria-valuenow=\"${score}\"><svg viewBox=\"0 0 100 100\" aria-hidden=\"true\"><circle class=\"ring-track\" cx=\"50\" cy=\"50\" r=\"45\" fill=\"none\" stroke=\"${trackColor}\" stroke-width=\"8\"/><circle class=\"ring-fill\" cx=\"50\" cy=\"50\" r=\"45\" fill=\"none\" stroke=\"${fillColor}\" stroke-width=\"8\" stroke-dasharray=\"${dashFill},${dashGap}\" stroke-linecap=\"round\" transform=\"rotate(-90 50 50)\"/><text x=\"50\" y=\"50\" text-anchor=\"middle\" dominant-baseline=\"central\" class=\"ring-text\">${score}</text><text x=\"50\" y=\"64\" text-anchor=\"middle\" dominant-baseline=\"central\" class=\"ring-max\">/${max}</text></svg></div></div>${\n\t\t\t\tbreakdown.length > 0\n\t\t\t\t\t? html`<div class=\"table-scroll\" part=\"table\"><table><caption class=\"roxy-sr-only\">${this.t('Guna Milan breakdown: each koota with the classification of person 1 and person 2, and the score it earned out of its maximum.')}</caption><thead><tr><th scope=\"col\">${this.t('Koota')}</th><th scope=\"col\">${this.t('Person 1')}</th><th scope=\"col\">${this.t('Person 2')}</th><th scope=\"col\" class=\"bar-col\">${this.t('Progress')}</th><th scope=\"col\" class=\"score\">${this.t('Score')}</th></tr></thead><tbody>${breakdown.map((b) => {\n\t\t\t\t\t\t\t\tconst score = b.score ?? 0;\n\t\t\t\t\t\t\t\tconst maxScore = b.maxScore ?? defaultMax(b.category);\n\t\t\t\t\t\t\t\tconst pct = maxScore ? (score / maxScore) * 100 : 0;\n\t\t\t\t\t\t\t\treturn html`<tr><td>${b.category} ${b.description ? html`<small>${b.description}</small>` : nothing}</td><td class=\"classification\">${b.person1 ?? ''}</td><td class=\"classification\">${b.person2 ?? ''}</td><td class=\"bar-cell bar-col\"><div class=\"mini-bar\"><span style=\"width:${pct}%\"></span></div></td><td class=\"score\">${formatNumber(this.effectiveLang(), score, 1)} / ${maxScore}</td></tr>`;\n\t\t\t\t\t\t\t})}</tbody></table></div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\t(d.doshas?.length ?? 0) > 0 || (d.doshaCancellations?.length ?? 0) > 0\n\t\t\t\t\t? html`<div class=\"tags\" part=\"details\">${d.doshas?.map((x) => html`<span class=\"dosha\">${x}</span>`)} ${d.doshaCancellations?.map(\n\t\t\t\t\t\t\t(x) =>\n\t\t\t\t\t\t\t\thtml`<span class=\"cancel\" title=\"${x.reason}\">${this.t('{{dosha}} cancelled', { dosha: x.dosha })}</span>`,\n\t\t\t\t\t\t)}</div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${renderFrameCaption(this.effectiveLang(), d.frame, this.translator)}</article>`;\n\t}\n}\n\nfunction defaultMax(name?: string): number {\n\treturn (name && KOOTA_MAX[name.toLowerCase()]) || 1;\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-guna-milan': RoxyGunaMilan;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport type { CalculateConnectionResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport {\n\tBODYGRAPH_VIEWBOX,\n\ttype BodygraphCenterId,\n\ttype GateSources,\n\trenderBodygraphSvg,\n} from '../utils/bodygraph-render.js';\nimport { bodygraphChartStyles } from '../utils/bodygraph-styles.js';\nimport { disclosureStyles } from '../utils/disclosure.js';\nimport { hdReadingStyles, renderHdFacts } from '../utils/hd-reading.js';\nimport {\n\ttype InterpSection,\n\tinterpAccordionStyles,\n} from '../utils/interp-accordion.js';\nimport { humanize } from '../utils/string.js';\n\ntype Connection = CalculateConnectionResponse;\ntype ConnectionChannel = Connection['channels'][number];\ntype CombinedCenter = Connection['centers'][number];\n\n/**\n * Human Design connection chart. Renders /human-design/connection: the composite of two charts as the electromagnetic / compromise / dominance channels they form together, plus the combined definition and a summary. The HD analog of synastry. Each channel row shows its two gates, name, circuit, the relationship dynamic, and which person carries which gate.\n *\n * @remarks\n * The chart answers one question: what exists between these two that does not exist in either of them alone. It leads with the combined bodygraph, drawn on the same geometry a single chart uses with each gate marked for whoever carries it, then the combined centers, then the channel list. `defined` is the state of the COMBINED bodygraph, `definedBy` names who already defines that center in their own chart, and the gap between the two is the reading: a center that comes back defined with an empty `definedBy` is defined by the connection itself, present when the two are together and absent when they are apart.\n *\n * The four dynamics are the taxonomy the summary counts, and a bare count of \"Dominance: 2\" means nothing to a reader who does not already know the system, so each one carries its definition behind the shared disclosure rather than shipping as a bare label.\n */\n@customElement('roxy-hd-connection')\nexport class RoxyHdConnection extends RoxyDataElement<Connection> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tdisclosureStyles,\n\t\tinterpAccordionStyles,\n\t\thdReadingStyles,\n\t\tbodygraphChartStyles,\n\t\tcss`.wrap{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.head{justify-content:space-between;align-items:baseline;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.count{color:var(--roxy-accent-ink,#b45309);font-size:var(--roxy-text-sm,.875rem);font-weight:var(--roxy-weight-bold,600);font-variant-numeric:tabular-nums}.note{margin:0 0 var(--roxy-space-sm,.5rem);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);line-height:1.6}.row{align-items:center;gap:var(--roxy-space-sm,.5rem);padding:var(--roxy-space-sm,.5rem) 0;border-bottom:1px solid var(--roxy-border,#e4e4e7);font-size:var(--roxy-text-sm,.875rem);flex-wrap:wrap;display:flex}.row:last-child{border-bottom:none}.cname{flex:1;min-width:8rem}.badge{border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);padding:1px 8px;display:inline-flex}.badge.defined{background:color-mix(in srgb, var(--roxy-success,#16a34a) 16%, transparent);color:var(--roxy-success-fg,#166534)}.badge.open{background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 45%, transparent);color:var(--roxy-fg,#0a0a0a)}.badge.together{background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 18%, transparent);color:var(--roxy-fg,#0a0a0a)}.who{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem)}table{border-collapse:collapse;width:100%;font-size:var(--roxy-text-sm,.875rem)}th,td{text-align:left;padding:var(--roxy-space-sm,.5rem);border-bottom:1px solid var(--roxy-border,#e4e4e7);white-space:nowrap}th{color:var(--roxy-muted,#71717a);font-weight:var(--roxy-weight-bold,600);text-transform:uppercase;font-size:var(--roxy-text-xs,.75rem);letter-spacing:.04em}tbody tr:last-child td{border-bottom:none}.gates{font-variant-numeric:tabular-nums;color:var(--roxy-accent-ink,#b45309);font-weight:var(--roxy-weight-bold,600)}.dyn{border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 16%, transparent);color:var(--roxy-fg,#0a0a0a);padding:1px 8px;display:inline-block}.held{font-variant-numeric:tabular-nums}.scroll{min-width:0;overflow-x:auto}.wrap{--src-left:#1f6fb2;--src-right:#b04e14;--src-ink:#fff}`,\n\t];\n\n\t/**\n\t * The combined bodygraph the two charts make together, on the same geometry a\n\t * single chart uses.\n\t *\n\t * @remarks\n\t * A gate is marked for whoever carries it, so a channel each person half owns\n\t * draws in two colours and a channel one person holds outright draws in one.\n\t * That is the electromagnetic and dominance distinction the summary counts,\n\t * shown rather than tallied. Centers take the COMBINED state, which is the\n\t * chart the two of them are when together; who defines one alone is named in\n\t * the centers section, where the badge can say it in words.\n\t */\n\tprivate renderChart(d: Connection) {\n\t\tconst gateSources = new Map<number, GateSources>();\n\t\tfor (const c of d.channels ?? []) {\n\t\t\tconst left = new Set(c.personAGates ?? []);\n\t\t\tconst right = new Set(c.personBGates ?? []);\n\t\t\tfor (const gate of [c.gateA, c.gateB]) {\n\t\t\t\tif (gate == null) continue;\n\t\t\t\tconst held = gateSources.get(gate) ?? { left: false, right: false };\n\t\t\t\theld.left ||= left.has(gate);\n\t\t\t\theld.right ||= right.has(gate);\n\t\t\t\tgateSources.set(gate, held);\n\t\t\t}\n\t\t}\n\t\tconst definedCenters = new Set<BodygraphCenterId>(\n\t\t\t(d.centers ?? [])\n\t\t\t\t.filter((c) => c.defined)\n\t\t\t\t.map((c) => c.id as BodygraphCenterId),\n\t\t);\n\t\tconst centerNames = new Map<BodygraphCenterId, string>(\n\t\t\t(d.centers ?? []).map((c) => [c.id as BodygraphCenterId, c.name]),\n\t\t);\n\t\tconst gateTitles = new Map<number, string>();\n\t\tfor (const c of d.channels ?? []) {\n\t\t\tconst name = c.name;\n\t\t\tfor (const gate of [c.gateA, c.gateB]) {\n\t\t\t\tif (gate == null) continue;\n\t\t\t\tconst held = gateSources.get(gate);\n\t\t\t\tconst who = [held?.left && 'A', held?.right && 'B']\n\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t.join(' + ');\n\t\t\t\tgateTitles.set(\n\t\t\t\t\tgate,\n\t\t\t\t\t[`Gate ${gate}`, name, who].filter(Boolean).join(' · '),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\treturn html`<svg class=\"chart\" part=\"chart\" viewBox=\"${BODYGRAPH_VIEWBOX}\" preserveAspectRatio=\"xMidYMid meet\" role=\"img\" aria-label=\"Combined Human Design bodygraph for two charts, with each gate marked for the person who carries it\"><title>Combined bodygraph</title>${renderBodygraphSvg({\n\t\t\t\tdefinedCenters,\n\t\t\t\tgateSources,\n\t\t\t\tgateTitles,\n\t\t\t\tcenterNames,\n\t\t\t\tstateWords: { defined: 'Defined', open: 'Open' },\n\t\t\t})}</svg><div class=\"legend\" part=\"legend\"><span><span class=\"swatch source src-left\"></span>Person A</span> <span><span class=\"swatch source src-right\"></span>Person B</span> <span><span class=\"swatch\"></span>Open center</span></div>`;\n\t}\n\n\tprotected renderData(d: Connection) {\n\t\tconst channels = d.channels ?? [];\n\t\tconst centers = d.centers ?? [];\n\t\tconst definedCenters = centers.filter((c) => c.defined).length;\n\n\t\treturn html`<div class=\"wrap\" part=\"card\" aria-label=\"Human Design connection\"><header class=\"head\" part=\"header\"><h2 class=\"title\">Connection</h2>${typeof d.totalChannels === 'number' ? html`<span class=\"count\">${d.totalChannels} channels</span>` : nothing}</header>${this.renderChart(d)} ${renderHdFacts([\n\t\t\t\t{ label: 'Channels', value: d.totalChannels?.toString() },\n\t\t\t\t{\n\t\t\t\t\tlabel: 'Combined definition',\n\t\t\t\t\tvalue: d.combinedDefinition\n\t\t\t\t\t\t? humanize(String(d.combinedDefinition))\n\t\t\t\t\t\t: undefined,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: 'Centers defined',\n\t\t\t\t\tvalue:\n\t\t\t\t\t\tcenters.length > 0\n\t\t\t\t\t\t\t? `${definedCenters} of ${centers.length}`\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t},\n\t\t\t])} ${\n\t\t\t\tthis.hideReadings\n\t\t\t\t\t? nothing\n\t\t\t\t\t: html`<p class=\"lead\">A connection chart reads the two charts as one bodygraph. Wherever the two of them together hold both gates of a channel, that channel is defined between them, and the centers it joins are defined in the connection whether or not either person defines them alone.</p>`\n\t\t\t} ${this.renderDynamics(d.summary)} ${this.renderCenters(centers)} ${this.renderChannels(channels, centers)}</div>`;\n\t}\n\n\t/**\n\t * The four ways two charts can meet in a channel, in canonical order, each with what it means. The count is the aside, so the taxonomy and the tally read as one thing: a dynamic with no channels still shows, because its absence is as much a fact of the connection as its presence.\n\t */\n\tprivate renderDynamics(s: Connection['summary'] | undefined) {\n\t\tif (!s) return nothing;\n\t\tconst sections: InterpSection[] = [\n\t\t\t{\n\t\t\t\tlabel: 'Electromagnetic',\n\t\t\t\taside: channelCount(s.electromagnetic),\n\t\t\t\tbody: 'Each person holds one of the two gates, so the channel completes only when they are together. The classic point of attraction.',\n\t\t\t},\n\t\t\t{\n\t\t\t\tlabel: 'Dominance',\n\t\t\t\taside: channelCount(s.dominance),\n\t\t\t\tbody: 'One person holds both gates and the other holds neither, so the conditioning runs one way, from the person who carries the channel to the person who does not.',\n\t\t\t},\n\t\t\t{\n\t\t\t\tlabel: 'Compromise',\n\t\t\t\taside: channelCount(s.compromise),\n\t\t\t\tbody: 'One person holds both gates and the other holds a single hanging gate of the same channel, so the channel is complete for one of them and half open for the other.',\n\t\t\t},\n\t\t\t{\n\t\t\t\tlabel: 'Companionship',\n\t\t\t\taside: channelCount(s.companionship),\n\t\t\t\tbody: 'Both people independently hold both gates, so neither needs the other to complete it. A shared and familiar frequency rather than an attraction.',\n\t\t\t},\n\t\t];\n\t\treturn this.renderInterpretation(\n\t\t\tsections,\n\t\t\t'hd-connection-dynamic',\n\t\t\t'Dynamics',\n\t\t);\n\t}\n\n\t/**\n\t * The nine centers of the combined chart. `defined` is the combined state and `definedBy` is who carries it alone, so the two together say whether a center is brought to the connection or created by it.\n\t */\n\tprivate renderCenters(centers: CombinedCenter[]) {\n\t\tif (centers.length === 0) return nothing;\n\t\treturn html`<section class=\"block\" part=\"section centers\"><h3>Centers</h3>${\n\t\t\t\t// The rows below are the state of each center and stay; this note is the\n\t\t\t\t// explanation of how to read them.\n\t\t\t\tthis.hideReadings\n\t\t\t\t\t? nothing\n\t\t\t\t\t: html`<p class=\"note\">Defined is the state of the combined chart. Beside it is who already defines that center in their own chart. A center defined only together is what the connection itself creates: it is there when the two are together and gone when they are apart. The combined definition counts how the defined centers hang together, so Single is one connected piece and a split is more than one.</p>`\n\t\t\t} ${centers.map((c) => {\n\t\t\t\tconst by = c.definedBy ?? [];\n\t\t\t\tconst a = by.includes('A');\n\t\t\t\tconst b = by.includes('B');\n\t\t\t\t// Only the connection defines it: defined in the combined chart, and in\n\t\t\t\t// neither chart on its own.\n\t\t\t\tconst together = Boolean(c.defined) && !a && !b;\n\t\t\t\treturn html`<div class=\"row\"><span class=\"cname\">${c.name ?? humanize(String(c.id ?? ''))}</span> <span class=\"badge ${c.defined ? (together ? 'together' : 'defined') : 'open'}\">${c.defined ? (together ? 'Only together' : 'Defined') : 'Open'} </span><span class=\"who\">${centerWho(Boolean(c.defined), a, b)}</span></div>`;\n\t\t\t})}</section>`;\n\t}\n\n\t/**\n\t * Every connected channel. The centers a channel joins are named, not left as ids, because the row is what explains a defined center in the block above: this is the wiring that produced it.\n\t */\n\tprivate renderChannels(\n\t\tchannels: ConnectionChannel[],\n\t\tcenters: CombinedCenter[],\n\t) {\n\t\tif (channels.length === 0) return nothing;\n\t\tconst names = new Map(\n\t\t\tcenters.map((c) => [String(c.id ?? ''), c.name ?? ''] as const),\n\t\t);\n\t\tconst centerName = (id: string) => names.get(id) || humanize(id);\n\n\t\treturn html`<section class=\"block\" part=\"section channels\"><h3>Channels</h3><div class=\"scroll\" part=\"table\" tabindex=\"0\" role=\"region\" aria-label=\"Connection channels\"><table role=\"table\"><caption class=\"roxy-sr-only\">Connection channels: each channel with its gates, the centers it links, its circuit and dynamic, and the gates person A and person B each hold.</caption><thead><tr><th scope=\"col\">Channel</th><th scope=\"col\">Gates</th><th scope=\"col\">Centers</th><th scope=\"col\">Circuit</th><th scope=\"col\">Dynamic</th><th scope=\"col\">Person A holds</th><th scope=\"col\">Person B holds</th></tr></thead><tbody>${channels.map(\n\t\t\t\t\t\t\t(c) => html`<tr><td>${c.name ?? ''}</td><td class=\"gates\">${c.gateA}-${c.gateB}</td><td>${(c.centers ?? []).map(centerName).join(' to ')}</td><td>${c.circuit ?? ''}</td><td>${c.dynamic ? html`<span class=\"dyn\">${c.dynamic}</span>` : nothing}</td><td class=\"held\">${gateList(c.personAGates)}</td><td class=\"held\">${gateList(c.personBGates)}</td></tr>`,\n\t\t\t\t\t\t)}</tbody></table></div></section>`;\n\t}\n}\n\n/** The aside of a dynamic. A zero is stated rather than hidden: no companionship channels is a finding about the pair. */\nfunction channelCount(n: number | undefined): string | undefined {\n\tif (typeof n !== 'number') return undefined;\n\tif (n === 0) return 'None';\n\treturn n === 1 ? '1 channel' : `${n} channels`;\n}\n\n/** Which gates of the channel this person carries. Neither is possible: a dominance channel is held entirely by the other person. */\nfunction gateList(gates: number[] | undefined): string {\n\tconst list = gates ?? [];\n\treturn list.length > 0 ? list.join(', ') : 'Neither';\n}\n\n/** Who defines the center in their own chart, spelled out. `definedBy` is empty both when the center is open in both charts and when only the connection defines it, so the combined state has to disambiguate. */\nfunction centerWho(defined: boolean, a: boolean, b: boolean): string {\n\tif (a && b) return 'Defined in both charts';\n\tif (a) return 'Defined by A';\n\tif (b) return 'Defined by B';\n\treturn defined\n\t\t? 'Defined by neither alone, only by the connection'\n\t\t: 'Open in both charts';\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-hd-connection': RoxyHdConnection;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport type { CalculatePentaResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { hdReadingStyles, renderHdFacts } from '../utils/hd-reading.js';\nimport {\n\tPENTA_VIEWBOX,\n\ttype PentaRung,\n\trenderPentaSvg,\n} from '../utils/penta-render.js';\n\ntype Penta = CalculatePentaResponse;\ntype PentaChannel = Penta['channels'][number];\ntype PentaGate = Penta['gates'][number];\n\n/**\n * Human Design penta. Renders /human-design/penta: the group field (3 to 5 people) as the penta channels they form, split into the upper (direction) and lower (execution) triangles, with each channel marked defined or open and core or not, and which members hold each gate. The team/business chart.\n *\n * @remarks\n * The penta is the only Human Design chart whose subject is a group rather than a person, so every row answers \"who supplies this\" as well as \"is it there\". The response reports that as zero-based indices into the member list that was sent, which is the one thing a card must not print raw: members are lettered instead, A for the first member sent, and the footnote says so.\n *\n * The ladder above them is the same data drawn: three bars for the Throat, the G and the Sacral, the six channels as rungs between them, solid where the group completes one and dashed where it does not, with each gate lettered for whoever holds it.\n *\n * Read in two passes. The channels are the functions the group performs, and a channel is a defined Strength only when both of its gates are held somewhere in the group, so the attribution line under each one shows which member carries which end. The twelve gates below are the same data seen per role, and they are where a gap shows up as itself: a gate held by nobody, which the summary also counts.\n *\n * Every word of prose on this card is written here rather than returned by the endpoint, and that changes nothing about the line: the lead, the two triangle notes, the gates note and the Core footnote are the written report on the chart, so `hide-readings` takes them the same way `roxy-hd-connection` takes its own. What stays is the whole penta: the fact tiles, both triangle headings, every channel with its gates, circuit, defined and core badges and its per-member attribution, and every gate row with its gap badge. The member-lettering footnote stays with them, because it is the legend those attributions are read through and the rows are undecodable without it.\n */\n@customElement('roxy-hd-penta')\nexport class RoxyHdPenta extends RoxyDataElement<Penta> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\thdReadingStyles,\n\t\tcss`.chart{width:100%;max-width:var(--roxy-chart-max-width,22rem);height:auto;margin:0 auto;display:block}.pn-bar{fill:color-mix(in srgb, var(--roxy-accent,#f59e0b) 12%, transparent);stroke:var(--roxy-accent-ink,#b45309);stroke-width:1.5px}.pn-bar-label{fill:var(--roxy-fg,#0a0a0a);font-size:15px;font-weight:var(--roxy-weight-bold,600);font-family:var(--roxy-font-sans)}.pn-rung{stroke:var(--roxy-border,#e4e4e7);stroke-width:6px;stroke-dasharray:6 6}.pn-rung.on{stroke:var(--roxy-accent-ink,#b45309);stroke-dasharray:none}.pn-rung.on.core{stroke-width:9px}.pn-name{fill:var(--roxy-muted,#71717a);font-size:12px;font-family:var(--roxy-font-sans)}.pn-name.on{fill:var(--roxy-fg,#0a0a0a);font-weight:var(--roxy-weight-bold,600)}.pn-gate-dot{fill:var(--roxy-surface,#fff);stroke:var(--roxy-border,#e4e4e7);stroke-width:1.5px;stroke-dasharray:4 4}.pn-gate-dot.on{stroke:var(--roxy-accent-ink,#b45309);stroke-dasharray:none}.pn-gate{fill:var(--roxy-muted,#71717a);font-size:13px;font-weight:var(--roxy-weight-bold,600);font-family:var(--roxy-font-sans)}.pn-gate.on{fill:var(--roxy-fg,#0a0a0a)}.pn-badge{fill:var(--roxy-accent-ink,#b45309)}.pn-badge-text{fill:var(--roxy-surface,#fff);font-size:9px;font-weight:var(--roxy-weight-bold,600);font-family:var(--roxy-font-sans)}.wrap{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.head{justify-content:space-between;align-items:baseline;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.count{color:var(--roxy-accent-ink,#b45309);font-size:var(--roxy-text-sm,.875rem);font-weight:var(--roxy-weight-bold,600)}.section{border-top:1px solid var(--roxy-border,#e4e4e7);padding-top:var(--roxy-space-md,1rem)}.section h3{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.06em;margin:0}.note{margin:var(--roxy-space-xs,.25rem) 0 0;font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);line-height:1.6}.row{align-items:center;gap:var(--roxy-space-sm,.5rem);padding:var(--roxy-space-sm,.5rem) 0;border-bottom:1px solid var(--roxy-border,#e4e4e7);font-size:var(--roxy-text-sm,.875rem);flex-wrap:wrap;display:flex}.row:last-child{border-bottom:none}.gates,.gate-id{font-variant-numeric:tabular-nums;color:var(--roxy-accent-ink,#b45309);font-weight:var(--roxy-weight-bold,600);min-width:3.5rem}.cname{flex:1;min-width:8rem}.circuit{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);display:block}.held{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);flex-basis:100%;line-height:1.6}.who{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem)}.badge{border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);padding:1px 8px;display:inline-flex}.badge.defined{background:color-mix(in srgb, var(--roxy-success,#16a34a) 16%, transparent);color:var(--roxy-success-fg,#166534)}.badge.open{background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 45%, transparent);color:var(--roxy-fg,#0a0a0a)}.badge.core{background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 18%, transparent);color:var(--roxy-fg,#0a0a0a)}.badge.gap{background:color-mix(in srgb, var(--roxy-danger,#dc2626) 16%, transparent);color:var(--roxy-danger-fg,#991b1b)}.footnote{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);margin:0;line-height:1.6}`,\n\t];\n\n\t/** The ladder: the six rungs and who holds each gate, drawn from the same data the rows below read. */\n\tprivate renderChart(d: Penta) {\n\t\tconst rungs: PentaRung[] = (d.channels ?? [])\n\t\t\t.filter((c) => c.gateA != null && c.gateB != null)\n\t\t\t.map((c) => ({\n\t\t\t\tgateA: c.gateA,\n\t\t\t\tgateB: c.gateB,\n\t\t\t\tname: c.name ?? '',\n\t\t\t\tdefined: c.defined === true,\n\t\t\t\tisCore: c.isCore === true,\n\t\t\t}));\n\t\tif (rungs.length === 0) return nothing;\n\t\tconst holders = new Map<number, readonly number[]>(\n\t\t\t(d.gates ?? [])\n\t\t\t\t.filter((g) => g.gate != null)\n\t\t\t\t.map((g) => [g.gate, g.heldBy ?? []]),\n\t\t);\n\t\treturn html`<svg class=\"chart\" part=\"chart\" viewBox=\"${PENTA_VIEWBOX}\" preserveAspectRatio=\"xMidYMid meet\" role=\"img\" aria-label=\"Penta ladder: the Throat, G and Sacral with the six penta channels between them, each marked defined or open and lettered with the members holding its gates\"><title>Penta</title>${renderPentaSvg({\n\t\t\t\trungs,\n\t\t\t\tholders,\n\t\t\t\tletterFor: memberLetter,\n\t\t\t})}</svg>`;\n\t}\n\n\tprotected renderData(d: Penta) {\n\t\tconst channels = d.channels ?? [];\n\t\tconst gates = d.gates ?? [];\n\t\tconst s = d.summary;\n\t\tconst gaps = s?.gapGates ?? [];\n\t\tconst hasCore = channels.some((c) => c.isCore);\n\n\t\treturn html`<div class=\"wrap\" part=\"card\" aria-label=\"Human Design penta\"><header class=\"head\" part=\"header\"><h2 class=\"title\">Penta</h2>${typeof d.memberCount === 'number' ? html`<span class=\"count\">${d.memberCount} members</span>` : nothing}</header>${this.renderChart(d)} ${renderHdFacts([\n\t\t\t\t{ label: 'Members', value: d.memberCount?.toString() },\n\t\t\t\t{\n\t\t\t\t\tlabel: 'Defined channels',\n\t\t\t\t\tvalue:\n\t\t\t\t\t\ttypeof s?.definedChannels === 'number' && channels.length > 0\n\t\t\t\t\t\t\t? `${s.definedChannels} of ${channels.length}`\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: 'Filled gates',\n\t\t\t\t\tvalue:\n\t\t\t\t\t\ttypeof s?.filledGates === 'number' && gates.length > 0\n\t\t\t\t\t\t\t? `${s.filledGates} of ${gates.length}`\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: 'Core',\n\t\t\t\t\tvalue:\n\t\t\t\t\t\ttypeof s?.coreDefined === 'boolean'\n\t\t\t\t\t\t\t? s.coreDefined\n\t\t\t\t\t\t\t\t? 'Defined'\n\t\t\t\t\t\t\t\t: 'Open'\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: 'Gap gates',\n\t\t\t\t\tvalue: gaps.length > 0 ? gaps.join(', ') : undefined,\n\t\t\t\t},\n\t\t\t])} ${\n\t\t\t\t// The tiles above count the members, the defined channels, the filled\n\t\t\t\t// gates and the gaps; this paragraph is what those counts mean.\n\t\t\t\tthis.hideReadings\n\t\t\t\t\t? nothing\n\t\t\t\t\t: html`<p class=\"lead\">A penta is the field three to five people form when they work as a group. It is read from the twelve penta gates the members bring between them: a channel with both of its gates held somewhere in the group is a defined Strength, and a gate no member holds is a gap the group has to compensate for.</p>`\n\t\t\t} ${this.renderGroup(\n\t\t\t\t'Upper (direction)',\n\t\t\t\tchannels.filter((c) => c.position === 'upper'),\n\t\t\t\t'Upper channels run from the G Center to the Throat. They carry the leadership of the group and how it presents itself.',\n\t\t\t)} ${this.renderGroup(\n\t\t\t\t'Lower (execution)',\n\t\t\t\tchannels.filter((c) => c.position === 'lower'),\n\t\t\t\t'Lower channels run from the G Center to the Sacral. They carry the managed, generative, resource work.',\n\t\t\t)} ${\n\t\t\t\t// A position the API adds later must not silently drop the channel from\n\t\t\t\t// the card: anything that is neither triangle still renders.\n\t\t\t\tthis.renderGroup(\n\t\t\t\t\t'Channels',\n\t\t\t\t\tchannels.filter(\n\t\t\t\t\t\t(c) => c.position !== 'upper' && c.position !== 'lower',\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t} ${\n\t\t\t\t// The Core badge on the row is the fact; this explains the tradition\n\t\t\t\t// behind it.\n\t\t\t\thasCore && !this.hideReadings\n\t\t\t\t\t? html`<p class=\"footnote\">Core is the 2/14 Channel of the Beat, the material core of the penta: gate 2 sets the direction for resources, gate 14 is the resources themselves.</p>`\n\t\t\t\t\t: nothing\n\t\t\t} ${this.renderGates(gates)} ${\n\t\t\t\t// Not exposition but the legend: every attribution above is a letter, and\n\t\t\t\t// without this line there is nothing to read them against.\n\t\t\t\tchannels.length > 0 || gates.length > 0\n\t\t\t\t\t? html`<p class=\"footnote\" part=\"legend\">Members are lettered in the order they were sent, so A is the first member of the group.</p>`\n\t\t\t\t\t: nothing\n\t\t\t}</div>`;\n\t}\n\n\t/**\n\t * One triangle of the penta. The note is what the triangle does, so it sits with the heading rather than being repeated on each of its three channels.\n\t */\n\tprivate renderGroup(label: string, channels: PentaChannel[], note?: string) {\n\t\tif (channels.length === 0) return nothing;\n\t\treturn html`<div class=\"section\" part=\"section channels\"><h3>${label}</h3>${note && !this.hideReadings ? html`<p class=\"note\">${note}</p>` : nothing} ${channels.map(\n\t\t\t\t(c) => html`<div class=\"row\"><span class=\"gates\">${c.gateA}-${c.gateB}</span> <span class=\"cname\">${c.name ?? ''} ${c.circuit ? html`<span class=\"circuit\">${c.circuit} circuit</span>` : nothing} </span><span class=\"badge ${c.defined ? 'defined' : 'open'}\">${c.defined ? 'Defined' : 'Open'}</span> ${c.isCore ? html`<span class=\"badge core\">Core</span>` : nothing} <span class=\"held\">Gate ${c.gateA} held by ${members(c.gateAHeldBy)}. Gate ${c.gateB} held by ${members(c.gateBHeldBy)}.</span></div>`,\n\t\t\t)}</div>`;\n\t}\n\n\t/**\n\t * The twelve penta gates, each with the role it brings and the members who carry it. A gate nobody holds is the gap the summary counts, so it is flagged rather than left as an empty attribution.\n\t */\n\tprivate renderGates(gates: PentaGate[]) {\n\t\tif (gates.length === 0) return nothing;\n\t\tconst filled = gates.filter((g) => g.filled).length;\n\t\treturn html`<div class=\"section\" part=\"section gates\"><h3>Gates (${filled} of ${gates.length} filled)</h3>${\n\t\t\t\tthis.hideReadings\n\t\t\t\t\t? nothing\n\t\t\t\t\t: html`<p class=\"note\">The role each gate brings to the group, and who carries it. A gap is a role no member holds, so the group compensates for it.</p>`\n\t\t\t} ${gates.map(\n\t\t\t\t(g) => html`<div class=\"row\"><span class=\"gate-id\">${g.gate}</span> <span class=\"cname\">${g.gateName ?? ''}</span> ${\n\t\t\t\t\t\tg.filled\n\t\t\t\t\t\t\t? html`<span class=\"who\">Held by ${members(g.heldBy)}</span>`\n\t\t\t\t\t\t\t: html`<span class=\"badge gap\">Gap</span>`\n\t\t\t\t\t}</div>`,\n\t\t\t)}</div>`;\n\t}\n}\n\n/**\n * Members are lettered, not numbered. The response reports them as zero-based indices into the member list that was sent, and printing a raw \"0\" on a card reads as a bug; \"Member 1\" for index 0 would contradict the API the developer is reading beside it. A letter is unambiguous either way, and it matches how `roxy-hd-connection` names person A and person B. Five is the ceiling: the endpoint takes 3 to 5 members.\n */\nconst MEMBER_LETTERS = 'ABCDE';\n\n/** How a member index is named anywhere on the card, so a badge on the ladder and an attribution line under a channel cannot letter the same member differently. */\nconst memberLetter = (i: number): string => MEMBER_LETTERS[i] ?? String(i + 1);\n\nfunction members(indices: number[] | undefined): string {\n\tconst held = (indices ?? []).map(memberLetter);\n\treturn held.length > 0 ? held.join(', ') : 'nobody';\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-hd-penta': RoxyHdPenta;\n\t}\n}\n", "import type { TemplateResult } from 'lit';\nimport { nothing, svg } from 'lit';\nimport { GATE_CENTER } from './bodygraph-render.js';\n\n/**\n * Fixed geometry and renderer for the Human Design penta, the group figure.\n *\n * @remarks\n * The penta is a ladder rather than a bodygraph: three horizontal bars for the Throat, G and Sacral, with the six channels as evenly spaced rungs between them, three above the G and three below. Its channels do sit on bodygraph gates, so {@link GATE_CENTER} decides which end of a rung a gate takes and which row it belongs to, rather than the order a pair is listed in.\n */\n\n/** One rung: its two gates, its name, and whether the group completes it. */\nexport interface PentaRung {\n\tgateA: number;\n\tgateB: number;\n\tname: string;\n\tdefined: boolean;\n\tisCore: boolean;\n}\n\n/** Who holds a gate, as the member indices the response reports. */\nexport type PentaHolders = ReadonlyMap<number, readonly number[]>;\n\nconst VIEW_W = 360;\nconst VIEW_H = 470;\nconst BAR_X = 8;\nconst BAR_W = VIEW_W - BAR_X * 2;\nconst BAR_H = 48;\nconst GATE_R = 15;\n/** Badge radius and step, sized so five holders on one gate stay inside their column. */\nconst BADGE_R = 7;\nconst BADGE_STEP = 16;\n\n/** The three bars, top to bottom, with the centre each one stands for. */\nconst BARS = [\n\t{ id: 'throat', label: 'Throat', y: 0 },\n\t{ id: 'g', label: 'G', y: 211 },\n\t{ id: 'sacral', label: 'Sacral', y: 422 },\n] as const;\n\nexport const PENTA_VIEWBOX = `0 0 ${VIEW_W} ${VIEW_H}`;\n\n/** Column centres, spread evenly across the bars for however many rungs a row holds. */\nfunction columnX(index: number, count: number): number {\n\treturn BAR_X + (BAR_W * (index * 2 + 1)) / (count * 2);\n}\n\n/** A rung sits above the G when one of its gates is in the Throat, below it otherwise. */\nfunction isUpper(rung: PentaRung): boolean {\n\treturn (\n\t\tGATE_CENTER[rung.gateA] === 'throat' || GATE_CENTER[rung.gateB] === 'throat'\n\t);\n}\n\n/** The gate that takes the top of a rung, which is the one in the higher centre. */\nfunction topGate(rung: PentaRung): number {\n\tconst upper = isUpper(rung);\n\tconst aIsTop = upper\n\t\t? GATE_CENTER[rung.gateA] === 'throat'\n\t\t: GATE_CENTER[rung.gateA] === 'g';\n\treturn aIsTop ? rung.gateA : rung.gateB;\n}\n\nfunction renderBars(): TemplateResult[] {\n\treturn BARS.map(\n\t\t(b) => svg`<g>\n\t\t\t<rect class=\"pn-bar\" x=${BAR_X} y=${b.y} width=${BAR_W} height=${BAR_H} rx=\"10\" />\n\t\t\t<text class=\"pn-bar-label\" x=${VIEW_W / 2} y=${b.y + BAR_H / 2} text-anchor=\"middle\" dominant-baseline=\"central\">${b.label}</text>\n\t\t</g>`,\n\t);\n}\n\n/** The letters of everyone holding a gate, laid out beside its circle. */\nfunction renderHolders(\n\tcx: number,\n\tcy: number,\n\theld: readonly number[],\n\tletterFor: (index: number) => string,\n): TemplateResult[] {\n\treturn held.map(\n\t\t(m, i) => svg`<g>\n\t\t\t<circle class=\"pn-badge\" cx=${cx + GATE_R + BADGE_R + 2 + i * BADGE_STEP} cy=${cy} r=${BADGE_R} />\n\t\t\t<text class=\"pn-badge-text\" x=${cx + GATE_R + BADGE_R + 2 + i * BADGE_STEP} y=${cy} text-anchor=\"middle\" dominant-baseline=\"central\">${letterFor(m)}</text>\n\t\t</g>`,\n\t);\n}\n\nfunction renderGate(\n\tcx: number,\n\tcy: number,\n\tgate: number,\n\theld: readonly number[],\n\tletterFor: (index: number) => string,\n): TemplateResult {\n\tconst on = held.length > 0;\n\treturn svg`<g>\n\t\t<circle class=\"pn-gate-dot ${on ? 'on' : ''}\" cx=${cx} cy=${cy} r=${GATE_R} />\n\t\t<text class=\"pn-gate ${on ? 'on' : ''}\" x=${cx} y=${cy} text-anchor=\"middle\" dominant-baseline=\"central\">${gate}</text>\n\t\t${renderHolders(cx, cy, held, letterFor)}\n\t</g>`;\n}\n\nexport interface PentaRenderInput {\n\trungs: readonly PentaRung[];\n\tholders: PentaHolders;\n\t/** How a member index is named on the chart, so the badge and the rows below read the same. */\n\tletterFor: (index: number) => string;\n}\n\n/**\n * Render the penta inner content. The caller wraps it in an `<svg>` with\n * {@link PENTA_VIEWBOX} and applies its own theming CSS.\n */\nexport function renderPentaSvg(input: PentaRenderInput): TemplateResult {\n\tconst rows: TemplateResult[] = [];\n\tfor (const upper of [true, false]) {\n\t\tconst inRow = input.rungs.filter((r) => isUpper(r) === upper);\n\t\tconst top = upper ? BAR_H : BARS[1].y + BAR_H;\n\t\tconst bottom = upper ? BARS[1].y : BARS[2].y;\n\t\tconst topCy = top + 26;\n\t\tconst botCy = bottom - 26;\n\t\tconst mid = (topCy + botCy) / 2;\n\t\tinRow.forEach((rung, i) => {\n\t\t\tconst cx = columnX(i, inRow.length);\n\t\t\tconst gTop = topGate(rung);\n\t\t\tconst gBottom = gTop === rung.gateA ? rung.gateB : rung.gateA;\n\t\t\tconst cls = `pn-rung ${rung.defined ? 'on' : ''} ${rung.isCore ? 'core' : ''}`;\n\t\t\trows.push(svg`<g>\n\t\t\t\t<line class=${cls} x1=${cx} y1=${topCy + GATE_R} x2=${cx} y2=${mid - 13} />\n\t\t\t\t<line class=${cls} x1=${cx} y1=${mid + 13} x2=${cx} y2=${botCy - GATE_R} />\n\t\t\t\t<text class=\"pn-name ${rung.defined ? 'on' : ''}\" x=${cx} y=${mid} text-anchor=\"middle\" dominant-baseline=\"central\">${rung.name}</text>\n\t\t\t\t${renderGate(cx, topCy, gTop, input.holders.get(gTop) ?? [], input.letterFor)}\n\t\t\t\t${renderGate(cx, botCy, gBottom, input.holders.get(gBottom) ?? [], input.letterFor)}\n\t\t\t</g>`);\n\t\t});\n\t}\n\treturn svg`${renderBars()}${rows.length > 0 ? rows : nothing}`;\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport type {\n\tCalculateProfileResponse,\n\tCalculateTypeResponse,\n} from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { disclosureStyles } from '../utils/disclosure.js';\nimport {\n\thdReadingStyles,\n\trenderHdFacts,\n\trenderHdKeynotes,\n\trenderHdThemes,\n} from '../utils/hd-reading.js';\nimport { interpAccordionStyles } from '../utils/interp-accordion.js';\nimport { display } from '../utils/localized.js';\n\ntype HdIdentity = CalculateTypeResponse | CalculateProfileResponse;\n\n/**\n * The Human Design identity read on its own, without the chart. Pass `data` from `/human-design/type` or `/human-design/profile`.\n *\n * @remarks\n * Both endpoints answer \"who is this person\" rather than \"how is the chart wired\", so one card serves both and detects which it was given: the type response leads with the type and carries the strategy, authority, and aura readings; the profile response carries the two lines and their keynotes. This is the same shape-detecting pattern `roxy-positions-table` uses across the five Western point-list endpoints.\n *\n * The interpretation surface (fact tiles, lead paragraph, signature and not-self pills, the exclusive reading accordion, the line keynotes) is shared with `roxy-bodygraph` through `utils/hd-reading.ts`, because `/human-design/type` returns a strict subset of the bodygraph interpretation fields. Reach for `roxy-bodygraph` when the chart itself is wanted; reach for this when only the reading is.\n *\n * The type, strategy, authority, signature and not-self values are read through `display()`, so a translated response prints the display half and the canonical English stays available to anything that compares. `/human-design/profile` carries no vocabulary at all: its profile is `5/1` and its two keynotes are prose the API translates in place.\n */\n@customElement('roxy-hd-type-card')\nexport class RoxyHdTypeCard extends RoxyDataElement<HdIdentity> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tdisclosureStyles,\n\t\tinterpAccordionStyles,\n\t\thdReadingStyles,\n\t\tcss`.wrap{background:var(--roxy-surface,#fff);width:100%;color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.head{justify-content:space-between;align-items:baseline;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.type-line{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem)}`,\n\t];\n\n\tprotected renderData(d: HdIdentity) {\n\t\treturn isTypeShape(d) ? this.renderType(d) : this.renderProfile(d);\n\t}\n\n\t/**\n\t * The type read. The tiles answer it at a glance, the lead paragraph grounds the type label in what the aura actually does, and the accordion carries the three interpretations that make the chart actionable: how to engage (strategy), how to decide (authority), and how the field is felt by others (aura).\n\t */\n\tprivate renderType(d: CalculateTypeResponse) {\n\t\treturn html`<div class=\"wrap\" part=\"card\"><header class=\"head\" part=\"header\"><h2 class=\"title\">${this.t('Type')}</h2>${\n\t\t\t\t\td.type || d.profile\n\t\t\t\t\t\t? html`<div class=\"type-line\">${[\n\t\t\t\t\t\t\t\tdisplay(d, 'type'),\n\t\t\t\t\t\t\t\td.profile\n\t\t\t\t\t\t\t\t\t? this.t('Profile {{profile}}', { profile: d.profile })\n\t\t\t\t\t\t\t\t\t: '',\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t\t\t\t.join(' · ')}</div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</header>${renderHdFacts([\n\t\t\t\t{ label: this.t('Type'), value: display(d, 'type') },\n\t\t\t\t{ label: this.t('Strategy'), value: display(d, 'strategy') },\n\t\t\t\t{ label: this.t('Authority'), value: display(d, 'authority') },\n\t\t\t\t{ label: this.t('Profile'), value: d.profile },\n\t\t\t])} ${\n\t\t\t\t// The tiles above name the type; this paragraph explains it.\n\t\t\t\td.typeDescription && !this.hideReadings\n\t\t\t\t\t? html`<p class=\"lead\">${d.typeDescription}</p>`\n\t\t\t\t\t: nothing\n\t\t\t} ${renderHdThemes(\n\t\t\t\td.signature ? display(d, 'signature') : undefined,\n\t\t\t\td.notSelf ? display(d, 'notSelf') : undefined,\n\t\t\t\tthis.translator,\n\t\t\t)} ${this.renderInterpretation(\n\t\t\t\t[\n\t\t\t\t\t{\n\t\t\t\t\t\tlabel: this.t('Strategy'),\n\t\t\t\t\t\taside: display(d, 'strategy'),\n\t\t\t\t\t\tbody: d.strategyDescription,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tlabel: this.t('Authority'),\n\t\t\t\t\t\taside: display(d, 'authority'),\n\t\t\t\t\t\tbody: d.authorityDescription,\n\t\t\t\t\t},\n\t\t\t\t\t{ label: this.t('Aura'), body: d.aura },\n\t\t\t\t],\n\t\t\t\t'hd-type-reading',\n\t\t\t)}</div>`;\n\t}\n\n\t/**\n\t * The profile read. Two keynote sentences, so they render open as a definition list rather than behind a disclosure: putting a single sentence behind a click costs the reader more than it saves.\n\t */\n\tprivate renderProfile(d: CalculateProfileResponse) {\n\t\treturn html`<div class=\"wrap\" part=\"card\"><header class=\"head\" part=\"header\"><h2 class=\"title\">${this.t('Profile')}</h2>${d.profile ? html`<div class=\"type-line\">${d.profile}</div>` : nothing}</header>${renderHdFacts([\n\t\t\t\t{ label: this.t('Profile'), value: d.profile },\n\t\t\t\t{\n\t\t\t\t\tlabel: this.t('Personality line'),\n\t\t\t\t\tvalue: d.personalityLine?.toString(),\n\t\t\t\t},\n\t\t\t\t{ label: this.t('Design line'), value: d.designLine?.toString() },\n\t\t\t])} ${\n\t\t\t\t// The section holds nothing but the two keynote sentences, and the line\n\t\t\t\t// numbers are already tiles above it, so it goes whole.\n\t\t\t\tthis.hideReadings\n\t\t\t\t\t? nothing\n\t\t\t\t\t: html`<section class=\"block\" part=\"section lines\"><h3>${this.t('Lines')}</h3>${renderHdKeynotes(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tpersonality: d.personalityKeynote,\n\t\t\t\t\t\t\t\tpersonalityLine: d.personalityLine,\n\t\t\t\t\t\t\t\tdesign: d.designKeynote,\n\t\t\t\t\t\t\t\tdesignLine: d.designLine,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tthis.translator,\n\t\t\t\t\t\t)}</section>`\n\t\t\t}</div>`;\n\t}\n}\n\n/** The two responses share only `profile`, so the type field is what tells them apart. */\nfunction isTypeShape(d: HdIdentity): d is CalculateTypeResponse {\n\treturn typeof (d as CalculateTypeResponse).type === 'string';\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-hd-type-card': RoxyHdTypeCard;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport type { CalculateVariablesResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { chevron, disclosureStyles } from '../utils/disclosure.js';\nimport { sideWord } from '../utils/hd-reading.js';\nimport { interpAccordionStyles } from '../utils/interp-accordion.js';\nimport { display } from '../utils/localized.js';\n\ntype Variables = CalculateVariablesResponse;\ntype Arrow = Variables['arrows'][number];\n\n/**\n * Human Design variables (the four arrows / PHS). Renders /human-design/variables: the four transformation arrows laid out as they sit on the bodygraph head (top-left/right Determination + Motivation, bottom-left/right Environment + Perspective), each showing its left/right direction, the digestion/environment/awareness/perspective labels, and its color/tone/base.\n *\n * @remarks\n * The quadrant grid stays the primary read: it is the arrow map as it appears on the chart, and it is what a reader scans first. The interpretation sits below it in an exclusive accordion, grouped by the two layers the arrows belong to (the body-side Primary Health System, the mind-side Rave Psychology), because the layer description is one text shared by the two arrows in it and belongs to the group rather than to each arrow. Only one arrow reading is ever open, so the card grows by one paragraph at most.\n *\n * `cognition` rides only on the determination arrow and is rendered when present. A low-confidence calculation (a birth time near a color or tone boundary) is flagged per arrow and for the chart as a whole.\n *\n * **Seven of the fields on an arrow come back twice**, canonical English plus a `*Localized` display copy (`name`, `layer`, `position`, `colorLabel`, `directionLabel`, `baseName` and the cognition `label`), so each is printed through `display()`. Everything the card COMPUTES with stays on the canonical half: `quadrantOrder` sorts on `position`, the layer grouping keys on `layer`, and the arrow glyph is chosen on `direction`, which is the wire enum `left` or `right` rather than a word. The activating body is the only vocabulary here with no localized partner and prints as sent.\n */\n@customElement('roxy-hd-variables')\nexport class RoxyHdVariables extends RoxyDataElement<Variables> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tdisclosureStyles,\n\t\tinterpAccordionStyles,\n\t\tcss`.wrap{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.grid{gap:var(--roxy-space-md,1rem);grid-template-columns:repeat(2,minmax(0,1fr));display:grid}.arrow{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-md,1rem);gap:var(--roxy-space-xs,.25rem);align-content:start;display:grid}.arrow-head{align-items:center;gap:var(--roxy-space-sm,.5rem);display:flex}.glyph{color:var(--roxy-accent-ink,#b45309);font-size:1.75rem;line-height:1}.name{font-weight:var(--roxy-weight-bold,600)}.layer{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.05em}.labels{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a)}.ctb{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);font-variant-numeric:tabular-nums}.note{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-warning-fg,#9a3412)}@container (width<=28rem){.grid{grid-template-columns:minmax(0,1fr)}}.group{margin-bottom:var(--roxy-space-md,1rem)}.group:last-of-type{margin-bottom:0}.group-head{font-size:var(--roxy-text-sm,.875rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-fg,#0a0a0a);margin:0}.group-note{margin:0 0 var(--roxy-space-sm,.5rem);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);line-height:1.6}.facets{gap:var(--roxy-space-xs,.25rem);margin:0;display:grid}.facets dt{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.05em}.facets dd{margin:0}.footnote{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);margin:0;line-height:1.6}`,\n\t];\n\n\tprotected renderData(d: Variables) {\n\t\t// Place the arrows by their bodygraph `position`, not response order: the\n\t\t// 2-col grid fills row-major, so sorting to Top left, Top right, Bottom left,\n\t\t// Bottom right keeps the design column (Determination + Environment) on the\n\t\t// left and the personality column (Motivation + Perspective) on the right,\n\t\t// which is the whole point of the four-arrow layout.\n\t\tconst arrows = [...(d.arrows ?? [])].sort(\n\t\t\t(a, b) => quadrantOrder(a.position) - quadrantOrder(b.position),\n\t\t);\n\t\treturn html`<div class=\"wrap\" part=\"card\" aria-label=\"${this.t('Human Design variables')}\"><h2 class=\"title\" part=\"header\">${this.t('Variables')}</h2><div class=\"grid\" part=\"details arrows\">${arrows.map((a) => this.renderArrow(a))}</div>${\n\t\t\t\td.confident === false\n\t\t\t\t\t? html`<p class=\"note\" role=\"note\">${\n\t\t\t\t\t\t\ttypeof d.confidenceMarginDeg === 'number'\n\t\t\t\t\t\t\t\t? this.t(\n\t\t\t\t\t\t\t\t\t\t'Low confidence: a birth time near a color or tone boundary (within {{margin}}°). Verify the exact birth time.',\n\t\t\t\t\t\t\t\t\t\t{ margin: d.confidenceMarginDeg },\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t: this.t(\n\t\t\t\t\t\t\t\t\t\t'Low confidence: a birth time near a color or tone boundary. Verify the exact birth time.',\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}</p>`\n\t\t\t\t\t: nothing\n\t\t\t} ${this.renderReading(arrows, d.baseDescription)}</div>`;\n\t}\n\n\tprivate renderArrow(a: Arrow) {\n\t\t// A left arrow is strategic/active, a right arrow receptive/passive in HD.\n\t\t// The glyph is chosen on `direction`, which is the wire enum and stays\n\t\t// `left` or `right` in every language; `directionLabel` is the word beside\n\t\t// it and is read through `display`.\n\t\tconst glyph = a.direction === 'left' ? '←' : '→';\n\t\tconst baseName = display(a, 'baseName');\n\t\treturn html`<div class=\"arrow\"><div class=\"arrow-head\"><span class=\"glyph\" aria-hidden=\"true\">${glyph}</span> <span class=\"name\">${display(a, 'name')}</span></div>${a.layer ? html`<span class=\"layer\">${display(a, 'layer')}</span>` : nothing} <span class=\"labels\">${[display(a, 'directionLabel'), display(a, 'colorLabel')].filter(Boolean).join(' · ')} </span>${\n\t\t\t\ttypeof a.color === 'number'\n\t\t\t\t\t? html`<span class=\"ctb\">${this.t(\n\t\t\t\t\t\t\t'Color {{color}} · Tone {{tone}} · Base {{base}}',\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tcolor: a.color,\n\t\t\t\t\t\t\t\ttone: a.tone ?? '',\n\t\t\t\t\t\t\t\tbase: a.base ?? '',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t)}${baseName ? `, ${baseName}` : ''}</span>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\t// The activating body is the one word on this card the response does not\n\t\t\t\t// localize, so it prints as sent. Its `side` is the machine enum\n\t\t\t\t// `personality` or `design`, which the component turns into a word, so\n\t\t\t\t// that half comes from the catalogue.\n\t\t\t\ta.activation?.planet\n\t\t\t\t\t? html`<span class=\"ctb\">${[a.activation.planet, sideWord(a.activation.side, this.translator)].filter(Boolean).join(' · ')}</span>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\ta.confident === false\n\t\t\t\t\t? html`<span class=\"note\" role=\"note\">${this.t('Knife-edge: could flip with a more precise birth time.')}</span>`\n\t\t\t\t\t: nothing\n\t\t\t}</div>`;\n\t}\n\n\t/**\n\t * The four readings, grouped by the layer each arrow belongs to. The layer\n\t * description is one text shared by both arrows of a layer, so it is lifted to\n\t * the group intro rather than repeated in each body. Group order follows the\n\t * already quadrant-sorted arrows, which puts the body layer before the mind\n\t * layer without naming either.\n\t *\n\t * Prose end to end, and the arrows themselves are already tiled above with\n\t * their direction, color, tone and base, so `hide-readings` takes the section\n\t * whole.\n\t */\n\tprivate renderReading(arrows: Arrow[], baseDescription: string | undefined) {\n\t\tconst readable = arrows.filter((a) => a.description || a.colorMeaning);\n\t\tif (readable.length === 0 || this.hideReadings) return nothing;\n\n\t\tconst groups = new Map<string, Arrow[]>();\n\t\tfor (const a of readable) {\n\t\t\tconst key = a.layer ?? '';\n\t\t\tconst bucket = groups.get(key);\n\t\t\tif (bucket) bucket.push(a);\n\t\t\telse groups.set(key, [a]);\n\t\t}\n\t\tlet index = 0;\n\n\t\treturn html`<section class=\"block\" part=\"section readings\"><h3>${this.t('Reading')}</h3>${[...groups].map(\n\t\t\t\t([layer, list]) => html`<div class=\"group\">${layer ? html`<p class=\"group-head\">${display(list[0], 'layer')}</p>` : nothing} ${\n\t\t\t\t\t\tlist[0]?.layerDescription\n\t\t\t\t\t\t\t? html`<p class=\"group-note\">${list[0].layerDescription}</p>`\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t} ${list.map((a) => this.renderArrowReading(a, index++ === 0))}</div>`,\n\t\t\t)} ${baseDescription ? html`<p class=\"footnote\">${this.t('Base')}. ${baseDescription}</p>` : nothing}</section>`;\n\t}\n\n\tprivate renderArrowReading(a: Arrow, open: boolean) {\n\t\t// Cognition rides only on the determination arrow, so its row appears on that\n\t\t// arrow alone. Its label joins the term because, unlike color and direction,\n\t\t// the tile above carries no cognition label to read it against.\n\t\tconst cog = a.cognition;\n\t\tconst cogLabel = cog ? display(cog, 'label') : '';\n\t\tconst facets: Array<{ label: string; body: string | undefined }> = [\n\t\t\t{ label: this.t('Color'), body: a.colorMeaning },\n\t\t\t{ label: this.t('Tone'), body: a.toneMeaning },\n\t\t\t{ label: this.t('Direction'), body: a.directionMeaning },\n\t\t\t{\n\t\t\t\tlabel: cogLabel\n\t\t\t\t\t? `${this.t('Cognition')} · ${cogLabel}`\n\t\t\t\t\t: this.t('Cognition'),\n\t\t\t\tbody: cog?.description,\n\t\t\t},\n\t\t].filter((f) => Boolean(f.body));\n\n\t\treturn html`<details class=\"interp-card\" part=\"reading\" name=\"hd-variable\" ?open=\"${open}\"><summary><span class=\"interp-lead\">${display(a, 'name')}</span> ${chevron()} ${a.position ? html`<span class=\"interp-aside\"><small>${display(a, 'position')}</small></span>` : nothing}</summary><div class=\"interp-body\">${a.description ? html`<p>${a.description}</p>` : nothing} ${\n\t\t\t\t\tfacets.length > 0\n\t\t\t\t\t\t? html`<dl class=\"facets\">${facets.map(\n\t\t\t\t\t\t\t\t(f) => html`<dt>${f.label}</dt><dd>${f.body}</dd>`,\n\t\t\t\t\t\t\t)}</dl>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</div></details>`;\n\t}\n}\n\n/** Canonical bodygraph reading order for the four arrows, so the 2-col grid lays them out by quadrant. Unknown positions sort last. */\nfunction quadrantOrder(position: string | undefined): number {\n\tswitch (position) {\n\t\tcase 'Top left':\n\t\t\treturn 0;\n\t\tcase 'Top right':\n\t\t\treturn 1;\n\t\tcase 'Bottom left':\n\t\t\treturn 2;\n\t\tcase 'Bottom right':\n\t\t\treturn 3;\n\t\tdefault:\n\t\t\treturn 99;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-hd-variables': RoxyHdVariables;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport type { ChromeString } from '../i18n/chrome-strings.js';\nimport { planetGlyph } from '../tokens/index.js';\nimport type { HeliacalResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { formatDateTime, formatNumber } from '../utils/format.js';\n\ntype Graha = HeliacalResponse['grahas'][number];\ntype Event = NonNullable<Graha['lastEvent']>;\n\n/** Udaya is the graha re-emerging from the Sun rays, asta is it disappearing into them. Sanskrit keys, never translated by the API, so the display word is ours to choose. */\nconst EVENT_WORD: Record<Event['type'], ChromeString> = {\n\tudaya: 'Rose',\n\tasta: 'Set',\n};\n\n/** Forward-looking form of the same two events, for the NEXT event rather than the last one. */\nconst NEXT_EVENT_WORD: Record<Event['type'], ChromeString> = {\n\tudaya: 'rises',\n\tasta: 'sets',\n};\n\n/**\n * Heliacal rising and setting (udaya and asta) for the six grahas that rise and set.\n *\n * @remarks\n * This is the muhurta surface: Guru Asta and Shukra Asta are the periods classical muhurta withholds marriage, so what a practitioner opens this for is a WINDOW, not a status. The window is therefore the headline of every row and the present verdict is the supporting line, because \"invisible until 12 August\" answers the question and \"invisible\" alone does not.\n *\n * Three things here are easy to render wrongly, and each is deliberate:\n *\n * `lastEvent` and `nextEvent` are genuinely NULLABLE, not merely optional. A graha far from conjunction can have no event inside the search horizon at all, which is normal for Mars, so a missing event is reported as its own state rather than as a blank date.\n *\n * The `kalamsa` limit on an EVENT can differ from the graha's CURRENT `kalamsa`, because the Surya Siddhanta gives Mercury and Venus a tighter limit while retrograde. Rendering one and labelling it the other would be a quiet lie, so each is shown against the event it belongs to.\n *\n * `horizon` is a real distinction a reader needs, not an internal detail: east means the graha is read before sunrise and is a morning object, west means after sunset and an evening one. A graha crosses to the other horizon as it passes the Sun, which is why an asta and the udaya after it usually sit on opposite horizons.\n */\n@customElement('roxy-heliacal-table')\nexport class RoxyHeliacalTable extends RoxyDataElement<HeliacalResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.wrap{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);background:var(--roxy-surface,#fff);box-shadow:var(--roxy-shadow-sm);overflow:hidden}.head{padding:var(--roxy-space-md,1rem);border-bottom:1px solid var(--roxy-border,#e4e4e7);gap:var(--roxy-space-xs,.25rem);display:grid}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.sub{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);margin:0}.rows{display:grid}.row{gap:var(--roxy-space-xs,.25rem);padding:var(--roxy-space-md,1rem);border-top:1px solid var(--roxy-border,#e4e4e7);grid-template-columns:minmax(0,1fr);display:grid}.row-top{align-items:baseline;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.graha{font-weight:var(--roxy-weight-bold,600);color:var(--roxy-fg,#0a0a0a)}.glyph{color:var(--roxy-muted,#71717a);margin-right:.35em}.state{font-size:var(--roxy-text-xs,.75rem);border-radius:var(--roxy-radius-sm,4px);border:1px solid var(--roxy-border,#e4e4e7);color:var(--roxy-fg,#0a0a0a);padding:.1rem .45rem}.state.invisible{background:color-mix(in srgb, var(--roxy-warning,#f59e0b) 14%, transparent)}.retro{color:var(--roxy-warning-fg,#9a3412);font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600)}.window{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a);margin:0}.meta{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);line-height:var(--roxy-leading-normal,1.5);margin:0}.num{font-variant-numeric:tabular-nums}`,\n\t];\n\n\tprotected renderData(d: HeliacalResponse) {\n\t\tconst grahas = d.grahas ?? [];\n\t\tif (!grahas.length) return this.renderEmpty();\n\n\t\treturn html`<div class=\"wrap\" part=\"card\" aria-label=\"${this.t('Heliacal visibility')}\"><header class=\"head\" part=\"header\"><h2 class=\"title\">${this.t('Heliacal rising and setting')}</h2><p class=\"sub\">${this.t(\n\t\t\t\t\t\t'Whether each graha stands far enough from the Sun to be seen, for {{date}}. The Sun and the nodes never appear here: they have no heliacal event.',\n\t\t\t\t\t\t{ date: d.date },\n\t\t\t\t\t)}</p></header><div class=\"rows\" part=\"table\">${grahas.map((g) => this.renderGraha(g))}</div></div>`;\n\t}\n\n\tprivate renderGraha(g: Graha) {\n\t\tconst glyph = planetGlyph(g.graha) ?? '';\n\t\treturn html`<article class=\"row\"><div class=\"row-top\"><span class=\"graha\">${glyph ? html`<span class=\"glyph\">${glyph}</span>` : nothing}${g.graha} </span><span class=\"state ${g.visible ? 'visible' : 'invisible'}\">${g.visible ? this.t('Visible') : this.t('Invisible')} </span>${\n\t\t\t\t\tg.retrograde\n\t\t\t\t\t\t? html`<span class=\"retro\" title=\"${this.t('retrograde')}\">R</span>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</div><p class=\"window\" part=\"window\">${this.windowLine(g)}</p><p class=\"meta\" part=\"details\">${this.metaLine(g)}</p></article>`;\n\t}\n\n\t/**\n\t * The headline: what a reader came for. Leads with the NEXT event, since that is the date a muhurta decision turns on, and falls back to the last one when nothing lies ahead inside the search horizon.\n\t */\n\tprivate windowLine(g: Graha) {\n\t\tconst next = g.nextEvent;\n\t\tconst last = g.lastEvent;\n\t\tif (next) {\n\t\t\tconst vars = {\n\t\t\t\tevent: this.t(NEXT_EVENT_WORD[next.type]),\n\t\t\t\twhere:\n\t\t\t\t\tnext.horizon === 'east'\n\t\t\t\t\t\t? this.t('in the east')\n\t\t\t\t\t\t: this.t('in the west'),\n\t\t\t\twhen: formatDateTime(this.effectiveLang(), next.datetime),\n\t\t\t};\n\t\t\treturn html`${\n\t\t\t\tg.visible\n\t\t\t\t\t? this.t('Visible until it {{event}} {{where}} on {{when}}', vars)\n\t\t\t\t\t: this.t('Invisible until it {{event}} {{where}} on {{when}}', vars)\n\t\t\t}`;\n\t\t}\n\t\tif (last) {\n\t\t\treturn html`${this.t(\n\t\t\t\t'{{event}} {{where}} on {{when}}, with no further event inside the search window',\n\t\t\t\t{\n\t\t\t\t\tevent: this.t(EVENT_WORD[last.type]),\n\t\t\t\t\twhere:\n\t\t\t\t\t\tlast.horizon === 'east'\n\t\t\t\t\t\t\t? this.t('in the east')\n\t\t\t\t\t\t\t: this.t('in the west'),\n\t\t\t\t\twhen: formatDateTime(this.effectiveLang(), last.datetime),\n\t\t\t\t},\n\t\t\t)}`;\n\t\t}\n\t\treturn html`${this.t(\n\t\t\t'No rising or setting inside the search window, which is normal for a graha far from the Sun',\n\t\t)}`;\n\t}\n\n\t/**\n\t * The supporting line: the measured separation against the limit it is judged by, plus which horizon the graha belongs to.\n\t *\n\t * Both numbers are degrees of TIME in oblique ascension rather than a plain longitude gap, which is the measure the Surya Siddhanta defines these limits in and the reason the verdict depends on latitude at all.\n\t *\n\t * @remarks\n\t * The differing-limit note is derived by COMPARING the two numbers the response carries, never by naming which grahas have a retrograde variant. Only Mercury and Venus do: ix.7-8 gives them 12 or 14 and 8 or 10, while Jupiter, Saturn, Mars and the Moon each have one limit whatever their motion. Tying the note to retrograde motion instead would claim a variant limit for a retrograde Jupiter, which does not exist, and a practitioner reads that as the component not knowing the rule. Comparing the data cannot make that mistake and needs no copy of the table.\n\t */\n\tprivate metaLine(g: Graha) {\n\t\tconst side =\n\t\t\tg.horizon === 'east'\n\t\t\t\t? this.t('a morning graha, read before sunrise')\n\t\t\t\t: this.t('an evening graha, read after sunset');\n\t\tconst at = g.nextEvent ?? g.lastEvent;\n\t\tconst shifts = at && at.kalamsa !== g.kalamsa;\n\t\tconst vars = {\n\t\t\tdegrees: formatNumber(this.effectiveLang(), g.timeDegrees, 2),\n\t\t\tlimit: formatNumber(this.effectiveLang(), g.kalamsa, 0),\n\t\t\tshifted: shifts ? formatNumber(this.effectiveLang(), at.kalamsa, 0) : '',\n\t\t};\n\t\t// Two whole sentences rather than a stem and an appended clause: the shift\n\t\t// only applies to Mercury and Venus, and a fragment cannot be translated.\n\t\treturn html`${\n\t\t\tshifts\n\t\t\t\t? this.t(\n\t\t\t\t\t\t'{{degrees}}° of time from the Sun against a limit of {{limit}}°, becoming {{shifted}}° at that event',\n\t\t\t\t\t\tvars,\n\t\t\t\t\t)\n\t\t\t\t: this.t(\n\t\t\t\t\t\t'{{degrees}}° of time from the Sun against a limit of {{limit}}°',\n\t\t\t\t\t\tvars,\n\t\t\t\t\t)\n\t\t} · ${side}`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-heliacal-table': RoxyHeliacalTable;\n\t}\n}\n", "import { css, html, nothing, svg } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport { trigramGlyph } from '../tokens/index.js';\nimport type {\n\tCastReadingResponse,\n\tGetDailyHexagramResponse,\n\tGetHexagramResponse,\n\tGetRandomHexagramResponse,\n\tHexagram,\n\tLookupHexagramResponse,\n} from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { disclosureStyles } from '../utils/disclosure.js';\nimport {\n\ttype InterpSection,\n\tinterpAccordionStyles,\n} from '../utils/interp-accordion.js';\n\ntype HexagramData =\n\t| GetHexagramResponse\n\t| GetRandomHexagramResponse\n\t| LookupHexagramResponse\n\t| GetDailyHexagramResponse\n\t| CastReadingResponse;\n\n/**\n * I Ching hexagram card. Renders /iching/hexagrams/{number}, /iching/cast,\n * /iching/daily, /iching/daily/cast.\n *\n * @remarks\n * **There is deliberately no `mode` input.** Which of the four shapes arrived is\n * shape-detected from the payload in {@link RoxyHexagram.resolveHexagram}, which\n * is the rule for every multi-endpoint component here: an attribute and a\n * response can disagree, and only one of them is the reading. A `mode` property\n * shipped anyway, reflected and typed into both framework wrappers, and nothing\n * ever read it.\n */\n@customElement('roxy-hexagram')\nexport class RoxyHexagram extends RoxyDataElement<HexagramData> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tdisclosureStyles,\n\t\tinterpAccordionStyles,\n\t\tcss`.card{background:var(--roxy-surface,#fff);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-lg,1.5rem);grid-template-columns:6rem 1fr;display:grid}@container (width<=480px){.card{grid-template-columns:1fr}}.glyphs{gap:var(--roxy-space-md,1rem);align-content:start;align-self:start;justify-items:center;display:grid}.symbol{color:var(--roxy-accent-ink,#b45309);font-size:3rem;line-height:1}.lines{gap:4px;width:4rem;display:grid}.line{justify-content:center;align-items:center;gap:4px;height:8px;display:flex}.seg{background:var(--roxy-fg,#0a0a0a);border-radius:1px;height:6px;display:block}.line.broken .seg{width:1.4rem}.line.solid .seg{width:3rem}.line.changing .seg{background:var(--roxy-accent,#f59e0b)}.title{font-size:var(--roxy-text-xl,1.5rem);font-weight:var(--roxy-weight-bold,600);letter-spacing:var(--roxy-tracking-tight);margin:0}.subtitle{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem);margin:0 0 var(--roxy-space-sm,.5rem)}.trigrams{gap:var(--roxy-space-md,1rem);margin-bottom:var(--roxy-space-sm,.5rem);color:var(--roxy-secondary,#475569);font-size:var(--roxy-text-xs,.75rem);text-transform:uppercase;letter-spacing:.06em;display:flex}.tri-glyph{font-size:var(--roxy-text-xl,1.5rem);color:var(--roxy-accent-ink,#b45309);vertical-align:middle;margin-right:4px}.judgment,.image,.message{margin:0 0 var(--roxy-space-sm,.5rem);font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a)}.judgment:before{content:\"Judgment. \";font-weight:var(--roxy-weight-bold,600);color:var(--roxy-secondary,#475569)}.image:before{content:\"Image. \";font-weight:var(--roxy-weight-bold,600);color:var(--roxy-secondary,#475569)}.line-meaning{padding-left:var(--roxy-space-sm,.5rem);border-left:2px solid var(--roxy-border,#e4e4e7);color:var(--roxy-muted,#71717a);margin:0}.changing{margin-top:var(--roxy-space-md,1rem);padding-top:var(--roxy-space-md,1rem);border-top:1px solid var(--roxy-border,#e4e4e7);color:var(--roxy-accent-ink,#b45309);font-size:var(--roxy-text-sm,.875rem)}`,\n\t];\n\n\tprivate resolveHexagram(): {\n\t\thex: Hexagram;\n\t\tlines?: number[];\n\t\tchangingLinePositions?: number[];\n\t\tdailyMessage?: string;\n\t\tresultingHexagram?: Hexagram;\n\t} | null {\n\t\tconst d = this.data;\n\t\tif (!d) return null;\n\t\tif ('hexagram' in d && d.hexagram) {\n\t\t\tif ('lines' in d) {\n\t\t\t\tconst cast = d as CastReadingResponse;\n\t\t\t\treturn {\n\t\t\t\t\thex: cast.hexagram as Hexagram,\n\t\t\t\t\tlines: cast.lines,\n\t\t\t\t\tchangingLinePositions: cast.changingLinePositions,\n\t\t\t\t\tresultingHexagram: cast.resultingHexagram as Hexagram | undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst daily = d as GetDailyHexagramResponse;\n\t\t\treturn {\n\t\t\t\thex: daily.hexagram as Hexagram,\n\t\t\t\tdailyMessage: daily.dailyMessage,\n\t\t\t};\n\t\t}\n\t\treturn { hex: d as Hexagram };\n\t}\n\n\tprotected renderData() {\n\t\tconst resolved = this.resolveHexagram();\n\t\tif (!resolved) return this.renderEmpty();\n\n\t\tconst {\n\t\t\thex: h,\n\t\t\tlines: castLines,\n\t\t\tchangingLinePositions,\n\t\t\tdailyMessage,\n\t\t\tresultingHexagram,\n\t\t} = resolved;\n\t\tconst lines = castLines ?? this.derivedLines(h);\n\t\tconst changing = new Set(changingLinePositions ?? []);\n\n\t\tconst readings = !this.hideReadings;\n\n\t\treturn html`<article class=\"card\" part=\"card\" aria-label=\"${this.t('I Ching hexagram')}\"><div class=\"glyphs\" part=\"chart\">${h.symbol ? html`<div class=\"symbol\">${h.symbol}</div>` : nothing}<div class=\"lines\" aria-hidden=\"true\">${lines\n\t\t\t\t\t\t.slice()\n\t\t\t\t\t\t.reverse()\n\t\t\t\t\t\t.map((l, idx) => {\n\t\t\t\t\t\t\t// reverse so visual top is line 6\n\t\t\t\t\t\t\tconst realIdx = lines.length - 1 - idx + 1;\n\t\t\t\t\t\t\tconst isChanging = changing.has(realIdx);\n\t\t\t\t\t\t\tconst broken = l === 6 || l === 8;\n\t\t\t\t\t\t\tconst cls = `${broken ? 'broken' : 'solid'}${isChanging ? ' changing' : ''}`;\n\t\t\t\t\t\t\treturn html`<div class=\"line ${cls}\">${\n\t\t\t\t\t\t\t\t\tbroken\n\t\t\t\t\t\t\t\t\t\t? svg`<span class=\"seg\"></span><span class=\"seg\"></span>`\n\t\t\t\t\t\t\t\t\t\t: svg`<span class=\"seg\"></span>`\n\t\t\t\t\t\t\t\t}</div>`;\n\t\t\t\t\t\t})}</div></div><div part=\"header\"><h2 class=\"title\">${\n\t\t\t\t\t\t// One text node, not two. The markup minifier collapses the space\n\t\t\t\t\t\t// between adjacent template expressions, which rendered the title as\n\t\t\t\t\t\t// \"30.The Clinging\" with the separator eaten. Join in JS instead.\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\th.number != null ? `${h.number}.` : '',\n\t\t\t\t\t\t\th.english ?? h.chinese ?? 'Hexagram',\n\t\t\t\t\t\t]\n\t\t\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t\t\t.join(' ')\n\t\t\t\t\t}</h2><p class=\"subtitle\">${h.chinese ? html`${h.chinese}` : nothing} ${h.pinyin ? html`· ${h.pinyin}` : nothing}</p><div class=\"trigrams\" part=\"details\">${\n\t\t\t\t\t\th.upperTrigram\n\t\t\t\t\t\t\t? html`<div>${this.t('Upper')} <span class=\"tri-glyph\">${trigramGlyph(h.upperTrigram) ?? ''}</span>${h.upperTrigram}</div>`\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t} ${\n\t\t\t\t\t\th.lowerTrigram\n\t\t\t\t\t\t\t? html`<div>${this.t('Lower')} <span class=\"tri-glyph\">${trigramGlyph(h.lowerTrigram) ?? ''}</span>${h.lowerTrigram}</div>`\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t}</div>${\n\t\t\t\t\t// The Judgment and the Image are the written oracle of the figure, so\n\t\t\t\t\t// they go with the readings; the figure, the trigrams and which lines\n\t\t\t\t\t// are moving are what stays.\n\t\t\t\t\th.judgment && readings\n\t\t\t\t\t\t? html`<p class=\"judgment\">${h.judgment}</p>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t} ${h.image && readings ? html`<p class=\"image\">${h.image}</p>` : nothing} ${dailyMessage && readings ? html`<p class=\"message\">${dailyMessage}</p>` : nothing} ${\n\t\t\t\t\th.interpretation?.general && readings\n\t\t\t\t\t\t? html`<p>${h.interpretation.general}</p>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t} ${\n\t\t\t\t\tchanging.size > 0\n\t\t\t\t\t\t? html`<div class=\"changing\" part=\"section changing-lines\">${this.t('Changing lines: {{lines}}.', {\n\t\t\t\t\t\t\t\tlines: Array.from(changing)\n\t\t\t\t\t\t\t\t\t.sort((a, b) => a - b)\n\t\t\t\t\t\t\t\t\t.join(', '),\n\t\t\t\t\t\t\t})} ${\n\t\t\t\t\t\t\t\tresultingHexagram?.english\n\t\t\t\t\t\t\t\t\t? html`${this.t('Becomes hexagram {{number}} {{name}}.', {\n\t\t\t\t\t\t\t\t\t\t\tnumber: resultingHexagram.number,\n\t\t\t\t\t\t\t\t\t\t\tname: resultingHexagram.english,\n\t\t\t\t\t\t\t\t\t\t})}`\n\t\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t\t}</div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t} ${this.renderLines(h, changing)}</div></article>`;\n\t}\n\n\t/**\n\t * The line readings. Each line carries its oracle statement and, since the 2026-07 API rewrite, a written meaning. Both are rendered: a bare \"Changing lines: 3\" leaves a reader no way to know what line 3 is telling them.\n\t *\n\t * @remarks\n\t * When lines are moving, only those lines are shown: a cast turns on the moving lines, and listing the other five buries the answer. With no lines moving (a lookup, a random draw, the daily hexagram) all six are shown, because there the hexagram is being read as a reference rather than as an answer to a question.\n\t */\n\tprivate renderLines(h: Hexagram, changing: Set<number>) {\n\t\tconst all = h.changingLines ?? [];\n\t\tif (all.length === 0) return nothing;\n\n\t\tconst isCast = changing.size > 0;\n\t\tconst shown = isCast ? all.filter((l) => changing.has(l.position)) : all;\n\t\tif (shown.length === 0) return nothing;\n\n\t\tconst sections: InterpSection[] = shown.map((l) => ({\n\t\t\tlabel: `Line ${l.position}`,\n\t\t\tbody: l.text ?? '',\n\t\t\textra: l.meaning\n\t\t\t\t? html`<p class=\"line-meaning\">${l.meaning}</p>`\n\t\t\t\t: nothing,\n\t\t}));\n\n\t\treturn this.renderInterpretation(\n\t\t\tsections,\n\t\t\t'hexagram-lines',\n\t\t\tisCast ? 'Changing lines' : 'Lines',\n\t\t);\n\t}\n\n\t/**\n\t * Lines for a static hexagram (lookup/random/daily, which carry no cast `lines` array): read the `binary` pattern. 6 digits BOTTOM to top, so index 0 is line 1, the bottom line, exactly like the cast `lines` array. 1 = yang (solid, 7), 0 = yin (broken, 8). Falls back to all-yang only if `binary` is malformed. The Unicode `symbol` block (U+4DC0) is in King Wen order, NOT line order, so it must never be used to derive the lines.\n\t *\n\t * @remarks\n\t * This used to `.reverse()`, because the API served `binary` top-to-bottom while documenting it bottom-to-top, and reversing was the only way to render the figure the right way up. The API fixed the data in 2026-07 (the same inversion was making `/cast` return the vertically MIRRORED hexagram for 56 of the 64 figures), so `binary` and `lines` now point the same way and the reverse would flip every asymmetric hexagram upside down. Do not put it back.\n\t */\n\tprivate derivedLines(h: Hexagram): number[] {\n\t\tconst binary = h.binary ?? '';\n\t\tif (/^[01]{6}$/.test(binary)) {\n\t\t\treturn Array.from(binary, (c) => (c === '1' ? 7 : 8));\n\t\t}\n\t\treturn Array.from({ length: 6 }, () => 7);\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-hexagram': RoxyHexagram;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport { planetGlyph } from '../tokens/index.js';\nimport type { GetHoraResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { formatDate, formatTimeRange } from '../utils/format.js';\n\ntype HoraPeriod = NonNullable<GetHoraResponse['dayHoras']>[number];\n\n/**\n * Vedic Hora (planetary hours) table. Renders /vedic-astrology/panchang/hora: the 12 daytime and 12 nighttime horas, each ruled by one of the seven classical planets in the Chaldean order, used for electional timing (start a venture in the hora of the planet that favours it). Day horas run sunrise to sunset, night horas sunset to next sunrise; each tile shows the ruling planet and its wall-clock window.\n */\n@customElement('roxy-hora-table')\nexport class RoxyHoraTable extends RoxyDataElement<GetHoraResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.wrap{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.head{justify-content:space-between;align-items:baseline;gap:var(--roxy-space-md,1rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.subtitle{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem);margin:0}.cols{gap:var(--roxy-space-md,1rem);grid-template-columns:repeat(2,minmax(0,1fr));display:grid}.section-label{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.06em;font-weight:var(--roxy-weight-bold,600);margin:0 0 var(--roxy-space-xs,.25rem) 0}.row{align-items:center;gap:var(--roxy-space-sm,.5rem);padding:var(--roxy-space-xs,.25rem) 0;border-bottom:1px solid var(--roxy-border,#e4e4e7);font-size:var(--roxy-text-sm,.875rem);display:flex}.row:last-child{border-bottom:none}.glyph{color:var(--roxy-accent-ink,#b45309);text-align:center;width:1.25em;font-size:1.15em;line-height:1}.planet{flex:1;font-weight:500}.time{color:var(--roxy-muted,#71717a);font-variant-numeric:tabular-nums;white-space:nowrap}.num{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);font-variant-numeric:tabular-nums;text-align:right;width:1.5em}@container (width<=30rem){.cols{grid-template-columns:1fr}}`,\n\t];\n\n\tprotected renderData(d: GetHoraResponse) {\n\t\tconst day = d.dayHoras ?? [];\n\t\tconst night = d.nightHoras ?? [];\n\t\tif (day.length === 0 && night.length === 0) return this.renderEmpty();\n\n\t\treturn html`<div class=\"wrap\" part=\"card\" aria-label=\"${this.t('Hora periods')}\"><div class=\"head\" part=\"header\"><h2 class=\"title\">${this.t('Hora')}</h2>${d.date ? html`<p class=\"subtitle\">${formatDate(this.effectiveLang(), d.date)}</p>` : nothing}</div><div class=\"cols\" part=\"table\">${this.renderColumn(this.t('Day'), day)} ${this.renderColumn(this.t('Night'), night)}</div></div>`;\n\t}\n\n\tprivate renderColumn(label: string, horas: HoraPeriod[]) {\n\t\tif (horas.length === 0) return nothing;\n\t\treturn html`<div class=\"col\"><p class=\"section-label\">${label}</p>${horas.map(\n\t\t\t\t(h) => html`<div class=\"row\"><span class=\"glyph\" aria-hidden=\"true\">${planetGlyph(h.planet) ?? '·'}</span> <span class=\"planet\">${h.planet}</span> <span class=\"time\">${formatTimeRange(this.effectiveLang(), h)}</span> ${typeof h.number === 'number' ? html`<span class=\"num\">${h.number}</span>` : nothing}</div>`,\n\t\t\t)}</div>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-hora-table': RoxyHoraTable;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { signGlyph } from '../tokens/index.js';\nimport type {\n\tGetDailyHoroscopeResponse,\n\tGetMonthlyHoroscopeResponse,\n\tGetWeeklyHoroscopeResponse,\n} from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { formatDate } from '../utils/format.js';\n\ntype HoroscopeData =\n\t| GetDailyHoroscopeResponse\n\t| GetWeeklyHoroscopeResponse\n\t| GetMonthlyHoroscopeResponse;\n\n/**\n * Daily, weekly, or monthly horoscope card. Pass `data` from\n * /astrology/horoscope/{sign}/{daily|weekly|monthly}.\n *\n * @remarks\n * A horoscope reads as prose end to end, but it is not one: the card has a real\n * data spine and `hide-readings` keeps all of it. The sign and its glyph, the\n * period and date, the energy meter, the sky strip (Moon sign, Moon phase and the\n * transits this reading was derived from), the lucky number, colour, days and\n * compatible signs, and the monthly key dates all stay. The overview paragraph,\n * the love, career, health, finance and advice sections, and the week-by-week\n * focus and advice go. The two prose blocks each carry their own heading, so each\n * goes whole rather than leaving one over nothing.\n */\n@customElement('roxy-horoscope-card')\nexport class RoxyHoroscopeCard extends RoxyDataElement<HoroscopeData> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.card{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.head{align-items:center;gap:var(--roxy-space-md,1rem);display:flex}.glyph{color:var(--roxy-accent-ink,#b45309);font-size:2.25rem;line-height:1}.title{font-size:var(--roxy-text-xl,1.5rem);font-weight:var(--roxy-weight-bold,600);letter-spacing:var(--roxy-tracking-tight);text-transform:capitalize;margin:0}.date{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-muted,#71717a)}.energy{font-variant-numeric:tabular-nums;font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-secondary,#475569);margin-left:auto}.energy-bar{background:var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-full,9999px);vertical-align:middle;width:6rem;height:6px;margin-left:6px;display:inline-block;overflow:hidden}.energy-bar>span{background:var(--roxy-accent,#f59e0b);height:100%;transition:width var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,cubic-bezier(.4, 0, .2, 1));display:block}.overview{font-size:var(--roxy-text-base,1rem);color:var(--roxy-fg,#0a0a0a);margin:0}.sections{gap:var(--roxy-space-md,1rem);grid-template-columns:repeat(auto-fit,minmax(12rem,1fr));display:grid}.section h3{margin:0 0 var(--roxy-space-xs,.25rem) 0;font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);font-weight:var(--roxy-weight-bold,600);text-transform:uppercase;letter-spacing:.06em}.section p{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a);margin:0}.lucky{gap:var(--roxy-space-sm,.5rem);border-top:1px solid var(--roxy-border,#e4e4e7);padding-top:var(--roxy-space-md,1rem);font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-secondary,#475569);flex-wrap:wrap;display:flex}.lucky strong{color:var(--roxy-fg,#0a0a0a);font-weight:var(--roxy-weight-bold,600)}.compat-wrap{align-items:center;gap:var(--roxy-space-xs,.25rem);flex-wrap:wrap;width:100%;display:flex}.compat{gap:var(--roxy-space-xs,.25rem);flex-wrap:wrap;display:flex}.compat span{background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 16%, transparent);color:var(--roxy-fg,#0a0a0a);border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);text-transform:capitalize;padding:2px 8px}.sky{gap:var(--roxy-space-sm,.5rem);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);display:grid}.moon-line{align-items:baseline;gap:.35rem var(--roxy-space-md,1rem);font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a);flex-wrap:wrap;display:flex}.moon-line b{font-weight:var(--roxy-weight-bold,600)}.moon-line .lbl{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);text-transform:uppercase;letter-spacing:.06em;font-weight:var(--roxy-weight-bold,600);margin-right:.35rem}.transits{gap:.25rem;margin:0;padding:0;list-style:none;display:grid}.transits li{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a);padding-left:.9rem;position:relative}.transits li:before{content:\"\";background:var(--roxy-accent,#f59e0b);border-radius:50%;width:5px;height:5px;position:absolute;top:.5em;left:0}.block-title{margin:0 0 var(--roxy-space-sm,.5rem);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);font-weight:var(--roxy-weight-bold,600);text-transform:uppercase;letter-spacing:.06em}.weeks{gap:var(--roxy-space-sm,.5rem);display:grid}.week{gap:var(--roxy-space-sm,.5rem);border-top:1px solid var(--roxy-border,#e4e4e7);padding-top:var(--roxy-space-sm,.5rem);grid-template-columns:3.5rem 1fr;align-items:baseline;display:grid}.week:first-child{border-top:0;padding-top:0}.week-no{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.06em;font-weight:var(--roxy-weight-bold,600)}.week-focus{font-weight:var(--roxy-weight-bold,600);color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-sm,.875rem)}.week-advice{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a);margin:.15rem 0 0}.dates{gap:.3rem var(--roxy-space-md,1rem);font-size:var(--roxy-text-sm,.875rem);grid-template-columns:max-content 1fr;margin:0;display:grid}.date-row{display:contents}.date-row dt{font-variant-numeric:tabular-nums;color:var(--roxy-accent-ink,#b45309);font-weight:var(--roxy-weight-bold,600);white-space:nowrap}.date-row dd{color:var(--roxy-fg,#0a0a0a);margin:0}`,\n\t];\n\n\t@property({ type: String, reflect: true })\n\tperiod: 'daily' | 'weekly' | 'monthly' = 'daily';\n\n\tprotected renderData(d: HoroscopeData) {\n\t\tconst sign = d.sign ?? '';\n\t\tconst glyph = signGlyph(sign) ?? '';\n\t\tconst energy =\n\t\t\t'energyRating' in d && typeof d.energyRating === 'number'\n\t\t\t\t? d.energyRating\n\t\t\t\t: null;\n\t\t// `date` is an ISO day; `week` and `month` are already human ranges.\n\t\tconst dateLabel =\n\t\t\t('date' in d && d.date && formatDate(this.effectiveLang(), d.date)) ||\n\t\t\t('week' in d && d.week) ||\n\t\t\t('month' in d && d.month) ||\n\t\t\t'';\n\n\t\treturn html`<article class=\"card\" part=\"card\" aria-label=\"${`${this.period} horoscope for ${sign}`}\"><header class=\"head\" part=\"header\"><span class=\"glyph\" aria-hidden=\"true\">${glyph}</span><div><h2 class=\"title\">${sign} ${this.period}</h2>${dateLabel ? html`<div class=\"date\">${dateLabel}</div>` : nothing}</div>${\n\t\t\t\t\tenergy !== null\n\t\t\t\t\t\t? html`<span class=\"energy\" part=\"details\" aria-label=\"${this.t('Energy {{value}} of 10', { value: energy })}\">${this.t('Energy {{value}}/10', { value: energy })} <span class=\"energy-bar\"><span style=\"width:${(energy / 10) * 100}%\"></span></span></span>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</header>${\n\t\t\t\t// The sky strip below is the evidence for this paragraph; the paragraph\n\t\t\t\t// is the read of it.\n\t\t\t\td.overview && !this.hideReadings\n\t\t\t\t\t? html`<p class=\"overview\">${d.overview}</p>`\n\t\t\t\t\t: nothing\n\t\t\t} ${this.renderSky(d)} ${\n\t\t\t\t// Five headed paragraphs and nothing else, so the block goes whole rather\n\t\t\t\t// than leaving five headings over nothing.\n\t\t\t\tthis.hideReadings\n\t\t\t\t\t? nothing\n\t\t\t\t\t: html`<div class=\"sections\" part=\"section outlook\">${\n\t\t\t\t\t\td.love\n\t\t\t\t\t\t\t? html`<div class=\"section\"><h3>${this.t('Love')}</h3><p>${d.love}</p></div>`\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t} ${\n\t\t\t\t\t\td.career\n\t\t\t\t\t\t\t? html`<div class=\"section\"><h3>${this.t('Career')}</h3><p>${d.career}</p></div>`\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t} ${\n\t\t\t\t\t\td.health\n\t\t\t\t\t\t\t? html`<div class=\"section\"><h3>${this.t('Health')}</h3><p>${d.health}</p></div>`\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t} ${\n\t\t\t\t\t\td.finance\n\t\t\t\t\t\t\t? html`<div class=\"section\"><h3>${this.t('Finance')}</h3><p>${d.finance}</p></div>`\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t} ${\n\t\t\t\t\t\t'advice' in d && d.advice\n\t\t\t\t\t\t\t? html`<div class=\"section\"><h3>${this.t('Advice')}</h3><p>${d.advice}</p></div>`\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t}</div>`\n\t\t\t} ${this.renderMonth(d)} ${(() => {\n\t\t\t\tconst luckyNumber =\n\t\t\t\t\t'luckyNumber' in d && d.luckyNumber !== undefined\n\t\t\t\t\t\t? d.luckyNumber\n\t\t\t\t\t\t: undefined;\n\t\t\t\tconst luckyColor =\n\t\t\t\t\t'luckyColor' in d && d.luckyColor ? d.luckyColor : '';\n\t\t\t\tconst luckyNumbers =\n\t\t\t\t\t'luckyNumbers' in d && d.luckyNumbers ? d.luckyNumbers : [];\n\t\t\t\tconst luckyDays = 'luckyDays' in d && d.luckyDays ? d.luckyDays : [];\n\t\t\t\tconst compatibleSigns = d.compatibleSigns ?? [];\n\t\t\t\tif (\n\t\t\t\t\tluckyNumber === undefined &&\n\t\t\t\t\t!luckyColor &&\n\t\t\t\t\tluckyNumbers.length === 0 &&\n\t\t\t\t\tluckyDays.length === 0 &&\n\t\t\t\t\tcompatibleSigns.length === 0\n\t\t\t\t)\n\t\t\t\t\treturn nothing;\n\t\t\t\treturn html`<div class=\"lucky\" part=\"details\">${\n\t\t\t\t\t\t\tluckyNumber !== undefined\n\t\t\t\t\t\t\t\t? html`<span>${this.t('Lucky number')} <strong>${luckyNumber}</strong></span>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t} ${\n\t\t\t\t\t\t\tluckyColor\n\t\t\t\t\t\t\t\t? html`<span>${this.t('Lucky color')} <strong>${luckyColor}</strong></span>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t} ${\n\t\t\t\t\t\t\tluckyNumbers.length\n\t\t\t\t\t\t\t\t? html`<span>${this.t('Lucky numbers')} <strong>${luckyNumbers.join(', ')}</strong></span>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t} ${\n\t\t\t\t\t\t\tluckyDays.length\n\t\t\t\t\t\t\t\t? html`<span>${this.t('Lucky days')} <strong>${luckyDays.join(', ')}</strong></span>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t} ${\n\t\t\t\t\t\t\tcompatibleSigns.length\n\t\t\t\t\t\t\t\t? html`<span class=\"compat-wrap\">${this.t('Best with')} <span class=\"compat\">${compatibleSigns.map(\n\t\t\t\t\t\t\t\t\t\t\t(s) => html`<span>${s}</span>`,\n\t\t\t\t\t\t\t\t\t\t)}</span></span>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t}</div>`;\n\t\t\t})()}</article>`;\n\t}\n\n\t/**\n\t * The sky behind the reading (daily only): where the Moon is, what phase it is in, and the transits the forecast was derived from. A daily horoscope that hides its transits is a fortune cookie; showing them is what makes this one auditable.\n\t */\n\tprivate renderSky(d: HoroscopeData) {\n\t\tconst moonSign = 'moonSign' in d ? d.moonSign : '';\n\t\tconst moonPhase = 'moonPhase' in d ? d.moonPhase : '';\n\t\tconst transits = ('activeTransits' in d ? d.activeTransits : []) ?? [];\n\t\tif (!moonSign && !moonPhase && transits.length === 0) return nothing;\n\t\tconst glyph = signGlyph(moonSign) ?? '';\n\t\treturn html`<div class=\"sky\" part=\"section sky\">${\n\t\t\t\tmoonSign || moonPhase\n\t\t\t\t\t? html`<div class=\"moon-line\">${\n\t\t\t\t\t\t\tmoonSign\n\t\t\t\t\t\t\t\t? html`<span><span class=\"lbl\">${this.t('Moon')}</span> <span aria-hidden=\"true\">${glyph}</span> <b>${moonSign}</b></span>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t} ${\n\t\t\t\t\t\t\tmoonPhase\n\t\t\t\t\t\t\t\t? html`<span><span class=\"lbl\">${this.t('Phase')}</span> <b>${moonPhase}</b></span>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t}</div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\ttransits.length\n\t\t\t\t\t? html`<ul class=\"transits\" aria-label=\"${this.t('Active transits')}\">${transits.map((t) => html`<li>${t}</li>`)}</ul>`\n\t\t\t\t\t: nothing\n\t\t\t}</div>`;\n\t}\n\n\t/** Monthly arc: the week-by-week focus and the dated events (lunations, retrogrades, ingresses) the month turns on. */\n\tprivate renderMonth(d: HoroscopeData) {\n\t\tconst weeks = ('weekByWeek' in d ? d.weekByWeek : []) ?? [];\n\t\tconst keyDates = ('keyDates' in d ? d.keyDates : []) ?? [];\n\t\tif (weeks.length === 0 && keyDates.length === 0) return nothing;\n\t\treturn html`${\n\t\t\t// Each week is a focus phrase and a line of advice, so the block is prose\n\t\t\t// under its own heading and goes whole.\n\t\t\tweeks.length && !this.hideReadings\n\t\t\t\t? html`<section part=\"section week-by-week\"><h3 class=\"block-title\">${this.t('Week by week')}</h3><div class=\"weeks\">${weeks.map(\n\t\t\t\t\t\t\t(w) => html`<div class=\"week\"><span class=\"week-no\">${this.t('Week {{n}}', { n: w.week })}</span><div><div class=\"week-focus\">${w.focus}</div>${w.advice ? html`<p class=\"week-advice\">${w.advice}</p>` : nothing}</div></div>`,\n\t\t\t\t\t\t)}</div></section>`\n\t\t\t\t: nothing\n\t\t} ${\n\t\t\t// Dated lunations, retrogrades and ingresses: the month's ephemeris, not a\n\t\t\t// read of it, so it stays.\n\t\t\tkeyDates.length\n\t\t\t\t? html`<section part=\"section key-dates\"><h3 class=\"block-title\">${this.t('Key dates')}</h3><dl class=\"dates\">${keyDates.map(\n\t\t\t\t\t\t\t(k) => html`<div class=\"date-row\"><dt>${formatDate(this.effectiveLang(), k.date) || k.date}</dt><dd>${k.event}</dd></div>`,\n\t\t\t\t\t\t)}</dl></section>`\n\t\t\t\t: nothing\n\t\t}`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-horoscope-card': RoxyHoroscopeCard;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement, state } from 'lit/decorators.js';\nimport type { KpChartResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { formatSignPosition } from '../utils/degree.js';\nimport { formatAyanamsa } from '../utils/format.js';\nimport { houseThemeLine, houseWords } from '../utils/house-themes.js';\nimport {\n\trenderTablist,\n\ttype TablistItem,\n\ttablistStyles,\n} from '../utils/tablist.js';\n\ntype Tab = 'planets' | 'cusps' | 'significators';\n\nconst TABS: ReadonlyArray<TablistItem<Tab>> = [\n\t{ id: 'planets', label: 'Planets' },\n\t{ id: 'cusps', label: 'Cusps' },\n\t{ id: 'significators', label: 'Significators' },\n];\n\n/** The four KP significator strength levels, strongest (1) to weakest (4). */\nconst LEVELS = [1, 2, 3, 4] as const;\n\ntype Significators = KpChartResponse['significators'];\ntype HouseSignificator = Significators['houseWise'][number];\ntype PlanetSignificator = Significators['planetWise'][number];\n\n/** A planet or node row, normalized so planets and Rahu/Ketu share a table. */\ninterface KpBody {\n\tname: string;\n\tlongitude?: number;\n\thouse?: number;\n\tnakshatra?: string;\n\tpada?: number;\n\tstarLord?: string;\n\tsubLord?: string;\n\tsubSubLord?: string;\n\tkpNumber?: number;\n\tretrograde?: boolean;\n}\n\n/** \"Chitra 3\" when the pada is known, otherwise the bare nakshatra. */\nfunction nakshatraPada(nakshatra?: string, pada?: number): string {\n\tif (!nakshatra) return '';\n\treturn typeof pada === 'number' ? `${nakshatra} ${pada}` : nakshatra;\n}\n\n/**\n * A strength-ordered chain, \"Venus > Mars > Jupiter\". The API repeats a body in\n * `all` / `allHouses` when it signifies at more than one level (Venus as both\n * star-of-owner and owner), which reads as a stutter, so the first occurrence\n * wins and the rest are folded away. The grouped level columns still show every\n * level that body appears at.\n */\nfunction chain(values: ReadonlyArray<string | number>): string {\n\treturn [...new Set(values.map(String))].join(' > ');\n}\n\n/** The same first-occurrence-wins fold {@link chain} applies, for the house wording beneath it, so the numbers and the words never disagree about how many houses there are. */\nfunction dedupe(houses: readonly number[] | undefined): number[] {\n\treturn [...new Set(houses ?? [])];\n}\n\n/**\n * KP (Krishnamurti Paddhati) chart. Renders /vedic-astrology/kp/chart: an\n * Ascendant summary, a planets-and-nodes table, a Placidus cusps table, and the\n * significator tables. The Ascendant is cusp 1, so its full four-lord chain is\n * already the first row of the cusps table and the header stays a summary.\n *\n * @remarks\n * The cusp and planet sub lords are the primary predictive surface in KP, so\n * every row carries star lord, sub lord, sub-sub lord, and KP number (1-249).\n * The significators tab is the event-timing surface: house-wise (which planets\n * signify each house) and planet-wise (which houses each planet signifies),\n * both graded L1 to L4 with the API's own level labels shown as the legend.\n *\n * Every bare house number is captioned from the response `houseThemes` map, so\n * \"11, 6\" reads \"gains, enemies\" and a cusp row says what that cusp is for. The\n * words are the requested language and the requested `focus` lens, which is why\n * they are read from the payload and never from a table held here.\n */\n@customElement('roxy-kp-chart')\nexport class RoxyKpChart extends RoxyDataElement<KpChartResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\ttablistStyles,\n\t\tcss`.wrap{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);background:var(--roxy-surface,#fff);box-shadow:var(--roxy-shadow-sm);width:100%;overflow:auto}.head{padding:var(--roxy-space-md,1rem);border-bottom:1px solid var(--roxy-border,#e4e4e7);gap:var(--roxy-space-xs,.25rem);display:grid}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.asc,.ayan{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem)}.asc strong{color:var(--roxy-fg,#0a0a0a)}.roxy-tablist{padding:0 var(--roxy-space-md,1rem)}table{border-collapse:collapse;width:100%;font-size:var(--roxy-text-sm,.875rem);min-width:620px}thead{background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 20%, transparent)}th,td{padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);text-align:left;white-space:nowrap}th{color:var(--roxy-muted,#71717a);font-weight:var(--roxy-weight-bold,600);text-transform:uppercase;font-size:var(--roxy-text-xs,.75rem);letter-spacing:.04em}tbody tr{border-top:1px solid var(--roxy-border,#e4e4e7)}td.body{font-weight:var(--roxy-weight-bold,600);color:var(--roxy-fg,#0a0a0a)}.retro{color:var(--roxy-warning-fg,#9a3412);font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);margin-left:4px}.num{font-variant-numeric:tabular-nums}.sig-head{padding:var(--roxy-space-md,1rem) var(--roxy-space-md,1rem) 0;gap:var(--roxy-space-xs,.25rem);display:grid}.sig-title{font-size:var(--roxy-text-sm,.875rem);font-weight:var(--roxy-weight-bold,600);margin:0}.sig-note{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);margin:0}.legend{gap:var(--roxy-space-xs,.25rem) var(--roxy-space-md,1rem);padding:var(--roxy-space-md,1rem) var(--roxy-space-md,1rem) 0;color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);flex-wrap:wrap;margin:0;list-style:none;display:flex}.legend b{color:var(--roxy-accent-ink,#b45309);margin-right:4px}.chain,.empty-cell{color:var(--roxy-muted,#71717a)}.themes{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-normal,400);text-transform:none;letter-spacing:normal;display:block}`,\n\t];\n\n\t@state()\n\tprivate activeTab: Tab = 'planets';\n\n\t/** Merge the 7 planets and the two nodes into one ordered body list. */\n\tprivate bodies(): KpBody[] {\n\t\tconst d = this.data;\n\t\tif (!d) return [];\n\t\tconst rows: KpBody[] = (d.planets ?? []).map((p) => ({\n\t\t\tname: p.planet,\n\t\t\tlongitude: p.longitude,\n\t\t\thouse: p.house,\n\t\t\tnakshatra: p.nakshatra,\n\t\t\tpada: p.pada,\n\t\t\tstarLord: p.starLord,\n\t\t\tsubLord: p.subLord,\n\t\t\tsubSubLord: p.subSubLord,\n\t\t\tkpNumber: p.kpNumber,\n\t\t\tretrograde: p.retrograde,\n\t\t}));\n\t\tconst nodes = d.nodes;\n\t\tfor (const [name, node] of [\n\t\t\t['Rahu', nodes?.rahu],\n\t\t\t['Ketu', nodes?.ketu],\n\t\t] as const) {\n\t\t\tif (node) {\n\t\t\t\trows.push({\n\t\t\t\t\tname,\n\t\t\t\t\tlongitude: node.longitude,\n\t\t\t\t\thouse: node.house,\n\t\t\t\t\tnakshatra: node.nakshatra,\n\t\t\t\t\tstarLord: node.starLord,\n\t\t\t\t\tsubLord: node.subLord,\n\t\t\t\t\tsubSubLord: node.subSubLord,\n\t\t\t\t\tkpNumber: node.kpNumber,\n\t\t\t\t\tretrograde: true,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn rows;\n\t}\n\n\tprotected renderData(d: KpChartResponse) {\n\t\tconst asc = d.ascendant;\n\n\t\treturn html`<div class=\"wrap\" part=\"card\" aria-label=\"KP chart\" tabindex=\"0\"><header class=\"head\" part=\"header\"><h2 class=\"title\">KP chart</h2>${\n\t\t\t\t\tasc\n\t\t\t\t\t\t? html`<div class=\"asc\">Ascendant: <strong>${typeof asc.longitude === 'number' ? formatSignPosition(asc.longitude) : (asc.sign ?? '')} </strong>${asc.nakshatra ? html`· ${nakshatraPada(asc.nakshatra, asc.pada)}` : nothing} ${asc.subLord ? html`· sub lord ${asc.subLord}` : nothing} ${typeof asc.kpNumber === 'number' ? html`· KP ${asc.kpNumber}` : nothing}</div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t} ${\n\t\t\t\t\ttypeof d.meta?.ayanamsa === 'number'\n\t\t\t\t\t\t? html`<div class=\"ayan\">Ayanamsa: ${formatAyanamsa(this.effectiveLang(), d.meta.ayanamsaType, d.meta.ayanamsa)} ${d.meta.houseSystem ? html`· ${d.meta.houseSystem} houses` : nothing}</div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</header>${renderTablist({\n\t\t\t\titems: TABS,\n\t\t\t\tactive: this.activeTab,\n\t\t\t\tonSelect: (id) => {\n\t\t\t\t\tthis.activeTab = id;\n\t\t\t\t},\n\t\t\t\tlabel: 'KP chart views',\n\t\t\t\tidPrefix: 'kp',\n\t\t\t\tcontrols: true,\n\t\t\t})}<div id=\"kp-panel-${this.activeTab}\" part=\"panel\" role=\"tabpanel\" aria-labelledby=\"kp-tab-${this.activeTab}\">${\n\t\t\t\t\tthis.activeTab === 'planets'\n\t\t\t\t\t\t? this.renderPlanets()\n\t\t\t\t\t\t: this.activeTab === 'cusps'\n\t\t\t\t\t\t\t? this.renderCusps()\n\t\t\t\t\t\t\t: this.renderSignificators()\n\t\t\t\t}</div></div>`;\n\t}\n\n\tprivate renderPlanets() {\n\t\tconst bodies = this.bodies();\n\t\tif (!bodies.length)\n\t\t\treturn html`<p class=\"roxy-empty\" role=\"status\">No planets</p>`;\n\t\treturn html`<table part=\"table\" role=\"table\"><caption class=\"roxy-sr-only\">KP planets and nodes: each body with its position, house, nakshatra and pada, star lord, sub lord, sub sub lord and KP number.</caption><thead><tr><th scope=\"col\">${this.t('Body')}</th><th scope=\"col\">Position</th><th scope=\"col\">House</th><th scope=\"col\">Nakshatra, pada</th><th scope=\"col\">Star lord</th><th scope=\"col\">Sub lord</th><th scope=\"col\">Sub-sub lord</th><th scope=\"col\">KP no.</th></tr></thead><tbody>${bodies.map(\n\t\t\t\t\t(b) => html`<tr><td class=\"body\">${b.name}${b.retrograde ? html`<span class=\"retro\">R</span>` : nothing}</td><td class=\"num\">${typeof b.longitude === 'number' ? formatSignPosition(b.longitude) : ''}</td><td class=\"num\">${typeof b.house === 'number' ? b.house : ''}</td><td>${nakshatraPada(b.nakshatra, b.pada)}</td><td>${b.starLord ?? ''}</td><td>${b.subLord ?? ''}</td><td>${b.subSubLord ?? ''}</td><td class=\"num\">${typeof b.kpNumber === 'number' ? b.kpNumber : ''}</td></tr>`,\n\t\t\t\t)}</tbody></table>`;\n\t}\n\n\tprivate renderCusps() {\n\t\tconst cusps = this.data?.cusps ?? [];\n\t\tconst themes = this.data?.houseThemes;\n\t\tif (!cusps.length)\n\t\t\treturn html`<p class=\"roxy-empty\" role=\"status\">No cusps</p>`;\n\t\treturn html`<table part=\"table\" role=\"table\"><caption class=\"roxy-sr-only\">KP Placidus cusps: each house cusp with what it signifies, its position, sign lord, nakshatra and pada, star lord, sub lord, sub sub lord and KP number.</caption><thead><tr><th scope=\"col\">House</th><th scope=\"col\">Position</th><th scope=\"col\">Sign lord</th><th scope=\"col\">Nakshatra, pada</th><th scope=\"col\">Star lord</th><th scope=\"col\">Sub lord</th><th scope=\"col\">Sub-sub lord</th><th scope=\"col\">KP no.</th></tr></thead><tbody>${cusps.map(\n\t\t\t\t\t(c) => html`<tr><td class=\"body num\">${c.house}${this.renderThemes(houseThemeLine(c.house, themes))}</td><td class=\"num\">${typeof c.longitude === 'number' ? formatSignPosition(c.longitude) : (c.sign ?? '')}</td><td>${c.signLord ?? ''}</td><td>${nakshatraPada(c.nakshatra, c.pada)}</td><td>${c.starLord ?? ''}</td><td>${c.subLord ?? ''}</td><td>${c.subSubLord ?? ''}</td><td class=\"num\">${typeof c.kpNumber === 'number' ? c.kpNumber : ''}</td></tr>`,\n\t\t\t\t)}</tbody></table>`;\n\t}\n\n\t/**\n\t * The KP event-timing surface. House-wise answers \"which planets can deliver\n\t * this house\", planet-wise answers \"what will this planet deliver in its\n\t * dasha\". The L1-L4 legend text is the API's own level description, so the\n\t * grading a reader sees is the grading the engine applied.\n\t */\n\tprivate renderSignificators() {\n\t\tconst sig = this.data?.significators;\n\t\tconst themes = this.data?.houseThemes;\n\t\tconst houseWise = sig?.houseWise ?? [];\n\t\tconst planetWise = sig?.planetWise ?? [];\n\t\tif (!houseWise.length && !planetWise.length)\n\t\t\treturn html`<p class=\"roxy-empty\" role=\"status\">No significators</p>`;\n\n\t\treturn html`${this.renderLevelLegend(houseWise)} ${\n\t\t\t\thouseWise.length\n\t\t\t\t\t? html`<div class=\"sig-head\"><h3 class=\"sig-title\">House-wise significators</h3><p class=\"sig-note\">Planets that signify each house, strongest level first.</p></div><table part=\"table\" role=\"table\"><caption class=\"roxy-sr-only\">KP house-wise significators: each house, the planets that signify it at levels 1 to 4, and the full strength order.</caption><thead><tr><th scope=\"col\">House</th>${LEVELS.map((l) => html`<th scope=\"col\">L${l}</th>`)}<th scope=\"col\">Strength order</th></tr></thead><tbody>${houseWise.map(\n\t\t\t\t\t\t\t\t\t(h) => html`<tr><td class=\"body num\">${h.house}${this.renderThemes(houseThemeLine(h.house, themes))}</td>${LEVELS.map((l) => this.renderCell(this.planetsAtLevel(h, l).join(', ')))}<td class=\"chain\">${chain(h.all ?? [])}</td></tr>`,\n\t\t\t\t\t\t\t\t)}</tbody></table>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\tplanetWise.length\n\t\t\t\t\t? html`<div class=\"sig-head\"><h3 class=\"sig-title\">Planet-wise significators</h3><p class=\"sig-note\">Houses each planet signifies, strongest level first.</p></div><table part=\"table\" role=\"table\"><caption class=\"roxy-sr-only\">KP planet-wise significators: each planet, the houses it signifies at levels 1 to 4, and the full strength order.</caption><thead><tr><th scope=\"col\">Planet</th>${LEVELS.map((l) => html`<th scope=\"col\">L${l}</th>`)}<th scope=\"col\">Strength order</th></tr></thead><tbody>${planetWise.map(\n\t\t\t\t\t\t\t\t\t(p) => html`<tr><td class=\"body\">${p.planet}</td>${LEVELS.map((l) => this.renderCell(this.housesAtLevel(p, l).join(', ')))}<td class=\"chain\">${chain(p.allHouses ?? [])}${this.renderThemes(houseWords(dedupe(p.allHouses), themes))}</td></tr>`,\n\t\t\t\t\t\t\t\t)}</tbody></table>`\n\t\t\t\t\t: nothing\n\t\t\t}`;\n\t}\n\n\t/** The house wording under a number cell, or nothing when the request did not ask for themes. */\n\tprivate renderThemes(words: string) {\n\t\treturn words ? html`<span class=\"themes\">${words}</span>` : nothing;\n\t}\n\n\t/** An em-dash-free placeholder keeps an empty level cell from reading as missing data. */\n\tprivate renderCell(value: string) {\n\t\treturn value\n\t\t\t? html`<td>${value}</td>`\n\t\t\t: html`<td class=\"empty-cell\">none</td>`;\n\t}\n\n\tprivate planetsAtLevel(h: HouseSignificator, level: number): string[] {\n\t\treturn (h.significators ?? [])\n\t\t\t.filter((s) => s.level === level)\n\t\t\t.flatMap((s) => s.planets ?? []);\n\t}\n\n\tprivate housesAtLevel(p: PlanetSignificator, level: number): number[] {\n\t\treturn (p.signifies ?? [])\n\t\t\t.filter((s) => s.level === level)\n\t\t\t.flatMap((s) => s.houses ?? []);\n\t}\n\n\t/** Level to the API's own label, taken from the first house that carries it. */\n\tprivate renderLevelLegend(houseWise: HouseSignificator[]) {\n\t\tconst labels = new Map<number, string>();\n\t\tfor (const h of houseWise) {\n\t\t\tfor (const s of h.significators ?? []) {\n\t\t\t\tif (s.description && !labels.has(s.level))\n\t\t\t\t\tlabels.set(s.level, s.description);\n\t\t\t}\n\t\t}\n\t\tif (labels.size === 0) return nothing;\n\t\treturn html`<ul class=\"legend\" part=\"legend\" aria-label=\"Significator levels\">${[...labels.entries()]\n\t\t\t\t.sort((a, b) => a[0] - b[0])\n\t\t\t\t.map(([level, label]) => html`<li><b>L${level}</b>${label}</li>`)}</ul>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-kp-chart': RoxyKpChart;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport type { KpPlanetsResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { formatNumber } from '../utils/format.js';\n\n/**\n * KP planets table with sub-lord and sub-sub-lord columns. Renders\n * /vedic-astrology/kp/planets.\n */\n@customElement('roxy-kp-planets-table')\nexport class RoxyKpPlanetsTable extends RoxyDataElement<KpPlanetsResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.wrap{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);background:var(--roxy-surface,#fff);box-shadow:var(--roxy-shadow-sm);overflow:auto}.head{padding:var(--roxy-space-md,1rem);border-bottom:1px solid var(--roxy-border,#e4e4e7);justify-content:space-between;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.ayanamsa{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem)}table{border-collapse:collapse;width:100%;font-size:var(--roxy-text-sm,.875rem);min-width:560px}thead{background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 20%, transparent)}th,td{padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);text-align:left;white-space:nowrap}th{color:var(--roxy-muted,#71717a);font-weight:var(--roxy-weight-bold,600);text-transform:uppercase;font-size:var(--roxy-text-xs,.75rem);letter-spacing:.04em}tbody tr{border-top:1px solid var(--roxy-border,#e4e4e7)}td.planet{font-weight:var(--roxy-weight-bold,600);color:var(--roxy-fg,#0a0a0a)}.retro{color:var(--roxy-warning-fg,#9a3412);font-size:var(--roxy-text-xs,.75rem);margin-left:4px}`,\n\t];\n\n\tprotected renderData(d: KpPlanetsResponse) {\n\t\tconst planets = d.planets ?? [];\n\n\t\treturn html`<div class=\"wrap\" part=\"card\" aria-label=\"KP planets table\" tabindex=\"0\"><header class=\"head\" part=\"header\"><h2 class=\"title\">KP planets</h2>${\n\t\t\t\t\ttypeof d.ayanamsa === 'number'\n\t\t\t\t\t\t? html`<span class=\"ayanamsa\">Ayanamsa: ${formatNumber(this.effectiveLang(), d.ayanamsa, 2)}°</span>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</header><table part=\"table\" role=\"table\"><caption class=\"roxy-sr-only\">KP planets: each planet with its sign, sign lord, nakshatra, star lord, sub lord, sub sub lord and KP number.</caption><thead><tr><th scope=\"col\">Planet</th><th scope=\"col\">Sign</th><th scope=\"col\">Sign lord</th><th scope=\"col\">Nakshatra</th><th scope=\"col\">Star lord</th><th scope=\"col\">Sub lord</th><th scope=\"col\">Sub-sub lord</th><th scope=\"col\">KP no.</th></tr></thead><tbody>${planets.map(\n\t\t\t\t\t\t(p) => html`<tr><td class=\"planet\">${p.planet} ${p.retrograde ? html`<span class=\"retro\">R</span>` : nothing}</td><td>${p.sign ?? ''}</td><td>${p.signLord ?? ''}</td><td>${p.nakshatra ?? ''}</td><td>${p.nakshatraLord ?? ''}</td><td>${p.subLord ?? ''}</td><td>${p.subSubLord ?? ''}</td><td>${p.kpNumber ?? ''}</td></tr>`,\n\t\t\t\t\t)}</tbody></table></div>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-kp-planets-table': RoxyKpPlanetsTable;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport type { KpRulingPlanetsResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { houseWords } from '../utils/house-themes.js';\n\n/**\n * KP ruling planets card. Renders /vedic-astrology/kp/ruling-planets: the day\n * lord, the Moon and Lagna stellar hierarchies (sign lord, star lord, sub\n * lord, sub-sub lord), the consolidated ruling-planet list ordered by\n * strength, and, when birth data is supplied, the house significators per\n * planet. The primary horary timing tool in KP astrology.\n *\n * @remarks\n * `significators` and `houseThemes` both arrive only when the request carried\n * birth data, so the significator table and its house wording appear together\n * or not at all. The wording is read from the response, never from a table held\n * here, so it follows the requested language and the requested `focus` lens.\n */\n@customElement('roxy-kp-ruling-planets')\nexport class RoxyKpRulingPlanets extends RoxyDataElement<KpRulingPlanetsResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.wrap{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);background:var(--roxy-surface,#fff);padding:var(--roxy-space-md,1rem);gap:var(--roxy-space-md,1rem);box-shadow:var(--roxy-shadow-sm);grid-template-columns:minmax(0,1fr);display:grid}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.day-lord{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem)}.day-lord strong{color:var(--roxy-fg,#0a0a0a)}.groups{gap:var(--roxy-space-md,1rem);grid-template-columns:repeat(auto-fit,minmax(11rem,1fr));display:grid}.group h3{margin:0 0 var(--roxy-space-xs,.25rem);font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.05em}.group dl{gap:2px var(--roxy-space-sm,.5rem);font-size:var(--roxy-text-sm,.875rem);grid-template-columns:auto 1fr;margin:0;display:grid}.group dt{color:var(--roxy-muted,#71717a)}.group dd{color:var(--roxy-fg,#0a0a0a);font-weight:var(--roxy-weight-bold,600);margin:0}.rp-list{gap:var(--roxy-space-xs,.25rem);flex-wrap:wrap;align-items:center;display:flex}.rp-label{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.05em;margin-right:var(--roxy-space-xs,.25rem)}.rp{font-size:var(--roxy-text-sm,.875rem);font-weight:var(--roxy-weight-bold,600);background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 18%, transparent);color:var(--roxy-fg,#0a0a0a);border-radius:999px;align-items:center;gap:.3em;padding:.15em .6em;display:inline-flex}.rp .rank{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-accent-ink,#b45309)}table{border-collapse:collapse;width:100%;font-size:var(--roxy-text-sm,.875rem)}th,td{padding:var(--roxy-space-xs,.25rem) var(--roxy-space-sm,.5rem);text-align:left;border-bottom:1px solid var(--roxy-border,#e4e4e7)}th{color:var(--roxy-muted,#71717a);font-weight:var(--roxy-weight-bold,600);text-transform:uppercase;font-size:var(--roxy-text-xs,.75rem);letter-spacing:.04em}.num{font-variant-numeric:tabular-nums}.themes{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);display:block}`,\n\t];\n\n\tprotected renderData(d: KpRulingPlanetsResponse) {\n\t\tconst significators = d.significators ?? [];\n\n\t\treturn html`<div class=\"wrap\" part=\"card\" aria-label=\"KP ruling planets\"><header part=\"header\"><h2 class=\"title\">KP ruling planets</h2>${\n\t\t\t\t\td.dayLord\n\t\t\t\t\t\t? html`<div class=\"day-lord\">Day lord: <strong>${d.dayLord}</strong></div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</header><div class=\"groups\" part=\"details\"><div class=\"group\"><h3>Moon</h3><dl><dt>Sign lord</dt><dd>${d.moonSignLord ?? ''}</dd><dt>Star lord</dt><dd>${d.moonStarLord ?? ''}</dd><dt>Sub lord</dt><dd>${d.moonSublord ?? ''}</dd><dt>Sub-sub lord</dt><dd>${d.moonSubSublord ?? ''}</dd></dl></div><div class=\"group\"><h3>Lagna</h3><dl><dt>Sign lord</dt><dd>${d.lagnaSignLord ?? ''}</dd><dt>Star lord</dt><dd>${d.lagnaStarLord ?? ''}</dd><dt>Sub lord</dt><dd>${d.lagnaSublord ?? ''}</dd><dt>Sub-sub lord</dt><dd>${d.lagnaSubSublord ?? ''}</dd></dl></div></div>${\n\t\t\t\td.rulingPlanets?.length\n\t\t\t\t\t? html`<div class=\"rp-list\" part=\"details\" role=\"list\" aria-label=\"Ruling planets by strength\"><span class=\"rp-label\">Ruling planets</span> ${d.rulingPlanets.map(\n\t\t\t\t\t\t\t(p, i) =>\n\t\t\t\t\t\t\t\thtml`<span class=\"rp\" role=\"listitem\"><span class=\"rank\">${i + 1}</span> ${p}</span>`,\n\t\t\t\t\t\t)}</div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\tsignificators.length\n\t\t\t\t\t? html`<table part=\"table\"><caption class=\"roxy-sr-only\">House significators: each ruling planet, the houses it signifies, and what those houses stand for.</caption><thead><tr><th scope=\"col\">Planet</th><th scope=\"col\">Signifies houses</th></tr></thead><tbody>${significators.map((s) => {\n\t\t\t\t\t\t\t\tconst words = houseWords(s.signifies, d.houseThemes);\n\t\t\t\t\t\t\t\treturn html`<tr><td>${s.planet}</td><td><span class=\"num\">${(s.signifies ?? []).join(', ')}</span> ${words ? html`<span class=\"themes\">${words}</span>` : nothing}</td></tr>`;\n\t\t\t\t\t\t\t})}</tbody></table>`\n\t\t\t\t\t: nothing\n\t\t\t}</div>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-kp-ruling-planets': RoxyKpRulingPlanets;\n\t}\n}\n", "import { css, html, nothing, svg } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport type { ChromeString } from '../i18n/chrome-strings.js';\nimport { planetGlyph } from '../tokens/index.js';\nimport type { LocalSpaceResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { formatDateTime } from '../utils/format.js';\nimport { planetColor } from '../utils/planet-color.js';\n\ntype Body = LocalSpaceResponse['bodies'][number];\n\nconst SIZE = 320;\nconst CENTER = SIZE / 2;\nconst RIM = 128;\nconst SPOKE = 118;\nconst GLYPH_R = 140;\nconst TICK_LABEL_R = 150;\n\n// Compass azimuth (0 = north, clockwise) to a screen point. North is up, east\n// is right, matching how the local space line is read off a real compass.\nfunction azimuthPoint(az: number, r: number): { x: number; y: number } {\n\tconst rad = (az * Math.PI) / 180;\n\treturn { x: CENTER + r * Math.sin(rad), y: CENTER - r * Math.cos(rad) };\n}\n\n/** The eight principal points of the rose, each with the English SOURCE its label is looked up by. All eight move together: a rose that translated only the corners would read half in one script. */\nconst PRINCIPAL: ReadonlyArray<{ az: number; source: ChromeString }> = [\n\t{ az: 0, source: 'N' },\n\t{ az: 45, source: 'NE' },\n\t{ az: 90, source: 'E' },\n\t{ az: 135, source: 'SE' },\n\t{ az: 180, source: 'S' },\n\t{ az: 225, source: 'SW' },\n\t{ az: 270, source: 'W' },\n\t{ az: 315, source: 'NW' },\n];\n\n/**\n * Local space compass. Plots each body from a /astrology/local-space response as\n * a directional line radiating from the birthplace at its azimuth (0 = north,\n * clockwise), with a 16-point ring. Bodies below the horizon are dimmed. Color\n * is per body and theme-token driven.\n */\n@customElement('roxy-local-space-compass')\nexport class RoxyLocalSpaceCompass extends RoxyDataElement<LocalSpaceResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.wrap{background:var(--roxy-surface,#fff);width:100%;color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-primary,#0f172a);margin:0}.meta{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem)}svg{width:100%;max-width:var(--roxy-chart-max-width,480px);aspect-ratio:1;height:auto;margin:0 auto;display:block}.dial{fill:none;stroke:var(--roxy-border,#e4e4e7)}.dial-fill{fill:color-mix(in srgb, var(--roxy-border,#e4e4e7) 10%, transparent);stroke:var(--roxy-border,#e4e4e7);stroke-width:1px}.cardinal-axis{stroke:var(--roxy-border,#e4e4e7);stroke-width:.5px}.tick{stroke:var(--roxy-secondary,#475569);stroke-width:.6px;opacity:.5}.compass-label{fill:var(--roxy-secondary,#475569);font-size:9px;font-weight:600;font-family:var(--roxy-font-sans)}.compass-label.cardinal,.center-dot{fill:var(--roxy-fg,#0a0a0a)}.spoke{stroke-width:1.4px}.spoke.below{stroke-dasharray:3 3;opacity:.4}.body-glyph{font-size:11px;font-weight:600;font-family:var(--roxy-font-sans)}.body-glyph.below{opacity:.45}.wrap>*{min-width:0}.table-scroll{-webkit-overflow-scrolling:touch;min-width:0;overflow-x:auto}.list{border-collapse:collapse;width:100%;font-size:var(--roxy-text-sm,.875rem)}.list th,.list td{text-align:left;border-bottom:1px solid var(--roxy-border,#e4e4e7);padding:4px 8px}.list th{color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.04em;font-weight:600;font-size:var(--roxy-text-xs,.75rem)}.list td.num{text-align:right;font-variant-numeric:tabular-nums}.body-cell{align-items:center;gap:.4rem;display:inline-flex}.body-dot{border-radius:50%;flex-shrink:0;width:10px;height:10px}.horizon-pill{border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);padding:1px 7px}.horizon-pill.up{background:color-mix(in srgb, var(--roxy-success,#16a34a) 16%, transparent);color:var(--roxy-success-fg,#166534)}.horizon-pill.down{background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 55%, transparent);color:var(--roxy-fg,#0a0a0a)}.summary{color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-sm,.875rem);margin:0}`,\n\t];\n\n\tprotected renderData(data: LocalSpaceResponse) {\n\t\tconst bodies = data.bodies ?? [];\n\t\tconst bd = data.birthDetails;\n\t\treturn html`<div class=\"wrap\" part=\"card\"><header part=\"header\"><h2 class=\"title\">${this.t('Local space')}</h2>${\n\t\t\t\t\tbd\n\t\t\t\t\t\t? html`<div class=\"meta\">${formatDateTime(this.effectiveLang(), bd.date, bd.time)}</div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</header>${this.renderDial(bodies)} ${data.summary ? html`<p class=\"summary\">${data.summary}</p>` : nothing} ${this.renderList(bodies)}</div>`;\n\t}\n\n\tprivate renderDial(bodies: Body[]) {\n\t\treturn html`<svg viewBox=\"0 0 ${SIZE} ${SIZE}\" part=\"chart\" role=\"img\" aria-label=\"${this.t('Local space compass of planetary directions from the birthplace')}\"><title>${this.t('Local space compass')}</title><desc>${this.t(\n\t\t\t\t\t'A compass centered on the birthplace. Each body is a line pointing to its azimuth, clockwise from north. Bodies below the horizon are dimmed.',\n\t\t\t\t)}</desc><circle class=\"dial-fill\" cx=\"${CENTER}\" cy=\"${CENTER}\" r=\"${RIM}\"/><circle class=\"dial\" cx=\"${CENTER}\" cy=\"${CENTER}\" r=\"${RIM * 0.66}\" stroke-width=\"0.5\"/><circle class=\"dial\" cx=\"${CENTER}\" cy=\"${CENTER}\" r=\"${RIM * 0.33}\" stroke-width=\"0.5\"/>${this.renderCompassRing()} ${this.renderSpokes(bodies)}<circle class=\"center-dot\" cx=\"${CENTER}\" cy=\"${CENTER}\" r=\"2.5\"/></svg>`;\n\t}\n\n\tprivate renderCompassRing() {\n\t\tconst ticks = [];\n\t\t// 16-point ring: a tick every 22.5 degrees.\n\t\tfor (let az = 0; az < 360; az += 22.5) {\n\t\t\tconst outer = azimuthPoint(az, RIM);\n\t\t\tconst inner = azimuthPoint(az, RIM - (az % 45 === 0 ? 8 : 4));\n\t\t\tticks.push(\n\t\t\t\tsvg`<line class=\"tick\" x1=${inner.x} y1=${inner.y} x2=${outer.x} y2=${outer.y} />`,\n\t\t\t);\n\t\t}\n\t\t// Cardinal cross.\n\t\tconst ns1 = azimuthPoint(0, RIM);\n\t\tconst ns2 = azimuthPoint(180, RIM);\n\t\tconst ew1 = azimuthPoint(90, RIM);\n\t\tconst ew2 = azimuthPoint(270, RIM);\n\t\tconst labels = PRINCIPAL.map(({ az, source }) => {\n\t\t\tconst pos = azimuthPoint(az, TICK_LABEL_R);\n\t\t\tconst cardinal = az % 90 === 0;\n\t\t\treturn svg`<text class=${`compass-label${cardinal ? ' cardinal' : ''}`} x=${pos.x} y=${pos.y} text-anchor=\"middle\" dominant-baseline=\"central\">${this.t(source)}</text>`;\n\t\t});\n\t\treturn svg`\n\t\t\t<line class=\"cardinal-axis\" x1=${ns1.x} y1=${ns1.y} x2=${ns2.x} y2=${ns2.y} />\n\t\t\t<line class=\"cardinal-axis\" x1=${ew1.x} y1=${ew1.y} x2=${ew2.x} y2=${ew2.y} />\n\t\t\t${ticks}${labels}`;\n\t}\n\n\tprivate renderSpokes(bodies: Body[]) {\n\t\treturn bodies.map((b, i) => {\n\t\t\tconst color = planetColor(b.planet, i);\n\t\t\tconst below = b.aboveHorizon === false;\n\t\t\tconst end = azimuthPoint(b.azimuth, SPOKE);\n\t\t\tconst glyphPos = azimuthPoint(b.azimuth, GLYPH_R);\n\t\t\t// Response symbol first, then the shared table, then the full name. Never a\n\t\t\t// truncation: `North Node.slice(0, 2)` drew a compass spoke labelled \"No\".\n\t\t\tconst glyph = b.symbol || planetGlyph(b.planet) || b.planet;\n\t\t\tconst altLabel = `${b.altitude > 0 ? '+' : ''}${Math.round(b.altitude)}°`;\n\t\t\treturn svg`<g>\n\t\t\t\t<line class=${`spoke${below ? ' below' : ''}`} stroke=${color} x1=${CENTER} y1=${CENTER} x2=${end.x} y2=${end.y}><title>${this.t('{{planet}} {{direction}} {{azimuth}}° altitude {{altitude}}', { planet: b.planet, direction: b.compassDirection, azimuth: Math.round(b.azimuth), altitude: altLabel })}</title></line>\n\t\t\t\t<text class=${`body-glyph${below ? ' below' : ''}`} fill=${color} x=${glyphPos.x} y=${glyphPos.y} text-anchor=\"middle\" dominant-baseline=\"central\">${glyph}</text>\n\t\t\t</g>`;\n\t\t});\n\t}\n\n\tprivate renderList(bodies: Body[]) {\n\t\tif (bodies.length === 0) return nothing;\n\t\treturn html`<div class=\"table-scroll\" part=\"table\"><table class=\"list\"><caption class=\"roxy-sr-only\">${this.t(\n\t\t\t\t\t'Local space directions: each body with its compass direction, azimuth, altitude and whether it sits above or below the horizon.',\n\t\t\t\t)}</caption><thead><tr><th scope=\"col\">${this.t('Body')}</th><th scope=\"col\">${this.t('Direction')}</th><th scope=\"col\" class=\"num\">${this.t('Azimuth')}</th><th scope=\"col\" class=\"num\">${this.t('Altitude')}</th><th scope=\"col\">${this.t('Horizon')}</th></tr></thead><tbody>${bodies.map((b, i) => {\n\t\t\t\t\tconst color = planetColor(b.planet, i);\n\t\t\t\t\tconst below = b.aboveHorizon === false;\n\t\t\t\t\treturn html`<tr><td><span class=\"body-cell\"><span class=\"body-dot\" style=\"${`background: ${color}`}\"></span> ${b.symbol ? html`${b.symbol}` : nothing}${b.planet}</span></td><td>${b.compassDirection}</td><td class=\"num\">${Math.round(b.azimuth)}°</td><td class=\"num\">${b.altitude > 0 ? '+' : ''}${Math.round(b.altitude)}°</td><td><span class=\"${`horizon-pill ${below ? 'down' : 'up'}`}\">${below ? this.t('Below') : this.t('Above')}</span></td></tr>`;\n\t\t\t\t})}</tbody></table></div>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-local-space-compass': RoxyLocalSpaceCompass;\n\t}\n}\n", "import { css, html, nothing, type PropertyValues } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { RoxyLocalizedElement } from '../i18n/localized-element.js';\nimport type { SearchCitiesResponse } from '../types/index.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { debounce } from '../utils/debounce.js';\nimport { DEFAULT_BASE_URL, readApiError } from '../utils/fetch-controller.js';\nimport {\n\tdispatchKeyRefusal,\n\tKEY_REFUSED_MESSAGE,\n\tkeyIsRefused,\n} from '../utils/key-guard.js';\n\ntype CityResult = SearchCitiesResponse['cities'][number];\n\n/**\n * Stateful location search input. Calls /location/search and emits\n * `roxy-location-select` CustomEvent with the chosen city. Required for any\n * chart endpoint.\n *\n * Behavior: 300ms input debounce, click-outside dismiss, keyboard navigation\n * with arrow keys / Enter / Escape, AbortController on stale requests. A search\n * that fails prints the reason under the field and keeps it there until the\n * query changes, so a key or quota fault is legible where it happens instead of\n * surfacing later as a missing field on submit.\n *\n * Attributes:\n *   api-key            optional. Direct call to roxyapi.com when set.\n *   publishable-key    optional. Browser-safe pk_* key with allowed_origins.\n *   endpoint           optional. Where the search sends its request. Absolute, or relative to the\n *                      page, so a site that routes API traffic through its own server names that\n *                      route here. Defaults to the public location endpoint.\n *   placeholder        optional. Input placeholder. Translated when it is the default (see below).\n *   default-value      optional. Pre-filled query.\n *   lang               optional. Display language. Resolves from the page when absent, EXCEPT\n *                      inside another component shadow root, which is why the form forwards it.\n *\n * The words this box writes (its placeholder, its empty state, its busy label, its refusal\n * message) read the page language. The CITY NAMES do not: they are the values the location API\n * returned and are printed as they came back, so the box and the response can never disagree.\n */\n@customElement('roxy-location-search')\nexport class RoxyLocationSearch extends RoxyLocalizedElement {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`:host{display:block;position:relative}.field{position:relative}input{width:100%;padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);font-size:var(--roxy-text-base,1rem);color:var(--roxy-fg,#0a0a0a);background:var(--roxy-bg,#fff);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);transition:border-color var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,cubic-bezier(.4, 0, .2, 1));box-sizing:border-box;font-family:inherit}input:focus{outline:2px solid var(--roxy-ring,#f59e0b66);outline-offset:2px;border-color:var(--roxy-accent-ink,#b45309)}.spinner{border:2px solid var(--roxy-muted,#71717a);border-top-color:#0000;border-radius:50%;width:14px;height:14px;animation:.7s linear infinite roxy-spin;position:absolute;top:50%;right:12px;transform:translateY(-50%)}@keyframes roxy-spin{to{transform:translateY(-50%)rotate(360deg)}}@media (prefers-reduced-motion:reduce){.spinner{animation:none}}.results{z-index:50;padding:var(--roxy-space-xs,.25rem);background:var(--roxy-bg,#fff);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);box-shadow:var(--roxy-shadow-md);max-height:22rem;animation:roxy-fade-in var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,cubic-bezier(.4, 0, .2, 1));margin:0;list-style:none;position:absolute;top:calc(100% + 4px);left:0;right:0;overflow-y:auto}.option{align-items:center;gap:var(--roxy-space-sm,.5rem);width:100%;padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);border-radius:var(--roxy-radius-sm,6px);text-align:left;font-family:inherit;font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a);cursor:pointer;transition:background-color var(--roxy-motion-duration,.2s);background:0 0;border:0;display:flex}.option:hover,.option[aria-selected=true]{background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 10%, transparent)}.option .city{font-weight:var(--roxy-weight-bold,600)}.option .where{color:var(--roxy-muted,#71717a);flex-grow:1}.option .tz{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);font-variant-numeric:tabular-nums}.empty{padding:var(--roxy-space-md,1rem);color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem)}.search-error{margin:var(--roxy-space-xs,.25rem) 0 0;color:var(--roxy-danger-fg,#991b1b);font-size:var(--roxy-text-sm,.875rem)}`,\n\t];\n\n\t@property({ type: String, attribute: 'api-key' })\n\tapiKey?: string;\n\n\t@property({ type: String, attribute: 'publishable-key' })\n\tpublishableKey?: string;\n\n\t/** Where the search sends its request. Built from {@link DEFAULT_BASE_URL} rather than written out again, so this box and every data request name one origin. */\n\t@property({ type: String })\n\tendpoint = `${DEFAULT_BASE_URL}/location/search`;\n\n\t/** Input placeholder. The default is a catalogue entry and renders in the page language; a caller-supplied one is printed as given, because a miss returns the source string unchanged. */\n\t@property({ type: String })\n\tplaceholder = 'Search city';\n\n\t@property({ type: String, attribute: 'default-value' })\n\tdefaultValue = '';\n\n\t@state()\n\tprivate query = '';\n\n\t@state()\n\tprivate results: CityResult[] = [];\n\n\t@state()\n\tprivate isOpen = false;\n\n\t@state()\n\tprivate isLoading = false;\n\n\t@state()\n\tprivate highlight = -1;\n\n\t/** True when a non-publishable key is present, so the search fail-closes: no network call, a visible error, matching {@link FetchController}. */\n\t@state()\n\tprivate keyBlocked = false;\n\n\t/** Message from the last failed search, or null. Rendered under the field and outliving the dropdown, so a failed request reads as a failure rather than as a city that does not exist. */\n\t@state()\n\tprivate error: string | null = null;\n\n\tprivate clickOutsideHandler?: (e: MouseEvent) => void;\n\tprivate abortController?: AbortController;\n\tprivate debouncedFetch = debounce((q: string) => {\n\t\tvoid this.fetchResults(q);\n\t}, 300);\n\n\tconnectedCallback(): void {\n\t\tsuper.connectedCallback();\n\t\tthis.query = this.defaultValue;\n\t\tthis.clickOutsideHandler = (e: MouseEvent) => {\n\t\t\tconst path = e.composedPath();\n\t\t\tif (!path.includes(this)) this.isOpen = false;\n\t\t};\n\t\tdocument.addEventListener('mousedown', this.clickOutsideHandler);\n\t}\n\n\tprotected willUpdate(changed: PropertyValues): void {\n\t\tif (changed.has('publishableKey') || changed.has('apiKey')) {\n\t\t\t// Effective key mirrors fetchResults: publishable-key wins, api-key is the\n\t\t\t// legacy fallback. A set-but-non-pk_ key fail-closes here, warning once.\n\t\t\tconst refused = keyIsRefused(this.publishableKey ?? this.apiKey);\n\t\t\tif (refused && !this.keyBlocked) dispatchKeyRefusal(this, { warn: true });\n\t\t\tthis.keyBlocked = refused;\n\t\t}\n\t}\n\n\tdisconnectedCallback(): void {\n\t\tsuper.disconnectedCallback();\n\t\tif (this.clickOutsideHandler) {\n\t\t\tdocument.removeEventListener('mousedown', this.clickOutsideHandler);\n\t\t}\n\t\tthis.debouncedFetch.cancel();\n\t\tif (this.abortController) {\n\t\t\tthis.abortController.abort();\n\t\t\tthis.abortController = undefined;\n\t\t}\n\t}\n\n\tprivate async fetchResults(q: string) {\n\t\t// Fail closed: never send a non-pk_ key. willUpdate has already flipped\n\t\t// keyBlocked and surfaced the error; this guards the direct-call paths.\n\t\tif (this.keyBlocked) return;\n\t\t// Abort any in-flight request so a stale response cannot overwrite a\n\t\t// fresher one (debounced typing) or land after disconnect.\n\t\tif (this.abortController) this.abortController.abort();\n\t\tconst controller = new AbortController();\n\t\tthis.abortController = controller;\n\t\tthis.isLoading = true;\n\t\ttry {\n\t\t\t// Resolved against the page, so a same-origin route (\"/api/roxy/location/search\")\n\t\t\t// is as valid an endpoint as an absolute URL. A site that proxies its API traffic\n\t\t\t// writes the first shape, and a bare `new URL()` accepts only the second.\n\t\t\tconst url = new URL(this.endpoint, document.baseURI);\n\t\t\turl.searchParams.set('q', q);\n\t\t\turl.searchParams.set('limit', '8');\n\t\t\tconst headers: Record<string, string> = {\n\t\t\t\tAccept: 'application/json',\n\t\t\t};\n\t\t\t// publishable-key wins when both are set. Surfacing the conflict at the\n\t\t\t// console (not as a thrown error) avoids breaking widgets that legitimately\n\t\t\t// have a stale secret key around while migrating to publishable.\n\t\t\tif (this.apiKey && this.publishableKey) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t'[roxy-location-search] both api-key and publishable-key set; using publishable-key. Remove api-key from your widget markup.',\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst key = this.publishableKey ?? this.apiKey;\n\t\t\tif (key) headers['X-API-Key'] = key;\n\t\t\tconst res = await fetch(url, { headers, signal: controller.signal });\n\t\t\tif (!res.ok) throw new Error(await readApiError(res));\n\t\t\tconst json = (await res.json()) as SearchCitiesResponse;\n\t\t\tif (controller.signal.aborted) return;\n\t\t\tthis.error = null;\n\t\t\tthis.results = json.cities ?? [];\n\t\t\t// Open even on zero matches, or the empty state can never render: gating this\n\t\t\t// on `length > 0` closed the box on an empty result and said nothing at all.\n\t\t\tthis.isOpen = true;\n\t\t\tthis.highlight = this.results.length > 0 ? 0 : -1;\n\t\t} catch (err) {\n\t\t\tif ((err as { name?: string })?.name === 'AbortError') return;\n\t\t\tthis.results = [];\n\t\t\tthis.isOpen = false;\n\t\t\tthis.highlight = -1;\n\t\t\tthis.error = err instanceof Error ? err.message : String(err);\n\t\t} finally {\n\t\t\tif (this.abortController === controller) {\n\t\t\t\tthis.abortController = undefined;\n\t\t\t}\n\t\t\tif (!controller.signal.aborted) this.isLoading = false;\n\t\t}\n\t}\n\n\tprivate onInput = (e: Event) => {\n\t\tconst value = (e.target as HTMLInputElement).value;\n\t\tthis.query = value;\n\t\tif (value.length < 2) {\n\t\t\tthis.results = [];\n\t\t\tthis.isOpen = false;\n\t\t\tthis.highlight = -1;\n\t\t\tthis.error = null;\n\t\t\treturn;\n\t\t}\n\t\tthis.debouncedFetch(value);\n\t};\n\n\tprivate select(city: CityResult) {\n\t\tthis.query = `${city.city}${city.province ? `, ${city.province}` : ''}, ${city.country}`;\n\t\tthis.isOpen = false;\n\t\tthis.results = [];\n\t\tthis.dispatchEvent(\n\t\t\tnew CustomEvent('roxy-location-select', {\n\t\t\t\tdetail: city,\n\t\t\t\tbubbles: true,\n\t\t\t\tcomposed: true,\n\t\t\t}),\n\t\t);\n\t}\n\n\tprivate onKeyDown = (e: KeyboardEvent) => {\n\t\tif (!this.isOpen || this.results.length === 0) {\n\t\t\tif (e.key === 'ArrowDown' && this.query.length >= 2) {\n\t\t\t\tvoid this.fetchResults(this.query);\n\t\t\t\te.preventDefault();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (e.key === 'ArrowDown') {\n\t\t\te.preventDefault();\n\t\t\tthis.highlight = (this.highlight + 1) % this.results.length;\n\t\t} else if (e.key === 'ArrowUp') {\n\t\t\te.preventDefault();\n\t\t\tthis.highlight =\n\t\t\t\t(this.highlight - 1 + this.results.length) % this.results.length;\n\t\t} else if (e.key === 'Enter') {\n\t\t\te.preventDefault();\n\t\t\tconst target = this.results[this.highlight] ?? this.results[0];\n\t\t\tif (target) this.select(target);\n\t\t} else if (e.key === 'Escape') {\n\t\t\tthis.isOpen = false;\n\t\t}\n\t};\n\n\trender() {\n\t\tif (this.keyBlocked) {\n\t\t\treturn html`<div class=\"roxy-error\" role=\"alert\">${this.t(KEY_REFUSED_MESSAGE)}</div>`;\n\t\t}\n\t\treturn html`<div class=\"field\"><input part=\"input\" type=\"text\" role=\"combobox\" aria-expanded=\"${this.isOpen ? 'true' : 'false'}\" aria-controls=\"roxy-location-listbox\" aria-activedescendant=\"${\n\t\t\t\t\tthis.isOpen && this.highlight >= 0\n\t\t\t\t\t\t? `roxy-location-option-${this.highlight}`\n\t\t\t\t\t\t: ''\n\t\t\t\t}\" aria-autocomplete=\"list\" autocomplete=\"off\" placeholder=\"${this.t(this.placeholder)}\" .value=\"${this.query}\" @input=\"${this.onInput}\" @keydown=\"${this.onKeyDown}\" @focus=\"${() => {\n\t\t\t\t\tif (this.results.length > 0) this.isOpen = true;\n\t\t\t\t}}\"> ${\n\t\t\t\tthis.isLoading\n\t\t\t\t\t? html`<span class=\"spinner\" role=\"status\" aria-label=\"${this.t('Loading')}\"></span>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\tthis.isOpen\n\t\t\t\t\t? html`<ul id=\"roxy-location-listbox\" part=\"results\" class=\"results\" role=\"listbox\">${\n\t\t\t\t\t\t\tthis.results.length === 0\n\t\t\t\t\t\t\t\t? html`<li class=\"empty\" role=\"status\">${this.t('No cities found')}</li>`\n\t\t\t\t\t\t\t\t: this.results.map(\n\t\t\t\t\t\t\t\t\t\t(city, idx) => html`<li role=\"presentation\"><button type=\"button\" part=\"option\" class=\"option\" role=\"option\" id=\"${`roxy-location-option-${idx}`}\" aria-selected=\"${this.highlight === idx ? 'true' : 'false'}\" @click=\"${() => this.select(city)}\" @mouseenter=\"${() => {\n\t\t\t\t\t\t\t\t\t\t\t\tthis.highlight = idx;\n\t\t\t\t\t\t\t\t\t\t\t}}\"><span class=\"city\">${city.city}</span> <span class=\"where\">${city.province ? html`${city.province},` : ''}${city.country}</span> <span class=\"tz\">UTC${city.utcOffset >= 0 ? '+' : ''}${city.utcOffset}</span></button></li>`,\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}</ul>`\n\t\t\t\t\t: nothing\n\t\t\t}</div>${\n\t\t\t/*\n\t\t\t * Outside `.field` on purpose, so it is not part of the dropdown and does not\n\t\t\t * close with it. The click that submits the surrounding form also dismisses\n\t\t\t * the dropdown through the click-outside handler, and that is the same moment\n\t\t\t * the form reports the field as unfilled, so a reason rendered inside the\n\t\t\t * popup would disappear exactly when it becomes worth reading.\n\t\t\t *\n\t\t\t * Through `t()` for the same reason {@link RoxyDataElement.renderError} does\n\t\t\t * it: most of what lands here is a wire fact that misses the catalogue and\n\t\t\t * renders unchanged, but one of them is ours, and translating a message on\n\t\t\t * one surface and not the other puts two languages on one page.\n\t\t\t */\n\t\t\tthis.error\n\t\t\t\t? html`<p class=\"search-error\" role=\"alert\">${this.t(this.error)}</p>`\n\t\t\t\t: nothing\n\t\t}`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-location-search': RoxyLocationSearch;\n\t}\n}\n", "/**\n * Lightweight debounce for input handlers. Used by location search.\n *\n * The returned function exposes a `.cancel()` method so callers can clear a\n * pending invocation when the host element disconnects, preventing the timer\n * from firing on a detached node and mutating reactive state after teardown.\n */\nexport interface Debounced<F extends (...args: never[]) => unknown> {\n\t(...args: Parameters<F>): void;\n\tcancel: () => void;\n}\n\nexport function debounce<F extends (...args: never[]) => unknown>(\n\tfn: F,\n\twait: number,\n): Debounced<F> {\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tconst debounced = ((...args: Parameters<F>) => {\n\t\tif (timer) clearTimeout(timer);\n\t\ttimer = setTimeout(() => {\n\t\t\ttimer = undefined;\n\t\t\tfn(...args);\n\t\t}, wait);\n\t}) as Debounced<F>;\n\tdebounced.cancel = () => {\n\t\tif (timer) {\n\t\t\tclearTimeout(timer);\n\t\t\ttimer = undefined;\n\t\t}\n\t};\n\treturn debounced;\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { moonPhaseEmoji } from '../tokens/index.js';\nimport type {\n\tGetCurrentMoonPhaseResponse,\n\tGetMoonCalendarResponse,\n\tGetUpcomingMoonPhasesResponse,\n} from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { formatDate, formatNumber, formatPercent } from '../utils/format.js';\n\ntype MoonPhaseData =\n\t| GetCurrentMoonPhaseResponse\n\t| GetUpcomingMoonPhasesResponse\n\t| GetMoonCalendarResponse;\ntype MoonListEntry =\n\t| GetUpcomingMoonPhasesResponse['phases'][number]\n\t| GetMoonCalendarResponse['calendar'][number];\n\n/**\n * Moon phase card. Renders /astrology/moon-phase/{current,upcoming,calendar/...}.\n *\n * @remarks\n * `hide-readings` keeps the whole ephemeris half: the phase glyph, the phase name, the date, and the illumination, age, sign and distance tiles, plus every row of the upcoming and calendar lists. Only `meaning.description` and its keyword chips go. The glyph is unaffected because it comes from `meaning.symbol`, which is the phase drawn rather than the phase interpreted.\n */\n@customElement('roxy-moon-phase')\nexport class RoxyMoonPhase extends RoxyDataElement<MoonPhaseData> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.card{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.hero{align-items:center;gap:var(--roxy-space-md,1rem);display:flex}.emoji{font-size:3rem;line-height:1}.label{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);text-transform:capitalize;margin:0}.date{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem)}.stats{gap:var(--roxy-space-md,1rem);font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-secondary,#475569);grid-template-columns:repeat(auto-fit,minmax(8rem,1fr));display:grid}.stats div span:first-child{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);text-transform:uppercase;letter-spacing:.06em;display:block}.stats strong{color:var(--roxy-fg,#0a0a0a);font-variant-numeric:tabular-nums}.meaning{color:var(--roxy-fg,#0a0a0a)}.keywords{gap:var(--roxy-space-xs,.25rem);margin-top:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.keywords span{background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 14%, transparent);border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);padding:2px 8px}.list{gap:var(--roxy-space-sm,.5rem);display:grid}.list-item{gap:var(--roxy-space-sm,.5rem);border-bottom:1px solid var(--roxy-border,#e4e4e7);padding:var(--roxy-space-sm,.5rem) 0;font-size:var(--roxy-text-sm,.875rem);grid-template-columns:2.5rem 1fr auto;align-items:center;display:grid}.list-item:last-child{border-bottom:none}`,\n\t];\n\n\t@property({ type: String, reflect: true })\n\tmode: 'current' | 'upcoming' | 'calendar' = 'current';\n\n\tprotected renderData(d: MoonPhaseData) {\n\t\tconst list: MoonListEntry[] =\n\t\t\t'phases' in d ? d.phases : 'calendar' in d ? d.calendar : [];\n\t\tif (this.mode !== 'current' && list.length > 0) {\n\t\t\tconst month = 'month' in d ? d.month : undefined;\n\t\t\tconst year = 'year' in d ? d.year : undefined;\n\t\t\treturn html`<article class=\"card\" part=\"card\" aria-label=\"${this.t('Moon phase calendar')}\"><h2 class=\"label\" part=\"header\">${month ?? 'Moon phases'} ${year ?? ''}</h2><div class=\"list\" part=\"table\" role=\"list\">${list.map((phase) => this.renderListItem(phase))}</div></article>`;\n\t\t}\n\t\tif (!('phase' in d)) return nothing;\n\t\treturn this.renderSingle(d);\n\t}\n\n\tprivate renderSingle(d: GetCurrentMoonPhaseResponse) {\n\t\t// The API ships the exact phase emoji in meaning.symbol; prefer it and fall\n\t\t// back to the name-derived glyph for the list endpoints that omit meaning.\n\t\tconst emoji = d.meaning?.symbol || phaseEmoji(d.phase);\n\t\treturn html`<article class=\"card\" part=\"card\" aria-label=\"${this.t('Current moon phase')}\"><div class=\"hero\" part=\"header\"><span class=\"emoji\" aria-hidden=\"true\">${emoji}</span><div><h2 class=\"label\">${d.phase ?? 'Moon'}</h2>${d.date ? html`<div class=\"date\">${formatDate(this.effectiveLang(), d.date)}</div>` : nothing}</div></div><div class=\"stats\" part=\"details\">${\n\t\t\t\t\ttypeof d.illumination === 'number'\n\t\t\t\t\t\t? html`<div><span>${this.t('Illumination')}</span> <strong>${formatPercent(this.effectiveLang(), d.illumination <= 1 ? d.illumination * 100 : d.illumination, 0)}</strong></div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t} ${\n\t\t\t\t\ttypeof d.age === 'number'\n\t\t\t\t\t\t? html`<div><span>${this.t('Age')}</span> <strong>${this.t('{{count}} days', { count: formatNumber(this.effectiveLang(), d.age, 1) })}</strong></div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t} ${\n\t\t\t\t\td.sign\n\t\t\t\t\t\t? html`<div><span>${this.t('Sign')}</span> <strong>${d.sign}</strong></div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t} ${\n\t\t\t\t\ttypeof d.distance === 'number'\n\t\t\t\t\t\t? html`<div><span>${this.t('Distance')}</span> <strong>${this.t('{{value}}k km', { value: formatNumber(this.effectiveLang(), d.distance / 1000, 0) })}</strong></div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</div>${\n\t\t\t\t// The tiles above are the ephemeris; the description and the chips that\n\t\t\t\t// belong to it are what the phase is taken to mean.\n\t\t\t\td.meaning?.description && !this.hideReadings\n\t\t\t\t\t? html`<p class=\"meaning\">${d.meaning.description}</p>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\td.meaning?.keywords?.length && !this.hideReadings\n\t\t\t\t\t? html`<div class=\"keywords\">${d.meaning.keywords.map((k) => html`<span>${k}</span>`)}</div>`\n\t\t\t\t\t: nothing\n\t\t\t}</article>`;\n\t}\n\n\tprivate renderListItem(p: MoonListEntry) {\n\t\tconst emoji = phaseEmoji(p.phase);\n\t\treturn html`<div class=\"list-item\" role=\"listitem\"><span aria-hidden=\"true\">${emoji}</span> <span>${p.phase}</span> <span>${formatDate(this.effectiveLang(), p.date)}</span></div>`;\n\t}\n}\n\n/**\n * Map a phase name to its emoji, tolerant of the live API naming. The\n * suffix-stripping and third/last quarter alias reconciliation now live in\n * {@link moonPhaseEmoji}, so every caller of the token resolves the same eight\n * phases from the same live API spellings.\n */\nfunction phaseEmoji(phase: string | undefined): string {\n\treturn moonPhaseEmoji(phase) ?? '🌙';\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-moon-phase': RoxyMoonPhase;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport type { NakshatraResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\n\n/**\n * Nakshatra reference card. Renders /vedic-astrology/nakshatras/{id}: the\n * lunar mansion number, longitude range, ruling planet, presiding deity,\n * symbol, native characteristics, and traditional remedies.\n *\n * @remarks\n * The classical attribution table is the card and it survives `hide-readings`\n * whole: the name, which of the twenty-seven it is, the longitude range it spans,\n * and its lord, deity and symbol. Those are what a practitioner reads a placement\n * against. The characteristics paragraph and the remedies are the reading, and\n * each carries its own heading, so each section goes whole.\n */\n@customElement('roxy-nakshatra-card')\nexport class RoxyNakshatraCard extends RoxyDataElement<NakshatraResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.wrap{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);padding:var(--roxy-space-md,1rem);gap:var(--roxy-space-md,1rem);box-shadow:var(--roxy-shadow-sm);grid-template-columns:minmax(0,1fr);display:grid}.head{align-items:baseline;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.name{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.number{color:var(--roxy-accent-ink,#b45309);font-size:var(--roxy-text-sm,.875rem);font-weight:var(--roxy-weight-bold,600)}.range{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem)}.facts{gap:var(--roxy-space-sm,.5rem);grid-template-columns:repeat(auto-fit,minmax(8rem,1fr));display:grid}.fact{gap:2px;display:grid}.fact dt{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);text-transform:uppercase;letter-spacing:.05em}.fact dd{color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-sm,.875rem);margin:0}.section h3{margin:0 0 var(--roxy-space-xs,.25rem);font-size:var(--roxy-text-sm,.875rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.05em}.section p{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a);margin:0;line-height:1.5}.remedies{gap:var(--roxy-space-xs,.25rem);display:grid}.remedy{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a)}.remedy strong{color:var(--roxy-muted,#71717a);font-weight:var(--roxy-weight-bold,600)}`,\n\t];\n\n\tprotected renderData(n: NakshatraResponse) {\n\t\tconst remedies = n.remedies;\n\n\t\treturn html`<article class=\"wrap\" part=\"card\" aria-label=\"${this.t('Nakshatra {{name}}', { name: n.name })}\"><header class=\"head\" part=\"header\"><h2 class=\"name\">${n.name}</h2>${\n\t\t\t\t\ttypeof n.number === 'number'\n\t\t\t\t\t\t? html`<span class=\"number\">${this.t('Nakshatra {{number}} of 27', { number: n.number })}</span>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t} ${n.range ? html`<span class=\"range\">${n.range}</span>` : nothing}</header><dl class=\"facts\" part=\"details\">${n.lord ? html`<div class=\"fact\"><dt>${this.t('Lord')}</dt><dd>${n.lord}</dd></div>` : nothing} ${n.deity ? html`<div class=\"fact\"><dt>${this.t('Deity')}</dt><dd>${n.deity}</dd></div>` : nothing} ${n.symbol ? html`<div class=\"fact\"><dt>${this.t('Symbol')}</dt><dd>${n.symbol}</dd></div>` : nothing}</dl>${\n\t\t\t\t// The lord, deity and symbol above are the mansion; this paragraph is\n\t\t\t\t// what a native of it is said to be like.\n\t\t\t\tn.characteristics && !this.hideReadings\n\t\t\t\t\t? html`<div class=\"section\" part=\"section characteristics\"><h3>${this.t('Characteristics')}</h3><p>${n.characteristics}</p></div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\tremedies && !this.hideReadings\n\t\t\t\t\t? html`<div class=\"section\" part=\"section remedies\"><h3>${this.t('Remedies')}</h3><div class=\"remedies\">${remedies.mantras ? html`<div class=\"remedy\"><strong>${this.t('Mantras:')}</strong> ${remedies.mantras}</div>` : nothing} ${remedies.gemstones ? html`<div class=\"remedy\"><strong>${this.t('Gemstones:')}</strong> ${remedies.gemstones}</div>` : nothing} ${remedies.rituals ? html`<div class=\"remedy\"><strong>${this.t('Rituals:')}</strong> ${remedies.rituals}</div>` : nothing}</div></div>`\n\t\t\t\t\t: nothing\n\t\t\t}</article>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-nakshatra-card': RoxyNakshatraCard;\n\t}\n}\n", "import { css, html, nothing, svg } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport {\n\taspectSymbol,\n\tplanetGlyph,\n\tSIGNS_ORDER,\n\tsignGlyph,\n} from '../tokens/index.js';\nimport type { NatalChartResponse } from '../types/index.js';\nimport { aspectLineStyle, aspectLineStyles } from '../utils/aspect-line.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport {\n\tarcMidpoint,\n\tarcSeparation,\n\tfanOut,\n\tlongitudeToSignPosition,\n\tnormalizeLongitude,\n\toppositePoint,\n\tpolarToCartesian,\n} from '../utils/degree.js';\nimport { disclosureStyles } from '../utils/disclosure.js';\nimport {\n\tASPECT_CLASS,\n\tformatAspectName,\n\tformatDateTime,\n\tformatNumber,\n\tformatPercent,\n\tnormalizeAspect,\n} from '../utils/format.js';\nimport {\n\ttype InterpSection,\n\tinterpAccordionStyles,\n\trenderKeywordChips,\n} from '../utils/interp-accordion.js';\nimport { display, displayList } from '../utils/localized.js';\nimport { capitalize, lookupKey } from '../utils/string.js';\nimport { renderTablist, tablistStyles } from '../utils/tablist.js';\n\ntype PlanetEntry = NatalChartResponse['planets'][number];\ntype AspectEntry = NatalChartResponse['aspects'][number];\ntype PatternEntry = NonNullable<NatalChartResponse['patterns']>[number];\n\n/**\n * Reading order for detected configurations. A practitioner reads the rare, chart-defining figures first (a Grand Cross reframes the whole chart) and the common ones last, so the response order is re-sorted rather than dumped. Ties break on tightness, the closest to exact first.\n */\nconst PATTERN_ORDER: Record<string, number> = {\n\tGRAND_CROSS: 0,\n\tGRAND_TRINE: 1,\n\tKITE: 2,\n\tT_SQUARE: 3,\n\tYOD: 4,\n\tMYSTIC_RECTANGLE: 5,\n\tSTELLIUM: 6,\n};\n\nconst SIZE = 420;\nconst CENTER = SIZE / 2;\nconst OUTER_R = 164;\nconst SIGN_R = 146;\nconst HOUSE_R = 120;\nconst PLANET_R = 96;\nconst ANGLE_TICK_R = 178;\nconst ANGLE_LABEL_R = 196;\n/**\n * How wide each mark on the wheel is, in user units, so {@link arcSeparation}\n * can turn it into the degrees of arc that mark needs at its own radius. These\n * are type metrics rather than shared constants: the glyph width tracks the\n * `.planet-glyph` font size, the degree allowance the `.planet-deg` one, and the\n * angle label the widest of `ASC`, `DSC`, `MC`, `IC`, `PoF` and `Vtx` plus the\n * gap that keeps two of them apart.\n */\nconst GLYPH_WIDTH = 14;\nconst DEG_LABEL_WIDTH = 15;\nconst ANGLE_LABEL_WIDTH = 28;\n\n/**\n * The chart shape the wheel actually renders.\n *\n * @remarks\n * `RelocationChartResponse` carries the same planets, houses, angles and birth details as `NatalChartResponse` (`RelocationPlanet` is field-for-field identical to the natal planet item), and differs only by having no `aspects`, `aspectsInterpretation`, `patterns`, `partOfFortune` or `summary`. The wheel already renders without them, so those five are simply optional here.\n *\n * Typing that contract is what lets {@link RoxyRelocationWheel} compose this component honestly. It used to pass its response through a `data as unknown as NatalChartResponse` double cast, which silently asserted five required fields that were not there.\n */\ntype ChartExtras =\n\t| 'aspects'\n\t| 'aspectsInterpretation'\n\t| 'patterns'\n\t| 'partOfFortune'\n\t| 'summary';\nexport type WheelChart = Omit<NatalChartResponse, ChartExtras> &\n\tPartial<Pick<NatalChartResponse, ChartExtras>>;\n\n/**\n * Western natal chart wheel. Renders the 12 zodiac signs, 12 houses, planet\n * markers, and aspect lines from a /astrology/natal-chart response.\n *\n * @remarks\n * **There is deliberately no `house-system` input, and re-adding one would be a\n * lie in the types.** The house system is a REQUEST parameter: /astrology/natal-chart\n * and /astrology/relocation-chart both take it and both echo the system they\n * actually used as a required `houseSystem` field, which is what the legend\n * chip reads. This component computes nothing astrological, so an attribute\n * could never move a cusp; all it could do is print a label contradicting the\n * cusps drawn beside it. One shipped anyway, declared and typed and wired\n * through both framework wrappers, and nothing ever read it, so a consumer who\n * followed our own generated types set it and got silence. Change the system in\n * the REQUEST (the self-fetch form already offers it, straight off the spec) and\n * the response relabels itself.\n *\n * **The equal-sector fallback is visible, on purpose.** With fewer than twelve\n * cusps in the response the wheel draws twelve equal 30 degree sectors from the\n * Ascendant, which is a different chart from a Placidus one; it says so in the\n * accessible name and in a legend chip rather than numbering the sectors 1 to 12\n * and letting a reader assume the response carried them.\n */\n@customElement('roxy-natal-chart')\nexport class RoxyNatalChart extends RoxyDataElement<WheelChart> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\taspectLineStyles,\n\t\ttablistStyles,\n\t\tdisclosureStyles,\n\t\tinterpAccordionStyles,\n\t\tcss`.wrap{background:var(--roxy-surface,#fff);width:100%;color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-primary,#0f172a);margin:0}.meta{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem)}svg{width:100%;max-width:var(--roxy-chart-max-width,560px);aspect-ratio:1;height:auto;margin:0 auto;display:block}.wheel-line{fill:none;stroke:var(--roxy-border,#e4e4e7)}.sign-glyph{fill:var(--roxy-secondary,#475569);font-size:14px;font-family:var(--roxy-font-sans)}.planet-glyph{fill:var(--roxy-accent,#f59e0b);font-size:14px;font-weight:600;font-family:var(--roxy-font-sans)}.planet-deg{fill:var(--roxy-fg,#0a0a0a);font-size:7px;font-family:var(--roxy-font-sans)}@container (width<=480px){.sign-glyph,.planet-glyph{font-size:18px}.planet-deg{font-size:10px}.house-num{font-size:12px}}.planet-deg .retro{fill:var(--roxy-danger,#dc2626)}.planet-leader{stroke:var(--roxy-accent,#f59e0b);stroke-width:.5px;opacity:.55}.house-num{fill:var(--roxy-muted,#71717a);font-size:9px;font-family:var(--roxy-font-sans)}.cusp-deg{fill:var(--roxy-muted,#71717a);font-size:6px;font-family:var(--roxy-font-sans)}.tick{stroke:var(--roxy-border,#e4e4e7)}.tick-major{stroke:var(--roxy-secondary,#475569)}.angle-marker{fill:var(--roxy-accent-ink,#b45309);font-size:10px;font-weight:700;font-family:var(--roxy-font-sans);letter-spacing:.04em}.angle-tick{stroke:var(--roxy-accent-ink,#b45309);stroke-width:1.5px}.legend{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);gap:var(--roxy-space-md,1rem);flex-wrap:wrap;display:flex}.legend-swatch{vertical-align:middle;border-radius:50%;width:8px;height:8px;margin-right:4px;display:inline-block}.legend .caveat{color:var(--roxy-fg,#0a0a0a);font-weight:var(--roxy-weight-bold,600);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-full,9999px);padding:1px 8px}.grid-scroll{-webkit-overflow-scrolling:touch;min-width:0;overflow-x:auto}table.aspect-grid{border-collapse:collapse;font-size:var(--roxy-text-xs,.75rem);margin:0 auto}table.aspect-grid th,table.aspect-grid td{text-align:center;border:1px solid var(--roxy-border,#e4e4e7);width:1.6rem;height:1.6rem;padding:0}table.aspect-grid th{color:var(--roxy-secondary,#475569);font-weight:var(--roxy-weight-bold,600)}table.aspect-grid td.cell{cursor:default}table.aspect-grid td.empty{background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 18%, transparent)}table.aspect-grid td .asp{font-size:.95em;line-height:1}table.aspect-grid td.aspect-trine .asp,table.aspect-grid td.aspect-sextile .asp{color:var(--roxy-success,#16a34a)}table.aspect-grid td.aspect-square .asp,table.aspect-grid td.aspect-opposition .asp{color:var(--roxy-danger,#dc2626)}table.aspect-grid td.aspect-conjunction .asp{color:var(--roxy-accent-ink,#b45309)}table.aspect-grid td.aspect-other .asp{color:var(--roxy-muted,#71717a)}.details{margin-top:var(--roxy-space-md,1rem)}.pill-row{gap:var(--roxy-space-xs,.25rem);margin-bottom:var(--roxy-space-xs,.25rem);flex-wrap:wrap;display:flex}.pill{border-radius:var(--roxy-radius-sm,4px);font-size:var(--roxy-text-xs,.75rem);background:color-mix(in srgb, var(--roxy-fg,#0f172a) 8%, transparent);color:var(--roxy-fg,#0f172a);padding:2px 8px}.pill--success{background:color-mix(in srgb, var(--roxy-success,#16a34a) 15%, transparent);color:var(--roxy-success-fg,#166534)}.pill--danger{background:color-mix(in srgb, var(--roxy-danger,#dc2626) 15%, transparent);color:var(--roxy-danger-fg,#991b1b)}.pill--muted{background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 60%, transparent);color:var(--roxy-fg,#0a0a0a)}.summary{color:var(--roxy-fg,#0f172a);font-size:var(--roxy-text-sm,.875rem);margin:var(--roxy-space-md,1rem) 0}.em-grid{border-collapse:collapse;font-size:var(--roxy-text-xs,.75rem);width:100%}.em-grid caption{caption-side:top;text-align:left;color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);padding-bottom:var(--roxy-space-xs,.25rem)}.em-grid th,.em-grid td{border:1px solid var(--roxy-border,#e4e4e7);text-align:center;vertical-align:middle;padding:3px 5px}.em-grid th{color:var(--roxy-muted,#71717a);font-weight:var(--roxy-weight-bold,600);text-transform:uppercase;letter-spacing:.04em}.em-grid th[scope=row]{text-align:left}.em-grid td{color:var(--roxy-accent,#f59e0b);min-width:1.4rem;font-size:.95em;line-height:1.4}.em-grid .em-total{color:var(--roxy-fg,#0a0a0a);font-weight:var(--roxy-weight-bold,600);background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 25%, transparent)}.em-grid .dominant{background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 10%, transparent)}.em-grid th.dominant,.em-grid td.em-total.dominant{color:var(--roxy-fg,#0a0a0a)}.pattern{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);margin-bottom:var(--roxy-space-xs,.25rem);gap:.35rem;display:grid}.pattern-head{flex-wrap:wrap;align-items:baseline;gap:.5rem;display:flex}.pattern-name{font-weight:var(--roxy-weight-bold,600)}.pattern-tag{border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 55%, transparent);color:var(--roxy-fg,#0a0a0a);text-transform:capitalize;padding:1px 8px}.pattern-tight{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);font-variant-numeric:tabular-nums;margin-left:auto}.pattern-planets{flex-wrap:wrap;gap:.25rem;display:flex}.planet-chip{border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 45%, transparent);color:var(--roxy-fg,#0a0a0a);align-items:baseline;gap:.3rem;padding:1px 8px;display:inline-flex}.planet-chip.apex{background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 20%, transparent);font-weight:var(--roxy-weight-bold,600)}.planet-chip .apex-tag{text-transform:uppercase;letter-spacing:.04em;font-size:.85em;font-weight:400}.pattern-interp{color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-sm,.875rem);margin:0;line-height:1.45}`,\n\t];\n\n\t/** Heading above the wheel. Defaults to \"Natal chart\"; reuse (e.g. the relocation wheel) sets its own. */\n\t@property({ type: String })\n\theading = 'Natal chart';\n\n\t/** Which view is showing: the wheel or the planet-by-planet aspect grid. */\n\t@state()\n\tprivate view: 'wheel' | 'grid' = 'wheel';\n\n\tprivate getPlanets(): PlanetEntry[] {\n\t\treturn this.data?.planets ?? [];\n\t}\n\n\tprivate getAscendant(): number {\n\t\treturn this.data?.ascendant?.longitude ?? 0;\n\t}\n\n\tprivate getMidheaven(): number | null {\n\t\tconst m = this.data?.midheaven?.longitude;\n\t\treturn typeof m === 'number' ? m : null;\n\t}\n\n\t/**\n\t * True when the response carried all twelve house cusps, so every house on the\n\t * wheel is a longitude the API sent rather than a 30 degree sector this\n\t * component invented. Read by the spokes, the numbers, the cusp degrees, the\n\t * accessible name and the legend, so the drawing and the words about it cannot\n\t * disagree about which chart is on screen.\n\t */\n\tprivate get hasCusps(): boolean {\n\t\treturn (this.data?.houses ?? []).length === 12;\n\t}\n\n\tprivate toAngle(lon: number): number {\n\t\treturn 180 + this.getAscendant() - lon;\n\t}\n\n\tprotected renderData(data: WheelChart) {\n\t\tconst planets = this.getPlanets();\n\t\tconst aspects = data.aspects ?? [];\n\t\tconst view = this.view;\n\n\t\treturn html`<div class=\"wrap\" part=\"card\"><header part=\"header\"><h2 class=\"title\">${this.t(this.heading)}</h2>${\n\t\t\t\t\tdata.birthDetails\n\t\t\t\t\t\t? html`<div class=\"meta\">${formatDateTime(this.effectiveLang(), data.birthDetails.date, data.birthDetails.time)}</div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</header>${\n\t\t\t\taspects.length > 0\n\t\t\t\t\t? html`${renderTablist({\n\t\t\t\t\t\t\titems: [\n\t\t\t\t\t\t\t\t{ id: 'wheel', label: this.t('Wheel') },\n\t\t\t\t\t\t\t\t{ id: 'grid', label: this.t('Aspect grid') },\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tactive: view,\n\t\t\t\t\t\t\tonSelect: (v) => {\n\t\t\t\t\t\t\t\tthis.view = v;\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tlabel: this.t('Natal chart views'),\n\t\t\t\t\t\t\tidPrefix: 'natal',\n\t\t\t\t\t\t\tcontrols: true,\n\t\t\t\t\t\t})}<div id=\"natal-panel-${view}\" part=\"panel\" role=\"tabpanel\" aria-labelledby=\"natal-tab-${view}\">${view === 'wheel' ? this.renderWheel(planets, aspects) : this.renderAspectGrid(planets, aspects)}</div>`\n\t\t\t\t\t: this.renderWheel(planets, aspects)\n\t\t\t}<div class=\"legend\" part=\"legend\"><span>${this.t('{{count}} planets', { count: planets.length })}</span> ${aspects.length > 0 ? html`<span>${this.t('{{count}} aspects', { count: aspects.length })}</span>` : nothing} ${\n\t\t\t\t\t// The system names the CUSPS, so it is only true while there are\n\t\t\t\t\t// cusps. Without them the caveat takes its place rather than sitting\n\t\t\t\t\t// beside it and contradicting it.\n\t\t\t\t\tthis.hasCusps && data.houseSystem\n\t\t\t\t\t\t? html`<span>${this.t('{{system}} houses', { system: data.houseSystem })}</span>`\n\t\t\t\t\t\t: html`<span class=\"caveat\">${this.t('Equal sectors from the Ascendant, no house cusps in this response')}</span>`\n\t\t\t\t} ${\n\t\t\t\t\taspects.length > 0\n\t\t\t\t\t\t? html`<span><span class=\"legend-swatch\" style=\"background:var(--roxy-success)\"></span>${this.t('Harmonious')}</span> <span><span class=\"legend-swatch\" style=\"background:var(--roxy-danger)\"></span>${this.t('Challenging')}</span>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</div>${this.renderDetails()} ${\n\t\t\t\t// Ungated on purpose. The figures are chart geometry, not a reading, and\n\t\t\t\t// only the paragraph inside each card answers to hide-readings. Do NOT\n\t\t\t\t// wrap this call in the flag: the reasoning, and the escape hatch for a\n\t\t\t\t// page that wants the block gone anyway, are on renderPatterns.\n\t\t\t\tthis.renderPatterns()\n\t\t\t} ${this.renderInterpretations()}</div>`;\n\t}\n\n\tprivate renderWheel(planets: PlanetEntry[], aspects: AspectEntry[]) {\n\t\treturn html`<svg viewBox=\"0 0 ${SIZE} ${SIZE}\" part=\"chart\" role=\"img\" aria-label=\"${\n\t\t\t\tthis.hasCusps\n\t\t\t\t\t? this.t('Natal chart wheel with twelve houses, planets, and aspects')\n\t\t\t\t\t: this.t(\n\t\t\t\t\t\t\t'Natal chart wheel with planets and aspects, houses shown as equal sectors from the Ascendant',\n\t\t\t\t\t\t)\n\t\t\t}\"><title>${this.t('Natal chart wheel')}</title><desc>${this.t('Twelve zodiac sign segments around a circular wheel. Planet glyphs are placed at their ecliptic longitudes. Aspect lines connect related planets.')}</desc><circle class=\"wheel-line\" cx=\"${CENTER}\" cy=\"${CENTER}\" r=\"${OUTER_R}\" stroke-width=\"1.5\"/><circle class=\"wheel-line\" cx=\"${CENTER}\" cy=\"${CENTER}\" r=\"${SIGN_R - 14}\" stroke-width=\"0.8\"/><circle class=\"wheel-line\" cx=\"${CENTER}\" cy=\"${CENTER}\" r=\"${HOUSE_R}\" stroke-width=\"1\"/><circle class=\"wheel-line\" cx=\"${CENTER}\" cy=\"${CENTER}\" r=\"${PLANET_R - 16}\" stroke-width=\"0.5\"/>${this.renderTicks()} ${this.renderSpokes()} ${this.renderSigns()} ${this.renderHouseNumbers()} ${this.renderCuspDegrees()} ${this.renderAspects(planets, aspects)} ${this.renderPlanets(planets)} ${this.renderAngles()}</svg>`;\n\t}\n\n\t/**\n\t * Planet-by-planet aspect grid: the lower-triangular matrix astrologers read\n\t * alongside the wheel. Each filled cell shows the aspect glyph colored by\n\t * nature, with the exact orb in the SVG-free `<title>` tooltip.\n\t */\n\tprivate renderAspectGrid(planets: PlanetEntry[], aspects: AspectEntry[]) {\n\t\t// Both values per body, deliberately: `name` is the English one the pairing\n\t\t// and the glyph table are keyed on, `label` is the one a reader sees. The\n\t\t// label is rendered verbatim, never through `capitalize`, which lowercases\n\t\t// the rest of the string and made a tooltip read \"North node\"; the pairing\n\t\t// folds through `lookupKey`, the same normalizer the glyph table uses.\n\t\tconst bodies = planets.map((p) => ({\n\t\t\tname: p.name,\n\t\t\tlabel: display(p, 'name'),\n\t\t}));\n\t\tconst pairKey = (a: string, b: string) =>\n\t\t\t[lookupKey(a), lookupKey(b)].sort().join('|');\n\t\tconst byPair = new Map<string, AspectEntry>();\n\t\tfor (const a of aspects) byPair.set(pairKey(a.planet1, a.planet2), a);\n\t\tif (bodies.length === 0)\n\t\t\treturn html`<p class=\"roxy-empty\" role=\"status\">${this.t('No planets to grid')}</p>`;\n\n\t\treturn html`<div class=\"grid-scroll\" part=\"section aspects table aspect-grid\"><table class=\"aspect-grid\"><caption class=\"roxy-sr-only\">${this.t('Planet by planet aspect grid: the aspect each pair of planets forms, read from the planet naming the row across to the planet naming the column.')}</caption><thead><tr><th></th>${bodies.slice(0, -1).map((b) => {\n\t\t\t\t\t\t\tconst g = planetGlyph(b.name) ?? b.label;\n\t\t\t\t\t\t\treturn html`<th scope=\"col\" title=\"${b.label}\">${g}</th>`;\n\t\t\t\t\t\t})}</tr></thead><tbody>${bodies.slice(1).map((row, ri) => {\n\t\t\t\t\t\tconst rowGlyph = planetGlyph(row.name) ?? row.label;\n\t\t\t\t\t\t// Row i (1-based) pairs with columns 0..i-1.\n\t\t\t\t\t\treturn html`<tr><th scope=\"row\" title=\"${row.label}\">${rowGlyph}</th>${bodies.slice(0, ri + 1).map((col) => {\n\t\t\t\t\t\t\t\tconst a = byPair.get(pairKey(row.name, col.name));\n\t\t\t\t\t\t\t\tif (!a) return html`<td class=\"empty\"></td>`;\n\t\t\t\t\t\t\t\tconst name = normalizeAspect(a);\n\t\t\t\t\t\t\t\t// Every aspect the API returns has a glyph, and a miss falls\n\t\t\t\t\t\t\t\t// back to the full aspect NAME. Never to initials or a slice:\n\t\t\t\t\t\t\t\t// an invented two-letter code in a one-glyph cell reads as a\n\t\t\t\t\t\t\t\t// deliberate abbreviation, which is how `sesquiquadrate`\n\t\t\t\t\t\t\t\t// once shipped rendering as the literal text `ses`.\n\t\t\t\t\t\t\t\tconst sym =\n\t\t\t\t\t\t\t\t\taspectSymbol(name) ?? display(a, 'type', formatAspectName(a));\n\t\t\t\t\t\t\t\tconst cls = ASPECT_CLASS[name] ?? 'aspect-other';\n\t\t\t\t\t\t\t\tconst orb = formatNumber(this.effectiveLang(), a.orb, 1);\n\t\t\t\t\t\t\t\treturn html`<td class=\"${`cell ${cls}`}\" title=\"${`${row.label} ${display(a, 'type', name)} ${col.label}${orb ? ` (${this.t('Orb')} ${orb}°)` : ''}`}\"><span class=\"asp\">${sym}</span></td>`;\n\t\t\t\t\t\t\t})} ${bodies.slice(ri + 1, -1).map(() => html`<td class=\"empty\"></td>`)}</tr>`;\n\t\t\t\t\t})}</tbody></table></div>`;\n\t}\n\n\t/**\n\t * The angle marks, fanned so two of them never print over each other.\n\t *\n\t * @remarks\n\t * Part of Fortune and the Vertex land wherever the chart puts them, and either\n\t * can fall within a couple of degrees of an axis, which is close enough for two\n\t * three-letter labels on one ring to become a single unreadable mash. They go\n\t * through the same {@link fanOut} the bodies do, so a crowded label steps\n\t * aside while its TICK stays at the true longitude and a leader joins the two.\n\t * The ring has about fourteen user units of margin left inside the viewBox, so\n\t * a crowded label cannot be pushed outward; it moves along the ring instead.\n\t */\n\tprivate renderAngles() {\n\t\tconst asc = this.getAscendant();\n\t\tconst mc = this.getMidheaven();\n\t\t// ASC/DESC and MC/IC are exact axes; DESC and IC are the opposite points.\n\t\tconst marks: Array<{ longitude: number; label: string }> = [\n\t\t\t{ longitude: asc, label: this.t('ASC') },\n\t\t\t{ longitude: oppositePoint(asc), label: this.t('DSC') },\n\t\t];\n\t\tif (mc !== null) {\n\t\t\tmarks.push({ longitude: mc, label: this.t('MC') });\n\t\t\tmarks.push({ longitude: oppositePoint(mc), label: this.t('IC') });\n\t\t}\n\t\tconst pof = this.data?.partOfFortune?.longitude;\n\t\tif (typeof pof === 'number') {\n\t\t\tmarks.push({ longitude: normalizeLongitude(pof), label: this.t('PoF') });\n\t\t}\n\t\tconst vertex = this.data?.vertex?.longitude;\n\t\tif (typeof vertex === 'number') {\n\t\t\tmarks.push({\n\t\t\t\tlongitude: normalizeLongitude(vertex),\n\t\t\t\tlabel: this.t('Vtx'),\n\t\t\t});\n\t\t}\n\t\treturn fanOut(\n\t\t\tmarks,\n\t\t\t(m) => m.longitude,\n\t\t\tarcSeparation(ANGLE_LABEL_WIDTH, ANGLE_LABEL_R),\n\t\t).map(({ item, longitude, displayLongitude }) =>\n\t\t\tthis.renderAngleMark(longitude, displayLongitude, item.label),\n\t\t);\n\t}\n\n\tprivate renderAngleMark(\n\t\tlongitude: number,\n\t\tdisplayLongitude: number,\n\t\tlabel: string,\n\t) {\n\t\t// The tick is the claim about position, so it stays at the true longitude;\n\t\t// the label is only a name for it and may step aside. A leader appears only\n\t\t// when the two part company, so an uncrowded chart draws exactly what it\n\t\t// drew before.\n\t\tconst angle = this.toAngle(longitude);\n\t\tconst labelAngle = this.toAngle(displayLongitude);\n\t\tconst tickInner = polarToCartesian(CENTER, CENTER, OUTER_R, angle);\n\t\tconst tickOuter = polarToCartesian(CENTER, CENTER, ANGLE_TICK_R, angle);\n\t\tconst labelPos = polarToCartesian(\n\t\t\tCENTER,\n\t\t\tCENTER,\n\t\t\tANGLE_LABEL_R,\n\t\t\tlabelAngle,\n\t\t);\n\t\tconst moved = Math.abs(displayLongitude - longitude) > 0.5;\n\t\tconst leaderEnd = polarToCartesian(\n\t\t\tCENTER,\n\t\t\tCENTER,\n\t\t\tANGLE_LABEL_R - 8,\n\t\t\tlabelAngle,\n\t\t);\n\t\treturn svg`\n\t\t\t<g>\n\t\t\t\t<line class=\"angle-tick\" x1=${tickInner.x} y1=${tickInner.y} x2=${tickOuter.x} y2=${tickOuter.y} />\n\t\t\t\t${\n\t\t\t\t\tmoved\n\t\t\t\t\t\t? svg`<line class=\"angle-tick\" x1=${tickOuter.x} y1=${tickOuter.y} x2=${leaderEnd.x} y2=${leaderEnd.y} />`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}\n\t\t\t\t<text class=\"angle-marker\" x=${labelPos.x} y=${labelPos.y} text-anchor=\"middle\" dominant-baseline=\"central\">${label}</text>\n\t\t\t</g>\n\t\t`;\n\t}\n\n\tprivate renderSpokes() {\n\t\t// Draw a spoke at each real house cusp longitude so Placidus / Koch\n\t\t// unequal houses render correctly. Fall back to 12 equal spokes from the\n\t\t// Ascendant only when the response carries no houses array, which the\n\t\t// accessible name and the legend caveat both declare.\n\t\tconst houses = this.data?.houses ?? [];\n\t\tconst cuspLongitudes = this.hasCusps\n\t\t\t? houses.map((h) => h.longitude)\n\t\t\t: Array.from({ length: 12 }, (_, i) => this.getAscendant() + i * 30);\n\t\treturn cuspLongitudes.map((lon) => {\n\t\t\tconst angle = this.toAngle(lon);\n\t\t\tconst start = polarToCartesian(CENTER, CENTER, HOUSE_R, angle);\n\t\t\tconst end = polarToCartesian(CENTER, CENTER, OUTER_R, angle);\n\t\t\treturn svg`<line class=\"wheel-line\" x1=${start.x} y1=${start.y} x2=${end.x} y2=${end.y} stroke-width=\"0.8\" />`;\n\t\t});\n\t}\n\n\tprivate renderSigns() {\n\t\treturn SIGNS_ORDER.map((sign, i) => {\n\t\t\tconst angle = this.toAngle(i * 30 + 15);\n\t\t\tconst pos = polarToCartesian(CENTER, CENTER, SIGN_R, angle);\n\t\t\treturn svg`<text class=\"sign-glyph\" x=${pos.x} y=${pos.y} text-anchor=\"middle\" dominant-baseline=\"central\">${signGlyph(sign) ?? ''}</text>`;\n\t\t});\n\t}\n\n\tprivate renderHouseNumbers() {\n\t\tconst houses = this.data?.houses ?? [];\n\t\t// Place each house number at the angular midpoint between its cusp and\n\t\t// the next, so the label sits inside the house even when houses are\n\t\t// unequal. Fall back to equal 30-degree sectors when houses are absent.\n\t\tif (this.hasCusps) {\n\t\t\treturn houses.map((house, i) => {\n\t\t\t\tconst next = houses[(i + 1) % 12];\n\t\t\t\tconst mid = arcMidpoint(\n\t\t\t\t\thouse.longitude,\n\t\t\t\t\tnext ? next.longitude : house.longitude + 30,\n\t\t\t\t);\n\t\t\t\tconst pos = polarToCartesian(\n\t\t\t\t\tCENTER,\n\t\t\t\t\tCENTER,\n\t\t\t\t\tHOUSE_R - 12,\n\t\t\t\t\tthis.toAngle(mid),\n\t\t\t\t);\n\t\t\t\treturn svg`<text class=\"house-num\" x=${pos.x} y=${pos.y} text-anchor=\"middle\" dominant-baseline=\"central\">${house.number}</text>`;\n\t\t\t});\n\t\t}\n\t\tconst ascSignIndex = Math.floor(this.getAscendant() / 30);\n\t\treturn Array.from({ length: 12 }, (_, i) => {\n\t\t\tconst angle = this.toAngle(i * 30 + 15);\n\t\t\tconst pos = polarToCartesian(CENTER, CENTER, HOUSE_R - 12, angle);\n\t\t\tconst houseNum = ((i - ascSignIndex + 12) % 12) + 1;\n\t\t\treturn svg`<text class=\"house-num\" x=${pos.x} y=${pos.y} text-anchor=\"middle\" dominant-baseline=\"central\">${houseNum}</text>`;\n\t\t});\n\t}\n\n\t/**\n\t * Degree ticks on the outer zodiac band: a short mark every 5 degrees and a\n\t * longer one on each 30-degree sign cusp, so the wheel reads like a\n\t * reference-grade chart rather than a bare ring of glyphs.\n\t */\n\tprivate renderTicks() {\n\t\tconst ticks = [];\n\t\tfor (let deg = 0; deg < 360; deg += 5) {\n\t\t\tconst angle = this.toAngle(deg);\n\t\t\tconst isMajor = deg % 30 === 0;\n\t\t\tconst inner = isMajor ? SIGN_R - 14 : OUTER_R - 5;\n\t\t\tconst a = polarToCartesian(CENTER, CENTER, inner, angle);\n\t\t\tconst b = polarToCartesian(CENTER, CENTER, OUTER_R, angle);\n\t\t\tticks.push(\n\t\t\t\tsvg`<line class=${isMajor ? 'tick tick-major' : 'tick'} x1=${a.x} y1=${a.y} x2=${b.x} y2=${b.y} stroke-width=${isMajor ? 1 : 0.5} />`,\n\t\t\t);\n\t\t}\n\t\treturn ticks;\n\t}\n\n\t/**\n\t * Degree-and-minute label printed next to each house cusp on the wheel, so\n\t * the exact cusp position is readable without leaving the chart.\n\t */\n\tprivate renderCuspDegrees() {\n\t\tconst houses = this.data?.houses ?? [];\n\t\tif (!this.hasCusps) return nothing;\n\t\treturn houses.map((house) => {\n\t\t\tconst angle = this.toAngle(house.longitude);\n\t\t\tconst pos = polarToCartesian(CENTER, CENTER, HOUSE_R + 9, angle);\n\t\t\tconst sp = longitudeToSignPosition(house.longitude);\n\t\t\treturn svg`<text class=\"cusp-deg\" x=${pos.x} y=${pos.y} text-anchor=\"middle\" dominant-baseline=\"central\">${sp.degree}°${String(sp.minute).padStart(2, '0')}'</text>`;\n\t\t});\n\t}\n\n\t/**\n\t * The bodies, fanned so a stellium stays readable.\n\t *\n\t * @remarks\n\t * Conjunctions inside a few degrees are the norm rather than the exception, so\n\t * every glyph is pushed forward only as far as it takes to clear its neighbour\n\t * and a leader runs back to the body's TRUE longitude on the rim. The\n\t * separation is derived from the radius each mark is drawn at rather than\n\t * fixed, so the degree label, which sits on a tighter ring than the glyph and\n\t * is the wider of the two, is what decides the spacing.\n\t */\n\tprivate renderPlanets(planets: PlanetEntry[]) {\n\t\tconst separation = Math.max(\n\t\t\tarcSeparation(GLYPH_WIDTH, PLANET_R),\n\t\t\tarcSeparation(DEG_LABEL_WIDTH, PLANET_R - 13),\n\t\t);\n\t\treturn fanOut(planets, (p) => p.longitude, separation).map((placed) => {\n\t\t\tconst {\n\t\t\t\titem: p,\n\t\t\t\tlongitude: trueLon,\n\t\t\t\tdisplayLongitude: displayLon,\n\t\t\t} = placed;\n\t\t\tconst trueAngle = this.toAngle(trueLon);\n\t\t\tconst displayAngle = this.toAngle(displayLon);\n\t\t\tconst glyphPos = polarToCartesian(CENTER, CENTER, PLANET_R, displayAngle);\n\t\t\tconst degPos = polarToCartesian(\n\t\t\t\tCENTER,\n\t\t\t\tCENTER,\n\t\t\t\tPLANET_R - 13,\n\t\t\t\tdisplayAngle,\n\t\t\t);\n\t\t\tconst rimPos = polarToCartesian(CENTER, CENTER, OUTER_R - 4, trueAngle);\n\t\t\tconst leaderInner = polarToCartesian(\n\t\t\t\tCENTER,\n\t\t\t\tCENTER,\n\t\t\t\tPLANET_R + 8,\n\t\t\t\tdisplayAngle,\n\t\t\t);\n\t\t\t// The glyph is looked up on the canonical English name and its fallback is\n\t\t\t// the localized one, because the fallback is TEXT the reader is left with.\n\t\t\tconst label = display(p, 'name');\n\t\t\tconst glyph = planetGlyph(p.name) ?? label;\n\t\t\tconst sp = longitudeToSignPosition(p.longitude);\n\t\t\tconst retro = p.isRetrograde === true;\n\t\t\tconst degLabel = `${sp.degree}°${String(sp.minute).padStart(2, '0')}'`;\n\t\t\tconst offset = Math.abs(displayLon - trueLon) > 0.5;\n\t\t\treturn svg`<g>\n\t\t\t\t${\n\t\t\t\t\toffset\n\t\t\t\t\t\t? svg`<line class=\"planet-leader\" x1=${rimPos.x} y1=${rimPos.y} x2=${leaderInner.x} y2=${leaderInner.y} />`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}\n\t\t\t\t<text class=\"planet-glyph\" x=${glyphPos.x} y=${glyphPos.y} text-anchor=\"middle\" dominant-baseline=\"central\"><title>${label}${retro ? ` ${this.t('retrograde')}` : ''} - ${degLabel} ${display(p, 'sign')}</title>${glyph}</text>\n\t\t\t\t<text class=\"planet-deg\" x=${degPos.x} y=${degPos.y} text-anchor=\"middle\" dominant-baseline=\"central\">${degLabel}${retro ? svg`<tspan class=\"retro\"> ℞</tspan>` : nothing}</text>\n\t\t\t</g>`;\n\t\t});\n\t}\n\n\tprivate renderDetails() {\n\t\tconst summary = this.data?.summary;\n\t\tconst ai = this.data?.aspectsInterpretation;\n\t\tif (!summary && !ai) return nothing;\n\n\t\t// Pairs, not names: the glyph beside each retrograde body is looked up on the\n\t\t// English value while the pill prints the localized one, and the two arrays\n\t\t// the API returns are index-aligned, which is what makes that possible.\n\t\tconst retrogrades = displayList(summary, 'retrogradePlanets');\n\n\t\treturn html`<div class=\"details\" part=\"details\">${\n\t\t\t\tsummary?.dominantElement || summary?.dominantModality\n\t\t\t\t\t? html`<div class=\"pill-row\">${summary.dominantElement ? html`<span class=\"pill\">${this.t('Dominant element')}: ${display(summary, 'dominantElement')}</span>` : nothing} ${summary.dominantModality ? html`<span class=\"pill\">${this.t('Dominant modality')}: ${display(summary, 'dominantModality')}</span>` : nothing}</div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\tai\n\t\t\t\t\t? html`<div class=\"pill-row\"><span class=\"pill pill--success\">${this.t('Harmonious')} ${ai.harmonious}</span> <span class=\"pill pill--danger\">${this.t('Challenging')} ${ai.challenging}</span> <span class=\"pill pill--muted\">${this.t('Neutral')} ${ai.neutral}</span></div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\tretrogrades.length > 0\n\t\t\t\t\t? html`<div class=\"pill-row\">${retrogrades.map(({ value, label }) => {\n\t\t\t\t\t\t\t// `?? ''` and never a slice: the full name is printed right\n\t\t\t\t\t\t\t// beside the glyph, so a miss must drop the glyph, not invent\n\t\t\t\t\t\t\t// one. This is the site that shipped `No North Node R`,\n\t\t\t\t\t\t\t// `So South Node R` and `Bl Black Moon Lilith` on a live chart.\n\t\t\t\t\t\t\tconst glyph = planetGlyph(value) ?? '';\n\t\t\t\t\t\t\treturn html`<span class=\"pill pill--muted\">${glyph} ${label} R</span>`;\n\t\t\t\t\t\t})}</div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\t// Prose about the aspect balance, not the balance itself: the counts\n\t\t\t\t// above it are the data and stay.\n\t\t\t\tai?.summary && !this.hideReadings\n\t\t\t\t\t? html`<p class=\"summary\">${ai.summary}</p>`\n\t\t\t\t\t: nothing\n\t\t\t} ${this.renderElementModalityGrid()}</div>`;\n\t}\n\n\t/**\n\t * Element by modality grid: the 4x3 cross-tab astrologers read for chart balance. Each body is placed by its sign into one cell (Fire/Earth/Air/Water row, Cardinal/Fixed/Mutable column).\n\t *\n\t * @remarks\n\t * The cells are derived from the planet signs, not read from `summary.elementDistribution`, because a 1D distribution cannot fill a cross-tab. That makes the body set the reconciliation risk: the API counts every body it returns (nodes, Chiron, and Black Moon Lilith included), so the grid must too, or the totals here would contradict the dominant-element pill rendered right above it. Hence the totals count placed bodies rather than `planets.length` (an unrecognized sign would otherwise inflate the grand total past the sum of its rows), the caption names the body set, and the dominant row and column are tinted from `summary` so the pill and the grid land on the same cell.\n\t *\n\t * **The seven headers are CHROME, not response vocabulary, and that is what licenses translating them.** The axes are the component's own 4x3 construction, so six of the seven have no field in the response to defer to and the seventh (the dominant pair) would otherwise have been the only translated word in the table. They therefore go through the chrome catalogue like every other heading here, and each catalogue value is pinned to what the API returns for `dominantElementLocalized` / `dominantModalityLocalized` so the tinted pill above the grid and the header on its row cannot read two different words for one element. `tests/i18n.test.ts` re-asserts that agreement on a render.\n\t *\n\t * **The English arrays stay, because they are the CELL KEYS.** `SIGNS_ORDER.indexOf` resolves each sign to an index and the modulo picks the bucket, so the array values are compared and keyed on, never read (lesson 31). Only the header text nodes move.\n\t */\n\tprivate renderElementModalityGrid() {\n\t\tconst planets = this.getPlanets();\n\t\tif (planets.length === 0) return nothing;\n\t\tconst ELEMENTS = ['Fire', 'Earth', 'Air', 'Water'] as const;\n\t\tconst MODALITIES = ['Cardinal', 'Fixed', 'Mutable'] as const;\n\t\t// Header text, keyed by the canonical bucket name. Written out as literal\n\t\t// `t()` calls rather than `this.t(el)` so the static scan in\n\t\t// `tests/i18n.test.ts` can see every one of them: a dynamic key is exactly\n\t\t// the shape that ships a string no catalogue carries.\n\t\tconst ELEMENT_LABEL: Record<string, string> = {\n\t\t\tFire: this.t('Fire'),\n\t\t\tEarth: this.t('Earth'),\n\t\t\tAir: this.t('Air'),\n\t\t\tWater: this.t('Water'),\n\t\t};\n\t\t// The column is three glyphs wide, so it shows an abbreviation the\n\t\t// CATALOGUE supplies and carries the full word as its `title`. Never a\n\t\t// substring of the translated word: `slice(0, 3)` counts UTF-16 units with\n\t\t// no idea where the word ends, so Spanish read `Fij` and Devanagari split a\n\t\t// matra off its consonant. `tokens.test.ts` fails on any small slice now.\n\t\tconst MODALITY_LABEL: Record<string, { abbr: string; full: string }> = {\n\t\t\tCardinal: { abbr: this.t('Car'), full: this.t('Cardinal') },\n\t\t\tFixed: { abbr: this.t('Fix'), full: this.t('Fixed') },\n\t\t\tMutable: { abbr: this.t('Mut'), full: this.t('Mutable') },\n\t\t};\n\t\tconst order = SIGNS_ORDER as readonly string[];\n\t\tconst summary = this.data?.summary;\n\t\t// English on both sides, on purpose. These three are lookups, not labels:\n\t\t// the dominant pair is COMPARED against the local element and modality\n\t\t// arrays and each sign is resolved to its index in SIGNS_ORDER, so reading\n\t\t// `dominantElementLocalized` or `signLocalized` here would match nothing on\n\t\t// a translated page and everything on an English one.\n\t\tconst dominantEl = capitalize(summary?.dominantElement ?? '');\n\t\tconst dominantMod = capitalize(summary?.dominantModality ?? '');\n\n\t\tconst cells: Record<string, Record<string, string[]>> = {};\n\t\tfor (const el of ELEMENTS)\n\t\t\tcells[el] = { Cardinal: [], Fixed: [], Mutable: [] };\n\t\tlet placed = 0;\n\t\tfor (const p of planets) {\n\t\t\tconst idx = order.indexOf(capitalize(p.sign ?? ''));\n\t\t\tif (idx < 0) continue;\n\t\t\tconst el = ELEMENTS[idx % 4];\n\t\t\tconst mod = MODALITIES[idx % 3];\n\t\t\t// The cell shows the glyph alone, so a miss falls back to the full name:\n\t\t\t// wide and obviously wrong, rather than a plausible two-letter code.\n\t\t\tconst glyph = planetGlyph(p.name) ?? display(p, 'name');\n\t\t\tcells[el]?.[mod]?.push(glyph);\n\t\t\tplaced++;\n\t\t}\n\t\tif (placed === 0) return nothing;\n\n\t\tconst colTotal = (m: string) =>\n\t\t\tELEMENTS.reduce((s, el) => s + (cells[el]?.[m]?.length ?? 0), 0);\n\n\t\t// Same scroll box as the aspect grid beside it. A cell holds a joined list\n\t\t// of glyphs, so it is normally tiny, but a body with no glyph renders its\n\t\t// FULL name there by design, and the table has to carry that without\n\t\t// dragging the card out with it. `.grid-scroll` pairs `overflow-x: auto`\n\t\t// with `min-width: 0`, and it is the `min-width` half that actually lets a\n\t\t// grid item scroll instead of growing to fit.\n\t\treturn html`<div class=\"grid-scroll\"><table class=\"em-grid\" part=\"table element-modality\" aria-label=\"${this.t('Element and modality distribution')}\"><caption>${this.t('All {{count}} bodies in the chart, placed by sign', { count: placed })}</caption><thead><tr><th></th>${MODALITIES.map(\n\t\t\t\t\t\t(m) =>\n\t\t\t\t\t\t\thtml`<th scope=\"col\" title=\"${MODALITY_LABEL[m]?.full ?? m}\" class=\"${m === dominantMod ? 'dominant' : ''}\">${MODALITY_LABEL[m]?.abbr ?? m}</th>`,\n\t\t\t\t\t)}<th scope=\"col\">${this.t('Total')}</th></tr></thead><tbody>${ELEMENTS.map((el) => {\n\t\t\t\t\tconst isDomRow = el === dominantEl;\n\t\t\t\t\tconst rowTotal = MODALITIES.reduce(\n\t\t\t\t\t\t(s, m) => s + (cells[el]?.[m]?.length ?? 0),\n\t\t\t\t\t\t0,\n\t\t\t\t\t);\n\t\t\t\t\treturn html`<tr><th scope=\"row\" class=\"${isDomRow ? 'dominant' : ''}\">${ELEMENT_LABEL[el] ?? el}</th>${MODALITIES.map(\n\t\t\t\t\t\t\t(m) =>\n\t\t\t\t\t\t\t\thtml`<td class=\"${isDomRow || m === dominantMod ? 'dominant' : ''}\">${(cells[el]?.[m] ?? []).join(' ')}</td>`,\n\t\t\t\t\t\t)}<td class=\"${isDomRow ? 'em-total dominant' : 'em-total'}\">${rowTotal}</td></tr>`;\n\t\t\t\t})}<tr><th scope=\"row\">${this.t('Total')}</th>${MODALITIES.map(\n\t\t\t\t\t\t(m) =>\n\t\t\t\t\t\t\thtml`<td class=\"${m === dominantMod ? 'em-total dominant' : 'em-total'}\">${colTotal(m)}</td>`,\n\t\t\t\t\t)}<td class=\"em-total\">${placed}</td></tr></tbody></table></div>`;\n\t}\n\n\t/**\n\t * Detected multi-planet configurations. Each card names the figure, tags the element or modality it pivots on, flags a dissociate (out-of-sign) figure, and puts the apex planet first with its own chip, because the apex is the point the whole configuration discharges through.\n\t *\n\t * @remarks\n\t * A pattern carries no localized partner for its `name`, `element`, `modality` or its `planets` list, so all four render English on every page. Do NOT translate the chips by looking each body up in `planets[].nameLocalized`: the response is the authority for its own vocabulary, and a second translation of the same fact assembled here is exactly what can end up disagreeing with the wheel beside it.\n\t *\n\t * **This block is DATA, so `hide-readings` thins it rather than removing it, and that split is deliberate.** A T-Square is a geometric fact about where the bodies sit, exactly like the aspect rows and the house cusps: the figure, its element and modality, its tightness and its planets are all measurements, and the only reading in the card is the paragraph, which {@link RoxyNatalChart.renderPattern} already drops. The line is the one the library states everywhere else and the one the docs promise: \"wheels, maps, tables, grids, legends, badges and every number stay, the interpretive prose goes\". `<roxy-aspects-table>` renders this identical `patterns` payload and makes the identical cut, so gating the whole block here would make the same figure appear and disappear depending on which component a page reached for.\n\t *\n\t * **A page that wants the block gone entirely has a lever, and it is not this flag.** The section carries `part=\"section patterns\"`, so `roxy-natal-chart::part(patterns) { display: none }` removes it. That is the documented answer for dropping a block of DATA, the same way `::part(readings)` styles the interpretation; `hide-readings` is the answer for prose, and stretching it to cover measurements would leave a practitioner with no way to publish the figures without the words.\n\t */\n\tprivate renderPatterns() {\n\t\tconst patterns = this.data?.patterns ?? [];\n\t\tif (patterns.length === 0) return nothing;\n\t\tconst sorted = [...patterns].sort(\n\t\t\t(a, b) =>\n\t\t\t\t(PATTERN_ORDER[a.kind] ?? 9) - (PATTERN_ORDER[b.kind] ?? 9) ||\n\t\t\t\t(b.tightness ?? 0) - (a.tightness ?? 0),\n\t\t);\n\t\treturn html`<section class=\"block\" part=\"section patterns\"><h3>${this.t('Chart patterns')}</h3>${sorted.map((p) => this.renderPattern(p))}</section>`;\n\t}\n\n\tprivate renderPattern(p: PatternEntry) {\n\t\tconst planets = p.planets ?? [];\n\t\t// Apex first: the spec already orders it first for Kite, T-Square, and Yod,\n\t\t// but sorting here keeps the chip row honest for any future pattern kind.\n\t\tconst ordered = p.apex\n\t\t\t? [...planets].sort((a, b) => Number(b === p.apex) - Number(a === p.apex))\n\t\t\t: planets;\n\t\treturn html`<div class=\"pattern\" part=\"pattern\"><div class=\"pattern-head\"><span class=\"pattern-name\">${p.name}</span> ${p.element ? html`<span class=\"pattern-tag\">${p.element}</span>` : nothing} ${p.modality ? html`<span class=\"pattern-tag\">${p.modality}</span>` : nothing} ${\n\t\t\t\t\tp.dissociate\n\t\t\t\t\t\t? html`<span class=\"pattern-tag\" title=\"${this.t('Out of sign: one or more planets sit outside the pattern element or modality, so the theme holds but runs weaker.')}\">${this.t('Dissociate')}</span>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t} ${\n\t\t\t\t\t// Math.round, not formatNumber(this.effectiveLang(), x, 0): that helper strips trailing\n\t\t\t\t\t// zeros, so a 100% tight pattern renders as \"1%\".\n\t\t\t\t\ttypeof p.tightness === 'number'\n\t\t\t\t\t\t? html`<span class=\"pattern-tight\">${this.t('{{percent}} tight', { percent: formatPercent(this.effectiveLang(), p.tightness, 0) })}</span>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</div><div class=\"pattern-planets\">${ordered.map((name) => {\n\t\t\t\t\tconst glyph = planetGlyph(name);\n\t\t\t\t\tconst isApex = Boolean(p.apex) && name === p.apex;\n\t\t\t\t\treturn html`<span class=\"${isApex ? 'planet-chip apex' : 'planet-chip'}\">${glyph ? html`<span aria-hidden=\"true\">${glyph}</span>` : nothing}${name}${isApex ? html`<span class=\"apex-tag\">${this.t('apex')}</span>` : nothing}</span>`;\n\t\t\t\t})}</div>${\n\t\t\t\t// The figure, its planets and its tightness are the finding; the\n\t\t\t\t// paragraph is the reading of it.\n\t\t\t\tp.interpretation && !this.hideReadings\n\t\t\t\t\t? html`<p class=\"pattern-interp\">${p.interpretation}</p>`\n\t\t\t\t\t: nothing\n\t\t\t}</div>`;\n\t}\n\n\tprivate renderInterpretations() {\n\t\tconst sections: InterpSection[] = this.getPlanets()\n\t\t\t.filter((p) => p.interpretation)\n\t\t\t.map((p) => {\n\t\t\t\tconst interp = p.interpretation!;\n\t\t\t\tconst glyph = planetGlyph(p.name) ?? '';\n\t\t\t\tconst deg = formatNumber(this.effectiveLang(), p.degree ?? 0, 1);\n\t\t\t\tconst label = display(p, 'name');\n\t\t\t\tconst lead = interp.summary || interp.detailed || '';\n\t\t\t\t// `detailed` only becomes a second paragraph when `summary` already\n\t\t\t\t// took the lead line, so a response carrying one or the other never\n\t\t\t\t// prints the same prose twice.\n\t\t\t\tconst detail = interp.summary ? interp.detailed : undefined;\n\t\t\t\treturn {\n\t\t\t\t\tlabel: `${glyph} ${label}`.trim(),\n\t\t\t\t\taside: [display(p, 'sign'), deg].filter(Boolean).join(' '),\n\t\t\t\t\tbody: lead,\n\t\t\t\t\textra: html`${detail ? html`<p>${detail}</p>` : nothing} ${renderKeywordChips(interp.keywords)}`,\n\t\t\t\t};\n\t\t\t});\n\t\treturn this.renderInterpretation(\n\t\t\tsections,\n\t\t\t'natal-planet-readings',\n\t\t\t'Planet readings',\n\t\t);\n\t}\n\n\tprivate renderAspects(planets: PlanetEntry[], aspects: AspectEntry[]) {\n\t\t// Keyed on the canonical English name at BOTH ends, because this map is how\n\t\t// an aspect finds the two longitudes its line is drawn between. The localized\n\t\t// names go in the `<title>` and nowhere near the key.\n\t\tconst planetMap = new Map<string, number>();\n\t\tfor (const p of planets) {\n\t\t\tif (typeof p.longitude !== 'number') continue;\n\t\t\tconst name = lookupKey(p.name);\n\t\t\tif (name) planetMap.set(name, p.longitude);\n\t\t}\n\t\treturn aspects.map((a) => {\n\t\t\tconst l1 = planetMap.get(lookupKey(a.planet1));\n\t\t\tconst l2 = planetMap.get(lookupKey(a.planet2));\n\t\t\tif (l1 === undefined || l2 === undefined) return nothing;\n\t\t\tconst p1 = polarToCartesian(\n\t\t\t\tCENTER,\n\t\t\t\tCENTER,\n\t\t\t\tPLANET_R - 18,\n\t\t\t\tthis.toAngle(l1),\n\t\t\t);\n\t\t\tconst p2 = polarToCartesian(\n\t\t\t\tCENTER,\n\t\t\t\tCENTER,\n\t\t\t\tPLANET_R - 18,\n\t\t\t\tthis.toAngle(l2),\n\t\t\t);\n\t\t\tconst aspectName = normalizeAspect(a);\n\t\t\tconst aspectClass = ASPECT_CLASS[aspectName] ?? 'aspect-other';\n\t\t\tconst orbLabel = formatNumber(this.effectiveLang(), a.orb, 1);\n\t\t\treturn svg`<line class=${`aspect ${aspectClass}`} style=${aspectLineStyle(a)} x1=${p1.x} y1=${p1.y} x2=${p2.x} y2=${p2.y}><title>${display(a, 'planet1')} ${display(a, 'type', aspectName)} ${display(a, 'planet2')}${orbLabel ? ` (${this.t('Orb')} ${orbLabel}°)` : ''}</title></line>`;\n\t\t});\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-natal-chart': RoxyNatalChart;\n\t}\n}\n", "/**\n * The aspect lines every wheel draws across its centre: one set of styles, and the per-line weight that makes a tight contact read as tighter than a wide one.\n *\n * @remarks\n * Three components draw this same figure, so the styles live here rather than in each of them. The nature colours and the strength weighting are carried on SEPARATE properties on purpose: SVG multiplies `opacity` by `stroke-opacity`, so a minor aspect stays muted for being minor AND scales with how exact it is, and neither signal has to know about the other.\n */\n\nimport { css } from 'lit';\n\n/** Shared aspect-line styling. Include in a wheel's `static styles` and pair with {@link aspectLineStyle} on each line. */\nexport const aspectLineStyles = css`.aspect{stroke-width:.8px;fill:none;opacity:.55}.aspect-trine,.aspect-sextile{stroke:var(--roxy-success,#16a34a)}.aspect-square,.aspect-opposition{stroke:var(--roxy-danger,#dc2626)}.aspect-conjunction{stroke:var(--roxy-accent-ink,#b45309)}.aspect-other{stroke:var(--roxy-muted,#71717a);stroke-opacity:.7}`;\n\n/** Faintest and heaviest an aspect line is drawn. The floor is deliberately above zero: a wide aspect is weak, not absent, and a line nobody can see reads as an aspect the chart never found. */\nconst MIN_OPACITY = 0.3;\nconst MAX_OPACITY = 0.9;\nconst MIN_WIDTH = 0.5;\nconst MAX_WIDTH = 1.8;\n\n/**\n * Per-line weight for one aspect, as an inline `style` value.\n *\n * @remarks\n * Driven by `strength`, which the response defines as a 0-100 percentage based on orb tightness, so this is a rendering of a number the API already computed rather than a second opinion about it. Returns `undefined` when the response carries no strength, which leaves the shared defaults in {@link aspectLineStyles} in charge.\n *\n * Inline rather than a class, because tightness is continuous: bucketing it into two or three named tiers would put a boundary somewhere the data has none, and a reader comparing two lines either side of it would see a step that is not in the chart.\n */\nexport function aspectLineStyle(a: { strength?: number }): string | undefined {\n\tconst { strength } = a;\n\tif (typeof strength !== 'number' || !Number.isFinite(strength)) {\n\t\treturn undefined;\n\t}\n\tconst t = Math.min(1, Math.max(0, strength / 100));\n\tconst opacity = MIN_OPACITY + t * (MAX_OPACITY - MIN_OPACITY);\n\tconst width = MIN_WIDTH + t * (MAX_WIDTH - MIN_WIDTH);\n\treturn `opacity:${opacity.toFixed(3)};stroke-width:${width.toFixed(3)}`;\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport type { ChromeString } from '../i18n/chrome-strings.js';\nimport type {\n\tCalculateBirthDayResponse,\n\tCalculateExpressionResponse,\n\tCalculateLifePathResponse,\n\tCalculateMaturityResponse,\n\tCalculatePersonalDayResponse,\n\tCalculatePersonalityResponse,\n\tCalculatePersonalMonthResponse,\n\tCalculatePersonalYearResponse,\n\tCalculateSoulUrgeResponse,\n\tGenerateNumerologyChartResponse,\n\tGetDailyNumberResponse,\n} from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { disclosureStyles } from '../utils/disclosure.js';\nimport { formatDate } from '../utils/format.js';\nimport {\n\ttype InterpSection,\n\tinterpAccordionStyles,\n} from '../utils/interp-accordion.js';\nimport { humanize } from '../utils/string.js';\n\n/**\n * Single-number numerology responses that share the number + meaning + calculation + karmic-debt shape. {@link RoxyNumerologyCard.renderNumberCard} renders any of them; the `type` attribute selects only the heading label.\n */\ntype NumberCardData =\n\t| CalculateLifePathResponse\n\t| CalculateExpressionResponse\n\t| CalculateSoulUrgeResponse\n\t| CalculatePersonalityResponse\n\t| CalculateBirthDayResponse\n\t| CalculateMaturityResponse;\n\ntype NumerologyData =\n\t| NumberCardData\n\t| CalculatePersonalYearResponse\n\t| CalculatePersonalDayResponse\n\t| CalculatePersonalMonthResponse\n\t| GetDailyNumberResponse\n\t| GenerateNumerologyChartResponse;\n\n/**\n * The interpretation block a numerology number carries: archetype keywords, the long reading, the strengths and challenges lists, and the three life-area readings. Every core number, the daily number, and the birth-day profile ship this same block (the profile omits spirituality), so one renderer serves them all. Partial, because the birth-day profile is the narrower shape.\n */\ntype NumerologyReading = Partial<CalculateLifePathResponse['meaning']>;\n\n/** Master numbers are never reduced, and a reader has to see immediately that they are looking at one. */\nconst MASTER_NUMBERS = new Set([11, 22, 33]);\n\n/** The three life-area readings, in the order a numerologist gives them. */\nconst GUIDANCE_FIELDS: ReadonlyArray<[keyof NumerologyReading, string]> = [\n\t['career', 'Career'],\n\t['relationships', 'Relationships'],\n\t['spirituality', 'Spirituality'],\n];\n\n/**\n * Numerology card. Renders /numerology/{life-path,expression,soul-urge,personality,birth-day,maturity,daily,personal-day,personal-month,personal-year,chart}.\n * Use the `type` attribute to switch the heading; the single-number types all share one layout.\n */\n@customElement('roxy-numerology-card')\nexport class RoxyNumerologyCard extends RoxyDataElement<NumerologyData> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tdisclosureStyles,\n\t\tinterpAccordionStyles,\n\t\tcss`.card{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.hero{align-items:center;gap:var(--roxy-space-md,1rem);display:flex}.numeral{font-size:4rem;line-height:1;font-weight:var(--roxy-weight-bold,600);color:var(--roxy-accent-ink,#b45309);font-variant-numeric:tabular-nums}.label{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.06em;margin:0}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.meaning{color:var(--roxy-fg,#0a0a0a);margin:0}.master{background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 18%, transparent);color:var(--roxy-fg,#0a0a0a);border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);margin-top:2px;padding:2px 10px;display:inline-block}.calc{font-family:var(--roxy-font-mono);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 30%, transparent);padding:var(--roxy-space-sm,.5rem);border-radius:var(--roxy-radius-sm,4px);white-space:pre-wrap;overflow-wrap:anywhere;margin:0}.chips{gap:var(--roxy-space-xs,.25rem);flex-wrap:wrap;display:flex}.chips span{background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 14%, transparent);color:var(--roxy-fg,#0a0a0a);border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);padding:2px 8px}.cores{gap:var(--roxy-space-sm,.5rem);border-top:1px solid var(--roxy-border,#e4e4e7);padding-top:var(--roxy-space-md,1rem);grid-template-columns:repeat(auto-fit,minmax(8rem,1fr));display:grid}.cores .item{font-size:var(--roxy-text-sm,.875rem);gap:2px;display:grid}.cores .item .core-head{align-items:baseline;gap:var(--roxy-space-xs,.25rem);display:flex}.cores .item .core-key{color:var(--roxy-muted,#71717a);text-transform:capitalize}.cores .item strong{color:var(--roxy-accent-ink,#b45309);font-variant-numeric:tabular-nums;font-size:var(--roxy-text-base,1rem);font-weight:var(--roxy-weight-bold,600)}.cores .item .core-title{color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-xs,.75rem)}.lists{gap:var(--roxy-space-md,1rem);grid-template-columns:repeat(auto-fit,minmax(14rem,1fr));display:grid}.lists h3{margin:0 0 var(--roxy-space-xs,.25rem);font-size:var(--roxy-text-sm,.875rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.06em}.lists ul{gap:var(--roxy-space-xs,.25rem);font-size:var(--roxy-text-sm,.875rem);margin:0;padding-left:1.1rem;line-height:1.6;display:grid}.sub{gap:var(--roxy-space-sm,.5rem);border-top:1px solid var(--roxy-border,#e4e4e7);padding-top:var(--roxy-space-md,1rem);display:grid}.sub>h3{font-size:var(--roxy-text-sm,.875rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.06em;margin:0}.sub-title{font-size:var(--roxy-text-base,1rem);font-weight:var(--roxy-weight-bold,600);margin:0}.sub p{font-size:var(--roxy-text-sm,.875rem);margin:0;line-height:1.6}.attrs{gap:var(--roxy-space-sm,.5rem);grid-template-columns:repeat(auto-fit,minmax(7rem,1fr));margin:0;display:grid}.attr{gap:2px;display:grid}.attr dt{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.05em}.attr dd{font-size:var(--roxy-text-sm,.875rem);margin:0;font-weight:500}.karmic{background:color-mix(in srgb, var(--roxy-warning,#ea580c) 12%, transparent);border:1px solid color-mix(in srgb, var(--roxy-warning,#ea580c) 32%, transparent);padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);border-radius:var(--roxy-radius-md,8px);font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a)}`,\n\t];\n\n\t@property({ type: String, reflect: true })\n\ttype:\n\t\t| 'life-path'\n\t\t| 'expression'\n\t\t| 'soul-urge'\n\t\t| 'personality'\n\t\t| 'birth-day'\n\t\t| 'maturity'\n\t\t| 'daily'\n\t\t| 'personal-day'\n\t\t| 'personal-month'\n\t\t| 'personal-year'\n\t\t| 'chart' = 'life-path';\n\n\tprotected renderData(d: NumerologyData) {\n\t\tconst headerLabel = this.t(LABELS[this.type] ?? this.type);\n\t\tif ('coreNumbers' in d) return this.renderChart(d, headerLabel);\n\t\t// Period reads share a number+theme+body shape but differ in field names;\n\t\t// check the most specific key first (a personal-day response also carries\n\t\t// personalMonth/personalYear), so the order is day -> month -> year.\n\t\tif ('personalDay' in d) return this.renderPersonalDay(d, headerLabel);\n\t\tif ('personalMonth' in d) return this.renderPersonalMonth(d, headerLabel);\n\t\tif ('personalYear' in d) return this.renderPersonalYear(d, headerLabel);\n\t\tif ('dailyMessage' in d) return this.renderDailyNumber(d, headerLabel);\n\t\treturn this.renderNumberCard(d as NumberCardData, headerLabel);\n\t}\n\n\tprivate renderNumberCard(d: NumberCardData, headerLabel: string) {\n\t\tconst readings = !this.hideReadings;\n\t\treturn html`<article class=\"card\" part=\"card\" aria-label=\"${headerLabel}\">${this.renderHero(headerLabel, d.number, d.meaning?.title, isMaster(d.number, d.type))} ${d.meaning?.description && readings ? html`<p class=\"meaning\">${d.meaning.description}</p>` : nothing} ${d.calculation ? html`<pre class=\"calc\" part=\"calculation\">${d.calculation}</pre>` : nothing} ${\n\t\t\t\t// The debt NUMBER is a fact about the chart; the sentences that follow\n\t\t\t\t// it are the reading of that fact.\n\t\t\t\td.hasKarmicDebt && d.karmicDebtNumber\n\t\t\t\t\t? html`<div class=\"karmic\" part=\"section karmic-debt\">${this.t('Karmic debt')} ${d.karmicDebtNumber}. ${readings ? karmicDebtText(d.karmicDebtMeaning) : ''}</div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${this.renderReading(d.meaning, 'numerology-guidance')}</article>`;\n\t}\n\n\t/** The daily number carries the same full interpretation as a core number, plus the message for the day. */\n\tprivate renderDailyNumber(d: GetDailyNumberResponse, headerLabel: string) {\n\t\tconst readings = !this.hideReadings;\n\t\treturn html`<article class=\"card\" part=\"card\" aria-label=\"${headerLabel}\">${this.renderHero(headerLabel, d.number, d.meaning?.title, isMaster(d.number, d.type))} ${d.dailyMessage && readings ? html`<p class=\"meaning\">${d.dailyMessage}</p>` : nothing} ${d.meaning?.description && readings ? html`<p class=\"meaning\">${d.meaning.description}</p>` : nothing} ${this.renderReading(d.meaning, 'numerology-daily-guidance')}</article>`;\n\t}\n\n\t/** A personal day sits inside its personal month, which sits inside its personal year. Dropping that nesting strips the day of the context that gives it meaning. */\n\tprivate renderPersonalDay(\n\t\td: CalculatePersonalDayResponse,\n\t\theaderLabel: string,\n\t) {\n\t\treturn html`<article class=\"card\" part=\"card\" aria-label=\"${headerLabel}\">${this.renderHero(headerLabel, d.personalDay, d.theme)} ${d.guidance && !this.hideReadings ? html`<p class=\"meaning\">${d.guidance}</p>` : nothing}<dl class=\"attrs\" part=\"details\">${this.attr(this.t('Date'), formatDate(this.effectiveLang(), d.targetDate))} ${this.attr(\n\t\t\t\t\tthis.t('Personal month'),\n\t\t\t\t\tjoinCycle(d.personalMonth, d.personalMonthTheme),\n\t\t\t\t)} ${this.attr(\n\t\t\t\t\tthis.t('Personal year'),\n\t\t\t\t\tjoinCycle(d.personalYear, d.personalYearTheme),\n\t\t\t\t)}</dl></article>`;\n\t}\n\n\tprivate renderPersonalMonth(\n\t\td: CalculatePersonalMonthResponse,\n\t\theaderLabel: string,\n\t) {\n\t\treturn html`<article class=\"card\" part=\"card\" aria-label=\"${headerLabel}\">${this.renderHero(headerLabel, d.personalMonth, d.theme)} ${d.focus && !this.hideReadings ? html`<p class=\"meaning\">${d.focus}</p>` : nothing}<dl class=\"attrs\" part=\"details\">${this.attr(this.t('Calendar month'), d.calendarMonth)} ${this.attr(\n\t\t\t\t\tthis.t('Personal year'),\n\t\t\t\t\tjoinCycle(d.personalYear, d.personalYearTheme),\n\t\t\t\t)}</dl></article>`;\n\t}\n\n\tprivate renderPersonalYear(\n\t\td: CalculatePersonalYearResponse,\n\t\theaderLabel: string,\n\t) {\n\t\tconst readings = !this.hideReadings;\n\t\treturn html`<article class=\"card\" part=\"card\" aria-label=\"${headerLabel}\">${this.renderHero(headerLabel, d.personalYear, d.theme)} ${d.cycle ? html`<p class=\"label\">${d.cycle}</p>` : nothing} ${d.forecast && readings ? html`<p class=\"meaning\">${d.forecast}</p>` : nothing} ${\n\t\t\t\treadings\n\t\t\t\t\t? this.renderLists(\n\t\t\t\t\t\t\t['Opportunities', d.opportunities],\n\t\t\t\t\t\t\t['Challenges', d.challenges],\n\t\t\t\t\t\t)\n\t\t\t\t\t: nothing\n\t\t\t} ${d.advice && readings ? html`<p class=\"meaning\">${d.advice}</p>` : nothing}</article>`;\n\t}\n\n\tprivate renderChart(d: GenerateNumerologyChartResponse, headerLabel: string) {\n\t\tconst cores = Object.entries(d.coreNumbers ?? {}).filter(([, v]) => v);\n\t\tconst ins = d.additionalInsights;\n\t\tconst lucky = d.luckyAssociations;\n\t\tconst profile = d.birthDayProfile;\n\t\tconst maturity = d.maturityStatus;\n\n\t\tconst readings = !this.hideReadings;\n\n\t\treturn html`<article class=\"card\" part=\"card\" aria-label=\"${headerLabel}\"><div part=\"header\"><p class=\"label\">${headerLabel}</p>${d.profile?.name ? html`<h2 class=\"title\">${d.profile.name}</h2>` : nothing} ${d.profile?.birthdate ? html`<p class=\"label\">${formatDate(this.effectiveLang(), d.profile.birthdate)}</p>` : nothing}</div>${d.summary && readings ? html`<p class=\"meaning\">${d.summary}</p>` : nothing} ${\n\t\t\t\tcores.length > 0\n\t\t\t\t\t? html`<div class=\"cores\" part=\"details cores\">${cores.map(\n\t\t\t\t\t\t\t([k, v]) => html`<div class=\"item\"><div class=\"core-head\"><span class=\"core-key\">${humanize(k)}</span> <strong>${v.number ?? ''}</strong></div>${v.meaning?.title ? html`<span class=\"core-title\">${v.meaning.title}</span>` : nothing} ${isMaster(v.number, v.type) ? html`<span class=\"master\">${this.t('Master')} ${v.number}</span>` : nothing}</div>`,\n\t\t\t\t\t\t)}</div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${this.renderInterpretation(\n\t\t\t\tcores.map(([k, v]) => ({\n\t\t\t\t\tlabel: humanize(k),\n\t\t\t\t\taside: [\n\t\t\t\t\t\tv.number != null ? `${v.number}` : '',\n\t\t\t\t\t\tisMaster(v.number, v.type) ? 'master' : '',\n\t\t\t\t\t\tv.hasKarmicDebt && v.karmicDebtNumber\n\t\t\t\t\t\t\t? `karmic debt ${v.karmicDebtNumber}`\n\t\t\t\t\t\t\t: '',\n\t\t\t\t\t]\n\t\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t\t.join(' · '),\n\t\t\t\t\tbody: v.meaning?.description ?? '',\n\t\t\t\t\textra: this.renderReading(v.meaning, `numerology-core-${k}`),\n\t\t\t\t})),\n\t\t\t\t'numerology-cores',\n\t\t\t\t'Core numbers',\n\t\t\t)} ${\n\t\t\t\tmaturity\n\t\t\t\t\t? html`<dl class=\"attrs\" part=\"details maturity\">${this.attr(this.t('Maturity'), maturity.isActive ? this.t('Active') : this.t('Not yet active'))} ${this.attr(this.t('Current age'), maturity.currentAge)} ${this.attr(this.t('Activates'), maturity.activationRange)}</dl>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\tprofile\n\t\t\t\t\t? html`<section class=\"sub\" part=\"section birth-day-profile\"><h3>${this.t('Birth day profile')}</h3>${\n\t\t\t\t\t\t\tprofile.title\n\t\t\t\t\t\t\t\t? html`<h4 class=\"sub-title\">${[profile.day ? `Day ${profile.day}` : '', profile.title].filter(Boolean).join(' · ')}</h4>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t} ${profile.description && readings ? html`<p>${profile.description}</p>` : nothing} ${this.renderReading(profile, 'numerology-birth-day-profile')}</section>`\n\t\t\t\t\t: nothing\n\t\t\t} ${ins ? this.renderInsights(ins) : nothing} ${\n\t\t\t\tlucky\n\t\t\t\t\t? html`<section class=\"sub\" part=\"section lucky-associations\"><h3>${this.t('Lucky associations')}</h3><dl class=\"attrs\">${this.attr(this.t('Day'), lucky.day)} ${this.attr(this.t('Element'), lucky.element)} ${this.attr(this.t('Ruling planet'), lucky.rulingPlanet)} ${this.attr(this.t('Colors'), lucky.colors?.join(', '))} ${this.attr(this.t('Gemstones'), lucky.gemstones?.join(', '))} ${this.attr(this.t('Compatible'), lucky.compatibleNumbers?.join(', '))} ${this.attr(this.t('Incompatible'), lucky.incompatibleNumbers?.join(', '))}</dl></section>`\n\t\t\t\t\t: nothing\n\t\t\t}</article>`;\n\t}\n\n\t/** Everything the chart carries beyond the six core numbers: the karmic analysis, the current year and month, the four pinnacles and their paired challenges, the name-derived numbers, and the letter analysis. */\n\tprivate renderInsights(\n\t\tins: NonNullable<GenerateNumerologyChartResponse['additionalInsights']>,\n\t) {\n\t\tconst lessons = ins.karmicLessons;\n\t\tconst debt = ins.karmicDebt;\n\t\tconst year = ins.personalYear;\n\t\tconst passion = ins.hiddenPassion;\n\t\tconst subconscious = ins.subconsciousSelf;\n\t\tconst letters = ins.nameLetters;\n\t\tconst present = Object.entries(lessons?.presentNumbers ?? {});\n\t\tconst readings = !this.hideReadings;\n\n\t\treturn html`${\n\t\t\tlessons\n\t\t\t\t? html`<section class=\"sub\" part=\"section karmic-lessons\"><h3>${this.t('Karmic lessons')}</h3>${\n\t\t\t\t\t\tlessons.missingNumbers?.length\n\t\t\t\t\t\t\t? html`<div class=\"chips\">${lessons.missingNumbers.map((n) => html`<span>${this.t('Missing')} ${n}</span>`)}</div>`\n\t\t\t\t\t\t\t: html`<p>${this.t('No numbers are missing from the birth name.')}</p>`\n\t\t\t\t\t} ${\n\t\t\t\t\t\tpresent.length > 0\n\t\t\t\t\t\t\t? html`<div class=\"chips\">${present.map(([digit, count]) => html`<span>${digit} x ${count}</span>`)}</div>`\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t} ${this.renderInterpretation(\n\t\t\t\t\t\t(lessons.lessons ?? []).map((l) => ({\n\t\t\t\t\t\t\tlabel: l.lesson ?? `Lesson ${l.number}`,\n\t\t\t\t\t\t\taside: l.number != null ? `${l.number}` : '',\n\t\t\t\t\t\t\tbody: l.description ?? '',\n\t\t\t\t\t\t\textra: l.howToOvercome\n\t\t\t\t\t\t\t\t? html`<p><strong>${this.t('How to overcome')}.</strong> ${l.howToOvercome}</p>`\n\t\t\t\t\t\t\t\t: nothing,\n\t\t\t\t\t\t})),\n\t\t\t\t\t\t'numerology-karmic-lessons',\n\t\t\t\t\t\t'Lessons',\n\t\t\t\t\t)}</section>`\n\t\t\t\t: nothing\n\t\t}${\n\t\t\tdebt?.hasKarmicDebt\n\t\t\t\t? html`<section class=\"sub\" part=\"section karmic-debt\"><h3>${this.t('Karmic debt')}</h3>${\n\t\t\t\t\t\tdebt.debtNumbers?.length\n\t\t\t\t\t\t\t? html`<div class=\"chips\">${debt.debtNumbers.map((n) => html`<span>${this.t('Debt')} ${n}</span>`)}</div>`\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t} ${this.renderInterpretation(\n\t\t\t\t\t\t(debt.meanings ?? []).map((m) => ({\n\t\t\t\t\t\t\tlabel: `${this.t('Karmic debt')} ${m.number}`,\n\t\t\t\t\t\t\tbody: m.description ?? '',\n\t\t\t\t\t\t\textra: html`${\n\t\t\t\t\t\t\t\tm.challenge\n\t\t\t\t\t\t\t\t\t? html`<p><strong>${this.t('Challenge')}.</strong> ${m.challenge}</p>`\n\t\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t\t}${\n\t\t\t\t\t\t\t\tm.resolution\n\t\t\t\t\t\t\t\t\t? html`<p><strong>${this.t('Resolution')}.</strong> ${m.resolution}</p>`\n\t\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t\t}`,\n\t\t\t\t\t\t})),\n\t\t\t\t\t\t'numerology-karmic-debt',\n\t\t\t\t\t\t'Debts',\n\t\t\t\t\t)}</section>`\n\t\t\t\t: nothing\n\t\t}${\n\t\t\tyear\n\t\t\t\t? html`<section class=\"sub\" part=\"section personal-year\"><h3>${this.t('Personal year')}</h3><h4 class=\"sub-title\">${[\n\t\t\t\t\t\t\tyear.personalYear != null ? `${year.personalYear}` : '',\n\t\t\t\t\t\t\tyear.theme ?? '',\n\t\t\t\t\t\t\tyear.cycle ?? '',\n\t\t\t\t\t\t]\n\t\t\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t\t\t.join(' · ')}</h4>${year.forecast && readings ? html`<p>${year.forecast}</p>` : nothing} ${\n\t\t\t\t\t\treadings\n\t\t\t\t\t\t\t? this.renderLists(\n\t\t\t\t\t\t\t\t\t['Opportunities', year.opportunities],\n\t\t\t\t\t\t\t\t\t['Challenges', year.challenges],\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t} ${year.advice && readings ? html`<p>${year.advice}</p>` : nothing} ${\n\t\t\t\t\t\t// The month and its theme name where the year sits now; `focus` is\n\t\t\t\t\t\t// the sentence about it.\n\t\t\t\t\t\tyear.personalMonth\n\t\t\t\t\t\t\t? html`<p><strong>${[\n\t\t\t\t\t\t\t\t\tyear.personalMonth.personalMonth != null\n\t\t\t\t\t\t\t\t\t\t? `${this.t('Personal month')} ${year.personalMonth.personalMonth}`\n\t\t\t\t\t\t\t\t\t\t: this.t('Personal month'),\n\t\t\t\t\t\t\t\t\tyear.personalMonth.theme ?? '',\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t\t\t\t\t.join(' · ')}.</strong> ${readings ? (year.personalMonth.focus ?? '') : ''}</p>`\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t}</section>`\n\t\t\t\t: nothing\n\t\t}${\n\t\t\t// Both period sections are an accordion and nothing else, so hiding the\n\t\t\t// readings takes the heading with it rather than leaving one over an\n\t\t\t// empty block.\n\t\t\tins.pinnacles?.length && readings\n\t\t\t\t? html`<section class=\"sub\" part=\"section pinnacles\"><h3>${this.t('Pinnacles')}</h3>${this.renderInterpretation(\n\t\t\t\t\t\tins.pinnacles.map((p) => ({\n\t\t\t\t\t\t\tlabel: [\n\t\t\t\t\t\t\t\tp.position != null ? `Pinnacle ${p.position}` : 'Pinnacle',\n\t\t\t\t\t\t\t\tp.meaning?.title ?? '',\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t\t\t\t.join(' · '),\n\t\t\t\t\t\t\taside: [\n\t\t\t\t\t\t\t\tp.number != null ? `${p.number}` : '',\n\t\t\t\t\t\t\t\tageRange(p.startAge, p.endAge),\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t\t\t\t.join(' · '),\n\t\t\t\t\t\t\tbody: p.meaning?.description ?? '',\n\t\t\t\t\t\t\textra: this.renderLists(\n\t\t\t\t\t\t\t\t['Opportunities', p.meaning?.opportunities],\n\t\t\t\t\t\t\t\t['Challenges', p.meaning?.challenges],\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t})),\n\t\t\t\t\t\t'numerology-pinnacles',\n\t\t\t\t\t\t'Life phases',\n\t\t\t\t\t)}</section>`\n\t\t\t\t: nothing\n\t\t}${\n\t\t\tins.challenges?.length && readings\n\t\t\t\t? html`<section class=\"sub\" part=\"section obstacle-periods\"><h3>${this.t('Challenges')}</h3>${this.renderInterpretation(\n\t\t\t\t\t\tins.challenges.map((c) => ({\n\t\t\t\t\t\t\tlabel: [\n\t\t\t\t\t\t\t\tc.position != null ? `Challenge ${c.position}` : 'Challenge',\n\t\t\t\t\t\t\t\tc.meaning?.title ?? '',\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t\t\t\t.join(' · '),\n\t\t\t\t\t\t\taside: [\n\t\t\t\t\t\t\t\tc.number != null ? `${c.number}` : '',\n\t\t\t\t\t\t\t\tageRange(c.startAge, c.endAge),\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t\t\t\t.join(' · '),\n\t\t\t\t\t\t\tbody: c.meaning?.description ?? '',\n\t\t\t\t\t\t\textra: html`${\n\t\t\t\t\t\t\t\tc.meaning?.lesson\n\t\t\t\t\t\t\t\t\t? html`<p><strong>${this.t('Lesson')}.</strong> ${c.meaning.lesson}</p>`\n\t\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t\t}${\n\t\t\t\t\t\t\t\tc.meaning?.howToOvercome\n\t\t\t\t\t\t\t\t\t? html`<p><strong>${this.t('How to overcome')}.</strong> ${c.meaning.howToOvercome}</p>`\n\t\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t\t}`,\n\t\t\t\t\t\t})),\n\t\t\t\t\t\t'numerology-challenges',\n\t\t\t\t\t\t'Obstacle periods',\n\t\t\t\t\t)}</section>`\n\t\t\t\t: nothing\n\t\t}${\n\t\t\tpassion || subconscious\n\t\t\t\t? html`<section class=\"sub\" part=\"section name-numbers\"><h3>${this.t('Name numbers')}</h3>${\n\t\t\t\t\t\tpassion\n\t\t\t\t\t\t\t? html`<div><h4 class=\"sub-title\">${[\n\t\t\t\t\t\t\t\t\t\tpassion.number != null\n\t\t\t\t\t\t\t\t\t\t\t? `Hidden passion ${passion.number}`\n\t\t\t\t\t\t\t\t\t\t\t: 'Hidden passion',\n\t\t\t\t\t\t\t\t\t\tpassion.title ?? '',\n\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t\t\t\t\t\t.join(' · ')}</h4>${passion.description && readings ? html`<p>${passion.description}</p>` : nothing} ${\n\t\t\t\t\t\t\t\t\ttypeof passion.count === 'number'\n\t\t\t\t\t\t\t\t\t\t? html`<p class=\"label\">${`Appears ${passion.count} times in the name`}</p>`\n\t\t\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t\t\t}</div>`\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t} ${\n\t\t\t\t\t\tsubconscious\n\t\t\t\t\t\t\t? html`<div><h4 class=\"sub-title\">${[\n\t\t\t\t\t\t\t\t\t\tsubconscious.number != null\n\t\t\t\t\t\t\t\t\t\t\t? `Subconscious self ${subconscious.number}`\n\t\t\t\t\t\t\t\t\t\t\t: 'Subconscious self',\n\t\t\t\t\t\t\t\t\t\tsubconscious.title ?? '',\n\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t\t\t\t\t\t.join(' · ')}</h4>${subconscious.description && readings ? html`<p>${subconscious.description}</p>` : nothing} ${\n\t\t\t\t\t\t\t\t\tsubconscious.uniqueNumbers?.length\n\t\t\t\t\t\t\t\t\t\t? html`<p class=\"label\">${`Numbers present: ${subconscious.uniqueNumbers.join(', ')}`}</p>`\n\t\t\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t\t\t}</div>`\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t}</section>`\n\t\t\t\t: nothing\n\t\t}${\n\t\t\tletters && readings\n\t\t\t\t? html`<section class=\"sub\" part=\"section name-letters\"><h3>${this.t('Name letters')}</h3>${this.renderInterpretation(\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tlabel: this.t('Cornerstone'),\n\t\t\t\t\t\t\t\taside: letterAside(\n\t\t\t\t\t\t\t\t\tletters.cornerstone?.letter,\n\t\t\t\t\t\t\t\t\tletters.cornerstone?.number,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\tbody: letters.cornerstone?.meaning ?? '',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tlabel: this.t('Capstone'),\n\t\t\t\t\t\t\t\taside: letterAside(\n\t\t\t\t\t\t\t\t\tletters.capstone?.letter,\n\t\t\t\t\t\t\t\t\tletters.capstone?.number,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\tbody: letters.capstone?.meaning ?? '',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tlabel: this.t('First vowel'),\n\t\t\t\t\t\t\t\taside: letterAside(letters.firstVowel?.letter),\n\t\t\t\t\t\t\t\tbody: letters.firstVowel?.meaning ?? '',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t\t'numerology-name-letters',\n\t\t\t\t\t\t'Letter analysis',\n\t\t\t\t\t)}</section>`\n\t\t\t\t: nothing\n\t\t}`;\n\t}\n\n\t/** Hero row shared by every single-number view: the numeral, the label, the archetype, and the master-number badge when the number is 11, 22, or 33. */\n\tprivate renderHero(\n\t\theaderLabel: string,\n\t\tnum: number | undefined,\n\t\ttitle: string | undefined,\n\t\tmaster = false,\n\t) {\n\t\treturn html`<div class=\"hero\" part=\"header\">${typeof num === 'number' ? html`<div class=\"numeral\">${num}</div>` : nothing}<div><p class=\"label\">${headerLabel}</p>${title ? html`<h2 class=\"title\">${title}</h2>` : nothing} ${master ? html`<span class=\"master\">${this.t('Master number')}</span>` : nothing}</div></div>`;\n\t}\n\n\t/**\n\t * Keyword chips, the strengths and challenges lists, and the three life-area readings behind a disclosure. The whole interpretation, laid out as a reading.\n\t *\n\t * Every part of it is prose or the shorthand of prose, so `hide-readings` drops the block whole rather than gating three pieces.\n\t */\n\tprivate renderReading(r: NumerologyReading | undefined, name: string) {\n\t\tif (!r || this.hideReadings) return nothing;\n\t\tconst sections: InterpSection[] = GUIDANCE_FIELDS.map(([key, label]) => ({\n\t\t\tlabel,\n\t\t\tbody: (r[key] as string | undefined) ?? '',\n\t\t}));\n\t\treturn html`${\n\t\t\tr.keywords?.length\n\t\t\t\t? html`<div class=\"chips\">${r.keywords.map((k) => html`<span>${k}</span>`)}</div>`\n\t\t\t\t: nothing\n\t\t}${this.renderLists(['Strengths', r.strengths], ['Challenges', r.challenges])}${this.renderInterpretation(sections, name, 'Guidance')}`;\n\t}\n\n\t/**\n\t * Side-by-side prose lists. Each entry names a trait and explains it, so they are bulleted paragraphs, never chips.\n\t *\n\t * @remarks\n\t * `h3`, not `h4`. This renders at the TOP level of the single-number card, where the only heading above it is the `h2` title, so an `h4` skips a level and fails the axe `heading-order` rule. Inside a chart section or an accordion body the heading above is already an `h3`, and a repeated level is fine: the rule flags skips, not repeats.\n\t */\n\tprivate renderLists(\n\t\t...lists: Array<[ChromeString, ReadonlyArray<string> | undefined]>\n\t) {\n\t\tconst shown = lists.filter(([, items]) => items && items.length > 0);\n\t\tif (shown.length === 0) return nothing;\n\t\treturn html`<div class=\"lists\">${shown.map(\n\t\t\t\t([label, items]) => html`<div><h3>${this.t(label)}</h3><ul>${(items ?? []).map((item) => html`<li>${item}</li>`)}</ul></div>`,\n\t\t\t)}</div>`;\n\t}\n\n\tprivate attr(label: string, value: string | number | undefined) {\n\t\tif (value === undefined || value === null || value === '') return nothing;\n\t\treturn html`<div class=\"attr\"><dt>${label}</dt><dd>${value}</dd></div>`;\n\t}\n}\n\nconst LABELS: Record<string, string> = {\n\t'life-path': 'Life Path',\n\texpression: 'Expression',\n\t'soul-urge': 'Soul Urge',\n\tpersonality: 'Personality',\n\t'birth-day': 'Birth Day',\n\tmaturity: 'Maturity',\n\tdaily: 'Daily Number',\n\t'personal-day': 'Personal Day',\n\t'personal-month': 'Personal month',\n\t'personal-year': 'Personal year',\n\tchart: 'Numerology chart',\n};\n\ntype KarmicDebtMeaning = CalculateLifePathResponse['karmicDebtMeaning'];\n\nfunction karmicDebtText(value: KarmicDebtMeaning | undefined): string {\n\tif (!value) return '';\n\treturn [value.description, value.challenge, value.resolution]\n\t\t.filter(Boolean)\n\t\t.join(' ');\n}\n\n/** A number is master when the API says so, or when it is one of the three that are never reduced. Both checks, because the period cycles carry the number without the `type` flag. */\nfunction isMaster(num: number | undefined, type?: string): boolean {\n\treturn type === 'master' || (num !== undefined && MASTER_NUMBERS.has(num));\n}\n\n/** \"5 · Freedom and change\", the cycle a smaller cycle sits inside. Empty when the API sent neither half. */\nfunction joinCycle(num: number | undefined, theme: string | undefined): string {\n\treturn [num != null ? `${num}` : '', theme ?? ''].filter(Boolean).join(' · ');\n}\n\n/** \"ages 27 to 35\", or \"ages 54 onward\" for the final phase, which the API closes with a null end age. */\nfunction ageRange(\n\tstart: number | null | undefined,\n\tend: number | null | undefined,\n): string {\n\tif (start == null) return '';\n\treturn end == null ? `ages ${start} onward` : `ages ${start} to ${end}`;\n}\n\n/** \"A · 1\" for a letter that carries a Pythagorean value, \"A\" for the first vowel, which does not. */\nfunction letterAside(letter?: string, num?: number): string {\n\treturn [letter ?? '', num != null ? `${num}` : '']\n\t\t.filter(Boolean)\n\t\t.join(' · ');\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-numerology-card': RoxyNumerologyCard;\n\t}\n}\n", "import { css, html, nothing, type TemplateResult } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport type {\n\tGetBasicPanchangResponse,\n\tGetDetailedPanchangResponse,\n} from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { formatSignPosition } from '../utils/degree.js';\nimport {\n\tformatDate,\n\tformatPercent,\n\tformatTime,\n\tformatTimeRange,\n\tformatWithSanskrit,\n} from '../utils/format.js';\n\ntype PanchangData = GetBasicPanchangResponse | GetDetailedPanchangResponse;\ntype Detailed = GetDetailedPanchangResponse;\ntype PanchangTime = Detailed['rahuKaal'];\ntype RashiPlacement = Detailed['moonSign'];\ntype SunNakshatra = Detailed['sunNakshatra'];\ntype Hora = Detailed['hora'];\ntype Panchaka = Detailed['panchaka'];\ntype Bhadra = Detailed['bhadra'];\ntype Chandrabalam = Detailed['chandrabalam'];\ntype Tarabalam = Detailed['tarabalam'];\ntype MoonSignTransition = Detailed['transitions']['moonSign'];\n\n/** One limb of the panchang: the headline value plus the muted detail line under it. */\ninterface Limb {\n\tlabel: string;\n\tvalue: string;\n\tmeta: string;\n}\n\n/** Joins the parts of a detail line, dropping anything the response omitted. */\nfunction meta(...parts: Array<string | number | undefined | null>): string {\n\treturn parts.filter(Boolean).join(' · ');\n}\n\n/** The headline value of a limb: its name plus the one qualifier that belongs on the same line. */\nfunction name(...parts: Array<string | undefined>): string {\n\treturn parts.filter(Boolean).join(', ');\n}\n\n/**\n * Panchang table for /vedic-astrology/panchang/{basic,detailed}.\n *\n * @remarks\n * The main grid lists the five limbs (tithi, nakshatra, yoga, karana, vara),\n * each with the reading that comes with it: the tithi ruling planet, deity,\n * element and how far it has elapsed; the nakshatra lord, deity and symbol; the\n * yoga and karana characteristics. Detailed mode adds the sunrise placements a\n * reader scans first (Moon rashi, Sun rashi, Sun nakshatra, current hora), the\n * exact transition times including the Moon sign change, and groups every timed\n * window into auspicious (fixed muhurtas plus each Amrit Kalam) and\n * inauspicious (Rahu Kaal, Yamaganda, Gulika, each Dur Muhurta and Varjyam,\n * plus Bhadra and Panchaka). Chandrabalam and Tarabalam close the card: which\n * birth rashis and birth nakshatras this day favors, the surface a muhurta is\n * actually chosen on.\n */\n@customElement('roxy-panchang-table')\nexport class RoxyPanchangTable extends RoxyDataElement<PanchangData> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.wrap{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);background:var(--roxy-surface,#fff);box-shadow:var(--roxy-shadow-sm);overflow:hidden}.head{padding:var(--roxy-space-md,1rem);border-bottom:1px solid var(--roxy-border,#e4e4e7);justify-content:space-between;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.date{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem)}table{border-collapse:collapse;width:100%;font-size:var(--roxy-text-sm,.875rem)}tbody tr:nth-child(odd){background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 24%, transparent)}th,td{padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);text-align:left;vertical-align:top}th{color:var(--roxy-muted,#71717a);font-weight:var(--roxy-weight-bold,600);text-transform:capitalize;width:38%}td{color:var(--roxy-fg,#0a0a0a);font-variant-numeric:tabular-nums}td small{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);font-variant-numeric:normal;margin-top:2px;line-height:1.5;display:block}.section{border-top:1px solid var(--roxy-border,#e4e4e7);padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);font-weight:var(--roxy-weight-bold,600);text-transform:uppercase;letter-spacing:.06em}.quiet{color:var(--roxy-muted,#71717a)}.chips{gap:var(--roxy-space-xs,.25rem);flex-wrap:wrap;margin-top:4px;display:flex}.chip{border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 55%, transparent);padding:1px 8px}.chip.good{background:color-mix(in srgb, var(--roxy-success,#16a34a) 14%, transparent);color:var(--roxy-success-fg,#166534)}.chip.bad{background:color-mix(in srgb, var(--roxy-danger,#dc2626) 14%, transparent);color:var(--roxy-danger-fg,#991b1b)}`,\n\t];\n\n\t@property({ type: String, reflect: true })\n\tdetail: 'basic' | 'detailed' = 'detailed';\n\n\tprotected renderData(d: PanchangData) {\n\t\tconst detailed = 'sunrise' in d ? d : null;\n\t\tconst basic = 'sunLongitude' in d ? d : null;\n\n\t\tconst muhurtas: Array<[string, PanchangTime | null | undefined]> = detailed\n\t\t\t? [\n\t\t\t\t\t['Brahma Muhurta', detailed.brahmaMuhurta],\n\t\t\t\t\t['Abhijit Muhurta', detailed.abhijitMuhurta],\n\t\t\t\t\t['Vijaya Muhurta', detailed.vijayaMuhurta],\n\t\t\t\t\t['Godhuli Muhurta', detailed.godhuliMuhurta],\n\t\t\t\t\t['Nishita Muhurta', detailed.nishitaMuhurta],\n\t\t\t\t\t['Pratah Sandhya', detailed.pratahSandhya],\n\t\t\t\t\t['Sayahna Sandhya', detailed.sayahnaSandhya],\n\t\t\t\t]\n\t\t\t: [];\n\n\t\tconst auspiciousWindows: Array<[string, PanchangTime]> = detailed\n\t\t\t? this.expandWindows(this.t('Amrit Kalam'), detailed.amritKalam)\n\t\t\t: [];\n\n\t\tconst inauspicious: Array<[string, PanchangTime | undefined]> = detailed\n\t\t\t? [\n\t\t\t\t\t['Rahu Kaal', detailed.rahuKaal],\n\t\t\t\t\t['Yamaganda', detailed.yamaganda],\n\t\t\t\t\t['Gulika', detailed.gulika],\n\t\t\t\t]\n\t\t\t: [];\n\n\t\tconst inauspiciousWindows: Array<[string, PanchangTime]> = detailed\n\t\t\t? [\n\t\t\t\t\t...this.expandWindows(this.t('Dur Muhurta'), detailed.durMuhurta),\n\t\t\t\t\t...this.expandWindows(this.t('Varjyam'), detailed.varjyam),\n\t\t\t\t]\n\t\t\t: [];\n\n\t\tconst showTimings = this.detail === 'detailed' && detailed !== null;\n\n\t\treturn html`<div class=\"wrap\" part=\"card\" aria-label=\"${this.t('Panchang')}\"><header class=\"head\" part=\"header\"><h2 class=\"title\">${this.t('Panchang')}</h2><span class=\"date\">${detailed ? formatDate(this.effectiveLang(), detailed.date) : ''}</span></header><table part=\"table\"><tbody>${this.limbs(d, detailed).map((l) => this.renderLimbRow(l))} ${\n\t\t\t\t\t\tdetailed\n\t\t\t\t\t\t\t? html`${this.renderRow(this.t('Sunrise'), formatTime(this.effectiveLang(), detailed.sunrise))} ${this.renderRow(this.t('Sunset'), formatTime(this.effectiveLang(), detailed.sunset))} ${this.renderRow(this.t('Moonrise'), formatTime(this.effectiveLang(), detailed.moonrise))} ${this.renderRow(this.t('Moonset'), formatTime(this.effectiveLang(), detailed.moonset))} ${this.renderRow(this.t('Moon sign'), this.formatRashi(detailed.moonSign))} ${this.renderRow(this.t('Sun sign'), this.formatRashi(detailed.sunSign))} ${this.renderRow(this.t('Sun nakshatra'), this.formatSunNakshatra(detailed.sunNakshatra))} ${this.renderRow(this.t('Hora'), this.formatHora(detailed.hora))}`\n\t\t\t\t\t\t\t: basic\n\t\t\t\t\t\t\t\t? html`${this.renderRow(this.t('Sun'), this.formatLongitude(basic.sunLongitude))} ${this.renderRow(this.t('Moon'), this.formatLongitude(basic.moonLongitude))}`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t}</tbody></table>${detailed ? this.renderTransitions(detailed.transitions) : nothing} ${\n\t\t\t\tshowTimings\n\t\t\t\t\t? html`<div class=\"section\" part=\"section auspicious\">${this.t('Auspicious muhurtas')}</div><table part=\"table auspicious\"><tbody>${this.renderPeriodRows([\n\t\t\t\t\t\t\t\t\t...muhurtas.filter(\n\t\t\t\t\t\t\t\t\t\t(m): m is [string, PanchangTime] => !!m[1],\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t...auspiciousWindows,\n\t\t\t\t\t\t\t\t])}</tbody></table><div class=\"section\" part=\"section inauspicious\">${this.t('Inauspicious periods')}</div><table part=\"table inauspicious\"><tbody>${this.renderPeriodRows([\n\t\t\t\t\t\t\t\t\t...inauspicious.filter(\n\t\t\t\t\t\t\t\t\t\t(m): m is [string, PanchangTime] => !!m[1],\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t...inauspiciousWindows,\n\t\t\t\t\t\t\t\t])} ${this.renderBhadraRow(detailed.bhadra)} ${this.renderPanchakaRow(detailed.panchaka)}</tbody></table>${this.renderBalams(detailed.chandrabalam, detailed.tarabalam)}`\n\t\t\t\t\t: nothing\n\t\t\t}</div>`;\n\t}\n\n\t/**\n\t * The five limbs, each with its own reading. Basic and detailed carry the\n\t * same limb shapes; only detailed carries the vara.\n\t */\n\tprivate limbs(d: PanchangData, detailed: Detailed | null): Limb[] {\n\t\tconst t = d.tithi;\n\t\tconst n = d.nakshatra;\n\t\tconst y = d.yoga;\n\t\tconst k = d.karana;\n\t\tconst rows: Limb[] = [\n\t\t\t{\n\t\t\t\tlabel: this.t('Tithi'),\n\t\t\t\tvalue: name(t?.name, t?.paksha ? `${t.paksha} paksha` : undefined),\n\t\t\t\tmeta: meta(\n\t\t\t\t\tt?.rulingPlanet ? `Ruled by ${t.rulingPlanet}` : undefined,\n\t\t\t\t\tt?.deity ? `Deity ${t.deity}` : undefined,\n\t\t\t\t\tt?.element,\n\t\t\t\t\ttypeof t?.percent === 'number'\n\t\t\t\t\t\t? `${formatPercent(this.effectiveLang(), t.percent, 0)} elapsed`\n\t\t\t\t\t\t: undefined,\n\t\t\t\t),\n\t\t\t},\n\t\t\t{\n\t\t\t\tlabel: this.t('Nakshatra'),\n\t\t\t\tvalue: name(\n\t\t\t\t\tn?.name,\n\t\t\t\t\ttypeof n?.pada === 'number' ? `pada ${n.pada}` : undefined,\n\t\t\t\t),\n\t\t\t\tmeta: meta(\n\t\t\t\t\tn?.lord ? `Lord ${n.lord}` : undefined,\n\t\t\t\t\tn?.deity ? `Deity ${n.deity}` : undefined,\n\t\t\t\t\tn?.symbol ? `Symbol ${n.symbol}` : undefined,\n\t\t\t\t\tn?.characteristics,\n\t\t\t\t),\n\t\t\t},\n\t\t\t{\n\t\t\t\tlabel: this.t('Yoga'),\n\t\t\t\tvalue: y?.name ?? '',\n\t\t\t\tmeta: y?.characteristics ?? '',\n\t\t\t},\n\t\t\t{\n\t\t\t\tlabel: this.t('Karana'),\n\t\t\t\tvalue: name(k?.name, k?.type),\n\t\t\t\tmeta: k?.characteristics ?? '',\n\t\t\t},\n\t\t];\n\t\tif (detailed?.vara) {\n\t\t\trows.push({\n\t\t\t\tlabel: this.t('Vara'),\n\t\t\t\tvalue: formatWithSanskrit(\n\t\t\t\t\tdetailed.vara.name,\n\t\t\t\t\tdetailed.vara.sanskritName,\n\t\t\t\t),\n\t\t\t\tmeta: detailed.vara.lord ? `Lord ${detailed.vara.lord}` : '',\n\t\t\t});\n\t\t}\n\t\treturn rows.filter((l) => Boolean(l.value));\n\t}\n\n\tprivate renderLimbRow(l: Limb) {\n\t\treturn html`<tr><th>${l.label}</th><td>${l.value}${l.meta ? html`<small>${l.meta}</small>` : nothing}</td></tr>`;\n\t}\n\n\t/** One label/value row, skipped when the value is empty. */\n\tprivate renderRow(\n\t\tlabel: string,\n\t\tvalue: string,\n\t): TemplateResult | typeof nothing {\n\t\tif (!value) return nothing;\n\t\treturn html`<tr><th>${label}</th><td>${value}</td></tr>`;\n\t}\n\n\tprivate renderTransitions(t: Detailed['transitions'] | undefined) {\n\t\tif (!t) return nothing;\n\t\treturn html`<div class=\"section\" part=\"section transitions\">${this.t('Next transitions')}</div><table part=\"table transitions\"><tbody>${this.renderTransitionRow(this.t('Tithi'), t.tithi)} ${this.renderTransitionRow(this.t('Nakshatra'), t.nakshatra)} ${this.renderTransitionRow(this.t('Yoga'), t.yoga)} ${this.renderTransitionRow(this.t('Karana'), t.karana)} ${this.renderMoonSignRow(t.moonSign)}</tbody></table>`;\n\t}\n\n\t/** Renders one row per [label, period] pair, dropping any with no range. */\n\tprivate renderPeriodRows(rows: Array<[string, PanchangTime]>) {\n\t\treturn rows.map(([k, v]) => {\n\t\t\tconst range = formatTimeRange(this.effectiveLang(), v);\n\t\t\treturn range\n\t\t\t\t? html`<tr><th>${k}</th><td>${range}</td></tr>`\n\t\t\t\t: nothing;\n\t\t});\n\t}\n\n\t/** Expands an array of periods into labeled rows, numbering when more than one. */\n\tprivate expandWindows(\n\t\tlabel: string,\n\t\twindows: PanchangTime[] | undefined,\n\t): Array<[string, PanchangTime]> {\n\t\tif (!windows || windows.length === 0) return [];\n\t\treturn windows.map((w, i) => [\n\t\t\twindows.length > 1 ? `${label} ${i + 1}` : label,\n\t\t\tw,\n\t\t]);\n\t}\n\n\t/**\n\t * Bhadra (Vishti karana) is avoided for every auspicious act, so \"none today\"\n\t * is as much of an answer as a window and is stated rather than left blank.\n\t */\n\tprivate renderBhadraRow(b: Bhadra | undefined) {\n\t\tif (!b) return nothing;\n\t\tconst span = this.formatSpan(b.startsAt, b.endsAt);\n\t\treturn html`<tr><th>${this.t('Bhadra (Vishti)')}</th><td>${b.active && span ? span : html`<span class=\"quiet\">${this.t('None today')}</span>`}</td></tr>`;\n\t}\n\n\t/**\n\t * Panchaka runs about five days, so the window commonly starts before or ends\n\t * after this date; the dosha type names which of the five it is.\n\t */\n\tprivate renderPanchakaRow(p: Panchaka | undefined) {\n\t\tif (!p) return nothing;\n\t\tconst span = this.formatSpan(p.startsAt, p.endsAt);\n\t\treturn html`<tr><th>${this.t('Panchaka')}</th><td>${\n\t\t\t\t\tp.active\n\t\t\t\t\t\t? html`${p.type ? `${p.type} Panchaka` : 'Panchaka'}${\n\t\t\t\t\t\t\t\tspan ? html`<small>${span}</small>` : nothing\n\t\t\t\t\t\t\t}`\n\t\t\t\t\t\t: html`<span class=\"quiet\">${this.t('None today')}</span>`\n\t\t\t\t}</td></tr>`;\n\t}\n\n\t/**\n\t * Chandrabalam and Tarabalam are read against the querent's own birth rashi\n\t * and birth nakshatra, so both lists are shown in full rather than summarized.\n\t */\n\tprivate renderBalams(c: Chandrabalam | undefined, t: Tarabalam | undefined) {\n\t\tif (!c && !t) return nothing;\n\t\treturn html`<div class=\"section\" part=\"section balams\">${this.t('Chandrabalam and Tarabalam')}</div><table part=\"table balams\"><tbody>${\n\t\t\t\t\t\tc\n\t\t\t\t\t\t\t? html`<tr><th>${this.t('Favorable Moon signs')}</th><td>${this.renderChips(c.favorableRashis, 'good')}</td></tr>${this.renderRow(this.t('Ashtama Chandra rashi'), c.ashtamaChandraRashi ?? '')}`\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t} ${\n\t\t\t\t\t\tt\n\t\t\t\t\t\t\t? html`<tr><th>${this.t('Favorable birth nakshatras')}</th><td>${this.renderChips(t.favorableNakshatras, 'good')}</td></tr><tr><th>${this.t('Unfavorable birth nakshatras')}</th><td>${this.renderChips(t.unfavorableNakshatras, 'bad')}</td></tr>`\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t}</tbody></table>`;\n\t}\n\n\tprivate renderChips(items: string[] | undefined, tone: 'good' | 'bad') {\n\t\tif (!items?.length)\n\t\t\treturn html`<span class=\"quiet\">${this.t('None')}</span>`;\n\t\treturn html`<div class=\"chips\">${items.map((i) => html`<span class=\"chip ${tone}\">${i}</span>`)}</div>`;\n\t}\n\n\tprivate renderTransitionRow(\n\t\tlabel: string,\n\t\tt: { endsAt?: string; next?: string; nextPada?: number } | undefined,\n\t) {\n\t\tif (!t?.endsAt) return nothing;\n\t\tconst time = formatTime(this.effectiveLang(), t.endsAt);\n\t\t// One whole sentence per shape, so each language orders the parts its own way.\n\t\tconst body = !t.next\n\t\t\t? this.t('ends {{time}}', { time })\n\t\t\t: typeof t.nextPada === 'number'\n\t\t\t\t? this.t('ends {{time}} to {{next}} pada {{pada}}', {\n\t\t\t\t\t\ttime,\n\t\t\t\t\t\tnext: t.next,\n\t\t\t\t\t\tpada: t.nextPada,\n\t\t\t\t\t})\n\t\t\t\t: this.t('ends {{time}} to {{next}}', { time, next: t.next });\n\t\treturn html`<tr><th>${label}</th><td>${body}</td></tr>`;\n\t}\n\n\tprivate renderMoonSignRow(m: MoonSignTransition | undefined) {\n\t\tif (!m?.changesAt) return nothing;\n\t\treturn html`<tr><th>${this.t('Moon sign')}</th><td>${\n\t\t\t\t\tm.next\n\t\t\t\t\t\t? this.t('{{sign}} until {{time}}, then {{next}}', {\n\t\t\t\t\t\t\t\tsign: m.current ?? '',\n\t\t\t\t\t\t\t\ttime: formatTime(this.effectiveLang(), m.changesAt),\n\t\t\t\t\t\t\t\tnext: m.next,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t: this.t('{{sign}} until {{time}}', {\n\t\t\t\t\t\t\t\tsign: m.current ?? '',\n\t\t\t\t\t\t\t\ttime: formatTime(this.effectiveLang(), m.changesAt),\n\t\t\t\t\t\t\t})\n\t\t\t\t}</td></tr>`;\n\t}\n\n\t/** A window that may end on the following day carries that date so it cannot be misread. */\n\tprivate formatSpan(\n\t\tstart: string | null | undefined,\n\t\tend: string | null | undefined,\n\t): string {\n\t\tconst range = formatTimeRange(this.effectiveLang(), {\n\t\t\tstart: start ?? undefined,\n\t\t\tend: end ?? undefined,\n\t\t});\n\t\tif (!range || !start || !end) return range;\n\t\treturn start.slice(0, 10) === end.slice(0, 10)\n\t\t\t? range\n\t\t\t: this.t('{{range}} (ends {{date}})', {\n\t\t\t\t\trange,\n\t\t\t\t\tdate: formatDate(this.effectiveLang(), end),\n\t\t\t\t});\n\t}\n\n\t/** \"English (Sanskrit)\" label for the Moon or Sun rashi at sunrise. */\n\tprivate formatRashi(r: RashiPlacement | undefined): string {\n\t\treturn formatWithSanskrit(r?.name, r?.sanskritName);\n\t}\n\n\t/** Sun nakshatra with pada and lord, the form a panchang reader expects. */\n\tprivate formatSunNakshatra(n: SunNakshatra | undefined): string {\n\t\tif (!n?.name) return '';\n\t\treturn meta(\n\t\t\tn.name,\n\t\t\ttypeof n.pada === 'number' ? `pada ${n.pada}` : undefined,\n\t\t\tn.lord ? `lord ${n.lord}` : undefined,\n\t\t);\n\t}\n\n\t/** Current planetary hora with its active window. */\n\tprivate formatHora(h: Hora | undefined): string {\n\t\tif (!h?.current) return '';\n\t\tconst range = formatTimeRange(this.effectiveLang(), h);\n\t\treturn range ? `${h.current} (${range})` : h.current;\n\t}\n\n\t/** Basic mode returns raw sidereal longitudes rather than named placements. */\n\tprivate formatLongitude(lon: number | undefined): string {\n\t\treturn typeof lon === 'number' ? formatSignPosition(lon) : '';\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-panchang-table': RoxyPanchangTable;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { planetGlyph, signGlyph } from '../tokens/index.js';\nimport type {\n\tArabicLotsResponse,\n\tAsteroidsResponse,\n\tLilithResponse,\n\tProgressionsResponse,\n\tSolarArcResponse,\n} from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport {\n\tformatDegreeInSign,\n\tlongitudeToSignPosition,\n} from '../utils/degree.js';\nimport { disclosureStyles } from '../utils/disclosure.js';\nimport { formatDate, formatNumber } from '../utils/format.js';\nimport {\n\ttype InterpSection,\n\tinterpAccordionStyles,\n} from '../utils/interp-accordion.js';\nimport { capitalize } from '../utils/string.js';\n\n/**\n * Union of the position-list Western responses this one editorial table renders.\n * Each carries an array of bodies in zodiac signs plus a per-body\n * interpretation; the table discriminates on which array key is present.\n */\ntype PositionsResponse =\n\t| AsteroidsResponse\n\t| LilithResponse\n\t| ProgressionsResponse\n\t| SolarArcResponse\n\t| ArabicLotsResponse;\n\ninterface Row {\n\tlabel: string;\n\tsign: string;\n\tdegree: number;\n\thouse?: number;\n\tspeed?: number;\n\tisRetrograde?: boolean;\n\tformula?: string;\n\tnatalLongitude?: number;\n\tinterpretation?: string;\n\tisAngle?: boolean;\n}\n\ninterface ViewModel {\n\ttitle: string;\n\tbadges: Array<{ label: string; value: string }>;\n\tsummary?: string;\n\trows: Row[];\n\tcols: { house: boolean; motion: boolean; formula: boolean; natal: boolean };\n}\n\n/**\n * Editorial positions table for the Western point-list endpoints: asteroids,\n * Black Moon Lilith, secondary progressions, solar arc directions, and the\n * Arabic lots. One component, five shapes: it detects the response by its array\n * key and shows only the columns that response carries (house, motion, formula,\n * or a natal-to-directed comparison), then lists each body reading below.\n */\n@customElement('roxy-positions-table')\nexport class RoxyPositionsTable extends RoxyDataElement<PositionsResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tdisclosureStyles,\n\t\tinterpAccordionStyles,\n\t\tcss`.wrap{background:var(--roxy-surface,#fff);width:100%;color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}header{align-items:baseline;gap:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-primary,#0f172a);margin:0}.badges{gap:var(--roxy-space-xs,.25rem);flex-wrap:wrap;display:flex}.badge{border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 14%, transparent);color:var(--roxy-fg,#0a0a0a);padding:2px 8px}.badge b{color:var(--roxy-accent-ink,#b45309);font-weight:600}.summary{color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-sm,.875rem);margin:0}.scroll{-webkit-overflow-scrolling:touch;min-width:0;overflow-x:auto}table{border-collapse:collapse;width:100%;font-size:var(--roxy-text-sm,.875rem)}caption{text-align:left;color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);padding-bottom:var(--roxy-space-xs,.25rem)}th,td{text-align:left;border-bottom:1px solid var(--roxy-border,#e4e4e7);white-space:nowrap;padding:6px 10px}th{color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.04em;font-weight:600;font-size:var(--roxy-text-xs,.75rem)}td.num{text-align:right;font-variant-numeric:tabular-nums}.body-cell{color:var(--roxy-fg,#0a0a0a);font-weight:500}.body-cell .glyph{color:var(--roxy-accent-ink,#b45309);margin-right:.35rem}tr.angle td{color:var(--roxy-secondary,#475569)}.sign{align-items:baseline;gap:.3rem;display:inline-flex}.sign .sg{color:var(--roxy-secondary,#475569)}.retro{color:var(--roxy-danger,#dc2626);font-weight:600}.formula{color:var(--roxy-muted,#71717a);font-variant-numeric:tabular-nums;white-space:normal}`,\n\t];\n\n\t/** Override the auto-derived heading. Empty keeps the per-shape default (e.g. \"Asteroids\"). */\n\t@property({ type: String })\n\theading = '';\n\n\tprotected renderData(data: PositionsResponse) {\n\t\tconst vm = this.toViewModel(data);\n\t\tconst cols = vm.cols;\n\t\treturn html`<div class=\"wrap\" part=\"card\"><header part=\"header\"><h2 class=\"title\">${this.heading || vm.title}</h2>${\n\t\t\t\t\tvm.badges.length\n\t\t\t\t\t\t? html`<div class=\"badges\" part=\"details\">${vm.badges.map((b) => html`<span class=\"badge\"><b>${b.label}</b> ${b.value}</span>`)}</div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</header>${\n\t\t\t\t// The response `summary` is a written overview of the positions, not a\n\t\t\t\t// count of them, so it goes with the readings.\n\t\t\t\tvm.summary && !this.hideReadings\n\t\t\t\t\t? html`<p class=\"summary\">${vm.summary}</p>`\n\t\t\t\t\t: nothing\n\t\t\t}<div class=\"scroll\" part=\"table\"><table><caption>${vm.title}</caption><thead><tr><th scope=\"col\">${this.t('Body')}</th><th scope=\"col\">${this.t('Position')}</th>${cols.natal ? html`<th scope=\"col\">${this.t('Natal')}</th>` : nothing} ${cols.house ? html`<th scope=\"col\" class=\"num\">${this.t('House')}</th>` : nothing} ${cols.motion ? html`<th scope=\"col\">${this.t('Motion')}</th>` : nothing} ${cols.formula ? html`<th scope=\"col\">${this.t('Formula')}</th>` : nothing}</tr></thead><tbody>${vm.rows.map((r) => this.renderRow(r, cols))}</tbody></table></div>${this.renderReadings(vm.rows)}</div>`;\n\t}\n\n\tprivate renderRow(r: Row, cols: ViewModel['cols']) {\n\t\tconst glyph = planetGlyph(r.label);\n\t\treturn html`<tr class=\"${r.isAngle ? 'angle' : ''}\"><td class=\"body-cell\">${glyph ? html`<span class=\"glyph\">${glyph}</span>` : nothing}${r.label}</td><td>${this.signCell(r.sign, r.degree)}</td>${\n\t\t\t\tcols.natal\n\t\t\t\t\t? html`<td>${r.natalLongitude != null ? this.signFromLongitude(r.natalLongitude) : html`—`}</td>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\tcols.house\n\t\t\t\t\t? html`<td class=\"num\">${r.house != null ? r.house : html`—`}</td>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\tcols.motion\n\t\t\t\t\t? html`<td>${\n\t\t\t\t\t\t\tr.speed != null\n\t\t\t\t\t\t\t\t? html`${formatNumber(this.effectiveLang(), r.speed, 3)}${this.t('°/day')}${r.isRetrograde ? html`<span class=\"retro\">℞</span>` : nothing}`\n\t\t\t\t\t\t\t\t: html`—`\n\t\t\t\t\t\t}</td>`\n\t\t\t\t\t: nothing\n\t\t\t} ${cols.formula ? html`<td class=\"formula\">${r.formula ?? html`—`}</td>` : nothing}</tr>`;\n\t}\n\n\tprivate signCell(sign: string, degree: number) {\n\t\tconst g = signGlyph(sign);\n\t\treturn html`<span class=\"sign\">${g ? html`<span class=\"sg\">${g}</span>` : nothing}${formatDegreeInSign(degree)} ${sign}</span>`;\n\t}\n\n\tprivate signFromLongitude(longitude: number) {\n\t\tconst p = longitudeToSignPosition(longitude);\n\t\treturn this.signCell(p.sign, p.degree + p.minute / 60);\n\t}\n\n\tprivate renderReadings(rows: Row[]) {\n\t\tconst sections: InterpSection[] = rows\n\t\t\t.filter((r) => r.interpretation)\n\t\t\t.map((r) => {\n\t\t\t\tconst glyph = planetGlyph(r.label) ?? '';\n\t\t\t\treturn {\n\t\t\t\t\tlabel: `${glyph} ${r.label}`.trim(),\n\t\t\t\t\taside: `${r.sign} ${formatDegreeInSign(r.degree)}`.trim(),\n\t\t\t\t\tbody: r.interpretation ?? '',\n\t\t\t\t};\n\t\t\t});\n\t\treturn this.renderInterpretation(\n\t\t\tsections,\n\t\t\t'positions-readings',\n\t\t\t'Readings',\n\t\t);\n\t}\n\n\tprivate toViewModel(data: PositionsResponse): ViewModel {\n\t\tif ('asteroids' in data) {\n\t\t\treturn {\n\t\t\t\ttitle: this.t('Asteroids'),\n\t\t\t\tbadges: data.houseSystem\n\t\t\t\t\t? [{ label: this.t('Houses'), value: data.houseSystem }]\n\t\t\t\t\t: [],\n\t\t\t\tsummary: data.summary,\n\t\t\t\tcols: { house: true, motion: true, formula: false, natal: false },\n\t\t\t\trows: data.asteroids.map((a) => ({\n\t\t\t\t\tlabel: a.name,\n\t\t\t\t\tsign: a.sign,\n\t\t\t\t\tdegree: a.degree,\n\t\t\t\t\thouse: a.house,\n\t\t\t\t\tspeed: a.speed,\n\t\t\t\t\tisRetrograde: a.isRetrograde,\n\t\t\t\t\tinterpretation: a.interpretation,\n\t\t\t\t})),\n\t\t\t};\n\t\t}\n\t\tif ('lilith' in data) {\n\t\t\treturn {\n\t\t\t\ttitle: this.t('Black Moon Lilith'),\n\t\t\t\tbadges: data.houseSystem\n\t\t\t\t\t? [{ label: this.t('Houses'), value: data.houseSystem }]\n\t\t\t\t\t: [],\n\t\t\t\tsummary: data.summary,\n\t\t\t\tcols: { house: true, motion: true, formula: false, natal: false },\n\t\t\t\trows: data.lilith.map((l) => ({\n\t\t\t\t\tlabel: this.t('{{variant}} apogee', {\n\t\t\t\t\t\tvariant: capitalize(l.variant),\n\t\t\t\t\t}),\n\t\t\t\t\tsign: l.sign,\n\t\t\t\t\tdegree: l.degree,\n\t\t\t\t\thouse: l.house,\n\t\t\t\t\tspeed: l.speed,\n\t\t\t\t\tisRetrograde: l.isRetrograde,\n\t\t\t\t\tinterpretation: l.interpretation,\n\t\t\t\t})),\n\t\t\t};\n\t\t}\n\t\tif ('directed' in data) {\n\t\t\treturn {\n\t\t\t\ttitle: this.t('Solar arc directions'),\n\t\t\t\tbadges: [\n\t\t\t\t\t{\n\t\t\t\t\t\tlabel: this.t('Arc'),\n\t\t\t\t\t\tvalue: `${formatNumber(this.effectiveLang(), data.solarArc, 2)}°`,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tlabel: this.t('Directed to'),\n\t\t\t\t\t\tvalue: formatDate(this.effectiveLang(), data.targetDate),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tsummary: data.summary,\n\t\t\t\tcols: { house: false, motion: false, formula: false, natal: true },\n\t\t\t\trows: data.directed.map((d) => ({\n\t\t\t\t\tlabel: d.name,\n\t\t\t\t\tsign: d.sign,\n\t\t\t\t\tdegree: d.degree,\n\t\t\t\t\tnatalLongitude: d.natalLongitude,\n\t\t\t\t\tinterpretation: d.interpretation,\n\t\t\t\t})),\n\t\t\t};\n\t\t}\n\t\tif ('lots' in data) {\n\t\t\treturn {\n\t\t\t\ttitle: this.t('Arabic lots'),\n\t\t\t\tbadges: data.sect\n\t\t\t\t\t? [{ label: this.t('Sect'), value: capitalize(data.sect) }]\n\t\t\t\t\t: [],\n\t\t\t\tsummary: data.summary,\n\t\t\t\tcols: { house: false, motion: false, formula: true, natal: false },\n\t\t\t\trows: data.lots.map((l) => ({\n\t\t\t\t\tlabel: l.name,\n\t\t\t\t\tsign: l.sign,\n\t\t\t\t\tdegree: l.degree,\n\t\t\t\t\tformula: l.formula,\n\t\t\t\t\tinterpretation: l.interpretation,\n\t\t\t\t})),\n\t\t\t};\n\t\t}\n\t\t// Secondary progressions: planets plus the progressed angles.\n\t\tconst angleRows: Row[] = [];\n\t\tif (data.ascendant) {\n\t\t\tangleRows.push({\n\t\t\t\tlabel: this.t('Ascendant'),\n\t\t\t\tsign: data.ascendant.sign,\n\t\t\t\tdegree: data.ascendant.degree,\n\t\t\t\tisAngle: true,\n\t\t\t});\n\t\t}\n\t\tif (data.midheaven) {\n\t\t\tangleRows.push({\n\t\t\t\tlabel: this.t('Midheaven'),\n\t\t\t\tsign: data.midheaven.sign,\n\t\t\t\tdegree: data.midheaven.degree,\n\t\t\t\tisAngle: true,\n\t\t\t});\n\t\t}\n\t\treturn {\n\t\t\ttitle: this.t('Secondary progressions'),\n\t\t\tbadges: [\n\t\t\t\t{\n\t\t\t\t\tlabel: this.t('Progressed to'),\n\t\t\t\t\tvalue: formatDate(this.effectiveLang(), data.targetDate),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: this.t('Elapsed'),\n\t\t\t\t\tvalue: this.t('{{years}} yrs', {\n\t\t\t\t\t\tyears: formatNumber(this.effectiveLang(), data.elapsedYears, 1),\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t],\n\t\t\tsummary: data.summary,\n\t\t\tcols: { house: true, motion: true, formula: false, natal: false },\n\t\t\trows: [\n\t\t\t\t...angleRows,\n\t\t\t\t...data.planets.map((p) => ({\n\t\t\t\t\tlabel: p.name,\n\t\t\t\t\tsign: p.sign,\n\t\t\t\t\tdegree: p.degree,\n\t\t\t\t\thouse: p.house,\n\t\t\t\t\tspeed: p.speed,\n\t\t\t\t\tisRetrograde: p.isRetrograde,\n\t\t\t\t\tinterpretation: p.interpretation,\n\t\t\t\t})),\n\t\t\t],\n\t\t};\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-positions-table': RoxyPositionsTable;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport { planetGlyph, signGlyph } from '../tokens/index.js';\nimport type { ProfectionsResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { formatDate } from '../utils/format.js';\n\n/**\n * Annual profection card. Renders a /astrology/profections response: the year's\n * profected whole-sign house and sign, the lord of the year and where it sits in\n * the natal chart, and the reading. A single focal card, not a table.\n *\n * @remarks\n * The profection itself is arithmetic: the age, the house it activates, the sign on it, and the lord with its natal placement. Those are the card, and they all survive `hide-readings`; the closing paragraph is the only interpretation on it.\n */\n@customElement('roxy-profection-card')\nexport class RoxyProfectionCard extends RoxyDataElement<ProfectionsResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.wrap{background:var(--roxy-surface,#fff);width:100%;color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}header{justify-content:space-between;align-items:baseline;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-primary,#0f172a);margin:0}.badge{border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 14%, transparent);color:var(--roxy-fg,#0a0a0a);padding:2px 8px}.badge b{color:var(--roxy-accent-ink,#b45309);font-weight:600}.focus{align-items:center;gap:var(--roxy-space-md,1rem);padding:var(--roxy-space-md,1rem);border-radius:var(--roxy-radius-md,8px);background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 9%, transparent);flex-wrap:wrap;display:flex}.age{justify-items:center;min-width:4.5rem;display:grid}.age .n{color:var(--roxy-accent-ink,#b45309);font-variant-numeric:tabular-nums;font-size:2rem;font-weight:700;line-height:1}.age .l{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.06em}.arrow{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem)}.house{gap:2px;display:grid}.house .h{font-size:var(--roxy-text-lg,1.125rem);color:var(--roxy-fg,#0a0a0a);font-weight:600}.house .sign{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-secondary,#475569)}.house .sign .sg{margin-right:.3rem}.lord{font-size:var(--roxy-text-sm,.875rem);flex-wrap:wrap;align-items:baseline;gap:.4rem .75rem;display:flex}.lord .label{color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.06em;font-size:var(--roxy-text-xs,.75rem)}.lord .value{color:var(--roxy-fg,#0a0a0a);font-weight:600}.lord .sub{color:var(--roxy-muted,#71717a)}.interp{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a);line-height:var(--roxy-leading-normal,1.5);margin:0}`,\n\t];\n\n\tprotected renderData(data: ProfectionsResponse) {\n\t\tconst sGlyph = signGlyph(data.profectedSign);\n\t\tconst lordGlyph = planetGlyph(data.lordOfYear);\n\t\tconst lordNatalGlyph = signGlyph(data.lordNatalPosition?.sign);\n\t\treturn html`<div class=\"wrap\" part=\"card\"><header part=\"header\"><h2 class=\"title\">${this.t('Annual profection')}</h2>${data.targetDate ? html`<span class=\"badge\"><b>${this.t('For')}</b> ${formatDate(this.effectiveLang(), data.targetDate)}</span>` : nothing}</header><div class=\"focus\" part=\"details\"><div class=\"age\"><span class=\"n\">${data.age}</span> <span class=\"l\">${this.t('Age')}</span></div><span class=\"arrow\">${this.t('Activates')}</span><div class=\"house\"><span class=\"h\">${this.t('House')} ${data.profectedHouse}</span> <span class=\"sign\">${sGlyph ? html`<span class=\"sg\">${sGlyph}</span>` : nothing}${data.profectedSign}</span></div></div><div class=\"lord\" part=\"details\"><span class=\"label\">${this.t('Lord of the year')}</span> <span class=\"value\">${lordGlyph ? html`${lordGlyph}` : nothing}${data.lordOfYear}</span> ${\n\t\t\t\t\tdata.lordNatalPosition\n\t\t\t\t\t\t? html`<span class=\"sub\">${this.t('Natal')} ${lordNatalGlyph ? html`<span>${lordNatalGlyph}</span>` : nothing}${this.t('{{sign}} · house {{house}}', { sign: data.lordNatalPosition.sign, house: data.lordNatalPosition.house })}</span>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</div>${\n\t\t\t\t// The house, the sign and the lord above are the profection; this is the\n\t\t\t\t// read of them.\n\t\t\t\tdata.interpretation && !this.hideReadings\n\t\t\t\t\t? html`<p class=\"interp\">${data.interpretation}</p>`\n\t\t\t\t\t: nothing\n\t\t\t}</div>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-profection-card': RoxyProfectionCard;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport type {\n\tGetAvasthaResponse,\n\tGetCenterResponse,\n\tGetCompoundNumberResponse,\n\tGetGateResponse,\n\tGetNumberMeaningResponse,\n\tGetPlanetMeaningResponse,\n\tGetRashiResponse,\n\tGetTrigramResponse,\n\tGetZodiacSignResponse,\n} from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { displayField, foldLocalized } from '../utils/localized.js';\nimport { humanize } from '../utils/string.js';\n\n/**\n * Every reference / glossary lookup this card renders. They do NOT share field names (a zodiac sign has 17 fields, a number meaning has 3), so the card renders heuristically rather than by fixed keys: a symbol + title hero, the prose fields as labelled paragraphs, keyword and string-list fields as chips, and every remaining scalar as an attribute grid. One card replaces eight near-identical bespoke ones.\n *\n * It is the second GENERIC renderer in the library, and it shares `<roxy-data>`'s two consequences. Its field labels come from the wire name through `humanize()`, so they are derived rather than literal and no catalogue keyed on English source text can reach them: `Reference` and the empty state are the only two words the card writes itself. And because it enumerates `Object.keys`, an additive API field is not automatically backwards compatible for it, which is why every level that reads a key runs through `foldLocalized` first.\n *\n * `hide-readings` needs no second classification here, because the bucketing IS the line: what `collect` calls prose is the reading and what it calls a fact is the data. So the labelled paragraphs go, the keyword chips go, and a list whose items run to sentence length (a sign's `strengths`) goes with them, while the attribute grid and the short-item lists (`famous`, `compatibleSigns`, chakras) stay. On a lookup that is almost entirely prose the card keeps its symbol, title and whatever facts the response carries, which is the same flat shape it already renders for a response that had no prose to begin with.\n */\ntype ReferenceData =\n\t| GetZodiacSignResponse\n\t| GetPlanetMeaningResponse\n\t| GetRashiResponse\n\t| GetTrigramResponse\n\t| GetGateResponse\n\t| GetCenterResponse\n\t| GetNumberMeaningResponse\n\t| GetCompoundNumberResponse\n\t| GetAvasthaResponse;\n\n/** Keys used to derive the title, in priority order. */\nconst TITLE_KEYS = ['name', 'english', 'title'];\n/** Keys whose string value is always treated as prose, even when short. */\nconst PROSE_KEYS = new Set([\n\t'description',\n\t'meaning',\n\t'characteristics',\n\t'tagline',\n\t'definedMeaning',\n\t'undefinedMeaning',\n\t'motto',\n]);\n/** Keys never shown as their own row/section (rendered elsewhere or noise). */\nconst SKIP_KEYS = new Set(['id', 'symbol', ...TITLE_KEYS]);\n\n/**\n * Longest a string may be before it is read as prose rather than as a value. The same threshold decides a scalar field and a list item, so `motto: \"I am.\"` stays a fact while `strengths: [\"You are the most courageous and ambitious sign, ...\"]` is recognised as sentences laid out as chips.\n */\nconst PROSE_LENGTH = 48;\n\n/**\n * A response key as a `part` name. Derived through {@link humanize} so one rule covers camelCase, snake_case and the space-joined keys `collect` builds for a nested field (`description short`), and the result is always the kebab-case a `part` has to be.\n */\nfunction partName(key: string): string {\n\treturn humanize(key).toLowerCase().replace(/\\s+/g, '-');\n}\n\n/** Best-effort one-line label for an object inside an array (e.g. a gate channel partner), joining its primitive values so it never renders as [object Object]. Folded first, or a translated response joins each value to its own translation. */\nfunction objectLabel(obj: Record<string, unknown>): string {\n\treturn Object.values(foldLocalized(obj))\n\t\t.filter((v) => typeof v === 'string' || typeof v === 'number')\n\t\t.map(String)\n\t\t.join(' · ');\n}\n\n@customElement('roxy-reference-card')\nexport class RoxyReferenceCard extends RoxyDataElement<ReferenceData> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.card{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.head{align-items:center;gap:var(--roxy-space-md,1rem);display:flex}.symbol{color:var(--roxy-accent-ink,#b45309);flex:none;font-size:2.5rem;line-height:1}.label{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.06em;margin:0}.name{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.prose{margin:0;line-height:1.6}.prose .prose-label{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.05em;margin-bottom:2px;display:block}.chips{gap:var(--roxy-space-xs,.25rem);flex-wrap:wrap;display:flex}.chips span{background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 14%, transparent);border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);padding:2px 8px}.list h3,.facts-wrap h3{margin:0 0 var(--roxy-space-xs,.25rem);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.06em}.facts{gap:var(--roxy-space-sm,.5rem);grid-template-columns:repeat(auto-fit,minmax(9rem,1fr));margin:0;display:grid}.fact dt{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);text-transform:capitalize}.fact dd{font-size:var(--roxy-text-sm,.875rem);margin:0}`,\n\t];\n\n\tprotected renderData(d: ReferenceData) {\n\t\t// Folded before anything reads a key. This card builds its whole output from\n\t\t// `Object.entries`, so the day the API began echoing `nameLocalized` beside\n\t\t// `name` it started drawing the same fact twice, once under each heading, on\n\t\t// a page whose owner changed nothing. The fold puts the reader's value under\n\t\t// the canonical key and drops the twin (lesson 31, the `<roxy-data>` half).\n\t\tconst rec = foldLocalized(d as Record<string, unknown>);\n\t\tconst title = this.deriveTitle(rec);\n\t\tconst symbol = typeof rec.symbol === 'string' ? rec.symbol : undefined;\n\t\tconst keywords = Array.isArray(rec.keywords)\n\t\t\t? (rec.keywords as unknown[]).map(String)\n\t\t\t: [];\n\n\t\tconst prose: Array<[string, string]> = [];\n\t\t/** Label, items, and whether the items read as sentences rather than values. */\n\t\tconst lists: Array<[string, string[], boolean]> = [];\n\t\tconst facts: Array<[string, string]> = [];\n\t\t// Bucket each field into prose / chip-list / fact. Recurses one level into\n\t\t// nested objects (description {short,long}, keywords {positive,negative},\n\t\t// meaning {...}, ichingHexagram {number,english}) and labels object-array\n\t\t// items by their primitives, so nested content is never dropped and an array\n\t\t// of objects never stringifies to [object Object].\n\t\tconst collect = (label: string, value: unknown, depth: number): void => {\n\t\t\tif (value == null) return;\n\t\t\tif (Array.isArray(value)) {\n\t\t\t\tconst items = value\n\t\t\t\t\t.filter((v) => v != null)\n\t\t\t\t\t.map((v) =>\n\t\t\t\t\t\ttypeof v === 'object'\n\t\t\t\t\t\t\t? objectLabel(v as Record<string, unknown>)\n\t\t\t\t\t\t\t: String(v),\n\t\t\t\t\t)\n\t\t\t\t\t.filter((s) => s.length > 0);\n\t\t\t\tif (items.length > 0)\n\t\t\t\t\tlists.push([\n\t\t\t\t\t\tlabel,\n\t\t\t\t\t\titems,\n\t\t\t\t\t\titems.some((i) => i.length > PROSE_LENGTH),\n\t\t\t\t\t]);\n\t\t\t} else if (typeof value === 'string') {\n\t\t\t\tif (PROSE_KEYS.has(label) || value.length > PROSE_LENGTH)\n\t\t\t\t\tprose.push([label, value]);\n\t\t\t\telse facts.push([label, value]);\n\t\t\t} else if (typeof value === 'number' || typeof value === 'boolean') {\n\t\t\t\tfacts.push([label, String(value)]);\n\t\t\t} else if (typeof value === 'object' && depth < 2) {\n\t\t\t\tfor (const [k, v] of Object.entries(\n\t\t\t\t\tfoldLocalized(value as Record<string, unknown>),\n\t\t\t\t)) {\n\t\t\t\t\tcollect(`${label} ${k}`, v, depth + 1);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\tfor (const [key, value] of Object.entries(rec)) {\n\t\t\tif (SKIP_KEYS.has(key)) continue;\n\t\t\t// The hero chip row already shows a top-level keywords ARRAY; an object\n\t\t\t// keywords ({positive,negative}) falls through to collect() instead.\n\t\t\tif (key === 'keywords' && Array.isArray(value)) continue;\n\t\t\tcollect(key, value, 0);\n\t\t}\n\n\t\tconst readings = !this.hideReadings;\n\n\t\treturn html`<article class=\"card\" part=\"card\" aria-label=\"${title}\"><header class=\"head\" part=\"header\">${symbol ? html`<span class=\"symbol\" aria-hidden=\"true\">${symbol}</span>` : nothing}<div><p class=\"label\">${this.t('Reference')}</p><h2 class=\"name\">${title}</h2></div></header>${keywords.length > 0 && readings ? html`<div class=\"chips\">${keywords.map((k) => html`<span>${k}</span>`)}</div>` : nothing} ${\n\t\t\t\treadings\n\t\t\t\t\t? prose.map(\n\t\t\t\t\t\t\t([key, text]) => html`<p class=\"prose\"><span class=\"prose-label\">${displayField(this.effectiveLang(), key)}</span>${text}</p>`,\n\t\t\t\t\t\t)\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\tfacts.length > 0\n\t\t\t\t\t? html`<div class=\"facts-wrap\" part=\"details\"><dl class=\"facts\">${facts.map(\n\t\t\t\t\t\t\t\t([key, value]) => html`<div class=\"fact\"><dt>${displayField(this.effectiveLang(), key)}</dt><dd>${value}</dd></div>`,\n\t\t\t\t\t\t\t)}</dl></div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${lists.map(([key, items, isProse]) =>\n\t\t\t\tisProse && !readings\n\t\t\t\t\t? nothing\n\t\t\t\t\t: html`<div class=\"list\" part=\"section ${partName(key)}\"><h3>${displayField(this.effectiveLang(), key)}</h3><div class=\"chips\">${items.map((i) => html`<span>${i}</span>`)}</div></div>`,\n\t\t\t)}</article>`;\n\t}\n\n\tprivate deriveTitle(rec: Record<string, unknown>): string {\n\t\tfor (const key of TITLE_KEYS) {\n\t\t\tif (typeof rec[key] === 'string') return rec[key] as string;\n\t\t}\n\t\tconst type = typeof rec.type === 'string' ? humanize(rec.type) : '';\n\t\tconst number = rec.number;\n\t\tif (number != null) return `${type} ${number}`.trim();\n\t\treturn this.t('Reference');\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-reference-card': RoxyReferenceCard;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport { ifDefined } from 'lit/directives/if-defined.js';\n// Registers <roxy-natal-chart>, reused below as the wheel renderer. The\n// relocation response is structurally a natal chart (same planets, houses, and\n// angles), so the wheel is shared rather than duplicated.\nimport './natal-chart.js';\nimport { planetGlyph } from '../tokens/index.js';\nimport type { RelocationChartResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { formatInteger } from '../utils/format.js';\n\ntype HouseChange =\n\tRelocationChartResponse['changes']['planetsChangedHouse'][number];\n\nconst glyphFor = (name: string): string => planetGlyph(name) ?? '';\n\n/**\n * Relocation chart. The birth instant is unchanged, so every planet keeps its\n * natal sign and degree while the houses and angles are recomputed for a new\n * place. Reuses {@link RoxyNatalChart} for the wheel (identical data shape) and\n * adds the relocation read: the move geometry, the planets that change house,\n * and the bodies pulled onto a relocated angle.\n *\n * @remarks\n * Composing another component means two things have to be forwarded or they stop\n * at the boundary: `hide-readings` is passed down, so suppressing prose here also\n * suppresses the wheel's planet readings; and the wheel's parts are re-exported,\n * because a part is only addressable one shadow root deep.\n */\n@customElement('roxy-relocation-wheel')\nexport class RoxyRelocationWheel extends RoxyDataElement<RelocationChartResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.stack{gap:var(--roxy-space-md,1rem);display:grid}.changes{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);display:grid}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-primary,#0f172a);margin:0}.move{gap:var(--roxy-space-xs,.25rem) var(--roxy-space-md,1rem);font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-muted,#71717a);flex-wrap:wrap;display:flex}.summary{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a);margin:0}.block-label{font-size:var(--roxy-text-xs,.75rem);text-transform:uppercase;letter-spacing:.06em;color:var(--roxy-muted,#71717a);margin:0 0 var(--roxy-space-xs,.25rem);font-weight:600}.chips{gap:var(--roxy-space-xs,.25rem);flex-wrap:wrap;display:flex}.chip{border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 16%, transparent);color:var(--roxy-fg,#0a0a0a);padding:2px 10px}.moves-list{gap:2px;margin:0;padding:0;list-style:none;display:grid}.moves-list li{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a)}.moves-list .arrow{color:var(--roxy-muted,#71717a)}.empty-note{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-muted,#71717a);margin:0}`,\n\t];\n\n\tprotected renderData(data: RelocationChartResponse) {\n\t\tconst c = data.changes;\n\t\treturn html`<div class=\"stack\" part=\"stack\"><roxy-natal-chart heading=\"Relocation chart\" lang=\"${ifDefined(this.effectiveLang())}\" exportparts=\"card, header, tablist, tab, panel, chart, table, aspects, aspect-grid, element-modality, legend, details, section, patterns, pattern, readings, reading\" ?hide-readings=\"${this.hideReadings}\" .data=\"${data}\"></roxy-natal-chart><section class=\"changes\" part=\"card changes\"><h3 class=\"title\">${this.t('What changes at this location')}</h3><div class=\"move\" part=\"details\">${\n\t\t\t\t\t\ttypeof c?.distanceKm === 'number'\n\t\t\t\t\t\t\t? html`<span>${\n\t\t\t\t\t\t\t\t\t// Under a kilometre the bearing is noise: \"0 km south of\n\t\t\t\t\t\t\t\t\t// birthplace\" reads as a broken calculation, not a same-place\n\t\t\t\t\t\t\t\t\t// relocation.\n\t\t\t\t\t\t\t\t\tMath.round(c.distanceKm) === 0\n\t\t\t\t\t\t\t\t\t\t? 'Same location as birth'\n\t\t\t\t\t\t\t\t\t\t: `${formatInteger(this.effectiveLang(), c.distanceKm)} km ${c.direction ?? ''} of birthplace`\n\t\t\t\t\t\t\t\t}</span>`\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t} ${\n\t\t\t\t\t\tc\n\t\t\t\t\t\t\t? html`<span>${\n\t\t\t\t\t\t\t\t\t// Name the sign in BOTH branches. Reporting only the boolean\n\t\t\t\t\t\t\t\t\t// put \"Ascendant keeps its sign\" directly above a summary\n\t\t\t\t\t\t\t\t\t// reading \"The Ascendant moves to Gemini\", and a reader\n\t\t\t\t\t\t\t\t\t// cannot tell which one is wrong. Neither was: the sign is\n\t\t\t\t\t\t\t\t\t// Gemini either way. Stating it reconciles the two.\n\t\t\t\t\t\t\t\t\tdata.ascendant?.sign\n\t\t\t\t\t\t\t\t\t\t? c.ascendantSignChanged\n\t\t\t\t\t\t\t\t\t\t\t? this.t('Ascendant moves to {{sign}}', {\n\t\t\t\t\t\t\t\t\t\t\t\t\tsign: data.ascendant.sign,\n\t\t\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\t\t: this.t('Ascendant stays in {{sign}}', {\n\t\t\t\t\t\t\t\t\t\t\t\t\tsign: data.ascendant.sign,\n\t\t\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\t: c.ascendantSignChanged\n\t\t\t\t\t\t\t\t\t\t\t? this.t('Ascendant changes sign')\n\t\t\t\t\t\t\t\t\t\t\t: this.t('Ascendant keeps its sign')\n\t\t\t\t\t\t\t\t}</span>`\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t}</div>${\n\t\t\t\t\t// The only prose here. The move geometry, the angular planets and the\n\t\t\t\t\t// house changes below it are all data and survive hide-readings.\n\t\t\t\t\tdata.interpretation?.summary && !this.hideReadings\n\t\t\t\t\t\t? html`<p class=\"summary\">${data.interpretation.summary}</p>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t} ${\n\t\t\t\t\tc?.angularPlanets?.length\n\t\t\t\t\t\t? html`<div part=\"section angular-planets\"><p class=\"block-label\">${this.t('Angular planets here')}</p><div class=\"chips\">${c.angularPlanets.map((p) => html`<span class=\"chip\">${glyphFor(p)} ${p}</span>`)}</div></div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}<div part=\"section house-changes\"><p class=\"block-label\">${this.t('Planets that change house')}</p>${\n\t\t\t\t\t\tc?.planetsChangedHouse?.length\n\t\t\t\t\t\t\t? html`<ul class=\"moves-list\">${c.planetsChangedHouse.map(\n\t\t\t\t\t\t\t\t\t(m: HouseChange) =>\n\t\t\t\t\t\t\t\t\t\thtml`<li>${glyphFor(m.planet)} ${this.t('{{planet}}: house {{from}} to {{to}}', { planet: m.planet, from: m.natalHouse, to: m.relocatedHouse })}</li>`,\n\t\t\t\t\t\t\t\t)}</ul>`\n\t\t\t\t\t\t\t: html`<p class=\"empty-note\">${this.t('No planet changes house at this location.')}</p>`\n\t\t\t\t\t}</div></section></div>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-relocation-wheel': RoxyRelocationWheel;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport type { ChromeString } from '../i18n/chrome-strings.js';\nimport { planetGlyph } from '../tokens/index.js';\nimport type { ShadbalaResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { formatNumber } from '../utils/format.js';\nimport { frameCaptionStyles, renderFrameCaption } from '../utils/frame.js';\n\ntype Planet = ShadbalaResponse['planets'][number];\n\n/** CSS variable and display name for each of the 6 bala components. */\nconst BALA_COMPONENTS: Array<{\n\tkey: keyof Pick<\n\t\tPlanet,\n\t\t| 'sthanaBala'\n\t\t| 'digBala'\n\t\t| 'kalaBala'\n\t\t| 'chestaBala'\n\t\t| 'naisargikaBala'\n\t\t| 'drikBala'\n\t>;\n\tsource: ChromeString;\n\tcolor: string;\n}> = [\n\t{ key: 'sthanaBala', source: 'Sthana', color: 'var(--roxy-info, #0284c7)' },\n\t{ key: 'digBala', source: 'Dig', color: 'var(--roxy-success, #16a34a)' },\n\t{ key: 'kalaBala', source: 'Kala', color: 'var(--roxy-warning, #ea580c)' },\n\t{ key: 'chestaBala', source: 'Chesta', color: 'var(--roxy-accent, #f59e0b)' },\n\t{\n\t\tkey: 'naisargikaBala',\n\t\tsource: 'Naisargika',\n\t\tcolor: 'var(--roxy-secondary, #475569)',\n\t},\n\t{ key: 'drikBala', source: 'Drik', color: 'var(--roxy-danger, #dc2626)' },\n];\n\n/**\n * Shadbala six-fold planetary strength table with stacked bar visualization.\n * Pass `data` from /vedic-astrology/shadbala.\n */\n@customElement('roxy-shadbala-table')\nexport class RoxyShadbalaTable extends RoxyDataElement<ShadbalaResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tframeCaptionStyles,\n\t\tcss`.wrap{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.head{justify-content:space-between;align-items:baseline;gap:var(--roxy-space-md,1rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.subtitle{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem);margin:0}.planet-row{align-items:center;gap:var(--roxy-space-sm,.5rem);padding:var(--roxy-space-sm,.5rem) 0;border-bottom:1px solid var(--roxy-border,#e4e4e7);grid-template-columns:8rem 1fr auto;display:grid}.planet-row:last-of-type{border-bottom:none}.planet-label{font-size:var(--roxy-text-sm,.875rem);font-weight:var(--roxy-weight-bold,600);align-items:center;gap:6px;display:flex}.glyph{font-size:1.2em;line-height:1}.bar-wrap{flex-direction:column;gap:4px;display:flex}.bar{border-radius:var(--roxy-radius-sm,4px);background:var(--roxy-border,#e4e4e7);height:12px;display:flex;overflow:hidden}.bar-segment{height:100%;transition:flex-grow var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,cubic-bezier(.4, 0, .2, 1))}.pills{flex-direction:column;align-items:flex-end;gap:4px;display:flex}.rupas-label{font-variant-numeric:tabular-nums;font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);white-space:nowrap}.phala{border-radius:var(--roxy-radius-full,9999px);background:var(--roxy-border,#e4e4e7);height:6px;display:flex;overflow:hidden}.phala-ishta{background:var(--roxy-success,#16a34a)}.phala-kashta{background:var(--roxy-danger,#dc2626)}.phala-label{justify-content:space-between;gap:var(--roxy-space-sm,.5rem);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);font-variant-numeric:tabular-nums;display:flex}.footnote{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);margin:0}.adequacy-badge{border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);padding:1px 6px;display:inline-block}.adequacy-badge--adequate{background:color-mix(in srgb, var(--roxy-success,#16a34a) 12%, transparent);color:var(--roxy-success-fg,#166534)}.adequacy-badge--weak{background:color-mix(in srgb, var(--roxy-danger,#dc2626) 12%, transparent);color:var(--roxy-danger-fg,#991b1b)}.rank-badge{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-accent-ink,#b45309);font-weight:var(--roxy-weight-bold,600)}.legend{gap:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);border-top:1px solid var(--roxy-border,#e4e4e7);padding-top:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.legend-row{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);align-items:center;gap:6px;display:flex}.legend-swatch{border-radius:var(--roxy-radius-sm,4px);flex-shrink:0;width:10px;height:10px;display:inline-block}@container (width<=480px){.planet-row{grid-template-rows:auto auto;grid-template-columns:6rem 1fr}.pills{flex-direction:row;grid-column:1/-1;justify-content:flex-start;align-items:center}}`,\n\t];\n\n\tprotected renderData(d: ShadbalaResponse) {\n\t\tif (!d.planets?.length) return this.renderEmpty();\n\n\t\tconst sorted = [...d.planets].sort(\n\t\t\t(a, b) => a.relativeRank - b.relativeRank,\n\t\t);\n\n\t\treturn html`<div class=\"wrap\" part=\"card\" aria-label=\"${this.t('Shadbala planetary strength')}\"><div class=\"head\" part=\"header\"><h2 class=\"title\">${this.t('Shadbala')}</h2><p class=\"subtitle\">${this.t('{{count}} planets ranked by strength', { count: sorted.length })}</p>${renderFrameCaption(this.effectiveLang(), d.frame, this.translator)}</div><div role=\"list\" part=\"chart bars\" aria-label=\"${this.t('Planet strength bars')}\">${sorted.map((p) => this.renderPlanetRow(p))}</div><div class=\"legend\" part=\"legend\" aria-label=\"${this.t('Strength component legend')}\">${BALA_COMPONENTS.map(\n\t\t\t\t\t(b) => html`<div class=\"legend-row\"><span class=\"legend-swatch\" style=\"background:${b.color}\" aria-hidden=\"true\"></span> ${this.t('{{component}} Bala', { component: this.t(b.source) })}</div>`,\n\t\t\t\t)}</div><p class=\"footnote\" part=\"legend\">${this.t(\n\t\t\t\t\t'Ishta Phala is the capacity to give benefic results, Kashta Phala the capacity to give malefic ones. Both are in virupas and are read together, since a planet can be strong and still deliver hardship.',\n\t\t\t\t)}</p></div>`;\n\t}\n\n\tprivate renderPlanetRow(p: Planet) {\n\t\tconst glyph = planetGlyph(p.planet) ?? '';\n\n\t\t// Compute positive component values (drikBala can be negative)\n\t\tconst values = BALA_COMPONENTS.map((b) => Math.max(0, p[b.key] as number));\n\t\tconst total = values.reduce((s, v) => s + v, 0);\n\n\t\tconst isAdequate =\n\t\t\ttypeof p.strengthRatio === 'number' && p.strengthRatio >= 1;\n\t\tconst badgeClass = isAdequate\n\t\t\t? 'adequacy-badge--adequate'\n\t\t\t: 'adequacy-badge--weak';\n\t\tconst badgeLabel = isAdequate ? 'adequate' : 'weak';\n\n\t\tconst rupasStr =\n\t\t\tformatNumber(this.effectiveLang(), p.totalRupas, 2) &&\n\t\t\tformatNumber(this.effectiveLang(), p.minRequired, 2)\n\t\t\t\t? `${formatNumber(this.effectiveLang(), p.totalRupas, 2)} / ${formatNumber(this.effectiveLang(), p.minRequired, 2)} R`\n\t\t\t\t: '';\n\n\t\treturn html`<div class=\"planet-row\" role=\"listitem\" aria-label=\"${this.t('{{planet}} Shadbala', { planet: p.planet })}\"><div class=\"planet-label\"><span class=\"glyph\" aria-hidden=\"true\">${glyph}</span> ${p.planet} <span class=\"rank-badge\" aria-label=\"${this.t('rank {{n}}', { n: p.relativeRank })}\">#${p.relativeRank}</span></div><div class=\"bar-wrap\"><div class=\"bar\" role=\"img\" aria-label=\"${this.t('Strength components for {{planet}}', { planet: p.planet })}\">${\n\t\t\t\t\t\ttotal > 0\n\t\t\t\t\t\t\t? BALA_COMPONENTS.map((b, i) => {\n\t\t\t\t\t\t\t\t\tconst v = values[i];\n\t\t\t\t\t\t\t\t\tif (v <= 0) return nothing;\n\t\t\t\t\t\t\t\t\tconst grow = (v / total) * 100;\n\t\t\t\t\t\t\t\t\treturn html`<div class=\"bar-segment\" style=\"flex-grow:${grow};background:${b.color}\" title=\"${this.t('{{component}} {{value}} virupas', { component: this.t(b.source), value: formatNumber(this.effectiveLang(), v, 1) })}\"></div>`;\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t}</div>${this.renderPhala(p)}</div><div class=\"pills\">${rupasStr ? html`<span class=\"rupas-label\">${rupasStr}</span>` : nothing} <span class=\"${`adequacy-badge ${badgeClass}`}\">${badgeLabel}</span></div></div>`;\n\t}\n\n\t/**\n\t * Ishta and Kashta Phala, the fruit a planet is capable of giving in its dasha\n\t * and transits. They are derived from Uchcha and Chesta Bala, not from the\n\t * six-fold total, so a planet can rank first on strength and still carry a\n\t * heavy Kashta share. Rendering only the total hides exactly that.\n\t */\n\tprivate renderPhala(p: Planet) {\n\t\tconst ishta = typeof p.ishtaPhala === 'number' ? p.ishtaPhala : 0;\n\t\tconst kashta = typeof p.kashtaPhala === 'number' ? p.kashtaPhala : 0;\n\t\tif (ishta + kashta <= 0) return nothing;\n\t\tconst ishtaStr = formatNumber(this.effectiveLang(), ishta, 1) || '0';\n\t\tconst kashtaStr = formatNumber(this.effectiveLang(), kashta, 1) || '0';\n\t\treturn html`<div class=\"phala\" role=\"img\" aria-label=\"${this.t('Ishta Phala {{ishta}}, Kashta Phala {{kashta}} virupas', { ishta: ishtaStr, kashta: kashtaStr })}\"><span class=\"phala-ishta\" style=\"flex-grow:${ishta}\"></span> <span class=\"phala-kashta\" style=\"flex-grow:${kashta}\"></span></div><div class=\"phala-label\"><span>${this.t('Ishta {{value}}', { value: ishtaStr })}</span> <span>${this.t('Kashta {{value}}', { value: kashtaStr })}</span></div>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-shadbala-table': RoxyShadbalaTable;\n\t}\n}\n", "import { css, html, nothing, svg } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport type { ChromeString } from '../i18n/chrome-strings.js';\nimport { planetGlyph, SIGNS_ORDER, signGlyph } from '../tokens/index.js';\nimport type { CalculateSynastryResponse } from '../types/index.js';\nimport { aspectLineStyle, aspectLineStyles } from '../utils/aspect-line.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { longitudeToSignPosition, polarToCartesian } from '../utils/degree.js';\nimport { chevron, disclosureStyles } from '../utils/disclosure.js';\nimport {\n\tASPECT_CLASS,\n\tformatAspectName,\n\tformatNumber,\n\tnormalizeAspect,\n} from '../utils/format.js';\nimport {\n\tinterpAccordionStyles,\n\trenderKeywordChips,\n} from '../utils/interp-accordion.js';\nimport { display } from '../utils/localized.js';\nimport { capitalize } from '../utils/string.js';\n\n/**\n * A planet as the synastry response now returns it.\n *\n * @remarks\n * Was `NatalChartResponse['planets'][number]`, because `/astrology/synastry` did not return positions and a caller had to merge two natal responses in by hand. The endpoint returns them, so the wheel is drawable from ONE call. The synastry shape is the plotting subset (no `interpretation`, `speed` or `latitude`); the wheel only ever read `name`, `longitude` and `isRetrograde`, so nothing is lost.\n */\ntype PlanetEntry = CalculateSynastryResponse['person1']['planets'][number];\ntype InterAspect = CalculateSynastryResponse['interAspects'][number];\ntype SynastrySummary = CalculateSynastryResponse['summary'];\ntype SynastryPerson = CalculateSynastryResponse['person1'];\n\n/** How many inter-aspects get a full reading before the rest fall back to the catalog table. A synastry can return 90+ contacts; a practitioner works the tightest ones. */\nconst READING_COUNT = 12;\n\nconst SIZE = 360;\nconst CENTER = SIZE / 2;\nconst OUTER_R = 170;\nconst SIGN_R = 154;\nconst P1_R = 124;\nconst P2_R = 96;\n\n/**\n * Dual-wheel synastry chart with inter-aspects table. Pass `data` from\n * /astrology/synastry.\n */\n@customElement('roxy-synastry-chart')\nexport class RoxySynastryChart extends RoxyDataElement<CalculateSynastryResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\taspectLineStyles,\n\t\tdisclosureStyles,\n\t\tinterpAccordionStyles,\n\t\tcss`.wrap{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.head{justify-content:space-between;align-items:center;gap:var(--roxy-space-md,1rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.score{font-variant-numeric:tabular-nums;font-weight:var(--roxy-weight-bold,600);color:var(--roxy-accent-ink,#b45309);font-size:var(--roxy-text-xl,1.5rem)}svg{width:100%;max-width:var(--roxy-chart-max-width,560px);aspect-ratio:1;height:auto;margin:0 auto;display:block}.wheel-line{fill:none;stroke:var(--roxy-border,#e4e4e7)}.sign{fill:var(--roxy-secondary,#475569);font-size:14px}.p1{fill:var(--roxy-accent,#f59e0b);font-size:13px;font-weight:600}.p2{fill:var(--roxy-info,#0284c7);font-size:13px;font-weight:600}.person-tag{opacity:.85;font-size:7px;font-weight:700}.planet-deg{fill:var(--roxy-muted,#71717a);font-size:7px;font-family:var(--roxy-font-sans)}.planet-deg .retro{fill:var(--roxy-danger,#dc2626)}.asc-tick{stroke:var(--roxy-accent-ink,#b45309);stroke-width:1px;opacity:.75}.asc-label{fill:var(--roxy-accent-ink,#b45309);font-size:9px;font-weight:700;font-family:var(--roxy-font-sans);letter-spacing:.04em}.legend-row{gap:var(--roxy-space-md,1rem);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);margin-top:calc(var(--roxy-space-xs,.25rem) * -1);flex-wrap:wrap;display:flex}.legend-row .caveat{font-style:italic}.legend-row .swatch{vertical-align:middle;border-radius:50%;width:8px;height:8px;margin-right:4px;display:inline-block}.summary{color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-base,1rem);margin:0}.people{gap:var(--roxy-space-sm,.5rem);grid-template-columns:repeat(auto-fit,minmax(11rem,1fr));display:grid}.person{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);gap:.3rem;display:grid}.person-name{font-weight:var(--roxy-weight-bold,600);font-size:var(--roxy-text-sm,.875rem);align-items:center;gap:.4rem;display:flex}.person-dot{border-radius:50%;flex-shrink:0;width:8px;height:8px}.person-dot.p1{background:var(--roxy-accent,#f59e0b)}.person-dot.p2{background:var(--roxy-info,#0284c7)}.big-three{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a);flex-wrap:wrap;gap:.25rem .75rem;display:flex}.big-three span{align-items:baseline;gap:.25rem;display:inline-flex}.big-three .lbl{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem)}.summary-pills{gap:var(--roxy-space-xs,.25rem);flex-wrap:wrap;display:flex}.pill{border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 60%, transparent);color:var(--roxy-fg,#0a0a0a);padding:2px 8px}.pill--success{background:color-mix(in srgb, var(--roxy-success,#16a34a) 12%, transparent);color:var(--roxy-success-fg,#166534)}.pill--danger{background:color-mix(in srgb, var(--roxy-danger,#dc2626) 12%, transparent);color:var(--roxy-danger-fg,#991b1b)}.glyph{font-size:1.1em;line-height:1}.static-head{align-items:center;gap:var(--roxy-space-sm,.5rem);font-size:var(--roxy-text-sm,.875rem);flex-wrap:wrap;font-weight:500;display:flex}.asp-name{font-weight:var(--roxy-weight-bold,600);text-transform:capitalize}.asp-name.harmonious{color:var(--roxy-success-fg,#166534)}.asp-name.challenging{color:var(--roxy-danger-fg,#991b1b)}.context{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);text-transform:uppercase;letter-spacing:.06em;font-weight:var(--roxy-weight-bold,600)}.catalog>summary{cursor:pointer;font-weight:var(--roxy-weight-bold,600);font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-fg,#0a0a0a);align-items:center;gap:.5rem;display:flex}.scroll{min-width:0;margin-top:var(--roxy-space-sm,.5rem);overflow-x:auto}table{border-collapse:collapse;width:100%;font-size:var(--roxy-text-sm,.875rem)}th,td{padding:var(--roxy-space-sm,.5rem);border-bottom:1px solid var(--roxy-border,#e4e4e7);text-align:left}th{color:var(--roxy-muted,#71717a);font-weight:var(--roxy-weight-bold,600);text-transform:uppercase;font-size:var(--roxy-text-xs,.75rem);letter-spacing:.06em}td.orb{font-variant-numeric:tabular-nums;color:var(--roxy-muted,#71717a)}.lists{gap:var(--roxy-space-md,1rem);grid-template-columns:repeat(auto-fit,minmax(14rem,1fr));display:grid}.lists h3{margin:0 0 var(--roxy-space-xs,.25rem) 0;font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.06em}.lists ul{padding-left:var(--roxy-space-md,1rem);font-size:var(--roxy-text-sm,.875rem);margin:0}.missing-planets{background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 8%, transparent);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-md,1rem);color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-sm,.875rem);line-height:1.5}.missing-planets code{font-family:var(--roxy-font-mono,ui-monospace, SFMono-Regular, Menlo, monospace);background:color-mix(in srgb, var(--roxy-fg,#0a0a0a) 6%, transparent);border-radius:4px;padding:0 4px;font-size:.95em}`,\n\t];\n\n\tprotected renderData(d: CalculateSynastryResponse) {\n\t\tconst { person1, person2, compatibilityScore, analysis } = d;\n\t\tconst interAspects = d.interAspects ?? [];\n\t\tconst p1Planets = person1?.planets ?? [];\n\t\tconst p2Planets = person2?.planets ?? [];\n\n\t\tconst score =\n\t\t\ttypeof compatibilityScore === 'number'\n\t\t\t\t? Math.round(compatibilityScore)\n\t\t\t\t: undefined;\n\t\tconst summaryText = analysis?.overall;\n\t\tconst strengths = analysis?.strengths ?? [];\n\t\tconst challenges = analysis?.challenges ?? [];\n\n\t\t// /astrology/synastry returns chart highlights (Sun, Moon, Ascendant) but\n\t\t// not full planet positions, so the dual wheel cannot be drawn from a bare\n\t\t// synastry response. Say so instead of rendering a blank wheel; every other\n\t\t// block still renders, so a caller who passes the bare response gets the\n\t\t// full reading minus the drawing.\n\t\tconst hasPlanets = p1Planets.length > 0 && p2Planets.length > 0;\n\t\t// The tightest contacts carry the reading. Sort a copy: `data` is the\n\t\t// caller's object.\n\t\tconst ranked = [...interAspects].sort(\n\t\t\t(a, b) => (b.strength ?? 0) - (a.strength ?? 0),\n\t\t);\n\t\tconst lead = ranked.slice(0, READING_COUNT);\n\n\t\treturn html`<div class=\"wrap\" part=\"card\" aria-label=\"${this.t('Synastry compatibility chart')}\"><div class=\"head\" part=\"header\"><h2 class=\"title\">${this.t('Synastry')}</h2>${\n\t\t\t\t\ttypeof score === 'number'\n\t\t\t\t\t\t? html`<span class=\"score\" aria-label=\"${this.t('Score {{score}} of 100', { score })}\">${score} / 100</span>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</div>${this.renderPeople(person1, person2)} ${\n\t\t\t\thasPlanets\n\t\t\t\t\t? html`<svg viewBox=\"0 0 ${SIZE} ${SIZE}\" part=\"chart\" role=\"img\" aria-label=\"${this.t('Dual chart wheel comparing two natal charts')}\"><title>${this.t('Synastry dual wheel')}</title><circle class=\"wheel-line\" cx=\"${CENTER}\" cy=\"${CENTER}\" r=\"${OUTER_R}\" stroke-width=\"1.5\"/><circle class=\"wheel-line\" cx=\"${CENTER}\" cy=\"${CENTER}\" r=\"${P2_R + 14}\" stroke-width=\"0.8\"/><circle class=\"wheel-line\" cx=\"${CENTER}\" cy=\"${CENTER}\" r=\"${P2_R - 14}\" stroke-width=\"0.6\"/>${this.renderSpokes()} ${this.renderSigns()} ${this.renderInterAspectLines(p1Planets, p2Planets, interAspects)} ${this.renderRing(p1Planets, P1_R, 'p1', 1)} ${this.renderRing(p2Planets, P2_R, 'p2', 2)} ${this.renderAscendants(d)}</svg><div class=\"legend-row\" part=\"legend\"><span><span class=\"swatch\" style=\"background:var(--roxy-accent)\"></span>${this.t('Person 1')}</span> <span><span class=\"swatch\" style=\"background:var(--roxy-info)\"></span>${this.t('Person 2')}</span> <span><span class=\"swatch\" style=\"background:var(--roxy-success)\"></span>${this.t('Harmonious')}</span> <span><span class=\"swatch\" style=\"background:var(--roxy-danger)\"></span>${this.t('Challenging')}</span> <span class=\"caveat\">${this.t('Sign sectors, not houses')}</span></div>`\n\t\t\t\t\t: html`<div class=\"missing-planets\" role=\"status\">${this.t('Synastry response missing planet positions.')} ${this.t(\n\t\t\t\t\t\t\t'A current {{endpoint}} response carries {{first}} and {{second}}, and the inter-aspect readings below still work without them.',\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tendpoint: '/astrology/synastry',\n\t\t\t\t\t\t\t\tfirst: 'person1.planets',\n\t\t\t\t\t\t\t\tsecond: 'person2.planets',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t)}</div>`\n\t\t\t} ${this.renderSummaryPills(d.summary)} ${\n\t\t\t\t// The contact counts above are the data; this paragraph is the read.\n\t\t\t\tsummaryText && !this.hideReadings\n\t\t\t\t\t? html`<p class=\"summary\">${summaryText}</p>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\tlead.length > 0\n\t\t\t\t\t? html`<section class=\"block\" part=\"section inter-aspects\"><h3>${this.t('Inter-aspects')}</h3>${lead.map((a, i) => this.renderAspectCard(a, i))}</section>`\n\t\t\t\t\t: nothing\n\t\t\t} ${ranked.length > lead.length ? this.renderCatalog(ranked) : nothing} ${\n\t\t\t\t// Each entry is a sentence about the pair, so the two lists are prose\n\t\t\t\t// laid out as bullets rather than data.\n\t\t\t\t!this.hideReadings && (strengths.length > 0 || challenges.length > 0)\n\t\t\t\t\t? html`<div class=\"lists\" part=\"section strengths-challenges\">${\n\t\t\t\t\t\t\tstrengths.length\n\t\t\t\t\t\t\t\t? html`<div><h3>${this.t('Strengths')}</h3><ul>${strengths.map((s) => html`<li>${s}</li>`)}</ul></div>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t} ${\n\t\t\t\t\t\t\tchallenges.length\n\t\t\t\t\t\t\t\t? html`<div><h3>${this.t('Challenges')}</h3><ul>${challenges.map((s) => html`<li>${s}</li>`)}</ul></div>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t}</div>`\n\t\t\t\t\t: nothing\n\t\t\t}</div>`;\n\t}\n\n\t/** Chart highlights for both people: the Sun, Moon, and Ascendant a reader checks before any aspect. Colour-dotted to match each person ring on the wheel. */\n\tprivate renderPeople(\n\t\tp1: SynastryPerson | undefined,\n\t\tp2: SynastryPerson | undefined,\n\t) {\n\t\tconst card = (p: SynastryPerson | undefined, index: 1 | 2) => {\n\t\t\tif (!p) return nothing;\n\t\t\t// Three columns, not two: the sign the GLYPH is keyed on stays English\n\t\t\t// whatever the page language, and only the third is read.\n\t\t\tconst asc = p.ascendant;\n\t\t\tconst big: Array<[ChromeString, string, string]> = [\n\t\t\t\t['Sun', p.sunSign ?? '', display(p, 'sunSign')],\n\t\t\t\t['Moon', p.moonSign ?? '', display(p, 'moonSign')],\n\t\t\t\t[\n\t\t\t\t\t'ASC',\n\t\t\t\t\tasc?.sign ?? '',\n\t\t\t\t\tasc\n\t\t\t\t\t\t? `${display(asc, 'sign')} ${formatNumber(this.effectiveLang(), asc.degree, 0)}°`\n\t\t\t\t\t\t: '',\n\t\t\t\t],\n\t\t\t];\n\t\t\tconst shown = big.filter(([, , text]) => text);\n\t\t\tif (shown.length === 0) return nothing;\n\t\t\treturn html`<div class=\"person\"><span class=\"person-name\"><span class=\"person-dot p${index}\" aria-hidden=\"true\"></span> ${p.name || this.t('Person {{n}}', { n: index })}</span><div class=\"big-three\">${shown.map(\n\t\t\t\t\t\t([label, sign, text]) => html`<span><span class=\"lbl\">${this.t(label)}</span> <span aria-hidden=\"true\">${signGlyph(sign) ?? ''}</span> ${text}</span>`,\n\t\t\t\t\t)}</div></div>`;\n\t\t};\n\t\tconst cards = [card(p1, 1), card(p2, 2)].filter((c) => c !== nothing);\n\t\tif (cards.length === 0) return nothing;\n\t\treturn html`<div class=\"people\">${cards}</div>`;\n\t}\n\n\t/** Contact balance for the pair. `byType` is a map, so its pairs are rendered, never the object. */\n\tprivate renderSummaryPills(s: SynastrySummary | undefined) {\n\t\tif (!s || typeof s !== 'object') return nothing;\n\t\tconst byType = Object.entries(s.byType ?? {}).sort((a, b) => b[1] - a[1]);\n\t\tif (typeof s.total !== 'number' && byType.length === 0) return nothing;\n\t\treturn html`<div class=\"summary-pills\" part=\"details\" role=\"region\" aria-label=\"${this.t('Inter-aspect summary')}\">${typeof s.total === 'number' ? html`<span class=\"pill\">${this.t('Total')}: ${s.total}</span>` : nothing} <span class=\"pill pill--success\">${this.t('Harmonious')}: ${s.harmonious}</span> <span class=\"pill pill--danger\">${this.t('Challenging')}: ${s.challenging}</span> <span class=\"pill\">${this.t('Neutral')}: ${s.neutral}</span> ${byType.map(\n\t\t\t\t([type, count]) =>\n\t\t\t\t\thtml`<span class=\"pill\">${formatAspectName({ type })}: ${count}</span>`,\n\t\t\t)}</div>`;\n\t}\n\n\t/**\n\t * One inter-chart contact as a reading. The header is the scannable line (both bodies, the aspect coloured by nature, orb and strength); the body leads with `meaning.relationshipContext`, which is the whole point of a synastry aspect: not what a trine means, but what THIS trine does to THESE two people.\n\t */\n\tprivate renderAspectCard(a: InterAspect, index: number) {\n\t\tconst g1 = planetGlyph(a.planet1) ?? '';\n\t\tconst g2 = planetGlyph(a.planet2) ?? '';\n\t\tconst nature = (a.interpretation ?? 'neutral').toLowerCase();\n\t\tconst meaning = a.meaning;\n\t\tconst lead = html`<span class=\"interp-lead\"><span aria-hidden=\"true\" class=\"glyph\">${g1}</span>${display(a, 'planet1')} <span class=\"asp-name ${nature}\">${display(a, 'type', formatAspectName(a))}</span> <span aria-hidden=\"true\" class=\"glyph\">${g2}</span>${display(a, 'planet2')}</span>`;\n\t\tconst aside = html`<span class=\"interp-aside\"><small>${this.t('orb {{orb}}° · str {{strength}}', { orb: formatNumber(this.effectiveLang(), a.orb, 2), strength: formatNumber(this.effectiveLang(), a.strength, 0) })}</small></span>`;\n\t\t// The header is the contact itself (both bodies, the aspect, orb and\n\t\t// strength) and is never a reading, so a card with nothing to disclose\n\t\t// already renders flat. Hiding the readings reuses that shape rather than\n\t\t// dropping the contact.\n\t\tif (\n\t\t\tthis.hideReadings ||\n\t\t\t(!meaning?.relationshipContext && !meaning?.description?.short)\n\t\t) {\n\t\t\treturn html`<div class=\"interp-card\" part=\"reading\"><div class=\"static-head\">${lead}${aside}</div></div>`;\n\t\t}\n\t\treturn html`<details class=\"interp-card\" part=\"reading\" name=\"synastry-aspects\" ?open=\"${index === 0}\"><summary>${lead}${chevron()}${aside}</summary><div class=\"interp-body\">${\n\t\t\t\t\tmeaning.relationshipContext\n\t\t\t\t\t\t? html`<p><span class=\"context\">${this.t('In this pairing')}</span> ${meaning.relationshipContext}</p>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t} ${meaning.description?.short ? html`<p>${meaning.description.short}</p>` : nothing} ${renderKeywordChips(meaning.keywords)}</div></details>`;\n\t}\n\n\t/** Every remaining contact, so nothing the endpoint returned is dropped. */\n\tprivate renderCatalog(ranked: InterAspect[]) {\n\t\treturn html`<details class=\"catalog\"><summary>${chevron()} ${this.t('All {{count}} inter-aspects', { count: ranked.length })}</summary><div class=\"scroll\" part=\"table\">${this.renderAspects(ranked)}</div></details>`;\n\t}\n\n\tprivate toAngle(longitude: number): number {\n\t\treturn 180 - longitude;\n\t}\n\n\tprivate renderSpokes() {\n\t\treturn Array.from({ length: 12 }, (_, i) => {\n\t\t\tconst angle = this.toAngle(i * 30);\n\t\t\tconst start = polarToCartesian(CENTER, CENTER, P2_R - 14, angle);\n\t\t\tconst end = polarToCartesian(CENTER, CENTER, OUTER_R, angle);\n\t\t\treturn svg`<line class=\"wheel-line\" x1=${start.x} y1=${start.y} x2=${end.x} y2=${end.y} stroke-width=\"0.6\" />`;\n\t\t});\n\t}\n\n\tprivate renderSigns() {\n\t\treturn SIGNS_ORDER.map((s, i) => {\n\t\t\tconst angle = this.toAngle(i * 30 + 15);\n\t\t\tconst pos = polarToCartesian(CENTER, CENTER, SIGN_R, angle);\n\t\t\treturn svg`<text class=\"sign\" x=${pos.x} y=${pos.y} text-anchor=\"middle\" dominant-baseline=\"central\">${signGlyph(s)}</text>`;\n\t\t});\n\t}\n\n\tprivate renderRing(\n\t\tplanets: PlanetEntry[],\n\t\tradius: number,\n\t\tcls: string,\n\t\tpersonIndex: 1 | 2,\n\t) {\n\t\treturn planets.map((p) => {\n\t\t\tif (!Number.isFinite(p.longitude)) return nothing;\n\t\t\tconst angle = this.toAngle(p.longitude);\n\t\t\tconst pos = polarToCartesian(CENTER, CENTER, radius, angle);\n\t\t\t// Degree label sits one tier inward from the glyph so the two\n\t\t\t// concentric rings never blur their numbers into the aspect lines.\n\t\t\tconst degOffset = personIndex === 1 ? -12 : -10;\n\t\t\tconst degPos = polarToCartesian(\n\t\t\t\tCENTER,\n\t\t\t\tCENTER,\n\t\t\t\tradius + degOffset,\n\t\t\t\tangle,\n\t\t\t);\n\t\t\tconst glyph = planetGlyph(p.name) ?? display(p, 'name');\n\t\t\tconst sp = longitudeToSignPosition(p.longitude);\n\t\t\tconst retro = p.isRetrograde === true;\n\t\t\tconst degLabel = `${sp.degree}°${String(sp.minute).padStart(2, '0')}'`;\n\t\t\t// The house each planet holds in its own chart.\n\t\t\tconst house =\n\t\t\t\ttypeof p.house === 'number'\n\t\t\t\t\t? ` · ${this.t('House {{n}}', { n: p.house })}`\n\t\t\t\t\t: '';\n\t\t\t// The sign comes from the response rather than from the longitude, so the\n\t\t\t// tooltip cannot disagree with the sign the API assigned.\n\t\t\tconst tooltip = `${display(p, 'name')}${retro ? ` ${this.t('retrograde')}` : ''} - ${degLabel} ${display(p, 'sign')}${house}`;\n\t\t\treturn svg`<g>\n\t\t\t\t<text class=${cls} x=${pos.x} y=${pos.y} text-anchor=\"middle\" dominant-baseline=\"central\"><title>${tooltip}</title>${glyph}<tspan class=\"person-tag\" dy=\"-0.55em\" dx=\"0.15em\">${personIndex}</tspan></text>\n\t\t\t\t<text class=\"planet-deg\" x=${degPos.x} y=${degPos.y} text-anchor=\"middle\" dominant-baseline=\"central\">${sp.degree}°${retro ? svg`<tspan class=\"retro\"> ℞</tspan>` : nothing}</text>\n\t\t\t</g>`;\n\t\t});\n\t}\n\n\t/**\n\t * Ascendant markers for both people. Drawn as small spokes at the inner\n\t * rim with the label outside, so the two rising signs are immediately\n\t * scannable on the wheel without depending on tooltips.\n\t */\n\tprivate renderAscendants(data: CalculateSynastryResponse) {\n\t\tconst items: ReturnType<typeof svg>[] = [];\n\t\tconst make = (\n\t\t\tasc: { sign: string; degree: number } | undefined,\n\t\t\tpersonIndex: 1 | 2,\n\t\t) => {\n\t\t\tif (!asc) return;\n\t\t\tconst signIdx = SIGNS_ORDER.findIndex(\n\t\t\t\t(s) => s.toLowerCase() === asc.sign.toLowerCase(),\n\t\t\t);\n\t\t\tif (signIdx === -1) return;\n\t\t\tconst longitude = signIdx * 30 + asc.degree;\n\t\t\tconst angle = this.toAngle(longitude);\n\t\t\tconst innerR = personIndex === 1 ? P1_R + 14 : P2_R + 14;\n\t\t\tconst tickPos = polarToCartesian(CENTER, CENTER, innerR, angle);\n\t\t\tconst labelPos = polarToCartesian(CENTER, CENTER, OUTER_R + 14, angle);\n\t\t\titems.push(svg`<g>\n\t\t\t\t<line class=\"asc-tick\" x1=${tickPos.x} y1=${tickPos.y} x2=${labelPos.x} y2=${labelPos.y} />\n\t\t\t\t<text class=\"asc-label\" x=${labelPos.x} y=${labelPos.y} text-anchor=\"middle\" dominant-baseline=\"central\">${this.t('ASC{{n}}', { n: personIndex })}</text>\n\t\t\t</g>`);\n\t\t};\n\t\tmake(data.person1?.ascendant, 1);\n\t\tmake(data.person2?.ascendant, 2);\n\t\treturn items;\n\t}\n\n\tprivate renderInterAspectLines(\n\t\tp1: PlanetEntry[],\n\t\tp2: PlanetEntry[],\n\t\taspects: InterAspect[],\n\t) {\n\t\tconst longitudeOf = (\n\t\t\tlist: PlanetEntry[],\n\t\t\tname: string,\n\t\t): number | undefined => {\n\t\t\tconst target = capitalize(name);\n\t\t\tfor (const p of list) {\n\t\t\t\tif (capitalize(p.name) !== target) continue;\n\t\t\t\tif (typeof p.longitude === 'number') return p.longitude;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\treturn aspects.map((a) => {\n\t\t\tconst l1 = longitudeOf(p1, a.planet1);\n\t\t\tconst l2 = longitudeOf(p2, a.planet2);\n\t\t\tif (l1 === undefined || l2 === undefined) return nothing;\n\t\t\tconst out = polarToCartesian(CENTER, CENTER, P1_R - 12, this.toAngle(l1));\n\t\t\tconst inn = polarToCartesian(CENTER, CENTER, P2_R + 8, this.toAngle(l2));\n\t\t\tconst aspectName = normalizeAspect(a);\n\t\t\tconst cls = ASPECT_CLASS[aspectName] ?? 'aspect-other';\n\t\t\tconst orbLabel = formatNumber(this.effectiveLang(), a.orb, 1);\n\t\t\treturn svg`<line class=${`aspect ${cls}`} style=${aspectLineStyle(a)} x1=${out.x} y1=${out.y} x2=${inn.x} y2=${inn.y}><title>${display(a, 'planet1')} ${display(a, 'type', formatAspectName(a))} ${display(a, 'planet2')}${orbLabel ? ` (${this.t('Orb')} ${orbLabel}°)` : ''}</title></line>`;\n\t\t});\n\t}\n\n\tprivate renderAspects(aspects: InterAspect[]) {\n\t\treturn html`<table><caption class=\"roxy-sr-only\">${this.t(\n\t\t\t\t\t'Inter-chart aspects: the planet from chart 1, the planet from chart 2, the aspect between them, the orb in degrees and the strength.',\n\t\t\t\t)}</caption><thead><tr><th scope=\"col\">${this.t('Planet 1')}</th><th scope=\"col\">${this.t('Planet 2')}</th><th scope=\"col\">${this.t('Aspect')}</th><th scope=\"col\">${this.t('Orb')}</th><th scope=\"col\">${this.t('Strength')}</th></tr></thead><tbody>${aspects.map(\n\t\t\t\t\t(a) => html`<tr><td>${display(a, 'planet1')}</td><td>${display(a, 'planet2')}</td><td>${display(a, 'type', formatAspectName(a))}</td><td class=\"orb\">${formatNumber(this.effectiveLang(), a.orb, 1)}</td><td>${formatNumber(this.effectiveLang(), a.strength, 0)}</td></tr>`,\n\t\t\t\t)}</tbody></table>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-synastry-chart': RoxySynastryChart;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement, state } from 'lit/decorators.js';\nimport type { GetCardResponse, GetDailyCardResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { disclosureStyles } from '../utils/disclosure.js';\nimport {\n\ttype InterpSection,\n\tinterpAccordionStyles,\n} from '../utils/interp-accordion.js';\nimport { renderTablist, tablistStyles } from '../utils/tablist.js';\n\ntype TarotData = GetCardResponse | GetDailyCardResponse;\n\n/** The five life-area readings every tarot orientation carries. Both shapes the component renders supply them: the reference card carries a set per orientation, the drawn card carries one set for the orientation it landed in. */\ntype TarotGuidance = Pick<\n\tGetCardResponse['upright'],\n\t'love' | 'career' | 'finances' | 'health' | 'spirituality'\n>;\n\nconst GUIDANCE_FIELDS: ReadonlyArray<[keyof TarotGuidance, string]> = [\n\t['love', 'Love'],\n\t['career', 'Career'],\n\t['finances', 'Finances'],\n\t['health', 'Health'],\n\t['spirituality', 'Spirituality'],\n];\n\n/**\n * Tarot card. Renders /tarot/cards/{id} and /tarot/daily.\n *\n * @remarks\n * The two endpoints are NOT the same card and must not be shown the same way. The reference card ships both orientations, so the reader chooses one and the whole reading (image, keywords, description, life areas) follows that choice. The daily card is DRAWN: the API ships exactly one orientation and one set of meanings for it, so there is nothing to switch to. It used to offer the same flip, which rotated the art and relabelled the card \"reversed\" while the text below it stayed the upright reading. The draw is now fixed and stated, never toggled.\n */\n@customElement('roxy-tarot-card')\nexport class RoxyTarotCard extends RoxyDataElement<TarotData> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tdisclosureStyles,\n\t\tinterpAccordionStyles,\n\t\ttablistStyles,\n\t\tcss`.card{background:var(--roxy-surface,#fff);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-lg,1.5rem);grid-template-columns:minmax(0,9rem) 1fr;align-items:start;display:grid}@container (width<=480px){.card{grid-template-columns:1fr}}.image{border-radius:var(--roxy-radius-md,8px);background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 60%, transparent);width:100%;height:auto;transition:transform var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,cubic-bezier(.4, 0, .2, 1));display:block}.image.reversed{transform:rotate(180deg)}.title{font-size:var(--roxy-text-xl,1.5rem);font-weight:var(--roxy-weight-bold,600);letter-spacing:var(--roxy-tracking-tight);margin:0}.meta{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem);text-transform:uppercase;letter-spacing:.06em;margin-bottom:var(--roxy-space-sm,.5rem)}.drawn{color:var(--roxy-fg,#0a0a0a);font-weight:var(--roxy-weight-bold,600)}.message{color:var(--roxy-fg,#0a0a0a);margin:var(--roxy-space-sm,.5rem) 0 var(--roxy-space-md,1rem)}.reading{margin:0 0 var(--roxy-space-md,1rem);white-space:pre-line}.roxy-tablist{margin:var(--roxy-space-sm,.5rem) 0}.chips{gap:var(--roxy-space-xs,.25rem);margin-top:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.chips span{background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 14%, transparent);color:var(--roxy-fg,#0a0a0a);border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);padding:2px 8px}`,\n\t];\n\n\t/** Which orientation of the REFERENCE card is being read. The daily draw ignores this: its orientation is whatever was drawn. */\n\t@state()\n\tprivate orientation: 'upright' | 'reversed' = 'upright';\n\n\tprotected renderData(d: TarotData) {\n\t\tif ('card' in d) return this.renderDailyCard(d);\n\t\treturn this.renderFullCard(d);\n\t}\n\n\tprivate renderDailyCard(d: GetDailyCardResponse) {\n\t\tconst card = d.card;\n\t\tconst reversed = Boolean(card.reversed);\n\t\tconst keywords = card.keywords ?? [];\n\t\t// One text node per span: the markup minifier collapses whitespace at an\n\t\t// expression boundary, so every separator is joined in JS, never in markup.\n\t\tconst meta = [card.arcana ? `${card.arcana} arcana` : '', card.suit ?? '']\n\t\t\t.filter(Boolean)\n\t\t\t.join(' · ');\n\n\t\tconst readings = !this.hideReadings;\n\n\t\treturn html`<article class=\"card\" part=\"card\" aria-label=\"${card.name ?? 'Tarot card'}\">${this.renderImage(card.imageUrl, card.name, reversed)}<div part=\"header\"><div class=\"meta\">${meta ? `${meta} · ` : ''}<span class=\"drawn\">${reversed ? 'drawn reversed' : 'drawn upright'}</span></div><h2 class=\"title\">${card.name ?? 'Tarot card'}</h2>${d.dailyMessage && readings ? html`<p class=\"message\">${d.dailyMessage}</p>` : nothing} ${card.meaning && readings ? html`<p class=\"reading\">${card.meaning}</p>` : nothing} ${\n\t\t\t\t\t// Keyword chips are the shorthand of the meaning above them, so they\n\t\t\t\t\t// go with the prose rather than with the card identity.\n\t\t\t\t\tkeywords.length > 0 && readings\n\t\t\t\t\t\t? html`<div class=\"chips\">${keywords.map((k) => html`<span>${k}</span>`)}</div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t} ${this.renderGuidance(card, 'tarot-daily-guidance')}</div></article>`;\n\t}\n\n\tprivate renderFullCard(d: GetCardResponse) {\n\t\tconst reversed = this.orientation === 'reversed';\n\t\tconst oriented = reversed ? d.reversed : d.upright;\n\t\tconst keywords =\n\t\t\t(reversed ? d.keywords?.reversed : d.keywords?.upright) ??\n\t\t\toriented?.keywords ??\n\t\t\t[];\n\n\t\tconst readings = !this.hideReadings;\n\n\t\treturn html`<article class=\"card\" part=\"card\" aria-label=\"${d.name ?? this.t('Tarot card')}\">${this.renderImage(d.imageUrl, d.name, reversed)}<div part=\"header\"><div class=\"meta\">${[\n\t\t\t\t\t\td.arcana ? `${d.arcana} arcana` : '',\n\t\t\t\t\t\td.suit ?? '',\n\t\t\t\t\t\td.number !== undefined && d.number !== null\n\t\t\t\t\t\t\t? `no. ${d.number}`\n\t\t\t\t\t\t\t: '',\n\t\t\t\t\t]\n\t\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t\t.join(' · ')}</div><h2 class=\"title\">${d.name ?? 'Tarot card'}</h2>${renderTablist({\n\t\t\t\t\titems: [\n\t\t\t\t\t\t{ id: 'upright', label: this.t('Upright') },\n\t\t\t\t\t\t{ id: 'reversed', label: this.t('Reversed') },\n\t\t\t\t\t],\n\t\t\t\t\tactive: this.orientation,\n\t\t\t\t\tonSelect: (id) => {\n\t\t\t\t\t\tthis.orientation = id;\n\t\t\t\t\t},\n\t\t\t\t\tlabel: this.t('Card orientation'),\n\t\t\t\t\tidPrefix: 'tarot',\n\t\t\t\t\tcontrols: true,\n\t\t\t\t})}<div id=\"tarot-panel-${this.orientation}\" part=\"panel\" role=\"tabpanel\" tabindex=\"0\" aria-labelledby=\"tarot-tab-${this.orientation}\">${oriented?.description && readings ? html`<p class=\"reading\">${oriented.description}</p>` : nothing} ${\n\t\t\t\t\t\tkeywords.length > 0 && readings\n\t\t\t\t\t\t\t? html`<div class=\"chips\">${keywords.map((k) => html`<span>${k}</span>`)}</div>`\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t} ${this.renderGuidance(oriented, `tarot-${this.orientation}-guidance`)}</div></div></article>`;\n\t}\n\n\t/** The artwork, rotated when the orientation being read is reversed. Decorative beyond its alt text: the orientation is stated in the meta line, so the image is never the only place it appears. */\n\tprivate renderImage(\n\t\timageUrl: string | undefined,\n\t\tname: string | undefined,\n\t\treversed: boolean,\n\t) {\n\t\tconst cls = `image ${reversed ? 'reversed' : ''}`;\n\t\tif (!imageUrl) {\n\t\t\treturn html`<div class=\"${cls}\" part=\"chart\" style=\"aspect-ratio:0.6;display:flex;align-items:center;justify-content:center;color:var(--roxy-muted)\">${name ?? '?'}</div>`;\n\t\t}\n\t\treturn html`<img class=\"${cls}\" part=\"chart\" src=\"${imageUrl}\" alt=\"${name ?? 'Tarot card'}\">`;\n\t}\n\n\t/** The five life-area readings for the orientation on screen. Grouped per orientation so switching the reference card swaps the whole accordion rather than leaving a stale section open. */\n\tprivate renderGuidance(guidance: TarotGuidance | undefined, name: string) {\n\t\tif (!guidance) return nothing;\n\t\tconst sections: InterpSection[] = GUIDANCE_FIELDS.map(([key, label]) => ({\n\t\t\tlabel,\n\t\t\tbody: guidance[key] ?? '',\n\t\t}));\n\t\treturn this.renderInterpretation(sections, name, 'Guidance');\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-tarot-card': RoxyTarotCard;\n\t}\n}\n", "import { css, html } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport type { ListCardsResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { capitalize } from '../utils/string.js';\n\n/** A single card row from the catalog response. Kept spec-derived so the tile never reads a field the API does not return. */\ntype CatalogCard = ListCardsResponse['cards'][number];\n\n/**\n * Tarot catalog. Renders GET /tarot/cards as a responsive gallery of the deck: each tile carries the Rider-Waite-Smith artwork, the card name, and an arcana/suit caption. Filter the deck server-side (arcana, suit, number, paging) and pass the page response; the component renders whatever cards it carries. Pairs with `<roxy-tarot-card>` for a single-card detail view and `<roxy-tarot-spread>` for readings.\n */\n@customElement('roxy-tarot-catalog')\nexport class RoxyTarotCatalog extends RoxyDataElement<ListCardsResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.wrap{gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.head{justify-content:space-between;align-items:baseline;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-fg,#0a0a0a);margin:0}.count{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem)}.grid{gap:var(--roxy-space-md,1rem);grid-template-columns:repeat(auto-fill,minmax(7rem,1fr));margin:0;padding:0;list-style:none;display:grid}.tile{gap:var(--roxy-space-xs,.25rem);background:var(--roxy-surface,#fff);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-sm,.5rem);box-shadow:var(--roxy-shadow-sm);display:grid}.art{aspect-ratio:2/3;border-radius:var(--roxy-radius-sm,4px);object-fit:cover;background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 35%, transparent);width:100%}.name{font-size:var(--roxy-text-sm,.875rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-fg,#0a0a0a);margin:0}.meta{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);margin:0}`,\n\t];\n\n\t/**\n\t * Override the auto-derived gallery heading. Empty by default, in which case the heading is \"Tarot deck\".\n\t */\n\t@property({ type: String, reflect: true })\n\theading = '';\n\n\tprotected renderData(d: ListCardsResponse) {\n\t\tconst cards = d.cards ?? [];\n\t\tif (cards.length === 0) return this.renderEmpty();\n\n\t\tconst title = this.heading || 'Tarot deck';\n\t\tconst total = typeof d.total === 'number' ? d.total : cards.length;\n\n\t\treturn html`<section class=\"wrap\" part=\"card\" aria-label=\"${title}\"><header class=\"head\" part=\"header\"><h2 class=\"title\">${title}</h2><span class=\"count\">${total} ${total === 1 ? 'card' : 'cards'}</span></header><ul class=\"grid\" part=\"section cards\">${cards.map(\n\t\t\t\t\t(c) => html`<li class=\"tile\">${\n\t\t\t\t\t\t\tc.imageUrl\n\t\t\t\t\t\t\t\t? html`<img class=\"art\" src=\"${c.imageUrl}\" alt=\"${c.name ?? 'Tarot card'}\" loading=\"lazy\">`\n\t\t\t\t\t\t\t\t: html`<div class=\"art\" aria-hidden=\"true\"></div>`\n\t\t\t\t\t\t}<p class=\"name\">${c.name}</p><p class=\"meta\">${cardMeta(c)}</p></li>`,\n\t\t\t\t)}</ul></section>`;\n\t}\n}\n\n/**\n * Caption line for a catalog tile. Minor Arcana cards name their suit (`Minor · Cups`); Major Arcana cards read `Major Arcana`. Both derive only from the spec `arcana` and `suit` fields.\n */\nfunction cardMeta(c: CatalogCard): string {\n\tif (c.suit) return `${capitalize(c.arcana)} · ${capitalize(c.suit)}`;\n\treturn `${capitalize(c.arcana)} Arcana`;\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-tarot-catalog': RoxyTarotCatalog;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport type {\n\tCastCareerSpreadResponse,\n\tCastCelticCrossResponse,\n\tCastCustomSpreadResponse,\n\tCastLoveSpreadResponse,\n\tCastReadingResponse,\n\tCastThreeCardResponse,\n\tCastYesNoResponse,\n\tDrawCardsResponse,\n} from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\n\ntype TarotSpreadData =\n\t| CastThreeCardResponse\n\t| CastCelticCrossResponse\n\t| CastLoveSpreadResponse\n\t| CastYesNoResponse\n\t| CastReadingResponse\n\t| CastCareerSpreadResponse\n\t| CastCustomSpreadResponse\n\t| DrawCardsResponse;\n\n/**\n * Tarot spread card. Renders /tarot/spreads/{three-card,celtic-cross,love},\n * /tarot/yes-no, /tarot/draw responses.\n *\n * @remarks\n * The cast is the data: which card landed in which position, whether it came up\n * reversed, its arcana, the art, and the yes or no verdict with its strength.\n * `hide-readings` keeps every one of those and drops the words: the per-position\n * interpretation, the per-card meaning on a raw draw, the keyword chips beside\n * the verdict, and the closing summary. A reader still sees the spread they drew,\n * which is the whole point of publishing the graphic under your own commentary.\n */\n/** \"three card\" -> \"Three card\". Spread names arrive lowercase from both the API and the attribute, and a heading should not. */\nfunction titleCase(v: string): string {\n\treturn v ? v.charAt(0).toUpperCase() + v.slice(1) : v;\n}\n\n@customElement('roxy-tarot-spread')\nexport class RoxyTarotSpread extends RoxyDataElement<TarotSpreadData> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.wrap{gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.head{justify-content:space-between;gap:var(--roxy-space-md,1rem);flex-wrap:wrap;align-items:baseline;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);text-transform:capitalize;margin:0}.question{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem);font-style:italic}.answer{border-radius:var(--roxy-radius-full,9999px);font-weight:var(--roxy-weight-bold,600);font-size:var(--roxy-text-base,1rem);text-transform:uppercase;letter-spacing:.06em;padding:4px 14px;display:inline-block}.answer.yes{background:color-mix(in srgb, var(--roxy-success,#16a34a) 16%, transparent);color:var(--roxy-success-fg,#166534)}.answer.no{background:color-mix(in srgb, var(--roxy-danger,#dc2626) 16%, transparent);color:var(--roxy-danger-fg,#991b1b)}.answer.maybe{background:color-mix(in srgb, var(--roxy-warning,#ea580c) 16%, transparent);color:var(--roxy-warning-fg,#9a3412)}.chips{gap:var(--roxy-space-xs,.25rem);flex-wrap:wrap;display:flex}.chips span{background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 14%, transparent);color:var(--roxy-fg,#0a0a0a);border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);padding:2px 8px}.verdict{gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,8rem) 1fr;align-items:start;display:grid}@container (width<=26rem){.verdict{grid-template-columns:1fr}}.verdict .meta{gap:var(--roxy-space-sm,.5rem);justify-items:start;display:grid}.arcana{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.06em;margin:0}.grid{gap:var(--roxy-space-md,1rem);grid-template-columns:repeat(auto-fit,minmax(8rem,1fr));display:grid}.card{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-sm,.5rem);background:var(--roxy-surface,#fff);gap:var(--roxy-space-xs,.25rem);grid-template-columns:minmax(0,1fr);display:grid}.label{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.06em;margin:0}.image{aspect-ratio:.6;background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 60%, transparent);border-radius:var(--roxy-radius-sm,4px);width:100%;font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);justify-content:center;align-items:center;display:flex;overflow:hidden}.image img{object-fit:cover;width:100%;height:100%;transition:transform var(--roxy-motion-duration,.2s) var(--roxy-motion-easing,cubic-bezier(.4, 0, .2, 1))}.image img.reversed{transform:rotate(180deg)}.name{font-size:var(--roxy-text-sm,.875rem);font-weight:var(--roxy-weight-bold,600);margin:0}.interp{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-secondary,#475569);margin:0}.reading{color:var(--roxy-fg,#0a0a0a);margin:0}`,\n\t];\n\n\t@property({ type: String, reflect: true })\n\tspread:\n\t\t| 'three-card'\n\t\t| 'celtic-cross'\n\t\t| 'love'\n\t\t| 'career'\n\t\t| 'custom'\n\t\t| 'yes-no'\n\t\t| 'draw' = 'three-card';\n\n\tprotected renderData(d: TarotSpreadData) {\n\t\tconst isYesNo = 'answer' in d;\n\t\tconst isDrawn = 'cards' in d && !('spread' in d);\n\t\tconst positions = isDrawn\n\t\t\t? []\n\t\t\t: 'positions' in d\n\t\t\t\t? (d.positions ?? [])\n\t\t\t\t: [];\n\t\tconst cards = isDrawn && 'cards' in d ? (d as DrawCardsResponse).cards : [];\n\t\tconst answer = isYesNo ? (d as CastYesNoResponse).answer : undefined;\n\t\tconst strength = isYesNo ? (d as CastYesNoResponse).strength : undefined;\n\t\tconst verdictCard = isYesNo ? (d as CastYesNoResponse).card : undefined;\n\t\t// Title from the RESPONSE SHAPE first, the attribute only as a last resort.\n\t\t// The API sends `spread: null` on several casts, and a host that cannot set\n\t\t// attributes (the WordPress plugin maps an operationId to a bare tag) always\n\t\t// falls through to the property default, so a yes or no reading was headed\n\t\t// \"three card\". Reading the shape means the heading is right either way.\n\t\tconst spreadLabel = isYesNo\n\t\t\t? 'Yes or no'\n\t\t\t: isDrawn\n\t\t\t\t? 'Card draw'\n\t\t\t\t: titleCase(\n\t\t\t\t\t\t('spread' in d ? (d as CastThreeCardResponse).spread : '') ||\n\t\t\t\t\t\t\tthis.spread.replace(/-/g, ' '),\n\t\t\t\t\t);\n\t\tconst question =\n\t\t\t'question' in d ? (d as CastThreeCardResponse).question : undefined;\n\t\tconst summary =\n\t\t\t'summary' in d ? (d as CastThreeCardResponse).summary : undefined;\n\t\tconst yesNoInterp = isYesNo\n\t\t\t? (d as CastYesNoResponse).interpretation\n\t\t\t: undefined;\n\t\tconst answerClass = answer\n\t\t\t? answer.toLowerCase().replace(/[^a-z]/g, '')\n\t\t\t: '';\n\n\t\treturn html`<article class=\"wrap\" part=\"card\" aria-label=\"${this.t('Tarot spread')}\"><header class=\"head\" part=\"header\"><h2 class=\"title\">${spreadLabel}</h2>${question ? html`<span class=\"question\">\"${question}\"</span>` : nothing}</header>${\n\t\t\t\tisYesNo\n\t\t\t\t\t? html`<div class=\"verdict\" part=\"section verdict\"><div class=\"card\"><div class=\"image\" part=\"chart\">${\n\t\t\t\t\t\t\t\t\tverdictCard?.imageUrl\n\t\t\t\t\t\t\t\t\t\t? html`<img src=\"${verdictCard.imageUrl}\" alt=\"${verdictCard.name ?? 'tarot card'}\" class=\"${verdictCard.reversed ? 'reversed' : ''}\">`\n\t\t\t\t\t\t\t\t\t\t: html`${verdictCard?.name ?? '?'}`\n\t\t\t\t\t\t\t\t}</div><p class=\"name\">${verdictCard?.name ?? ''} ${verdictCard?.reversed ? html`<small>${this.t('(reversed)')}</small>` : nothing}</p>${verdictCard?.arcana ? html`<p class=\"arcana\">${this.t('{{arcana}} arcana', { arcana: verdictCard.arcana })}</p>` : nothing}</div><div class=\"meta\" part=\"details\"><div><span class=\"${`answer ${answerClass}`}\">${answer}</span> ${strength ? html`<small>· ${strength}</small>` : nothing}</div>${\n\t\t\t\t\t\t\t\t// The answer and its strength are the cast; the chips are what\n\t\t\t\t\t\t\t\t// the card is taken to say.\n\t\t\t\t\t\t\t\tverdictCard?.keywords &&\n\t\t\t\t\t\t\t\tverdictCard.keywords.length > 0 &&\n\t\t\t\t\t\t\t\t!this.hideReadings\n\t\t\t\t\t\t\t\t\t? html`<div class=\"chips\">${verdictCard.keywords.map((k) => html`<span>${k}</span>`)}</div>`\n\t\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t\t}</div></div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\tpositions.length > 0\n\t\t\t\t\t? html`<div class=\"grid\" part=\"section positions\">${positions.map(\n\t\t\t\t\t\t\t(p) => html`<div class=\"card\"><p class=\"label\">${p.name ?? ''}</p><div class=\"image\" part=\"chart\">${\n\t\t\t\t\t\t\t\t\t\tp.card?.imageUrl\n\t\t\t\t\t\t\t\t\t\t\t? html`<img src=\"${p.card.imageUrl}\" alt=\"${p.card.name ?? 'tarot card'}\" class=\"${p.card.reversed ? 'reversed' : ''}\">`\n\t\t\t\t\t\t\t\t\t\t\t: html`${p.card?.name ?? '?'}`\n\t\t\t\t\t\t\t\t\t}</div><p class=\"name\">${p.card?.name ?? ''} ${p.card?.reversed ? html`<small>${this.t('(reversed)')}</small>` : nothing}</p>${p.interpretation && !this.hideReadings ? html`<p class=\"interp\">${p.interpretation}</p>` : nothing}</div>`,\n\t\t\t\t\t\t)}</div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\tcards.length > 0\n\t\t\t\t\t? html`<div class=\"grid\" part=\"section cards\">${cards.map(\n\t\t\t\t\t\t\t(c) => html`<div class=\"card\"><div class=\"image\" part=\"chart\">${\n\t\t\t\t\t\t\t\t\t\tc.imageUrl\n\t\t\t\t\t\t\t\t\t\t\t? html`<img src=\"${c.imageUrl}\" alt=\"${c.name ?? 'tarot card'}\" class=\"${c.reversed ? 'reversed' : ''}\">`\n\t\t\t\t\t\t\t\t\t\t\t: html`${c.name ?? '?'}`\n\t\t\t\t\t\t\t\t\t}</div><p class=\"name\">${c.name ?? ''} ${c.reversed ? html`<small>${this.t('(reversed)')}</small>` : nothing}</p>${c.meaning && !this.hideReadings ? html`<p class=\"interp\">${c.meaning}</p>` : nothing}</div>`,\n\t\t\t\t\t\t)}</div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${summary && !this.hideReadings ? html`<p class=\"reading\">${summary}</p>` : nothing} ${yesNoInterp && !this.hideReadings ? html`<p class=\"reading\">${yesNoInterp}</p>` : nothing}</article>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-tarot-spread': RoxyTarotSpread;\n\t}\n}\n", "import { css, html, nothing, svg } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { planetGlyph, SIGNS_ORDER, signGlyph } from '../tokens/index.js';\nimport type {\n\tCalculateTransitAspectsResponse,\n\tNatalChartResponse,\n} from '../types/index.js';\nimport { aspectLineStyle, aspectLineStyles } from '../utils/aspect-line.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport {\n\tarcMidpoint,\n\tarcSeparation,\n\tfanOut,\n\tlongitudeToSignPosition,\n\tnormalizeLongitude,\n\toppositePoint,\n\tpolarToCartesian,\n} from '../utils/degree.js';\nimport { disclosureStyles } from '../utils/disclosure.js';\nimport {\n\tASPECT_CLASS,\n\tformatAspectName,\n\tformatDateTime,\n\tformatNumber,\n\tnormalizeAspect,\n} from '../utils/format.js';\nimport {\n\ttype InterpSection,\n\tinterpAccordionStyles,\n\trenderKeywordChips,\n} from '../utils/interp-accordion.js';\nimport { display } from '../utils/localized.js';\nimport { capitalize } from '../utils/string.js';\n\ntype Body = CalculateTransitAspectsResponse['transitPlanets'][number];\ntype TransitAspect = CalculateTransitAspectsResponse['aspects'][number];\ntype TransitSummary = CalculateTransitAspectsResponse['summary'];\n\n/**\n * The transit-aspects response plus the natal frame it now carries.\n *\n * @remarks\n * `/astrology/transit-aspects` returns `houses` and `ascendant` field for field identical to the\n * natal-chart ones, which is what lets the prop and the payload share one code path below. They are\n * declared here as OPTIONAL rather than read straight off the generated response type because\n * `specs/openapi.json` is refreshed from live only after the API deploys, so the type does not name\n * them yet and this component must compile against both the old spec and the new one. Once the spec\n * refresh lands the intersection is a no-op and this alias can go.\n */\ntype TransitAspectsPayload = CalculateTransitAspectsResponse &\n\tPartial<Pick<NatalChartResponse, 'houses' | 'ascendant'>>;\n\n/**\n * Radii, outermost first. Every one is explicit rather than derived from its\n * neighbour, because the two rings of glyphs and their two bands of degree\n * labels have to interleave without touching, and an offset-from-the-last-ring\n * scheme hides which pair is actually the tight one.\n */\nconst SIZE = 400;\nconst CENTER = SIZE / 2;\n/** ASC/DSC label. Inside the rim by more than half the label width, or the axis text clips on the viewBox edge. */\nconst AXIS_LABEL_R = 184;\n/** Outer rim of the zodiac band. */\nconst OUTER_R = 170;\n/** Sign glyphs, and the inner edge of the band that holds them. */\nconst SIGN_R = 154;\nconst BAND_R = 140;\n/** Transiting bodies: the OUTER ring, which is how a bi-wheel is read. Degree labels sit outward, into the free space under the sign band. */\nconst TRANSIT_R = 124;\nconst TRANSIT_DEG_R = 135;\n/** Natal bodies: the INNER ring, the chart being transited. Degree labels sit inward, so the two bands of numbers never meet. */\nconst NATAL_R = 94;\nconst NATAL_DEG_R = 83;\n/** Aspect lines run between the rings: out of the transit ring, in to the natal one. */\nconst TRANSIT_LINE_R = 112;\nconst NATAL_LINE_R = 104;\n/** Innermost circle, closing the wheel under the natal degree labels. */\nconst HUB_R = 70;\n/** House sector numbers, inside the hub: the only band on this wheel with nothing else in it. Drawn ONLY from real cusps, the response ones or a page override. */\nconst HOUSE_NUM_R = 58;\n/**\n * Widths a fanned cluster has to clear. The glyph is the wider mark but sits on\n * the larger radius, and the degree label is narrower on a smaller one, so which\n * of the two actually binds depends on the ring and neither can be assumed.\n */\nconst GLYPH_WIDTH = 13;\nconst DEG_LABEL_WIDTH = 15;\n/** Leader line: a tick at the body's true longitude, and the foot of the line beside the displaced glyph. Both offsets are measured from the ring, signed so the leader always runs into the gap between the two rings. */\nconst LEADER_TICK = 8;\nconst LEADER_FOOT = 4;\n\n/**\n * Glyph for an API body, or the name a reader sees when the library has none: the SVG ring position is the sole identifier for that body, so a miss must stay visibly wrong rather than a plausible fabricated abbreviation.\n *\n * Two arguments because the two halves answer to different languages. `name` is the canonical English value the glyph table is keyed on and must stay English; `label` is what is left ON SCREEN when that lookup misses, so it is the localized one.\n */\nconst glyphFor = (name: string, label: string): string =>\n\tplanetGlyph(name) ?? label;\n\n/** `12°34'` from a raw ecliptic longitude, the form a practitioner reads off a wheel. */\nconst degLabel = (longitude: number): string => {\n\tconst sp = longitudeToSignPosition(longitude);\n\treturn `${sp.degree}°${String(sp.minute).padStart(2, '0')}'`;\n};\n\n/** Bodies keyed by their canonical ENGLISH name, so an aspect or a table row can find a longitude without rescanning the array. The API keeps `name` English in every language for exactly this, and keying on `nameLocalized` would resolve nothing on a translated page. */\nconst byName = (list: Body[]): Map<string, Body> => {\n\tconst m = new Map<string, Body>();\n\tfor (const p of list) m.set(capitalize(p.name), p);\n\treturn m;\n};\n\n/**\n * Natal and transit bi-wheel: the natal chart on the inner ring, the transiting\n * bodies on the outer ring, and a line for every transit-to-natal aspect the\n * response returns. Pass `data` from /astrology/transit-aspects.\n *\n * @remarks\n * **Nothing astrological is computed here.** Every aspect line is drawn from an\n * entry in the response `aspects` array; the only arithmetic is placing a\n * longitude on a circle. `planet1` is the TRANSITING body and `planet2` the\n * NATAL one, which was settled empirically rather than from the field\n * description because reading them the other way round still draws a\n * plausible-looking wheel: against a live response every one of the 70 returned\n * orbs reconciles with the transit-to-natal separation, and only 4 with the\n * reverse.\n *\n * **The response carries real houses, and it did not always.** Every body comes\n * back with a `house` read against the NATAL cusps (a transiting body reports the\n * natal house it is currently passing through) plus a top-level `houseSystem`.\n * The fourteen natal house numbers reconcile body\n * for body with /astrology/natal-chart for the same birth data, and requesting\n * `whole-sign` moves them. An earlier revision of the API did return `1` for\n * every body, and the comment saying so outlived the fix and went on justifying a\n * missing House column for months. The numbers are rendered, in their own\n * columns, labelled with the system the response names.\n *\n * **The GEOMETRY arrived next, and the wheel now draws itself.** The response also\n * carries the twelve natal cusps as `houses` and the natal `ascendant`, the same\n * twelve the house numbers above were read against, so the sector ring and the\n * real horizon come out of the one call the page already made. That closed the\n * house-less bi-wheel every embed drew through WordPress, /embed and the CDN\n * widgets, none of which can compose a second call. {@link RoxyTransitWheel.houses}\n * and {@link RoxyTransitWheel.ascendant} remain as OVERRIDES and keep winning\n * where a page sets them, which is what a stored response predating the fields,\n * or a trimming proxy, needs.\n *\n * **Nothing is ever derived, whichever source it came from.** A payload with\n * neither field still fixes 0 degrees Aries on the left horizon, says so in the\n * legend and in the SVG description rather than leaving a reader to discover it,\n * and draws no house divisions at all. Equal sectors derived from an Ascendant\n * and labelled with a quadrant system would replace one false statement with a\n * worse one. Every cusp on this wheel is a longitude some source stated, placed\n * on a circle, never one this component worked out.\n */\n@customElement('roxy-transit-wheel')\nexport class RoxyTransitWheel extends RoxyDataElement<CalculateTransitAspectsResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\taspectLineStyles,\n\t\tdisclosureStyles,\n\t\tinterpAccordionStyles,\n\t\tcss`.wrap{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.head{justify-content:space-between;align-items:baseline;gap:var(--roxy-space-md,1rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.subtitle{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem);margin:0}svg{width:100%;max-width:var(--roxy-chart-max-width,560px);aspect-ratio:1;height:auto;margin:0 auto;display:block}.wheel-line{fill:none;stroke:var(--roxy-border,#e4e4e7)}.sign-glyph{fill:var(--roxy-secondary,#475569);font-size:14px;font-family:var(--roxy-font-sans)}.natal-glyph{fill:var(--roxy-accent-ink,#b45309);font-size:13px;font-weight:600;font-family:var(--roxy-font-sans)}.transit-glyph{fill:var(--roxy-info,#0284c7);font-size:13px;font-weight:600;font-family:var(--roxy-font-sans)}.leader{stroke:currentColor;stroke-width:.6px;opacity:.6}.leader.natal-glyph{stroke:var(--roxy-accent-ink,#b45309)}.leader.transit-glyph{stroke:var(--roxy-info,#0284c7)}.planet-deg{fill:var(--roxy-muted,#71717a);font-size:7px;font-family:var(--roxy-font-sans)}.planet-deg .retro{fill:var(--roxy-danger,#dc2626)}@container (width<=480px){.sign-glyph,.natal-glyph,.transit-glyph{font-size:18px}.planet-deg{font-size:10px}.house-num{font-size:12px}}.tick{stroke:var(--roxy-border,#e4e4e7)}.tick-major{stroke:var(--roxy-secondary,#475569)}.house-cusp{stroke:var(--roxy-secondary,#475569);stroke-width:1px;stroke-dasharray:3 3}.house-num{fill:var(--roxy-secondary,#475569);font-size:9px;font-family:var(--roxy-font-sans)}.axis-tick{stroke:var(--roxy-accent-ink,#b45309);stroke-width:1.5px}.axis-label{fill:var(--roxy-accent-ink,#b45309);font-size:10px;font-weight:700;font-family:var(--roxy-font-sans);letter-spacing:.04em}.swatch{vertical-align:middle;border-radius:50%;width:8px;height:8px;margin-right:4px;display:inline-block}.swatch--natal{background:var(--roxy-accent-ink,#b45309)}.swatch--transit{background:var(--roxy-info,#0284c7)}.swatch--harmonious{background:var(--roxy-success,#16a34a)}.swatch--challenging{background:var(--roxy-danger,#dc2626)}.legend{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);gap:var(--roxy-space-md,1rem);flex-wrap:wrap;display:flex}.summary-pills{gap:var(--roxy-space-sm,.5rem);margin-bottom:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.pill{padding:2px var(--roxy-space-sm,.5rem);border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);border:1px solid;align-items:center;gap:4px;display:inline-flex}.pill--muted{color:var(--roxy-fg,#0a0a0a);background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 60%, transparent)}.pill--success{color:var(--roxy-success-fg,#166534);background:color-mix(in srgb, var(--roxy-success,#16a34a) 10%, transparent)}.pill--danger{color:var(--roxy-danger-fg,#991b1b);background:color-mix(in srgb, var(--roxy-danger,#dc2626) 10%, transparent)}.strongest{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);font-size:var(--roxy-text-sm,.875rem);flex-wrap:wrap;align-items:center;gap:.4rem;display:flex}.strongest .label{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);text-transform:uppercase;letter-spacing:.06em;font-weight:var(--roxy-weight-bold,600)}.strongest .meta{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);font-variant-numeric:tabular-nums;margin-left:auto}.glyph{font-size:1.1em;line-height:1}.nature-badge{font-size:var(--roxy-text-xs,.75rem);text-transform:capitalize;border-radius:9999px;padding:1px 8px;font-weight:600;display:inline-block}.nature-badge.harmonious{background:color-mix(in srgb, var(--roxy-success,#16a34a) 12%, transparent);color:var(--roxy-success-fg,#166534)}.nature-badge.challenging{background:color-mix(in srgb, var(--roxy-danger,#dc2626) 12%, transparent);color:var(--roxy-danger-fg,#991b1b)}.nature-badge.neutral{background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 60%, transparent);color:var(--roxy-fg,#0a0a0a)}.scroll{-webkit-overflow-scrolling:touch;min-width:0;overflow-x:auto}table{border-collapse:collapse;width:100%;font-size:var(--roxy-text-sm,.875rem)}th,td{padding:var(--roxy-space-sm,.5rem);border-bottom:1px solid var(--roxy-border,#e4e4e7);text-align:left;white-space:nowrap}thead th{color:var(--roxy-muted,#71717a);text-transform:uppercase;font-size:var(--roxy-text-xs,.75rem);letter-spacing:.06em}th{font-weight:var(--roxy-weight-bold,600)}td{font-variant-numeric:tabular-nums}.retro-badge{color:var(--roxy-danger-fg,#991b1b);font-weight:var(--roxy-weight-bold,600);margin-left:.25rem}`,\n\t];\n\n\t/** Heading above the wheel. The default is translated; a heading the page sets is passed through the catalogue too, so a host that supplies its own English wording still gets it localized when one is registered for it. */\n\t@property({ type: String })\n\theading = 'Transits';\n\n\t/**\n\t * Natal Ascendant as an ecliptic longitude in degrees (0-360), to override the\n\t * one the response carries.\n\t *\n\t * @remarks\n\t * Leave it unset on a live /astrology/transit-aspects response: that endpoint\n\t * returns the natal `ascendant` and the wheel reads it, so the horizon is\n\t * right with no page wiring at all. Set it only to orient the wheel on a\n\t * DIFFERENT horizon than the response describes, or to give one to a payload\n\t * that carries none. Either way it ROTATES the wheel so the given longitude\n\t * falls on the left horizon and draws the ASC/DSC axis. It draws no house\n\t * divisions, because a rotation is not a cusp; that is what\n\t * {@link RoxyTransitWheel.houses} is for. With neither the prop nor a payload\n\t * ascendant the wheel falls back to the honest default: 0 degrees Aries on\n\t * the left.\n\t */\n\t@property({ type: Number })\n\tascendant?: number;\n\n\t/**\n\t * The twelve natal house cusps, to override the ones the response carries.\n\t * Takes the /astrology/natal-chart `houses` array verbatim, or twelve bare\n\t * cusp longitudes in house order.\n\t *\n\t * @remarks\n\t * Leave it unset on a live /astrology/transit-aspects response: that endpoint\n\t * returns the same twelve natal cusps its house numbers are read against, so\n\t * the sector ring draws itself. Set it to draw a different cusp set than the\n\t * response carries, or to give one to a payload that carries none (a stored\n\t * response predating the field, or a proxy that trims it). Supplying either\n\t * source draws the twelve cusp lines and their numbers and, unless an\n\t * ascendant is also available, rotates the first cusp onto the left horizon.\n\t *\n\t * Both accepted shapes exist because the natural source is a natal-chart\n\t * response a host already holds, and passing `chart.houses` straight through\n\t * has to work; twelve numbers is the terser form for a host that stored only\n\t * the longitudes. Anything that does not resolve to houses 1 to 12 with finite\n\t * longitudes is ignored outright rather than half drawn, which is the same\n\t * degrade-honestly rule the rest of this component follows: a partial house\n\t * ring is a claim about placements the wheel cannot support.\n\t */\n\t@property({ type: Array })\n\thouses?: NatalChartResponse['houses'] | number[];\n\n\t/** The response widened to the natal frame it carries. One cast, so nothing below repeats it. */\n\tprivate get payload(): TransitAspectsPayload | undefined {\n\t\treturn this.data as TransitAspectsPayload | undefined;\n\t}\n\n\t/**\n\t * The Ascendant longitude the wheel orients on, or undefined.\n\t *\n\t * @remarks\n\t * The PROP wins over the payload wherever both exist: a page that passes one\n\t * has said something the response cannot know, and silently preferring the\n\t * response would make the prop dead on exactly the responses that carry the\n\t * field.\n\t */\n\tprivate get effectiveAscendant(): number | undefined {\n\t\tfor (const value of [this.ascendant, this.payload?.ascendant?.longitude]) {\n\t\t\tif (typeof value === 'number' && Number.isFinite(value)) return value;\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t/** True when an ascendant is available at all, so the ASC/DSC axis is a real horizon rather than one derived from a cusp. */\n\tprivate get ascendantGiven(): boolean {\n\t\treturn this.effectiveAscendant !== undefined;\n\t}\n\n\t/** True when the sector ring came from the page rather than from the response, so the legend can say which. */\n\tprivate get cuspsFromPage(): boolean {\n\t\treturn Array.isArray(this.houses);\n\t}\n\n\t/**\n\t * The cusps to draw, normalized to houses 1 to 12 in order, or null. The prop\n\t * first, then the ones the response carries, on the same precedence rule as\n\t * {@link RoxyTransitWheel.effectiveAscendant}.\n\t *\n\t * @remarks\n\t * Sorted by house number rather than trusted in array order, and rejected\n\t * whole unless the twelve numbers are exactly 1 to 12 with finite longitudes.\n\t * A cusp set that fails either check is not a house division, and drawing part\n\t * of one would put a sector boundary where the host never claimed there was\n\t * one. A malformed PROP is rejected outright rather than falling through to\n\t * the payload: the page overrode the response on purpose, and quietly drawing\n\t * the response cusps under a bad override would be a different chart than\n\t * either source asked for.\n\t */\n\tprivate get cusps(): Array<{ number: number; longitude: number }> | null {\n\t\tconst raw = this.houses ?? this.payload?.houses;\n\t\tif (!Array.isArray(raw) || raw.length !== 12) return null;\n\t\tconst out = raw\n\t\t\t.map((h, i) =>\n\t\t\t\ttypeof h === 'number'\n\t\t\t\t\t? { number: i + 1, longitude: h }\n\t\t\t\t\t: { number: Number(h?.number), longitude: Number(h?.longitude) },\n\t\t\t)\n\t\t\t.sort((a, b) => a.number - b.number);\n\t\tconst complete = out.every(\n\t\t\t(c, i) => c.number === i + 1 && Number.isFinite(c.longitude),\n\t\t);\n\t\treturn complete ? out : null;\n\t}\n\n\t/**\n\t * Ecliptic longitude to SVG angle. 0 degrees is at 3 o'clock and positive\n\t * angles run clockwise on screen, so subtracting the longitude makes the\n\t * zodiac run counterclockwise, which is how a chart wheel is read. The 180\n\t * offset puts the reference point on the left horizon: the supplied Ascendant\n\t * first, else the supplied first house cusp, else 0 Aries.\n\t *\n\t * The Ascendant wins because the two are not the same longitude under every\n\t * system: whole-sign puts cusp 1 at 0 degrees of the rising sign, which can be\n\t * most of a sign away from the Ascendant itself.\n\t */\n\tprivate toAngle(longitude: number): number {\n\t\tconst origin = this.effectiveAscendant ?? this.cusps?.[0]?.longitude ?? 0;\n\t\treturn 180 + origin - longitude;\n\t}\n\n\tprotected renderData(d: CalculateTransitAspectsResponse) {\n\t\tconst natal = d.natalPlanets ?? [];\n\t\tconst transit = d.transitPlanets ?? [];\n\t\tconst aspects = d.aspects ?? [];\n\t\tconst when = formatDateTime(this.effectiveLang(), d.transitDate);\n\t\t// Label then number, at every count and in every language: the catalogue\n\t\t// carries no plural rules, so \"1 aspects\" is the alternative in English and\n\t\t// three wrong endings are the alternative in Russian.\n\t\tconst count =\n\t\t\taspects.length > 0\n\t\t\t\t? this.t('Aspects to the natal chart: {{count}}', {\n\t\t\t\t\t\tcount: aspects.length,\n\t\t\t\t\t})\n\t\t\t\t: '';\n\t\t// The House columns and the house-system chip both stand on the SAME fact:\n\t\t// that a body came back with a house. Derived once from the payload rather\n\t\t// than assumed from the type, so a host feeding an older captured response\n\t\t// loses the columns instead of printing a column of blanks under a system\n\t\t// name.\n\t\tconst houses = [...natal, ...transit].some((p) => Number.isFinite(p.house));\n\n\t\treturn html`<div class=\"wrap\" part=\"card\" aria-label=\"${this.t('Natal and transit bi-wheel')}\"><div class=\"head\" part=\"header\"><h2 class=\"title\">${this.t(this.heading)}</h2>${\n\t\t\t\t\twhen || count\n\t\t\t\t\t\t? html`<p class=\"subtitle\">${[when, count].filter(Boolean).join(' · ')}</p>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</div>${this.renderWheel(natal, transit, aspects)} ${this.renderLegend(natal, transit, aspects, houses ? d.houseSystem : undefined)} ${this.renderSummary(d.summary)} ${this.renderPositions(natal, transit, houses)} ${this.renderReadings(aspects)}</div>`;\n\t}\n\n\t/**\n\t * What sits on the left horizon, in words. Rendered as a legend chip AND as a\n\t * sentence of the SVG description, from this one getter, so the drawing and\n\t * its accessible text cannot end up claiming different orientations.\n\t */\n\tprivate get orientationCaption(): string {\n\t\tif (this.ascendantGiven) return this.t('Ascendant on the left horizon');\n\t\tif (this.cusps) return this.t('First house cusp on the left horizon');\n\t\treturn this.t('Sign wheel, 0° Aries on the left');\n\t}\n\n\t/**\n\t * Whether the twelve sectors on the wheel are real cusps, in words, and where\n\t * they came from. Same dual use as {@link RoxyTransitWheel.orientationCaption}.\n\t *\n\t * @remarks\n\t * The source is named because the caption is provenance: a reader\n\t * reconciling this wheel against another calculator needs to know whether the\n\t * cusps are the ones the endpoint computed the house numbers against, or a set\n\t * the page substituted for them.\n\t */\n\tprivate get cuspCaption(): string {\n\t\tif (!this.cusps) return this.t('No house cusps');\n\t\treturn this.cuspsFromPage\n\t\t\t? this.t('House cusps supplied by the page')\n\t\t\t: this.t('House cusps from the response');\n\t}\n\n\tprivate renderWheel(\n\t\tnatal: Body[],\n\t\ttransit: Body[],\n\t\taspects: TransitAspect[],\n\t) {\n\t\treturn html`<svg viewBox=\"0 0 ${SIZE} ${SIZE}\" part=\"chart\" role=\"img\" aria-label=\"${this.t('Bi-wheel with natal bodies on the inner ring and transiting bodies on the outer ring')}\"><title>${this.t('Natal and transit bi-wheel')}</title><desc>${this.t('Twelve zodiac sign segments around a circular wheel. Natal bodies sit at their ecliptic longitudes on the inner ring and transiting bodies on the outer ring, and each line joins a transiting body to the natal body it aspects.')} ${this.orientationCaption}. ${this.cuspCaption}.</desc><circle class=\"wheel-line\" cx=\"${CENTER}\" cy=\"${CENTER}\" r=\"${OUTER_R}\" stroke-width=\"1.5\"/><circle class=\"wheel-line\" cx=\"${CENTER}\" cy=\"${CENTER}\" r=\"${BAND_R}\" stroke-width=\"0.8\"/><circle class=\"wheel-line\" cx=\"${CENTER}\" cy=\"${CENTER}\" r=\"${TRANSIT_LINE_R}\" stroke-width=\"0.6\"/><circle class=\"wheel-line\" cx=\"${CENTER}\" cy=\"${CENTER}\" r=\"${HUB_R}\" stroke-width=\"0.5\"/>${this.renderTicks()} ${this.renderSpokes()} ${this.renderSigns()} ${this.renderHouses()} ${this.renderAspectLines(natal, transit, aspects)} ${this.renderRing(natal, NATAL_R, NATAL_DEG_R, 'natal-glyph', this.t('Natal'), 1)} ${this.renderRing(transit, TRANSIT_R, TRANSIT_DEG_R, 'transit-glyph', this.t('Transiting'), -1)} ${this.renderAxis()}</svg>`;\n\t}\n\n\t/** A short mark every 5 degrees and a longer one on each sign cusp, so the band reads as a degree scale rather than a bare ring. */\n\tprivate renderTicks() {\n\t\tconst ticks = [];\n\t\tfor (let deg = 0; deg < 360; deg += 5) {\n\t\t\tconst angle = this.toAngle(deg);\n\t\t\tconst isMajor = deg % 30 === 0;\n\t\t\tconst a = polarToCartesian(\n\t\t\t\tCENTER,\n\t\t\t\tCENTER,\n\t\t\t\tisMajor ? BAND_R : OUTER_R - 5,\n\t\t\t\tangle,\n\t\t\t);\n\t\t\tconst b = polarToCartesian(CENTER, CENTER, OUTER_R, angle);\n\t\t\tticks.push(\n\t\t\t\tsvg`<line class=${isMajor ? 'tick tick-major' : 'tick'} x1=${a.x} y1=${a.y} x2=${b.x} y2=${b.y} stroke-width=${isMajor ? 1 : 0.5} />`,\n\t\t\t);\n\t\t}\n\t\treturn ticks;\n\t}\n\n\t/** Twelve sign boundaries, drawn from the inner ring outward. Solid, and always exactly 30 degrees apart: house cusps are the dashed lines from {@link RoxyTransitWheel.renderHouses} and only exist when a cusp set is available. */\n\tprivate renderSpokes() {\n\t\treturn SIGNS_ORDER.map((_, i) => {\n\t\t\tconst angle = this.toAngle(i * 30);\n\t\t\tconst start = polarToCartesian(CENTER, CENTER, HUB_R, angle);\n\t\t\tconst end = polarToCartesian(CENTER, CENTER, OUTER_R, angle);\n\t\t\treturn svg`<line class=\"wheel-line\" x1=${start.x} y1=${start.y} x2=${end.x} y2=${end.y} stroke-width=\"0.6\" />`;\n\t\t});\n\t}\n\n\t/**\n\t * The twelve house sectors, drawn ONLY from cusps some source stated: the ones\n\t * the response carries, or the {@link RoxyTransitWheel.houses} override.\n\t *\n\t * @remarks\n\t * Dashed and inset to the sign band, because the solid spokes beside them are\n\t * sign boundaries and a reader who cannot tell the two apart cannot reconcile\n\t * the chart against another calculator. Each number sits at the arc midpoint\n\t * of its own sector, so an unequal house carries its label inside itself; the\n\t * hub is the one band on this wheel with nothing else in it.\n\t *\n\t * Nothing here is derived. With no cusps available this renders nothing at all\n\t * rather than twelve equal sectors from the Ascendant, which would be a\n\t * different chart wearing the same label.\n\t *\n\t * **Known and accepted: a fanned glyph can sit one sector off.** {@link fanOut}\n\t * displaces a crowded glyph in ANGLE, so a body a degree inside a cusp can be\n\t * drawn just past it; measured on a real Capricorn stellium, the natal Sun\n\t * glyph landed in the ninth sector while the body is in the eighth. The leader\n\t * runs back to a tick at the true longitude and the House column is the\n\t * authoritative number, which is the same trade professional chart software\n\t * makes. Do not \"fix\" it by clamping a glyph to its sector: that would move the\n\t * mark AND still not be the position.\n\t */\n\tprivate renderHouses() {\n\t\tconst cusps = this.cusps;\n\t\tif (!cusps) return nothing;\n\t\treturn cusps.map((cusp, i) => {\n\t\t\tconst next = cusps[(i + 1) % 12];\n\t\t\tconst angle = this.toAngle(cusp.longitude);\n\t\t\tconst start = polarToCartesian(CENTER, CENTER, HUB_R, angle);\n\t\t\tconst end = polarToCartesian(CENTER, CENTER, BAND_R, angle);\n\t\t\tconst label = polarToCartesian(\n\t\t\t\tCENTER,\n\t\t\t\tCENTER,\n\t\t\t\tHOUSE_NUM_R,\n\t\t\t\tthis.toAngle(\n\t\t\t\t\tarcMidpoint(cusp.longitude, next?.longitude ?? cusp.longitude + 30),\n\t\t\t\t),\n\t\t\t);\n\t\t\treturn svg`<g>\n\t\t\t\t<line class=\"house-cusp\" x1=${start.x} y1=${start.y} x2=${end.x} y2=${end.y} />\n\t\t\t\t<text class=\"house-num\" x=${label.x} y=${label.y} text-anchor=\"middle\" dominant-baseline=\"central\">${cusp.number}</text>\n\t\t\t</g>`;\n\t\t});\n\t}\n\n\tprivate renderSigns() {\n\t\treturn SIGNS_ORDER.map((sign, i) => {\n\t\t\tconst pos = polarToCartesian(\n\t\t\t\tCENTER,\n\t\t\t\tCENTER,\n\t\t\t\tSIGN_R,\n\t\t\t\tthis.toAngle(i * 30 + 15),\n\t\t\t);\n\t\t\treturn svg`<text class=\"sign-glyph\" x=${pos.x} y=${pos.y} text-anchor=\"middle\" dominant-baseline=\"central\">${signGlyph(sign)}</text>`;\n\t\t});\n\t}\n\n\t/**\n\t * One ring of bodies, fanned apart only as far as legibility demands.\n\t *\n\t * @remarks\n\t * A real transit chart stacks four bodies inside a couple of degrees often\n\t * enough that drawing every glyph at its exact angle makes the inner ring\n\t * unreadable, degree labels printed over each other included. {@link fanOut}\n\t * pushes a crowded member forward and a thin leader line runs from the glyph\n\t * back to a tick at its TRUE longitude, so the drawing never claims a position\n\t * the response did not give. The separation is derived from the radius, so the\n\t * inner ring automatically asks for more degrees of arc than the outer one for\n\t * the same glyph.\n\t *\n\t * `leaderSign` points that tick outward from the natal ring and inward from\n\t * the transit ring, i.e. always into the gap between the two, so a leader is\n\t * never mistaken for one of the aspect lines crossing the middle.\n\t */\n\tprivate renderRing(\n\t\tbodies: Body[],\n\t\tradius: number,\n\t\tdegRadius: number,\n\t\tcls: string,\n\t\tkind: string,\n\t\tleaderSign: 1 | -1,\n\t) {\n\t\tconst separation = Math.max(\n\t\t\tarcSeparation(GLYPH_WIDTH, radius),\n\t\t\tarcSeparation(DEG_LABEL_WIDTH, degRadius),\n\t\t);\n\t\treturn fanOut(bodies, (p) => p.longitude, separation).map(\n\t\t\t({ item: p, longitude, displayLongitude }) => {\n\t\t\t\tconst angle = this.toAngle(displayLongitude);\n\t\t\t\tconst pos = polarToCartesian(CENTER, CENTER, radius, angle);\n\t\t\t\tconst degPos = polarToCartesian(CENTER, CENTER, degRadius, angle);\n\t\t\t\tconst retro = p.isRetrograde === true;\n\t\t\t\t// Whole degrees on the wheel and the full degree-and-minute in the\n\t\t\t\t// tooltip and the positions table, matching `roxy-synastry-chart`, the\n\t\t\t\t// other two-ring wheel here. A `12°34'` label is roughly twice as wide as\n\t\t\t\t// `12°`, and on a ring this size two of them collide in a cluster the\n\t\t\t\t// fan-out has already separated the GLYPHS of, so the extra precision\n\t\t\t\t// costs the legibility of the position it is trying to state.\n\t\t\t\tconst label = `${longitudeToSignPosition(longitude).degree}°`;\n\t\t\t\tconst body = display(p, 'name');\n\t\t\t\tconst tooltip = `${kind} ${body}${retro ? ` ${this.t('retrograde')}` : ''} - ${degLabel(longitude)} ${display(p, 'sign')}`;\n\t\t\t\tconst displaced = Math.abs(displayLongitude - longitude) > 0.5;\n\t\t\t\tconst tick = polarToCartesian(\n\t\t\t\t\tCENTER,\n\t\t\t\t\tCENTER,\n\t\t\t\t\tradius + leaderSign * LEADER_TICK,\n\t\t\t\t\tthis.toAngle(longitude),\n\t\t\t\t);\n\t\t\t\tconst foot = polarToCartesian(\n\t\t\t\t\tCENTER,\n\t\t\t\t\tCENTER,\n\t\t\t\t\tradius + leaderSign * LEADER_FOOT,\n\t\t\t\t\tangle,\n\t\t\t\t);\n\t\t\t\treturn svg`<g>\n\t\t\t\t${\n\t\t\t\t\tdisplaced\n\t\t\t\t\t\t? svg`<line class=${`leader ${cls}`} x1=${tick.x} y1=${tick.y} x2=${foot.x} y2=${foot.y} />`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}\n\t\t\t\t<text class=${cls} x=${pos.x} y=${pos.y} text-anchor=\"middle\" dominant-baseline=\"central\"><title>${tooltip}</title>${glyphFor(p.name, body)}</text>\n\t\t\t\t<text class=\"planet-deg\" x=${degPos.x} y=${degPos.y} text-anchor=\"middle\" dominant-baseline=\"central\">${label}${retro ? svg`<tspan class=\"retro\"> ℞</tspan>` : nothing}</text>\n\t\t\t</g>`;\n\t\t\t},\n\t\t);\n\t}\n\n\t/** One line per returned aspect, from the transiting body on the outer ring to the natal body on the inner one. */\n\tprivate renderAspectLines(\n\t\tnatal: Body[],\n\t\ttransit: Body[],\n\t\taspects: TransitAspect[],\n\t) {\n\t\tconst natalBy = byName(natal);\n\t\tconst transitBy = byName(transit);\n\t\treturn aspects.map((a) => {\n\t\t\tconst t = transitBy.get(capitalize(a.planet1));\n\t\t\tconst n = natalBy.get(capitalize(a.planet2));\n\t\t\tif (\n\t\t\t\t!t ||\n\t\t\t\t!n ||\n\t\t\t\t!Number.isFinite(t.longitude) ||\n\t\t\t\t!Number.isFinite(n.longitude)\n\t\t\t)\n\t\t\t\treturn nothing;\n\t\t\tconst from = polarToCartesian(\n\t\t\t\tCENTER,\n\t\t\t\tCENTER,\n\t\t\t\tTRANSIT_LINE_R,\n\t\t\t\tthis.toAngle(t.longitude),\n\t\t\t);\n\t\t\tconst to = polarToCartesian(\n\t\t\t\tCENTER,\n\t\t\t\tCENTER,\n\t\t\t\tNATAL_LINE_R,\n\t\t\t\tthis.toAngle(n.longitude),\n\t\t\t);\n\t\t\tconst name = normalizeAspect(a);\n\t\t\tconst orb = formatNumber(this.effectiveLang(), a.orb, 1);\n\t\t\treturn svg`<line class=${`aspect ${ASPECT_CLASS[name] ?? 'aspect-other'}`} style=${aspectLineStyle(a)} x1=${from.x} y1=${from.y} x2=${to.x} y2=${to.y}><title>${this.t('Transiting')} ${display(a, 'planet1')} ${display(a, 'type', name)} ${this.t('Natal')} ${display(a, 'planet2')}${orb ? ` (${this.t('Orb')} ${orb}°)` : ''}</title></line>`;\n\t\t});\n\t}\n\n\t/**\n\t * ASC and DSC ticks, drawn ONLY when an ascendant is available, from the prop\n\t * or from the response. Nothing is derived: the descendant is the exact\n\t * opposite point of that value.\n\t *\n\t * Gated on the ascendant alone, never on the cusps. Under whole-sign the first\n\t * cusp is 0 degrees of the rising sign rather than the Ascendant, so labelling\n\t * it ASC would be wrong on exactly the charts a traditional reader casts.\n\t */\n\tprivate renderAxis() {\n\t\tconst given = this.effectiveAscendant;\n\t\tif (given === undefined) return nothing;\n\t\tconst asc = normalizeLongitude(given);\n\t\treturn [asc, oppositePoint(asc)].map((lon, i) => {\n\t\t\tconst angle = this.toAngle(lon);\n\t\t\tconst inner = polarToCartesian(CENTER, CENTER, OUTER_R, angle);\n\t\t\tconst outer = polarToCartesian(CENTER, CENTER, AXIS_LABEL_R, angle);\n\t\t\treturn svg`<g>\n\t\t\t\t<line class=\"axis-tick\" x1=${inner.x} y1=${inner.y} x2=${outer.x} y2=${outer.y} />\n\t\t\t\t<text class=\"axis-label\" x=${outer.x} y=${outer.y} text-anchor=\"middle\" dominant-baseline=\"central\">${i === 0 ? this.t('ASC') : this.t('DSC')}</text>\n\t\t\t</g>`;\n\t\t});\n\t}\n\n\t/**\n\t * The key to the wheel, and the one place the orientation is stated in words.\n\t * A reader who cannot tell whether the divisions are signs or houses cannot\n\t * reconcile the chart against another calculator, so the caption is\n\t * provenance rather than decoration.\n\t *\n\t * `houseSystem` labels the house NUMBERS in the table, which is the only thing\n\t * the response can vouch for, and the cusp chip separately says where the\n\t * drawn sectors came from. Two statements, each true on its own, rather than\n\t * one chip implying the ring and the numbers share a source.\n\t */\n\tprivate renderLegend(\n\t\tnatal: Body[],\n\t\ttransit: Body[],\n\t\taspects: TransitAspect[],\n\t\thouseSystem: string | undefined,\n\t) {\n\t\treturn html`<div class=\"legend\" part=\"legend\"><span><span class=\"swatch swatch--natal\"></span>${this.t('{{count}} natal bodies', { count: natal.length })}</span> <span><span class=\"swatch swatch--transit\"></span>${this.t('{{count}} transiting bodies', { count: transit.length })}</span> ${\n\t\t\t\taspects.length > 0\n\t\t\t\t\t? html`<span><span class=\"swatch swatch--harmonious\"></span>${this.t('Harmonious')}</span> <span><span class=\"swatch swatch--challenging\"></span>${this.t('Challenging')}</span>`\n\t\t\t\t\t: nothing\n\t\t\t} ${houseSystem ? html`<span>${this.t('{{system}} houses', { system: houseSystem })}</span>` : nothing} <span>${this.orientationCaption}</span> <span>${this.cuspCaption}</span></div>`;\n\t}\n\n\t/** Transit weather: the counts and the tightest contact. All of it is data, so all of it survives hide-readings. */\n\tprivate renderSummary(s: TransitSummary | undefined) {\n\t\tif (!s) return nothing;\n\t\tconst byType = Object.entries(s.byType ?? {}).sort((a, b) => b[1] - a[1]);\n\t\treturn html`<div part=\"details\"><div class=\"summary-pills\" role=\"region\" aria-label=\"${this.t('Transit aspect summary')}\">${typeof s.total === 'number' ? html`<span class=\"pill pill--muted\">${this.t('Total')}: ${s.total}</span>` : nothing} <span class=\"pill pill--success\">${this.t('Harmonious')}: ${s.harmonious}</span> <span class=\"pill pill--danger\">${this.t('Challenging')}: ${s.challenging}</span> <span class=\"pill pill--muted\">${this.t('Neutral')}: ${s.neutral}</span> ${byType.map(\n\t\t\t\t\t// English in every language, and nothing to do about it here: `byType`\n\t\t\t\t\t// is an object KEYED by the canonical aspect name, so the response\n\t\t\t\t\t// carries no localized partner for these the way `aspects[]` does.\n\t\t\t\t\t([type, count]) =>\n\t\t\t\t\t\thtml`<span class=\"pill pill--muted\">${formatAspectName({ type })}: ${count}</span>`,\n\t\t\t\t)}</div>${s.strongest ? this.renderStrongest(s.strongest) : nothing}</div>`;\n\t}\n\n\t/** The tightest transit by orb: the one contact most likely to be felt, lifted out so it is not buried among seventy others. */\n\tprivate renderStrongest(s: NonNullable<TransitSummary['strongest']>) {\n\t\tconst transiting = display(s, 'planet1');\n\t\tconst natal = display(s, 'planet2');\n\t\treturn html`<div class=\"strongest\" part=\"details strongest\"><span class=\"label\">${this.t('Strongest')}</span> <span aria-hidden=\"true\" class=\"glyph\">${glyphFor(s.planet1, transiting)}</span>${this.t('Transiting')} ${transiting} <span class=\"nature-badge ${(s.interpretation ?? 'neutral').toLowerCase()}\">${display(s, 'type', formatAspectName(s))}</span> <span aria-hidden=\"true\" class=\"glyph\">${glyphFor(s.planet2, natal)}</span>${this.t('Natal')} ${natal} <span class=\"meta\">${s.isApplying ? this.t('Applying') : this.t('Separating')} · ${this.t('Orb')} ${formatNumber(this.effectiveLang(), s.orb, 2)}° · ${this.t('Strength')} ${formatNumber(this.effectiveLang(), s.strength, 0)}</span></div>`;\n\t}\n\n\t/**\n\t * Both rings as exact positions, so every degree the wheel abbreviates to a\n\t * glyph stays readable and nothing the response carries is dropped.\n\t *\n\t * @remarks\n\t * The House columns are where the response's house numbers land, because the\n\t * wheel has no sectors to put them in unless a cusp set is available. Both\n\t * columns are read against the NATAL cusps: the transiting one is the natal\n\t * house that body is currently passing through, which is the whole point of a\n\t * transit report and is why the two are not the same fact under one heading.\n\t * `showHouses` comes from the payload rather than the type, so a response\n\t * without them loses the columns instead of printing a column of blanks.\n\t */\n\tprivate renderPositions(natal: Body[], transit: Body[], showHouses: boolean) {\n\t\tif (natal.length === 0 && transit.length === 0) return nothing;\n\t\tconst natalBy = byName(natal);\n\t\tconst transitBy = byName(transit);\n\t\tconst rows = natal.length > 0 ? natal : transit;\n\t\tconst cell = (p: Body | undefined) =>\n\t\t\tp\n\t\t\t\t? html`${signGlyph(p.sign) ?? ''} ${degLabel(p.longitude)} ${display(p, 'sign')}${p.isRetrograde ? html`<span class=\"retro-badge\" aria-label=\"${this.t('retrograde')}\">℞</span>` : nothing}`\n\t\t\t\t: nothing;\n\t\tconst houseCell = (p: Body | undefined) =>\n\t\t\tshowHouses\n\t\t\t\t? html`<td>${p && Number.isFinite(p.house) ? p.house : nothing}</td>`\n\t\t\t\t: nothing;\n\t\t// tabindex + role: the House columns pushed this table past the card, so it\n\t\t// genuinely scrolls now, and a scrollable region with no focusable content\n\t\t// of its own is unreachable by keyboard. Same pattern as `hd-connection`,\n\t\t// except the name is borrowed from the caption rather than written again:\n\t\t// one translated sentence, and it cannot drift from what the table holds.\n\t\treturn html`<div class=\"scroll\" part=\"table\" tabindex=\"0\" role=\"region\" aria-labelledby=\"transit-positions\"><table><caption id=\"transit-positions\" class=\"roxy-sr-only\">${this.t('Every body with its natal position and its position on the transit date, each as a zodiac sign and a degree.')} ${showHouses ? this.t('Both house numbers are read against the natal house cusps.') : nothing}</caption><thead><tr><th scope=\"col\">${this.t('Body')}</th><th scope=\"col\"><span class=\"swatch swatch--natal\" aria-hidden=\"true\"></span>${this.t('Natal')}</th>${showHouses ? html`<th scope=\"col\">${this.t('Natal house')}</th>` : nothing}<th scope=\"col\"><span class=\"swatch swatch--transit\" aria-hidden=\"true\"></span>${this.t('Transiting')}</th>${showHouses ? html`<th scope=\"col\">${this.t('Transiting house')}</th>` : nothing}</tr></thead><tbody>${rows.map((p) => {\n\t\t\t\t\t\tconst key = capitalize(p.name);\n\t\t\t\t\t\tconst body = display(p, 'name');\n\t\t\t\t\t\treturn html`<tr><th scope=\"row\"><span aria-hidden=\"true\" class=\"glyph\">${glyphFor(p.name, body)}</span> ${body}</th><td>${cell(natalBy.get(key))}</td>${houseCell(natalBy.get(key))}<td>${cell(transitBy.get(key))}</td>${houseCell(transitBy.get(key))}</tr>`;\n\t\t\t\t\t})}</tbody></table></div>`;\n\t}\n\n\t/**\n\t * The written transit readings, routed through the base so one `hide-readings`\n\t * attribute drops every one of them from the markup. The aspect itself (both\n\t * bodies, the type, applying or separating, orb and strength) is data and\n\t * stays on the wheel, in the summary and in the positions table regardless.\n\t */\n\tprivate renderReadings(aspects: TransitAspect[]) {\n\t\tconst sections: InterpSection[] = aspects\n\t\t\t.filter((a) => a.transitInterpretation?.summary)\n\t\t\t.map((a) => {\n\t\t\t\tconst t = a.transitInterpretation as NonNullable<\n\t\t\t\t\tTransitAspect['transitInterpretation']\n\t\t\t\t>;\n\t\t\t\tconst transiting = display(a, 'planet1');\n\t\t\t\tconst natal = display(a, 'planet2');\n\t\t\t\treturn {\n\t\t\t\t\tlabel: `${glyphFor(a.planet1, transiting)} ${transiting} ${display(a, 'type', formatAspectName(a))} ${glyphFor(a.planet2, natal)} ${natal}`,\n\t\t\t\t\taside: `${this.t('Orb')} ${formatNumber(this.effectiveLang(), a.orb, 2)}° · ${this.t('Strength')} ${formatNumber(this.effectiveLang(), a.strength, 0)}`,\n\t\t\t\t\tbody: t.summary,\n\t\t\t\t\textra: html`${t.impact ? html`<p><strong>${this.t('Impact')}:</strong> ${t.impact}</p>` : nothing} ${t.timing ? html`<p><strong>${this.t('Timing')}:</strong> ${t.timing}</p>` : nothing} ${t.guidance ? html`<p><strong>${this.t('Guidance')}:</strong> ${t.guidance}</p>` : nothing} ${renderKeywordChips(t.keywords)}`,\n\t\t\t\t};\n\t\t\t});\n\t\treturn this.renderInterpretation(\n\t\t\tsections,\n\t\t\t'transit-aspect-readings',\n\t\t\t'Transit readings',\n\t\t);\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-transit-wheel': RoxyTransitWheel;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement, state } from 'lit/decorators.js';\nimport { planetGlyph, signGlyph } from '../tokens/index.js';\nimport type { TransitsResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { chevron, disclosureStyles } from '../utils/disclosure.js';\nimport {\n\tformatAspectName,\n\tformatDate,\n\tformatNumber,\n\tformatTime,\n} from '../utils/format.js';\nimport {\n\treadingDetailStyles,\n\trenderReadingDetail,\n} from '../utils/interp-accordion.js';\nimport { renderTablist, tablistStyles } from '../utils/tablist.js';\n\n/**\n * Transit positions and aspect table. Pass `data` from /astrology/transits.\n * When natalChart is included in the request, `data.transitAspects` and\n * `data.summary` are present and rendered automatically.\n */\n@customElement('roxy-transits-table')\nexport class RoxyTransitsTable extends RoxyDataElement<TransitsResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\ttablistStyles,\n\t\tdisclosureStyles,\n\t\treadingDetailStyles,\n\t\tcss`.wrap{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.head{justify-content:space-between;align-items:baseline;gap:var(--roxy-space-md,1rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.subtitle{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem);margin:0}.summary-pills{gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.pill{padding:2px var(--roxy-space-sm,.5rem);border-radius:var(--roxy-radius-full,9999px);font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);border:1px solid;align-items:center;gap:4px;display:inline-flex}.pill--muted{color:var(--roxy-fg,#0a0a0a);background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 60%, transparent)}.pill--success{color:var(--roxy-success-fg,#166534);background:color-mix(in srgb, var(--roxy-success,#16a34a) 10%, transparent)}.pill--danger{color:var(--roxy-danger-fg,#991b1b);background:color-mix(in srgb, var(--roxy-danger,#dc2626) 10%, transparent)}table{border-collapse:collapse;width:100%;font-size:var(--roxy-text-sm,.875rem)}th,td{padding:var(--roxy-space-sm,.5rem);border-bottom:1px solid var(--roxy-border,#e4e4e7);text-align:left}th{color:var(--roxy-muted,#71717a);font-weight:var(--roxy-weight-bold,600);text-transform:uppercase;font-size:var(--roxy-text-xs,.75rem);letter-spacing:.06em}.section-label{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.06em;font-weight:var(--roxy-weight-bold,600);margin:0 0 var(--roxy-space-xs,.25rem) 0}.glyph{margin-right:2px;font-size:1.1em;line-height:1}.planet-cell{white-space:nowrap;align-items:center;gap:4px;display:flex}.retro-badge{border-radius:var(--roxy-radius-sm,4px);background:color-mix(in srgb, var(--roxy-warning,#ea580c) 12%, transparent);color:var(--roxy-warning-fg,#9a3412);font-size:.7em;font-weight:var(--roxy-weight-bold,600);vertical-align:middle;margin-left:2px;padding:1px 4px;display:inline-block}.speed{font-variant-numeric:tabular-nums;color:var(--roxy-muted,#71717a);white-space:nowrap}.speed-arrow{font-size:.85em}td.num{font-variant-numeric:tabular-nums;color:var(--roxy-muted,#71717a)}.overflow-scroll{-webkit-overflow-scrolling:touch;min-width:0;overflow-x:auto}.aspect-card{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);margin-bottom:var(--roxy-space-xs,.25rem)}.aspect-card summary{cursor:pointer;color:var(--roxy-fg,#0a0a0a);flex-wrap:wrap;align-items:center;gap:.5em;font-weight:500;display:flex}.aspect-card summary .meta{color:var(--roxy-muted,#71717a);font-weight:400;font-size:var(--roxy-text-xs,.75rem);font-variant-numeric:tabular-nums;margin-left:auto}.aspect-card .interp-body{margin-top:var(--roxy-space-xs,.25rem);color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-sm,.875rem);line-height:1.45}.aspect-card .interp-body p{margin:0 0 var(--roxy-space-xs,.25rem)}.nature-badge{font-size:var(--roxy-text-xs,.75rem);border-radius:9999px;padding:1px 8px;font-weight:600;display:inline-block}.nature-badge.harmonious{background:color-mix(in srgb, var(--roxy-success,#16a34a) 12%, transparent);color:var(--roxy-success-fg,#166534)}.nature-badge.challenging{background:color-mix(in srgb, var(--roxy-danger,#dc2626) 12%, transparent);color:var(--roxy-danger-fg,#991b1b)}.nature-badge.neutral{background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 60%, transparent);color:var(--roxy-fg,#0a0a0a)}`,\n\t];\n\n\t/** Which panel is showing: planet positions or the transit-to-natal aspects. */\n\t@state()\n\tprivate tab: 'positions' | 'aspects' = 'positions';\n\n\tprotected renderData(d: TransitsResponse) {\n\t\tif (!d.transitPlanets?.length) return this.renderEmpty();\n\n\t\tconst {\n\t\t\ttransitDate,\n\t\t\ttransitTime,\n\t\t\ttransitPlanets,\n\t\t\ttransitAspects,\n\t\t\tsummary,\n\t\t} = d;\n\n\t\tconst dateStr = [\n\t\t\tformatDate(this.effectiveLang(), transitDate),\n\t\t\tformatTime(this.effectiveLang(), transitTime),\n\t\t]\n\t\t\t.filter(Boolean)\n\t\t\t.join(' ');\n\t\tconst aspectCount = transitAspects?.length ?? 0;\n\t\tconst tab = this.tab;\n\n\t\treturn html`<div class=\"wrap\" part=\"card\" aria-label=\"${this.t('Transits')}\"><div class=\"head\" part=\"header\"><h2 class=\"title\">${this.t('Transits')}</h2>${dateStr ? html`<p class=\"subtitle\">${dateStr}</p>` : nothing}</div>${\n\t\t\t\taspectCount > 0\n\t\t\t\t\t? html`${renderTablist({\n\t\t\t\t\t\t\titems: [\n\t\t\t\t\t\t\t\t{ id: 'positions', label: this.t('Positions') },\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tid: 'aspects',\n\t\t\t\t\t\t\t\t\tlabel: this.t('Aspects ({{count}})', { count: aspectCount }),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tactive: tab,\n\t\t\t\t\t\t\tonSelect: (v) => {\n\t\t\t\t\t\t\t\tthis.tab = v;\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tlabel: this.t('Transit views'),\n\t\t\t\t\t\t\tidPrefix: 'transits',\n\t\t\t\t\t\t\tcontrols: true,\n\t\t\t\t\t\t})}<div id=\"transits-panel-${tab}\" part=\"panel\" role=\"tabpanel\" aria-labelledby=\"transits-tab-${tab}\">${\n\t\t\t\t\t\t\t\ttab === 'positions'\n\t\t\t\t\t\t\t\t\t? html`<div class=\"overflow-scroll\">${this.renderPlanetsTable(transitPlanets)}</div>`\n\t\t\t\t\t\t\t\t\t: html`${summary ? this.renderSummaryPills(summary) : nothing}<div class=\"overflow-scroll\">${this.renderAspectsList(transitAspects ?? [])}</div>`\n\t\t\t\t\t\t\t}</div>`\n\t\t\t\t\t: html`<div class=\"overflow-scroll\">${this.renderPlanetsTable(transitPlanets)}</div>`\n\t\t\t}</div>`;\n\t}\n\n\tprivate renderSummaryPills(\n\t\tsummary: NonNullable<TransitsResponse['summary']>,\n\t) {\n\t\treturn html`<div class=\"summary-pills\" part=\"details\" role=\"region\" aria-label=\"${this.t('Aspect summary')}\"><span class=\"pill pill--muted\">${this.t('Total')}: ${summary.totalAspects} </span><span class=\"pill pill--success\">${this.t('Harmonious')}: ${summary.harmonious} </span><span class=\"pill pill--danger\">${this.t('Challenging')}: ${summary.challenging} </span><span class=\"pill pill--muted\">${this.t('Neutral')}: ${summary.neutral}</span></div>`;\n\t}\n\n\tprivate renderPlanetsTable(planets: TransitsResponse['transitPlanets']) {\n\t\treturn html`<table class=\"planets-table\" part=\"table\"><caption class=\"roxy-sr-only\">${this.t(\n\t\t\t\t\t'Transiting planets: each planet with its current sign, degree and daily speed.',\n\t\t\t\t)}</caption><thead><tr><th scope=\"col\">${this.t('Planet')}</th><th scope=\"col\">${this.t('Sign')}</th><th scope=\"col\">${this.t('Degree')}</th><th scope=\"col\">${this.t('Speed')}</th></tr></thead><tbody>${planets.map((p) => {\n\t\t\t\t\tconst pGlyph = planetGlyph(p.name) ?? '';\n\t\t\t\t\tconst sGlyph = signGlyph(p.sign) ?? '';\n\t\t\t\t\tconst speedArrow = p.speed >= 0 ? '↑' : '↓';\n\t\t\t\t\treturn html`<tr><td><div class=\"planet-cell\"><span class=\"glyph\" aria-hidden=\"true\">${pGlyph}</span> ${p.name} ${\n\t\t\t\t\t\t\t\t\tp.isRetrograde\n\t\t\t\t\t\t\t\t\t\t? html`<span class=\"retro-badge\" aria-label=\"${this.t('retrograde')}\">R</span>`\n\t\t\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t\t\t}</div></td><td><div class=\"planet-cell\"><span class=\"glyph\" aria-hidden=\"true\">${sGlyph}</span> ${p.sign}</div></td><td class=\"num\">${formatNumber(this.effectiveLang(), p.degree, 2)}</td><td class=\"speed\"><span class=\"speed-arrow\" aria-hidden=\"true\">${speedArrow}</span> ${formatNumber(this.effectiveLang(), Math.abs(p.speed), 2)}${this.t('°/day')}</td></tr>`;\n\t\t\t\t})}</tbody></table>`;\n\t}\n\n\tprivate renderAspectsList(\n\t\taspects: NonNullable<TransitsResponse['transitAspects']>,\n\t) {\n\t\treturn html`<div part=\"section aspects\" role=\"list\" aria-label=\"${this.t('Transit aspects')}\">${aspects.map((a, idx) => {\n\t\t\t\tconst tGlyph = planetGlyph(a.transitPlanet) ?? '';\n\t\t\t\tconst nGlyph = planetGlyph(a.natalPlanet) ?? '';\n\t\t\t\tconst nature = (a.nature ?? 'neutral').toLowerCase();\n\t\t\t\tconst interp = a.interpretation;\n\t\t\t\tconst type = formatAspectName(a);\n\t\t\t\tconst status = a.isApplying ? this.t('Applying') : this.t('Separating');\n\t\t\t\treturn html`<details class=\"aspect-card\" part=\"reading\" role=\"listitem\" name=\"transit-aspects\" ?open=\"${idx === 0}\"><summary><span aria-hidden=\"true\">${tGlyph}</span> ${a.transitPlanet} <span class=\"nature-badge ${nature}\">${type}</span> <span aria-hidden=\"true\">${nGlyph}</span> ${a.natalPlanet} <span class=\"meta\">${this.t('{{status}} · orb {{orb}}° · str {{strength}}', {\n\t\t\t\t\t\t\t\tstatus,\n\t\t\t\t\t\t\t\torb: formatNumber(this.effectiveLang(), a.orb, 2),\n\t\t\t\t\t\t\t\tstrength: formatNumber(this.effectiveLang(), a.strength, 1),\n\t\t\t\t\t\t\t})} </span>${chevron()}</summary><div class=\"interp-body\">${renderReadingDetail(interp, this.translator)}</div></details>`;\n\t\t\t})}</div>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-transits-table': RoxyTransitsTable;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport type { ChromeString } from '../i18n/chrome-strings.js';\nimport { signGlyph } from '../tokens/index.js';\nimport type { UpagrahaResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { formatSignPosition } from '../utils/degree.js';\nimport { formatNumber } from '../utils/format.js';\nimport { frameCaptionStyles, renderFrameCaption } from '../utils/frame.js';\n\ntype Upagraha = UpagrahaResponse['timeBased'][number];\n\n/**\n * The two groups the response ships, each with the line a reader needs to know what it is looking at. Time-based positions depend on birth time, place and weekday; the Dhuma group is pure arithmetic from the Sun.\n */\nconst GROUPS: ReadonlyArray<{\n\tkey: 'timeBased' | 'sunBased';\n\ttitleSource: ChromeString;\n\tnoteSource: ChromeString;\n}> = [\n\t{\n\t\tkey: 'timeBased',\n\t\ttitleSource: 'Time based',\n\t\tnoteSource:\n\t\t\t'From the eightfold division of the day or night, so these depend on the birth time, the place and the weekday.',\n\t},\n\t{\n\t\tkey: 'sunBased',\n\t\ttitleSource: 'Sun based',\n\t\tnoteSource:\n\t\t\t'The Dhuma group, derived by fixed arc from the Sun. Dhuma is the Sun plus 133 degrees 20 minutes, and each of the rest follows from the one before it.',\n\t},\n];\n\n/**\n * Upagraha positions. Renders /vedic-astrology/upagraha: the eleven upagrahas (shadowy sub-planets) in the two groups the tradition separates them into, each with its sidereal longitude, rashi, degree in sign, and nakshatra with pada.\n *\n * @remarks\n * Gulika and Mandi are rendered as the two SEPARATE points they are. Several implementations treat the names as synonyms and print one row; the two are computed from different moments of the Saturn segment and sit a few degrees apart, which is exactly the difference a practitioner is checking when they open this table.\n *\n * Row order is the response order and is not re-sorted: the API returns each group in its classical derivation order, which is the order the positions are computed and taught in.\n */\n@customElement('roxy-upagraha-table')\nexport class RoxyUpagrahaTable extends RoxyDataElement<UpagrahaResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tframeCaptionStyles,\n\t\tcss`.wrap{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);background:var(--roxy-surface,#fff);box-shadow:var(--roxy-shadow-sm);overflow:hidden}.head{padding:var(--roxy-space-md,1rem);border-bottom:1px solid var(--roxy-border,#e4e4e7)}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.group-head{padding:var(--roxy-space-md,1rem) var(--roxy-space-md,1rem) 0;gap:var(--roxy-space-xs,.25rem);display:grid}.group-title{font-size:var(--roxy-text-sm,.875rem);font-weight:var(--roxy-weight-bold,600);margin:0}.group-note{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);line-height:var(--roxy-leading-normal,1.5);margin:0}.scroll{min-width:0;overflow-x:auto}table{border-collapse:collapse;width:100%;font-size:var(--roxy-text-sm,.875rem);min-width:520px}thead{background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 20%, transparent)}th,td{padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);text-align:left;white-space:nowrap}th{color:var(--roxy-muted,#71717a);font-weight:var(--roxy-weight-bold,600);text-transform:uppercase;font-size:var(--roxy-text-xs,.75rem);letter-spacing:.04em}tbody tr{border-top:1px solid var(--roxy-border,#e4e4e7)}td.name{font-weight:var(--roxy-weight-bold,600);color:var(--roxy-fg,#0a0a0a)}.glyph{color:var(--roxy-muted,#71717a);margin-right:.4em}.num{font-variant-numeric:tabular-nums}`,\n\t];\n\n\tprotected renderData(d: UpagrahaResponse) {\n\t\tconst groups = GROUPS.filter((g) => d[g.key]?.length);\n\t\tif (!groups.length) return this.renderEmpty();\n\n\t\treturn html`<div class=\"wrap\" part=\"card\" aria-label=\"${this.t('Upagraha positions')}\"><header class=\"head\" part=\"header\"><h2 class=\"title\">${this.t('Upagrahas')}</h2>${renderFrameCaption(this.effectiveLang(), d.frame, this.translator)}</header>${groups.map((g) => this.renderGroup(g, d[g.key]))}</div>`;\n\t}\n\n\tprivate renderGroup(group: (typeof GROUPS)[number], rows: Upagraha[]) {\n\t\t// The part name distinguishes the two groups, so it must be a static\n\t\t// literal per branch rather than interpolated: a computed `part=${...}`\n\t\t// is invisible to the catalog scanner.\n\t\tconst body = html`<div class=\"group-head\"><h3 class=\"group-title\">${this.t(group.titleSource)}</h3><p class=\"group-note\">${this.t(group.noteSource)}</p></div><div class=\"scroll\" part=\"table\" tabindex=\"0\"><table role=\"table\"><caption class=\"roxy-sr-only\">${this.t(\n\t\t\t\t\t\t\t'{{group}} upagrahas: each sub-planet with its rashi, degree in sign, sidereal longitude, and nakshatra with pada.',\n\t\t\t\t\t\t\t{ group: this.t(group.titleSource) },\n\t\t\t\t\t\t)}</caption><thead><tr><th scope=\"col\">${this.t('Upagraha')}</th><th scope=\"col\">${this.t('Rashi')}</th><th scope=\"col\">${this.t('Degree')}</th><th scope=\"col\">${this.t('Longitude')}</th><th scope=\"col\">${this.t('Nakshatra')}</th><th scope=\"col\">${this.t('Pada')}</th></tr></thead><tbody>${rows.map((u) => {\n\t\t\t\t\t\t\tconst sGlyph = signGlyph(u.rashi) ?? '';\n\t\t\t\t\t\t\treturn html`<tr><td class=\"name\">${u.name}</td><td>${sGlyph ? html`<span class=\"glyph\">${sGlyph}</span>` : nothing}${u.rashi ?? ''}</td><td class=\"num\">${typeof u.longitude === 'number' ? formatSignPosition(u.longitude) : ''}</td><td class=\"num\">${formatNumber(this.effectiveLang(), u.longitude, 2)}</td><td>${u.nakshatra ?? ''}</td><td class=\"num\">${u.nakshatraPada ?? ''}</td></tr>`;\n\t\t\t\t\t\t})}</tbody></table></div>`;\n\t\treturn group.key === 'timeBased'\n\t\t\t? html`<section part=\"section time-based\">${body}</section>`\n\t\t\t: html`<section part=\"section sun-based\">${body}</section>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-upagraha-table': RoxyUpagrahaTable;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport type { CalculateDrishtiResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { formatSignPosition } from '../utils/degree.js';\nimport { formatDate, formatNumber, formatTime } from '../utils/format.js';\n\n/**\n * Vedic graha drishti (planetary aspects) table. Renders /vedic-astrology/aspects: which planet casts an aspect on which, by special Vedic rules (every graha aspects the 7th; Mars the 4th and 8th, Jupiter the 5th and 9th, Saturn the 3rd and 10th). Mutual aspects (two planets aspecting each other) are surfaced first as they are the strongest sambandha. Each row shows the aspecting planet, the aspect kind, the aspected planet, its strength and orb.\n *\n * @remarks\n * `datetime` is the chart time, the wall clock of the request, NOT UTC: hold the date and time fixed and vary `timezone` and every longitude in the response moves, while `datetime` does not. It is labelled \"Chart time\" for that reason. The spec description calling it UTC is wrong and is tracked upstream. The sidereal positions the aspects were derived from are shown above the table so a reader can check the drishti against the actual placements.\n */\n@customElement('roxy-vedic-aspects')\nexport class RoxyVedicAspects extends RoxyDataElement<CalculateDrishtiResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.wrap{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-lg,1.5rem);box-shadow:var(--roxy-shadow-sm);gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.head{justify-content:space-between;align-items:baseline;gap:var(--roxy-space-md,1rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.subtitle{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem);margin:0}.section-label{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);text-transform:uppercase;letter-spacing:.06em;font-weight:var(--roxy-weight-bold,600);margin:0 0 var(--roxy-space-xs,.25rem) 0}.mutual{gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.mutual-pill{border-radius:var(--roxy-radius-full,9999px);background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 14%, transparent);color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-sm,.875rem);font-weight:var(--roxy-weight-bold,600);align-items:center;gap:.35rem;padding:2px 10px;display:inline-flex}.mutual-pill .rel{font-size:var(--roxy-text-xs,.75rem);font-weight:400}.position{border-radius:var(--roxy-radius-full,9999px);background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 55%, transparent);font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-fg,#0a0a0a);font-variant-numeric:tabular-nums;align-items:baseline;gap:.35rem;padding:2px 10px;display:inline-flex}.position strong{color:var(--roxy-fg,#0a0a0a);font-size:var(--roxy-text-sm,.875rem)}.overflow-scroll{-webkit-overflow-scrolling:touch;min-width:0;overflow-x:auto}table{border-collapse:collapse;width:100%;font-size:var(--roxy-text-sm,.875rem)}th,td{padding:var(--roxy-space-sm,.5rem);border-bottom:1px solid var(--roxy-border,#e4e4e7);text-align:left;white-space:nowrap}th{color:var(--roxy-muted,#71717a);font-weight:var(--roxy-weight-bold,600);text-transform:uppercase;font-size:var(--roxy-text-xs,.75rem);letter-spacing:.06em}tbody tr:last-child td{border-bottom:none}.kind{border-radius:var(--roxy-radius-full,9999px);background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 55%, transparent);font-size:var(--roxy-text-xs,.75rem);padding:1px 8px;font-weight:600;display:inline-block}.num{font-variant-numeric:tabular-nums;color:var(--roxy-muted,#71717a)}.bar{background:var(--roxy-accent,#f59e0b);vertical-align:middle;border-radius:3px;height:6px;margin-right:6px;display:inline-block}`,\n\t];\n\n\tprotected renderData(d: CalculateDrishtiResponse) {\n\t\tconst aspects = d.aspects ?? [];\n\t\tconst mutual = d.mutualAspects ?? [];\n\t\tconst planets = d.planets ?? [];\n\t\tif (aspects.length === 0 && mutual.length === 0) return this.renderEmpty();\n\t\tconst when = d.datetime\n\t\t\t? `${formatDate(this.effectiveLang(), d.datetime)}, ${formatTime(this.effectiveLang(), d.datetime)}`\n\t\t\t: '';\n\n\t\treturn html`<div class=\"wrap\" part=\"card\" aria-label=\"${this.t('Vedic aspects')}\"><div class=\"head\" part=\"header\"><h2 class=\"title\">${this.t('Vedic aspects')}</h2>${when ? html`<p class=\"subtitle\">${this.t('Chart time {{when}}', { when })}</p>` : nothing}</div>${\n\t\t\t\tplanets.length > 0\n\t\t\t\t\t? html`<div part=\"section positions\"><p class=\"section-label\">${this.t('Sidereal positions')}</p><div class=\"mutual\">${planets.map(\n\t\t\t\t\t\t\t\t(p) => html`<span class=\"position\"><strong>${p.name}</strong> ${typeof p.longitude === 'number' ? formatSignPosition(p.longitude) : (p.sign ?? '')}</span>`,\n\t\t\t\t\t\t\t)}</div></div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\tmutual.length > 0\n\t\t\t\t\t? html`<div part=\"section mutual-aspects\"><p class=\"section-label\">${this.t('Mutual aspects')}</p><div class=\"mutual\">${mutual.map(\n\t\t\t\t\t\t\t\t(m) => html`<span class=\"mutual-pill\">${m.planet1} ⟷ ${m.planet2} ${m.aspectType ? html`<span class=\"rel\">${m.aspectType}</span>` : nothing}</span>`,\n\t\t\t\t\t\t\t)}</div></div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\taspects.length > 0\n\t\t\t\t\t? html`<div class=\"overflow-scroll\" part=\"table\"><table><caption class=\"roxy-sr-only\">${this.t('Vedic planetary aspects: aspecting planet, aspect type, aspected planet, strength and orb.')}</caption><thead><tr><th scope=\"col\">${this.t('From')}</th><th scope=\"col\">${this.t('Aspect')}</th><th scope=\"col\">${this.t('To')}</th><th scope=\"col\">${this.t('Strength')}</th><th scope=\"col\">${this.t('Orb')}</th></tr></thead><tbody>${aspects.map(\n\t\t\t\t\t\t\t\t\t(a) => html`<tr><td>${a.aspectingPlanet}</td><td><span class=\"kind\">${a.aspectType}</span></td><td>${a.aspectedPlanet}</td><td>${typeof a.strength === 'number' ? html`<span class=\"bar\" style=\"width:${Math.max(4, Math.min(100, a.strength) * 0.4)}px\"></span><span class=\"num\">${formatNumber(this.effectiveLang(), a.strength, 0)}</span>` : nothing}</td><td class=\"num\">${typeof a.orb === 'number' ? `${formatNumber(this.effectiveLang(), a.orb, 2)}°` : ''}</td></tr>`,\n\t\t\t\t\t\t\t\t)}</tbody></table></div>`\n\t\t\t\t\t: nothing\n\t\t\t}</div>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-vedic-aspects': RoxyVedicAspects;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport type { ChromeString } from '../i18n/chrome-strings.js';\nimport { planetGlyph } from '../tokens/index.js';\nimport type { GetVedicDailyReadingResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { formatDateTime, formatNumber } from '../utils/format.js';\nimport { frameCaptionStyles, renderFrameProvenance } from '../utils/frame.js';\n\ntype Daily = GetVedicDailyReadingResponse;\ntype Graha = Daily['grahas'][number];\ntype Limb = Daily['panchanga']['tithi'];\ntype Finance = NonNullable<Daily['areas']['finance']>;\ntype FinanceLink = NonNullable<Finance['drivers']>[number];\n\n/**\n * The six graha states in the order a reader scans them, each beside the English source its label is looked up by.\n *\n * @remarks\n * A record keyed by the response enum rather than a string built at the call site: the key is checked against the catalogue by the compiler, and a state added upstream fails to typecheck here instead of rendering its raw wire value. The ORDER is fixed here too, so the summary strip reads the same on a day where one state has no grahas.\n */\nconst STATE_LABEL: Record<Graha['state'], ChromeString> = {\n\tfavourable: 'Favourable',\n\tunderdelivered: 'Underdelivered',\n\tobstructed: 'Obstructed',\n\tvoid: 'Void',\n\taggravated: 'Aggravated',\n\tunfavourable: 'Unfavourable',\n};\nconst STATES = Object.keys(STATE_LABEL) as Array<Graha['state']>;\n\n/** Kakshas per sign. Fixed by the definition: each sign divides into eight stretches of 3 degrees 45 minutes. */\nconst KAKSHA_COUNT = 8;\n\n/** The four verdict bands, shared by the day and by each area score. */\nconst BAND_LABEL: Record<NonNullable<Daily['verdict']>, ChromeString> = {\n\t'very-strong': 'Very strong',\n\tstrong: 'Strong',\n\tmoderate: 'Moderate',\n\tweak: 'Weak',\n};\n\n/** The three dasha levels the ladder can carry. */\nconst LEVEL_LABEL: Record<Daily['dasha'][number]['level'], ChromeString> = {\n\tmahadasha: 'Mahadasha',\n\tantardasha: 'Antardasha',\n\tpratyantardasha: 'Pratyantardasha',\n};\n\n/**\n * Vedic daily reading. Renders POST /vedic-astrology/daily: one native, one day, one call.\n *\n * @remarks\n * **The score is a rare-high scale and is deliberately not drawn as a gauge.** It is the share of evaluated grahas that support the native, and the classical rules behind it cancel far more often than they deliver, so an ordinary day sits low. A ring, a bar or a percentage badge would read every ordinary day as a bad one, so the verdict band is the headline and the number supports it.\n *\n * **Every window is an ARRAY because the Moon can change nakshatra or rashi inside the panchanga day.** Tara and Chandrabala are rendered as one chip per window with its own validity, never as a single current value, and `ashtamaChandra` stays its own mark rather than folding into the favourable flag.\n *\n * **Three frames, not one.** The response is computed across a natal, a transit and a KP frame, two ayanamsas over three instants, and each names the sections it governs. All three are printed for the same reason one is printed on every other Vedic card: a chart that does not say which sky produced it cannot be reconciled against any other.\n *\n * House wording comes from `houseThemes` on the response, never from a table here, so the words a reader sees match the lens the call was made with.\n */\n@customElement('roxy-vedic-daily')\nexport class RoxyVedicDaily extends RoxyDataElement<Daily> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tframeCaptionStyles,\n\t\tcss`.wrap{background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);box-shadow:var(--roxy-shadow-sm);display:grid;overflow:hidden}.head{padding:var(--roxy-space-md,1rem);border-bottom:1px solid var(--roxy-border,#e4e4e7);gap:var(--roxy-space-sm,.5rem);display:grid}.title-row{justify-content:space-between;align-items:baseline;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.when{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem)}.verdict-row{align-items:center;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.verdict{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);border-radius:var(--roxy-radius-full,9999px);background:color-mix(in srgb, var(--roxy-muted,#71717a) 14%, transparent);padding:.15rem .6rem}.verdict.very-strong,.verdict.strong{background:color-mix(in srgb, var(--roxy-success,#16a34a) 16%, transparent);color:var(--roxy-success-fg,#14532d)}.verdict.moderate{background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 18%, transparent);color:var(--roxy-accent-ink,#b45309)}.verdict.weak{background:color-mix(in srgb, var(--roxy-muted,#71717a) 16%, transparent);color:var(--roxy-secondary,#475569)}.evidence{color:var(--roxy-secondary,#475569);font-size:var(--roxy-text-sm,.875rem);font-variant-numeric:tabular-nums}.tally{flex-wrap:wrap;gap:.35rem;display:flex}.pill{font-size:var(--roxy-text-xs,.75rem);border-radius:var(--roxy-radius-full,9999px);border:1px solid var(--roxy-border,#e4e4e7);color:var(--roxy-secondary,#475569);font-variant-numeric:tabular-nums;padding:1px 8px}.block{padding:var(--roxy-space-md,1rem);border-top:1px solid var(--roxy-border,#e4e4e7);gap:var(--roxy-space-sm,.5rem);display:grid}.block h3{font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);text-transform:uppercase;letter-spacing:.06em;color:var(--roxy-muted,#71717a);margin:0}.facts{gap:var(--roxy-space-sm,.5rem);grid-template-columns:repeat(auto-fit,minmax(7.5rem,1fr));margin:0;display:grid}.fact{gap:2px;display:grid}.fact dt{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);text-transform:uppercase;letter-spacing:.05em}.fact dd{font-size:var(--roxy-text-sm,.875rem);margin:0}.fact .until{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);display:block}.scroll{-webkit-overflow-scrolling:touch;overflow-x:auto}table{border-collapse:collapse;width:100%;font-size:var(--roxy-text-sm,.875rem)}th,td{text-align:left;border-bottom:1px solid var(--roxy-border,#e4e4e7);white-space:nowrap;padding:.35rem .5rem}th{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);text-transform:uppercase;letter-spacing:.05em;font-weight:var(--roxy-weight-bold,600)}td.num{font-variant-numeric:tabular-nums}.glyph{color:var(--roxy-muted,#71717a);margin-right:.35em}.state{font-size:var(--roxy-text-xs,.75rem);border-radius:var(--roxy-radius-full,9999px);background:color-mix(in srgb, var(--roxy-muted,#71717a) 14%, transparent);cursor:help;padding:1px 8px}.state.favourable{background:color-mix(in srgb, var(--roxy-success,#16a34a) 16%, transparent);color:var(--roxy-success-fg,#14532d)}.state.underdelivered,.state.obstructed,.state.void{background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 16%, transparent);color:var(--roxy-accent-ink,#b45309)}.state.aggravated,.state.unfavourable{background:color-mix(in srgb, var(--roxy-danger,#dc2626) 14%, transparent);color:var(--roxy-danger-fg,#991b1b)}.kaksha{cursor:help;align-items:center;gap:.35rem;display:inline-flex}.dot{background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 90%, transparent);border-radius:50%;width:7px;height:7px}.dot.yes{background:var(--roxy-success,#16a34a)}.dot.no{background:var(--roxy-warning,#f59e0b)}.chips{flex-wrap:wrap;gap:.35rem;display:flex}.window{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);font-size:var(--roxy-text-sm,.875rem);gap:1px;padding:.3rem .6rem;display:grid}.window small{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);font-variant-numeric:tabular-nums}.window.yes{border-color:color-mix(in srgb, var(--roxy-success,#16a34a) 45%, transparent)}.window.no{border-color:color-mix(in srgb, var(--roxy-danger,#dc2626) 35%, transparent)}.mark{color:var(--roxy-danger-fg,#991b1b)}.lists{gap:var(--roxy-space-md,1rem);grid-template-columns:repeat(auto-fit,minmax(12rem,1fr));display:grid}.lists ul{font-size:var(--roxy-text-sm,.875rem);line-height:var(--roxy-leading-normal,1.5);margin:0;padding-left:1.1rem}.note{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-xs,.75rem);line-height:var(--roxy-leading-normal,1.5);margin:0}`,\n\t];\n\n\tprotected renderData(d: Daily) {\n\t\tconst grahas = d.grahas ?? [];\n\t\tif (!grahas.length) return this.renderEmpty();\n\n\t\treturn html`<div class=\"wrap\" part=\"card\" aria-label=\"${this.t('Vedic daily reading')}\">${this.renderHead(d)} ${this.renderPanchanga(d)} ${this.renderGrahas(grahas, d.houseThemes)} ${this.renderWindows(d)} ${this.renderFinance(d)} ${this.renderDasha(d)} ${this.renderProvenance(d)}</div>`;\n\t}\n\n\t/** Verdict band, the count behind it, and the per-state tally. */\n\tprivate renderHead(d: Daily) {\n\t\tconst counts = new Map(\n\t\t\t(d.tally ?? []).map((t) => [t.state as string, t.count]),\n\t\t);\n\t\tconst supportive = counts.get('favourable') ?? 0;\n\t\treturn html`<header class=\"head\" part=\"header\"><div class=\"title-row\"><h2 class=\"title\">${this.t('Vedic daily')}</h2><span class=\"when\">${formatDateTime(this.effectiveLang(), d.dayStart)} — ${formatDateTime(this.effectiveLang(), d.dayEnd)}</span></div><div class=\"verdict-row\"><span class=\"verdict ${d.verdict}\">${this.t(BAND_LABEL[d.verdict])}</span> <span class=\"evidence\">${this.t('{{supportive}} of {{evaluated}} grahas support this day', {\n\t\t\t\t\t\tsupportive,\n\t\t\t\t\t\tevaluated: d.evaluated,\n\t\t\t\t\t})}</span></div>${\n\t\t\t\tcounts.size\n\t\t\t\t\t? html`<div class=\"tally\" part=\"legend\">${STATES.filter((s) => counts.get(s)).map(\n\t\t\t\t\t\t\t(s) =>\n\t\t\t\t\t\t\t\thtml`<span class=\"pill\">${this.t(STATE_LABEL[s])} ${counts.get(s)}</span>`,\n\t\t\t\t\t\t)}</div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\td.subject\n\t\t\t\t\t? html`<p class=\"note\">${this.t(\n\t\t\t\t\t\t\t'Born with the Moon in {{rashi}}, nakshatra {{nakshatra}}',\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\trashi: d.subject.janmaRashi,\n\t\t\t\t\t\t\t\tnakshatra: d.subject.janmaNakshatra,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t)}</p>`\n\t\t\t\t\t: nothing\n\t\t\t} ${(d.degraded ?? []).map(\n\t\t\t\t(x) =>\n\t\t\t\t\thtml`<p class=\"note\">${this.t('{{component}} unavailable: {{reason}}', { component: x.component, reason: x.reason })}</p>`,\n\t\t\t)}</header>`;\n\t}\n\n\t/** The five limbs, each with the instant it gives way to the next. */\n\tprivate renderPanchanga(d: Daily) {\n\t\tconst p = d.panchanga;\n\t\tif (!p) return nothing;\n\t\tconst limb = (label: string, l: Limb | undefined) =>\n\t\t\tl\n\t\t\t\t? html`<div class=\"fact\"><dt>${label}</dt><dd>${l.number ? `${l.number}. ` : ''}${l.name} ${l.validTo ? html`<span class=\"until\">${this.t('until {{time}}', { time: formatDateTime(this.effectiveLang(), l.validTo) })}</span>` : nothing}</dd></div>`\n\t\t\t\t: nothing;\n\t\treturn html`<section class=\"block\" part=\"section panchanga\"><h3>${this.t('Panchang')}</h3><dl class=\"facts\" part=\"details\"><div class=\"fact\"><dt>${this.t('Vara')}</dt><dd>${p.vara}</dd></div><div class=\"fact\"><dt>${this.t('Paksha')}</dt><dd>${p.paksha}</dd></div>${limb(this.t('Tithi'), p.tithi)} ${limb(this.t('Nakshatra'), p.nakshatra)} ${limb(this.t('Yoga'), p.yoga)} ${limb(this.t('Karana'), p.karana)}</dl></section>`;\n\t}\n\n\t/** The words this response gives a house. Read from `houseThemes` rather than from a table here, so the wording follows the lens the call was made with, and joined for display because it arrives as a list. */\n\tprivate theme(themes: Daily['houseThemes'], house: number): string {\n\t\treturn themes?.[String(house)]?.join(', ') ?? '';\n\t}\n\n\t/** The nine grahas, one row each, with the classical rule that produced the state. */\n\tprivate renderGrahas(grahas: Graha[], themes: Daily['houseThemes']) {\n\t\treturn html`<section class=\"block\" part=\"section grahas\"><h3>${this.t('Grahas today')}</h3><div class=\"scroll\"><table part=\"table\"><caption class=\"roxy-sr-only\">${this.t('Each transiting graha with its sign, the house it occupies from the natal Moon, its bindus, its kaksha and the state the classical rules give it.')}</caption><thead><tr><th scope=\"col\">${this.t('Graha')}</th><th scope=\"col\">${this.t('Sign')}</th><th scope=\"col\">${this.t('House')}</th><th scope=\"col\">${this.t('Bindus')}</th><th scope=\"col\">${this.t('Kaksha')}</th><th scope=\"col\">${this.t('State')}</th></tr></thead><tbody>${grahas.map((g) => {\n\t\t\t\t\t\t\tconst glyph = planetGlyph(g.graha) ?? '';\n\t\t\t\t\t\t\tconst theme = this.theme(themes, g.houseFromMoon);\n\t\t\t\t\t\t\treturn html`<tr><td>${glyph ? html`<span class=\"glyph\">${glyph}</span>` : nothing}${g.graha}</td><td>${g.sign}</td><td class=\"num\" title=\"${theme}\">${g.houseFromMoon}</td><td class=\"num\">${typeof g.binduCount === 'number' ? g.binduCount : ''}</td><td class=\"num\">${this.renderKaksha(g.kaksha)}</td><td><span class=\"state ${g.state}\" title=\"${g.stateSource ?? ''}\">${this.t(STATE_LABEL[g.state])}</span></td></tr>`;\n\t\t\t\t\t\t})}</tbody></table></div></section>`;\n\t}\n\n\t/**\n\t * The kaksha a graha currently occupies, as its position within the sign.\n\t *\n\t * @remarks\n\t * `bindu` has three states and the third is not a negative: `null` means the graha has no Bhinnashtakavarga to read, which is Rahu and Ketu, so it takes the neutral mark and the sentence stops rather than reporting an unfavourable stretch nobody calculated.\n\t */\n\tprivate renderKaksha(k: Graha['kaksha']) {\n\t\tif (!k || typeof k.number !== 'number') return nothing;\n\t\tconst verdict = k.bindu === true ? 'yes' : k.bindu === false ? 'no' : '';\n\t\tconst gave =\n\t\t\tk.bindu === true\n\t\t\t\t? this.t('this kaksha lord gave bindu')\n\t\t\t\t: k.bindu === false\n\t\t\t\t\t? this.t('this kaksha lord gave no bindu')\n\t\t\t\t\t: '';\n\t\tconst head = this.t(\n\t\t\t'Kaksha {{n}} of {{total}}, ruled by {{graha}}, spanning {{start}}° to {{end}}° of the sign',\n\t\t\t{\n\t\t\t\tn: k.number,\n\t\t\t\ttotal: KAKSHA_COUNT,\n\t\t\t\tgraha: k.lord,\n\t\t\t\tstart: formatNumber(this.effectiveLang(), k.startDegree, 2),\n\t\t\t\tend: formatNumber(this.effectiveLang(), k.endDegree, 2),\n\t\t\t},\n\t\t);\n\t\treturn html`<span class=\"kaksha\" title=\"${gave ? `${head} · ${gave}` : head}\">${k.number}/${KAKSHA_COUNT} <span class=\"dot ${verdict}\" aria-hidden=\"true\"></span></span>`;\n\t}\n\n\t/** Tara and Chandrabala, one chip per validity window. */\n\tprivate renderWindows(d: Daily) {\n\t\tconst tara = d.tara ?? [];\n\t\tconst bala = d.chandrabala ?? [];\n\t\tif (!tara.length && !bala.length) return nothing;\n\t\tconst span = (from?: string, to?: string) =>\n\t\t\thtml`<small>${formatDateTime(this.effectiveLang(), from)} — ${formatDateTime(this.effectiveLang(), to)}</small>`;\n\t\treturn html`<section class=\"block\" part=\"section windows\"><h3>${this.t('Tara and Chandrabala')}</h3>${\n\t\t\t\ttara.length\n\t\t\t\t\t? html`<div class=\"chips\">${tara.map(\n\t\t\t\t\t\t\t(t) => html`<span class=\"window\"><span>${t.number}. ${t.name}${t.quality ? ` · ${t.quality}` : ''}</span> ${span(t.validFrom, t.validTo)}</span>`,\n\t\t\t\t\t\t)}</div>`\n\t\t\t\t\t: nothing\n\t\t\t} ${\n\t\t\t\tbala.length\n\t\t\t\t\t? html`<div class=\"chips\">${bala.map(\n\t\t\t\t\t\t\t(b) => html`<span class=\"window ${b.favourable ? 'yes' : 'no'}\"><span>${this.t('Moon in {{sign}}, house {{n}}', { sign: b.moonSign, n: b.houseFromMoon })} ${b.ashtamaChandra ? html`<span class=\"mark\">· ${this.t('Ashtama Chandra')}</span>` : nothing} </span>${span(b.validFrom, b.validTo)}</span>`,\n\t\t\t\t\t\t)}</div>`\n\t\t\t\t\t: nothing\n\t\t\t}</section>`;\n\t}\n\n\t/** The finance area, when the day could be scored for it. */\n\tprivate renderFinance(d: Daily) {\n\t\tconst f = d.areas?.finance;\n\t\tif (!f) return nothing;\n\t\tconst themes = d.houseThemes;\n\t\t// A connection reads as the lord, the house it reaches in the words the\n\t\t// response supplied, how strong that reach is on the KP grading, and which\n\t\t// running levels the lord holds.\n\t\tconst line = (c: FinanceLink) => {\n\t\t\tconst theme = this.theme(themes, c.house);\n\t\t\treturn html`<li>${c.graha} · ${this.t('House {{n}}', { n: c.house })}${theme ? ` (${theme})` : ''} · ${this.t('level {{level}} {{grade}}', { level: c.level, grade: c.grade })} ${c.dashaLevels?.length ? html`<span class=\"note\">${c.dashaLevels.map((l: NonNullable<FinanceLink['dashaLevels']>[number]) => this.t(LEVEL_LABEL[l])).join(', ')}</span>` : nothing}</li>`;\n\t\t};\n\t\treturn html`<section class=\"block\" part=\"section finance\"><h3>${this.t('Finance')}</h3><div class=\"verdict-row\"><span class=\"verdict ${f.band ?? ''}\">${f.band ? this.t(BAND_LABEL[f.band]) : ''}</span> ${\n\t\t\t\t\ttypeof f.positive === 'number' && typeof f.negative === 'number'\n\t\t\t\t\t\t? html`<span class=\"evidence\">${this.t('{{positive}} positive against {{negative}} negative', {\n\t\t\t\t\t\t\t\tpositive: f.positive,\n\t\t\t\t\t\t\t\tnegative: f.negative,\n\t\t\t\t\t\t\t})}</span>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</div><div class=\"lists\">${\n\t\t\t\t\tf.drivers?.length\n\t\t\t\t\t\t? html`<div><h3>${this.t('Drivers')}</h3><ul>${f.drivers.map(line)}</ul></div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t} ${\n\t\t\t\t\tf.cautions?.length\n\t\t\t\t\t\t? html`<div><h3>${this.t('Cautions')}</h3><ul>${f.cautions.map(line)}</ul></div>`\n\t\t\t\t\t\t: nothing\n\t\t\t\t}</div></section>`;\n\t}\n\n\t/** The dasha ladder running under the day. */\n\tprivate renderDasha(d: Daily) {\n\t\tconst rows = d.dasha ?? [];\n\t\tif (!rows.length) return nothing;\n\t\treturn html`<section class=\"block\" part=\"section dasha\"><h3>${this.t('Dasha')}</h3><div class=\"chips\">${rows.map(\n\t\t\t\t\t(r) => html`<span class=\"window\"><span>${this.t(LEVEL_LABEL[r.level])} · ${r.lord}</span> <small>${formatDateTime(this.effectiveLang(), r.startDate)} — ${formatDateTime(this.effectiveLang(), r.endDate)}</small></span>`,\n\t\t\t\t)}</div></section>`;\n\t}\n\n\t/** All three frames, each naming what it governs. */\n\tprivate renderProvenance(d: Daily) {\n\t\tif (!d.frames) return nothing;\n\t\treturn html`<section class=\"block\" part=\"section frame\">${renderFrameProvenance(this.effectiveLang(), d.frames, this.translator)}</section>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-vedic-daily': RoxyVedicDaily;\n\t}\n}\n", "import { html } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport type { BirthChartResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { frameCaptionStyles, renderFrameCaption } from '../utils/frame.js';\nimport {\n\ttype ChartStyle,\n\ttype KundliViewModel,\n\trenderKundliCaptions,\n\trenderKundliStyleTablist,\n\trenderKundliSvg,\n\ttoKundliViewModel,\n} from '../utils/kundli-render.js';\nimport { kundliStyles } from '../utils/kundli-styles.js';\nimport { tablistStyles } from '../utils/tablist.js';\n\n/**\n * Vedic kundli (D1 Rashi chart). Pass `data` from /vedic-astrology/birth-chart.\n * Three regional render styles are available; the visible tablist lets the\n * end user switch between South / North / East at any time. The same planet-\n * in-sign data feeds every style, so the toggle is purely a layout choice.\n *\n * Each planet shows its abbreviation and whole-degree-within-sign, with an\n * SVG tooltip carrying exact position, nakshatra, pada, and avastha. The host\n * page sets the initial style via `chart-style` attribute; from there the\n * user takes over.\n *\n * The ascendant (house 1) defaults to the Janma Lagna carried in the response\n * `meta`. Set `chart-reference=\"moon\"` to render the Chandra Lagna (Moon as\n * ascendant) from the same response, or `lagna-override=\"<rashi>\"` to pin the\n * reference to any sign (Surya Lagna, Arudha Lagna, a custom point). The same\n * planet-in-sign data feeds every reference; only the house numbering rotates.\n *\n * Theming flows through CSS custom properties on `:host`, so the chart\n * adopts the host page palette without runtime color probing.\n */\n@customElement('roxy-vedic-kundli')\nexport class RoxyVedicKundli extends RoxyDataElement<BirthChartResponse> {\n\tstatic styles = [baseStyles, frameCaptionStyles, kundliStyles, tablistStyles];\n\n\t@property({ type: String, reflect: true, attribute: 'chart-style' })\n\tchartStyle: ChartStyle = 'north';\n\n\t/**\n\t * Ascendant reference point. `'lagna'` (default) uses the Janma Lagna from the response; `'moon'` renders the Chandra Lagna (Moon sign as house 1) from the same response. An explicit {@link lagnaOverride} wins over this.\n\t */\n\t@property({ type: String, reflect: true, attribute: 'chart-reference' })\n\tchartReference: 'lagna' | 'moon' = 'lagna';\n\n\t/**\n\t * Explicit rashi/sign name (case-insensitive, e.g. `\"cancer\"`) to use as the ascendant, overriding both the Janma Lagna and {@link chartReference}. Empty by default (standard Janma Lagna). Use for Surya Lagna, Arudha Lagna, or any custom reference chart.\n\t */\n\t@property({ type: String, reflect: true, attribute: 'lagna-override' })\n\tlagnaOverride = '';\n\n\t/**\n\t * Resolve the ascendant override fed to {@link toKundliViewModel}. An explicit `lagna-override` wins; otherwise `chart-reference=\"moon\"` derives the Moon rashi from the response `meta`. Returns `undefined` for the default Janma Lagna path.\n\t */\n\tprivate resolveReference(): string | undefined {\n\t\tif (this.lagnaOverride) return this.lagnaOverride;\n\t\tif (this.chartReference === 'moon') return this.data?.meta?.Moon?.rashi;\n\t\treturn undefined;\n\t}\n\n\tprivate viewModel(): KundliViewModel | null {\n\t\tif (!this.data?.meta) return null;\n\t\treturn toKundliViewModel(this.data.meta, {\n\t\t\tdivisionLabel: 'D1 Rashi',\n\t\t\tlagnaOverride: this.resolveReference(),\n\t\t\tstates: this.data,\n\t\t});\n\t}\n\n\tprivate setStyle = (next: ChartStyle) => {\n\t\tthis.chartStyle = next;\n\t};\n\n\tprotected renderData(d: BirthChartResponse) {\n\t\tconst vm = this.viewModel();\n\t\tif (!vm) return this.renderEmpty();\n\t\tconst title =\n\t\t\tthis.chartReference === 'moon' && !this.lagnaOverride\n\t\t\t\t? this.t('Chandra lagna')\n\t\t\t\t: this.t('Vedic kundli');\n\t\treturn html`<div class=\"wrap\" part=\"card\"><div class=\"header\" part=\"header\"><div><h2 class=\"title\">${title}</h2>${renderFrameCaption(this.effectiveLang(), d.frame, this.translator)} ${renderKundliCaptions(vm, this.translator)}</div>${renderKundliStyleTablist(this.chartStyle, this.setStyle, this.translator)}</div><div id=\"kundli-panel-${this.chartStyle}\" part=\"panel\" role=\"tabpanel\" tabindex=\"0\" aria-labelledby=\"kundli-tab-${this.chartStyle}\"><svg viewBox=\"0 0 400 400\" preserveAspectRatio=\"xMidYMid meet\" part=\"chart\" role=\"img\" aria-label=\"${this.t('Vedic birth chart with twelve sign houses')}\"><title>${this.t('Vedic kundli')}</title>${renderKundliSvg(vm, this.chartStyle, this.translator)}</svg></div></div>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-vedic-kundli': RoxyVedicKundli;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport { planetGlyph, signGlyph } from '../tokens/index.js';\nimport type { BirthChartResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { formatSignPosition } from '../utils/degree.js';\nimport { chevron, disclosureStyles } from '../utils/disclosure.js';\nimport { formatNumber, formatWithSanskrit } from '../utils/format.js';\nimport { frameCaptionStyles, renderFrameCaption } from '../utils/frame.js';\n\n/**\n * Fixed display order: Lagna pinned first as the chart frame, then the nine\n * grahas in classical sequence. Any graha not in this list is appended.\n */\nconst GRAHA_ORDER = [\n\t'Lagna',\n\t'Sun',\n\t'Moon',\n\t'Mars',\n\t'Mercury',\n\t'Jupiter',\n\t'Venus',\n\t'Saturn',\n\t'Rahu',\n\t'Ketu',\n];\n\ntype MetaEntry = BirthChartResponse['meta'][string];\n\n/**\n * Vedic planetary positions table. Renders /vedic-astrology/birth-chart `meta`\n * as the full reference-grade positions grid a practitioner reads alongside\n * the kundli wheel: graha, rashi, exact degree, nakshatra and pada, nakshatra\n * lord, bhava (house), all three avastha systems, and retrograde.\n *\n * @remarks\n * The three avastha systems each get their own column rather than one merged\n * state, because they answer different questions and a reader looks up exactly\n * one of them at a time: Baladi is the age state read from the degree, Jagradadi\n * the waking state and Deeptadi the dispositional state, both read from sign\n * dignity. Dignity is a relationship to a sign lord, which Rahu, Ketu and the\n * Lagna do not have, so the API returns only `awastha` for those three and their\n * last two cells are deliberately EMPTY. They are not rendered as a dash or a\n * placeholder: both read as data that failed to arrive.\n *\n * The positions grid is the default view. The same birth-chart response also\n * carries chart-wide conditions and readings, surfaced as collapsed accordions\n * below the grid so they never crowd the table: combust grahas (astangata),\n * planetary wars (graha yuddha), per-graha rashi and nakshatra interpretations,\n * the active classical yogas (present === true), and the twelve bhava\n * significations. Each accordion renders only when its source array or map is\n * non-empty.\n */\n@customElement('roxy-vedic-planets-table')\nexport class RoxyVedicPlanetsTable extends RoxyDataElement<BirthChartResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tframeCaptionStyles,\n\t\tdisclosureStyles,\n\t\tcss`.wrap{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);background:var(--roxy-surface,#fff);box-shadow:var(--roxy-shadow-sm);overflow:hidden}.head{padding:var(--roxy-space-md,1rem);border-bottom:1px solid var(--roxy-border,#e4e4e7)}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.scroll{min-width:0;overflow-x:auto}table{border-collapse:collapse;width:100%;font-size:var(--roxy-text-sm,.875rem);min-width:760px}thead{background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 20%, transparent)}th,td{padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);text-align:left;white-space:nowrap}th{color:var(--roxy-muted,#71717a);font-weight:var(--roxy-weight-bold,600);text-transform:uppercase;font-size:var(--roxy-text-xs,.75rem);letter-spacing:.04em}tbody tr{border-top:1px solid var(--roxy-border,#e4e4e7)}tbody tr.lagna{background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 10%, transparent)}tbody tr.modern-first{border-top-width:2px}td.graha{font-weight:var(--roxy-weight-bold,600);color:var(--roxy-fg,#0a0a0a)}.glyph{color:var(--roxy-muted,#71717a);margin-right:.4em}tbody tr.lagna .glyph{color:var(--roxy-accent-ink,#b45309)}.retro{color:var(--roxy-warning-fg,#9a3412);font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600)}.num{font-variant-numeric:tabular-nums}details.panel{border-top:1px solid var(--roxy-border,#e4e4e7)}details.panel>summary{justify-content:space-between;align-items:center;gap:var(--roxy-space-sm,.5rem);padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);cursor:pointer;font-size:var(--roxy-text-sm,.875rem);font-weight:var(--roxy-weight-bold,600);color:var(--roxy-fg,#0a0a0a);display:flex}details.panel>summary:focus-visible{outline:2px solid var(--roxy-ring,#f59e0b66);outline-offset:-2px}.summary-count{margin-left:auto;margin-right:var(--roxy-space-xs,.25rem);font-weight:var(--roxy-weight-normal,400);color:var(--roxy-muted,#71717a);font-variant-numeric:tabular-nums}.panel-body{padding:0 var(--roxy-space-md,1rem) var(--roxy-space-md,1rem);gap:var(--roxy-space-sm,.5rem);grid-template-columns:minmax(0,1fr);display:grid}.condition{font-size:var(--roxy-text-sm,.875rem);flex-wrap:wrap;align-items:baseline;gap:.4em;display:flex}.condition .planet{font-weight:var(--roxy-weight-bold,600)}.condition .detail{color:var(--roxy-muted,#71717a);font-variant-numeric:tabular-nums}.condition .winner{color:var(--roxy-success-fg,#166534);font-weight:var(--roxy-weight-bold,600)}.interp{gap:.15em;display:grid}.interp .planet{font-weight:var(--roxy-weight-bold,600);font-size:var(--roxy-text-sm,.875rem)}.interp p{font-size:var(--roxy-text-sm,.875rem);line-height:var(--roxy-leading-normal,1.5);margin:0}.interp .label{color:var(--roxy-muted,#71717a);font-weight:var(--roxy-weight-bold,600)}.bhava{gap:.15em;display:grid}.bhava .name{font-weight:var(--roxy-weight-bold,600);font-size:var(--roxy-text-sm,.875rem)}.bhava .desc{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-muted,#71717a);line-height:var(--roxy-leading-normal,1.5);margin:0}.quality{font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);text-transform:uppercase;letter-spacing:.04em}.quality.positive{color:var(--roxy-success-fg,#166534)}.quality.negative{color:var(--roxy-danger-fg,#991b1b)}.quality.both{color:var(--roxy-muted,#71717a)}`,\n\t];\n\n\t/** Ordered [name, entry] pairs: GRAHA_ORDER first, then any extras. */\n\tprivate orderedRows(): Array<[string, MetaEntry]> {\n\t\tconst meta = this.data?.meta ?? {};\n\t\tconst seen = new Set<string>();\n\t\tconst rows: Array<[string, MetaEntry]> = [];\n\t\tfor (const name of GRAHA_ORDER) {\n\t\t\tconst entry = meta[name];\n\t\t\tif (entry) {\n\t\t\t\trows.push([name, entry]);\n\t\t\t\tseen.add(name);\n\t\t\t}\n\t\t}\n\t\tfor (const [name, entry] of Object.entries(meta)) {\n\t\t\tif (!seen.has(name)) rows.push([name, entry]);\n\t\t}\n\t\treturn rows;\n\t}\n\n\tprotected renderData(d: BirthChartResponse) {\n\t\tif (!d.meta) return this.renderEmpty();\n\t\tconst rows = this.orderedRows();\n\n\t\treturn html`<div class=\"wrap\" part=\"card\" aria-label=\"${this.t('Vedic planetary positions')}\"><header class=\"head\" part=\"header\"><h2 class=\"title\">${this.t('Planetary positions')}</h2>${renderFrameCaption(this.effectiveLang(), d.frame, this.translator)}</header><div class=\"scroll\" part=\"table\" tabindex=\"0\"><table role=\"table\"><caption class=\"roxy-sr-only\">${this.t(\n\t\t\t\t\t\t'Vedic planetary positions: each graha with its rashi, degree, nakshatra, pada, nakshatra lord, house, its state in all three avastha systems, and retrograde state. Jagradadi and Deeptadi are read from sign dignity, which the nodes and the Lagna do not have, so those two cells are blank on the Rahu, Ketu and Lagna rows. Uranus, Neptune and Pluto appear only when asked for and rule no sign, so every avastha and house cell is blank on their rows too.',\n\t\t\t\t\t)}</caption><thead><tr><th scope=\"col\">${this.t('Graha')}</th><th scope=\"col\">${this.t('Rashi')}</th><th scope=\"col\">${this.t('Degree')}</th><th scope=\"col\">${this.t('Nakshatra')}</th><th scope=\"col\">${this.t('Pada')}</th><th scope=\"col\">${this.t('Nak. lord')}</th><th scope=\"col\">${this.t('House')}</th><th scope=\"col\" title=\"${this.t('Baladi: the five age states, set by degree within the sign')}\">${this.t('Baladi')}</th><th scope=\"col\" title=\"${this.t('Jagradadi: the three waking states, set by sign dignity. The seven classical grahas only')}\">${this.t('Jagradadi')}</th><th scope=\"col\" title=\"${this.t('Deeptadi: the nine dispositional states, set by sign dignity. The seven classical grahas only')}\">${this.t('Deeptadi')}</th><th scope=\"col\">${this.t('Retro')}</th></tr></thead><tbody>${rows.map(([name, p]) => {\n\t\t\t\t\t\tconst isLagna = (p.graha ?? name) === 'Lagna';\n\t\t\t\t\t\tconst glyph = planetGlyph(p.graha ?? name) ?? '';\n\t\t\t\t\t\tconst sGlyph = signGlyph(p.rashi) ?? '';\n\t\t\t\t\t\treturn html`<tr class=\"${isLagna ? 'lagna' : ''}\"><td class=\"graha\">${glyph ? html`<span class=\"glyph\">${glyph}</span>` : nothing}${p.graha ?? name}</td><td>${sGlyph ? html`<span class=\"glyph\">${sGlyph}</span>` : nothing}${p.rashi ?? ''}</td><td class=\"num\">${typeof p.longitude === 'number' ? formatSignPosition(p.longitude) : ''}</td><td>${p.nakshatra?.name ?? ''}</td><td class=\"num\">${p.nakshatra?.pada ?? ''}</td><td>${p.nakshatra?.lord ?? ''}</td><td class=\"num\">${typeof p.house === 'number' ? p.house : ''}</td><td>${p.awastha ?? ''}</td><td>${p.jagradadi ?? ''}</td><td>${p.deeptadi ?? ''}</td><td>${p.isRetrograde ? html`<span class=\"retro\">R</span>` : nothing}</td></tr>`;\n\t\t\t\t\t})} ${this.renderModernRows()}</tbody></table></div>${this.renderCombustion()} ${this.renderPlanetaryWar()} ${this.renderInterpretations()} ${this.renderYogas()} ${this.renderHouses()}</div>`;\n\t}\n\n\t/**\n\t * Uranus, Neptune and Pluto, present only when the caller sent `modernPlanets: true`.\n\t *\n\t * @remarks\n\t * They arrive in their OWN array rather than inside `meta`, which is deliberate on the API side and load-bearing here: a component iterating `meta` must not pick them up by accident, because classical Jyotish is defined over nine grahas. The moderns rule no sign, so they have no dignity and therefore no Baladi, Jagradadi or Deeptadi state and no house lordship.\n\t *\n\t * Those cells are left BLANK rather than filled with a zero or a dash, matching what the nodes and the Lagna already do two columns over. A zero would read as a measured state of zero strength, which is a different and false claim from \"this system does not apply here\".\n\t */\n\tprivate renderModernRows() {\n\t\tconst moderns = this.data?.modernPlanets ?? [];\n\t\tif (moderns.length === 0) return nothing;\n\t\treturn moderns.map((m, i) => {\n\t\t\tconst sGlyph = signGlyph(m.rashi) ?? '';\n\t\t\treturn html`<tr class=\"modern ${i === 0 ? 'modern-first' : ''}\"><td class=\"graha\">${formatWithSanskrit(m.planet, m.sanskritName)}</td><td>${sGlyph ? html`<span class=\"glyph\">${sGlyph}</span>` : nothing}${m.rashi ?? ''}</td><td class=\"num\">${typeof m.longitude === 'number' ? formatSignPosition(m.longitude) : ''}</td><td>${m.nakshatra?.name ?? ''}</td><td class=\"num\">${m.nakshatra?.pada ?? ''}</td><td>${m.nakshatra?.lord ?? ''}</td><td></td><td></td><td></td><td></td><td>${m.isRetrograde ? html`<span class=\"retro\">R</span>` : nothing}</td></tr>`;\n\t\t});\n\t}\n\n\tprivate renderCombustion() {\n\t\tconst combust = this.data?.combustion ?? [];\n\t\tif (combust.length === 0) return nothing;\n\t\treturn html`<details class=\"panel\" part=\"section combustion\"><summary>${this.t('Combust grahas')}<span class=\"summary-count\">${combust.length}</span>${chevron()}</summary><div class=\"panel-body\">${combust.map((c) => {\n\t\t\t\t\tconst glyph = planetGlyph(c.planet) ?? '';\n\t\t\t\t\tconst dist = formatNumber(this.effectiveLang(), c.distanceFromSun, 2);\n\t\t\t\t\tconst orb = formatNumber(this.effectiveLang(), c.orb, 1);\n\t\t\t\t\treturn html`<div class=\"condition\"><span class=\"planet\">${glyph ? `${glyph} ` : ''}${c.planet}</span> <span class=\"detail\">${this.t('{{distance}} deg from Sun, within {{orb}} deg orb', { distance: dist, orb })}</span></div>`;\n\t\t\t\t})}</div></details>`;\n\t}\n\n\tprivate renderPlanetaryWar() {\n\t\tconst wars = this.data?.planetaryWar ?? [];\n\t\tif (wars.length === 0) return nothing;\n\t\treturn html`<details class=\"panel\" part=\"section planetary-war\"><summary>${this.t('Planetary wars')}<span class=\"summary-count\">${wars.length}</span>${chevron()}</summary><div class=\"panel-body\">${wars.map((w) => {\n\t\t\t\t\tconst dist = formatNumber(this.effectiveLang(), w.distance, 2);\n\t\t\t\t\treturn html`<div class=\"condition\"><span class=\"planet\">${this.t('{{first}} vs {{second}}', { first: w.planet1, second: w.planet2 })}</span> <span class=\"detail\">${this.t('{{distance}} deg apart', { distance: dist })}</span> <span class=\"winner\">${this.t('{{graha}} wins', { graha: w.winner })}</span></div>`;\n\t\t\t\t})}</div></details>`;\n\t}\n\n\tprivate renderInterpretations() {\n\t\t// The one block on this table that is prose about the result rather than a\n\t\t// measurement of it, which is the line `hide-readings` is drawn on. The\n\t\t// columns, the combustion and planetary-war panels and the yoga list are\n\t\t// all facts and stay. Without this the component rendered a `readings` part\n\t\t// that the attribute did not touch, against a README that promises every\n\t\t// component with a written interpretation acts on it.\n\t\tif (this.hideReadings) return nothing;\n\t\tconst interp = this.data?.interpretations ?? {};\n\t\tconst entries = this.orderedRows()\n\t\t\t.map(([name, p]) => [p.graha ?? name, interp[p.graha ?? name]] as const)\n\t\t\t.filter(([, v]) => v != null);\n\t\tif (entries.length === 0) return nothing;\n\t\treturn html`<details class=\"panel\" part=\"section readings\"><summary>${this.t('Interpretations')}<span class=\"summary-count\">${entries.length}</span>${chevron()}</summary><div class=\"panel-body\">${entries.map(([name, v]) => {\n\t\t\t\t\tconst glyph = planetGlyph(name) ?? '';\n\t\t\t\t\treturn html`<div class=\"interp\"><span class=\"planet\">${glyph ? `${glyph} ` : ''}${name}</span> ${v.rashi ? html`<p><span class=\"label\">${this.t('Rashi.')}</span> ${v.rashi}</p>` : nothing} ${v.nakshatra ? html`<p><span class=\"label\">${this.t('Nakshatra.')}</span> ${v.nakshatra}</p>` : nothing}</div>`;\n\t\t\t\t})}</div></details>`;\n\t}\n\n\tprivate renderHouses() {\n\t\tconst houses = (this.data?.houses ?? []).filter(\n\t\t\t(h) => h.name || h.description,\n\t\t);\n\t\tif (houses.length === 0) return nothing;\n\t\treturn html`<details class=\"panel\" part=\"section bhava-significations\"><summary>${this.t('Bhava significations')}<span class=\"summary-count\">${houses.length}</span>${chevron()}</summary><div class=\"panel-body\">${houses.map(\n\t\t\t\t\t(h) => html`<div class=\"bhava\"><span class=\"name\">${h.number}. ${h.name ?? ''}</span> ${h.description ? html`<p class=\"desc\">${h.description}</p>` : nothing}</div>`,\n\t\t\t\t)}</div></details>`;\n\t}\n\n\tprivate renderYogas() {\n\t\tconst yogas = (this.data?.yogas ?? []).filter((y) => y.present);\n\t\tif (yogas.length === 0) return nothing;\n\t\treturn html`<details class=\"panel\" part=\"section yogas\"><summary>${this.t('Yogas')}<span class=\"summary-count\">${yogas.length}</span>${chevron()}</summary><div class=\"panel-body\">${yogas.map(\n\t\t\t\t\t(y) => html`<div class=\"bhava\"><span class=\"name\">${y.name} ${y.quality ? html`<span class=\"quality ${y.quality.toLowerCase()}\">${y.quality}</span>` : nothing}</span> ${y.result ? html`<p class=\"desc\">${y.result}</p>` : nothing}</div>`,\n\t\t\t\t)}</div></details>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-vedic-planets-table': RoxyVedicPlanetsTable;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport type { ChromeString } from '../i18n/chrome-strings.js';\nimport { planetGlyph, signGlyph } from '../tokens/index.js';\nimport type { NatalChartResponse } from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { formatSignPosition } from '../utils/degree.js';\nimport { formatNumber } from '../utils/format.js';\nimport { display } from '../utils/localized.js';\nimport { capitalize } from '../utils/string.js';\n\n/** A body or point row, normalized so planets and the four angles share a table. */\ninterface BodyRow {\n\t/** Canonical English name. Keyed on by the glyph table, so it never localizes. */\n\tname: string;\n\t/** The name a reader sees. Same as {@link BodyRow.name} until the response carries a translation. */\n\tlabel: string;\n\tsign?: string;\n\t/** The sign a reader sees, for the same reason. */\n\tsignLabel?: string;\n\tlongitude?: number;\n\thouse?: number;\n\tspeed?: number;\n\tisRetrograde?: boolean;\n\t/** Essential dignity by sign. Absent for the nodes, Chiron and Lilith, which rule no sign, and an absent value is a different answer from `peregrine`, so the cell stays blank rather than reading as the neutral state. */\n\tdignity?: string;\n\t/** True for the chart angles (ASC, MC, Part of Fortune, Vertex). */\n\tisPoint?: boolean;\n}\n\n/**\n * Western planetary positions table. Renders a /astrology/natal-chart response\n * as the reference-grade positions grid astrologers read alongside the wheel:\n * every body with its sign, exact degree, house, and daily motion, followed by\n * the four chart points (Ascendant, Midheaven, Part of Fortune, Vertex).\n */\n@customElement('roxy-western-planets-table')\nexport class RoxyWesternPlanetsTable extends RoxyDataElement<NatalChartResponse> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tcss`.wrap{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);background:var(--roxy-surface,#fff);box-shadow:var(--roxy-shadow-sm);overflow:auto}.head{padding:var(--roxy-space-md,1rem);border-bottom:1px solid var(--roxy-border,#e4e4e7)}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}table{border-collapse:collapse;width:100%;font-size:var(--roxy-text-sm,.875rem);min-width:460px}thead{background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 20%, transparent)}th,td{padding:var(--roxy-space-sm,.5rem) var(--roxy-space-md,1rem);text-align:left;white-space:nowrap}th{color:var(--roxy-muted,#71717a);font-weight:var(--roxy-weight-bold,600);text-transform:uppercase;font-size:var(--roxy-text-xs,.75rem);letter-spacing:.04em}tbody tr{border-top:1px solid var(--roxy-border,#e4e4e7)}tbody tr.point{background:color-mix(in srgb, var(--roxy-accent,#f59e0b) 8%, transparent)}td.body{font-weight:var(--roxy-weight-bold,600);color:var(--roxy-fg,#0a0a0a)}.glyph{color:var(--roxy-muted,#71717a);margin-right:.4em}.retro{color:var(--roxy-danger,#dc2626);font-weight:var(--roxy-weight-bold,600)}.num{font-variant-numeric:tabular-nums}`,\n\t];\n\n\t/** Build the ordered row list: the planets array, then the four chart points. */\n\tprivate rows(): BodyRow[] {\n\t\tconst d = this.data;\n\t\tif (!d) return [];\n\t\tconst rows: BodyRow[] = (d.planets ?? []).map((p) => ({\n\t\t\tname: p.name,\n\t\t\tlabel: display(p, 'name'),\n\t\t\tsign: p.sign,\n\t\t\tsignLabel: display(p, 'sign'),\n\t\t\tlongitude: p.longitude,\n\t\t\thouse: p.house,\n\t\t\tspeed: p.speed,\n\t\t\tisRetrograde: p.isRetrograde,\n\t\t\tdignity: p.dignity,\n\t\t}));\n\t\t// The four chart points are copy this component writes rather than values the\n\t\t// response names, so each is typed to its catalogue source and the compiler\n\t\t// rejects one no catalogue carries.\n\t\tconst points: ReadonlyArray<\n\t\t\treadonly [ChromeString, { sign?: string; longitude?: number } | undefined]\n\t\t> = [\n\t\t\t['Ascendant', d.ascendant],\n\t\t\t['Midheaven', d.midheaven],\n\t\t\t['Part of Fortune', d.partOfFortune],\n\t\t\t['Vertex', d.vertex],\n\t\t];\n\t\tfor (const [name, point] of points) {\n\t\t\tif (point) {\n\t\t\t\trows.push({\n\t\t\t\t\tname,\n\t\t\t\t\tlabel: this.t(name),\n\t\t\t\t\tsign: point.sign,\n\t\t\t\t\tsignLabel: display(point, 'sign'),\n\t\t\t\t\tlongitude: point.longitude,\n\t\t\t\t\tisPoint: true,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn rows;\n\t}\n\n\tprotected renderData(d: NatalChartResponse) {\n\t\tif (!d.planets) return this.renderEmpty();\n\t\tconst rows = this.rows();\n\n\t\treturn html`<div class=\"wrap\" part=\"card\" aria-label=\"${this.t('Western planetary positions')}\" tabindex=\"0\"><header class=\"head\" part=\"header\"><h2 class=\"title\">${this.t('Planetary positions')}</h2></header><table role=\"table\" part=\"table\"><caption class=\"roxy-sr-only\">${this.t('Western planetary positions: each body with its sign, degree, house, essential dignity and motion. The dignity cell is blank for the lunar nodes, Chiron and Lilith, which rule no sign and therefore hold no dignity at all.')}</caption><thead><tr><th scope=\"col\">${this.t('Body')}</th><th scope=\"col\">${this.t('Sign')}</th><th scope=\"col\">${this.t('Degree')}</th><th scope=\"col\">${this.t('House')}</th><th scope=\"col\">${this.t('Dignity')}</th><th scope=\"col\">${this.t('Motion')}</th></tr></thead><tbody>${rows.map((r) => {\n\t\t\t\t\t\tconst glyph = planetGlyph(r.name) ?? '';\n\t\t\t\t\t\tconst sGlyph = signGlyph(r.sign) ?? '';\n\t\t\t\t\t\tconst speed =\n\t\t\t\t\t\t\ttypeof r.speed === 'number'\n\t\t\t\t\t\t\t\t? formatNumber(this.effectiveLang(), r.speed, 3)\n\t\t\t\t\t\t\t\t: '';\n\t\t\t\t\t\treturn html`<tr class=\"${r.isPoint ? 'point' : ''}\"><td class=\"body\">${glyph ? html`<span class=\"glyph\">${glyph}</span>` : nothing}${r.label}</td><td>${sGlyph ? html`<span class=\"glyph\">${sGlyph}</span>` : nothing}${r.signLabel ?? ''}</td><td class=\"num\">${typeof r.longitude === 'number' ? formatSignPosition(r.longitude, r.signLabel) : ''}</td><td class=\"num\">${typeof r.house === 'number' ? r.house : ''}</td><td class=\"dignity\">${r.dignity ? capitalize(r.dignity) : ''}</td><td class=\"num\">${speed ? html`${speed}${this.t('°/day')}` : nothing} ${r.isRetrograde ? html`<span class=\"retro\">℞</span>` : nothing}</td></tr>`;\n\t\t\t\t\t})}</tbody></table></div>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-western-planets-table': RoxyWesternPlanetsTable;\n\t}\n}\n", "import { css, html, nothing } from 'lit';\nimport { customElement, state } from 'lit/decorators.js';\nimport type { ChromeString } from '../i18n/chrome-strings.js';\nimport type {\n\tDetectYogasResponse,\n\tGetYogaResponse,\n\tListYogasResponse,\n} from '../types/index.js';\nimport { RoxyDataElement } from '../utils/base-element.js';\nimport { baseStyles } from '../utils/base-styles.js';\nimport { frameCaptionStyles, renderFrameCaption } from '../utils/frame.js';\nimport { capitalize } from '../utils/string.js';\n\ntype YogaListData =\n\t| ListYogasResponse\n\t| GetYogaResponse\n\t| DetectYogasResponse\n\t| { yogas: Array<GetYogaResponse> };\n\ntype DetectedYoga = DetectYogasResponse['yogas'][number];\n\n/** Anything the three modes render that may carry a classical family: a detect verdict (always), a catalog row or a detail (only the Nabhasa ones). */\ntype FamilyBearing = { family?: DetectedYoga['family'] };\n\n/**\n * The family that silenced a yoga, when one did.\n *\n * @remarks\n * A yoga that failed its own rule and one that matched but was outranked are both `present: false`, and they read very differently: a failed rule says nothing about the chart, while \"Kedara would apply but Pasa outranks it\" is a real feature of it. `suppressedBy` states which, as a stable untranslated key.\n *\n * Read the field, never the `evidence` sentence. That prose is translated on every locale, so matching it groups correctly in English and nowhere else.\n */\nfunction suppressor(y: DetectedYoga): string | undefined {\n\treturn y.suppressedBy || undefined;\n}\n\n/**\n * The four Nabhasa families in the order the tradition arranges them, with the single-combination yogas first.\n *\n * @remarks\n * ORDER only, never membership. Every verdict carries its own `family`, so no yoga-to-family table belongs in this component; one would be a second source of truth that a rule change upstream could silently invalidate. A test binds this list to the response schema so a new family cannot arrive unordered.\n */\nexport const FAMILY_ORDER = [\n\t'classical',\n\t'asraya',\n\t'dala',\n\t'akriti',\n\t'sankhya',\n] as const;\n\n/** Sort key placing a known family in classical order and anything unmapped last, so an unrecognised value still renders rather than disappearing. */\nfunction familyRank(y: FamilyBearing): number {\n\tconst i = FAMILY_ORDER.indexOf(y.family as (typeof FAMILY_ORDER)[number]);\n\treturn i === -1 ? FAMILY_ORDER.length : i;\n}\n\n/** The three detect verdicts in reading order, each with the note that explains the group. Ordered by what a reader wants first: what fired, what nearly fired, then the rest as reference. */\nconst VERDICTS = [\n\t{\n\t\tid: 'present',\n\t\tsource: 'Present' as ChromeString,\n\t\topen: true,\n\t\tnoteSource:\n\t\t\t'Every classical condition is satisfied by this chart.' as ChromeString,\n\t},\n\t{\n\t\tid: 'outranked',\n\t\tsource: 'Outranked' as ChromeString,\n\t\topen: true,\n\t\tnoteSource:\n\t\t\t'The rule matched, but a stronger family silences it under the classical precedence norms. Each card names the family that took precedence.' as ChromeString,\n\t},\n\t{\n\t\tid: 'absent',\n\t\tsource: 'Not present' as ChromeString,\n\t\topen: false,\n\t\tnoteSource:\n\t\t\t'At least one classical condition fails. Read the evidence for which.' as ChromeString,\n\t},\n] as const;\n\ntype Verdict = (typeof VERDICTS)[number]['id'];\n\n/**\n * Yoga catalog and detail renderer. Accepts four data modes:\n *   - Catalog: ListYogasResponse (yogas array of {id, name} + total)\n *   - Detail: GetYogaResponse (single yoga with description, result, quality)\n *   - Detail array: { yogas: Array<GetYogaResponse> } for pre-filtered sets\n *   - Detect: DetectYogasResponse (each yoga carries a present verdict + evidence); grouped by verdict, each badged present, outranked or not present, with its classical evidence\n *\n * All multi-item modes include a live search filter.\n *\n * @remarks\n * Detect returns a verdict for every yoga in the detected set on every chart, of which a real chart fires low single digits, so a flat list buries the handful a reader came for. They are grouped by verdict, in reading order: what fired, what would have fired but was outranked, then everything that failed its rule, collapsed because it is reference rather than result. Every grouping and every label is read from the response, so this component holds no table of yoga data of its own.\n *\n * Verdict is the primary grouping and `family` the secondary sort within it, rather than grouping by family outright. A reader opens a detection result to learn what a chart has; the Nabhasa arrangement is how the set is catalogued, not what a chart says. Sorting inside each verdict gives the classical order where it is readable and keeps the answer at the top.\n *\n * The compact catalog mode shows no family chip: only part of the glossary is Nabhasa, so a chip on some rows and a gap on the rest reads as missing data. Filter the catalog with the `family` query parameter instead.\n *\n * `hide-readings` leaves the detection intact and takes the prose off each card: the `description` and the Effects disclosure go, and the name, quality chip, family chip and verdict badge stay, so a card with nothing to disclose renders in the flat shape it already uses. `evidence` stays with them, because it is the classical condition trace behind the verdict rather than a reading of it, the same provenance role the frame caption plays. The verdict group notes are this component's own copy explaining how to read the grouping, not something the endpoint returned, so they stay too.\n */\n@customElement('roxy-yoga-list')\nexport class RoxyYogaList extends RoxyDataElement<YogaListData> {\n\tstatic styles = [\n\t\tbaseStyles,\n\t\tframeCaptionStyles,\n\t\tcss`.wrap{gap:var(--roxy-space-md,1rem);grid-template-columns:minmax(0,1fr);display:grid}.head{justify-content:space-between;align-items:baseline;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;display:flex}.title{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);margin:0}.count{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-muted,#71717a)}.search-wrap{align-items:center;gap:var(--roxy-space-sm,.5rem);display:flex}.search{width:100%;max-width:280px;font-size:var(--roxy-text-sm,.875rem);font-family:var(--roxy-font-sans);border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);outline:none;padding:.35em .75em}.search::placeholder{color:var(--roxy-fg,#0a0a0a);opacity:.65}.search:focus{border-color:var(--roxy-accent,#f59e0b);box-shadow:0 0 0 2px color-mix(in srgb, var(--roxy-accent,#f59e0b) 30%, transparent)}.grid{gap:var(--roxy-space-sm,.5rem);grid-template-columns:repeat(auto-fill,minmax(180px,1fr));display:grid}.yoga-chip{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);font-size:var(--roxy-text-sm,.875rem);background:var(--roxy-surface,#fff);color:var(--roxy-fg,#0a0a0a);word-break:break-word;padding:.4em .8em}.yoga-chip .yoga-id{color:var(--roxy-fg,#0a0a0a);opacity:.75;margin-top:.15em;font-size:.7em;display:block}.detail-card{border:1px solid var(--roxy-border,#e4e4e7);border-radius:var(--roxy-radius-md,8px);padding:var(--roxy-space-md,1rem);background:var(--roxy-surface,#fff);gap:var(--roxy-space-sm,.5rem);display:grid}.detail-name{font-size:var(--roxy-text-lg,1.125rem);font-weight:var(--roxy-weight-bold,600);align-items:center;gap:var(--roxy-space-sm,.5rem);flex-wrap:wrap;margin:0;display:flex}.quality-chip{font-size:var(--roxy-text-xs,.75rem);border-radius:999px;padding:.15em .6em;font-weight:600;display:inline-block}.quality-Positive{background:color-mix(in srgb, var(--roxy-success,#22c55e) 18%, transparent);color:var(--roxy-success-fg,#15803d);border:1px solid color-mix(in srgb, var(--roxy-success,#22c55e) 40%, transparent)}.quality-Negative{background:color-mix(in srgb, var(--roxy-danger,#ef4444) 18%, transparent);color:var(--roxy-danger-fg,#b91c1c);border:1px solid color-mix(in srgb, var(--roxy-danger,#ef4444) 40%, transparent)}.quality-Both{background:color-mix(in srgb, var(--roxy-warning,#f59e0b) 18%, transparent);color:var(--roxy-warning-fg,#b45309);border:1px solid color-mix(in srgb, var(--roxy-warning,#f59e0b) 40%, transparent)}.family-chip{font-size:var(--roxy-text-xs,.75rem);border:1px solid var(--roxy-border,#e4e4e7);color:var(--roxy-fg,#0a0a0a);border-radius:999px;padding:.15em .6em;font-weight:500;display:inline-block}.present-badge{font-size:var(--roxy-text-xs,.75rem);border-radius:999px;padding:.15em .6em;font-weight:600;display:inline-block}.present-badge.is-present{background:color-mix(in srgb, var(--roxy-success,#16a34a) 16%, transparent);color:var(--roxy-success-fg,#166534)}.present-badge.is-absent{background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 55%, transparent);color:var(--roxy-fg,#0a0a0a)}.present-badge.is-outranked{background:color-mix(in srgb, var(--roxy-warning,#f59e0b) 16%, transparent);color:var(--roxy-warning-fg,#b45309)}.detail-card.absent{background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 22%, var(--roxy-surface,#fff))}.detail-card.outranked{background:color-mix(in srgb, var(--roxy-border,#e4e4e7) 10%, var(--roxy-surface,#fff))}.group-stack{gap:var(--roxy-space-md,1rem);display:grid}.group{gap:var(--roxy-space-sm,.5rem);display:grid}.group-summary{align-items:baseline;gap:var(--roxy-space-sm,.5rem);cursor:pointer;color:var(--roxy-muted,#71717a);list-style:none;display:flex}.group-summary:before{content:\"+\";color:var(--roxy-accent-ink,#b45309);font-size:1.1em;line-height:1}.group[open]>.group-summary:before{content:\"-\"}.group-label{font-size:var(--roxy-text-xs,.75rem);font-weight:var(--roxy-weight-bold,600);text-transform:uppercase;letter-spacing:.06em}.group-count{font-size:var(--roxy-text-xs,.75rem);font-variant-numeric:tabular-nums}.group-note,.evidence{font-size:var(--roxy-text-xs,.75rem);color:var(--roxy-muted,#71717a);line-height:var(--roxy-leading-normal,1.5);margin:0}.description{font-size:var(--roxy-text-sm,.875rem);color:var(--roxy-muted,#71717a);line-height:var(--roxy-leading-normal,1.5);margin:0}.detail-card details{font-size:var(--roxy-text-sm,.875rem)}.detail-card details>summary{cursor:pointer;color:var(--roxy-accent-ink,#b45309);align-items:center;gap:.4em;padding:.25em 0;font-weight:500;list-style:none;display:flex}.detail-card details>summary:before{content:\"+\";font-size:1.1em;line-height:1}.detail-card details[open]>summary:before{content:\"-\"}.detail-card details .result-body{padding-top:var(--roxy-space-xs,.25rem);color:var(--roxy-fg,#0a0a0a);line-height:var(--roxy-leading-normal,1.5)}.no-results{color:var(--roxy-muted,#71717a);font-size:var(--roxy-text-sm,.875rem);padding:var(--roxy-space-md,1rem) 0;text-align:center}.detail-grid{gap:var(--roxy-space-sm,.5rem);display:grid}`,\n\t];\n\n\t@state()\n\tprivate filter = '';\n\n\tprivate readonly handleInput = (e: Event) => {\n\t\tthis.filter = (e.target as HTMLInputElement).value;\n\t};\n\n\t/** The verdict a detected yoga falls under. Both signals come from the response, so nothing about the classical scheme is duplicated here. */\n\tprivate verdictOf(y: DetectedYoga): Verdict {\n\t\treturn y.present ? 'present' : suppressor(y) ? 'outranked' : 'absent';\n\t}\n\n\t/**\n\t * One verdict group, or nothing when no yoga fell under it (a chart with no outranked yoga shows no Outranked heading).\n\t *\n\t * A `<details>` rather than a plain section: the rule-failed group is thirty-odd cards of reference on a real chart, and collapsing keeps it in the DOM, searchable and indexed, while the result stays at the top. A group forces itself open while a filter is active, so a match can never hide inside a collapsed heading.\n\t */\n\tprivate renderVerdictGroup(\n\t\tverdict: (typeof VERDICTS)[number],\n\t\tyogas: DetectedYoga[],\n\t) {\n\t\tif (!yogas.length) return nothing;\n\t\tconst ordered = [...yogas].sort((a, b) => familyRank(a) - familyRank(b));\n\t\treturn html`<details class=\"group\" part=\"section verdict\" ?open=\"${verdict.open || !!this.filter}\"><summary class=\"group-summary\"><span class=\"group-label\">${this.t(verdict.source)}</span> <span class=\"group-count\">${yogas.length}</span></summary><p class=\"group-note\">${this.t(verdict.noteSource)}</p><div class=\"detail-grid\">${ordered.map((y) => this.renderDetectCard(y))}</div></details>`;\n\t}\n\n\tprivate renderQualityChip(quality: string) {\n\t\tconst cls = `quality-chip quality-${quality}`;\n\t\treturn html`<span class=\"${cls}\">${quality}</span>`;\n\t}\n\n\t/**\n\t * The classical family as a neutral chip. Rendered in all three modes because all three carry the field, and left out entirely when absent, which is the correct reading for a catalog row that is not Nabhasa.\n\t *\n\t * Capitalized rather than mapped: every value is already a Sanskrit proper noun, so a label table would be a second source of truth for five strings that need no translation.\n\t */\n\tprivate renderFamilyChip(y: FamilyBearing) {\n\t\treturn y.family\n\t\t\t? html`<span class=\"family-chip\" title=\"${this.t('Classical family')}\">${capitalize(y.family)}</span>`\n\t\t\t: nothing;\n\t}\n\n\tprivate renderDetailCard(yoga: GetYogaResponse) {\n\t\treturn html`<div class=\"detail-card\"><p class=\"detail-name\">${yoga.name} ${yoga.quality ? this.renderQualityChip(yoga.quality) : nothing} ${this.renderFamilyChip(yoga)}</p>${\n\t\t\t\tyoga.description && !this.hideReadings\n\t\t\t\t\t? html`<p class=\"description\">${yoga.description}</p>`\n\t\t\t\t\t: nothing\n\t\t\t} ${this.renderEffects(yoga.result)}</div>`;\n\t}\n\n\t/**\n\t * The Effects disclosure, the one written reading a yoga card carries.\n\t *\n\t * @remarks\n\t * Deliberately NOT the shared `renderInterpAccordion`: that helper emits a titled `<section>` per accordion, and a detect response puts thirty-odd of these inside verdict groups, so each card would grow a Reading heading of its own. It is the same compact expander the verdict group above it uses, and it carries `part=\"reading\"` so one `::part(reading)` rule still reaches it.\n\t */\n\tprivate renderEffects(result: string | undefined) {\n\t\tif (!result || this.hideReadings) return nothing;\n\t\treturn html`<details part=\"reading\"><summary>${this.t('Effects')}</summary><div class=\"result-body\">${result}</div></details>`;\n\t}\n\n\t/**\n\t * Detect-mode card: the three-way verdict, the classical family, and the evidence behind it.\n\t *\n\t * An outranked card names the family that silenced it (`Outranked by Akriti`) rather than saying only that something did. It is exact rather than parsed out of prose, and it reads the same under any `lang`.\n\t */\n\tprivate renderDetectCard(y: DetectedYoga) {\n\t\tconst by = y.present ? undefined : suppressor(y);\n\t\tconst [cls, label] = y.present\n\t\t\t? ['is-present', 'Present']\n\t\t\t: by\n\t\t\t\t? ['is-outranked', `Outranked by ${capitalize(by)}`]\n\t\t\t\t: ['is-absent', 'Not present'];\n\t\treturn html`<div class=\"detail-card ${y.present ? '' : by ? 'outranked' : 'absent'}\"><p class=\"detail-name\">${y.name} ${y.quality ? this.renderQualityChip(y.quality) : nothing} ${this.renderFamilyChip(y)} <span class=\"present-badge ${cls}\">${label}</span></p>${y.description && !this.hideReadings ? html`<p class=\"description\">${y.description}</p>` : nothing} ${y.present ? this.renderEffects(y.result) : nothing} ${y.evidence ? html`<p class=\"evidence\">${y.evidence}</p>` : nothing}</div>`;\n\t}\n\n\tprotected renderData(d: YogaListData) {\n\t\tconst lc = this.filter.toLowerCase();\n\n\t\t// Detail mode: single GetYogaResponse\n\t\tif (\n\t\t\t'description' in d &&\n\t\t\ttypeof (d as GetYogaResponse).description === 'string'\n\t\t) {\n\t\t\tconst yoga = d as GetYogaResponse;\n\t\t\treturn html`<div class=\"wrap\" part=\"card\">${this.renderDetailCard(yoga)}</div>`;\n\t\t}\n\n\t\t// Detail-array mode: { yogas: Array<GetYogaResponse> } where items have description\n\t\tif ('yogas' in d && Array.isArray((d as { yogas: unknown[] }).yogas)) {\n\t\t\tconst allYogas = (\n\t\t\t\td as { yogas: Array<GetYogaResponse | { id: string; name: string }> }\n\t\t\t).yogas;\n\n\t\t\t// Detect mode: every entry carries a `present` verdict. Group by verdict,\n\t\t\t// badge each present, outranked or not present, and surface the classical\n\t\t\t// evidence. Must precede the detail-array check because detect entries\n\t\t\t// also carry `description`.\n\t\t\tif (allYogas.length > 0 && 'present' in allYogas[0]) {\n\t\t\t\tconst detected = allYogas as DetectedYoga[];\n\t\t\t\tconst filtered = lc\n\t\t\t\t\t? detected.filter((y) => y.name.toLowerCase().includes(lc))\n\t\t\t\t\t: detected;\n\t\t\t\tconst presentCount =\n\t\t\t\t\t(d as DetectYogasResponse).total ??\n\t\t\t\t\tdetected.filter((y) => y.present).length;\n\t\t\t\treturn html`<div class=\"wrap\" part=\"card\"><div class=\"head\" part=\"header\"><h2 class=\"title\">${this.t('Detected yogas')}</h2><span class=\"count\">${this.t('{{count}} of {{total}} present', { count: presentCount, total: detected.length })}</span></div>${renderFrameCaption(this.effectiveLang(), (d as DetectYogasResponse).frame, this.translator)}<div class=\"search-wrap\"><input class=\"search\" type=\"search\" placeholder=\"${this.t('Filter yogas...')}\" aria-label=\"${this.t('Filter detected yogas by name')}\" .value=\"${this.filter}\" @input=\"${this.handleInput}\"></div><div class=\"group-stack\" part=\"section verdicts\" role=\"region\" aria-live=\"polite\" aria-label=\"${this.t('Detected yogas')}\">${\n\t\t\t\t\t\t\tfiltered.length > 0\n\t\t\t\t\t\t\t\t? VERDICTS.map((v) =>\n\t\t\t\t\t\t\t\t\t\tthis.renderVerdictGroup(\n\t\t\t\t\t\t\t\t\t\t\tv,\n\t\t\t\t\t\t\t\t\t\t\tfiltered.filter((y) => this.verdictOf(y) === v.id),\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t: html`<p class=\"no-results\">${this.t('No yogas match your search.')}</p>`\n\t\t\t\t\t\t}</div></div>`;\n\t\t\t}\n\n\t\t\tconst isDetailArray = allYogas.length > 0 && 'description' in allYogas[0];\n\n\t\t\tif (isDetailArray) {\n\t\t\t\tconst detailYogas = allYogas as GetYogaResponse[];\n\t\t\t\tconst filtered = lc\n\t\t\t\t\t? detailYogas.filter((y) => y.name.toLowerCase().includes(lc))\n\t\t\t\t\t: detailYogas;\n\t\t\t\tconst total = (d as ListYogasResponse).total;\n\t\t\t\treturn html`<div class=\"wrap\" part=\"card\"><div class=\"head\" part=\"header\"><h2 class=\"title\">${this.t('Yoga catalog')}</h2>${\n\t\t\t\t\t\t\ttotal !== undefined\n\t\t\t\t\t\t\t\t? html`<span class=\"count\">${this.t('{{count}} total', { count: total })}</span>`\n\t\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t\t}</div><div class=\"search-wrap\"><input class=\"search\" type=\"search\" placeholder=\"${this.t('Filter yogas...')}\" aria-label=\"${this.t('Filter yoga list by name')}\" .value=\"${this.filter}\" @input=\"${this.handleInput}\"></div><div class=\"detail-grid\" part=\"section yogas\" role=\"region\" aria-live=\"polite\" aria-label=\"${this.t('Yoga results')}\">${\n\t\t\t\t\t\t\tfiltered.length > 0\n\t\t\t\t\t\t\t\t? filtered.map((y) => this.renderDetailCard(y))\n\t\t\t\t\t\t\t\t: html`<p class=\"no-results\">${this.t('No yogas match your search.')}</p>`\n\t\t\t\t\t\t}</div></div>`;\n\t\t\t}\n\n\t\t\t// Catalog mode: ListYogasResponse with {id, name} items\n\t\t\tconst catalogYogas = allYogas as Array<{ id: string; name: string }>;\n\t\t\tconst filtered = lc\n\t\t\t\t? catalogYogas.filter((y) => y.name.toLowerCase().includes(lc))\n\t\t\t\t: catalogYogas;\n\t\t\tconst total = (d as ListYogasResponse).total;\n\t\t\treturn html`<div class=\"wrap\" part=\"card\"><div class=\"head\" part=\"header\"><h2 class=\"title\">${this.t('Yoga catalog')}</h2>${\n\t\t\t\t\t\ttotal !== undefined\n\t\t\t\t\t\t\t? html`<span class=\"count\">${this.t('{{count}} total', { count: total })}</span>`\n\t\t\t\t\t\t\t: nothing\n\t\t\t\t\t}</div><div class=\"search-wrap\"><input class=\"search\" type=\"search\" placeholder=\"${this.t('Filter yogas...')}\" aria-label=\"${this.t('Filter yoga list by name')}\" .value=\"${this.filter}\" @input=\"${this.handleInput}\"></div><div class=\"grid\" part=\"section yogas\" role=\"region\" aria-live=\"polite\" aria-label=\"${this.t('Yoga results')}\">${\n\t\t\t\t\t\tfiltered.length > 0\n\t\t\t\t\t\t\t? filtered.map(\n\t\t\t\t\t\t\t\t\t(y) => html`<div class=\"yoga-chip\">${y.name}</div>`,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t: html`<p class=\"no-results\">${this.t('No yogas match your search.')}</p>`\n\t\t\t\t\t}</div></div>`;\n\t\t}\n\n\t\treturn html`<div class=\"roxy-empty\" role=\"status\">${this.t('No yoga data')}</div>`;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'roxy-yoga-list': RoxyYogaList;\n\t}\n}\n", "/**\n * Single source of truth for component metadata. Every consumer that needs\n * the (tag, pascal, slug, description, heading) tuple reads from here:\n * scripts/build-react.ts, scripts/build-registry.ts, scripts/sync-docs.ts,\n * scripts/build-widgets.ts, and the browser-side apps/docs/manifest.js\n * (mirrored at build time).\n *\n * Hand-maintained. Add a component → add one entry here. The OpenAPI spec\n * does not yet carry component metadata, so this stays manual.\n */\n\nexport interface RoxyComponent {\n\t/** Pascal-case React export name, e.g. RoxyNatalChart */\n\tpascal: string;\n\t/** Custom-element tag, e.g. roxy-natal-chart */\n\ttag: string;\n\t/** Slug used in registry filenames and shadcn paths, e.g. natal-chart */\n\tslug: string;\n\t/** Short human-readable heading shown on demo cards. */\n\theading: string;\n\t/** One-line description for registry / docs / SEO meta. */\n\tdescription: string;\n\t/** Domain column label in the synced README/AGENTS table. */\n\tdocsLabel: string;\n\t/** Endpoint(s) column body in the synced README/AGENTS table. */\n\tendpointLabel: string;\n\t/** What-it-renders column body in the synced README/AGENTS table. */\n\tdocsSummary: string;\n\t/** Filter category in the browser demo grid. */\n\ttopic: string;\n\t/**\n\t * True when the component does not consume a typed RoxyAPI response from a\n\t * consumer server route. Three cases today:\n\t *   - <roxy-data>: pure renderer, accepts any shape, no fetch.\n\t *   - <roxy-location-search>: calls /location/search itself via publishable key.\n\t *   - <roxy-endpoint-form>: introspects the OpenAPI spec, emits roxy-submit.\n\t * The shadcn registry codegen emits a different doc body for these so we\n\t * never document a server route example with an undefined `data` reference.\n\t */\n\tselfFetching?: boolean;\n}\n\nexport const ROXY_COMPONENTS: readonly RoxyComponent[] = [\n\t{\n\t\tpascal: 'RoxyNatalChart',\n\t\ttag: 'roxy-natal-chart',\n\t\tslug: 'natal-chart',\n\t\theading: 'Natal chart',\n\t\tdescription:\n\t\t\t'Western natal chart wheel for /astrology/natal-chart responses',\n\t\tdocsLabel: 'Western',\n\t\tendpointLabel: 'POST /astrology/natal-chart',\n\t\tdocsSummary: 'Natal chart wheel with planet glyphs and aspect lines',\n\t\ttopic: 'Astrology',\n\t},\n\t{\n\t\tpascal: 'RoxySynastryChart',\n\t\ttag: 'roxy-synastry-chart',\n\t\tslug: 'synastry-chart',\n\t\theading: 'Synastry',\n\t\tdescription: 'Dual-wheel synastry chart with inter-aspects table',\n\t\tdocsLabel: 'Western',\n\t\tendpointLabel: 'POST /astrology/synastry',\n\t\tdocsSummary: 'Dual-wheel synastry with inter-aspects table',\n\t\ttopic: 'Astrology',\n\t},\n\t{\n\t\tpascal: 'RoxyWesternPlanetsTable',\n\t\ttag: 'roxy-western-planets-table',\n\t\tslug: 'western-planets-table',\n\t\theading: 'Western planets',\n\t\tdescription:\n\t\t\t'Western planetary positions table with sign, degree, house, and motion plus the chart angles',\n\t\tdocsLabel: 'Western',\n\t\tendpointLabel: 'POST /astrology/natal-chart',\n\t\tdocsSummary:\n\t\t\t'Sign, degree, house, motion columns plus ASC, MC, PoF, Vertex',\n\t\ttopic: 'Astrology',\n\t},\n\t{\n\t\tpascal: 'RoxyTransitsTable',\n\t\ttag: 'roxy-transits-table',\n\t\tslug: 'transits-table',\n\t\theading: 'Transits',\n\t\tdescription: 'Live planet positions plus aspects to a natal chart',\n\t\tdocsLabel: 'Western',\n\t\tendpointLabel: 'POST /astrology/transits',\n\t\tdocsSummary:\n\t\t\t'Transit planet positions plus optional aspects to a natal chart',\n\t\ttopic: 'Astrology',\n\t},\n\t{\n\t\tpascal: 'RoxyTransitWheel',\n\t\ttag: 'roxy-transit-wheel',\n\t\tslug: 'transit-wheel',\n\t\theading: 'Transit bi-wheel',\n\t\tdescription:\n\t\t\t'Natal and transit bi-wheel with an aspect line for every transit to the natal chart',\n\t\tdocsLabel: 'Western',\n\t\tendpointLabel: 'POST /astrology/transit-aspects',\n\t\tdocsSummary:\n\t\t\t'Natal chart on the inner ring, transiting bodies on the outer ring, aspect lines between them',\n\t\ttopic: 'Astrology',\n\t},\n\t{\n\t\tpascal: 'RoxyAspectsTable',\n\t\ttag: 'roxy-aspects-table',\n\t\tslug: 'aspects-table',\n\t\theading: 'Aspects',\n\t\tdescription:\n\t\t\t'Natal and transit aspect grid with chart-pattern detection (grand trines, t-squares, stelliums, yods)',\n\t\tdocsLabel: 'Western',\n\t\tendpointLabel:\n\t\t\t'POST /astrology/aspects, /astrology/transit-aspects, /astrology/aspect-patterns',\n\t\tdocsSummary:\n\t\t\t'Aspect rows coloured by nature with orb and strength, plus detected chart patterns',\n\t\ttopic: 'Astrology',\n\t},\n\t{\n\t\tpascal: 'RoxyMoonPhase',\n\t\ttag: 'roxy-moon-phase',\n\t\tslug: 'moon-phase',\n\t\theading: 'Moon phase',\n\t\tdescription: 'Moon phase card and calendar',\n\t\tdocsLabel: 'Western',\n\t\tendpointLabel: 'GET /astrology/moon-phase/{current,upcoming,calendar/...}',\n\t\tdocsSummary: 'Moon phase card and calendar',\n\t\ttopic: 'Astrology',\n\t},\n\t{\n\t\tpascal: 'RoxyHoroscopeCard',\n\t\ttag: 'roxy-horoscope-card',\n\t\tslug: 'horoscope-card',\n\t\theading: 'Daily horoscope',\n\t\tdescription:\n\t\t\t'Daily, weekly, or monthly horoscope card for /astrology/horoscope/...',\n\t\tdocsLabel: 'Western',\n\t\tendpointLabel: 'GET /astrology/horoscope/{sign}/{daily,weekly,monthly}',\n\t\tdocsSummary: 'Daily, weekly, or monthly horoscope card',\n\t\ttopic: 'Astrology',\n\t},\n\t{\n\t\tpascal: 'RoxyAstrocartographyMap',\n\t\ttag: 'roxy-astrocartography-map',\n\t\tslug: 'astrocartography-map',\n\t\theading: 'Astrocartography',\n\t\tdescription:\n\t\t\t'Astrocartography world map plotting each body Midheaven, IC, Ascendant, and Descendant relocation lines',\n\t\tdocsLabel: 'Western',\n\t\tendpointLabel: 'POST /astrology/astrocartography',\n\t\tdocsSummary:\n\t\t\t'World map of planetary MC, IC, Ascendant, and Descendant lines with per-line interpretations',\n\t\ttopic: 'Astrology',\n\t},\n\t{\n\t\tpascal: 'RoxyLocalSpaceCompass',\n\t\ttag: 'roxy-local-space-compass',\n\t\tslug: 'local-space-compass',\n\t\theading: 'Local space',\n\t\tdescription:\n\t\t\t'Local space compass plotting each body direction from the birthplace by azimuth and altitude',\n\t\tdocsLabel: 'Western',\n\t\tendpointLabel: 'POST /astrology/local-space',\n\t\tdocsSummary:\n\t\t\t'Compass dial of planetary azimuth lines from the birthplace, dimmed below the horizon',\n\t\ttopic: 'Astrology',\n\t},\n\t{\n\t\tpascal: 'RoxyRelocationWheel',\n\t\ttag: 'roxy-relocation-wheel',\n\t\tslug: 'relocation-wheel',\n\t\theading: 'Relocation chart',\n\t\tdescription:\n\t\t\t'Relocation chart wheel recomputing houses and angles for a new place, with the planets that change house',\n\t\tdocsLabel: 'Western',\n\t\tendpointLabel: 'POST /astrology/relocation-chart',\n\t\tdocsSummary:\n\t\t\t'Relocated chart wheel plus the move geometry, angular planets, and planets that change house',\n\t\ttopic: 'Astrology',\n\t},\n\t{\n\t\tpascal: 'RoxyPositionsTable',\n\t\ttag: 'roxy-positions-table',\n\t\tslug: 'positions-table',\n\t\theading: 'Positions table',\n\t\tdescription:\n\t\t\t'Editorial positions table for asteroids, Black Moon Lilith, secondary progressions, solar arc directions, and the Arabic lots',\n\t\tdocsLabel: 'Western',\n\t\tendpointLabel:\n\t\t\t'POST /astrology/asteroids, /astrology/lilith, /astrology/progressions, /astrology/solar-arc, /astrology/arabic-lots',\n\t\tdocsSummary:\n\t\t\t'Body, sign, degree, and per-shape columns (house, motion, formula, or natal arc) plus each reading',\n\t\ttopic: 'Astrology',\n\t},\n\t{\n\t\tpascal: 'RoxyEphemerisTable',\n\t\ttag: 'roxy-ephemeris-table',\n\t\tslug: 'ephemeris-table',\n\t\theading: 'Ephemeris',\n\t\tdescription:\n\t\t\t'Monthly ephemeris: every body day by day, with each sign change and retrograde stretch called out',\n\t\tdocsLabel: 'Cross',\n\t\tendpointLabel:\n\t\t\t'POST /astrology/planets/monthly, /vedic-astrology/planetary-positions/monthly',\n\t\tdocsSummary:\n\t\t\t'Per-body sign changes and retrograde windows for the month, over the full daily position grid',\n\t\ttopic: 'Astrology',\n\t},\n\t{\n\t\tpascal: 'RoxyFixedStars',\n\t\ttag: 'roxy-fixed-stars',\n\t\tslug: 'fixed-stars',\n\t\theading: 'Fixed stars',\n\t\tdescription:\n\t\t\t'Fixed star conjunctions to the natal chart, tightest first, with the precessed star catalog',\n\t\tdocsLabel: 'Western',\n\t\tendpointLabel: 'POST /astrology/fixed-stars',\n\t\tdocsSummary:\n\t\t\t'Star to natal point conjunctions with readings, plus a catalog of position, magnitude, nature, and keywords',\n\t\ttopic: 'Astrology',\n\t},\n\t{\n\t\tpascal: 'RoxyProfectionCard',\n\t\ttag: 'roxy-profection-card',\n\t\tslug: 'profection-card',\n\t\theading: 'Annual profection',\n\t\tdescription:\n\t\t\t'Annual profection card with the profected house and sign, the lord of the year, and its natal placement',\n\t\tdocsLabel: 'Western',\n\t\tendpointLabel: 'POST /astrology/profections',\n\t\tdocsSummary:\n\t\t\t'Profected house and sign for the year, the lord of the year, its natal placement, and the reading',\n\t\ttopic: 'Astrology',\n\t},\n\t{\n\t\tpascal: 'RoxyCompatibilityCard',\n\t\ttag: 'roxy-compatibility-card',\n\t\tslug: 'compatibility-card',\n\t\theading: 'Compatibility score',\n\t\tdescription: 'Cross-domain compatibility score card',\n\t\tdocsLabel: 'Cross',\n\t\tendpointLabel:\n\t\t\t'POST /astrology/compatibility-score, /numerology/compatibility, /biorhythm/compatibility',\n\t\tdocsSummary: 'Score card with category breakdown',\n\t\ttopic: 'Astrology',\n\t},\n\t{\n\t\tpascal: 'RoxyVedicKundli',\n\t\ttag: 'roxy-vedic-kundli',\n\t\tslug: 'vedic-kundli',\n\t\theading: 'Vedic kundli',\n\t\tdescription:\n\t\t\t'South, North, or East Indian Vedic kundli for /vedic-astrology/birth-chart with per-planet degree and nakshatra detail, plus an optional Chandra Lagna (Moon-as-ascendant) reference view',\n\t\tdocsLabel: 'Vedic',\n\t\tendpointLabel: 'POST /vedic-astrology/birth-chart',\n\t\tdocsSummary:\n\t\t\t'South, North, or East Indian kundli with degree detail and optional Chandra Lagna view',\n\t\ttopic: 'Vedic',\n\t},\n\t{\n\t\tpascal: 'RoxyDivisionalChart',\n\t\ttag: 'roxy-divisional-chart',\n\t\tslug: 'divisional-chart',\n\t\theading: 'Divisional chart',\n\t\tdescription: 'D2 to D60 varga chart wheel with Vargottama markers',\n\t\tdocsLabel: 'Vedic',\n\t\tendpointLabel: 'POST /vedic-astrology/{divisional-chart,navamsa}',\n\t\tdocsSummary:\n\t\t\t'Generic divisional varga wheel from D2 Hora to D60 Shashtiamsa',\n\t\ttopic: 'Vedic',\n\t},\n\t{\n\t\tpascal: 'RoxyKpChart',\n\t\ttag: 'roxy-kp-chart',\n\t\tslug: 'kp-chart',\n\t\theading: 'KP chart',\n\t\tdescription:\n\t\t\t'Full KP chart with Ascendant, Placidus cusps, and planets and nodes in tabbed stellar-hierarchy tables',\n\t\tdocsLabel: 'Vedic (KP)',\n\t\tendpointLabel: 'POST /vedic-astrology/kp/chart',\n\t\tdocsSummary:\n\t\t\t'Ascendant, cusps, planets and nodes with KP stellar hierarchy and house meanings',\n\t\ttopic: 'Vedic',\n\t},\n\t{\n\t\tpascal: 'RoxyVedicPlanetsTable',\n\t\ttag: 'roxy-vedic-planets-table',\n\t\tslug: 'vedic-planets-table',\n\t\theading: 'Vedic planets',\n\t\tdescription:\n\t\t\t'Vedic planetary positions table with degree, nakshatra, pada, nakshatra lord, bhava, and all three avastha systems',\n\t\tdocsLabel: 'Vedic',\n\t\tendpointLabel: 'POST /vedic-astrology/birth-chart',\n\t\tdocsSummary:\n\t\t\t'Degree, nakshatra, pada, lord, bhava, Baladi, Jagradadi and Deeptadi columns',\n\t\ttopic: 'Vedic',\n\t},\n\t{\n\t\tpascal: 'RoxyKpPlanetsTable',\n\t\ttag: 'roxy-kp-planets-table',\n\t\tslug: 'kp-planets-table',\n\t\theading: 'KP planets',\n\t\tdescription: 'KP planets table with sub-lord and sub-sub-lord columns',\n\t\tdocsLabel: 'Vedic (KP)',\n\t\tendpointLabel: 'POST /vedic-astrology/kp/planets',\n\t\tdocsSummary: 'Sub-lord and sub-sub-lord columns',\n\t\ttopic: 'Vedic',\n\t},\n\t{\n\t\tpascal: 'RoxyKpRulingPlanets',\n\t\ttag: 'roxy-kp-ruling-planets',\n\t\tslug: 'kp-ruling-planets',\n\t\theading: 'KP ruling planets',\n\t\tdescription:\n\t\t\t'KP ruling planets with day lord, Moon and Lagna stellar hierarchies, and house significators',\n\t\tdocsLabel: 'Vedic (KP)',\n\t\tendpointLabel: 'POST /vedic-astrology/kp/ruling-planets',\n\t\tdocsSummary:\n\t\t\t'Day lord, Moon/Lagna hierarchies, ruling planets, significators with house meanings',\n\t\ttopic: 'Vedic',\n\t},\n\t{\n\t\tpascal: 'RoxyAshtakavargaGrid',\n\t\ttag: 'roxy-ashtakavarga-grid',\n\t\tslug: 'ashtakavarga-grid',\n\t\theading: 'Ashtakavarga',\n\t\tdescription: 'Sarva and Bhinna ashtakavarga heatmap with bindu scores',\n\t\tdocsLabel: 'Vedic',\n\t\tendpointLabel: 'POST /vedic-astrology/ashtakavarga',\n\t\tdocsSummary: 'Sarva, Bhinna, and Shodhya Pinda views in a tabbed heatmap',\n\t\ttopic: 'Vedic',\n\t},\n\t{\n\t\tpascal: 'RoxyShadbalaTable',\n\t\ttag: 'roxy-shadbala-table',\n\t\tslug: 'shadbala-table',\n\t\theading: 'Shadbala',\n\t\tdescription: 'Six-fold planetary strength with adequacy badge per planet',\n\t\tdocsLabel: 'Vedic',\n\t\tendpointLabel: 'POST /vedic-astrology/shadbala',\n\t\tdocsSummary:\n\t\t\t'Six-fold planetary strength bar plus rupas and adequacy badge',\n\t\ttopic: 'Vedic',\n\t},\n\t{\n\t\tpascal: 'RoxyDashaTimeline',\n\t\ttag: 'roxy-dasha-timeline',\n\t\tslug: 'dasha-timeline',\n\t\theading: 'Vimshottari dasha',\n\t\tdescription:\n\t\t\t'Vimshottari dasha timeline, mahadasha through prana, with the active period highlighted',\n\t\tdocsLabel: 'Vedic',\n\t\tendpointLabel: 'POST /vedic-astrology/dasha/{current,major,sub/...}',\n\t\tdocsSummary:\n\t\t\t'Vimshottari mahadasha, antardasha, pratyantardasha, sookshma and prana, drill-down at every level',\n\t\ttopic: 'Vedic',\n\t},\n\t{\n\t\tpascal: 'RoxyGunaMilan',\n\t\ttag: 'roxy-guna-milan',\n\t\tslug: 'guna-milan',\n\t\theading: 'Guna milan',\n\t\tdescription: '36-point Ashtakoota matrimonial compatibility breakdown',\n\t\tdocsLabel: 'Vedic',\n\t\tendpointLabel: 'POST /vedic-astrology/compatibility',\n\t\tdocsSummary: '36-point Ashtakoota with eight sub-scores',\n\t\ttopic: 'Vedic',\n\t},\n\t{\n\t\tpascal: 'RoxyPanchangTable',\n\t\ttag: 'roxy-panchang-table',\n\t\tslug: 'panchang-table',\n\t\theading: 'Panchang',\n\t\tdescription:\n\t\t\t'Panchang muhurta table with auspicious and inauspicious periods',\n\t\tdocsLabel: 'Vedic',\n\t\tendpointLabel: 'POST /vedic-astrology/panchang/{basic,detailed}',\n\t\tdocsSummary: '15+ muhurtas in detailed mode',\n\t\ttopic: 'Vedic',\n\t},\n\t{\n\t\tpascal: 'RoxyVedicAspects',\n\t\ttag: 'roxy-vedic-aspects',\n\t\tslug: 'vedic-aspects',\n\t\theading: 'Vedic aspects',\n\t\tdescription:\n\t\t\t'Vedic graha drishti table: which planet aspects which, with mutual aspects surfaced first',\n\t\tdocsLabel: 'Vedic',\n\t\tendpointLabel: 'POST /vedic-astrology/aspects',\n\t\tdocsSummary:\n\t\t\t'Graha drishti rows with aspect type, strength, and orb, plus mutual aspects',\n\t\ttopic: 'Vedic',\n\t},\n\t{\n\t\tpascal: 'RoxyHoraTable',\n\t\ttag: 'roxy-hora-table',\n\t\tslug: 'hora-table',\n\t\theading: 'Hora',\n\t\tdescription:\n\t\t\t'Vedic Hora (planetary hours) table: 12 day and 12 night horas by ruling planet for electional timing',\n\t\tdocsLabel: 'Vedic',\n\t\tendpointLabel: 'POST /vedic-astrology/panchang/hora',\n\t\tdocsSummary: 'Day and night planetary hours with ruling planet and window',\n\t\ttopic: 'Vedic',\n\t},\n\t{\n\t\tpascal: 'RoxyChoghadiyaGrid',\n\t\ttag: 'roxy-choghadiya-grid',\n\t\tslug: 'choghadiya-grid',\n\t\theading: 'Choghadiya',\n\t\tdescription: 'Day and night Choghadiya muhurta tiles for activity timing',\n\t\tdocsLabel: 'Vedic',\n\t\tendpointLabel: 'POST /vedic-astrology/panchang/choghadiya',\n\t\tdocsSummary: 'Day and night Choghadiya muhurta tiles colored by effect',\n\t\ttopic: 'Vedic',\n\t},\n\t{\n\t\tpascal: 'RoxyHeliacalTable',\n\t\ttag: 'roxy-heliacal-table',\n\t\tslug: 'heliacal-table',\n\t\theading: 'Heliacal rising and setting',\n\t\tdescription:\n\t\t\t'Whether each graha stands far enough from the Sun to be seen, with the date it next rises or sets',\n\t\tdocsLabel: 'Vedic',\n\t\tendpointLabel: 'POST /vedic-astrology/heliacal',\n\t\tdocsSummary:\n\t\t\t'Udaya and asta windows for the six visible grahas, the calculation behind Guru Asta and Shukra Asta',\n\t\ttopic: 'Vedic',\n\t},\n\t{\n\t\tpascal: 'RoxyVedicDaily',\n\t\ttag: 'roxy-vedic-daily',\n\t\tslug: 'vedic-daily',\n\t\theading: 'Vedic daily',\n\t\tdescription:\n\t\t\t'One native, one day: the verdict band, every graha state behind it, the Moon windows and the finance area',\n\t\tdocsLabel: 'Vedic',\n\t\tendpointLabel: 'POST /vedic-astrology/daily',\n\t\tdocsSummary:\n\t\t\t'Composed Vedic daily reading with the classical rule behind every graha state, the Tara and Chandrabala windows in full, and all three sidereal frames named',\n\t\ttopic: 'Vedic',\n\t},\n\t{\n\t\tpascal: 'RoxyGocharaTable',\n\t\ttag: 'roxy-gochara-table',\n\t\tslug: 'gochara-table',\n\t\theading: 'Gochara transits',\n\t\tdescription:\n\t\t\t'Where each graha transits now, the natal house it falls in, and the Ashtakavarga-qualified kaksha verdict',\n\t\tdocsLabel: 'Vedic',\n\t\tendpointLabel: 'POST /vedic-astrology/transit',\n\t\tdocsSummary:\n\t\t\t'Vedic gochara with aspects to the natal chart and the Gochara Kaksha reading drawn as a position within the sign',\n\t\ttopic: 'Vedic',\n\t},\n\t{\n\t\tpascal: 'RoxyBhavaBalaTable',\n\t\ttag: 'roxy-bhava-bala-table',\n\t\tslug: 'bhava-bala-table',\n\t\theading: 'Bhava Bala',\n\t\tdescription:\n\t\t\t'The twelve houses ranked by strength, with the three classical components that make up each total',\n\t\tdocsLabel: 'Vedic',\n\t\tendpointLabel: 'POST /vedic-astrology/bhava-bala',\n\t\tdocsSummary:\n\t\t\t'House strength in rupas and virupas, ranked, with Bhavadhipati, Dig and Drishti Bala shown as proportions of the total',\n\t\ttopic: 'Vedic',\n\t},\n\t{\n\t\tpascal: 'RoxyBhavChalitTable',\n\t\ttag: 'roxy-bhav-chalit-table',\n\t\tslug: 'bhav-chalit-table',\n\t\theading: 'Bhav Chalit',\n\t\tdescription:\n\t\t\t'Which grahas change house once the bhavas are cut at their real cusps instead of at sign boundaries',\n\t\tdocsLabel: 'Vedic',\n\t\tendpointLabel: 'POST /vedic-astrology/bhav-chalit',\n\t\tdocsSummary:\n\t\t\t'The Chalit chart against the Rashi chart, leading with how many grahas move and which, plus the unequal bhava spans',\n\t\ttopic: 'Vedic',\n\t},\n\t{\n\t\tpascal: 'RoxyUpagrahaTable',\n\t\ttag: 'roxy-upagraha-table',\n\t\tslug: 'upagraha-table',\n\t\theading: 'Upagrahas',\n\t\tdescription:\n\t\t\t'The eleven upagrahas in their time-based and Sun-based groups, with rashi, degree, and nakshatra',\n\t\tdocsLabel: 'Vedic',\n\t\tendpointLabel: 'POST /vedic-astrology/upagraha',\n\t\tdocsSummary:\n\t\t\t'Time-based and Sun-based upagrahas with rashi, degree, longitude and nakshatra',\n\t\ttopic: 'Vedic',\n\t},\n\t{\n\t\tpascal: 'RoxyCharaKarakas',\n\t\ttag: 'roxy-chara-karakas',\n\t\tslug: 'chara-karakas',\n\t\theading: 'Chara karakas',\n\t\tdescription:\n\t\t\t'Jaimini chara karakas ranked Atmakaraka first, with the scheme that produced the ranking',\n\t\tdocsLabel: 'Vedic',\n\t\tendpointLabel: 'POST /vedic-astrology/chara-karakas',\n\t\tdocsSummary:\n\t\t\t'Karaka offices in rank order with graha, degree, scheme, and what each is read for',\n\t\ttopic: 'Vedic',\n\t},\n\t{\n\t\tpascal: 'RoxyArudhaPadas',\n\t\ttag: 'roxy-arudha-padas',\n\t\tslug: 'arudha-padas',\n\t\theading: 'Arudha padas',\n\t\tdescription:\n\t\t\t'The twelve Jaimini arudha padas with their derivation, the Arudha Lagna and the Upapada marked',\n\t\tdocsLabel: 'Vedic',\n\t\tendpointLabel: 'POST /vedic-astrology/arudha',\n\t\tdocsSummary:\n\t\t\t'Twelve padas with bhava, lord, pada rashi, house from Lagna, and the classical exception marked',\n\t\ttopic: 'Vedic',\n\t},\n\t{\n\t\tpascal: 'RoxyYogaList',\n\t\ttag: 'roxy-yoga-list',\n\t\tslug: 'yoga-list',\n\t\theading: 'Yoga catalog',\n\t\tdescription:\n\t\t\t'Yoga reference cards from the catalog with optional detail mode',\n\t\tdocsLabel: 'Vedic',\n\t\tendpointLabel:\n\t\t\t'GET /vedic-astrology/yoga, POST /vedic-astrology/yoga/detect',\n\t\tdocsSummary:\n\t\t\t'Filterable yoga cards from the 300 plus yoga catalog, grouped by verdict in detect mode',\n\t\ttopic: 'Vedic',\n\t},\n\t{\n\t\tpascal: 'RoxyNakshatraCard',\n\t\ttag: 'roxy-nakshatra-card',\n\t\tslug: 'nakshatra-card',\n\t\theading: 'Nakshatra',\n\t\tdescription:\n\t\t\t'Nakshatra reference card with lord, deity, symbol, characteristics, and remedies',\n\t\tdocsLabel: 'Vedic',\n\t\tendpointLabel: 'GET /vedic-astrology/nakshatras/{id}',\n\t\tdocsSummary: 'Lord, deity, symbol, characteristics, remedies',\n\t\ttopic: 'Vedic',\n\t},\n\t{\n\t\tpascal: 'RoxyDoshaCard',\n\t\ttag: 'roxy-dosha-card',\n\t\tslug: 'dosha-card',\n\t\theading: 'Manglik dosha',\n\t\tdescription: 'Manglik, Kaal Sarp, or Sade Sati presence card',\n\t\tdocsLabel: 'Vedic',\n\t\tendpointLabel: 'POST /vedic-astrology/dosha/{manglik,kalsarpa,sadhesati}',\n\t\tdocsSummary: 'Presence, severity, remedies, scoped effects',\n\t\ttopic: 'Vedic',\n\t},\n\t{\n\t\tpascal: 'RoxyNumerologyCard',\n\t\ttag: 'roxy-numerology-card',\n\t\tslug: 'numerology-card',\n\t\theading: 'Life path number',\n\t\tdescription:\n\t\t\t'Numerology card for life path, expression, soul urge, personality, personal year, or full chart',\n\t\tdocsLabel: 'Numerology',\n\t\tendpointLabel:\n\t\t\t'POST /numerology/{life-path,expression,soul-urge,personality,birth-day,maturity,daily,personal-day,personal-month,personal-year,chart}',\n\t\tdocsSummary:\n\t\t\t'Life path, expression, soul urge, personality, personal year, full chart',\n\t\ttopic: 'Numerology',\n\t},\n\t{\n\t\tpascal: 'RoxyTarotCard',\n\t\ttag: 'roxy-tarot-card',\n\t\tslug: 'tarot-card',\n\t\theading: 'Daily tarot card',\n\t\tdescription: 'Single tarot card with upright/reversed flip animation',\n\t\tdocsLabel: 'Tarot',\n\t\tendpointLabel: 'GET /tarot/cards/{id}, POST /tarot/daily',\n\t\tdocsSummary: 'Single card with upright and reversed flip',\n\t\ttopic: 'Tarot',\n\t},\n\t{\n\t\tpascal: 'RoxyTarotCatalog',\n\t\ttag: 'roxy-tarot-catalog',\n\t\tslug: 'tarot-catalog',\n\t\theading: 'Tarot deck',\n\t\tdescription:\n\t\t\t'Responsive tarot deck gallery with card art, name, and an arcana and suit caption from the tarot catalog response',\n\t\tdocsLabel: 'Tarot',\n\t\tendpointLabel: 'GET /tarot/cards',\n\t\tdocsSummary: 'Deck gallery tiles with card art, name, and arcana and suit',\n\t\ttopic: 'Tarot',\n\t},\n\t{\n\t\tpascal: 'RoxyTarotSpread',\n\t\ttag: 'roxy-tarot-spread',\n\t\tslug: 'tarot-spread',\n\t\theading: 'Three-card spread',\n\t\tdescription:\n\t\t\t'Tarot spread renderer for three-card, Celtic Cross, love, or yes/no',\n\t\tdocsLabel: 'Tarot',\n\t\tendpointLabel:\n\t\t\t'POST /tarot/spreads/{three-card,celtic-cross,love}, /tarot/yes-no, /tarot/draw',\n\t\tdocsSummary: 'Spreads with positions and reading',\n\t\ttopic: 'Tarot',\n\t},\n\t{\n\t\tpascal: 'RoxyBodygraph',\n\t\ttag: 'roxy-bodygraph',\n\t\tslug: 'bodygraph',\n\t\theading: 'Bodygraph',\n\t\tdescription:\n\t\t\t'Human Design bodygraph with nine centers, channels, and activated gates, plus the full reading: type, strategy, authority, profile, definition, incarnation cross, and a gate and line meaning for every activation',\n\t\tdocsLabel: 'Human Design',\n\t\tendpointLabel: 'POST /human-design/bodygraph',\n\t\tdocsSummary:\n\t\t\t'Nine-center chart with defined and open centers, active channels, and gates, plus the type, strategy, authority, profile, and definition readings, the channels by circuit, the centers, and every activation with its gate and line meaning',\n\t\ttopic: 'Human Design',\n\t},\n\t{\n\t\tpascal: 'RoxyHdTypeCard',\n\t\ttag: 'roxy-hd-type-card',\n\t\tslug: 'hd-type-card',\n\t\theading: 'HD type',\n\t\tdescription:\n\t\t\t'Human Design type reading without the chart: type, strategy, authority, and profile, with the aura mechanic, the signature and not-self themes, and the interpretation behind each label. Also renders the profile response with its two line keynotes',\n\t\tdocsLabel: 'Human Design',\n\t\tendpointLabel: 'POST /human-design/type, /human-design/profile',\n\t\tdocsSummary:\n\t\t\t'Type, strategy, authority, and profile tiles with the aura, signature, and not-self themes, plus the reading behind each label and the profile line keynotes',\n\t\ttopic: 'Human Design',\n\t},\n\t{\n\t\tpascal: 'RoxyHdConnection',\n\t\ttag: 'roxy-hd-connection',\n\t\tslug: 'hd-connection',\n\t\theading: 'HD connection',\n\t\tdescription:\n\t\t\t'Human Design connection chart: the composite channels two people form, with circuit, dynamic, and which person holds each gate',\n\t\tdocsLabel: 'Human Design',\n\t\tendpointLabel: 'POST /human-design/connection',\n\t\tdocsSummary:\n\t\t\t'Electromagnetic, compromise, and dominance channels between two charts',\n\t\ttopic: 'Human Design',\n\t},\n\t{\n\t\tpascal: 'RoxyHdPenta',\n\t\ttag: 'roxy-hd-penta',\n\t\tslug: 'hd-penta',\n\t\theading: 'HD penta',\n\t\tdescription:\n\t\t\t'Human Design penta for a 3 to 5 person group: upper and lower triangle channels, defined or open, with core channels and member coverage',\n\t\tdocsLabel: 'Human Design',\n\t\tendpointLabel: 'POST /human-design/penta',\n\t\tdocsSummary: 'Group penta channels split into upper and lower triangles',\n\t\ttopic: 'Human Design',\n\t},\n\t{\n\t\tpascal: 'RoxyHdVariables',\n\t\ttag: 'roxy-hd-variables',\n\t\tslug: 'hd-variables',\n\t\theading: 'HD variables',\n\t\tdescription:\n\t\t\t'Human Design variables: the four PHS arrows (determination, environment, perspective, motivation) with left/right direction, color, tone, and base, plus the reading for each arrow and the cognition',\n\t\tdocsLabel: 'Human Design',\n\t\tendpointLabel: 'POST /human-design/variables',\n\t\tdocsSummary:\n\t\t\t'The four transformation arrows with direction, color, tone, and base, plus a reading per arrow grouped by layer and the cognition',\n\t\ttopic: 'Human Design',\n\t},\n\t{\n\t\tpascal: 'RoxyForecastTimeline',\n\t\ttag: 'roxy-forecast-timeline',\n\t\tslug: 'forecast-timeline',\n\t\theading: 'Forecast timeline',\n\t\tdescription:\n\t\t\t'Cross-domain forecast event strip colored by domain and weighted by significance',\n\t\tdocsLabel: 'Forecast',\n\t\tendpointLabel: 'POST /forecast/{timeline,significant-dates,transits}',\n\t\tdocsSummary:\n\t\t\t'Date-grouped events across Western, Vedic, and biorhythm domains, weighted by significance',\n\t\ttopic: 'Forecast',\n\t},\n\t{\n\t\tpascal: 'RoxyForecastDigest',\n\t\ttag: 'roxy-forecast-digest',\n\t\tslug: 'forecast-digest',\n\t\theading: 'Forecast digest',\n\t\tdescription:\n\t\t\t'Rolled-up forecast across the next 24 hours, 7, 30, and 90 days with per-domain breakdown and top events',\n\t\tdocsLabel: 'Forecast',\n\t\tendpointLabel: 'POST /forecast/digest',\n\t\tdocsSummary:\n\t\t\t'Per-window event counts, domain breakdown, and the highest-significance events',\n\t\ttopic: 'Forecast',\n\t},\n\t{\n\t\tpascal: 'RoxyBiorhythmChart',\n\t\ttag: 'roxy-biorhythm-chart',\n\t\tslug: 'biorhythm-chart',\n\t\theading: 'Daily biorhythm',\n\t\tdescription: 'Daily biorhythm bars or multi-day forecast cycle lines',\n\t\tdocsLabel: 'Biorhythm',\n\t\tendpointLabel: 'POST /biorhythm/{daily,forecast,critical-days}',\n\t\tdocsSummary: 'Daily bars, forecast cycle lines, critical days',\n\t\ttopic: 'Biorhythm',\n\t},\n\t{\n\t\tpascal: 'RoxyHexagram',\n\t\ttag: 'roxy-hexagram',\n\t\tslug: 'hexagram',\n\t\theading: 'I Ching hexagram',\n\t\tdescription:\n\t\t\t'I Ching hexagram drawn line by line with trigram glyphs, judgment, image, and a reading for every line: the oracle statement and what it asks of the querent. A cast highlights the moving lines and shows only their readings, plus the hexagram it transforms into',\n\t\tdocsLabel: 'I Ching',\n\t\tendpointLabel:\n\t\t\t'GET /iching/hexagrams/{number}, /iching/cast, POST /iching/daily, /iching/daily/cast',\n\t\tdocsSummary:\n\t\t\t'Hexagram figure with trigrams, judgment, image, and a reading per line (statement plus meaning); a cast highlights the moving lines and the resulting hexagram',\n\t\ttopic: 'I Ching',\n\t},\n\t{\n\t\tpascal: 'RoxyCrystalCard',\n\t\ttag: 'roxy-crystal-card',\n\t\tslug: 'crystal-card',\n\t\theading: 'Crystal detail',\n\t\tdescription:\n\t\t\t'Single-crystal detail card with photo, spiritual, emotional, and physical meaning, metaphysical attributes, affirmation, and pairings',\n\t\tdocsLabel: 'Crystals',\n\t\tendpointLabel: 'GET /crystals/{id}',\n\t\tdocsSummary:\n\t\t\t'Photo, meaning sections, chakra, zodiac, element, hardness, keywords, and pairings',\n\t\ttopic: 'Crystals',\n\t},\n\t{\n\t\tpascal: 'RoxyCrystalGrid',\n\t\ttag: 'roxy-crystal-grid',\n\t\tslug: 'crystal-grid',\n\t\theading: 'Crystal grid',\n\t\tdescription:\n\t\t\t'Responsive crystal gallery with photo, name, and colour swatches from any crystals list response',\n\t\tdocsLabel: 'Crystals',\n\t\tendpointLabel:\n\t\t\t'GET /crystals, /crystals/chakra/{chakra}, /crystals/element/{element}, /crystals/zodiac/{sign}, /crystals/birthstone/{month}, /crystals/search',\n\t\tdocsSummary: 'Crystal gallery tiles with photo, name, and colour swatches',\n\t\ttopic: 'Crystals',\n\t},\n\t{\n\t\tpascal: 'RoxyDreamCard',\n\t\ttag: 'roxy-dream-card',\n\t\tslug: 'dream-card',\n\t\theading: 'Dream symbol',\n\t\tdescription:\n\t\t\t'Dream symbol card with the symbol name, full interpretation, and dictionary letter',\n\t\tdocsLabel: 'Dreams',\n\t\tendpointLabel: 'GET /dreams/symbols/{id}',\n\t\tdocsSummary: 'Symbol name, interpretation body, and letter chip',\n\t\ttopic: 'Dreams',\n\t},\n\t{\n\t\tpascal: 'RoxyDreamSearch',\n\t\ttag: 'roxy-dream-search',\n\t\tslug: 'dream-search',\n\t\theading: 'Dream search',\n\t\tdescription:\n\t\t\t'Dream-symbol search results as selectable tiles; emits roxy-symbol-select to pair with roxy-dream-card',\n\t\tdocsLabel: 'Dreams',\n\t\tendpointLabel: 'GET /dreams/symbols',\n\t\tdocsSummary: 'Matched dream symbols as selectable tiles with a letter chip',\n\t\ttopic: 'Dreams',\n\t},\n\t{\n\t\tpascal: 'RoxyAngelNumberCard',\n\t\ttag: 'roxy-angel-number-card',\n\t\tslug: 'angel-number-card',\n\t\theading: 'Angel number',\n\t\tdescription:\n\t\t\t'Angel number card with title, core message, badges, keywords, life-area interpretations, affirmation, and action steps',\n\t\tdocsLabel: 'Angel Numbers',\n\t\tendpointLabel: 'GET /angel-numbers/numbers/{number}',\n\t\tdocsSummary:\n\t\t\t'Number meaning with spiritual, love, career, money, twin flame, biblical, and shadow sections',\n\t\ttopic: 'Angel Numbers',\n\t},\n\t{\n\t\tpascal: 'RoxyAngelNumberLookup',\n\t\ttag: 'roxy-angel-number-lookup',\n\t\tslug: 'angel-number-lookup',\n\t\theading: 'Angel number lookup',\n\t\tdescription:\n\t\t\t'Angel number sequence analysis with pattern classification, the known meaning when present, and a digit-root fallback for any number',\n\t\tdocsLabel: 'Angel Numbers',\n\t\tendpointLabel: 'GET /angel-numbers/lookup',\n\t\tdocsSummary: 'Pattern analysis plus known meaning and digit-root fallback',\n\t\ttopic: 'Angel Numbers',\n\t},\n\t{\n\t\tpascal: 'RoxyReferenceCard',\n\t\ttag: 'roxy-reference-card',\n\t\tslug: 'reference-card',\n\t\theading: 'Reference card',\n\t\tdescription:\n\t\t\t'Glossary entry for any reference lookup: zodiac sign, planet meaning, rashi, I Ching trigram, Human Design gate or center, numerology number or compound number',\n\t\tdocsLabel: 'Reference',\n\t\tendpointLabel:\n\t\t\t'GET /astrology/{signs,planet-meanings}/{id}, /vedic-astrology/rashis/{id}, /iching/trigrams/{id}, /human-design/{gates,centers}/{id}, /numerology/{meanings,compound-number}/{number}',\n\t\tdocsSummary:\n\t\t\t'Symbol, name, description, keyword chips, and an attribute grid for any glossary lookup',\n\t\ttopic: 'Helpers',\n\t},\n\t{\n\t\tpascal: 'RoxyEndpointForm',\n\t\ttag: 'roxy-endpoint-form',\n\t\tslug: 'endpoint-form',\n\t\theading: 'Schema-driven form',\n\t\tdescription:\n\t\t\t'Schema-driven form that emits roxy-submit with a validated payload',\n\t\tdocsLabel: 'Helper',\n\t\tendpointLabel: 'Any endpoint, from the spec',\n\t\tdocsSummary: 'Schema-driven form, emits roxy-submit',\n\t\ttopic: 'Helpers',\n\t\tselfFetching: true,\n\t},\n\t{\n\t\tpascal: 'RoxyLocationSearch',\n\t\ttag: 'roxy-location-search',\n\t\tslug: 'location-search',\n\t\theading: 'City search',\n\t\tdescription: 'City search input with debounced /location/search calls',\n\t\tdocsLabel: 'Helper',\n\t\tendpointLabel: 'GET /location/search',\n\t\tdocsSummary: 'Debounced city search input, emits roxy-location-select',\n\t\ttopic: 'Helpers',\n\t\tselfFetching: true,\n\t},\n\t{\n\t\tpascal: 'RoxyData',\n\t\ttag: 'roxy-data',\n\t\tslug: 'data',\n\t\theading: 'Generic renderer',\n\t\tdescription: 'Generic fallback renderer for any OpenAPI response shape',\n\t\tdocsLabel: 'Helper',\n\t\tendpointLabel: 'Any response shape',\n\t\tdocsSummary: 'Generic fallback renderer for unknown shapes',\n\t\ttopic: 'Helpers',\n\t\tselfFetching: true,\n\t},\n];\n\nexport type RoxyComponentSlug = (typeof ROXY_COMPONENTS)[number]['slug'];\n"],
  "mappings": "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N,QAAS,WACT,YAAa,qDACb,UAAW,UACX,cAAe,2BACf,YAAa,+CACb,MAAO,WACR,EACA,CACC,OAAQ,0BACR,IAAK,6BACL,KAAM,wBACN,QAAS,kBACT,YACC,+FACD,UAAW,UACX,cAAe,8BACf,YACC,gEACD,MAAO,WACR,EACA,CACC,OAAQ,oBACR,IAAK,sBACL,KAAM,iBACN,QAAS,WACT,YAAa,sDACb,UAAW,UACX,cAAe,2BACf,YACC,kEACD,MAAO,WACR,EACA,CACC,OAAQ,mBACR,IAAK,qBACL,KAAM,gBACN,QAAS,mBACT,YACC,sFACD,UAAW,UACX,cAAe,kCACf,YACC,gGACD,MAAO,WACR,EACA,CACC,OAAQ,mBACR,IAAK,qBACL,KAAM,gBACN,QAAS,UACT,YACC,wGACD,UAAW,UACX,cACC,kFACD,YACC,qFACD,MAAO,WACR,EACA,CACC,OAAQ,gBACR,IAAK,kBACL,KAAM,aACN,QAAS,aACT,YAAa,+BACb,UAAW,UACX,cAAe,4DACf,YAAa,+BACb,MAAO,WACR,EACA,CACC,OAAQ,oBACR,IAAK,sBACL,KAAM,iBACN,QAAS,kBACT,YACC,wEACD,UAAW,UACX,cAAe,yDACf,YAAa,2CACb,MAAO,WACR,EACA,CACC,OAAQ,0BACR,IAAK,4BACL,KAAM,uBACN,QAAS,mBACT,YACC,0GACD,UAAW,UACX,cAAe,mCACf,YACC,+FACD,MAAO,WACR,EACA,CACC,OAAQ,wBACR,IAAK,2BACL,KAAM,sBACN,QAAS,cACT,YACC,+FACD,UAAW,UACX,cAAe,8BACf,YACC,wFACD,MAAO,WACR,EACA,CACC,OAAQ,sBACR,IAAK,wBACL,KAAM,mBACN,QAAS,mBACT,YACC,2GACD,UAAW,UACX,cAAe,mCACf,YACC,+FACD,MAAO,WACR,EACA,CACC,OAAQ,qBACR,IAAK,uBACL,KAAM,kBACN,QAAS,kBACT,YACC,gIACD,UAAW,UACX,cACC,sHACD,YACC,qGACD,MAAO,WACR,EACA,CACC,OAAQ,qBACR,IAAK,uBACL,KAAM,kBACN,QAAS,YACT,YACC,oGACD,UAAW,QACX,cACC,gFACD,YACC,gGACD,MAAO,WACR,EACA,CACC,OAAQ,iBACR,IAAK,mBACL,KAAM,cACN,QAAS,cACT,YACC,8FACD,UAAW,UACX,cAAe,8BACf,YACC,8GACD,MAAO,WACR,EACA,CACC,OAAQ,qBACR,IAAK,uBACL,KAAM,kBACN,QAAS,oBACT,YACC,0GACD,UAAW,UACX,cAAe,8BACf,YACC,oGACD,MAAO,WACR,EACA,CACC,OAAQ,wBACR,IAAK,0BACL,KAAM,qBACN,QAAS,sBACT,YAAa,wCACb,UAAW,QACX,cACC,2FACD,YAAa,qCACb,MAAO,WACR,EACA,CACC,OAAQ,kBACR,IAAK,oBACL,KAAM,eACN,QAAS,eACT,YACC,4LACD,UAAW,QACX,cAAe,oCACf,YACC,yFACD,MAAO,OACR,EACA,CACC,OAAQ,sBACR,IAAK,wBACL,KAAM,mBACN,QAAS,mBACT,YAAa,sDACb,UAAW,QACX,cAAe,mDACf,YACC,iEACD,MAAO,OACR,EACA,CACC,OAAQ,cACR,IAAK,gBACL,KAAM,WACN,QAAS,WACT,YACC,yGACD,UAAW,aACX,cAAe,iCACf,YACC,mFACD,MAAO,OACR,EACA,CACC,OAAQ,wBACR,IAAK,2BACL,KAAM,sBACN,QAAS,gBACT,YACC,qHACD,UAAW,QACX,cAAe,oCACf,YACC,+EACD,MAAO,OACR,EACA,CACC,OAAQ,qBACR,IAAK,wBACL,KAAM,mBACN,QAAS,aACT,YAAa,0DACb,UAAW,aACX,cAAe,mCACf,YAAa,oCACb,MAAO,OACR,EACA,CACC,OAAQ,sBACR,IAAK,yBACL,KAAM,oBACN,QAAS,oBACT,YACC,+FACD,UAAW,aACX,cAAe,0CACf,YACC,sFACD,MAAO,OACR,EACA,CACC,OAAQ,uBACR,IAAK,yBACL,KAAM,oBACN,QAAS,eACT,YAAa,0DACb,UAAW,QACX,cAAe,qCACf,YAAa,6DACb,MAAO,OACR,EACA,CACC,OAAQ,oBACR,IAAK,sBACL,KAAM,iBACN,QAAS,WACT,YAAa,6DACb,UAAW,QACX,cAAe,iCACf,YACC,gEACD,MAAO,OACR,EACA,CACC,OAAQ,oBACR,IAAK,sBACL,KAAM,iBACN,QAAS,oBACT,YACC,0FACD,UAAW,QACX,cAAe,sDACf,YACC,oGACD,MAAO,OACR,EACA,CACC,OAAQ,gBACR,IAAK,kBACL,KAAM,aACN,QAAS,aACT,YAAa,0DACb,UAAW,QACX,cAAe,sCACf,YAAa,4CACb,MAAO,OACR,EACA,CACC,OAAQ,oBACR,IAAK,sBACL,KAAM,iBACN,QAAS,WACT,YACC,kEACD,UAAW,QACX,cAAe,kDACf,YAAa,gCACb,MAAO,OACR,EACA,CACC,OAAQ,mBACR,IAAK,qBACL,KAAM,gBACN,QAAS,gBACT,YACC,4FACD,UAAW,QACX,cAAe,gCACf,YACC,8EACD,MAAO,OACR,EACA,CACC,OAAQ,gBACR,IAAK,kBACL,KAAM,aACN,QAAS,OACT,YACC,uGACD,UAAW,QACX,cAAe,sCACf,YAAa,8DACb,MAAO,OACR,EACA,CACC,OAAQ,qBACR,IAAK,uBACL,KAAM,kBACN,QAAS,aACT,YAAa,6DACb,UAAW,QACX,cAAe,4CACf,YAAa,2DACb,MAAO,OACR,EACA,CACC,OAAQ,oBACR,IAAK,sBACL,KAAM,iBACN,QAAS,8BACT,YACC,oGACD,UAAW,QACX,cAAe,iCACf,YACC,sGACD,MAAO,OACR,EACA,CACC,OAAQ,iBACR,IAAK,mBACL,KAAM,cACN,QAAS,cACT,YACC,4GACD,UAAW,QACX,cAAe,8BACf,YACC,+JACD,MAAO,OACR,EACA,CACC,OAAQ,mBACR,IAAK,qBACL,KAAM,gBACN,QAAS,mBACT,YACC,4GACD,UAAW,QACX,cAAe,gCACf,YACC,mHACD,MAAO,OACR,EACA,CACC,OAAQ,qBACR,IAAK,wBACL,KAAM,mBACN,QAAS,aACT,YACC,oGACD,UAAW,QACX,cAAe,mCACf,YACC,yHACD,MAAO,OACR,EACA,CACC,OAAQ,sBACR,IAAK,yBACL,KAAM,oBACN,QAAS,cACT,YACC,sGACD,UAAW,QACX,cAAe,oCACf,YACC,sHACD,MAAO,OACR,EACA,CACC,OAAQ,oBACR,IAAK,sBACL,KAAM,iBACN,QAAS,YACT,YACC,mGACD,UAAW,QACX,cAAe,iCACf,YACC,iFACD,MAAO,OACR,EACA,CACC,OAAQ,mBACR,IAAK,qBACL,KAAM,gBACN,QAAS,gBACT,YACC,2FACD,UAAW,QACX,cAAe,sCACf,YACC,qFACD,MAAO,OACR,EACA,CACC,OAAQ,kBACR,IAAK,oBACL,KAAM,eACN,QAAS,eACT,YACC,iGACD,UAAW,QACX,cAAe,+BACf,YACC,kGACD,MAAO,OACR,EACA,CACC,OAAQ,eACR,IAAK,iBACL,KAAM,YACN,QAAS,eACT,YACC,kEACD,UAAW,QACX,cACC,+DACD,YACC,0FACD,MAAO,OACR,EACA,CACC,OAAQ,oBACR,IAAK,sBACL,KAAM,iBACN,QAAS,YACT,YACC,mFACD,UAAW,QACX,cAAe,uCACf,YAAa,iDACb,MAAO,OACR,EACA,CACC,OAAQ,gBACR,IAAK,kBACL,KAAM,aACN,QAAS,gBACT,YAAa,iDACb,UAAW,QACX,cAAe,2DACf,YAAa,+CACb,MAAO,OACR,EACA,CACC,OAAQ,qBACR,IAAK,uBACL,KAAM,kBACN,QAAS,mBACT,YACC,kGACD,UAAW,aACX,cACC,yIACD,YACC,2EACD,MAAO,YACR,EACA,CACC,OAAQ,gBACR,IAAK,kBACL,KAAM,aACN,QAAS,mBACT,YAAa,yDACb,UAAW,QACX,cAAe,2CACf,YAAa,6CACb,MAAO,OACR,EACA,CACC,OAAQ,mBACR,IAAK,qBACL,KAAM,gBACN,QAAS,aACT,YACC,oHACD,UAAW,QACX,cAAe,mBACf,YAAa,8DACb,MAAO,OACR,EACA,CACC,OAAQ,kBACR,IAAK,oBACL,KAAM,eACN,QAAS,oBACT,YACC,sEACD,UAAW,QACX,cACC,iFACD,YAAa,qCACb,MAAO,OACR,EACA,CACC,OAAQ,gBACR,IAAK,iBACL,KAAM,YACN,QAAS,YACT,YACC,sNACD,UAAW,eACX,cAAe,+BACf,YACC,+OACD,MAAO,cACR,EACA,CACC,OAAQ,iBACR,IAAK,oBACL,KAAM,eACN,QAAS,UACT,YACC,yPACD,UAAW,eACX,cAAe,iDACf,YACC,+JACD,MAAO,cACR,EACA,CACC,OAAQ,mBACR,IAAK,qBACL,KAAM,gBACN,QAAS,gBACT,YACC,iIACD,UAAW,eACX,cAAe,gCACf,YACC,yEACD,MAAO,cACR,EACA,CACC,OAAQ,cACR,IAAK,gBACL,KAAM,WACN,QAAS,WACT,YACC,2IACD,UAAW,eACX,cAAe,2BACf,YAAa,4DACb,MAAO,cACR,EACA,CACC,OAAQ,kBACR,IAAK,oBACL,KAAM,eACN,QAAS,eACT,YACC,wMACD,UAAW,eACX,cAAe,+BACf,YACC,oIACD,MAAO,cACR,EACA,CACC,OAAQ,uBACR,IAAK,yBACL,KAAM,oBACN,QAAS,oBACT,YACC,mFACD,UAAW,WACX,cAAe,uDACf,YACC,6FACD,MAAO,UACR,EACA,CACC,OAAQ,qBACR,IAAK,uBACL,KAAM,kBACN,QAAS,kBACT,YACC,2GACD,UAAW,WACX,cAAe,wBACf,YACC,iFACD,MAAO,UACR,EACA,CACC,OAAQ,qBACR,IAAK,uBACL,KAAM,kBACN,QAAS,kBACT,YAAa,yDACb,UAAW,YACX,cAAe,iDACf,YAAa,kDACb,MAAO,WACR,EACA,CACC,OAAQ,eACR,IAAK,gBACL,KAAM,WACN,QAAS,mBACT,YACC,uQACD,UAAW,UACX,cACC,uFACD,YACC,iKACD,MAAO,SACR,EACA,CACC,OAAQ,kBACR,IAAK,oBACL,KAAM,eACN,QAAS,iBACT,YACC,wIACD,UAAW,WACX,cAAe,qBACf,YACC,qFACD,MAAO,UACR,EACA,CACC,OAAQ,kBACR,IAAK,oBACL,KAAM,eACN,QAAS,eACT,YACC,mGACD,UAAW,WACX,cACC,iJACD,YAAa,8DACb,MAAO,UACR,EACA,CACC,OAAQ,gBACR,IAAK,kBACL,KAAM,aACN,QAAS,eACT,YACC,qFACD,UAAW,SACX,cAAe,2BACf,YAAa,oDACb,MAAO,QACR,EACA,CACC,OAAQ,kBACR,IAAK,oBACL,KAAM,eACN,QAAS,eACT,YACC,yGACD,UAAW,SACX,cAAe,sBACf,YAAa,+DACb,MAAO,QACR,EACA,CACC,OAAQ,sBACR,IAAK,yBACL,KAAM,oBACN,QAAS,eACT,YACC,yHACD,UAAW,gBACX,cAAe,sCACf,YACC,gGACD,MAAO,eACR,EACA,CACC,OAAQ,wBACR,IAAK,2BACL,KAAM,sBACN,QAAS,sBACT,YACC,uIACD,UAAW,gBACX,cAAe,4BACf,YAAa,8DACb,MAAO,eACR,EACA,CACC,OAAQ,oBACR,IAAK,sBACL,KAAM,iBACN,QAAS,iBACT,YACC,kKACD,UAAW,YACX,cACC,wLACD,YACC,0FACD,MAAO,SACR,EACA,CACC,OAAQ,mBACR,IAAK,qBACL,KAAM,gBACN,QAAS,qBACT,YACC,qEACD,UAAW,SACX,cAAe,8BACf,YAAa,wCACb,MAAO,UACP,aAAc,EACf,EACA,CACC,OAAQ,qBACR,IAAK,uBACL,KAAM,kBACN,QAAS,cACT,YAAa,0DACb,UAAW,SACX,cAAe,uBACf,YAAa,0DACb,MAAO,UACP,aAAc,EACf,EACA,CACC,OAAQ,WACR,IAAK,YACL,KAAM,OACN,QAAS,mBACT,YAAa,2DACb,UAAW,SACX,cAAe,qBACf,YAAa,+CACb,MAAO,UACP,aAAc,EACf,CACD,EnGxuBO,IAAMC,GACZC,GAAgB,IAAKC,GAAMA,EAAE,IAAI",
  "names": ["src_exports", "__export", "ROXY_COMPONENTS", "ROXY_UI_COMPONENTS", "ROXY_UI_VERSION", "RoxyAngelNumberCard", "RoxyAngelNumberLookup", "RoxyArudhaPadas", "RoxyAshtakavargaGrid", "RoxyAspectsTable", "RoxyAstrocartographyMap", "RoxyBhavChalitTable", "RoxyBhavaBalaTable", "RoxyBiorhythmChart", "RoxyBodygraph", "RoxyCharaKarakas", "RoxyChoghadiyaGrid", "RoxyCompatibilityCard", "RoxyCrystalCard", "RoxyCrystalGrid", "RoxyDashaTimeline", "RoxyData", "RoxyDivisionalChart", "RoxyDoshaCard", "RoxyDreamCard", "RoxyDreamSearch", "RoxyEndpointForm", "RoxyEphemerisTable", "RoxyFixedStars", "RoxyForecastDigest", "RoxyForecastTimeline", "RoxyGocharaTable", "RoxyGunaMilan", "RoxyHdConnection", "RoxyHdPenta", "RoxyHdTypeCard", "RoxyHdVariables", "RoxyHeliacalTable", "RoxyHexagram", "RoxyHoraTable", "RoxyHoroscopeCard", "RoxyKpChart", "RoxyKpPlanetsTable", "RoxyKpRulingPlanets", "RoxyLocalSpaceCompass", "RoxyLocationSearch", "RoxyMoonPhase", "RoxyNakshatraCard", "RoxyNatalChart", "RoxyNumerologyCard", "RoxyPanchangTable", "RoxyPositionsTable", "RoxyProfectionCard", "RoxyReferenceCard", "RoxyRelocationWheel", "RoxyShadbalaTable", "RoxySynastryChart", "RoxyTarotCard", "RoxyTarotCatalog", "RoxyTarotSpread", "RoxyTransitWheel", "RoxyTransitsTable", "RoxyUpagrahaTable", "RoxyVedicAspects", "RoxyVedicDaily", "RoxyVedicKundli", "RoxyVedicPlanetsTable", "RoxyWesternPlanetsTable", "RoxyYogaList", "roxyDataScript", "serializeRoxyData", "__toCommonJS", "import_lit", "import_decorators", "LIFE_AREA_SECTIONS", "buildMeaningSections", "t", "meaning", "biblical", "shadow", "sections", "body", "import_lit", "import_decorators", "import_if_defined", "import_lit", "API_LANGUAGES", "resolveLang", "el", "inherited", "apiLang", "tag", "base", "API_LANGUAGES", "lookupKey", "s", "capitalize", "humanize", "c", "GLOBAL_KEY", "registry", "holder", "existing", "created", "interpolate", "template", "vars", "match", "key", "translate", "lang", "source", "catalogs", "registry", "tag", "catalog", "lookupKey", "labelsFor", "lang", "fieldLabels", "registry", "tag", "fieldLabel", "name", "optionLabel", "field", "value", "onLocaleChange", "listener", "listeners", "LocaleController", "host", "onLocaleChange", "RoxyLocalizedElement", "source", "vars", "LocaleController", "translate", "resolveLang", "import_lit", "baseStyles", "KEY_REFUSED_MESSAGE", "keyIsRefused", "key", "dispatchKeyRefusal", "host", "opts", "KEY_REFUSED_MESSAGE", "DEFAULT_BASE_URL", "readApiError", "res", "body", "FetchController", "host", "req", "controller", "json", "err", "signal", "url", "k", "v", "headers", "keyIsRefused", "KEY_REFUSED_MESSAGE", "dispatchKeyRefusal", "buildRequest", "endpoint", "method", "values", "queryKeys", "rest", "path", "_match", "name", "inQuery", "query", "import_lit", "import_lit", "chevron", "disclosureStyles", "readingDetailStyles", "interpAccordionStyles", "renderKeywordChips", "keywords", "k", "renderReadingDetail", "d", "t", "line", "label", "value", "renderInterpAccordion", "sections", "name", "heading", "shown", "s", "i", "chevron", "ROXY_DATA_CLASS", "serializeRoxyData", "data", "roxyDataScript", "isJsonScript", "el", "MarkupDataController", "host", "parsed", "inline", "child", "text", "submitContextConverter", "value", "parsed", "RoxyDataElement", "RoxyLocalizedElement", "detail", "buildRequest", "MarkupDataController", "FetchController", "baseStyles", "changed", "names", "n", "rules", "sections", "name", "heading", "renderInterpAccordion", "data", "body", "raw", "values", "message", "banner", "__decorateClass", "RoxyAngelNumberCard", "RoxyDataElement", "d", "energy", "energyClass", "keywords", "steps", "k", "s", "buildMeaningSections", "baseStyles", "interpAccordionStyles", "__decorateClass", "import_lit", "import_decorators", "ENERGY_LABEL", "RoxyAngelNumberLookup", "RoxyDataElement", "d", "known", "root", "heading", "steps", "k", "s", "meaning", "name", "biblical", "shadow", "buildMeaningSections", "baseStyles", "disclosureStyles", "interpAccordionStyles", "__decorateClass", "import_lit", "import_decorators", "resolve", "table", "name", "lookupKey", "PLANET_GLYPH", "planetGlyph", "PLANET_ABBR", "planetAbbr", "SIGN_GLYPH", "signGlyph", "SIGN_ABBR", "signAbbr", "SIGNS_ORDER", "ASPECT_SYMBOL", "aspectSymbol", "TRIGRAM_GLYPH", "trigramGlyph", "MOON_PHASE_EMOJI", "moonPhaseEmoji", "key", "bare", "alias", "import_lit", "DATE_ONLY", "BARE_TIME", "NAIVE_DATETIME", "CANONICAL", "intlLocales", "locale", "cached", "canonical", "resolveDisplayDate", "input", "naive", "formatTime", "d", "timeZone", "formatDate", "formatTimeRange", "t", "start", "end", "formatDateRange", "a", "b", "formatDateGrain", "grain", "formatMonthDay", "formatWeekdayDay", "formatMonthYear", "year", "month", "monthName", "formatInteger", "value", "formatNumber", "dp", "formatPercent", "ASPECT_CLASS", "normalizeAspect", "formatAspectName", "capitalize", "AYANAMSA_LABEL", "formatAyanamsa", "type", "degrees", "label", "humanize", "deg", "formatDateTime", "time", "merged", "date", "clock", "distinctSanskrit", "name", "sanskritName", "formatWithSanskrit", "english", "sanskrit", "frameCaptionStyles", "renderFrameCaption", "locale", "frame", "t", "label", "formatAyanamsa", "degrees", "formatNumber", "renderFrameProvenance", "frames", "rows", "f", "governs", "LEAD_IDS", "RoxyArudhaPadas", "RoxyDataElement", "d", "padas", "exceptions", "p", "renderFrameCaption", "glyph", "sign", "signGlyph", "cell", "g", "baseStyles", "frameCaptionStyles", "__decorateClass", "import_lit", "import_decorators", "import_lit", "tablistStyles", "renderTablist", "opts", "items", "active", "onSelect", "label", "idPrefix", "controls", "e", "idx", "it", "delta", "next", "root", "TABS", "RoxyAshtakavargaGrid", "RoxyDataElement", "d", "signs", "renderFrameCaption", "renderTablist", "id", "source", "count", "note", "sav", "sign", "i", "signGlyph", "bhinna", "reduced", "rsav", "rows", "caption", "totals", "s", "row", "_", "pinda", "baseStyles", "frameCaptionStyles", "tablistStyles", "__decorateClass", "import_lit", "import_decorators", "provenance", "o", "orbs", "bodies", "PATTERN_ORDER", "RoxyAspectsTable", "RoxyDataElement", "d", "aspects", "patterns", "summary", "date", "formatDate", "options", "a", "i", "b", "p", "s", "total", "byType", "type", "count", "formatAspectName", "g1", "planetGlyph", "g2", "nature", "formatNumber", "idx", "status", "meaning", "transit", "hasBody", "header", "chevron", "m", "text", "renderReadingDetail", "t", "planets", "ordered", "formatPercent", "name", "glyph", "isApex", "baseStyles", "disclosureStyles", "interpAccordionStyles", "__decorateClass", "import_lit", "import_decorators", "PLANET_COLOR", "CYCLE", "planetColor", "name", "index", "capitalize", "WORLD_LAND_PATH", "W", "H", "lonToX", "lon", "latToY", "lat", "TROPIC", "POLAR", "formatLon", "formatLat", "toSegments", "points", "segments", "current", "prevLon", "p", "s", "ANGLE_LABEL", "RoxyAstrocartographyMap", "RoxyDataElement", "data", "lines", "bd", "formatDateTime", "WORLD_LAND_PATH", "l", "i", "meridians", "x", "axis", "parallels", "y", "refs", "line", "index", "color", "planetColor", "glyph", "planetGlyph", "items", "planet", "angle", "dashed", "labelY", "anchor", "best", "pts", "rows", "chevron", "text", "code", "baseStyles", "disclosureStyles", "interpAccordionStyles", "__decorateClass", "import_lit", "import_decorators", "houseWords", "houses", "themes", "h", "houseThemeLine", "house", "RoxyBhavChalitTable", "RoxyDataElement", "d", "grahas", "bhavas", "moved", "g", "count", "renderFrameCaption", "b", "themes", "glyph", "planetGlyph", "words", "houseWords", "occupants", "formatNumber", "baseStyles", "frameCaptionStyles", "__decorateClass", "import_lit", "import_decorators", "COMPONENTS", "RoxyBhavaBalaTable", "RoxyDataElement", "d", "bhavas", "peak", "b", "renderFrameCaption", "c", "themes", "words", "houseWords", "formatNumber", "v", "baseStyles", "frameCaptionStyles", "__decorateClass", "import_lit", "import_decorators", "CYCLE_COLOR", "FORECAST_CYCLES", "CYCLE_LABEL", "RoxyBiorhythmChart", "RoxyDataElement", "d", "raw", "entries", "cycle", "value", "v", "normalized", "spot", "humanize", "formatPercent", "pct", "color", "days", "w", "h", "xStep", "s", "formatDateRange", "points", "day", "i", "x", "y", "formatDate", "doubles", "sections", "label", "baseStyles", "disclosureStyles", "interpAccordionStyles", "__decorateClass", "import_lit", "import_decorators", "import_lit", "X_MIN", "X_MAX", "Y_MIN", "Y_MAX", "UNIT", "VIEW_W", "VIEW_H", "g", "x", "y", "AXIS", "mirrorX", "GATE_RADIUS", "GATE_FONT_SIZE", "CENTER_NAME_FONT_SIZE", "COL", "EDGE", "G_EDGE", "GATES_BY_CENTER", "SPLEEN_TO_SOLAR_PLEXUS", "buildGatePoints", "points", "centerOf", "spleenGate", "center", "gates", "gate", "GATE_POINTS", "GATE_CENTER", "CHART_AXIS_X", "shape", "corners", "squareShape", "halfWidth", "top", "bottom", "SQUARE_HALF", "SPLEEN_SHAPE", "CENTER_GEOMETRY", "CHANNEL_PAIRS", "BODY_PATH", "buildBodyPath", "m", "segs", "pt", "i", "ptm", "p", "polygonPoints", "pts", "sourceClass", "s", "renderBody", "renderChannels", "gateSources", "lines", "a", "b", "pa", "pb", "mid", "from", "sources", "cls", "renderCenters", "defined", "names", "stateWords", "c", "isDefined", "label", "at", "centroid", "renderCenterName", "words", "longest", "n", "w", "room", "widthAt", "size", "UPPERCASE_EM", "step", "xs", "lo", "hi", "t", "halfDisc", "sweep", "renderGates", "titles", "out", "num", "title", "BODYGRAPH_VIEWBOX", "renderBodygraphSvg", "input", "import_lit", "bodygraphChartStyles", "CENTER_NAME_FONT_SIZE", "GATE_FONT_SIZE", "import_lit", "hdReadingStyles", "sideWord", "side", "t", "renderHdFacts", "facts", "shown", "f", "renderHdThemes", "signature", "notSelf", "renderHdKeynotes", "k", "displayField", "lang", "name", "fieldLabel", "humanize", "displayOption", "field", "value", "optionLabel", "LOCALIZED", "display", "row", "key", "english", "rec", "localized", "canonical", "displayList", "labels", "label", "foldLocalized", "folded", "base", "LEGEND_COLORS", "groupByGate", "gates", "byGate", "g", "bucket", "RoxyBodygraph", "RoxyDataElement", "d", "centers", "definedCenters", "c", "gateSources", "gate", "list", "gateTitles", "centerNames", "display", "BODYGRAPH_VIEWBOX", "renderBodygraphSvg", "titles", "bodies", "sideWord", "planetGlyph", "ic", "renderHdFacts", "renderHdThemes", "named", "color", "CENTER_GEOMETRY", "names", "sections", "renderHdKeynotes", "channels", "groups", "key", "index", "circuit", "chevron", "id", "definedCount", "i", "personality", "design", "split", "side", "shown", "sideNote", "renderTablist", "v", "open", "glyph", "body", "hex", "baseStyles", "tablistStyles", "disclosureStyles", "interpAccordionStyles", "hdReadingStyles", "bodygraphChartStyles", "__decorateClass", "import_lit", "import_decorators", "SCHEMES", "RoxyCharaKarakas", "RoxyDataElement", "d", "karakas", "renderFrameCaption", "k", "i", "isFirst", "glyph", "planetGlyph", "sGlyph", "signGlyph", "formatNumber", "baseStyles", "frameCaptionStyles", "__decorateClass", "import_lit", "import_decorators", "RoxyChoghadiyaGrid", "RoxyDataElement", "period", "now", "start", "end", "effectClass", "current", "lordGlyph", "planetGlyph", "timeRange", "formatTimeRange", "d", "date", "dayChoghadiya", "nightChoghadiya", "formatDate", "p", "baseStyles", "__decorateClass", "import_lit", "import_decorators", "RELATIONSHIP_PLANETS", "ELEMENTS", "RoxyCompatibilityCard", "RoxyDataElement", "d", "out", "k", "v", "score", "breakdown", "rating", "archetype", "advice", "summary", "interpretation", "strengths", "challenges", "keyAspects", "readings", "humanize", "s", "a", "formatAspect", "b", "sc", "persons", "sections", "planet", "pair", "at", "p", "sign", "capitalize", "deg", "glyph", "signGlyph", "degree", "planetGlyph", "eb", "p1", "p2", "max", "e", "shared", "el", "cores", "label", "core", "formatPercent", "name", "cycle", "baseStyles", "disclosureStyles", "interpAccordionStyles", "__decorateClass", "locale", "withReading", "aspect", "normalizeAspect", "orb", "formatNumber", "head", "import_lit", "import_decorators", "COLOR_WORDS", "CSS_COLORS", "COLOR_QUALIFIERS", "cssColor", "name", "bases", "qualifiers", "word", "qualifier", "base", "first", "second", "color", "partner", "keepPct", "RoxyCrystalCard", "RoxyDataElement", "d", "colors", "keywords", "pairs", "month", "monthName", "c", "cssColor", "k", "p", "label", "value", "values", "m", "present", "v", "text", "baseStyles", "__decorateClass", "import_lit", "import_decorators", "RoxyCrystalGrid", "RoxyDataElement", "d", "crystals", "title", "total", "c", "col", "cssColor", "monthName", "baseStyles", "__decorateClass", "import_lit", "import_decorators", "levelOf", "d", "parentOf", "formatBalance", "b", "parts", "periodHouseWords", "sig", "themes", "houseWords", "formatSpan", "locale", "p", "start", "formatDate", "end", "formatDateTime", "RoxyDashaTimeline", "RoxyDataElement", "periods", "maxYears", "grain", "grainFor", "readings", "frame", "tabs", "view", "t", "renderTablist", "v", "chain", "rows", "n", "formatNumber", "formatAyanamsa", "lord", "label", "value", "parent", "span", "began", "formatDuration", "sections", "levels", "period", "remaining", "left", "active", "houses", "now", "max", "years", "width", "current", "progress", "trackClass", "cls", "formatDateGrain", "baseStyles", "disclosureStyles", "interpAccordionStyles", "tablistStyles", "__decorateClass", "DAYS_PER_YEAR", "days", "hours", "minYears", "import_lit", "import_decorators", "import_if_defined", "TITLE_KEYS", "MAX_TITLE_LEN", "IMAGE_KEYS", "SKIP_KEYS", "QUOTE_KEYS", "SUPPRESS_ALWAYS", "SUPPRESS_NAMED", "SUPPRESS_NOISE", "DETAILS_KEYS", "DEEP_DEPTH", "DETAILS_ROWS", "normKey", "k", "isBadgeKey", "ISO_DATE", "ENUM_STRING", "LINK_STRING", "MAX_DEPTH", "isPrimitive", "value", "isComplex", "isEmptyValue", "values", "RoxyData", "RoxyDataElement", "data", "arr", "v", "rows", "clean", "r", "keys", "complex", "mixed", "colClass", "table", "displayField", "row", "obj", "folded", "foldLocalized", "titled", "n", "input", "titleKey", "imageKey", "summaryKey", "quoteKey", "entries", "sections", "key", "size", "body", "heading", "formatNumber", "time", "formatTime", "date", "formatDate", "humanize", "seen", "baseStyles", "__decorateClass", "import_lit", "import_decorators", "import_lit", "normalizeLongitude", "lon", "wrapped", "longitudeToSignPosition", "longitude", "signIndex", "within", "degree", "minuteFloat", "minute", "second", "SIGNS_ORDER", "formatSignPosition", "signLabel", "sign", "splitDegreeInSign", "deg", "formatDegreeInSign", "oppositePoint", "arcMidpoint", "start", "end", "s", "span", "fanOut", "items", "longitudeOf", "minSeparation", "placed", "item", "a", "b", "i", "prev", "cur", "wanted", "last", "shift", "p", "arcSeparation", "width", "radius", "polarToCartesian", "cx", "cy", "angleDeg", "angleRad", "VIEW_BOX", "MARGIN", "INNER", "CENTRE", "RASHI_TO_SIGN", "SIGNS_ORDER", "s", "CHART_STYLES", "RETRO_MARK", "COMBUST_MARK", "WAR_MARK", "signIndex", "sign", "isDivisionalPlacement", "p", "cellSign", "longitudeToSignPosition", "grahaLabel", "abbr", "planetAbbr", "retro", "degree", "grahaTitle", "t", "parts", "capitalize", "divisional", "sp", "minute", "pada", "renderPlanetStack", "planets", "cx", "baseY", "lineHeight", "startY", "j", "yPos", "toKundliViewModel", "meta", "options", "divisionLabel", "lagnaOverride", "states", "combust", "c", "wars", "w", "placements", "override", "lagnaSign", "name", "pos", "rashiKey", "graha", "key", "SOUTH_CELL", "SOUTH_CELL_GRID", "southCellRect", "g", "renderSouthFrame", "a", "b", "d", "e", "houseNumberInSign", "lagnaIdx", "signIdx", "renderSouthCell", "isLagna", "houseNum", "r", "cy", "sAbbr", "signAbbr", "slashInset", "renderSouthSvg", "vm", "lagnaKey", "NORTH_VERTICES", "centroidOf", "pts", "x", "y", "NORTH_HOUSE_CENTERS", "renderNorthFrame", "tl", "tr", "br", "bl", "top", "right", "bottom", "left", "renderNorthCell", "rashiNum", "rashiOffsetY", "ascOffsetY", "rashiLabel", "renderNorthSvg", "i", "EAST_CELL", "eastCells", "polys", "out", "points", "EAST_CELLS", "renderEastFrame", "renderEastCell", "cell", "cen", "polyPoints", "renderEastSvg", "hasLagna", "renderGrahaMarkLegend", "placed", "keys", "renderMissingLagnaNote", "renderKundliCaptions", "legend", "renderKundliSvg", "style", "renderKundliStyleTablist", "active", "setStyle", "renderTablist", "id", "source", "import_lit", "kundliStyles", "RoxyDivisionalChart", "RoxyDataElement", "next", "division", "label", "toKundliViewModel", "d", "vm", "vargottama", "distinctSanskrit", "renderFrameCaption", "renderKundliCaptions", "renderKundliStyleTablist", "renderKundliSvg", "planet", "planetGlyph", "baseStyles", "frameCaptionStyles", "kundliStyles", "tablistStyles", "__decorateClass", "import_lit", "import_decorators", "DOSHA_LABELS", "RoxyDoshaCard", "RoxyDataElement", "d", "present", "label", "sevLower", "tier", "pct", "barColor", "renderFrameCaption", "r", "sections", "key", "value", "nestedKey", "nestedValue", "baseStyles", "frameCaptionStyles", "__decorateClass", "import_lit", "import_decorators", "RoxyDreamCard", "RoxyDataElement", "d", "baseStyles", "__decorateClass", "import_lit", "import_decorators", "RoxyDreamSearch", "RoxyDataElement", "d", "symbols", "total", "s", "baseStyles", "__decorateClass", "import_lit", "import_decorators", "import_if_defined", "TILE_MAX", "LOCATION_TRIO", "LOCATION_PAIR", "splitCoordinateName", "name", "m", "ZODIAC_LOWER", "SIGNS_ORDER", "s", "resolveSchema", "schema", "all", "representative", "union", "scalarType", "real", "t", "classifyInput", "type", "isZodiacEnum", "values", "lower", "v", "toField", "resolved", "opts", "rep", "buildFormModel", "op", "schemas", "endpoint", "fields", "hasLang", "bodyRef", "bodySchema", "required", "sub", "subRequired", "subName", "subSchema", "r", "coord", "param", "deriveTitle", "summary", "lead", "humanize", "deriveSubmitLabel", "e", "sliceFileName", "method", "path", "ROXY_UI_VERSION", "PROD_SPEC_URL", "SLICE_BASE", "ROXY_UI_VERSION", "specCache", "SpecHttpError", "status", "SpecMissingOperationError", "method", "path", "loadSpec", "url", "pending", "specCache", "res", "SpecHttpError", "err", "randomSeed", "parseArrayValue", "raw", "trimmed", "parsed", "s", "RoxyEndpointForm", "RoxyLocalizedElement", "PROD_SPEC_URL", "SpecMissingOperationError", "spec", "path", "op", "buildFormModel", "SLICE_BASE", "sliceFileName", "model", "init", "f", "remembered", "name", "value", "LOCATION_TRIO", "g", "keys", "group", "inGroup", "LOCATION_PAIR", "n", "own", "req", "only", "deriveSubmitLabel", "apiLang", "displayField", "field", "displayOption", "e", "detail", "keyOf", "next", "lat", "lon", "tz", "opts", "cur", "root", "missing", "out", "v", "queryKeys", "lang", "labels", "locGroups", "isLocation", "lead", "chevron", "labelId", "zodiac", "isZodiacEnum", "active", "opt", "i", "selected", "glyph", "signGlyph", "id", "checked", "type", "placeholder", "names", "humanize", "fields", "flat", "flatReq", "flatOpt", "named", "reqGroups", "optGroups", "hasAdvanced", "kind", "baseStyles", "disclosureStyles", "__decorateClass", "import_lit", "import_decorators", "SAMPLE_INSTANT", "RoxyEphemerisTable", "RoxyDataElement", "data", "vm", "locale", "formatMonthYear", "b", "signs", "s", "glyph", "planetGlyph", "i", "r", "p", "degree", "minute", "splitDegreeInSign", "g", "signGlyph", "label", "display", "date", "formatMonthDay", "start", "end", "range", "row", "formatWeekdayDay", "cell", "body", "classes", "order", "series", "day", "samples", "bodies", "name", "first", "last", "ingresses", "retrogrades", "ingressDates", "stationDates", "cur", "prev", "open", "rows", "byName", "labels", "key", "out", "sign", "SIGNS_ORDER", "baseStyles", "__decorateClass", "import_lit", "import_decorators", "RoxyFixedStars", "RoxyDataElement", "data", "conjunctions", "stars", "formatNumber", "c", "i", "chevron", "s", "g", "signGlyph", "formatDegreeInSign", "k", "baseStyles", "disclosureStyles", "interpAccordionStyles", "__decorateClass", "import_lit", "import_decorators", "RoxyForecastDigest", "RoxyDataElement", "d", "windows", "range", "formatDate", "w", "days", "top", "a", "b", "byDomain", "domains", "dom", "n", "humanize", "e", "sig", "label", "formatNumber", "baseStyles", "__decorateClass", "import_lit", "import_decorators", "DOMAIN_LABEL", "DOMAIN_ORDER", "RoxyForecastTimeline", "RoxyDataElement", "d", "events", "grouped", "present", "dom", "e", "formatDate", "date", "dayEvents", "map", "key", "list", "sig", "width", "body", "capitalize", "target", "aspect", "normalizeAspect", "aspectClass", "ASPECT_CLASS", "aspectSym", "aspectSymbol", "formatAspectName", "orb", "formatNumber", "qualifier", "humanize", "baseStyles", "__decorateClass", "import_lit", "import_decorators", "KAKSHA_COUNT", "RoxyGocharaTable", "RoxyDataElement", "d", "planets", "key", "moonSign", "p", "formatDateTime", "renderFrameCaption", "k", "glyph", "planetGlyph", "pos", "longitudeToSignPosition", "formatDegreeInSign", "label", "items", "a", "formatAspectName", "formatNumber", "here", "verdict", "total", "head", "gave", "_", "i", "baseStyles", "frameCaptionStyles", "__decorateClass", "import_lit", "import_decorators", "KOOTA_MAX", "RoxyGunaMilan", "RoxyDataElement", "d", "breakdown", "b", "score", "max", "pct", "trackColor", "fillColor", "dashFill", "dashGap", "formatNumber", "formatPercent", "maxScore", "defaultMax", "x", "renderFrameCaption", "baseStyles", "frameCaptionStyles", "__decorateClass", "name", "import_lit", "import_decorators", "RoxyHdConnection", "RoxyDataElement", "d", "gateSources", "c", "left", "right", "gate", "held", "definedCenters", "centerNames", "gateTitles", "name", "who", "BODYGRAPH_VIEWBOX", "renderBodygraphSvg", "channels", "centers", "renderHdFacts", "humanize", "s", "sections", "channelCount", "by", "b", "together", "centerWho", "names", "centerName", "id", "gateList", "baseStyles", "disclosureStyles", "interpAccordionStyles", "hdReadingStyles", "bodygraphChartStyles", "__decorateClass", "gates", "list", "defined", "a", "import_lit", "import_decorators", "import_lit", "VIEW_W", "VIEW_H", "BAR_X", "BAR_W", "BAR_H", "GATE_R", "BADGE_R", "BADGE_STEP", "BARS", "PENTA_VIEWBOX", "columnX", "index", "count", "isUpper", "rung", "GATE_CENTER", "topGate", "renderBars", "b", "renderHolders", "cx", "cy", "held", "letterFor", "m", "i", "renderGate", "gate", "on", "renderPentaSvg", "input", "rows", "upper", "inRow", "r", "top", "bottom", "topCy", "botCy", "mid", "gTop", "gBottom", "cls", "RoxyHdPenta", "RoxyDataElement", "d", "rungs", "c", "holders", "g", "PENTA_VIEWBOX", "renderPentaSvg", "memberLetter", "channels", "gates", "s", "gaps", "hasCore", "renderHdFacts", "label", "note", "members", "filled", "baseStyles", "hdReadingStyles", "__decorateClass", "MEMBER_LETTERS", "i", "indices", "held", "import_lit", "import_decorators", "RoxyHdTypeCard", "RoxyDataElement", "d", "isTypeShape", "display", "renderHdFacts", "renderHdThemes", "renderHdKeynotes", "baseStyles", "disclosureStyles", "interpAccordionStyles", "hdReadingStyles", "__decorateClass", "import_lit", "import_decorators", "RoxyHdVariables", "RoxyDataElement", "d", "arrows", "a", "b", "quadrantOrder", "glyph", "baseName", "display", "sideWord", "baseDescription", "readable", "groups", "key", "bucket", "index", "layer", "list", "open", "cog", "cogLabel", "facets", "f", "chevron", "baseStyles", "disclosureStyles", "interpAccordionStyles", "__decorateClass", "position", "import_lit", "import_decorators", "EVENT_WORD", "NEXT_EVENT_WORD", "RoxyHeliacalTable", "RoxyDataElement", "d", "grahas", "g", "glyph", "planetGlyph", "next", "last", "vars", "formatDateTime", "side", "at", "shifts", "formatNumber", "baseStyles", "__decorateClass", "import_lit", "import_decorators", "RoxyHexagram", "RoxyDataElement", "d", "cast", "daily", "resolved", "h", "castLines", "changingLinePositions", "dailyMessage", "resultingHexagram", "lines", "changing", "readings", "l", "idx", "realIdx", "isChanging", "broken", "trigramGlyph", "a", "b", "all", "isCast", "shown", "sections", "binary", "c", "baseStyles", "disclosureStyles", "interpAccordionStyles", "__decorateClass", "import_lit", "import_decorators", "RoxyHoraTable", "RoxyDataElement", "d", "day", "night", "formatDate", "label", "horas", "h", "planetGlyph", "formatTimeRange", "baseStyles", "__decorateClass", "import_lit", "import_decorators", "RoxyHoroscopeCard", "RoxyDataElement", "d", "sign", "glyph", "signGlyph", "energy", "dateLabel", "formatDate", "luckyNumber", "luckyColor", "luckyNumbers", "luckyDays", "compatibleSigns", "s", "moonSign", "moonPhase", "transits", "t", "weeks", "keyDates", "w", "k", "baseStyles", "__decorateClass", "import_lit", "import_decorators", "TABS", "LEVELS", "nakshatraPada", "nakshatra", "pada", "chain", "values", "dedupe", "houses", "RoxyKpChart", "RoxyDataElement", "d", "rows", "p", "nodes", "name", "node", "asc", "formatSignPosition", "formatAyanamsa", "renderTablist", "id", "bodies", "b", "cusps", "themes", "c", "houseThemeLine", "sig", "houseWise", "planetWise", "l", "h", "houseWords", "words", "value", "level", "s", "labels", "label", "baseStyles", "tablistStyles", "__decorateClass", "import_lit", "import_decorators", "RoxyKpPlanetsTable", "RoxyDataElement", "d", "planets", "formatNumber", "p", "baseStyles", "__decorateClass", "import_lit", "import_decorators", "RoxyKpRulingPlanets", "RoxyDataElement", "d", "significators", "p", "i", "s", "words", "houseWords", "baseStyles", "__decorateClass", "import_lit", "import_decorators", "SIZE", "CENTER", "RIM", "SPOKE", "GLYPH_R", "TICK_LABEL_R", "azimuthPoint", "az", "rad", "PRINCIPAL", "RoxyLocalSpaceCompass", "RoxyDataElement", "data", "bodies", "bd", "formatDateTime", "ticks", "outer", "inner", "ns1", "ns2", "ew1", "ew2", "labels", "source", "pos", "cardinal", "b", "i", "color", "planetColor", "below", "end", "glyphPos", "glyph", "planetGlyph", "altLabel", "baseStyles", "__decorateClass", "import_lit", "import_decorators", "debounce", "fn", "wait", "timer", "debounced", "args", "RoxyLocationSearch", "RoxyLocalizedElement", "DEFAULT_BASE_URL", "debounce", "q", "value", "target", "changed", "refused", "keyIsRefused", "dispatchKeyRefusal", "controller", "url", "headers", "key", "res", "readApiError", "json", "err", "city", "KEY_REFUSED_MESSAGE", "idx", "baseStyles", "__decorateClass", "import_lit", "import_decorators", "RoxyMoonPhase", "RoxyDataElement", "d", "list", "month", "year", "phase", "emoji", "phaseEmoji", "formatDate", "formatPercent", "formatNumber", "k", "p", "baseStyles", "__decorateClass", "moonPhaseEmoji", "import_lit", "import_decorators", "RoxyNakshatraCard", "RoxyDataElement", "n", "remedies", "baseStyles", "__decorateClass", "import_lit", "import_decorators", "import_lit", "aspectLineStyles", "MIN_OPACITY", "MAX_OPACITY", "MIN_WIDTH", "MAX_WIDTH", "aspectLineStyle", "a", "strength", "t", "opacity", "width", "PATTERN_ORDER", "SIZE", "CENTER", "OUTER_R", "SIGN_R", "HOUSE_R", "PLANET_R", "ANGLE_TICK_R", "ANGLE_LABEL_R", "GLYPH_WIDTH", "DEG_LABEL_WIDTH", "ANGLE_LABEL_WIDTH", "RoxyNatalChart", "RoxyDataElement", "m", "lon", "data", "planets", "aspects", "view", "formatDateTime", "renderTablist", "v", "bodies", "p", "display", "pairKey", "a", "b", "lookupKey", "byPair", "g", "planetGlyph", "row", "ri", "rowGlyph", "col", "name", "normalizeAspect", "sym", "aspectSymbol", "formatAspectName", "cls", "ASPECT_CLASS", "orb", "formatNumber", "asc", "mc", "marks", "oppositePoint", "pof", "normalizeLongitude", "vertex", "fanOut", "arcSeparation", "item", "longitude", "displayLongitude", "label", "angle", "labelAngle", "tickInner", "polarToCartesian", "tickOuter", "labelPos", "moved", "leaderEnd", "houses", "h", "_", "i", "start", "end", "SIGNS_ORDER", "sign", "pos", "signGlyph", "house", "next", "mid", "arcMidpoint", "ascSignIndex", "houseNum", "ticks", "deg", "isMajor", "inner", "sp", "longitudeToSignPosition", "separation", "placed", "trueLon", "displayLon", "trueAngle", "displayAngle", "glyphPos", "degPos", "rimPos", "leaderInner", "glyph", "retro", "degLabel", "offset", "summary", "ai", "retrogrades", "displayList", "value", "ELEMENTS", "MODALITIES", "ELEMENT_LABEL", "MODALITY_LABEL", "order", "dominantEl", "capitalize", "dominantMod", "cells", "el", "idx", "mod", "colTotal", "s", "isDomRow", "rowTotal", "patterns", "sorted", "ordered", "formatPercent", "isApex", "sections", "interp", "lead", "detail", "renderKeywordChips", "planetMap", "l1", "l2", "p1", "p2", "aspectName", "aspectClass", "orbLabel", "aspectLineStyle", "baseStyles", "aspectLineStyles", "tablistStyles", "disclosureStyles", "interpAccordionStyles", "__decorateClass", "import_lit", "import_decorators", "MASTER_NUMBERS", "GUIDANCE_FIELDS", "RoxyNumerologyCard", "RoxyDataElement", "d", "headerLabel", "LABELS", "readings", "isMaster", "karmicDebtText", "formatDate", "joinCycle", "cores", "v", "ins", "lucky", "profile", "maturity", "k", "humanize", "lessons", "debt", "year", "passion", "subconscious", "letters", "present", "n", "digit", "count", "l", "m", "p", "ageRange", "letterAside", "num", "title", "master", "r", "name", "sections", "key", "label", "lists", "shown", "items", "item", "value", "baseStyles", "disclosureStyles", "interpAccordionStyles", "__decorateClass", "type", "theme", "start", "end", "letter", "import_lit", "import_decorators", "meta", "parts", "name", "RoxyPanchangTable", "RoxyDataElement", "d", "detailed", "basic", "muhurtas", "auspiciousWindows", "inauspicious", "inauspiciousWindows", "showTimings", "formatDate", "l", "formatTime", "m", "t", "n", "y", "k", "rows", "formatPercent", "formatWithSanskrit", "label", "value", "v", "range", "formatTimeRange", "windows", "w", "i", "b", "span", "p", "c", "items", "tone", "time", "body", "start", "end", "r", "h", "lon", "formatSignPosition", "baseStyles", "__decorateClass", "import_lit", "import_decorators", "RoxyPositionsTable", "RoxyDataElement", "data", "vm", "cols", "b", "r", "glyph", "planetGlyph", "formatNumber", "sign", "degree", "g", "signGlyph", "formatDegreeInSign", "longitude", "p", "longitudeToSignPosition", "rows", "sections", "l", "capitalize", "formatDate", "d", "angleRows", "baseStyles", "disclosureStyles", "interpAccordionStyles", "__decorateClass", "import_lit", "import_decorators", "RoxyProfectionCard", "RoxyDataElement", "data", "sGlyph", "signGlyph", "lordGlyph", "planetGlyph", "lordNatalGlyph", "formatDate", "baseStyles", "__decorateClass", "import_lit", "import_decorators", "TITLE_KEYS", "PROSE_KEYS", "SKIP_KEYS", "PROSE_LENGTH", "partName", "key", "humanize", "objectLabel", "obj", "foldLocalized", "v", "RoxyReferenceCard", "RoxyDataElement", "d", "rec", "title", "symbol", "keywords", "prose", "lists", "facts", "collect", "label", "value", "depth", "items", "s", "i", "k", "readings", "text", "displayField", "isProse", "type", "number", "baseStyles", "__decorateClass", "import_lit", "import_decorators", "import_if_defined", "glyphFor", "name", "planetGlyph", "RoxyRelocationWheel", "RoxyDataElement", "data", "c", "formatInteger", "p", "m", "baseStyles", "__decorateClass", "import_lit", "import_decorators", "BALA_COMPONENTS", "RoxyShadbalaTable", "RoxyDataElement", "d", "sorted", "a", "b", "renderFrameCaption", "p", "glyph", "planetGlyph", "values", "total", "s", "v", "isAdequate", "badgeClass", "badgeLabel", "rupasStr", "formatNumber", "i", "grow", "ishta", "kashta", "ishtaStr", "kashtaStr", "baseStyles", "frameCaptionStyles", "__decorateClass", "import_lit", "import_decorators", "READING_COUNT", "SIZE", "CENTER", "OUTER_R", "SIGN_R", "P1_R", "P2_R", "RoxySynastryChart", "RoxyDataElement", "d", "person1", "person2", "compatibilityScore", "analysis", "interAspects", "p1Planets", "p2Planets", "score", "summaryText", "strengths", "challenges", "hasPlanets", "ranked", "a", "b", "lead", "i", "s", "p1", "p2", "card", "p", "index", "asc", "shown", "display", "formatNumber", "text", "label", "sign", "signGlyph", "cards", "c", "byType", "type", "count", "formatAspectName", "g1", "planetGlyph", "g2", "nature", "meaning", "aside", "chevron", "renderKeywordChips", "longitude", "_", "angle", "start", "polarToCartesian", "end", "SIGNS_ORDER", "pos", "planets", "radius", "cls", "personIndex", "degPos", "glyph", "sp", "longitudeToSignPosition", "retro", "degLabel", "house", "tooltip", "data", "items", "make", "signIdx", "innerR", "tickPos", "labelPos", "aspects", "longitudeOf", "list", "name", "target", "capitalize", "l1", "l2", "out", "inn", "aspectName", "normalizeAspect", "ASPECT_CLASS", "orbLabel", "aspectLineStyle", "baseStyles", "aspectLineStyles", "disclosureStyles", "interpAccordionStyles", "__decorateClass", "import_lit", "import_decorators", "GUIDANCE_FIELDS", "RoxyTarotCard", "RoxyDataElement", "d", "card", "reversed", "keywords", "meta", "readings", "k", "oriented", "renderTablist", "id", "imageUrl", "name", "cls", "guidance", "sections", "key", "label", "baseStyles", "disclosureStyles", "interpAccordionStyles", "tablistStyles", "__decorateClass", "import_lit", "import_decorators", "RoxyTarotCatalog", "RoxyDataElement", "d", "cards", "title", "total", "c", "cardMeta", "baseStyles", "__decorateClass", "capitalize", "import_lit", "import_decorators", "titleCase", "v", "RoxyTarotSpread", "RoxyDataElement", "d", "isYesNo", "isDrawn", "positions", "cards", "answer", "strength", "verdictCard", "spreadLabel", "question", "summary", "yesNoInterp", "answerClass", "k", "p", "c", "baseStyles", "__decorateClass", "import_lit", "import_decorators", "SIZE", "CENTER", "AXIS_LABEL_R", "OUTER_R", "SIGN_R", "BAND_R", "TRANSIT_R", "TRANSIT_DEG_R", "NATAL_R", "NATAL_DEG_R", "TRANSIT_LINE_R", "NATAL_LINE_R", "HUB_R", "HOUSE_NUM_R", "GLYPH_WIDTH", "DEG_LABEL_WIDTH", "LEADER_TICK", "LEADER_FOOT", "glyphFor", "name", "label", "planetGlyph", "degLabel", "longitude", "sp", "longitudeToSignPosition", "byName", "list", "m", "p", "capitalize", "RoxyTransitWheel", "RoxyDataElement", "value", "raw", "out", "h", "i", "a", "b", "c", "d", "natal", "transit", "aspects", "when", "formatDateTime", "count", "houses", "ticks", "deg", "angle", "isMajor", "polarToCartesian", "SIGNS_ORDER", "_", "start", "end", "cusps", "cusp", "next", "arcMidpoint", "sign", "pos", "signGlyph", "bodies", "radius", "degRadius", "cls", "kind", "leaderSign", "separation", "arcSeparation", "fanOut", "displayLongitude", "degPos", "retro", "body", "display", "tooltip", "displaced", "tick", "foot", "natalBy", "transitBy", "t", "n", "from", "to", "normalizeAspect", "orb", "formatNumber", "ASPECT_CLASS", "aspectLineStyle", "given", "asc", "normalizeLongitude", "oppositePoint", "lon", "inner", "outer", "houseSystem", "s", "byType", "type", "formatAspectName", "transiting", "showHouses", "rows", "cell", "houseCell", "key", "sections", "renderKeywordChips", "baseStyles", "aspectLineStyles", "disclosureStyles", "interpAccordionStyles", "__decorateClass", "import_lit", "import_decorators", "RoxyTransitsTable", "RoxyDataElement", "d", "transitDate", "transitTime", "transitPlanets", "transitAspects", "summary", "dateStr", "formatDate", "formatTime", "aspectCount", "tab", "renderTablist", "v", "planets", "p", "pGlyph", "planetGlyph", "sGlyph", "signGlyph", "speedArrow", "formatNumber", "aspects", "a", "idx", "tGlyph", "nGlyph", "nature", "interp", "type", "formatAspectName", "status", "chevron", "renderReadingDetail", "baseStyles", "tablistStyles", "disclosureStyles", "readingDetailStyles", "__decorateClass", "import_lit", "import_decorators", "GROUPS", "RoxyUpagrahaTable", "RoxyDataElement", "d", "groups", "g", "renderFrameCaption", "group", "rows", "body", "u", "sGlyph", "signGlyph", "formatSignPosition", "formatNumber", "baseStyles", "frameCaptionStyles", "__decorateClass", "import_lit", "import_decorators", "RoxyVedicAspects", "RoxyDataElement", "d", "aspects", "mutual", "planets", "when", "formatDate", "formatTime", "p", "formatSignPosition", "m", "a", "formatNumber", "baseStyles", "__decorateClass", "import_lit", "import_decorators", "STATE_LABEL", "STATES", "KAKSHA_COUNT", "BAND_LABEL", "LEVEL_LABEL", "RoxyVedicDaily", "RoxyDataElement", "d", "grahas", "counts", "t", "supportive", "formatDateTime", "s", "x", "p", "limb", "label", "l", "themes", "house", "g", "glyph", "planetGlyph", "theme", "k", "verdict", "gave", "head", "formatNumber", "tara", "bala", "span", "from", "to", "b", "f", "line", "c", "rows", "r", "renderFrameProvenance", "baseStyles", "frameCaptionStyles", "__decorateClass", "import_lit", "import_decorators", "RoxyVedicKundli", "RoxyDataElement", "next", "toKundliViewModel", "d", "vm", "title", "renderFrameCaption", "renderKundliCaptions", "renderKundliStyleTablist", "renderKundliSvg", "baseStyles", "frameCaptionStyles", "kundliStyles", "tablistStyles", "__decorateClass", "import_lit", "import_decorators", "GRAHA_ORDER", "RoxyVedicPlanetsTable", "RoxyDataElement", "meta", "seen", "rows", "name", "entry", "d", "renderFrameCaption", "p", "isLagna", "glyph", "planetGlyph", "sGlyph", "signGlyph", "formatSignPosition", "moderns", "m", "i", "formatWithSanskrit", "combust", "chevron", "c", "dist", "formatNumber", "orb", "wars", "w", "interp", "entries", "v", "houses", "h", "yogas", "y", "baseStyles", "frameCaptionStyles", "disclosureStyles", "__decorateClass", "import_lit", "import_decorators", "RoxyWesternPlanetsTable", "RoxyDataElement", "d", "rows", "p", "display", "points", "name", "point", "r", "glyph", "planetGlyph", "sGlyph", "signGlyph", "speed", "formatNumber", "formatSignPosition", "capitalize", "baseStyles", "__decorateClass", "import_lit", "import_decorators", "suppressor", "y", "FAMILY_ORDER", "familyRank", "i", "VERDICTS", "RoxyYogaList", "RoxyDataElement", "verdict", "yogas", "ordered", "a", "b", "quality", "cls", "capitalize", "yoga", "result", "by", "label", "d", "lc", "allYogas", "detected", "filtered", "presentCount", "renderFrameCaption", "v", "detailYogas", "total", "catalogYogas", "baseStyles", "frameCaptionStyles", "__decorateClass", "ROXY_COMPONENTS", "ROXY_UI_COMPONENTS", "ROXY_COMPONENTS", "c"]
}
