infinite-agents-public/themed_hybrids/ui_hybrid_13.html

1033 lines
34 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cosmic Calendar - Space Exploration Calendar System</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Arial', sans-serif;
background: #000;
color: #fff;
overflow-x: hidden;
position: relative;
}
/* Cosmic Background */
.cosmic-background {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: radial-gradient(ellipse at center, #0a0a2a 0%, #000 100%);
z-index: -2;
}
.stars {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
}
.star {
position: absolute;
background: #fff;
border-radius: 50%;
animation: twinkle 3s infinite;
}
@keyframes twinkle {
0%, 100% { opacity: 0.3; transform: scale(1); }
50% { opacity: 1; transform: scale(1.2); }
}
/* Nebula Effects */
.nebula {
position: fixed;
width: 300px;
height: 300px;
background: radial-gradient(circle, rgba(138, 43, 226, 0.2), transparent 70%);
filter: blur(40px);
animation: drift 30s infinite;
z-index: -1;
}
.nebula:nth-child(2) {
background: radial-gradient(circle, rgba(30, 144, 255, 0.2), transparent 70%);
animation-duration: 40s;
animation-direction: reverse;
}
@keyframes drift {
0%, 100% { transform: translate(0, 0) rotate(0deg); }
33% { transform: translate(100px, -50px) rotate(120deg); }
66% { transform: translate(-50px, 100px) rotate(240deg); }
}
/* Header */
.header {
text-align: center;
padding: 30px;
background: linear-gradient(to bottom, rgba(0, 0, 40, 0.8), transparent);
position: relative;
z-index: 10;
}
.header h1 {
font-size: 3em;
background: linear-gradient(90deg, #4a90e2, #9b59b6, #e74c3c);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
text-shadow: 0 0 30px rgba(74, 144, 226, 0.5);
margin-bottom: 10px;
}
.header p {
color: #8a9bb3;
font-size: 1.2em;
}
/* Main Container */
.container {
max-width: 1400px;
margin: 0 auto;
padding: 20px;
display: grid;
grid-template-columns: 300px 1fr 300px;
gap: 30px;
position: relative;
z-index: 10;
}
/* Left Panel - Time Zones */
.time-zones {
background: rgba(0, 0, 40, 0.6);
border: 1px solid rgba(74, 144, 226, 0.3);
border-radius: 15px;
padding: 20px;
backdrop-filter: blur(10px);
}
.time-zones h2 {
color: #4a90e2;
margin-bottom: 20px;
font-size: 1.3em;
}
.planet-time {
margin-bottom: 15px;
padding: 15px;
background: rgba(74, 144, 226, 0.1);
border-radius: 10px;
position: relative;
overflow: hidden;
cursor: pointer;
transition: all 0.3s;
}
.planet-time:hover {
background: rgba(74, 144, 226, 0.2);
transform: translateX(5px);
}
.planet-time::before {
content: '';
position: absolute;
width: 40px;
height: 40px;
background: radial-gradient(circle, var(--planet-color), transparent);
border-radius: 50%;
top: 50%;
left: -20px;
transform: translateY(-50%);
animation: rotate 20s linear infinite;
}
@keyframes rotate {
0% { transform: translateY(-50%) rotate(0deg); }
100% { transform: translateY(-50%) rotate(360deg); }
}
.planet-name {
font-weight: bold;
color: #fff;
margin-bottom: 5px;
}
.planet-time-display {
color: #8a9bb3;
font-family: monospace;
font-size: 1.1em;
}
/* Calendar Grid */
.calendar-container {
background: rgba(0, 0, 40, 0.6);
border: 1px solid rgba(74, 144, 226, 0.3);
border-radius: 15px;
padding: 20px;
backdrop-filter: blur(10px);
}
.calendar-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.month-year {
font-size: 1.8em;
color: #4a90e2;
text-align: center;
flex: 1;
}
.nav-button {
background: rgba(74, 144, 226, 0.2);
border: 1px solid rgba(74, 144, 226, 0.5);
color: #fff;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
transition: all 0.3s;
font-size: 1.2em;
}
.nav-button:hover {
background: rgba(74, 144, 226, 0.4);
transform: scale(1.05);
box-shadow: 0 0 15px rgba(74, 144, 226, 0.5);
}
.calendar-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 10px;
position: relative;
}
.day-header {
text-align: center;
color: #8a9bb3;
padding: 10px;
font-weight: bold;
}
.calendar-day {
aspect-ratio: 1;
background: rgba(0, 0, 0, 0.5);
border: 1px solid rgba(74, 144, 226, 0.2);
border-radius: 10px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s;
position: relative;
overflow: hidden;
}
.calendar-day:hover {
background: rgba(74, 144, 226, 0.2);
border-color: rgba(74, 144, 226, 0.5);
transform: scale(1.05);
z-index: 1;
}
.calendar-day.other-month {
opacity: 0.3;
}
.calendar-day.today {
background: radial-gradient(circle, rgba(255, 215, 0, 0.3), transparent);
border-color: rgba(255, 215, 0, 0.5);
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { box-shadow: 0 0 10px rgba(255, 215, 0, 0.5); }
50% { box-shadow: 0 0 25px rgba(255, 215, 0, 0.8); }
}
.day-number {
font-size: 1.2em;
font-weight: bold;
margin-bottom: 5px;
}
.event-indicator {
width: 6px;
height: 6px;
background: var(--event-color);
border-radius: 50%;
margin: 2px;
animation: orbit 3s linear infinite;
}
@keyframes orbit {
0% { transform: rotate(0deg) translateX(10px) rotate(0deg); }
100% { transform: rotate(360deg) translateX(10px) rotate(-360deg); }
}
/* Right Panel - Events */
.events-panel {
background: rgba(0, 0, 40, 0.6);
border: 1px solid rgba(74, 144, 226, 0.3);
border-radius: 15px;
padding: 20px;
backdrop-filter: blur(10px);
}
.events-panel h2 {
color: #4a90e2;
margin-bottom: 20px;
font-size: 1.3em;
}
.add-event-btn {
width: 100%;
background: linear-gradient(45deg, #4a90e2, #9b59b6);
border: none;
color: #fff;
padding: 15px;
border-radius: 10px;
cursor: pointer;
font-size: 1.1em;
transition: all 0.3s;
margin-bottom: 20px;
}
.add-event-btn:hover {
transform: translateY(-2px);
box-shadow: 0 10px 20px rgba(74, 144, 226, 0.3);
}
.event-item {
background: rgba(74, 144, 226, 0.1);
border: 1px solid rgba(74, 144, 226, 0.3);
border-radius: 10px;
padding: 15px;
margin-bottom: 15px;
position: relative;
overflow: hidden;
transition: all 0.3s;
}
.event-item:hover {
background: rgba(74, 144, 226, 0.2);
transform: translateX(-5px);
}
.event-item::before {
content: '';
position: absolute;
width: 20px;
height: 20px;
background: radial-gradient(circle, var(--event-color), transparent);
border-radius: 50%;
top: 50%;
right: -10px;
transform: translateY(-50%);
animation: pulse 2s infinite;
}
.event-title {
font-weight: bold;
color: #fff;
margin-bottom: 5px;
}
.event-time {
color: #8a9bb3;
font-size: 0.9em;
}
/* Event Modal */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
z-index: 1000;
backdrop-filter: blur(5px);
}
.modal-content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: linear-gradient(135deg, rgba(0, 0, 40, 0.95), rgba(74, 144, 226, 0.1));
border: 1px solid rgba(74, 144, 226, 0.5);
border-radius: 20px;
padding: 30px;
width: 90%;
max-width: 500px;
box-shadow: 0 0 50px rgba(74, 144, 226, 0.5);
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.modal-header h3 {
color: #4a90e2;
font-size: 1.5em;
}
.close-btn {
background: none;
border: none;
color: #fff;
font-size: 1.5em;
cursor: pointer;
transition: all 0.3s;
}
.close-btn:hover {
color: #e74c3c;
transform: rotate(90deg);
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
color: #8a9bb3;
margin-bottom: 5px;
}
.form-group input,
.form-group select,
.form-group textarea {
width: 100%;
background: rgba(0, 0, 0, 0.5);
border: 1px solid rgba(74, 144, 226, 0.3);
color: #fff;
padding: 10px;
border-radius: 5px;
transition: all 0.3s;
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
outline: none;
border-color: rgba(74, 144, 226, 0.8);
box-shadow: 0 0 10px rgba(74, 144, 226, 0.3);
}
.color-picker {
display: flex;
gap: 10px;
margin-top: 5px;
}
.color-option {
width: 30px;
height: 30px;
border-radius: 50%;
cursor: pointer;
transition: all 0.3s;
border: 2px solid transparent;
}
.color-option:hover {
transform: scale(1.2);
}
.color-option.selected {
border-color: #fff;
box-shadow: 0 0 10px currentColor;
}
.save-btn {
background: linear-gradient(45deg, #4a90e2, #9b59b6);
border: none;
color: #fff;
padding: 12px 30px;
border-radius: 5px;
cursor: pointer;
font-size: 1.1em;
transition: all 0.3s;
width: 100%;
}
.save-btn:hover {
transform: translateY(-2px);
box-shadow: 0 10px 20px rgba(74, 144, 226, 0.3);
}
/* Satellite Signals (Reminders) */
.satellite-signal {
position: fixed;
width: 100px;
height: 100px;
border: 2px solid rgba(255, 215, 0, 0.5);
border-radius: 50%;
animation: signal-pulse 3s infinite;
pointer-events: none;
z-index: 100;
}
@keyframes signal-pulse {
0% {
transform: scale(0.5);
opacity: 1;
}
100% {
transform: scale(2);
opacity: 0;
}
}
/* Comet Trail (Recurring Events) */
.comet {
position: fixed;
width: 10px;
height: 10px;
background: #fff;
border-radius: 50%;
box-shadow: 0 0 10px #fff;
z-index: 5;
}
.comet-trail {
position: absolute;
width: 100px;
height: 2px;
background: linear-gradient(to left, transparent, rgba(255, 255, 255, 0.8));
top: 50%;
right: 10px;
transform: translateY(-50%);
}
/* Orbital Paths */
.orbital-path {
position: absolute;
border: 1px dashed rgba(74, 144, 226, 0.3);
border-radius: 50%;
pointer-events: none;
animation: rotate 30s linear infinite;
}
/* Constellation Connections */
.constellation-line {
position: absolute;
height: 1px;
background: rgba(74, 144, 226, 0.3);
transform-origin: left center;
pointer-events: none;
}
/* Responsive */
@media (max-width: 1200px) {
.container {
grid-template-columns: 1fr;
}
.time-zones,
.events-panel {
max-width: 600px;
margin: 0 auto;
}
}
</style>
</head>
<body>
<div class="cosmic-background"></div>
<div class="stars" id="starsContainer"></div>
<div class="nebula" style="top: 20%; left: 10%;"></div>
<div class="nebula" style="bottom: 20%; right: 10%;"></div>
<header class="header">
<h1>Cosmic Calendar</h1>
<p>Navigate Time Through the Stars</p>
</header>
<div class="container">
<!-- Time Zones Panel -->
<div class="time-zones">
<h2>Planetary Time Zones</h2>
<div id="timeZones"></div>
</div>
<!-- Calendar -->
<div class="calendar-container">
<div class="calendar-header">
<button class="nav-button" onclick="changeMonth(-1)"></button>
<div class="month-year" id="monthYear"></div>
<button class="nav-button" onclick="changeMonth(1)"></button>
</div>
<div class="calendar-grid" id="calendarGrid"></div>
</div>
<!-- Events Panel -->
<div class="events-panel">
<h2>Celestial Events</h2>
<button class="add-event-btn" onclick="openModal()">Launch New Event</button>
<div id="eventsList"></div>
</div>
</div>
<!-- Event Modal -->
<div class="modal" id="eventModal">
<div class="modal-content">
<div class="modal-header">
<h3>Create Celestial Event</h3>
<button class="close-btn" onclick="closeModal()">×</button>
</div>
<form id="eventForm">
<div class="form-group">
<label>Event Name</label>
<input type="text" id="eventName" placeholder="Enter celestial event name" required>
</div>
<div class="form-group">
<label>Date</label>
<input type="date" id="eventDate" required>
</div>
<div class="form-group">
<label>Time</label>
<input type="time" id="eventTime" required>
</div>
<div class="form-group">
<label>Description</label>
<textarea id="eventDescription" rows="3" placeholder="Describe your cosmic journey"></textarea>
</div>
<div class="form-group">
<label>Event Type</label>
<select id="eventType">
<option value="planet">Planet (Single Event)</option>
<option value="comet">Comet (Recurring Weekly)</option>
<option value="asteroid">Asteroid (Recurring Monthly)</option>
<option value="star">Star (Important)</option>
</select>
</div>
<div class="form-group">
<label>Celestial Color</label>
<div class="color-picker">
<div class="color-option" style="background: #e74c3c;" data-color="#e74c3c"></div>
<div class="color-option" style="background: #f39c12;" data-color="#f39c12"></div>
<div class="color-option" style="background: #f1c40f;" data-color="#f1c40f"></div>
<div class="color-option" style="background: #2ecc71;" data-color="#2ecc71"></div>
<div class="color-option" style="background: #3498db;" data-color="#3498db"></div>
<div class="color-option selected" style="background: #9b59b6;" data-color="#9b59b6"></div>
<div class="color-option" style="background: #1abc9c;" data-color="#1abc9c"></div>
<div class="color-option" style="background: #ecf0f1;" data-color="#ecf0f1"></div>
</div>
</div>
<div class="form-group">
<label>Reminder (Satellite Signal)</label>
<select id="eventReminder">
<option value="none">No Signal</option>
<option value="5">5 minutes before</option>
<option value="15">15 minutes before</option>
<option value="30">30 minutes before</option>
<option value="60">1 hour before</option>
<option value="1440">1 day before</option>
</select>
</div>
<button type="submit" class="save-btn">Launch Event</button>
</form>
</div>
</div>
<script>
// Global variables
let currentDate = new Date();
let selectedDate = null;
let events = JSON.parse(localStorage.getItem('cosmicEvents')) || [];
let selectedColor = '#9b59b6';
// Time zones with planet names
const planetaryTimeZones = [
{ name: 'Mercury', offset: -8, color: '#8b7355' },
{ name: 'Venus', offset: -5, color: '#ffd700' },
{ name: 'Earth', offset: 0, color: '#4169e1' },
{ name: 'Mars', offset: 3, color: '#cd5c5c' },
{ name: 'Jupiter', offset: 6, color: '#daa520' },
{ name: 'Saturn', offset: 9, color: '#f4a460' }
];
// Initialize
function init() {
createStars();
updateTimeZones();
renderCalendar();
renderEvents();
setupEventListeners();
checkReminders();
createComets();
setInterval(updateTimeZones, 1000);
setInterval(checkReminders, 60000); // Check every minute
}
// Create stars
function createStars() {
const starsContainer = document.getElementById('starsContainer');
for (let i = 0; i < 200; i++) {
const star = document.createElement('div');
star.className = 'star';
star.style.left = Math.random() * 100 + '%';
star.style.top = Math.random() * 100 + '%';
star.style.width = star.style.height = Math.random() * 3 + 'px';
star.style.animationDelay = Math.random() * 3 + 's';
starsContainer.appendChild(star);
}
}
// Update time zones
function updateTimeZones() {
const container = document.getElementById('timeZones');
container.innerHTML = '';
planetaryTimeZones.forEach(zone => {
const now = new Date();
now.setHours(now.getHours() + zone.offset);
const planetDiv = document.createElement('div');
planetDiv.className = 'planet-time';
planetDiv.style.setProperty('--planet-color', zone.color);
planetDiv.innerHTML = `
<div class="planet-name">${zone.name}</div>
<div class="planet-time-display">${now.toLocaleTimeString()}</div>
`;
container.appendChild(planetDiv);
});
}
// Render calendar
function renderCalendar() {
const year = currentDate.getFullYear();
const month = currentDate.getMonth();
const firstDay = new Date(year, month, 1).getDay();
const daysInMonth = new Date(year, month + 1, 0).getDate();
const prevDaysInMonth = new Date(year, month, 0).getDate();
document.getElementById('monthYear').textContent =
currentDate.toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
const grid = document.getElementById('calendarGrid');
grid.innerHTML = '';
// Day headers
const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
days.forEach(day => {
const header = document.createElement('div');
header.className = 'day-header';
header.textContent = day;
grid.appendChild(header);
});
// Previous month days
for (let i = firstDay - 1; i >= 0; i--) {
const day = prevDaysInMonth - i;
createDayElement(day, month - 1, year, true);
}
// Current month days
for (let day = 1; day <= daysInMonth; day++) {
createDayElement(day, month, year, false);
}
// Next month days
const totalCells = firstDay + daysInMonth;
const nextDays = totalCells > 35 ? 42 - totalCells : 35 - totalCells;
for (let day = 1; day <= nextDays; day++) {
createDayElement(day, month + 1, year, true);
}
// Draw constellation lines between events
drawConstellations();
}
// Create day element
function createDayElement(day, month, year, isOtherMonth) {
const dayDiv = document.createElement('div');
dayDiv.className = 'calendar-day';
if (isOtherMonth) dayDiv.classList.add('other-month');
const today = new Date();
const cellDate = new Date(year, month, day);
if (cellDate.toDateString() === today.toDateString()) {
dayDiv.classList.add('today');
}
dayDiv.innerHTML = `<div class="day-number">${day}</div>`;
// Add event indicators
const dayEvents = events.filter(event => {
const eventDate = new Date(event.date);
return eventDate.toDateString() === cellDate.toDateString();
});
dayEvents.forEach(event => {
const indicator = document.createElement('div');
indicator.className = 'event-indicator';
indicator.style.setProperty('--event-color', event.color);
dayDiv.appendChild(indicator);
});
dayDiv.onclick = () => selectDate(cellDate);
document.getElementById('calendarGrid').appendChild(dayDiv);
}
// Select date
function selectDate(date) {
selectedDate = date;
document.getElementById('eventDate').value = date.toISOString().split('T')[0];
openModal();
}
// Change month
function changeMonth(direction) {
currentDate.setMonth(currentDate.getMonth() + direction);
renderCalendar();
}
// Event management
function openModal() {
document.getElementById('eventModal').style.display = 'block';
createOrbitalPaths();
}
function closeModal() {
document.getElementById('eventModal').style.display = 'none';
document.getElementById('eventForm').reset();
}
// Setup event listeners
function setupEventListeners() {
// Color picker
document.querySelectorAll('.color-option').forEach(option => {
option.addEventListener('click', function() {
document.querySelectorAll('.color-option').forEach(o => o.classList.remove('selected'));
this.classList.add('selected');
selectedColor = this.dataset.color;
});
});
// Form submission
document.getElementById('eventForm').addEventListener('submit', function(e) {
e.preventDefault();
const event = {
id: Date.now(),
name: document.getElementById('eventName').value,
date: document.getElementById('eventDate').value,
time: document.getElementById('eventTime').value,
description: document.getElementById('eventDescription').value,
type: document.getElementById('eventType').value,
color: selectedColor,
reminder: document.getElementById('eventReminder').value
};
events.push(event);
localStorage.setItem('cosmicEvents', JSON.stringify(events));
renderCalendar();
renderEvents();
closeModal();
// Create satellite signal effect
if (event.reminder !== 'none') {
createSatelliteSignal();
}
});
}
// Render events list
function renderEvents() {
const container = document.getElementById('eventsList');
container.innerHTML = '';
const today = new Date().toDateString();
const todayEvents = events.filter(event =>
new Date(event.date).toDateString() === today
);
todayEvents.forEach(event => {
const eventDiv = document.createElement('div');
eventDiv.className = 'event-item';
eventDiv.style.setProperty('--event-color', event.color);
eventDiv.innerHTML = `
<div class="event-title">${event.name}</div>
<div class="event-time">${event.time} - ${event.type}</div>
`;
container.appendChild(eventDiv);
});
}
// Check reminders
function checkReminders() {
const now = new Date();
events.forEach(event => {
if (event.reminder === 'none') return;
const eventDateTime = new Date(event.date + ' ' + event.time);
const reminderTime = new Date(eventDateTime.getTime() - event.reminder * 60000);
if (Math.abs(now - reminderTime) < 60000) {
showReminder(event);
}
});
}
// Show reminder
function showReminder(event) {
createSatelliteSignal();
if (Notification.permission === 'granted') {
new Notification('Cosmic Calendar Reminder', {
body: `${event.name} starts in ${event.reminder} minutes`,
icon: 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><circle cx="50" cy="50" r="40" fill="%234a90e2"/></svg>'
});
}
}
// Create satellite signal effect
function createSatelliteSignal() {
const signal = document.createElement('div');
signal.className = 'satellite-signal';
signal.style.left = Math.random() * window.innerWidth + 'px';
signal.style.top = Math.random() * window.innerHeight + 'px';
document.body.appendChild(signal);
setTimeout(() => signal.remove(), 3000);
}
// Create comets for recurring events
function createComets() {
setInterval(() => {
const recurringEvents = events.filter(e =>
e.type === 'comet' || e.type === 'asteroid'
);
if (recurringEvents.length > 0 && Math.random() < 0.3) {
const comet = document.createElement('div');
comet.className = 'comet';
comet.innerHTML = '<div class="comet-trail"></div>';
const startY = Math.random() * window.innerHeight;
comet.style.top = startY + 'px';
comet.style.right = '-10px';
document.body.appendChild(comet);
// Animate comet
let position = -10;
const animation = setInterval(() => {
position += 5;
comet.style.right = position + 'px';
if (position > window.innerWidth + 100) {
clearInterval(animation);
comet.remove();
}
}, 20);
}
}, 5000);
}
// Create orbital paths in modal
function createOrbitalPaths() {
const modal = document.querySelector('.modal-content');
for (let i = 0; i < 3; i++) {
const path = document.createElement('div');
path.className = 'orbital-path';
path.style.width = (i + 1) * 150 + 'px';
path.style.height = (i + 1) * 150 + 'px';
path.style.top = '50%';
path.style.left = '50%';
path.style.transform = 'translate(-50%, -50%)';
path.style.animationDuration = (i + 1) * 10 + 's';
modal.appendChild(path);
setTimeout(() => path.remove(), 5000);
}
}
// Draw constellation lines between events
function drawConstellations() {
const dayElements = document.querySelectorAll('.calendar-day:not(.other-month)');
const eventDays = [];
dayElements.forEach(day => {
if (day.querySelector('.event-indicator')) {
eventDays.push(day);
}
});
// Connect nearby events
for (let i = 0; i < eventDays.length - 1; i++) {
for (let j = i + 1; j < eventDays.length; j++) {
if (Math.random() < 0.3) { // 30% chance to connect
connectDays(eventDays[i], eventDays[j]);
}
}
}
}
// Connect two days with a line
function connectDays(day1, day2) {
const rect1 = day1.getBoundingClientRect();
const rect2 = day2.getBoundingClientRect();
const container = document.querySelector('.calendar-grid');
const containerRect = container.getBoundingClientRect();
const x1 = rect1.left + rect1.width / 2 - containerRect.left;
const y1 = rect1.top + rect1.height / 2 - containerRect.top;
const x2 = rect2.left + rect2.width / 2 - containerRect.left;
const y2 = rect2.top + rect2.height / 2 - containerRect.top;
const distance = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
const angle = Math.atan2(y2 - y1, x2 - x1) * 180 / Math.PI;
const line = document.createElement('div');
line.className = 'constellation-line';
line.style.width = distance + 'px';
line.style.left = x1 + 'px';
line.style.top = y1 + 'px';
line.style.transform = `rotate(${angle}deg)`;
container.appendChild(line);
}
// Request notification permission
if ('Notification' in window && Notification.permission === 'default') {
Notification.requestPermission();
}
// Initialize on load
window.onload = init;
// Handle window resize
window.addEventListener('resize', () => {
renderCalendar();
});
</script>
</body>
</html>