/**
 * TitleCardEditor - Manages Intro Title Card and Outro Ending Screen UI & Settings
 */
class TitleCardEditor {
  constructor(state, player) {
    this.state = state;
    this.player = player;
    this.initEvents();
    this.initFontOptions();
    this.syncInputsFromState();

    this.state.on("styleChanged", () => this.syncInputsFromState());
    this.state.on("projectLoaded", () => this.syncInputsFromState());
  }

  async initFontOptions() {
    const fontSelectIds = [
      "intro-title-font",
      "intro-credits-font",
      "outro-title-font",
      "outro-credits-font"
    ];

    try {
      const res = await fetch("/api/fonts");
      const data = await res.json();
      const fonts = data.fonts || [];

      fontSelectIds.forEach(id => {
        const select = document.getElementById(id);
        if (!select) return;
        const curVal = select.value;
        select.innerHTML = '<option value="">(跟隨全域歌詞字型)</option>';
        fonts.forEach(f => {
          const opt = document.createElement("option");
          opt.value = f.name;
          opt.innerText = f.displayName;
          select.appendChild(opt);
        });
        if (curVal) select.value = curVal;
      });

      this.syncInputsFromState();
    } catch (e) {
      console.log("Could not load fonts for title cards:", e);
    }
  }

  initEvents() {
    const s = (id) => document.getElementById(id);

    // --- INTRO CARD EVENTS ---
    const chkIntro = s("intro-card-toggle");
    if (chkIntro) {
      chkIntro.addEventListener("change", (e) => {
        this.state.updateTitleCard("intro", { enabled: e.target.checked });
      });
    }

    const inIntroStart = s("intro-start-time");
    if (inIntroStart) {
      const updateIntroStart = (e) => {
        this.state.updateTitleCard("intro", { startTime: Math.max(0, parseFloat(e.target.value) || 0) });
      };
      inIntroStart.addEventListener("change", updateIntroStart);
      inIntroStart.addEventListener("input", updateIntroStart);
    }

    const btnSetIntroCur = s("btn-set-intro-start-cur");
    if (btnSetIntroCur) {
      btnSetIntroCur.addEventListener("click", () => {
        const cur = (this.player && this.player.video) ? this.player.video.currentTime : (this.state.currentTime || 0);
        const rounded = Math.round(cur * 10) / 10;
        if (inIntroStart) inIntroStart.value = rounded;
        this.state.updateTitleCard("intro", { startTime: rounded });
      });
    }

    const btnPrevIntro = s("btn-preview-intro-card");
    if (btnPrevIntro) {
      btnPrevIntro.addEventListener("click", () => {
        const intro = this.state.style.titleCards?.intro;
        const st = (intro && typeof intro.startTime === "number") ? intro.startTime : 1.0;
        if (this.player) this.player.seek(st + 0.5);
      });
    }

    const inIntroDur = s("intro-duration");
    if (inIntroDur) {
      const updateIntroDur = (e) => {
        this.state.updateTitleCard("intro", { duration: Math.max(0.5, parseFloat(e.target.value) || 4.5) });
      };
      inIntroDur.addEventListener("change", updateIntroDur);
      inIntroDur.addEventListener("input", updateIntroDur);
    }

    const slIntroFade = s("intro-fade-duration");
    if (slIntroFade) {
      slIntroFade.addEventListener("input", (e) => {
        const val = parseFloat(e.target.value) / 10;
        const lbl = s("intro-fade-duration-val");
        if (lbl) lbl.innerText = `${val.toFixed(1)}s`;
        this.state.updateTitleCard("intro", { fadeDuration: val });
      });
    }

    // Intro Title
    const inIntroTitle = s("intro-title-text");
    if (inIntroTitle) {
      inIntroTitle.addEventListener("input", (e) => {
        this.state.updateTitleCard("intro", { titleText: e.target.value });
      });
    }

    const selIntroTitleFont = s("intro-title-font");
    if (selIntroTitleFont) {
      selIntroTitleFont.addEventListener("change", (e) => {
        this.state.updateTitleCard("intro", { titleFontFamily: e.target.value });
      });
    }

    const chkIntroTitleBold = s("intro-title-bold");
    if (chkIntroTitleBold) {
      chkIntroTitleBold.addEventListener("change", (e) => {
        this.state.updateTitleCard("intro", { titleBold: e.target.checked });
      });
    }

    const slIntroTitleSpacing = s("intro-title-spacing");
    if (slIntroTitleSpacing) {
      slIntroTitleSpacing.addEventListener("input", (e) => {
        const val = parseInt(e.target.value, 10);
        const lbl = s("intro-title-spacing-val");
        if (lbl) lbl.innerText = `${val}px`;
        this.state.updateTitleCard("intro", { titleLetterSpacing: val });
      });
    }

    const slIntroTitleSize = s("intro-title-size");
    if (slIntroTitleSize) {
      slIntroTitleSize.addEventListener("input", (e) => {
        const val = parseInt(e.target.value, 10);
        const lbl = s("intro-title-size-val");
        if (lbl) lbl.innerText = `${val}px`;
        this.state.updateTitleCard("intro", { titleFontSize: val });
      });
    }

    const slIntroTitleY = s("intro-title-y");
    if (slIntroTitleY) {
      slIntroTitleY.addEventListener("input", (e) => {
        const val = parseInt(e.target.value, 10);
        const lbl = s("intro-title-y-val");
        if (lbl) lbl.innerText = `${val}%`;
        this.state.updateTitleCard("intro", { titleY: val / 100 });
      });
    }

    const colIntroTitleFill = s("intro-title-fill");
    if (colIntroTitleFill) {
      colIntroTitleFill.addEventListener("input", (e) => {
        this.state.updateTitleCard("intro", { titleFillColor: e.target.value });
      });
    }

    const colIntroTitleOut = s("intro-title-outline");
    if (colIntroTitleOut) {
      colIntroTitleOut.addEventListener("input", (e) => {
        this.state.updateTitleCard("intro", { titleOutlineColor: e.target.value });
      });
    }

    const slIntroTitleOutW = s("intro-title-outline-w");
    if (slIntroTitleOutW) {
      slIntroTitleOutW.addEventListener("input", (e) => {
        const val = parseInt(e.target.value, 10);
        const lbl = s("intro-title-outline-w-val");
        if (lbl) lbl.innerText = `${val}px`;
        this.state.updateTitleCard("intro", { titleOutlineWidth: val });
      });
    }

    // Intro Credits
    const txtIntroCredits = s("intro-credits-text");
    if (txtIntroCredits) {
      txtIntroCredits.addEventListener("input", (e) => {
        this.state.updateTitleCard("intro", { creditsText: e.target.value });
      });
    }

    const selIntroCreditsFont = s("intro-credits-font");
    if (selIntroCreditsFont) {
      selIntroCreditsFont.addEventListener("change", (e) => {
        this.state.updateTitleCard("intro", { creditsFontFamily: e.target.value });
      });
    }

    const chkIntroCreditsBold = s("intro-credits-bold");
    if (chkIntroCreditsBold) {
      chkIntroCreditsBold.addEventListener("change", (e) => {
        this.state.updateTitleCard("intro", { creditsBold: e.target.checked });
      });
    }

    const slIntroCreditsSpacing = s("intro-credits-spacing");
    if (slIntroCreditsSpacing) {
      slIntroCreditsSpacing.addEventListener("input", (e) => {
        const val = parseInt(e.target.value, 10);
        const lbl = s("intro-credits-spacing-val");
        if (lbl) lbl.innerText = `${val}px`;
        this.state.updateTitleCard("intro", { creditsLetterSpacing: val });
      });
    }

    const slIntroCreditsSize = s("intro-credits-size");
    if (slIntroCreditsSize) {
      slIntroCreditsSize.addEventListener("input", (e) => {
        const val = parseInt(e.target.value, 10);
        const lbl = s("intro-credits-size-val");
        if (lbl) lbl.innerText = `${val}px`;
        this.state.updateTitleCard("intro", { creditsFontSize: val });
      });
    }

    const slIntroCreditsY = s("intro-credits-y");
    if (slIntroCreditsY) {
      slIntroCreditsY.addEventListener("input", (e) => {
        const val = parseInt(e.target.value, 10);
        const lbl = s("intro-credits-y-val");
        if (lbl) lbl.innerText = `${val}%`;
        this.state.updateTitleCard("intro", { creditsY: val / 100 });
      });
    }

    const colIntroCreditsFill = s("intro-credits-fill");
    if (colIntroCreditsFill) {
      colIntroCreditsFill.addEventListener("input", (e) => {
        this.state.updateTitleCard("intro", { creditsFillColor: e.target.value });
      });
    }

    const colIntroCreditsOut = s("intro-credits-outline");
    if (colIntroCreditsOut) {
      colIntroCreditsOut.addEventListener("input", (e) => {
        this.state.updateTitleCard("intro", { creditsOutlineColor: e.target.value });
      });
    }

    const slIntroCreditsOutW = s("intro-credits-outline-w");
    if (slIntroCreditsOutW) {
      slIntroCreditsOutW.addEventListener("input", (e) => {
        const val = parseInt(e.target.value, 10);
        const lbl = s("intro-credits-outline-w-val");
        if (lbl) lbl.innerText = `${val}px`;
        this.state.updateTitleCard("intro", { creditsOutlineWidth: val });
      });
    }


    // --- OUTRO CARD EVENTS ---
    const chkOutro = s("outro-card-toggle");
    if (chkOutro) {
      chkOutro.addEventListener("change", (e) => {
        this.state.updateTitleCard("outro", { enabled: e.target.checked });
      });
    }

    const inOutroStart = s("outro-start-time");
    if (inOutroStart) {
      const updateOutroStart = (e) => {
        this.state.updateTitleCard("outro", { startTime: Math.max(0, parseFloat(e.target.value) || 0) });
      };
      inOutroStart.addEventListener("change", updateOutroStart);
      inOutroStart.addEventListener("input", updateOutroStart);
    }

    const btnSetOutroCur = s("btn-set-outro-start-cur");
    if (btnSetOutroCur) {
      btnSetOutroCur.addEventListener("click", () => {
        const cur = (this.player && this.player.video) ? this.player.video.currentTime : (this.state.currentTime || 0);
        const rounded = Math.round(cur * 10) / 10;
        if (inOutroStart) inOutroStart.value = rounded;
        this.state.updateTitleCard("outro", { startTime: rounded });
      });
    }

    const btnPrevOutro = s("btn-preview-outro-card");
    if (btnPrevOutro) {
      btnPrevOutro.addEventListener("click", () => {
        const outro = this.state.style.titleCards?.outro;
        let st = (outro && typeof outro.startTime === "number") ? outro.startTime : 0;
        if (st <= 0) {
          const maxDur = (this.player && this.player.video && this.player.video.duration) || 30;
          st = Math.max(0, maxDur - 5.0);
        }
        if (this.player) this.player.seek(st + 0.5);
      });
    }

    const inOutroDur = s("outro-duration");
    if (inOutroDur) {
      const updateOutroDur = (e) => {
        this.state.updateTitleCard("outro", { duration: Math.max(0.5, parseFloat(e.target.value) || 4.0) });
      };
      inOutroDur.addEventListener("change", updateOutroDur);
      inOutroDur.addEventListener("input", updateOutroDur);
    }

    const slOutroFade = s("outro-fade-duration");
    if (slOutroFade) {
      slOutroFade.addEventListener("input", (e) => {
        const val = parseFloat(e.target.value) / 10;
        const lbl = s("outro-fade-duration-val");
        if (lbl) lbl.innerText = `${val.toFixed(1)}s`;
        this.state.updateTitleCard("outro", { fadeDuration: val });
      });
    }

    // Outro Title
    const inOutroTitle = s("outro-title-text");
    if (inOutroTitle) {
      inOutroTitle.addEventListener("input", (e) => {
        this.state.updateTitleCard("outro", { titleText: e.target.value });
      });
    }

    const selOutroTitleFont = s("outro-title-font");
    if (selOutroTitleFont) {
      selOutroTitleFont.addEventListener("change", (e) => {
        this.state.updateTitleCard("outro", { titleFontFamily: e.target.value });
      });
    }

    const chkOutroTitleBold = s("outro-title-bold");
    if (chkOutroTitleBold) {
      chkOutroTitleBold.addEventListener("change", (e) => {
        this.state.updateTitleCard("outro", { titleBold: e.target.checked });
      });
    }

    const slOutroTitleSpacing = s("outro-title-spacing");
    if (slOutroTitleSpacing) {
      slOutroTitleSpacing.addEventListener("input", (e) => {
        const val = parseInt(e.target.value, 10);
        const lbl = s("outro-title-spacing-val");
        if (lbl) lbl.innerText = `${val}px`;
        this.state.updateTitleCard("outro", { titleLetterSpacing: val });
      });
    }

    const slOutroTitleSize = s("outro-title-size");
    if (slOutroTitleSize) {
      slOutroTitleSize.addEventListener("input", (e) => {
        const val = parseInt(e.target.value, 10);
        const lbl = s("outro-title-size-val");
        if (lbl) lbl.innerText = `${val}px`;
        this.state.updateTitleCard("outro", { titleFontSize: val });
      });
    }

    const slOutroTitleY = s("outro-title-y");
    if (slOutroTitleY) {
      slOutroTitleY.addEventListener("input", (e) => {
        const val = parseInt(e.target.value, 10);
        const lbl = s("outro-title-y-val");
        if (lbl) lbl.innerText = `${val}%`;
        this.state.updateTitleCard("outro", { titleY: val / 100 });
      });
    }

    const colOutroTitleFill = s("outro-title-fill");
    if (colOutroTitleFill) {
      colOutroTitleFill.addEventListener("input", (e) => {
        this.state.updateTitleCard("outro", { titleFillColor: e.target.value });
      });
    }

    const colOutroTitleOut = s("outro-title-outline");
    if (colOutroTitleOut) {
      colOutroTitleOut.addEventListener("input", (e) => {
        this.state.updateTitleCard("outro", { titleOutlineColor: e.target.value });
      });
    }

    const slOutroTitleOutW = s("outro-title-outline-w");
    if (slOutroTitleOutW) {
      slOutroTitleOutW.addEventListener("input", (e) => {
        const val = parseInt(e.target.value, 10);
        const lbl = s("outro-title-outline-w-val");
        if (lbl) lbl.innerText = `${val}px`;
        this.state.updateTitleCard("outro", { titleOutlineWidth: val });
      });
    }

    // Outro Credits
    const txtOutroCredits = s("outro-credits-text");
    if (txtOutroCredits) {
      txtOutroCredits.addEventListener("input", (e) => {
        this.state.updateTitleCard("outro", { creditsText: e.target.value });
      });
    }

    const selOutroCreditsFont = s("outro-credits-font");
    if (selOutroCreditsFont) {
      selOutroCreditsFont.addEventListener("change", (e) => {
        this.state.updateTitleCard("outro", { creditsFontFamily: e.target.value });
      });
    }

    const chkOutroCreditsBold = s("outro-credits-bold");
    if (chkOutroCreditsBold) {
      chkOutroCreditsBold.addEventListener("change", (e) => {
        this.state.updateTitleCard("outro", { creditsBold: e.target.checked });
      });
    }

    const slOutroCreditsSpacing = s("outro-credits-spacing");
    if (slOutroCreditsSpacing) {
      slOutroCreditsSpacing.addEventListener("input", (e) => {
        const val = parseInt(e.target.value, 10);
        const lbl = s("outro-credits-spacing-val");
        if (lbl) lbl.innerText = `${val}px`;
        this.state.updateTitleCard("outro", { creditsLetterSpacing: val });
      });
    }

    const slOutroCreditsSize = s("outro-credits-size");
    if (slOutroCreditsSize) {
      slOutroCreditsSize.addEventListener("input", (e) => {
        const val = parseInt(e.target.value, 10);
        const lbl = s("outro-credits-size-val");
        if (lbl) lbl.innerText = `${val}px`;
        this.state.updateTitleCard("outro", { creditsFontSize: val });
      });
    }

    const slOutroCreditsY = s("outro-credits-y");
    if (slOutroCreditsY) {
      slOutroCreditsY.addEventListener("input", (e) => {
        const val = parseInt(e.target.value, 10);
        const lbl = s("outro-credits-y-val");
        if (lbl) lbl.innerText = `${val}%`;
        this.state.updateTitleCard("outro", { creditsY: val / 100 });
      });
    }

    const colOutroCreditsFill = s("outro-credits-fill");
    if (colOutroCreditsFill) {
      colOutroCreditsFill.addEventListener("input", (e) => {
        this.state.updateTitleCard("outro", { creditsFillColor: e.target.value });
      });
    }

    const colOutroCreditsOut = s("outro-credits-outline");
    if (colOutroCreditsOut) {
      colOutroCreditsOut.addEventListener("input", (e) => {
        this.state.updateTitleCard("outro", { creditsOutlineColor: e.target.value });
      });
    }

    const slOutroCreditsOutW = s("outro-credits-outline-w");
    if (slOutroCreditsOutW) {
      slOutroCreditsOutW.addEventListener("input", (e) => {
        const val = parseInt(e.target.value, 10);
        const lbl = s("outro-credits-outline-w-val");
        if (lbl) lbl.innerText = `${val}px`;
        this.state.updateTitleCard("outro", { creditsOutlineWidth: val });
      });
    }
  }

  syncInputsFromState() {
    const s = (id) => document.getElementById(id);
    const setVal = (id, val) => { const el = s(id); if (el && val !== undefined) el.value = val; };
    const setCheck = (id, checked) => { const el = s(id); if (el) el.checked = !!checked; };
    const setLabel = (id, text) => { const el = s(id); if (el) el.innerText = text; };

    const tc = this.state.style.titleCards || {};
    const intro = tc.intro || {};
    const outro = tc.outro || {};

    // --- Sync Intro ---
    setCheck("intro-card-toggle", intro.enabled !== false);
    setVal("intro-start-time", (typeof intro.startTime === "number") ? intro.startTime : 1.0);
    setVal("intro-duration", intro.duration || 4.5);
    const inFade = (typeof intro.fadeDuration === "number") ? intro.fadeDuration : 0.6;
    setVal("intro-fade-duration", Math.round(inFade * 10));
    setLabel("intro-fade-duration-val", `${inFade.toFixed(1)}s`);

    setVal("intro-title-text", intro.titleText || "");
    setVal("intro-title-font", intro.titleFontFamily || "");
    setCheck("intro-title-bold", intro.titleBold !== false);
    const inTitleSpacing = (typeof intro.titleLetterSpacing === "number") ? intro.titleLetterSpacing : 6;
    setVal("intro-title-spacing", inTitleSpacing);
    setLabel("intro-title-spacing-val", `${inTitleSpacing}px`);

    const inTitleSize = intro.titleFontSize || 86;
    setVal("intro-title-size", inTitleSize);
    setLabel("intro-title-size-val", `${inTitleSize}px`);
    const inTitleY = Math.round((intro.titleY || 0.35) * 100);
    setVal("intro-title-y", inTitleY);
    setLabel("intro-title-y-val", `${inTitleY}%`);
    setVal("intro-title-fill", intro.titleFillColor || "#00E676");
    setVal("intro-title-outline", intro.titleOutlineColor || "#FFFFFF");
    const inTitleOutW = (typeof intro.titleOutlineWidth === "number") ? intro.titleOutlineWidth : 10;
    setVal("intro-title-outline-w", inTitleOutW);
    setLabel("intro-title-outline-w-val", `${inTitleOutW}px`);

    setVal("intro-credits-text", intro.creditsText || "");
    setVal("intro-credits-font", intro.creditsFontFamily || "");
    setCheck("intro-credits-bold", intro.creditsBold !== false);
    const inCredSpacing = (typeof intro.creditsLetterSpacing === "number") ? intro.creditsLetterSpacing : 2;
    setVal("intro-credits-spacing", inCredSpacing);
    setLabel("intro-credits-spacing-val", `${inCredSpacing}px`);

    const inCredSize = intro.creditsFontSize || 42;
    setVal("intro-credits-size", inCredSize);
    setLabel("intro-credits-size-val", `${inCredSize}px`);
    const inCredY = Math.round((intro.creditsY || 0.55) * 100);
    setVal("intro-credits-y", inCredY);
    setLabel("intro-credits-y-val", `${inCredY}%`);
    setVal("intro-credits-fill", intro.creditsFillColor || "#FFFFFF");
    setVal("intro-credits-outline", intro.creditsOutlineColor || "#000000");
    const inCredOutW = (typeof intro.creditsOutlineWidth === "number") ? intro.creditsOutlineWidth : 6;
    setVal("intro-credits-outline-w", inCredOutW);
    setLabel("intro-credits-outline-w-val", `${inCredOutW}px`);

    // --- Sync Outro ---
    setCheck("outro-card-toggle", !!outro.enabled);
    setVal("outro-start-time", (typeof outro.startTime === "number") ? outro.startTime : 0);
    setVal("outro-duration", outro.duration || 4.0);
    const outFade = (typeof outro.fadeDuration === "number") ? outro.fadeDuration : 0.8;
    setVal("outro-fade-duration", Math.round(outFade * 10));
    setLabel("outro-fade-duration-val", `${outFade.toFixed(1)}s`);

    setVal("outro-title-text", outro.titleText || "");
    setVal("outro-title-font", outro.titleFontFamily || "");
    setCheck("outro-title-bold", outro.titleBold !== false);
    const outTitleSpacing = (typeof outro.titleLetterSpacing === "number") ? outro.titleLetterSpacing : 6;
    setVal("outro-title-spacing", outTitleSpacing);
    setLabel("outro-title-spacing-val", `${outTitleSpacing}px`);

    const outTitleSize = outro.titleFontSize || 76;
    setVal("outro-title-size", outTitleSize);
    setLabel("outro-title-size-val", `${outTitleSize}px`);
    const outTitleY = Math.round((outro.titleY || 0.38) * 100);
    setVal("outro-title-y", outTitleY);
    setLabel("outro-title-y-val", `${outTitleY}%`);
    setVal("outro-title-fill", outro.titleFillColor || "#FFFFFF");
    setVal("outro-title-outline", outro.titleOutlineColor || "#000000");
    const outTitleOutW = (typeof outro.titleOutlineWidth === "number") ? outro.titleOutlineWidth : 8;
    setVal("outro-title-outline-w", outTitleOutW);
    setLabel("outro-title-outline-w-val", `${outTitleOutW}px`);

    setVal("outro-credits-text", outro.creditsText || "");
    setVal("outro-credits-font", outro.creditsFontFamily || "");
    setCheck("outro-credits-bold", outro.creditsBold !== false);
    const outCredSpacing = (typeof outro.creditsLetterSpacing === "number") ? outro.creditsLetterSpacing : 2;
    setVal("outro-credits-spacing", outCredSpacing);
    setLabel("outro-credits-spacing-val", `${outCredSpacing}px`);

    const outCredSize = outro.creditsFontSize || 36;
    setVal("outro-credits-size", outCredSize);
    setLabel("outro-credits-size-val", `${outCredSize}px`);
    const outCredY = Math.round((outro.creditsY || 0.55) * 100);
    setVal("outro-credits-y", outCredY);
    setLabel("outro-credits-y-val", `${outCredY}%`);
    setVal("outro-credits-fill", outro.creditsFillColor || "#E0E0E0");
    setVal("outro-credits-outline", outro.creditsOutlineColor || "#000000");
    const outCredOutW = (typeof outro.creditsOutlineWidth === "number") ? outro.creditsOutlineWidth : 5;
    setVal("outro-credits-outline-w", outCredOutW);
    setLabel("outro-credits-outline-w-val", `${outCredOutW}px`);
  }

  collectFromUI() {
    const s = (id) => document.getElementById(id);
    const getVal = (id, fallback) => { const el = s(id); return el ? el.value : fallback; };
    const getNum = (id, fallback) => { const el = s(id); return el ? (parseFloat(el.value) || 0) : fallback; };
    const getInt = (id, fallback) => { const el = s(id); return el ? (parseInt(el.value, 10) || 0) : fallback; };
    const getCheck = (id, fallback) => { const el = s(id); return el ? el.checked : fallback; };

    const intro = {
      enabled: getCheck("intro-card-toggle", true),
      startTime: getNum("intro-start-time", 1.0),
      duration: Math.max(0.5, getNum("intro-duration", 4.5)),
      fadeDuration: Math.round(getInt("intro-fade-duration", 6)) / 10,
      titleText: getVal("intro-title-text", ""),
      titleFontFamily: getVal("intro-title-font", ""),
      titleBold: getCheck("intro-title-bold", true),
      titleLetterSpacing: getInt("intro-title-spacing", 6),
      titleFontSize: getInt("intro-title-size", 86),
      titleY: Math.round(getInt("intro-title-y", 35)) / 100,
      titleFillColor: getVal("intro-title-fill", "#00E676"),
      titleOutlineColor: getVal("intro-title-outline", "#FFFFFF"),
      titleOutlineWidth: getInt("intro-title-outline-w", 10),
      creditsText: getVal("intro-credits-text", ""),
      creditsFontFamily: getVal("intro-credits-font", ""),
      creditsBold: getCheck("intro-credits-bold", true),
      creditsLetterSpacing: getInt("intro-credits-spacing", 2),
      creditsFontSize: getInt("intro-credits-size", 42),
      creditsY: Math.round(getInt("intro-credits-y", 55)) / 100,
      creditsFillColor: getVal("intro-credits-fill", "#FFFFFF"),
      creditsOutlineColor: getVal("intro-credits-outline", "#000000"),
      creditsOutlineWidth: getInt("intro-credits-outline-w", 6),
    };

    const outro = {
      enabled: getCheck("outro-card-toggle", false),
      startTime: getNum("outro-start-time", 0),
      duration: Math.max(0.5, getNum("outro-duration", 4.0)),
      fadeDuration: Math.round(getInt("outro-fade-duration", 8)) / 10,
      titleText: getVal("outro-title-text", ""),
      titleFontFamily: getVal("outro-title-font", ""),
      titleBold: getCheck("outro-title-bold", true),
      titleLetterSpacing: getInt("outro-title-spacing", 6),
      titleFontSize: getInt("outro-title-size", 76),
      titleY: Math.round(getInt("outro-title-y", 38)) / 100,
      titleFillColor: getVal("outro-title-fill", "#FFFFFF"),
      titleOutlineColor: getVal("outro-title-outline", "#000000"),
      titleOutlineWidth: getInt("outro-title-outline-w", 8),
      creditsText: getVal("outro-credits-text", ""),
      creditsFontFamily: getVal("outro-credits-font", ""),
      creditsBold: getCheck("outro-credits-bold", true),
      creditsLetterSpacing: getInt("outro-credits-spacing", 2),
      creditsFontSize: getInt("outro-credits-size", 36),
      creditsY: Math.round(getInt("outro-credits-y", 55)) / 100,
      creditsFillColor: getVal("outro-credits-fill", "#E0E0E0"),
      creditsOutlineColor: getVal("outro-credits-outline", "#000000"),
      creditsOutlineWidth: getInt("outro-credits-outline-w", 5),
    };

    return { intro, outro };
  }

  syncToState() {
    const titleCards = this.collectFromUI();
    if (!this.state.style.titleCards) this.state.style.titleCards = {};
    this.state.style.titleCards.intro = { ...(this.state.style.titleCards.intro || {}), ...titleCards.intro };
    this.state.style.titleCards.outro = { ...(this.state.style.titleCards.outro || {}), ...titleCards.outro };
  }
}

window.TitleCardEditor = TitleCardEditor;
