// Asset URL helpers (v2: serve from R2 via /api/asset)
function imgUrl(iso, lang) {
  const date = iso.replace(/-/g, "");
  return `/api/asset?date=${date}&type=img&lang=${lang}`;
}
function articleUrl(iso, lang) {
  const date = iso.replace(/-/g, "");
  return `/api/asset?date=${date}&type=article&lang=${lang}`;
}

// Views: Archive, Entry, Pipeline + App root

// ─────────────────────────────────────────────
// Archive view
// ─────────────────────────────────────────────
function ArchiveView({ lang, query, onOpen }) {
  const ui = window.UI;
  const days = window.DAYS;
  const [moodFilter, setMoodFilter] = React.useState("all");
  const [bookFilter, setBookFilter] = React.useState("all");

  // Build unique mood and book lists
  const moods = React.useMemo(() => {
    const map = new Map();
    days.forEach(d => {
      const key = d.weather.mood;
      const eng = d.weather.mood_eng;
      if (!map.has(key)) map.set(key, { key, cht: key, eng, count: 0 });
      map.get(key).count++;
    });
    return Array.from(map.values()).sort((a, b) => b.count - a.count);
  }, []);

  const books = React.useMemo(() => {
    const map = new Map();
    days.forEach(d => {
      const key = d.scripture.book_cht;
      const eng = d.scripture.book_eng;
      if (!map.has(key)) map.set(key, { key, cht: key, eng, count: 0 });
      map.get(key).count++;
    });
    return Array.from(map.values()).sort((a, b) => b.count - a.count);
  }, []);

  // Search + filter
  const filtered = React.useMemo(() => {
    let result = days;
    if (moodFilter !== "all") result = result.filter(d => d.weather.mood === moodFilter);
    if (bookFilter !== "all") result = result.filter(d => d.scripture.book_cht === bookFilter);
    if (query.trim()) {
      const q = query.toLowerCase();
      result = result.filter(d => {
        const hay = [
          d.date_cht, d.date_eng, d.iso,
          d.weather.cht, d.weather.eng, d.weather.badge_cht, d.weather.badge_eng,
          d.weather.mood, d.weather.mood_eng,
          d.scripture.cht, d.scripture.niv, d.scripture.ref_cht, d.scripture.ref_eng,
          d.scripture.book_cht, d.scripture.book_eng,
          d.reflection.cht, d.reflection.eng,
          d.history.world.cht, d.history.world.eng, d.history.world.year,
          d.history.birth.cht, d.history.birth.eng, d.history.birth.year,
          d.history.death.cht, d.history.death.eng, d.history.death.year,
          d.history.custom.cht, d.history.custom.eng, d.history.custom.year
        ].join(" ").toLowerCase();
        return hay.includes(q);
      });
    }
    return result;
  }, [moodFilter, bookFilter, query]);

  const today = days && days.length > 0 ? days[0] : null;

  const hasFilters = moodFilter !== "all" || bookFilter !== "all" || query.trim();

  return (
    <div className="page">
      {!query && !hasFilters && (
        <>
          {/* Hero */}
          <section className="archive-hero">

            <h1>{lang === "cht" ? "每日靜思" : "A Daily Moment"}</h1>
            <div className="alt-title">{lang === "cht" ? "A Daily Moment" : "每日靜思"}</div>
            <p className="hero-subtitle">{lang === "cht" ? "每天都是新的，讓每一天變得不平凡" : "Every day is a new day, be enchanted."}</p>
            <div className="divider-cross">
              <span className="line"></span>
              <Icon name="cross" size={16} />
              <span className="line"></span>
            </div>
          </section>

          {/* Featured today */}
          {today && <FeaturedToday day={today} lang={lang} onOpen={onOpen} />}
        </>
      )}

      {/* Filters + grid */}
      <div className="filters-row">
        <div className="filters-left">
          <h2 className="section-title">{ui.nav_archive[lang]}</h2>
          <span className="section-count">{ui.archive_count[lang](days.length)}</span>
        </div>
      </div>

      <div className="filter-line">
        <span className="filter-group-label">{ui.filter_mood[lang]}</span>
        <button className={"chip" + (moodFilter === "all" ? " active" : "")} onClick={() => setMoodFilter("all")}>
          {ui.filter_all[lang]}<span className="count">{days.length}</span>
        </button>
        {moods.map(m => (
          <button
            key={m.key}
            className={"chip" + (moodFilter === m.key ? " active" : "")}
            onClick={() => setMoodFilter(moodFilter === m.key ? "all" : m.key)}
          >
            {lang === "cht" ? m.cht : m.eng}<span className="count">{m.count}</span>
          </button>
        ))}
      </div>

      <div className="filter-line">
        <span className="filter-group-label">{ui.filter_book[lang]}</span>
        <button className={"chip" + (bookFilter === "all" ? " active" : "")} onClick={() => setBookFilter("all")}>
          {ui.filter_all[lang]}<span className="count">{days.length}</span>
        </button>
        {books.map(b => (
          <button
            key={b.key}
            className={"chip" + (bookFilter === b.key ? " active" : "")}
            onClick={() => setBookFilter(bookFilter === b.key ? "all" : b.key)}
          >
            {lang === "cht" ? b.cht : b.eng}<span className="count">{b.count}</span>
          </button>
        ))}
        {hasFilters && (
          <button className="clear-filters" onClick={() => { setMoodFilter("all"); setBookFilter("all"); }}>
            {ui.filter_clear[lang]}
          </button>
        )}
      </div>

      <div style={{ marginTop: 6, marginBottom: 14, fontSize: 12.5, color: "var(--ink-muted)", letterSpacing: "0.04em", textAlign: "center" }}>
        {ui.filtered_count[lang](filtered.length, days.length)}
      </div>

      {filtered.length === 0 ? (
        <div className="empty">
          <div className="icon"><Icon name="info" size={28} /></div>
          <div>{ui.empty[lang]}</div>
        </div>
      ) : (
        <div className="grid">
          {filtered.map(d => (
            <article key={d.iso} className="card" onClick={() => onOpen(d.iso)}>
              <div className="card-thumb">
                {d.has_real_infographic ? (
                  <>
<img src={imgUrl(d.iso, lang)} alt="" loading="lazy" />
                  </>
                ) : (
                  <MiniInfographic day={d} lang={lang} />
                )}
              </div>
              <div className="card-body">
                <div className="card-date">{lang === "cht" ? d.date_cht : d.date_eng}</div>
                <div className="card-ref">{lang === "cht" ? d.scripture.ref_cht : (d.scripture.ref_eng || "").replace(/\s*NIV\s*/gi, "").trim()}</div>
                {lang === "eng" && d.scripture.niv && (
                  <div className="card-niv">&#8220;{d.scripture.niv.length > 80 ? d.scripture.niv.slice(0, 80) + "…" : d.scripture.niv}&#8221;</div>
                )}
              </div>
            </article>
          ))}
        </div>
      )}
    </div>
  );
}

// Featured today block
function FeaturedToday({ day, lang, onOpen }) {
  const ui = window.UI;
  if (!day) return null;
  const m = moodFor(day);

  async function handleFeaturedShare(e) {
    e.stopPropagation();
    const isCht = lang === "cht";
    const pageUrl = `https://daily.moment.bridge-n-build.org/#entry/${day.iso}`;
    const imageSrc = day.has_real_infographic
      ? imgUrl(day.iso, lang)
      : null;

    const msg = isCht
      ? [`📅 今日：${day.date_cht}`, `🌤 天氣：${day.weather.badge_cht}`, ``, `✝ 今日金句（和合本）`, `「${day.scripture.cht}」`, `— ${day.scripture.ref_cht}`, ``, `🌿 靈修心語`, `${day.reflection.cht}`, ``, `🕊 每日靜思 · A Daily Moment`, pageUrl].join("\n")
      : [`📅 Today: ${day.date_eng}`, `🌤 Weather: ${day.weather.badge_eng}`, ``, `✝ Today's Scripture`, `"${day.scripture.niv}"`, `— ${day.scripture.ref_eng}`, ``, `🌿 Reflection`, `${day.reflection.eng}`, ``, `🕊 A reflection from:`, pageUrl].join("\n");

    if (imageSrc && navigator.canShare) {
      try {
        const resp = await fetch(imageSrc);
        const blob = await resp.blob();
        const file = new File([blob], `daily-moment-${day.iso}.png`, { type: blob.type || "image/png" });
        if (navigator.canShare({ files: [file] })) { await navigator.share({ files: [file], text: msg }); return; }
      } catch (e) {}
    }
    window.open("https://wa.me/?text=" + encodeURIComponent(msg), "_blank", "noopener");
  }

  return (
    <section className="featured" onClick={() => onOpen(day.iso)} role="button" tabIndex={0}
      onKeyDown={(e) => { if (e.key === "Enter") onOpen(day.iso); }}>
      <div className="featured-eyebrow featured-eyebrow-top">
        <span className="dot"></span>
        <span>{ui.today_label[lang]}</span>
      </div>
      <div className="featured-image">
        {day.has_real_infographic ? (
          <img src={imgUrl(day.iso, lang)} alt="" />
        ) : (
          <MiniInfographic day={day} lang={lang} />
        )}
      </div>
      <div className="featured-body">
        <div className="featured-eyebrow featured-eyebrow-body">
          <span className="dot"></span>
          <span>{ui.today_label[lang]}</span>
        </div>
        <div className="featured-date">{lang === "cht" ? day.date_cht : day.date_eng}</div>
        <div className="featured-date-sub">{lang === "cht" ? day.date_eng : day.date_cht}</div>

        <div className="featured-scripture">
          <div className="verse">
            {lang === "cht" ? `「${day.scripture.cht}」` : `"${day.scripture.niv}"`}
          </div>
          <div className="ref">— {lang === "cht" ? day.scripture.ref_cht : day.scripture.ref_eng}</div>
        </div>

        <div className="featured-meta">
          <span className="meta-chip">
            <span className="ico"><Icon name={m.icon} size={14} /></span>
            {lang === "cht" ? day.weather.badge_cht : day.weather.badge_eng}
          </span>
          <span className="meta-chip">
            <span className="ico"><Icon name="leaf" size={14} /></span>
            {lang === "cht" ? day.reflection.cht : day.reflection.eng}
          </span>
        </div>

        <div className="featured-history">
          <div className="featured-history-row">
            <span>🌍</span>
            <span><strong>{day.history.world.year}{lang === "cht" ? "年" : ""}</strong> — {lang === "cht" ? day.history.world.cht : day.history.world.eng}</span>
          </div>
          <div className="featured-history-row">
            <span>🎂</span>
            <span><strong>{day.history.birth.year}{lang === "cht" ? "年" : ""}</strong> — {lang === "cht" ? day.history.birth.cht : day.history.birth.eng}</span>
          </div>
          <div className="featured-history-row">
            <span>🕊️</span>
            <span><strong>{day.history.death.year}{lang === "cht" ? "年" : ""}</strong> — {lang === "cht" ? day.history.death.cht : day.history.death.eng}</span>
          </div>
        </div>

        <div className="featured-actions">
          <a className="featured-cta">
            <span>{ui.read_today[lang]}</span>
            <Icon name="arrow-right" size={16} />
          </a>
          <button className="featured-share" onClick={handleFeaturedShare}>
            <Icon name="share" size={15} />
            <span>{lang === "cht" ? "分享" : "Share"}</span>
          </button>
        </div>
      </div>
    </section>
  );
}

// ─────────────────────────────────────────────
// Entry view
// ─────────────────────────────────────────────
const _CHT_ONES = ["", "一", "二", "三", "四", "五", "六", "七", "八", "九"];
function numToCht(n) {
  n = parseInt(n, 10);
  if (n <= 0) return "";
  if (n < 10) return _CHT_ONES[n];
  if (n < 20) return "十" + _CHT_ONES[n % 10];
  if (n < 100) {
    const t = Math.floor(n / 10), o = n % 10;
    return _CHT_ONES[t] + "十" + (o ? _CHT_ONES[o] : "");
  }
  // 100–999
  const h = Math.floor(n / 100), rem = n % 100;
  const hundreds = _CHT_ONES[h] + "百";
  if (rem === 0) return hundreds;
  if (rem < 10) return hundreds + "零" + _CHT_ONES[rem];
  if (rem < 20) return hundreds + "一十" + _CHT_ONES[rem % 10];
  const t = Math.floor(rem / 10), o = rem % 10;
  return hundreds + _CHT_ONES[t] + "十" + (o ? _CHT_ONES[o] : "");
}

function dateToChtSpoken(iso) {
  const [y, mo, d] = iso.split("-").map(Number);
  return `${y}年${numToCht(mo)}月${numToCht(d)}日`;
}

function dateToEngSpoken(iso) {
  const [y, mo, d] = iso.split("-").map(Number);
  const ORD = ["","first","second","third","fourth","fifth","sixth","seventh","eighth","ninth","tenth",
    "eleventh","twelfth","thirteenth","fourteenth","fifteenth","sixteenth","seventeenth","eighteenth","nineteenth","twentieth",
    "twenty-first","twenty-second","twenty-third","twenty-fourth","twenty-fifth","twenty-sixth","twenty-seventh","twenty-eighth","twenty-ninth","thirtieth","thirty-first"];
  const MON = ["","January","February","March","April","May","June","July","August","September","October","November","December"];
  const ONES = ["","one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve","thirteen","fourteen","fifteen","sixteen","seventeen","eighteen","nineteen"];
  const TENS = ["","","twenty","thirty","forty","fifty","sixty","seventy","eighty","ninety"];
  function yearToEng(y) {
    const thou = Math.floor(y / 1000), rem = y % 1000, hund = Math.floor(rem / 100), to = rem % 100;
    const parts = [];
    if (thou) parts.push(ONES[thou] + " thousand");
    if (hund) parts.push(ONES[hund] + " hundred");
    if (to) {
      const prefix = parts.length ? "and " : "";
      if (to < 20) parts.push(prefix + ONES[to]);
      else parts.push(prefix + TENS[Math.floor(to / 10)] + (to % 10 ? "-" + ONES[to % 10] : ""));
    }
    return parts.join(" ");
  }
  return `The ${ORD[d]} of ${MON[mo]}, ${yearToEng(y)}`;
}

function refToChtSpoken(ref) {
  const m = ref.match(/^(.+?)\s*(\d+)\s*[：:]\s*(\d+)(?:\s*[-–]\s*(\d+))?/);
  if (!m) return ref;
  const book = m[1], chapter = m[2], verse1 = m[3], verse2 = m[4];
  const chapterWord = book.trim() === "詩篇" ? "篇" : "章";
  let spoken = book + numToCht(chapter) + chapterWord + numToCht(verse1);
  if (verse2) spoken += "到" + numToCht(verse2);
  spoken += "節";
  return spoken;
}

function CalendarPopup({ currentIso, onSelect, onClose, lang }) {
  const days = window.DAYS;
  // Normalise iso to YYYY-MM-DD regardless of stored format
  function normIso(s) {
    if (!s) return "";
    s = String(s);
    if (s.includes("-")) return s;
    return s.replace(/(\d{4})(\d{2})(\d{2})/, "$1-$2-$3");
  }
  const normCurrent = normIso(currentIso);
  const isoSet = new Set(days.map(d => normIso(d.iso)));
  const [y, mo] = normCurrent.split("-").map(Number);
  const [viewYear, setViewYear] = React.useState(y);
  const [viewMonth, setViewMonth] = React.useState(mo);

  const MONTH_CHT = ["","一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"];
  const MONTH_ENG = ["","January","February","March","April","May","June","July","August","September","October","November","December"];
  const DOW_CHT = ["日","一","二","三","四","五","六"];
  const DOW_ENG = ["Su","Mo","Tu","We","Th","Fr","Sa"];
  const dow = lang === "cht" ? DOW_CHT : DOW_ENG;

  function prevMonth() { if (viewMonth === 1) { setViewYear(v => v-1); setViewMonth(12); } else setViewMonth(m => m-1); }
  function nextMonth() { if (viewMonth === 12) { setViewYear(v => v+1); setViewMonth(1); } else setViewMonth(m => m+1); }

  const firstDay = new Date(viewYear, viewMonth - 1, 1).getDay();
  const daysInMonth = new Date(viewYear, viewMonth, 0).getDate();
  const cells = [];
  for (let i = 0; i < firstDay; i++) cells.push(null);
  for (let d = 1; d <= daysInMonth; d++) cells.push(d);

  function isoFor(d) { return `${viewYear}-${String(viewMonth).padStart(2,"0")}-${String(d).padStart(2,"0")}`; }

  return (
    <div className="cal-overlay" onClick={onClose}>
      <div className="cal-popup" onClick={e => e.stopPropagation()}>
        <div className="cal-header">
          <button className="cal-nav" onClick={prevMonth}><Icon name="chevron-left" size={14} /></button>
          <span className="cal-title">{lang === "cht" ? `${viewYear}年${MONTH_CHT[viewMonth]}` : `${MONTH_ENG[viewMonth]} ${viewYear}`}</span>
          <button className="cal-nav" onClick={nextMonth}><Icon name="chevron-right" size={14} /></button>
        </div>
        <div className="cal-grid">
          {dow.map(d => <div key={d} className="cal-dow">{d}</div>)}
          {cells.map((d, i) => {
            if (!d) return <div key={"e"+i} />;
            const iso = isoFor(d);
            const hasPost = isoSet.has(iso);
            const isCurrent = iso === normCurrent;
            return (
              <button key={iso} className={"cal-day" + (isCurrent ? " current" : "") + (hasPost ? " has-post" : " no-post")}
                onClick={() => { if (hasPost) { onSelect(iso); onClose(); } }}
                disabled={!hasPost}>
                {d}
              </button>
            );
          })}
        </div>
      </div>
    </div>
  );
}

function EntryView({ iso, lang, onNav, onOpen }) {
  const ui = window.UI;
  const days = window.DAYS;
  const idx = days.findIndex(d => d.iso === iso);
  const day = days[idx];
  if (!day) return null;
  const prev = days[idx + 1]; // older
  const next = days[idx - 1]; // newer
  const m = moodFor(day);

  // Admin preview override — when admin uploads a day and clicks Preview,
  // it stashes blob URLs here so the entry shows the freshly-uploaded images.
  const previewUrls = (window.__ADMIN_PREVIEW_URLS && window.__ADMIN_PREVIEW_URLS[iso]) || null;
  const imageSrc = previewUrls
    ? (lang === "cht" ? previewUrls.cht : previewUrls.eng)
    : imgUrl(day.iso, lang);

  // Whether a standalone HTML article exists for this language (loaded via iframe)
  const hasArticle = day.articles && day.articles[lang] === true;
  const articleSrc = hasArticle ? articleUrl(day.iso, lang) : null;
  // Optional in-page preview from admin (Blob URL)
  const articlePreviewUrl = (window.__ADMIN_PREVIEW_ARTICLES && window.__ADMIN_PREVIEW_ARTICLES[iso] && window.__ADMIN_PREVIEW_ARTICLES[iso][lang]) || null;
  const finalArticleSrc = articlePreviewUrl || articleSrc;

  const [lightbox, setLightbox] = React.useState(false);
  const [showCalendar, setShowCalendar] = React.useState(false);
  const [speaking, setSpeaking] = React.useState(false);
  const [fontSize, setFontSize] = React.useState(1); // multiplier: 1 / 1.2 / 1.4
  const iframeRef = React.useRef(null);
  const speakTimerRef = React.useRef(null);

  function handleIframeLoad() {
    const iframe = iframeRef.current;
    if (!iframe) return;
    try {
      const h = iframe.contentDocument.body.scrollHeight;
      if (h > 0) iframe.style.height = h + "px";
    } catch (e) {}
  }

  React.useEffect(() => { window.scrollTo({ top: 0, behavior: "instant" }); }, [iso]);
  React.useEffect(() => { return () => window.speechSynthesis && window.speechSynthesis.cancel(); }, [iso]);

  function handleSpeak() {
    if (!window.speechSynthesis) return;
    if (speaking) {
      window.speechSynthesis.cancel();
      clearTimeout(speakTimerRef.current);
      setSpeaking(false);
      return;
    }
    const isCht = lang === "cht";

    // Pull prayer + reflection from same-origin iframe
    let prayerText = "";
    let reflectionText = isCht ? day.reflection.cht : day.reflection.eng;
    try {
      const doc = iframeRef.current && iframeRef.current.contentDocument;
      if (doc) {
        prayerText = doc.querySelector(".prayer-text")?.textContent?.trim() || "";
        reflectionText = doc.querySelector(".reflection-text")?.textContent?.trim() || reflectionText;
      }
    } catch (e) {}

    const dateSpoken = isCht ? dateToChtSpoken(day.iso) : dateToEngSpoken(day.iso);
    const scriptureText = isCht ? day.scripture.cht : day.scripture.niv;
    const ref = isCht ? refToChtSpoken(day.scripture.ref_cht) : day.scripture.ref_eng;
    const closing = isCht ? "每天都是新的，讓每一天變得不平凡" : "Every day is a new day, be enchanted.";
    const sep = isCht ? "。" : ". ";

    const parts = [dateSpoken,
      isCht ? "今日金句" : "Today's Scripture", scriptureText + sep + ref];
    if (prayerText) parts.push(isCht ? "讓我們一同禱告" : "Today's Prayer", prayerText);
    parts.push(isCht ? "每日靜思" : "A Daily Moment", reflectionText, closing);

    const audio = window.__musicAudio;

    // CHT pronunciation corrections: display text → spoken text
    const CHT_PRONOUNCE = [
      [/聖名/g, "聖明"],
    ];
    function normaliseCht(t) {
      return CHT_PRONOUNCE.reduce((s, [pat, rep]) => s.replace(pat, rep), t);
    }

    function startReading() {
      const uttLang = isCht ? "zh-HK" : "en-GB";
      let cancelled = false;

      function speakNext(idx) {
        if (cancelled || idx >= parts.length) {
          if (!cancelled) {
            setSpeaking(false);
            // After closing line finishes, let music play 5 more seconds then stop
            if (audio) speakTimerRef.current = setTimeout(() => audio.pause(), 5000);
          }
          return;
        }
        const utt = new SpeechSynthesisUtterance(isCht ? normaliseCht(parts[idx]) : parts[idx]);
        utt.lang = uttLang;
        utt.rate = 0.9;
        utt.onend = () => { speakTimerRef.current = setTimeout(() => speakNext(idx + 1), 1000); };
        utt.onerror = (e) => { if (e.error !== "canceled") setSpeaking(false); else cancelled = true; };
        window.speechSynthesis.speak(utt);
      }

      // Patch cancel so the onerror branch knows it's intentional
      const origCancel = window.speechSynthesis.cancel.bind(window.speechSynthesis);
      window.speechSynthesis.cancel = () => { cancelled = true; clearTimeout(speakTimerRef.current); origCancel(); window.speechSynthesis.cancel = origCancel; };

      speakNext(0);
    }

    // Always start/resume music, wait 2 s, then begin reading
    if (audio) audio.play().catch(() => {});
    setSpeaking(true);
    speakTimerRef.current = setTimeout(startReading, 2000);
  }

  const fontSizes = [1, 1.2, 1.45];
  const fontLabels = ["A", "A+", "A++"];

  async function handleShare() {
    const isCht = lang === "cht";
    const pageUrl = `https://daily.moment.bridge-n-build.org/#entry/${iso}`;

    const msg = isCht
      ? [
          `📅 今日：${day.date_cht}`,
          `🌤 天氣：${day.weather.badge_cht}`,
          ``,
          `✝ 今日金句（和合本）`,
          `「${day.scripture.cht}」`,
          `— ${day.scripture.ref_cht}`,
          ``,
          `🌿 靈修心語`,
          `${day.reflection.cht}`,
          ``,
          `🕊 每日靜思 · A Daily Moment`,
          pageUrl,
        ].join("\n")
      : [
          `📅 Today: ${day.date_eng}`,
          `🌤 Weather: ${day.weather.badge_eng}`,
          ``,
          `✝ Today's Scripture`,
          `"${day.scripture.niv}"`,
          `— ${day.scripture.ref_eng}`,
          ``,
          `🌿 Reflection`,
          `${day.reflection.eng}`,
          ``,
          `🕊 A reflection from:`,
          pageUrl,
        ].join("\n");

    // Try Web Share API with image file (works on mobile)
    if ((day.has_real_infographic || previewUrls) && imageSrc && navigator.canShare) {
      try {
        const resp = await fetch(imageSrc);
        const blob = await resp.blob();
        const file = new File([blob], `daily-moment-${iso}.png`, { type: blob.type || "image/png" });
        if (navigator.canShare({ files: [file] })) {
          await navigator.share({ files: [file], text: msg });
          return;
        }
      } catch (e) {}
    }

    // Fallback: open WhatsApp with text
    window.open("https://wa.me/?text=" + encodeURIComponent(msg), "_blank", "noopener");
  }

  const toolbarControls = (
    <>
      <button className={"toolbar-btn" + (speaking ? " active" : "")} onClick={handleSpeak} title={lang === "cht" ? "朗讀" : "Read aloud"}>
        <span style={{ fontSize: 15 }}>{speaking ? "⏹" : "🔊"}</span>
        <span>{speaking ? (lang === "cht" ? "停止" : "Stop") : (lang === "cht" ? "朗讀" : "Read aloud")}</span>
      </button>
      <div className="toolbar-font-group" style={{ margin: 0 }}>
        {fontLabels.map((label, i) => (
          <button key={i} className={"toolbar-font-btn" + (fontSize === i ? " active" : "")} onClick={() => setFontSize(i)}>{label}</button>
        ))}
      </div>
    </>
  );

  return (
    <div className="page">
      <div className="entry-topnav">
        <div className="entry-topnav-left">
          <button className="toolbar-btn" onClick={handleShare} title={lang === "cht" ? "分享" : "Share"}>
            <Icon name="share" size={15} />
            <span>{lang === "cht" ? "分享" : "Share"}</span>
          </button>
          <button className="entry-back" onClick={() => onNav("archive")}>
            <Icon name="arrow-left" size={14} />
            <span>{ui.back_to_archive[lang]}</span>
          </button>
        </div>
        <div className="day-stepper" style={{ position: "relative" }}>
          <button onClick={() => prev && onOpen(prev.iso)} disabled={!prev} aria-label="Previous day" title={ui.prev_day[lang]}>
            <Icon name="chevron-left" size={16} />
          </button>
          <button className="label label-btn" onClick={() => setShowCalendar(v => !v)} title={lang === "cht" ? "選擇日期" : "Pick a date"}>
            {lang === "cht" ? day.date_cht : day.date_eng}
          </button>
          <button onClick={() => next && onOpen(next.iso)} disabled={!next} aria-label="Next day" title={ui.next_day[lang]}>
            <Icon name="chevron-right" size={16} />
          </button>
          {showCalendar && <CalendarPopup currentIso={iso} onSelect={onOpen} onClose={() => setShowCalendar(false)} lang={lang} />}
        </div>
        <div className="entry-topnav-right">
          {toolbarControls}
        </div>
      </div>

      <div className="entry">
        {lightbox && (
          <div className="lightbox-overlay" onClick={() => setLightbox(false)}>
            <button className="lightbox-close" onClick={() => setLightbox(false)} aria-label="Close">
              <Icon name="x" size={22} />
            </button>
            <img src={imageSrc} alt="" className="lightbox-img" onClick={e => e.stopPropagation()} />
          </div>
        )}
        <div className="entry-image-col">
          <div className="entry-image-frame" style={{ cursor: (day.has_real_infographic || previewUrls) ? "zoom-in" : "default" }}
            onClick={() => { if ((day.has_real_infographic || previewUrls) && imageSrc) setLightbox(true); }}>
            {(day.has_real_infographic || previewUrls) && imageSrc ? (
              <img src={imageSrc} alt={lang === "cht" ? "資訊圖" : "Infographic"} />
            ) : (
              <MiniInfographic day={day} lang={lang} />
            )}
          </div>
          <div className="entry-image-actions">
            <button className="image-action" onClick={async () => {
              if (!imageSrc) return;
              try {
                const resp = await fetch(imageSrc);
                const blob = await resp.blob();
                const url = URL.createObjectURL(blob);
                const a = document.createElement("a");
                a.href = url;
                a.download = `daily-moment-${iso}-${lang}.png`;
                document.body.appendChild(a);
                a.click();
                document.body.removeChild(a);
                setTimeout(() => URL.revokeObjectURL(url), 2000);
              } catch(e) {
                window.open(imageSrc, "_blank", "noopener");
              }
            }}>
              <Icon name="download" size={14} />
              <span>{ui.download_image[lang]}</span>
            </button>
            <button className="image-action image-action-share" onClick={handleShare}>
              <Icon name="share" size={14} />
              <span>{ui.share[lang]}</span>
            </button>
          </div>
        </div>

        <div className="entry-toolbar-float">
          {toolbarControls}
        </div>

        <article className="article-col">
          {finalArticleSrc ? (
            <div className="article-iframe-wrap">
              <div className="article-iframe-bar">
                <div className="article-iframe-meta">
                  <span className="dot-light"></span>
                  <span>{lang === "cht" ? "完整文章" : "Full article"}</span>
                </div>
                <div className="article-iframe-date-center">
                  <span className="article-iframe-date">{lang === "cht" ? day.date_cht : day.date_eng}</span>
                </div>
                <div className="article-iframe-actions">
                  <a className="iframe-action" href={finalArticleSrc} target="_blank" rel="noopener" title={lang === "cht" ? "在新分頁開啟" : "Open in new tab"}>
                    <span style={{ fontSize: 15, lineHeight: 1 }}>⛶</span>
                    <span>{lang === "cht" ? "新分頁" : "New tab"}</span>
                  </a>
                </div>
              </div>
              <iframe
                key={finalArticleSrc}
                ref={iframeRef}
                src={finalArticleSrc}
                className="article-iframe"
                title={(lang === "cht" ? day.date_cht : day.date_eng) + " — full article"}
                loading="lazy"
                onLoad={handleIframeLoad}
                style={{ zoom: fontSizes[fontSize] }}
              />
            </div>
          ) : (
            <div style={{ zoom: fontSizes[fontSize] }}><>
              <h2 className="article-title">{lang === "cht" ? "每日靜思" : "A Daily Moment"}</h2>
              <div className="article-date">{lang === "cht" ? day.date_cht : day.date_eng}</div>
              <div className="article-no-html-note">
                {lang === "cht"
                  ? "此語言的完整文章尚未提供。以下顯示結構化內容。"
                  : "Full article for this language not yet available — showing structured content below."}
              </div>
              <div className="article-divider"></div>

              <section className="article-section">
                <div className="article-section-eyebrow">{ui.weather_today[lang]}</div>
                <div className="weather-card">
                  <span className="badge-pill">
                    <Icon name={m.icon} size={13} />
                    {lang === "cht" ? day.weather.badge_cht : day.weather.badge_eng}
                  </span>
                  <div className="article-body">
                    <p>{lang === "cht" ? day.weather.cht : day.weather.eng}</p>
                    <p style={{ fontSize: 13, color: "var(--ink-muted)", marginTop: 12 }}>
                      <Icon name="leaf" size={12} /> {day.history.custom.year} — {lang === "cht" ? day.history.custom.cht : day.history.custom.eng}
                    </p>
                  </div>
                </div>
              </section>

              <section className="article-section">
                <div className="article-section-eyebrow">{lang === "cht" ? "歷史上的今天" : "On this day"}</div>
                <div className="history-rows">
                  <div className="history-card">
                    <div className="icon">🌍</div>
                    <div className="label">{ui.world_event[lang]}</div>
                    <div className="year">{day.history.world.year}</div>
                    <div className="text">{lang === "cht" ? day.history.world.cht : day.history.world.eng}</div>
                  </div>
                  <div className="history-card">
                    <div className="icon">🎂</div>
                    <div className="label">{ui.born_today[lang]}</div>
                    <div className="year">{day.history.birth.year}</div>
                    <div className="text">{lang === "cht" ? day.history.birth.cht : day.history.birth.eng}</div>
                  </div>
                  <div className="history-card">
                    <div className="icon">🕊️</div>
                    <div className="label">{ui.passed_today[lang]}</div>
                    <div className="year">{day.history.death.year}</div>
                    <div className="text">{lang === "cht" ? day.history.death.cht : day.history.death.eng}</div>
                  </div>
                </div>
              </section>

              <section className="article-section">
                <div className="scripture-panel">
                  <div className="label">
                    ✝ {lang === "cht" ? "今日金句（和合本）" : "Today's Scripture"}
                  </div>
                  <div className="verse">
                    {lang === "cht" ? `「${day.scripture.cht}」` : `"${day.scripture.niv}"`}
                  </div>
                  <div className="verse-bar"></div>
                  <div className="ref">— {lang === "cht" ? day.scripture.ref_cht : day.scripture.ref_eng}</div>
                </div>
              </section>

              <section className="article-section">
                <div className="article-section-eyebrow">{ui.reflection[lang]}</div>
                <div className="reflection-card">
                  <p className="text">
                    <span className="heart">♥&nbsp;&nbsp;</span>
                    {lang === "cht" ? day.reflection.cht : day.reflection.eng}
                  </p>
                </div>
              </section>

              <div className="article-sources">
                {ui.sources_label[lang]}: {ui.sources_line[lang]}
              </div>
            </></div>
          )}
        </article>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────
// About / Reflection view
// ─────────────────────────────────────────────
function AboutView({ lang }) {
  const a = window.ABOUT;
  const paragraphs = lang === "cht" ? a.paragraphs_cht : a.paragraphs_eng;
  return (
    <div className="about-page">
      <div className="about-eyebrow">
        <span className="line"></span>
        <Icon name="cross" size={12} />
        <span>{a.eyebrow[lang]}</span>
        <Icon name="cross" size={12} />
        <span className="line"></span>
      </div>
      <h2 className="about-title">{a.title[lang]}</h2>

      <div className="about-body">
        {paragraphs.map((p, i) => (
          <p key={i} className={"about-para" + (i === paragraphs.length - 1 ? " about-para-final" : "")}>
            {p}
          </p>
        ))}
      </div>

      <a className="rthk-card" href={a.link.href} target="_blank" rel="noopener">
        <div className="rthk-mark">
          <span style={{ fontSize: 24 }}>📻</span>
        </div>
        <div className="rthk-text">
          <div className="rthk-eyebrow">RTHK · Radio 4</div>
          <div className="rthk-title">{lang === "cht" ? "晚禱" : "Reflections"}</div>
          <div className="rthk-sub">{lang === "cht" ? "每周一至五 · 23:57 (UTC+08:00) · 播放中" : "Monday to Friday · 23:57 (UTC+08:00) · still on air"}</div>
        </div>
        <div className="rthk-cta">
          <span>{lang === "cht" ? a.link.label_cht : a.link.label_eng}</span>
          <Icon name="arrow-right" size={14} />
        </div>
      </a>

      <div className="about-signoff">
        <Icon name="leaf" size={14} />
        <span>{lang === "cht" ? "Bridge & Build × 每日靜思" : "Bridge & Build × A Daily Moment"}</span>
      </div>
    </div>
  );
}

Object.assign(window, { ArchiveView, EntryView, AboutView, FeaturedToday });
