Files
python-tdd/src/static_src/scss/_natus.scss

316 lines
8.1 KiB
SCSS
Raw Normal View History

PICK SKY overlay: D3 natal wheel, Character model, PySwiss aspects+tz PySwiss: - calculate_aspects() in calc.py (conjunction/sextile/square/trine/opposition with orbs) - /api/tz/ endpoint (timezonefinder lat/lon → IANA timezone) - aspects included in /api/chart/ response - timezonefinder==8.2.2 added to requirements - 14 new unit tests (test_calc.py) + 12 new integration tests (TimezoneApiTest, aspect fields) Main app: - Sign, Planet, AspectType, HouseLabel reference models + seeded migrations (0032–0033) - Character model with birth_dt/lat/lon/place, house_system, chart_data, celtic_cross, confirmed_at/retired_at lifecycle (migration 0034) - natus_preview proxy view: calls PySwiss /api/chart/ + optional /api/tz/ auto-resolution, computes planet-in-house distinctions, returns enriched JSON - natus_save view: find-or-create draft Character, confirmed_at on action='confirm' - natus-wheel.js: D3 v7 SVG natal wheel (elements pie, signs, houses, planets, aspects, ASC/MC axes); NatusWheel.draw() / redraw() / clear() - _natus_overlay.html: Nominatim place autocomplete (debounced 400ms), geolocation button with reverse-geocode city name, live chart preview (debounced 300ms), tz auto-fill, NVM / SAVE SKY footer; html.natus-open class toggle pattern - _natus.scss: Gaussian backdrop+modal, two-column form|wheel layout, suggestion dropdown, portrait collapse at 600px, landscape sidebar z-index sink - room.html: include overlay when table_status == SKY_SELECT Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 02:09:26 -04:00
// ─── Natus (Pick Sky) overlay ────────────────────────────────────────────────
// Gaussian backdrop + centred modal, matching the gate/sig overlay pattern.
// Open state: html.natus-open (added by JS on PICK SKY click).
//
// Layout: header / two-column body (form | wheel) / footer
// Collapses to stacked single-column below 600 px.
// ── Scroll-lock ───────────────────────────────────────────────────────────────
html.natus-open {
overflow: hidden;
#id_aperture_fill { opacity: 1; }
}
// ── Backdrop ──────────────────────────────────────────────────────────────────
.natus-backdrop {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.75);
backdrop-filter: blur(5px);
z-index: 100;
pointer-events: none;
// Hidden until html.natus-open
opacity: 0;
transition: opacity 0.15s ease;
}
html.natus-open .natus-backdrop {
opacity: 1;
}
// ── Overlay shell (positions + scrolls the modal) ─────────────────────────────
.natus-overlay {
position: fixed;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
z-index: 120;
overflow-y: auto;
overscroll-behavior: contain;
pointer-events: none;
// Hidden until html.natus-open
visibility: hidden;
@media (orientation: landscape) {
$sidebar-w: 4rem;
left: $sidebar-w;
right: $sidebar-w;
}
}
html.natus-open .natus-overlay {
visibility: visible;
pointer-events: none; // modal itself is pointer-events: auto
}
// ── Modal panel ───────────────────────────────────────────────────────────────
.natus-modal {
pointer-events: auto;
display: flex;
flex-direction: column;
width: 92vw;
max-width: 840px;
max-height: 92vh;
border: 0.1rem solid rgba(var(--terUser), 0.25);
border-radius: 0.5rem;
background: rgba(var(--priUser), 1);
overflow: hidden;
// Fade + slide in
opacity: 0;
transform: translateY(1rem);
transition: opacity 0.2s ease, transform 0.2s ease;
}
html.natus-open .natus-modal {
opacity: 1;
transform: translateY(0);
}
// ── Header ────────────────────────────────────────────────────────────────────
.natus-modal-header {
flex-shrink: 0;
padding: 0.6rem 1rem;
border-bottom: 0.1rem solid rgba(var(--terUser), 0.15);
display: flex;
flex-direction: row;
align-items: baseline;
gap: 0.75rem;
h2 {
margin: 0;
font-size: 1.1rem;
letter-spacing: 0.06em;
span { color: rgba(var(--secUser), 1); }
}
p {
margin: 0;
font-size: 0.7rem;
opacity: 0.55;
}
}
// ── Body: two columns ─────────────────────────────────────────────────────────
.natus-modal-body {
flex: 1;
min-height: 0;
display: flex;
flex-direction: row;
overflow: hidden;
}
// Form column — fixed width, scrollable
.natus-form-col {
flex: 0 0 240px;
overflow-y: auto;
padding: 0.9rem 1rem;
border-right: 0.1rem solid rgba(var(--terUser), 0.12);
display: flex;
flex-direction: column;
gap: 0.65rem;
}
// Wheel column — fills remaining space
.natus-wheel-col {
flex: 1;
min-width: 0;
display: flex;
align-items: center;
justify-content: center;
padding: 0.75rem;
background: rgba(var(--priUser), 0.5);
position: relative;
}
.natus-svg {
display: block;
width: 100%;
height: 100%;
aspect-ratio: 1 / 1;
max-width: 400px;
max-height: 400px;
}
// ── Form fields ───────────────────────────────────────────────────────────────
.natus-field {
display: flex;
flex-direction: column;
gap: 0.25rem;
label {
font-size: 0.6rem;
text-transform: uppercase;
letter-spacing: 0.1em;
color: rgba(var(--quaUser), 0.8);
}
input {
width: 100%;
// inherits global input styles
}
small {
font-size: 0.58rem;
opacity: 0.45;
line-height: 1.3;
}
}
// Place search field wrapper: text input + geo button inline
.natus-place-field { position: relative; }
.natus-place-wrap {
display: flex;
gap: 0.4rem;
align-items: center;
input { flex: 1; min-width: 0; }
.btn-sm { flex-shrink: 0; }
}
// Nominatim suggestion dropdown
.natus-suggestions {
position: absolute;
left: 0;
right: 0;
top: calc(100% + 2px);
z-index: 10;
background: rgba(var(--priUser), 1);
border: 0.1rem solid rgba(var(--terUser), 0.3);
border-radius: 0.3rem;
overflow-y: auto;
max-height: 10rem;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
}
.natus-suggestion-item {
display: block;
width: 100%;
padding: 0.4rem 0.6rem;
text-align: left;
background: none;
border: none;
border-bottom: 0.05rem solid rgba(var(--terUser), 0.1);
font-size: 0.65rem;
color: rgba(var(--ninUser), 0.85);
cursor: pointer;
line-height: 1.35;
&:last-child { border-bottom: none; }
&:hover, &:focus {
background: rgba(var(--terUser), 0.12);
color: rgba(var(--ninUser), 1);
outline: none;
}
}
// Coords row: lat | lon (read-only, populated by place selection)
.natus-coords {
flex-direction: row;
align-items: flex-end;
gap: 0.4rem;
> div {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 0.25rem;
label {
font-size: 0.6rem;
text-transform: uppercase;
letter-spacing: 0.1em;
color: rgba(var(--quaUser), 0.8);
}
input {
width: 100%;
opacity: 0.6;
cursor: default;
}
}
}
// ── Status line ───────────────────────────────────────────────────────────────
.natus-status {
font-size: 0.65rem;
opacity: 0.6;
min-height: 1rem;
text-align: center;
&--error {
opacity: 1;
color: rgba(var(--priRd), 1);
}
}
// ── Footer ────────────────────────────────────────────────────────────────────
.natus-modal-footer {
flex-shrink: 0;
padding: 0.6rem 1rem;
border-top: 0.1rem solid rgba(var(--terUser), 0.15);
display: flex;
justify-content: flex-end;
gap: 0.5rem;
}
// ── Narrow / portrait ─────────────────────────────────────────────────────────
@media (max-width: 600px) {
.natus-modal {
width: 96vw;
max-height: 96vh;
}
.natus-modal-body {
flex-direction: column;
overflow-y: auto;
}
.natus-form-col {
flex: 0 0 auto;
border-right: none;
border-bottom: 0.1rem solid rgba(var(--terUser), 0.12);
}
.natus-wheel-col {
flex: 0 0 280px;
}
}
// ── Sidebar z-index sink (landscape sidebars must go below backdrop) ───────────
@media (orientation: landscape) {
html.natus-open body .container .navbar,
html.natus-open body #id_footer {
z-index: 90;
}
}