diff --git a/src/apps/gameboard/static/apps/gameboard/natus-wheel.js b/src/apps/gameboard/static/apps/gameboard/natus-wheel.js
index 384d33d..1cea60d 100644
--- a/src/apps/gameboard/static/apps/gameboard/natus-wheel.js
+++ b/src/apps/gameboard/static/apps/gameboard/natus-wheel.js
@@ -103,6 +103,12 @@ const NatusWheel = (() => {
'Cooperation', 'Regeneration', 'Enterprise', 'Career', 'Reward', 'Reprisal',
];
+ // Cardinal / Fixed / Mutable — parallel index to SIGNS
+ const SIGN_MODALITIES = [
+ 'Cardinal', 'Fixed', 'Mutable', 'Cardinal', 'Fixed', 'Mutable',
+ 'Cardinal', 'Fixed', 'Mutable', 'Cardinal', 'Fixed', 'Mutable',
+ ];
+
// ── State ─────────────────────────────────────────────────────────────────
let _svg = null;
@@ -186,6 +192,21 @@ const NatusWheel = (() => {
return ((ecliptic % 360) + 360) % 360 % 30;
}
+ /** Return 1-based house number for an ecliptic degree given 12 cusps. */
+ function _planetHouse(degree, cusps) {
+ degree = ((degree % 360) + 360) % 360;
+ for (let i = 0; i < 12; i++) {
+ const start = ((cusps[i] % 360) + 360) % 360;
+ const end = ((cusps[(i + 1) % 12] % 360) + 360) % 360;
+ if (start < end) {
+ if (start <= degree && degree < end) return i + 1;
+ } else {
+ if (degree >= start || degree < end) return i + 1;
+ }
+ }
+ return 1;
+ }
+
/** Inline SVG for a zodiac sign icon (preloaded path). */
function _signIconSvg(signName) {
const d = _signPaths[signName];
@@ -400,8 +421,14 @@ const NatusWheel = (() => {
if (_ttBody) {
_ttBody.innerHTML =
- `
${item.name} (${sym})
` +
- `@${inDeg}° ${pdata.sign} (${icon})${rx}
` +
+ `` +
+ `` +
+ `@${inDeg}° ${pdata.sign}${rx}` +
+ `${icon}` +
+ `
` +
aspectHtml;
}
@@ -524,14 +551,41 @@ const NatusWheel = (() => {
function _activateSign(idx) {
_activeRing = 'signs';
_activeIdx = idx;
- const sign = _signItems[idx];
+ const sign = _signItems[idx];
+ const elKey = sign.element.toLowerCase();
+ const modality = SIGN_MODALITIES[idx];
+ const vecImg = _elementVectorImg(sign.element);
+ const iconSvg = _signIconSvg(sign.name) ||
+ `${sign.symbol}`;
+
+ let planetsHtml = '';
+ if (_currentData) {
+ const cusps = (_currentData.houses || {}).cusps || [];
+ const inSign = Object.entries(_currentData.planets || {})
+ .filter(([, p]) => p.sign === sign.name)
+ .sort((a, b) => a[1].degree - b[1].degree);
+ inSign.forEach(([pname, pdata]) => {
+ const psym = PLANET_SYMBOLS[pname] || pname[0];
+ const inDeg = _inSignDeg(pdata.degree).toFixed(1);
+ const house = cusps.length ? _planetHouse(pdata.degree, cusps) : null;
+ const domain = house ? HOUSE_LABELS[house] : '';
+ planetsHtml +=
+ `${psym} @${inDeg}°` +
+ (domain ? `, House of ${domain}` : '') + `
`;
+ });
+ }
+
if (_ttBody) {
_ttBody.innerHTML =
``;
+ `${sign.name}` +
+ `${iconSvg}` +
+ `` +
+ `` +
+ `${modality} ${sign.element}` +
+ vecImg +
+ `
` +
+ (planetsHtml ? `${planetsHtml}
` : '');
}
_positionTooltipAtItem('signs', idx);
if (_tooltipEl) {
@@ -544,12 +598,28 @@ const NatusWheel = (() => {
_activeRing = 'houses';
_activeIdx = idx;
const house = _houseItems[idx];
+ const cusps = (_currentData && (_currentData.houses || {}).cusps) || [];
+
+ let planetsHtml = '';
+ if (cusps.length && _currentData) {
+ const inHouse = Object.entries(_currentData.planets || {})
+ .filter(([, p]) => _planetHouse(p.degree, cusps) === house.num)
+ .sort((a, b) => a[1].degree - b[1].degree);
+ inHouse.forEach(([pname, pdata]) => {
+ const psym = PLANET_SYMBOLS[pname] || pname[0];
+ const inDeg = _inSignDeg(pdata.degree).toFixed(1);
+ const sicon = _signIconSvg(pdata.sign) || (SIGNS.find(s => s.name === pdata.sign) || {}).symbol || '';
+ planetsHtml += `${psym} @${inDeg}° ${sicon}
`;
+ });
+ }
+
if (_ttBody) {
_ttBody.innerHTML =
``;
+ `House of ${house.label}` +
+ `${house.num}` +
+ `` +
+ (planetsHtml ? `${planetsHtml}
` : '');
}
_positionTooltipAtItem('houses', idx);
if (_tooltipEl) {
diff --git a/src/static_src/scss/_natus.scss b/src/static_src/scss/_natus.scss
index 9ae330d..dd0e8bd 100644
--- a/src/static_src/scss/_natus.scss
+++ b/src/static_src/scss/_natus.scss
@@ -505,10 +505,83 @@ body[class*="-light"] {
padding: 0.75rem 0.75rem 0.75rem 1.5rem;
min-width: 14rem;
- .tt-title { font-size: 1.25rem; font-weight: 700; margin-bottom: 0.3rem; }
+ .tt-title { font-size: 1.25rem; font-weight: 700; margin-bottom: 0; }
.tt-description { font-size: 0.75rem; }
.tt-sign-icon { fill: currentColor; vertical-align: middle; margin-bottom: 0.1em; }
+ // Planet tooltip — flex row: name | symbol; location row: @deg° Sign | sign icon
+ .tt-planet-header {
+ display: flex;
+ align-items: baseline;
+ justify-content: space-between;
+ gap: 0.5rem;
+ margin-bottom: 0.2rem;
+ }
+ .tt-planet-sym {
+ font-size: 1.2rem;
+ opacity: 0.85;
+ }
+ .tt-planet-loc {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 0.5rem;
+ font-size: 0.8rem;
+ margin-bottom: 0.3rem;
+ }
+ .tt-planet-sign-icon { font-size: 1.2rem; line-height: 1; }
+
+ // Sign tooltip — name in element color | SVG icon; modality | vector; planets
+ .tt-sign-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 0.5rem;
+ margin-bottom: 0.2rem;
+ }
+ .tt-sign-icon-wrap {
+ font-size: 1.5rem;
+ line-height: 1;
+ flex-shrink: 0;
+ .tt-sign-icon { fill: currentColor; }
+ }
+ .tt-sign-meta {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ font-size: 0.75rem;
+ opacity: 0.85;
+ margin-bottom: 0.3rem;
+ }
+ .tt-sign-planets {
+ display: flex;
+ flex-direction: column;
+ gap: 0.15rem;
+ margin-top: 0.1rem;
+ font-size: 0.85rem;
+ }
+
+ // House tooltip — "House of X" | number; planets in house
+ .tt-house-header {
+ display: flex;
+ align-items: baseline;
+ justify-content: space-between;
+ gap: 0.5rem;
+ margin-bottom: 0.3rem;
+ }
+ .tt-house-num {
+ font-size: 1.4rem;
+ font-weight: 700;
+ opacity: 0.55;
+ flex-shrink: 0;
+ }
+ .tt-house-planets {
+ display: flex;
+ flex-direction: column;
+ gap: 0.15rem;
+ font-size: 0.85rem;
+ }
+
// DON|DOFF aspect line toggle — stacked at top-left outside the tooltip box,
// matching the PRV/NXT pattern at the bottom corners.
.nw-asp-don,