// Malaysia Calendar — app.jsx
const { useState, useMemo } = React;

// ─── PUBLIC HOLIDAYS (update once a year) ────────────────────────────────────
const PUBLIC_HOLIDAYS = [
  { date: "2025-01-01", name: "New Year's Day" },
  { date: "2025-01-29", name: "Chinese New Year" },
  { date: "2025-01-30", name: "Chinese New Year (2nd day)" },
  { date: "2025-02-01", name: "Federal Territory Day" },
  { date: "2025-03-30", name: "Hari Raya Aidilfitri" },
  { date: "2025-03-31", name: "Hari Raya Aidilfitri (2nd day)" },
  { date: "2025-03-31", name: "Nuzul Al-Quran" },
  { date: "2025-05-01", name: "Labour Day" },
  { date: "2025-05-12", name: "Hari Raya Aidiladha" },
  { date: "2025-05-13", name: "Hari Raya Aidiladha (2nd day)" },
  { date: "2025-05-26", name: "Wesak Day" },
  { date: "2025-06-02", name: "Yang di-Pertuan Agong's Birthday" },
  { date: "2025-07-07", name: "Awal Muharram" },
  { date: "2025-08-31", name: "National Day (Merdeka)" },
  { date: "2025-09-16", name: "Malaysia Day" },
  { date: "2025-09-17", name: "Maulidur Rasul" },
  { date: "2025-10-20", name: "Deepavali" },
  { date: "2025-12-25", name: "Christmas Day" },
  { date: "2026-01-01", name: "New Year's Day" },
  { date: "2026-01-17", name: "Chinese New Year" },
  { date: "2026-01-18", name: "Chinese New Year (2nd day)" },
  { date: "2026-02-02", name: "Federal Territory Day" },
  { date: "2026-03-20", name: "Hari Raya Aidilfitri" },
  { date: "2026-03-21", name: "Hari Raya Aidilfitri (2nd day)" },
  { date: "2026-05-01", name: "Labour Day" },
  { date: "2026-05-14", name: "Wesak Day" },
  { date: "2026-05-29", name: "Hari Raya Aidiladha" },
  { date: "2026-05-30", name: "Hari Raya Aidiladha (2nd day)" },
  { date: "2026-06-01", name: "Yang di-Pertuan Agong's Birthday" },
  { date: "2026-06-26", name: "Awal Muharram" },
  { date: "2026-08-31", name: "National Day (Merdeka)" },
  { date: "2026-09-06", name: "Maulidur Rasul" },
  { date: "2026-09-16", name: "Malaysia Day" },
  { date: "2026-11-07", name: "Deepavali" },
  { date: "2026-12-25", name: "Christmas Day" },
];

// ─── SHARED HELPERS (must be first — used by all engines below) ───────────────
function jdeToMYTStr(jde) {
  const d = new Date((jde - 2440587.5) * 86400000 + 8 * 3600000);
  return `${d.getUTCFullYear()}-${String(d.getUTCMonth()+1).padStart(2,'0')}-${String(d.getUTCDate()).padStart(2,'0')}`;
}
function addDaysStr(dateStr, n) {
  const [y,m,d] = dateStr.split('-').map(Number);
  const dt = new Date(y, m-1, d+n);
  return `${dt.getFullYear()}-${String(dt.getMonth()+1).padStart(2,'0')}-${String(dt.getDate()).padStart(2,'0')}`;
}

const LUNATION_YEAR = 1 / 12.3685;

// ─── LUNAR ENGINE: 初一 & 十五 ─────────────────────────────────────────────────
// 初一 = new moon date (astronomical)
// 十五 = new moon + 14 days (15th day of lunar month, NOT astronomical full moon)
const lunarCache = {};
function getLunarMapForYear(year) {
  if (lunarCache[year]) return lunarCache[year];
  const map = {};
  // Collect all new moons falling in this year
  const newMoons = new Set();
  for (let dy = -LUNATION_YEAR; dy <= 1.1; dy += LUNATION_YEAR) {
    const nm = jdeToMYTStr(window.lunarEngine.newMoon(year + dy));
    if (nm.startsWith(String(year))) newMoons.add(nm);
  }
  // 初一 = that date, 十五 = +14 days
  newMoons.forEach(nm => {
    map[nm] = { label: '初一' };
    map[addDaysStr(nm, 14)] = { label: '十五' };
  });
  // Edge case: prev year's last new moon + 14 days may land in this year
  for (let dy = -LUNATION_YEAR; dy <= 1.1; dy += LUNATION_YEAR) {
    const nm = jdeToMYTStr(window.lunarEngine.newMoon((year - 1) + dy));
    if (nm.startsWith(String(year - 1))) {
      const shiwu = addDaysStr(nm, 14);
      if (shiwu.startsWith(String(year))) map[shiwu] = { label: '十五' };
    }
  }
  return lunarCache[year] = map;
}

// ─── 观音 ENGINE ──────────────────────────────────────────────────────────────
// 正月初九, 二月十九 (圣诞🎂), 六月十九 (成道日), 九月十九 (出家日)
const guanyinCache = {};
function getGuanyinMapForYear(year) {
  if (guanyinCache[year]) return guanyinCache[year];
  // Collect new moons from prev + current year to find CNY
  const collect = (y) => {
    const s = new Set();
    for (let dy = -LUNATION_YEAR; dy <= 1.1; dy += LUNATION_YEAR) {
      const nm = jdeToMYTStr(window.lunarEngine.newMoon(y + dy));
      if (nm.startsWith(String(y))) s.add(nm);
    }
    return [...s].sort();
  };
  const all = [...collect(year - 1), ...collect(year)].sort();
  // CNY = new moon between Jan 20 – Feb 20
  const cnyNM = all.find(nm => nm >= `${year}-01-20` && nm <= `${year}-02-20`);
  if (!cnyNM) return guanyinCache[year] = {};
  const idx = all.indexOf(cnyNM);
  const map = {};
  const add = (monthOffset, dayOffset, label, name) => {
    const nm = all[idx + monthOffset];
    if (nm) map[addDaysStr(nm, dayOffset)] = { label, name };
  };
  add(0, 8,  '正月初九', '观音菩萨');
  add(1, 18, '二月十九', '观音菩萨圣诞 🎂');
  add(5, 18, '六月十九', '观音菩萨成道日');
  add(8, 18, '九月十九', '观音菩萨出家日');
  return guanyinCache[year] = map;
}

// ─── DATE HELPERS (GMT+8) ─────────────────────────────────────────────────────
function todayMYT() {
  const d = new Date(Date.now() + 8 * 3600000);
  return `${d.getUTCFullYear()}-${String(d.getUTCMonth()+1).padStart(2,'0')}-${String(d.getUTCDate()).padStart(2,'0')}`;
}
function addDays(dateStr, n) {
  const [y,m,d] = dateStr.split('-').map(Number);
  const dt = new Date(y, m-1, d+n);
  return `${dt.getFullYear()}-${String(dt.getMonth()+1).padStart(2,'0')}-${String(dt.getDate()).padStart(2,'0')}`;
}
function getDaysInMonth(y, m) { return new Date(y, m+1, 0).getDate(); }
function getDayOfWeek(dateStr) {
  const [y,m,d] = dateStr.split('-').map(Number);
  return new Date(y, m-1, d).getDay();
}
function daysFromToday(dateStr, todayStr) {
  const [ty,tm,td] = todayStr.split('-').map(Number);
  const [ey,em,ed] = dateStr.split('-').map(Number);
  return Math.round((new Date(ey,em-1,ed) - new Date(ty,tm-1,td)) / 86400000);
}

const MONTHS     = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
const DAYS_SHORT = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];

// ─── EVENT LOOKUP ─────────────────────────────────────────────────────────────
const holidayMap = {};
PUBLIC_HOLIDAYS.forEach(h => {
  if (!holidayMap[h.date]) holidayMap[h.date] = [];
  holidayMap[h.date].push(h.name);
});

function getEvents(dateStr) {
  const year = parseInt(dateStr.slice(0, 4));
  return {
    holidays: holidayMap[dateStr] || [],
    lunar:    getLunarMapForYear(year)[dateStr]    || null,
    guanyin:  getGuanyinMapForYear(year)[dateStr]  || null,
  };
}

function getUpcoming(todayStr, days = 120) {
  const result = [];
  for (let i = 0; i <= days; i++) {
    const key = addDays(todayStr, i);
    const { holidays, lunar, guanyin } = getEvents(key);
    if (holidays.length || lunar || guanyin) result.push({ key, holidays, lunar, guanyin });
  }
  return result;
}

// ─── COMPONENTS ───────────────────────────────────────────────────────────────
function Pill({ active, color, onClick, children }) {
  return (
    <button onClick={onClick} style={{
      display:"flex", alignItems:"center", gap:6,
      padding:"6px 12px", borderRadius:20,
      border: active ? `1.5px solid ${color}` : "1.5px solid #2a2f3f",
      background: active ? color + "22" : "#161b27",
      color: active ? color : "#6a6050",
      fontSize:13, fontWeight: active ? 600 : 400,
      cursor:"pointer", transition:"all 0.15s",
    }}>{children}</button>
  );
}

function Input({ placeholder, value, onChange, type="text" }) {
  return (
    <input type={type} placeholder={placeholder} value={value}
      onChange={e => onChange(e.target.value)}
      style={{ width:"100%", padding:"10px 12px", borderRadius:8,
        background:"#0d1120", border:"1px solid #2a2f3f",
        color:"#e8e3d8", fontSize:13, outline:"none", marginBottom:10 }}
    />
  );
}

// ─── APP ──────────────────────────────────────────────────────────────────────
function App() {
  const todayStr = todayMYT();
  const [ty, tm] = todayStr.split('-').map(Number);

  const [viewYear,     setViewYear]     = useState(ty);
  const [viewMonth,    setViewMonth]    = useState(tm - 1);
  const [tab,          setTab]          = useState("upcoming");
  const [showHoliday,  setShowHoliday]  = useState(true);
  const [showChiniOne, setShowChiniOne] = useState(true);
  const [showChiniWu,  setShowChiniWu]  = useState(true);
  const [showGuanyin,  setShowGuanyin]  = useState(true);

  const [bellOpen,    setBellOpen]    = useState(false);
  const [regEmail,    setRegEmail]    = useState("");
  const [regPhone,    setRegPhone]    = useState("");
  const [regStatus,   setRegStatus]   = useState(null);
  const [regMsg,      setRegMsg]      = useState("");
  const [unsubEmail,  setUnsubEmail]  = useState("");
  const [unsubStatus, setUnsubStatus] = useState(null);
  const [unsubMsg,    setUnsubMsg]    = useState("");
  const [showUnsub,   setShowUnsub]   = useState(false);

  // Pre-warm caches
  useMemo(() => {
    getLunarMapForYear(ty); getLunarMapForYear(ty + 1);
    getGuanyinMapForYear(ty); getGuanyinMapForYear(ty + 1);
  }, [ty]);

  const allUpcoming = useMemo(() => getUpcoming(todayStr, 120), [todayStr]);

  const upcoming = useMemo(() => allUpcoming
    .map(ev => ({
      ...ev,
      holidays: showHoliday ? ev.holidays : [],
      lunar:    ev.lunar && ((ev.lunar.label === '初一' && showChiniOne) || (ev.lunar.label === '十五' && showChiniWu)) ? ev.lunar : null,
      guanyin:  showGuanyin ? ev.guanyin : null,
    }))
    .filter(ev => ev.holidays.length || ev.lunar || ev.guanyin),
  [allUpcoming, showHoliday, showChiniOne, showChiniWu, showGuanyin]);

  const calGrid = useMemo(() => {
    const first = new Date(viewYear, viewMonth, 1).getDay();
    const total = getDaysInMonth(viewYear, viewMonth);
    const cells = Array(first).fill(null);
    for (let d = 1; d <= total; d++) cells.push(d);
    return cells;
  }, [viewYear, viewMonth]);

  function prevMonth() { viewMonth === 0 ? (setViewMonth(11), setViewYear(y => y-1)) : setViewMonth(m => m-1); }
  function nextMonth() { viewMonth === 11 ? (setViewMonth(0), setViewYear(y => y+1)) : setViewMonth(m => m+1); }
  function daysLabel(ds) {
    const d = daysFromToday(ds, todayStr);
    if (d === 0) return "Today 🎉";
    if (d === 1) return "Tomorrow";
    return `In ${d} days`;
  }

  async function handleSubscribe() {
    if (!regEmail || !/^[^@]+@[^@]+\.[^@]+$/.test(regEmail)) {
      setRegStatus('error'); setRegMsg('Please enter a valid email address.'); return;
    }
    setRegStatus('loading'); setRegMsg('');
    try {
      const res  = await fetch('/api/subscribe', { method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({ email:regEmail, phone:regPhone }) });
      const data = await res.json();
      if (data.ok) { setRegStatus('ok'); setRegMsg('✓ Subscribed! You will receive daily reminders.'); setRegEmail(''); setRegPhone(''); }
      else         { setRegStatus('error'); setRegMsg(data.error || 'Something went wrong.'); }
    } catch(e) { setRegStatus('error'); setRegMsg('Network error. Please try again.'); }
  }

  async function handleUnsubscribe() {
    if (!unsubEmail) { setUnsubStatus('error'); setUnsubMsg('Please enter your email.'); return; }
    setUnsubStatus('loading'); setUnsubMsg('');
    try {
      const res  = await fetch('/api/unsubscribe', { method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({ email:unsubEmail }) });
      const data = await res.json();
      if (data.ok) { setUnsubStatus('ok'); setUnsubMsg('✓ Unsubscribed successfully.'); setUnsubEmail(''); }
      else         { setUnsubStatus('error'); setUnsubMsg(data.error || 'Email not found.'); }
    } catch(e) { setUnsubStatus('error'); setUnsubMsg('Network error. Please try again.'); }
  }

  const sc = s => s === 'ok' ? '#6ad89a' : s === 'error' ? '#e8706a' : '#8a9ab8';

  const FilterBar = () => (
    <div style={{ display:"flex", gap:8, flexWrap:"wrap", marginBottom:16 }}>
      <Pill active={showHoliday}  color="#d4a843" onClick={() => setShowHoliday(v => !v)}>🏖 Public Holiday</Pill>
      <Pill active={showChiniOne} color="#e8706a" onClick={() => setShowChiniOne(v => !v)}>🌑 初一</Pill>
      <Pill active={showChiniWu}  color="#6a9fd8" onClick={() => setShowChiniWu(v => !v)}>🌕 十五</Pill>
      <Pill active={showGuanyin}  color="#c084fc" onClick={() => setShowGuanyin(v => !v)}>🙏 观音</Pill>
    </div>
  );

  return (
    <div style={{ minHeight:"100vh", background:"#0f1117", color:"#e8e3d8", fontFamily:"'Segoe UI',system-ui,sans-serif", paddingBottom:60 }}>

      {/* Header */}
      <div style={{ background:"linear-gradient(135deg,#1a1f2e,#12161f)", borderBottom:"1px solid #2a2f3f", padding:"20px 20px 0" }}>
        <div style={{ maxWidth:520, margin:"0 auto" }}>
          <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", marginBottom:4 }}>
            <div style={{ display:"flex", alignItems:"center", gap:12 }}>
              <span style={{ fontSize:28 }}>🏮</span>
              <div>
                <div style={{ fontSize:20, fontWeight:700, color:"#f0e6c8" }}>Malaysia Calendar</div>
                <div style={{ fontSize:12, color:"#8a7f6a", marginTop:2 }}>
                  {DAYS_SHORT[getDayOfWeek(todayStr)]}, {todayStr.slice(8)} {MONTHS[tm-1]} {ty} · MYT
                </div>
              </div>
            </div>
            <button onClick={() => { setBellOpen(v => !v); setRegStatus(null); setUnsubStatus(null); setShowUnsub(false); }} style={{
              position:"relative", background: bellOpen ? "#1e2a40" : "#161b27",
              border: bellOpen ? "1.5px solid #6a9fd8" : "1.5px solid #2a2f3f",
              borderRadius:10, padding:"8px 12px", cursor:"pointer", color:"#6a9fd8", fontSize:18,
            }}>🔔</button>
          </div>
          <div style={{ display:"flex", gap:0, marginTop:12 }}>
            {[["upcoming","Upcoming"],["calendar","Calendar"]].map(([id, label]) => (
              <button key={id} onClick={() => setTab(id)} style={{
                flex:1, padding:"10px 0", background:"transparent", border:"none",
                borderBottom: tab === id ? "2px solid #d4a843" : "2px solid transparent",
                color: tab === id ? "#d4a843" : "#6a6050",
                fontWeight: tab === id ? 600 : 400, fontSize:14, cursor:"pointer",
              }}>{label}</button>
            ))}
          </div>
        </div>
      </div>

      <div style={{ maxWidth:520, margin:"0 auto", padding:"0 16px" }}>

        {/* Bell panel */}
        {bellOpen && (
          <div style={{ marginTop:16, background:"#161b27", borderRadius:12, border:"1px solid #2a3f5f", padding:16 }}>
            <div style={{ fontSize:15, fontWeight:700, color:"#8ab8e8", marginBottom:4 }}>🔔 Daily Notifications</div>
            <div style={{ fontSize:12, color:"#6a6050", marginBottom:16, lineHeight:1.6 }}>
              Get notified every morning at <strong style={{ color:"#c8c0b0" }}>8:00 AM</strong> on event days. Enter your email and optionally your WhatsApp number.
            </div>
            {!showUnsub ? (
              <>
                <Input placeholder="Email address *" value={regEmail} onChange={setRegEmail} type="email" />
                <Input placeholder="WhatsApp number (e.g. 601112345678)" value={regPhone} onChange={setRegPhone} />
                {regMsg && <div style={{ fontSize:12, color:sc(regStatus), marginBottom:10, padding:"8px 10px", background:"#0d1120", borderRadius:6 }}>{regMsg}</div>}
                <button onClick={handleSubscribe} disabled={regStatus === 'loading'} style={{
                  width:"100%", padding:"11px", borderRadius:8, cursor:"pointer", fontSize:13, fontWeight:600, border:"none",
                  background: regStatus === 'ok' ? "#1a4a2a" : "#1a3a5c",
                  color: regStatus === 'ok' ? "#6ad89a" : "#8ab8e8",
                  opacity: regStatus === 'loading' ? 0.6 : 1,
                }}>
                  {regStatus === 'loading' ? "Subscribing..." : regStatus === 'ok' ? "✓ Subscribed!" : "Subscribe for Reminders"}
                </button>
                <button onClick={() => { setShowUnsub(true); setRegStatus(null); setRegMsg(''); }} style={{ width:"100%", marginTop:8, padding:"9px", borderRadius:8, cursor:"pointer", fontSize:12, background:"transparent", border:"1px solid #2a2f3f", color:"#6a6050" }}>
                  Unsubscribe / remove my details
                </button>
              </>
            ) : (
              <>
                <div style={{ fontSize:13, color:"#8a9ab8", marginBottom:10 }}>Enter your email to unsubscribe:</div>
                <Input placeholder="Your email address" value={unsubEmail} onChange={setUnsubEmail} type="email" />
                {unsubMsg && <div style={{ fontSize:12, color:sc(unsubStatus), marginBottom:10, padding:"8px 10px", background:"#0d1120", borderRadius:6 }}>{unsubMsg}</div>}
                <button onClick={handleUnsubscribe} disabled={unsubStatus === 'loading'} style={{
                  width:"100%", padding:"11px", borderRadius:8, cursor:"pointer", fontSize:13, fontWeight:600, border:"none",
                  background:"#3a1a1a", color:"#e8706a", opacity: unsubStatus === 'loading' ? 0.6 : 1,
                }}>
                  {unsubStatus === 'loading' ? "Removing..." : "Remove My Details"}
                </button>
                <button onClick={() => { setShowUnsub(false); setUnsubStatus(null); setUnsubMsg(''); }} style={{ width:"100%", marginTop:8, padding:"9px", borderRadius:8, cursor:"pointer", fontSize:12, background:"transparent", border:"1px solid #2a2f3f", color:"#6a6050" }}>
                  ← Back to Subscribe
                </button>
              </>
            )}
          </div>
        )}

        {/* ── UPCOMING TAB ── */}
        {tab === "upcoming" && (
          <div style={{ paddingTop:16 }}>
            <FilterBar />
            {upcoming.length === 0 && (
              <div style={{ textAlign:"center", color:"#6a6050", padding:40, fontSize:14 }}>No events match your filters in the next 120 days.</div>
            )}
            {upcoming.map(({ key, holidays, lunar, guanyin }) => {
              const [ey, em, ed] = key.split('-').map(Number);
              return (
                <div key={key} style={{ display:"flex", gap:14, marginBottom:10, background:"#161b27", borderRadius:12, padding:"14px 16px", border:"1px solid #222840" }}>
                  <div style={{ minWidth:52, textAlign:"center", background:"#0d1120", borderRadius:8, padding:"8px 4px", alignSelf:"flex-start" }}>
                    <div style={{ fontSize:22, fontWeight:700, color:"#f0e6c8", lineHeight:1 }}>{ed}</div>
                    <div style={{ fontSize:11, color:"#6a6050", marginTop:2 }}>{MONTHS[em-1]}</div>
                    <div style={{ fontSize:10, color:"#6a6050" }}>{ey}</div>
                  </div>
                  <div style={{ flex:1 }}>
                    <div style={{ fontSize:11, color:"#6a7fa8", marginBottom:6, fontWeight:500 }}>
                      {DAYS_SHORT[getDayOfWeek(key)]} · {daysLabel(key)}
                    </div>
                    {holidays.map((h, i) => (
                      <div key={i} style={{ display:"flex", alignItems:"center", gap:8, marginBottom:5 }}>
                        <div style={{ width:6, height:6, borderRadius:"50%", background:"#d4a843", flexShrink:0 }} />
                        <span style={{ fontSize:14, color:"#e8d89a" }}>{h}</span>
                      </div>
                    ))}
                    {lunar && (
                      <div style={{ display:"flex", alignItems:"center", gap:8, marginBottom:5 }}>
                        <div style={{ width:6, height:6, borderRadius:"50%", flexShrink:0, background: lunar.label === '初一' ? "#c0392b" : "#6a9fd8" }} />
                        <span style={{ fontSize:13, fontWeight:600, color: lunar.label === '初一' ? "#e8706a" : "#8ab8e8" }}>{lunar.label} 拜观音</span>
                      </div>
                    )}
                    {guanyin && (
                      <div style={{ display:"flex", alignItems:"center", gap:8, marginBottom:5 }}>
                        <div style={{ width:6, height:6, borderRadius:"50%", flexShrink:0, background:"#c084fc" }} />
                        <span style={{ fontSize:13, fontWeight:600, color:"#c084fc", marginRight:6 }}>{guanyin.label}</span>
                        <span style={{ fontSize:12, color:"#8a6aaa" }}>{guanyin.name}</span>
                      </div>
                    )}
                  </div>
                </div>
              );
            })}
          </div>
        )}

        {/* ── CALENDAR TAB ── */}
        {tab === "calendar" && (
          <div style={{ paddingTop:16 }}>
            <FilterBar />
            <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", marginBottom:12 }}>
              <button onClick={prevMonth} style={{ background:"#1a1f2e", border:"1px solid #2a2f3f", color:"#e8e3d8", borderRadius:8, padding:"8px 14px", cursor:"pointer", fontSize:16 }}>‹</button>
              <span style={{ fontSize:16, fontWeight:600, color:"#f0e6c8" }}>{MONTHS[viewMonth]} {viewYear}</span>
              <button onClick={nextMonth} style={{ background:"#1a1f2e", border:"1px solid #2a2f3f", color:"#e8e3d8", borderRadius:8, padding:"8px 14px", cursor:"pointer", fontSize:16 }}>›</button>
            </div>
            <div style={{ display:"grid", gridTemplateColumns:"repeat(7,1fr)", gap:2, marginBottom:4 }}>
              {DAYS_SHORT.map(d => <div key={d} style={{ textAlign:"center", fontSize:11, color:"#6a6050", fontWeight:600, padding:"4px 0" }}>{d}</div>)}
            </div>
            <div style={{ display:"grid", gridTemplateColumns:"repeat(7,1fr)", gap:2 }}>
              {calGrid.map((day, i) => {
                if (!day) return <div key={i} />;
                const key = `${viewYear}-${String(viewMonth+1).padStart(2,'0')}-${String(day).padStart(2,'0')}`;
                const { holidays:allH, lunar:allL, guanyin:allG } = getEvents(key);
                const isToday = key === todayStr;
                const isPast  = key < todayStr;
                const visH = showHoliday ? allH : [];
                const visL = allL && ((allL.label === '初一' && showChiniOne) || (allL.label === '十五' && showChiniWu)) ? allL : null;
                const visG = showGuanyin ? allG : null;
                const hasH   = visH.length > 0;
                const hasOne = visL?.label === '初一';
                const hasWu  = visL?.label === '十五';
                const hasG   = !!visG;
                const tip = [...allH, ...(allL ? [`${allL.label} 拜观音`] : []), ...(allG ? [`${allG.label} ${allG.name}`] : [])].join('\n');
                return (
                  <div key={i} title={tip} style={{
                    borderRadius:8, padding:"6px 4px 4px", textAlign:"center", minHeight:52,
                    background: isToday ? "#1e2a40" : hasH ? "#1e1a0d" : hasG ? "#1c1628" : "#161b27",
                    border: isToday ? "1.5px solid #d4a843" : hasH ? "1px solid #3a3010" : hasG ? "1px solid #3a2060" : "1px solid #1e2030",
                    opacity: isPast ? 0.4 : 1,
                  }}>
                    <div style={{ fontSize:13, fontWeight: isToday ? 700 : 400, color: isToday ? "#d4a843" : hasH ? "#e8d89a" : "#c8c0b0" }}>{day}</div>
                    <div style={{ display:"flex", justifyContent:"center", gap:2, marginTop:3 }}>
                      {hasH   && <div style={{ width:5, height:5, borderRadius:"50%", background:"#d4a843" }} />}
                      {hasOne && <div style={{ width:5, height:5, borderRadius:"50%", background:"#c0392b" }} />}
                      {hasWu  && <div style={{ width:5, height:5, borderRadius:"50%", background:"#6a9fd8" }} />}
                      {hasG   && <div style={{ width:5, height:5, borderRadius:"50%", background:"#c084fc" }} />}
                    </div>
                    {(hasOne || hasWu) && <div style={{ fontSize:8, color: hasOne ? "#e8706a" : "#8ab8e8", marginTop:2, lineHeight:1 }}>{hasOne ? "初一" : "十五"}</div>}
                    {hasG && !hasOne && !hasWu && <div style={{ fontSize:7, color:"#c084fc", marginTop:2, lineHeight:1 }}>观音</div>}
                  </div>
                );
              })}
            </div>

            {/* Month event list */}
            {(() => {
              const ms = `${viewYear}-${String(viewMonth+1).padStart(2,'0')}`;
              const evs = [];
              if (showHoliday) Object.entries(holidayMap).forEach(([k,v]) => { if (k.startsWith(ms)) v.forEach(h => evs.push({ key:k, text:h, type:"holiday" })); });
              const lm = getLunarMapForYear(viewYear);
              Object.entries(lm).forEach(([k,l]) => {
                if (!k.startsWith(ms)) return;
                if ((l.label === '初一' && showChiniOne) || (l.label === '十五' && showChiniWu))
                  evs.push({ key:k, text:`${l.label} 拜观音`, type:l.label });
              });
              if (showGuanyin) {
                const gm = getGuanyinMapForYear(viewYear);
                Object.entries(gm).forEach(([k,g]) => {
                  if (k.startsWith(ms)) evs.push({ key:k, text:`${g.label} ${g.name}`, type:'guanyin' });
                });
              }
              evs.sort((a,b) => a.key.localeCompare(b.key));
              if (!evs.length) return <div style={{ marginTop:16, color:"#6a6050", fontSize:13, textAlign:"center" }}>No matching events this month.</div>;
              return (
                <div style={{ marginTop:16 }}>
                  <div style={{ fontSize:11, color:"#6a6050", fontWeight:600, marginBottom:8, letterSpacing:"0.5px" }}>{MONTHS[viewMonth].toUpperCase()} {viewYear}</div>
                  {evs.map((e, i) => {
                    const ed  = parseInt(e.key.slice(8));
                    const dot = e.type === "holiday" ? "#d4a843" : e.type === "初一" ? "#c0392b" : e.type === "十五" ? "#6a9fd8" : "#c084fc";
                    return (
                      <div key={i} style={{ display:"flex", alignItems:"center", gap:10, padding:"9px 12px", marginBottom:4, background:"#161b27", borderRadius:8, border:"1px solid #1e2030" }}>
                        <div style={{ width:7, height:7, borderRadius:"50%", background:dot, flexShrink:0 }} />
                        <span style={{ fontSize:12, color:"#8a9ab8", minWidth:58 }}>{DAYS_SHORT[getDayOfWeek(e.key)]} {ed}</span>
                        <span style={{ fontSize:13, color:"#c8c0b0" }}>{e.text}</span>
                      </div>
                    );
                  })}
                </div>
              );
            })()}
          </div>
        )}
      </div>
    </div>
  );
}
