<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8" />

  <meta name="viewport" content="width=device-width, initial-scale=1" />

  <title>Haunted Inc. Events</title>

  <link href="https://fonts.googleapis.com/css2?family=Creepster&family=Jolly+Ledger&family=Syne+Mono&display=swap" rel="stylesheet" />

  <style>

    body {

      font-family: 'Syne Mono', monospace;

      background-color: #000;

      margin: 0;

      padding: 20px 10px;

      max-width: 90vw;

      min-width: 300px;

      margin-left: auto;

      margin-right: auto;

      color: #fff;

      box-sizing: border-box;

      -webkit-font-smoothing: antialiased;

      -moz-osx-font-smoothing: grayscale;

    }

    h1 {

      font-family: 'Creepster', cursive;

      font-weight: 700;

      font-size: 3rem;

      margin-bottom: 0.3em;

      color: #C11C84;

      text-align: center;

      user-select: none;

    }

    h2.section-title {

      font-family: 'Jolly Ledger', cursive;

      font-size: 2rem;

      font-weight: 700;

      color: #C11C84;

      margin: 0 0 0.75em 0;

      text-align: center;

      user-select: none;

    }

    #calendar {

      max-width: 100%;

      display: flex;

      flex-direction: column;

      gap: 2rem;

    }

    .events-list, .upcoming-list {

      background-color: #1a1a1a;

      border-radius: 16px;

      padding: 16px 24px;

      box-shadow: 2px 4px 8px rgba(193, 28, 132, 0.5);

      font-family: 'Syne Mono', monospace;

      font-size: 1rem;

      color: #fff;

      max-height: 400px;

      overflow-y: auto;

    }

    .events-list {

      min-height: 100px;

    }

    .event-item {

      padding: 14px 10px;

      border-bottom: 1px solid #4c004a;

      cursor: pointer;

      user-select: none;

      transition: background-color 0.3s ease;

      display: flex;

      flex-direction: column;

      gap: 6px;

    }

    .event-item:last-child {

      border-bottom: none;

    }

    .event-item:hover,

    .event-item:focus-visible {

      background-color: #350035;

      outline: none;

    }

    .event-summary {

      display: flex;

      flex-direction: column;

      gap: 4px;

      word-wrap: break-word;

    }

    .event-title {

      font-family: 'Jolly Ledger', cursive;

      font-size: 1.3rem;

      font-weight: 700;

      color: #f8bbd0;

      display: flex;

      align-items: center;

      gap: 12px;

      flex-wrap: wrap;

    }

    .plus18-label {

      border: 2px solid #ff4081;

      border-radius: 12px;

      padding: 2px 8px;

      font-size: 0.8rem;

      font-weight: 700;

      color: #ff4081;

      user-select: none;

      white-space: nowrap;

    }

    .event-time, .event-streamers {

      font-weight: 600;

      color: #e0a4ce;

      font-size: 0.95rem;

      white-space: nowrap;

    }

    .event-details {

      margin-top: 10px;

      padding-left: 8px;

      font-family: 'Syne Mono', monospace;

      font-size: 0.9rem;

      color: #ddd;

      line-height: 1.4em;

      display: none;

      word-wrap: break-word;

    }

    .event-details strong {

      color: #C11C84;

    }

    .event-item.expanded .event-details {

      display: block;

    }

    @media (min-width: 480px) {

      .event-item {

        flex-direction: row;

        justify-content: space-between;

        align-items: center;

      }

      .event-summary {

        flex-direction: row;

        gap: 16px;

        flex-wrap: wrap;

        align-items: center;

      }

      .event-title {

        white-space: nowrap;

        flex-grow: 1;

        overflow: hidden;

        text-overflow: ellipsis;

      }

    }

  </style>

</head>

<body>

  <h1>Haunted Inc. Events</h1>

  <div id="calendar" role="region" aria-label="Events Calendar">

    <section aria-label="Today’s events">

      <h2 class="section-title">Today</h2>

      <div id="today-events" class="events-list" aria-live="polite" tabindex="0">

        Loading...

      </div>

    </section>

    <section aria-label="Upcoming events">

      <h2 class="section-title">Upcoming</h2>

      <div id="upcoming-events" class="upcoming-list" aria-live="polite" tabindex="0">

        Loading...

      </div>

    </section>

  </div>

  <script>

    // Converts array of event rows into event objects with Date instances

    function createEvents(data) {

      if (!Array.isArray(data)) return [];

      return data.map(row => {

        if (!row.Date || !row.Time) return null;

        const [year, month, day] = row.Date.split('-').map(Number);

        const [hours, minutes] = row.Time.split(':').map(Number);

        const dateTime = new Date(year, month - 1, day, hours, minutes);

        if (isNaN(dateTime.getTime())) return null;

        return {

          id: 'evt_' + Math.random().toString(36).slice(2),

          title: row.EventName || '',

          datetime: dateTime,

          plus18: Boolean(row.Plus18),

          streamers: row.Streamers || '',

          location: row.Location || '',

          description: row.Description || ''

        };

      }).filter(e => e !== null);

    }

    // Format a Date object to readable date string

    function formatDate(date) {

      return date.toLocaleDateString(undefined, {

        year: 'numeric',

        month: 'long',

        day: 'numeric'

      });

    }

    // Format a Date object to readable time string

    function formatTime(date) {

      return date.toLocaleTimeString(undefined, {

        hour: 'numeric',

        minute: '2-digit',

        hour12: true

      });

    }

    // Check if two dates represent the same local calendar day

    function isSameDay(d1, d2) {

      return d1.getFullYear() === d2.getFullYear() &&

             d1.getMonth() === d2.getMonth() &&

             d1.getDate() === d2.getDate();

    }

    // Render events inside a container element

    function renderEvents(container, events, isExpandable = true) {

      container.innerHTML = '';

      if (events.length === 0) {

        container.textContent = 'No events.';

        return;

      }

      events.forEach(e => {

        const item = document.createElement('div');

        item.className = 'event-item';

        item.tabIndex = 0;

        item.setAttribute('role', isExpandable ? 'button' : 'article');

        item.setAttribute('aria-expanded', 'false');

        item.setAttribute('aria-label', e.title + (e.plus18 ? ', 18 Plus Stream' : '') + `, at ${formatTime(e.datetime)}`);

        const summary = document.createElement('div');

        summary.className = 'event-summary';

        const title = document.createElement('div');

        title.className = 'event-title';

        title.textContent = e.title;

        if (e.plus18) {

          const plusLabel = document.createElement('span');

          plusLabel.className = 'plus18-label';

          plusLabel.textContent = '18 Plus Stream';

          title.appendChild(plusLabel);

        }

        const time = document.createElement('div');

        time.className = 'event-time';

        time.textContent = formatTime(e.datetime);

        const streamers = document.createElement('div');

        streamers.className = 'event-streamers';

        streamers.textContent = e.streamers;

        summary.appendChild(title);

        summary.appendChild(time);

        if (e.streamers) summary.appendChild(streamers);

        item.appendChild(summary);

        if (isExpandable) {

          const details = document.createElement('div');

          details.className = 'event-details';

          details.innerHTML = `

            <p><strong>Location:</strong> ${e.location || 'N/A'}</p>

            <p><strong>Description:</strong> ${e.description || 'N/A'}</p>

          `;

          item.appendChild(details);

          item.addEventListener('click', () => toggleExpanded(item));

          item.addEventListener('keydown', e => {

            if (e.key === 'Enter' || e.key === ' ') {

              e.preventDefault();

              toggleExpanded(item);

            }

          });

        }

        container.appendChild(item);

      });

    }

    function toggleExpanded(item) {

      const expanded = item.classList.toggle('expanded');

      item.setAttribute('aria-expanded', expanded);

    }

    // Fetch events by calling Apps Script backend

    function fetchEvents() {

      return new Promise((resolve, reject) => {

        google.script.run.withSuccessHandler(resolve).withFailureHandler(reject).getEvents();

      });

    }

    // Load events and render Today and Upcoming sections

    function loadEvents() {

      fetchEvents()

        .then(data => {

          const events = createEvents(data);

          const now = new Date();

          const todayEvents = events.filter(ev => isSameDay(ev.datetime, now));

          const tomorrow = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1);

          const upcomingEvents = events.filter(ev => ev.datetime >= tomorrow);

          renderEvents(document.getElementById('today-events'), todayEvents);

          renderEvents(document.getElementById('upcoming-events'), upcomingEvents, false);

        })

        .catch(err => {

          console.error('Error fetching events:', err);

          document.getElementById('today-events').textContent = 'Failed to load events.';

          document.getElementById('upcoming-events').textContent = 'Failed to load events.';

        });

    }

    // Refresh events hourly on the hour

    function scheduleHourlyRefresh() {

      const now = new Date();

      const msUntilNextHour = (60 - now.getMinutes()) * 60 * 1000 - now.getSeconds() * 1000 - now.getMilliseconds();

      setTimeout(() => {

        loadEvents();

        setInterval(loadEvents, 60 * 60 * 1000);

      }, msUntilNextHour);

    }

    window.onload = () => {

      loadEvents();

      scheduleHourlyRefresh();

    };

  </script>

</body>

</html>