/* ========================================================================
   screens-instagram.jsx — Instagram scheduler (centerpiece) + calendar
   ====================================================================== */

const IGQueueRow = ({ ix, thumb, image, cap, when, date, status, active, onSelect, checked = false, onToggle }) => (
  <div className={"ig-post-row " + status}>
    <label className="ig-row-select" onClick={(event) => event.stopPropagation()}>
      <input type="checkbox" checked={checked} onChange={onToggle} aria-label={`Select post ${ix}`} />
      <span />
    </label>
    <div className="grip"><span /><span /><span /></div>
    <button className="thumb" style={{ background: thumb, backgroundImage: image ? `url(${image})` : undefined, backgroundSize: "cover", backgroundPosition: "center" }} onClick={onSelect}>
      <span className="ix">{String(ix).padStart(2, "0")}</span>
      {active && <span className="ix" style={{ right: 4, left: "auto", background: "#EFFF00", color: "#1A1813" }}>ON</span>}
    </button>
    <div style={{ minWidth: 0 }}>
      <div className="cap">{cap}</div>
      <div className="meta">
        <span>{status}</span>
        <span>·</span>
        <span>3 IMG</span>
        <span>·</span>
        <span style={{ color: "#EFFF00" }}>QS 94</span>
      </div>
    </div>
    <div className="when">
      <span className="d">{date}</span>
      <span>{when}</span>
    </div>
    <div className="actions">
      <button><Icon name="edit" size={11} stroke={2} /></button>
      <button><Icon name="copy" size={11} stroke={2} /></button>
      <button><Icon name="more" size={11} stroke={2} /></button>
    </div>
  </div>
);

let InstagramScreen = () => (
  <div style={{ height: "100%", display: "flex", background: "#1A1813" }}>
    <SidebarNav active="instagram" />
    <div style={{ flex: 1, display: "flex", flexDirection: "column", overflow: "hidden" }}>
      <TopBar project="Instagram Scheduler" />

      {/* header strip */}
      <div style={{ padding: "20px 28px 0" }}>
        <div style={{ display: "flex", alignItems: "flex-end", justifyContent: "space-between", flexWrap: "wrap", gap: 12 }}>
          <div>
            <div style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 10.5, letterSpacing: "0.22em", color: "#EFFF00", fontWeight: 800, textTransform: "uppercase", display: "inline-flex", alignItems: "center", gap: 8 }}><span style={{ width: 18, height: 1, background: "#EFFF00" }} />INSTAGRAM · CONTENT BANK</div>
            <h1 style={{ fontFamily: "'Anton', Impact, sans-serif", fontSize: 48, lineHeight: 1, textTransform: "uppercase", margin: "6px 0 4px", color: "#FFF" }}>Queue · 30 posts</h1>
            <div style={{ color: "#8A8A8A", fontSize: 12, display: "flex", gap: 10, flexWrap: "wrap" }}>
              <span>22 scheduled · 6 draft · 2 ready · 0 posted</span>
              <span>·</span>
              <SyncChip state="sync" />
              <span style={{ color: "#FFB23F", fontFamily: "'JetBrains Mono', monospace", fontWeight: 800, letterSpacing: "0.14em", textTransform: "uppercase", fontSize: 10 }}>· Instagram not connected</span>
            </div>
          </div>
          <div style={{ display: "flex", gap: 8 }}>
            <button className="btn btn-secondary"><Icon name="copy" size={12} stroke={2} /> Copy all captions</button>
            <button className="btn btn-secondary"><Icon name="csv" size={12} stroke={2} /> Export CSV</button>
            <button className="btn btn-secondary"><Icon name="md" size={12} stroke={2} /> Export Markdown</button>
            <button className="btn btn-primary"><BoltMini size={13} color="#1A1813" /> Generate 30 ideas</button>
          </div>
        </div>

        {/* AI toolbar */}
        <div style={{ marginTop: 14, background: "#201E18", border: "1px solid #3A372F", padding: 12, display: "grid", gridTemplateColumns: "auto 1fr auto", gap: 12, alignItems: "center" }}>
          <span className="ai-chip"><Icon name="sparkle" size={11} stroke={2} /> AI BULK</span>
          <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
            {[
              { l: "+ 30 IDEAS",       on: false },
              { l: "+ 90 IDEAS",       on: false },
              { l: "BULK CAPTIONS",    on: true  },
              { l: "BULK HASHTAGS",    on: false },
              { l: "BULK CTAS",        on: false },
              { l: "BULK ALT TEXT",    on: false },
              { l: "BULK FIRST COMMENT", on: false },
            ].map(t => (
              <span key={t.l} style={{ padding: "5px 10px", background: t.on ? "#EFFF00" : "#2C2A23", color: t.on ? "#1A1813" : "#C8C8C5", border: t.on ? "1px solid #ADC700" : "1px solid #333", fontFamily: "'JetBrains Mono', monospace", fontSize: 10, fontWeight: 800, letterSpacing: "0.12em" }}>{t.l}</span>
            ))}
          </div>
          <div style={{ display: "flex", gap: 6, alignItems: "center" }}>
            <span style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 10, letterSpacing: "0.14em", color: "#8A8A8A", textTransform: "uppercase", fontWeight: 800 }}>SCHEDULE</span>
            <button className="btn btn-secondary btn-sm">Same time daily</button>
            <button className="btn btn-secondary btn-sm">Choose date · time</button>
          </div>
        </div>
      </div>

      {/* 3 columns: queue + editor + composer */}
      <div style={{ flex: 1, overflow: "hidden", display: "grid", gridTemplateColumns: "1.1fr 1.4fr 360px", gap: 0 }}>
        {/* LEFT — queue list */}
        <div style={{ borderRight: "1px solid #3A372F", padding: "12px 12px 12px 28px", overflowY: "auto" }}>
          <div style={{ display: "flex", gap: 6, marginBottom: 10 }}>
            {["All · 30","Draft · 6","Ready · 2","Scheduled · 22","Posted · 0"].map((t, i) => (
              <button key={t} className="chip" style={{ background: i === 0 ? "#EFFF00" : "#26241E", color: i === 0 ? "#1A1813" : "#C8C8C5", border: i === 0 ? "1px solid #ADC700" : "1px solid #3A372F", fontSize: 9.5, padding: "4px 8px" }}>{t}</button>
            ))}
          </div>
          <div className="ig-queue">
            {[
              { i: 1,  thumb: "linear-gradient(135deg, #FF6B35, #C9522A)", cap: "One folder. Every idea.\nBuilders die from forgotten ideas, not bad ones.", date: "MON 06", when: "09:00", status: "scheduled" },
              { i: 2,  thumb: "linear-gradient(135deg, #4DA8FF, #1E5FCC)", cap: "3 founders. 4 weeks. Zero launches. Until they tried this.", date: "TUE 07", when: "09:00", status: "scheduled" },
              { i: 3,  thumb: "linear-gradient(135deg, #EFFF00, #ADC700)", cap: "Why your last app died. (it wasn't the code)", date: "WED 08", when: "09:00", status: "ready" },
              { i: 4,  thumb: "linear-gradient(135deg, #1E1E1E, #1A1813)", cap: "Streak or starve. Founder ritual that ships.", date: "THU 09", when: "09:00", status: "scheduled" },
              { i: 5,  thumb: "linear-gradient(135deg, #A86BFF, #6B3DCC)", cap: "Notes that compile. Renoo turns daily capture into ship-ready Codex prompts.", date: "FRI 10", when: "09:00", status: "scheduled" },
              { i: 6,  thumb: "linear-gradient(135deg, #4ADE80, #1FA659)", cap: "Day 1 of building in public. Here's everything I'm shipping this month.", date: "SAT 11", when: "10:30", status: "draft" },
              { i: 7,  thumb: "linear-gradient(135deg, #FFB23F, #C97A1A)", cap: "Behind the scenes: how we shipped 14 features in 7 days.", date: "MON 13", when: "09:00", status: "scheduled" },
              { i: 8,  thumb: "linear-gradient(135deg, #FF4D5E, #C9303F)", cap: "I deleted 12 productivity apps last week. Here's what I'm replacing them with.", date: "TUE 14", when: "09:00", status: "draft" },
            ].map(p => <IGQueueRow key={p.i} ix={p.i} {...p} />)}
          </div>
        </div>

        {/* CENTER — editor */}
        <div style={{ overflowY: "auto", padding: 22, background: "#201E18" }}>
          <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 14 }}>
            <span style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 10, letterSpacing: "0.22em", color: "#EFFF00", fontWeight: 800, textTransform: "uppercase" }}>POST 02 / 30 · EDITING</span>
            <Pill tone="yellow" className="ml-auto">SCHEDULED · TUE 09:00</Pill>
            <span className="pill pill-yellow" style={{ fontSize: 9 }}>QS 94</span>
            <span className="pill pill-yellow-soft" style={{ fontSize: 9 }}>BRAND 92</span>
          </div>

          {/* media + caption editor */}
          <div style={{ display: "grid", gridTemplateColumns: "180px 1fr", gap: 14 }}>
            <div>
              <div style={{ background: "linear-gradient(135deg, #4DA8FF, #1E5FCC)", aspectRatio: "1/1", border: "1px solid #333", position: "relative", display: "grid", placeItems: "center" }}>
                <div style={{ fontFamily: "'Anton', Impact, sans-serif", fontSize: 28, color: "#FFF", textTransform: "uppercase", textAlign: "center", padding: "0 12px", letterSpacing: "0.02em" }}>3 FOUNDERS.<br />4 WEEKS.<br />ZERO LAUNCHES.</div>
                <div style={{ position: "absolute", top: 8, left: 8, fontFamily: "'JetBrains Mono', monospace", fontSize: 9, color: "#FFF", padding: "2px 6px", background: "rgba(0,0,0,0.4)", letterSpacing: "0.12em", fontWeight: 800 }}>SLIDE 1/3</div>
              </div>
              <div style={{ display: "flex", gap: 4, marginTop: 8 }}>
                {[2,3].map(s => (
                  <div key={s} style={{ flex: 1, aspectRatio: "1/1", background: "#1E5FCC", border: "1px solid #333", display: "grid", placeItems: "center", color: "#FFF", fontFamily: "'JetBrains Mono', monospace", fontSize: 10, fontWeight: 800, letterSpacing: "0.12em" }}>{s}/3</div>
                ))}
                <div style={{ flex: 1, aspectRatio: "1/1", background: "#201E18", border: "1px dashed #333", display: "grid", placeItems: "center", color: "#5C5C5C" }}><Icon name="plus" size={14} stroke={2} /></div>
              </div>
              <label style={{ display: "flex", alignItems: "center", gap: 6, fontSize: 11, color: "#FFF", marginTop: 12 }}>
                <span style={{ width: 16, height: 16, borderRadius: 3, background: "#EFFF00", display: "grid", placeItems: "center", color: "#1A1813" }}><Icon name="check" size={11} stroke={2.8} /></span>
                Repost as Story
              </label>
              <label style={{ display: "flex", alignItems: "center", gap: 6, fontSize: 11, color: "#FFF", marginTop: 6 }}>
                <span style={{ width: 16, height: 16, borderRadius: 3, border: "1px solid #333", background: "#201E18" }} />
                Pin to grid
              </label>
            </div>
            <div>
              <label className="field-label">Caption</label>
              <textarea className="textarea" defaultValue={"3 founders. 4 weeks. Zero launches.\n\nUntil they tried this →\n\nBuilders die from forgotten ideas, not bad ones. Renoo is a 4-second capture app for builders shipping under chaos.\n\nLink in bio · 7-day trial."} style={{ minHeight: 140 }} />
              <div style={{ display: "flex", justifyContent: "space-between", marginTop: 6, fontFamily: "'JetBrains Mono', monospace", fontSize: 10, color: "#8A8A8A", letterSpacing: "0.1em" }}>
                <span>178 / 2200 CHARS · 4 LINES</span>
                <span style={{ color: "#4ADE80" }}>READABILITY · A</span>
              </div>

              <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginTop: 14 }}>
                <div>
                  <label className="field-label">Hashtags</label>
                  <input className="input" defaultValue="#indiehacker #buildinpublic #notesapp #codex #founder" />
                </div>
                <div>
                  <label className="field-label">CTA</label>
                  <input className="input" defaultValue="Link in bio · 7-day trial" />
                </div>
                <div>
                  <label className="field-label">First comment</label>
                  <input className="input" defaultValue="Try Renoo free — link in bio." />
                </div>
                <div>
                  <label className="field-label">Alt text (a11y)</label>
                  <input className="input" defaultValue="Bold quote card on blue: '3 founders. 4 weeks. Zero launches.'" />
                </div>
                <div>
                  <label className="field-label">Story link sticker</label>
                  <input className="input" defaultValue="https://renoo.app/?ref=ig-02" />
                </div>
                <div>
                  <label className="field-label">Scheduled</label>
                  <div className="input" style={{ display: "flex", alignItems: "center", gap: 8 }}>
                    <Icon name="calendar" size={13} stroke={2} color="#EFFF00" />
                    <span>Tue · 7 Nov · 09:00 BST</span>
                  </div>
                </div>
              </div>

              {/* improve toolbar */}
              <div style={{ marginTop: 16, display: "flex", gap: 6, flexWrap: "wrap" }}>
                {["Punchier","Shorter","More premium","More viral","Founder voice","Stronger CTA","Add hook","A/B 5 hooks","Rewrite for Threads"].map(t => (
                  <button key={t} className="chip" style={{ background: "#2C2A23", color: "#C8C8C5", border: "1px solid #333" }}>{t}</button>
                ))}
              </div>

              <div style={{ marginTop: 16, display: "flex", gap: 8 }}>
                <button className="btn btn-secondary"><Icon name="refresh" size={12} stroke={2} /> Regenerate</button>
                <button className="btn btn-secondary"><Icon name="copy" size={12} stroke={2} /> Copy</button>
                <span style={{ marginLeft: "auto" }} />
                <button className="btn btn-secondary">Save draft</button>
                <button className="btn btn-primary"><Icon name="calendar" size={12} stroke={2} /> Schedule</button>
              </div>
            </div>
          </div>
        </div>

        {/* RIGHT — AI optimisation + connection */}
        <div style={{ borderLeft: "1px solid #3A372F", padding: 18, background: "#1A1813", overflowY: "auto", display: "flex", flexDirection: "column", gap: 14 }}>
          <div className="card" style={{ padding: 14 }}>
            <div className="card-title" style={{ marginBottom: 10 }}>AI optimisation</div>
            <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
              {[
                { l: "Clicks", v: true  },
                { l: "Likes",  v: false },
                { l: "Saves",  v: true  },
                { l: "Shares", v: true  },
                { l: "Comments", v: false },
              ].map(t => (
                <label key={t.l} style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "6px 10px", background: "#201E18", border: "1px solid #3A372F", fontSize: 12, color: "#FFF" }}>
                  <span>{t.l}</span>
                  <span style={{ width: 30, height: 16, borderRadius: 99, background: t.v ? "#EFFF00" : "#1A1A1A", border: t.v ? "1px solid #ADC700" : "1px solid #333", position: "relative" }}>
                    <span style={{ position: "absolute", top: 1, left: t.v ? 15 : 1, width: 12, height: 12, background: t.v ? "#1A1813" : "#5C5C5C", borderRadius: 99, transition: "all .15s" }} />
                  </span>
                </label>
              ))}
              {[
                { l: "Viral hook",   v: true  },
                { l: "Strong CTA",   v: true  },
                { l: "Founder voice",v: true  },
                { l: "Premium tone", v: false },
              ].map(t => (
                <label key={t.l} style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "6px 10px", background: "#201E18", border: "1px solid #3A372F", fontSize: 12, color: "#FFF" }}>
                  <span>{t.l}</span>
                  <span style={{ width: 30, height: 16, borderRadius: 99, background: t.v ? "#EFFF00" : "#1A1A1A", border: t.v ? "1px solid #ADC700" : "1px solid #333", position: "relative" }}>
                    <span style={{ position: "absolute", top: 1, left: t.v ? 15 : 1, width: 12, height: 12, background: t.v ? "#1A1813" : "#5C5C5C", borderRadius: 99 }} />
                  </span>
                </label>
              ))}
            </div>
          </div>

          {/* connect IG */}
          <div style={{ background: "#201E18", border: "1px solid rgba(255,178,63,0.3)", padding: 14, position: "relative", overflow: "hidden" }}>
            <div style={{ position: "absolute", top: 0, left: 0, right: 0, height: 3, background: "#FFB23F" }} />
            <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 8 }}>
              <Icon name="alert" size={15} stroke={2.4} color="#FFB23F" />
              <span style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 11, letterSpacing: "0.18em", color: "#FFB23F", fontWeight: 800, textTransform: "uppercase" }}>NOT CONNECTED</span>
            </div>
            <div style={{ fontSize: 12.5, color: "#C8C8C5", lineHeight: 1.55 }}>
              Auto-posting requires connecting to <b style={{ color: "#FFF" }}>Meta / Instagram Graph</b>. Until then, BoltKit schedules your queue and copies posts to your clipboard at the right time.
            </div>
            <button className="btn btn-secondary btn-sm" style={{ marginTop: 12, width: "100%" }} onClick={() => window.BoltKitConnectInstagram?.()}><Icon name="insta" size={12} stroke={2} /> Connect Instagram</button>
          </div>

          {/* future platforms */}
          <div className="card" style={{ padding: 14 }}>
            <div className="card-title" style={{ marginBottom: 10 }}>Future platforms</div>
            <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
              {[
                { i: "tiktok",   l: "TikTok",       on: false },
                { i: "youtube",  l: "YouTube Shorts", on: false },
                { i: "linkedin", l: "LinkedIn",     on: false },
                { i: "google",   l: "X / Twitter",  on: false },
                { i: "facebook", l: "Facebook Page",on: false },
                { i: "meta",     l: "Threads",      on: false },
              ].map(p => (
                <div key={p.l} style={{ display: "grid", gridTemplateColumns: "auto 1fr auto", gap: 10, padding: "8px 10px", background: "#1A1813", border: "1px solid #3A372F", alignItems: "center" }}>
                  <Icon name={p.i} size={13} stroke={2} color="#8A8A8A" />
                  <span style={{ fontSize: 12, color: "#C8C8C5" }}>{p.l}</span>
                  <span className="pill pill-dark" style={{ fontSize: 8.5 }}>SOON</span>
                </div>
              ))}
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
);

InstagramScreen = () => {
  const initialImportedPosts = (() => {
    try { return JSON.parse(localStorage.getItem("boltkit.scheduledPosts.v1") || "[]"); } catch { return []; }
  })();
  const [queue, setQueue] = React.useState(Array.isArray(initialImportedPosts) ? initialImportedPosts : []);
  const [bulkTime, setBulkTime] = React.useState("09:00");
  const [bulkStartDate, setBulkStartDate] = React.useState(new Date().toISOString().slice(0, 10));
  const weekDays = [
    ["Mon", 1],
    ["Tue", 2],
    ["Wed", 3],
    ["Thu", 4],
    ["Fri", 5],
    ["Sat", 6],
    ["Sun", 0],
  ];
  const [bulkDays, setBulkDays] = React.useState([1, 2, 3, 4, 5]);
  const [queueStatus, setQueueStatus] = React.useState("");
  const [metaAccount, setMetaAccount] = React.useState(null);
  const [scheduleStarted, setScheduleStarted] = React.useState(false);
  const [queueFilter, setQueueFilter] = React.useState("All");
  const [activeGoals, setActiveGoals] = React.useState(["Clicks", "Saves", "Viral hook", "Strong CTA", "Founder voice"]);
  const dateKey = (date) => {
    const safe = date instanceof Date && !Number.isNaN(date.getTime()) ? date : new Date();
    const year = safe.getFullYear();
    const month = String(safe.getMonth() + 1).padStart(2, "0");
    const day = String(safe.getDate()).padStart(2, "0");
    return `${year}-${month}-${day}`;
  };
  const cleanTime = (value) => /^\d{2}:\d{2}$/.test(String(value || "")) ? value : "09:00";
  // Append Z — times are stored and returned as UTC (rowToPost: date.toISOString().slice(11,16))
  // Without Z, new Date() treats the string as local time, causing wrong "missed" detection in non-UTC timezones.
  const scheduledDate = (post = {}) => post.date ? new Date(`${post.date}T${cleanTime(post.time)}:00Z`) : null;
  const isPastQueuePost = (post = {}) => {
    const date = scheduledDate(post);
    const status = String(post.status || "").toLowerCase();
    return Boolean(date && date <= new Date() && !["posted", "published", "sent", "cancelled"].includes(status));
  };
  const saveQueueRemote = () => {}; // localStorage-only; Supabase not configured
  React.useEffect(() => {
    const pid = (() => { try { return JSON.parse(localStorage.getItem("boltkit.currentProject.v1") || "{}").id || null; } catch { return null; } })();
    const url = pid ? `/api/meta/status?projectId=${encodeURIComponent(pid)}` : "/api/meta/status";
    fetch(url)
      .then(r => r.json())
      .then(data => {
        const connected = (data.accounts || []).find(a => a.connection_status === "connected") || (data.accounts || [])[0];
        setMetaAccount(connected || null);
      })
      .catch(() => setMetaAccount(null));
  }, []);
  const saveQueue = (next) => {
    setQueue(next);
    localStorage.setItem("boltkit.scheduledPosts.v1", JSON.stringify(next));
    saveQueueRemote(next, { quiet: true });
    return next;
  };
  const scoreQueue = (items) => {
    const source = items;
    const checks = source.map((post) => {
      const caption = post.caption || post.cap || "";
      const hashtags = String(post.hashtags || "");
      const hasCta = Boolean(post.cta || /save|comment|dm|link|tap|follow|share/i.test(caption));
      const hasHook = caption.trim().split(/\s+/).length >= 5;
      const hashtagCount = (hashtags.match(/#/g) || []).length;
      const score = Math.min(100, 35 + (hasHook ? 20 : 0) + (hasCta ? 20 : 0) + Math.min(20, hashtagCount * 4) + (post.altText ? 5 : 0));
      return { score, hasCta, hasHook, hashtagCount };
    });
    const avg = Math.round(checks.reduce((sum, item) => sum + item.score, 0) / Math.max(checks.length, 1));
    const needsCta = checks.filter(item => !item.hasCta).length;
    const lowTags = checks.filter(item => item.hashtagCount < 4).length;
    return { avg, needsCta, lowTags, total: source.length };
  };
  const [health, setHealth] = React.useState(null);
  const bulkScheduleAll = () => {
    if (!queue.length) {
      setQueueStatus("Import posts from the extractor first, then bulk schedule them here.");
      window.BoltKitToast?.("Import posts first, then bulk schedule.");
      return;
    }
    if (!bulkDays.length) {
      setQueueStatus("Choose at least one posting day before AI scheduling.");
      window.BoltKitToast?.("Choose at least one posting day.");
      return;
    }
    const allowedDays = [...bulkDays].sort((a, b) => a - b);
    const now = new Date();
    const safeTime = cleanTime(bulkTime);
    const [hour = 9, minute = 0] = safeTime.split(":").map(Number);
    const nextSlot = (slotIndex) => {
      const date = new Date(`${bulkStartDate || dateKey(new Date())}T12:00:00`);
      let matches = 0;
      let guard = 0;
      while (guard < 730) {
        const scheduledAt = new Date(date);
        scheduledAt.setHours(hour, minute, 0, 0);
        if (allowedDays.includes(date.getDay()) && scheduledAt > now) {
          if (matches === slotIndex) return scheduledAt;
          matches += 1;
        }
        date.setDate(date.getDate() + 1);
        guard += 1;
      }
      const fallback = new Date(now);
      fallback.setDate(fallback.getDate() + slotIndex + 1);
      fallback.setHours(hour, minute, 0, 0);
      return fallback;
    };
    const next = queue.map((post, index) => {
      const scheduled = nextSlot(index);
      return {
        ...post,
        date: dateKey(scheduled),
        time: safeTime,
        status: "Scheduled",
        storyStatus: post.storyEnabled ? "Scheduled" : post.storyStatus,
        scheduledBy: "AI bulk schedule",
        schedulePlan: {
          startDate: bulkStartDate || dateKey(new Date()),
          time: safeTime,
          weekdays: allowedDays
        },
        platforms: post.platforms?.length ? post.platforms : ["Instagram", ...(post.storyEnabled ? ["Facebook"] : [])]
      };
    });
    saveQueue(next);
    const dayLabels = weekDays.filter(([, value]) => allowedDays.includes(value)).map(([label]) => label).join(", ");
    const skippedPast = new Date(`${bulkStartDate || dateKey(new Date())}T${safeTime}:00`) <= now;
    setQueueStatus(`${next.length} post(s) scheduled from ${next[0]?.date || bulkStartDate} at ${safeTime} on ${dayLabels}. Open Calendar to review.${skippedPast ? " Past times were skipped automatically." : ""}`);
    window.BoltKitToast?.(`${next.length} posts scheduled at ${safeTime}.`);
  };
  const toggleBulkDay = (day) => {
    setBulkDays((items) => items.includes(day) ? items.filter(item => item !== day) : [...items, day].sort((a, b) => a - b));
  };
  const toggleGoal = (goal) => {
    setActiveGoals(items => items.includes(goal) ? items.filter(item => item !== goal) : [...items, goal]);
  };
  const addLinkStickerToAll = () => {
    if (!queue.length) {
      setQueueStatus("Import posts from the extractor first, then add link stickers to the queue.");
      window.BoltKitToast?.("Import posts first.");
      return;
    }
    const defaultLink = "https://boltkit.co.uk";
    const next = queue.map(post => ({
      ...post,
      storyEnabled: true,
      storyLink: post.storyLink || defaultLink,
      storyStatus: post.status === "Scheduled" ? "Scheduled" : "Draft"
    }));
    saveQueue(next);
    setQueueStatus(`Link stickers added to ${next.length} Story draft(s).`);
    window.BoltKitToast?.("Link stickers added to all Story drafts.");
  };
  const startSchedule = () => {
    if (!queue.length) {
      setQueueStatus("Import posts from the extractor first, then start the schedule.");
      window.BoltKitToast?.("Import posts first.");
      return;
    }
    const next = queue.map(post => ({
      ...post,
      status: post.status === "Draft" ? "Scheduled" : post.status || "Scheduled",
      scheduleStarted: true,
      aiGoals: activeGoals
    }));
    saveQueue(next);
    setScheduleStarted(true);
    setQueueStatus(metaAccount ? `Schedule confirmed for @${metaAccount.username || "Instagram"}. ${next.length} post(s) queued.` : `${next.length} post(s) scheduled. Connect Instagram to enable auto-posting.`);
    window.BoltKitToast?.("Schedule started.");
  };
  const runHealthCheck = () => {
    const result = scoreQueue(queue);
    setHealth(result);
    setQueueStatus(`AI Viral SEO Health Check: ${result.avg}/100 across ${result.total} post(s).`);
    window.BoltKitToast?.(`Health check ${result.avg}/100.`);
  };
  const csvEscape = (value) => `"${String(value || "").replace(/"/g, '""')}"`;
  const postsForExport = () => posts.map((post, index) => ({
    number: index + 1,
    title: post.title || post.mediaName || `Post ${index + 1}`,
    caption: post.caption || post.cap || "",
    hashtags: post.hashtags || "",
    cta: post.cta || "",
    date: post.date || "",
    time: post.time || post.when || "",
    status: post.status || "draft",
    platforms: Array.isArray(post.platforms) ? post.platforms.join(" + ") : "Instagram"
  }));
  const copyAllCaptions = async () => {
    const text = postsForExport()
      .map(post => `Post ${post.number}: ${post.title}\n${post.caption}\n${post.hashtags}\nCTA: ${post.cta}`.trim())
      .join("\n\n---\n\n");
    if (!text.trim()) {
      setQueueStatus("No captions to copy yet. Import or generate posts first.");
      window.BoltKitToast?.("No captions ready yet.");
      return;
    }
    await navigator.clipboard?.writeText(text);
    setQueueStatus(`${posts.length} caption(s) copied.`);
    window.BoltKitToast?.("Captions copied.");
  };
  const exportQueueCsv = () => {
    const rows = postsForExport();
    const header = ["number", "title", "caption", "hashtags", "cta", "date", "time", "status", "platforms"];
    const csv = [header.join(","), ...rows.map(row => header.map(key => csvEscape(row[key])).join(","))].join("\n");
    const blob = new Blob([csv], { type: "text/csv;charset=utf-8" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = "boltkit-instagram-queue.csv";
    a.click();
    URL.revokeObjectURL(url);
    setQueueStatus(`${rows.length} queue post(s) exported as CSV.`);
    window.BoltKitToast?.("Scheduler CSV downloaded.");
  };
  const generateMoreIdeas = () => {
    window.BoltKitGo?.("generate", "Choose a social pack to generate more post ideas.");
  };
  const copySelectedPost = async () => {
    const caption = selectedPost.caption || selectedPost.cap || "";
    const text = `${caption}\n\n${selectedPost.hashtags || ""}\nCTA: ${selectedPost.cta || ""}`.trim();
    await navigator.clipboard?.writeText(text);
    setQueueStatus("Selected post copy copied.");
    window.BoltKitToast?.("Post copy copied.");
  };
  const saveSelectedDraft = () => {
    if (!queue.length) {
      setQueueStatus("Sample draft shown. Import posts first to save queue changes.");
      window.BoltKitToast?.("Import real posts first.");
      return;
    }
    saveQueue(queue);
    setQueueStatus("Draft saved locally with Supabase sync attempt.");
    window.BoltKitToast?.("Draft saved.");
  };
  const scheduleSelectedFromEditor = () => {
    if (!queue.length) {
      setQueueStatus("Import posts first, then schedule an individual post.");
      window.BoltKitToast?.("Import posts first.");
      return;
    }
    const target = selectedPost;
    const targetKey = postKey(target, selectedIndex);
    const now = new Date();
    const scheduled = new Date(`${bulkStartDate || dateKey(now)}T${cleanTime(bulkTime)}:00`);
    if (scheduled <= now) {
      scheduled.setDate(scheduled.getDate() + 1);
    }
    const next = queue.map((post, index) => postKey(post, index) === targetKey ? {
      ...post,
      date: dateKey(scheduled),
      time: cleanTime(bulkTime),
      status: "Scheduled",
      platforms: post.platforms?.length ? post.platforms : ["Instagram"]
    } : post);
    saveQueue(next);
    setQueueStatus(`Scheduled "${target.title || target.mediaName || "selected post"}" for ${dateKey(scheduled)} at ${cleanTime(bulkTime)}.`);
    window.BoltKitToast?.("Post scheduled.");
  };
  const posts = queue.length ? queue.map((post, index) => ({
    ...post,
    i: index + 1,
    image: post.remoteUrl || post.previewUrl || window.__boltkitSchedulerPreviews?.[post.localPreviewKey || post.id] || "",
    thumb: post.remoteUrl || post.previewUrl ? "#050505" : "linear-gradient(135deg, #EFFF00, #1A1813)",
    cap: post.caption || post.title || post.mediaName || "Imported post",
    date: post.date || "Draft",
    when: post.time || "09:00",
    status: isPastQueuePost(post) ? "missed time" : String(post.status || "draft").toLowerCase()
  })) : [];
  const [selectedIndex, setSelectedIndex] = React.useState(0);
  const [selectedQueueIds, setSelectedQueueIds] = React.useState([]);
  const visiblePosts = posts.filter(post => {
    const isMissed = isPastQueuePost(post);
    if (queueFilter === "All") return true;
    if (queueFilter === "Missed") return isMissed;
    if (queueFilter === "Unscheduled") return !post.date || String(post.status || "").toLowerCase().includes("draft");
    const status = String(post.status || "").toLowerCase();
    return status.includes(queueFilter.toLowerCase());
  });
  React.useEffect(() => setSelectedIndex(0), [queueFilter]);
  const selectedPost = visiblePosts[selectedIndex] || visiblePosts[0] || posts[0] || {};
  const queueHealth = health || scoreQueue(queue);
  const goals = ["Clicks", "Likes", "Saves", "Shares", "Viral hook", "Strong CTA", "Founder voice", "Premium tone"];
  const isConnected = Boolean(metaAccount);
  const postKey = (post, index) => String(post.id || post.mediaName || post.title || post.i || index);
  const selectedCount = selectedQueueIds.length;
  const missedCount = queue.filter(isPastQueuePost).length;
  const allVisibleSelected = visiblePosts.length > 0 && visiblePosts.every((post, index) => selectedQueueIds.includes(postKey(post, index)));
  const toggleQueuePost = (post, index) => {
    const key = postKey(post, index);
    setSelectedQueueIds((items) => items.includes(key) ? items.filter(item => item !== key) : [...items, key]);
  };
  const toggleAllQueue = () => {
    setSelectedQueueIds(allVisibleSelected ? [] : visiblePosts.map((post, index) => postKey(post, index)));
  };
  const cancelSelectedPosts = () => {
    if (!selectedCount) {
      setQueueStatus("Select one or more posts first, then cancel them.");
      window.BoltKitToast?.("Select posts first.");
      return;
    }
    if (!queue.length) {
      setSelectedQueueIds([]);
      setQueueStatus("Sample posts cannot be cancelled. Import real posts first.");
      window.BoltKitToast?.("Sample posts only.");
      return;
    }
    const selected = new Set(selectedQueueIds);
    const next = queue.filter((post, index) => !selected.has(postKey(post, index)));
    saveQueue(next);
    setSelectedQueueIds([]);
    setSelectedIndex(0);
    setQueueStatus(`${queue.length - next.length} post(s) cancelled and removed from the queue.`);
    window.BoltKitToast?.(`${queue.length - next.length} post(s) cancelled.`);
  };
  return (
    <div className="ig-redesign-shell">
      <SidebarNav active="instagram" />
      <main className="ig-redesign-main">
        <TopBar project="Creator Workspace" />
        <div className="ig-redesign-scroll">
          <section className="ig-hero-panel">
            <div>
              <div className="ig-eyebrow"><span />INSTAGRAM · SCHEDULER</div>
              <h1>Plan a week of posts without the messy tabs.</h1>
              <p>Upload media, generate captions, tune the AI goals, then schedule each post from one calm creator workspace.</p>
              <div className="ig-status-row">
                <SyncChip state="sync" />
                <Pill tone={isConnected ? "green" : "amber"}>{isConnected ? `Instagram connected @${metaAccount.username || "account"}` : "Instagram not connected"}</Pill>
                {!!missedCount && <Pill tone="red">{missedCount} missed time</Pill>}
                <span>{posts.length} draft{posts.length === 1 ? "" : "s"} · {queue.length ? "imported from extractor" : "sample queue"}</span>
              </div>
            </div>
            <div className="ig-hero-actions">
              <button data-bk-bound="true" className="btn btn-secondary" onClick={copyAllCaptions}><Icon name="copy" size={12} stroke={2} /> Copy captions</button>
              <button data-bk-bound="true" className="btn btn-secondary" onClick={exportQueueCsv}><Icon name="csv" size={12} stroke={2} /> Export CSV</button>
              <button data-bk-bound="true" className="btn btn-primary" onClick={generateMoreIdeas}><BoltMini size={13} color="#1A1813" /> Generate 30 ideas</button>
            </div>
          </section>

          <section className="ig-bulk-bar">
            <div className="ig-bulk-row">
              <span className="ai-chip"><Icon name="sparkle" size={11} stroke={2} /> AI BULK</span>
              {["+30 ideas", "+90 ideas", "Bulk captions", "Bulk hashtags", "Bulk CTAs", "Alt text", "First comment"].map((label, index) => (
                <button key={label} className={"ig-pill-button" + (index === 2 ? " active" : "")}>{label}</button>
              ))}
            </div>
            <div className="ig-bulk-row">
              <label className="ig-bulk-time">
                <span>Start date</span>
                <input type="date" value={bulkStartDate} onChange={(event) => setBulkStartDate(event.target.value)} />
              </label>
              <label className="ig-bulk-time">
                <span>Time</span>
                <input type="time" value={bulkTime} onChange={(event) => setBulkTime(event.target.value)} />
              </label>
              <div className="ig-bulk-days" aria-label="Posting weekdays">
                {weekDays.map(([label, value]) => (
                  <button key={label} className={bulkDays.includes(value) ? "on" : ""} onClick={() => toggleBulkDay(value)}>{label}</button>
                ))}
              </div>
              <button data-bk-bound="true" className="btn btn-primary btn-sm" onClick={bulkScheduleAll}><Icon name="calendar" size={11} stroke={2} /> AI schedule all</button>
              <button data-bk-bound="true" className="btn btn-primary btn-sm" onClick={startSchedule}><Icon name="play" size={11} stroke={2} /> Start schedule</button>
              <button data-bk-bound="true" className="btn btn-secondary btn-sm" onClick={runHealthCheck}><Icon name="sparkle" size={11} stroke={2} /> Viral SEO check</button>
              <button data-bk-bound="true" className="btn btn-ghost btn-sm" onClick={addLinkStickerToAll}><Icon name="link" size={11} stroke={2} /> Link sticker</button>
            </div>
          </section>
          <section className="ig-health-strip">
            <div>
              <b>AI #ViralSEOCheck Health Check</b>
              <span>{queueStatus || (missedCount ? `${missedCount} post(s) have missed their scheduled time. Run AI schedule all again to move them into future slots.` : "Run a quick check before scheduling: hook, CTA, hashtags and accessibility.")}</span>
            </div>
            <Pill tone={queueHealth.avg >= 80 ? "green" : queueHealth.avg >= 65 ? "yellow" : "amber"}>{queueHealth.avg}/100</Pill>
            <small>{queueHealth.needsCta} need CTA · {queueHealth.lowTags} need hashtags</small>
          </section>

          <section className="ig-workspace">
            <aside className="ig-queue-panel">
              <div className="ig-panel-head">
                <div>
                  <div className="card-title">Post queue</div>
                  <div className="card-sub">Choose a post, set the date, keep the week moving</div>
                </div>
                <Pill tone="yellow">{posts.length}</Pill>
              </div>
              <div className="ig-chip-row compact">
                {["All", "Unscheduled", "Draft", "Ready", "Scheduled", "Missed"].map((label) => (
                  <button data-bk-bound="true" key={label} className={"ig-pill-button" + (queueFilter === label ? " active" : "")} onClick={() => setQueueFilter(label)}>{label}</button>
                ))}
              </div>
              <div className="ig-queue-select-bar">
                <button data-bk-bound="true" className="ig-pill-button" onClick={toggleAllQueue}>{allVisibleSelected ? "Clear all" : "Select all"}</button>
                <span>{selectedCount} selected</span>
                <button data-bk-bound="true" className="ig-pill-button danger" onClick={cancelSelectedPosts} disabled={!selectedCount}>Cancel selected</button>
              </div>
              <div className="ig-queue-clean">
                {visiblePosts.length
                  ? visiblePosts.map((post, index) => <IGQueueRow key={post.id || post.i} ix={post.i} {...post} active={index === selectedIndex} checked={selectedQueueIds.includes(postKey(post, index))} onToggle={() => toggleQueuePost(post, index)} onSelect={() => setSelectedIndex(index)} />)
                  : (
                    <div className="ig-empty-queue">
                      <b>No {queueFilter.toLowerCase()} posts yet.</b>
                      <span>Import post images from the extractor, generate more ideas, or switch back to All.</span>
                      <div>
                        <button data-bk-bound="true" className="btn btn-secondary btn-sm" onClick={() => setQueueFilter("All")}>Show all</button>
                        <button data-bk-bound="true" className="btn btn-primary btn-sm" onClick={() => window.BoltKitGo?.("extractor")}>Import posts</button>
                      </div>
                    </div>
                  )}
              </div>
            </aside>

            <section className="ig-editor-panel">
              <div className="ig-panel-head">
                <div>
                  <div className="ig-eyebrow small"><span />POST 02 / 30</div>
                  <h2>{selectedPost.title || selectedPost.mediaName || "Imported scheduler draft"}</h2>
                </div>
                <div className="ig-score-pair">
                  <Pill tone={scheduleStarted ? "green" : "yellow"}>{scheduleStarted ? "Started" : "QS 94"}</Pill>
                  <Pill tone="green">Brand 92</Pill>
                </div>
              </div>

              <div className="ig-editor-grid">
                <div className="ig-media-card">
                  <div className="ig-media-preview" style={selectedPost.image ? { backgroundImage: `url(${selectedPost.image})`, backgroundSize: "cover", backgroundPosition: "center" } : undefined}>
                    <span>{selectedPost.mediaName || "SLIDE 1 / 3"}</span>
                    {!selectedPost.image && <b>30 ideas.<br />One brand.<br />Ready to post.</b>}
                  </div>
                  <div className="ig-media-strip">
                    <button>2/3</button>
                    <button>3/3</button>
                    <button><Icon name="plus" size={14} stroke={2} /></button>
                  </div>
                  <label className="ig-check on"><span><Icon name="check" size={11} stroke={3} /></span>Post to Story too</label>
                  <label className="ig-check"><span />Add link sticker</label>
                </div>

                <div className="ig-copy-editor">
                  <label className="field-label">Caption</label>
                  <textarea className="textarea" value={selectedPost.caption || selectedPost.cap || "30 post ideas from one brand pack.\n\nNo blank caption box. No messy folders. No guessing what to post next.\n\nBoltKit turns your links, assets and content notes into captions, hooks, hashtags and a clean posting calendar.\n\nSave this for your next content batch."} readOnly />
                  <div className="ig-form-grid">
                    <label><span>Hashtags</span><input className="input" value={selectedPost.hashtags || "#contentcreator #socialmediatips #instagramtips #creatorbusiness"} readOnly /></label>
                    <label><span>CTA</span><input className="input" value={selectedPost.cta || "Save this for your next batch day"} readOnly /></label>
                    <label><span>First comment</span><input className="input" value={selectedPost.firstComment || "Want the checklist? Drop 'CONTENT' below."} readOnly /></label>
                    <label><span>Scheduled</span><input className="input" value={`${selectedPost.date || "Choose date"} · ${selectedPost.time || selectedPost.when || "09:00"}`} readOnly /></label>
                    <label className="wide"><span>Alt text</span><input className="input" value={selectedPost.altText || selectedPost.title || "Imported social post image."} readOnly /></label>
                  </div>
                </div>
              </div>

              <div className="ig-improve-row">
                {["Punchier", "Shorter", "More viral", "Creator voice", "Stronger CTA", "A/B 5 hooks"].map(label => (
                  <button data-bk-bound="true" key={label} className="ig-pill-button" onClick={() => { setQueueStatus(`${label} improvement queued for the selected post.`); window.BoltKitToast?.(`${label} improvement queued.`); }}>{label}</button>
                ))}
              </div>
              <div className="ig-editor-actions">
                <button data-bk-bound="true" className="btn btn-secondary" onClick={() => window.BoltKitGo?.("generate", "Generate a fresh social caption pack.")}><Icon name="refresh" size={12} stroke={2} /> Regenerate</button>
                <button data-bk-bound="true" className="btn btn-secondary" onClick={copySelectedPost}><Icon name="copy" size={12} stroke={2} /> Copy</button>
                <span />
                <button data-bk-bound="true" className="btn btn-secondary" onClick={saveSelectedDraft}>Save draft</button>
                <button data-bk-bound="true" className="btn btn-primary" onClick={scheduleSelectedFromEditor}><Icon name="calendar" size={12} stroke={2} /> Schedule post</button>
              </div>
            </section>

            <aside className="ig-side-panel">
              <div className="ig-card-soft">
                <div className="card-title">AI optimisation goals</div>
                <div className="ig-goal-grid">
                  {goals.map((goal, index) => (
                    <button key={goal} className={"ig-goal" + (activeGoals.includes(goal) ? " on" : "")} onClick={() => toggleGoal(goal)}>
                      <span>{goal}</span>
                      <i />
                    </button>
                  ))}
                </div>
              </div>
              <div className="ig-warning-card">
                <div><Icon name={isConnected ? "check" : "alert"} size={15} stroke={2.4} color={isConnected ? "#4ADE80" : "#FFB23F"} /> {isConnected ? "CONNECTED" : "NOT CONNECTED"}</div>
                <p>{isConnected ? `Connected to @${metaAccount.username || "Instagram"}. First launch uses schedule, export and manual posting; auto-posting stays disabled until Meta review and worker testing are complete.` : "First launch uses schedule, export and manual posting. Meta auto-posting will come after permissions, encrypted token storage and worker tests are complete."}</p>
                <button className="btn btn-secondary btn-sm" onClick={() => window.BoltKitConnectInstagram?.()}><Icon name="insta" size={12} stroke={2} /> {isConnected ? "Reconnect Instagram" : "Connect Instagram"}</button>
              </div>
              <div className="ig-card-soft">
                <div className="card-title">Coming next</div>
                {["TikTok", "YouTube Shorts", "LinkedIn", "Threads", "Facebook Page"].map(label => (
                  <div key={label} className="ig-soon-row"><span>{label}</span><Pill tone="dark">Soon</Pill></div>
                ))}
              </div>
            </aside>
          </section>
        </div>
      </main>
    </div>
  );
};

/* CONTENT CALENDAR */
let CalendarScreen = () => (
  <div style={{ height: "100%", display: "flex", background: "#1A1813" }}>
    <SidebarNav active="calendar" />
    <div style={{ flex: 1, display: "flex", flexDirection: "column", overflow: "hidden" }}>
      <TopBar />
      <div style={{ flex: 1, overflowY: "auto", padding: "24px 28px 40px" }}>
        <div style={{ display: "flex", alignItems: "flex-end", justifyContent: "space-between", marginBottom: 18, flexWrap: "wrap", gap: 12 }}>
          <div>
            <div style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 10.5, letterSpacing: "0.22em", color: "#EFFF00", fontWeight: 800, textTransform: "uppercase", display: "inline-flex", alignItems: "center", gap: 8 }}><span style={{ width: 18, height: 1, background: "#EFFF00" }} />CONTENT CALENDAR</div>
            <h1 style={{ fontFamily: "'Anton', Impact, sans-serif", fontSize: 48, lineHeight: 1, textTransform: "uppercase", margin: "6px 0 0", color: "#FFF" }}>November 2026</h1>
          </div>
          <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
            <div style={{ display: "flex", background: "#201E18", border: "1px solid #3A372F" }}>
              {["Month","Week","Day"].map((v, i) => (
                <div key={v} style={{ padding: "8px 14px", background: i === 0 ? "#EFFF00" : "transparent", color: i === 0 ? "#1A1813" : "#C8C8C5", fontFamily: "'JetBrains Mono', monospace", fontSize: 10.5, letterSpacing: "0.14em", fontWeight: 800, textTransform: "uppercase", cursor: "pointer" }}>{v}</div>
              ))}
            </div>
            <button className="icon-btn"><Icon name="chevron_left" size={13} stroke={2} /></button>
            <button className="icon-btn"><Icon name="chevron_right" size={13} stroke={2} /></button>
            <button className="btn btn-secondary btn-sm"><Icon name="filter" size={11} stroke={2} /> Filters</button>
            <button className="btn btn-primary btn-sm"><Icon name="plus" size={11} stroke={2.4} /> New post</button>
          </div>
        </div>

        {/* filter chips */}
        <div style={{ display: "flex", gap: 6, marginBottom: 14, flexWrap: "wrap" }}>
          {["All projects","Renoo","Atelier","Lofti","Instagram","TikTok","Reels","Drafts","Scheduled","Posted"].map((c, i) => (
            <span key={c} className="chip" style={{ background: i === 0 ? "#FFF" : "#26241E", color: i === 0 ? "#1A1813" : "#C8C8C5", border: i === 0 ? "1px solid #FFF" : "1px solid #3A372F", fontSize: 9.5 }}>{c}</span>
          ))}
        </div>

        {/* week header */}
        <div className="cal-grid">
          {["MON","TUE","WED","THU","FRI","SAT","SUN"].map(d => (
            <div key={d} style={{ background: "#1A1813", padding: "8px 8px", fontFamily: "'JetBrains Mono', monospace", fontSize: 10, color: "#8A8A8A", letterSpacing: "0.18em", fontWeight: 800 }}>{d}</div>
          ))}
        </div>

        {/* month grid */}
        <div className="cal-grid" style={{ borderTop: 0 }}>
          {/* 5 rows × 7 cols = 35 cells. Start with last days of Oct, then Nov */}
          {Array.from({ length: 35 }).map((_, i) => {
            const day = i - 5; // Oct ends, Nov starts at col 6
            const dim = day < 1 || day > 30;
            const today = day === 6;
            const events = {
              1:  [{ l: "RENOO · IG 1", t: "y" }],
              2:  [{ l: "RENOO · IG 2", t: "y" }, { l: "REEL 02", t: "b" }],
              3:  [{ l: "RENOO · IG 3", t: "y" }],
              6:  [{ l: "ATELIER · IG", t: "g" }, { l: "RENOO · IG 5", t: "y" }, { l: "TikTok 01", t: "b" }],
              7:  [{ l: "RENOO · IG 6", t: "y" }, { l: "+ 2 MORE", t: "gh" }],
              8:  [{ l: "RENOO · IG 7", t: "y" }],
              9:  [{ l: "RENOO · IG 8", t: "y" }, { l: "LinkedIn", t: "b" }],
              10: [{ l: "RENOO · IG 9", t: "y" }],
              13: [{ l: "LOFTI · IG 1", t: "g" }],
              14: [{ l: "RENOO · IG 10", t: "y" }, { l: "REEL 04", t: "b" }],
              15: [{ l: "RENOO · IG 11", t: "y" }],
              16: [{ l: "RENOO · IG 12", t: "y" }],
              17: [{ l: "ATELIER · IG", t: "g" }],
              20: [{ l: "RENOO · IG 13", t: "y" }],
              21: [{ l: "RENOO · IG 14", t: "y" }],
              22: [{ l: "RENOO · IG 15", t: "y" }],
              23: [{ l: "RENOO · IG 16", t: "y" }],
              24: [{ l: "LOFTI · IG 2",  t: "g" }],
              27: [{ l: "RENOO · IG 17", t: "y" }, { l: "REEL 05", t: "b" }],
              28: [{ l: "RENOO · IG 18", t: "y" }],
              29: [{ l: "RENOO · IG 19", t: "y" }],
              30: [{ l: "RENOO · IG 20", t: "y" }, { l: "LAUNCH DAY", t: "gh" }],
            }[day] || [];
            return (
              <div key={i} className={"cal-cell" + (dim ? " dim" : "") + (today ? " today" : "")}>
                <div className="d">{dim ? (day < 1 ? 25 + i : day - 30) : day}</div>
                {events.map((e, j) => <div key={j} className={"cal-event " + (e.t === "y" ? "" : e.t)}>{e.l}</div>)}
              </div>
            );
          })}
        </div>

        {/* legend */}
        <div style={{ marginTop: 14, display: "flex", gap: 18, color: "#8A8A8A", fontFamily: "'JetBrains Mono', monospace", fontSize: 10.5, letterSpacing: "0.14em", textTransform: "uppercase", fontWeight: 800, alignItems: "center" }}>
          <span style={{ display: "flex", alignItems: "center", gap: 6 }}><span style={{ width: 12, height: 12, background: "#EFFF00" }} />RENOO</span>
          <span style={{ display: "flex", alignItems: "center", gap: 6 }}><span style={{ width: 12, height: 12, background: "#4DA8FF" }} />REELS · TIKTOK</span>
          <span style={{ display: "flex", alignItems: "center", gap: 6 }}><span style={{ width: 12, height: 12, background: "#4ADE80" }} />OTHER PROJECTS</span>
          <span style={{ marginLeft: "auto" }}>Drag posts to reschedule · Cmd+click to duplicate</span>
        </div>
      </div>
    </div>
  </div>
);

CalendarScreen = () => {
  const initialScheduledQueue = (() => {
    try { return JSON.parse(localStorage.getItem("boltkit.scheduledPosts.v1") || "[]"); } catch { return []; }
  })();
  const [queuePosts, setQueuePosts] = React.useState(Array.isArray(initialScheduledQueue) ? initialScheduledQueue : []);
  const firstScheduled = queuePosts.find(post => post.date)?.date || new Date().toISOString().slice(0, 10);
  const [visibleMonth, setVisibleMonth] = React.useState(() => {
    const start = new Date(`${firstScheduled}T12:00:00`);
    return new Date(start.getFullYear(), start.getMonth(), 1, 12);
  });
  const [selectedDate, setSelectedDate] = React.useState(firstScheduled);
  const [selectedPost, setSelectedPost] = React.useState(queuePosts[0]?.id || "renoo-ig-05");
  const [time, setTime] = React.useState(queuePosts[0]?.time || "09:00");
  const [platforms, setPlatforms] = React.useState(["Instagram", "Facebook"]);
  const [calendarMode, setCalendarMode] = React.useState("Month");
  const [publishingId, setPublishingId] = React.useState(null);
  const [igAccount, setIgAccount] = React.useState(null);
  const [autoDateStatus, setAutoDateStatus] = React.useState("");
  const [autopilotState, setAutopilotState] = React.useState("idle"); // idle | pushing | on | error
  const [igPerDay, setIgPerDay] = React.useState(7); // posts per day for auto-date
  const [confirmClearAll, setConfirmClearAll] = React.useState(false);
  const [dragDraftId, setDragDraftId] = React.useState(null); // for drafts-tray drag
  const [draftPosts, setDraftPosts] = React.useState(() => {
    try { return JSON.parse(localStorage.getItem("boltkit.igPosts.v2") || "[]"); } catch { return []; }
  });

  // Auto-jump ref: navigates visibleMonth to first scheduled post ONCE when posts first load
  const hasAutoJumpedIG = React.useRef(false);
  React.useEffect(() => {
    if (hasAutoJumpedIG.current || !queuePosts.length) return;
    const first = queuePosts.find(p => p.date && String(p.date).match(/\d{4}-\d{2}-\d{2}/));
    if (!first) return;
    const d = new Date(`${first.date}T12:00:00`);
    setVisibleMonth(new Date(d.getFullYear(), d.getMonth(), 1, 12));
    setSelectedDate(first.date);
    hasAutoJumpedIG.current = true;
  }, [queuePosts.length]);

  // Ref to trigger an immediate server reload (e.g. on project switch)
  const igLoadTrigger = React.useRef(null);

  // Load from Supabase on mount — syncs status changes; never removes local posts
  React.useEffect(() => {
    let running = false;
    const loadFromServer = async () => {
      if (running) return;
      running = true;
      try {
        const proj = (() => { try { return JSON.parse(localStorage.getItem("boltkit.currentProject.v1") || "{}"); } catch { return {}; } })();
        const ctrl = new AbortController();
        const timer = setTimeout(() => ctrl.abort(), 20000);
        let resp;
        try {
          resp = await fetch("/api/supabase-scheduled-posts", {
            method: "POST", headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ action: "load", project: proj }),
            signal: ctrl.signal,
          });
        } finally { clearTimeout(timer); }
        const data = await resp.json();
        if (!data.ok || !Array.isArray(data.posts)) return;
        const local = (() => { try { return JSON.parse(localStorage.getItem("boltkit.scheduledPosts.v1") || "[]"); } catch { return []; } })();
        if (!data.posts.length) return; // server empty — keep whatever local has
        if (!local.length) {
          // Don't restore from server if the user explicitly cleared within the last 30 min
          try {
            const clrAt = parseInt(localStorage.getItem("boltkit.igCalClearedAt") || "0");
            if (clrAt && (Date.now() - clrAt) < 60 * 60 * 1000) return;
          } catch {}
          // First load with empty local: populate from server
          try { localStorage.setItem("boltkit.scheduledPosts.v1", JSON.stringify(data.posts)); } catch {}
          setQueuePosts(data.posts);
          return;
        }
        // Status-sync mode: update statuses from server; keep all local posts intact
        const serverMap = new Map(data.posts.map(p => [p.id, p]));
        const localIds = new Set(local.map(p => p.id));
        let changed = false;
        const synced = local.map(p => {
          const sv = serverMap.get(p.id);
          if (!sv) return p;
          if (sv.status !== p.status) { changed = true; return { ...p, status: sv.status }; }
          return p;
        });
        const serverOnly = data.posts.filter(p => !localIds.has(p.id));
        if (serverOnly.length) { changed = true; synced.push(...serverOnly); }
        if (changed) {
          try { localStorage.setItem("boltkit.scheduledPosts.v1", JSON.stringify(synced)); } catch {}
          setQueuePosts(synced);
        }
      } catch {} finally { running = false; }
    };
    igLoadTrigger.current = loadFromServer;
    loadFromServer();
    const t = setInterval(loadFromServer, 120000); // refresh every 2 min
    return () => { clearInterval(t); igLoadTrigger.current = null; };
  }, []);

  // When user switches project — clear this project's local cache and reload from Supabase
  React.useEffect(() => {
    const onSwitch = () => {
      try { localStorage.setItem("boltkit.scheduledPosts.v1", JSON.stringify([])); } catch {}
      setQueuePosts([]);
      setTimeout(() => igLoadTrigger.current?.(), 150);
    };
    window.addEventListener("boltkit:project-switched", onSwitch);
    return () => window.removeEventListener("boltkit:project-switched", onSwitch);
  }, []);

  React.useEffect(() => {
    const pid = (() => { try { return JSON.parse(localStorage.getItem("boltkit.currentProject.v1") || "{}").id || null; } catch { return null; } })();
    const url = pid ? `/api/meta/status?projectId=${encodeURIComponent(pid)}` : "/api/meta/status";
    fetch(url).then(r => r.json()).then(d => {
      if (d.accounts?.[0]) setIgAccount(d.accounts[0]);
    }).catch(() => {});
  }, []);

  // Reload queue whenever any screen writes new posts to localStorage
  React.useEffect(() => {
    const reload = () => {
      try {
        const posts = JSON.parse(localStorage.getItem("boltkit.scheduledPosts.v1") || "[]");
        setQueuePosts(Array.isArray(posts) ? posts : []);
      } catch {}
    };
    window.addEventListener("boltkit:saved", reload);
    const t = setInterval(reload, 10000);
    return () => { clearInterval(t); window.removeEventListener("boltkit:saved", reload); };
  }, []);

  // Reload draft posts whenever generate screen saves
  React.useEffect(() => {
    const reload = () => {
      try { setDraftPosts(JSON.parse(localStorage.getItem("boltkit.igPosts.v2") || "[]")); } catch {}
    };
    window.addEventListener("boltkit:saved", reload);
    const t = setInterval(reload, 10000);
    return () => { clearInterval(t); window.removeEventListener("boltkit:saved", reload); };
  }, []);

  // Auto-date all undated posts starting today at igPerDay posts/day
  const autoDateAllPosts = () => {
    const undated = queuePosts.filter(p => !p.date || !String(p.date).match(/\d{4}-\d{2}-\d{2}/));
    if (!undated.length) { window.BoltKitToast?.({ msg: "All posts already have dates.", type: "success" }); return; }
    const ppd = Math.max(1, Math.min(12, igPerDay));
    const pad = (n) => String(n).padStart(2, "0");
    const dKey = (d) => `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())}`;
    const tStr = (d) => `${pad(d.getHours())}:${pad(d.getMinutes())}`;

    // First post fires 15 minutes from now so it goes out almost immediately
    // (the publish worker runs every 15 min). The rest spread evenly across each
    // day's 07:00 → 23:59 window; once a slot would cross midnight it rolls to
    // 07:00 the next day. Per-day density is set by the igPerDay slider.
    const DAY_START = 7 * 60;        // 07:00
    const DAY_END = 23 * 60 + 59;    // 23:59 — last slot of a day
    const interval = ppd > 1 ? Math.round((DAY_END - DAY_START) / (ppd - 1)) : (DAY_END - DAY_START);

    let cursor = new Date(Date.now() + 15 * 60 * 1000); // start 15 min from upload
    cursor.setSeconds(0, 0);
    const dated = undated.map((post) => {
      const slot = { ...post, date: dKey(cursor), time: tStr(cursor), status: "Scheduled" };
      const nextMins = cursor.getHours() * 60 + cursor.getMinutes() + interval;
      const adv = new Date(cursor);
      if (nextMins > DAY_END) {
        // crossed midnight — roll to 07:00 the next day
        adv.setDate(adv.getDate() + 1);
        adv.setHours(Math.floor(DAY_START / 60), DAY_START % 60, 0, 0);
      } else if (nextMins < DAY_START) {
        // small-hours slot (only after a late-night first post) — pull up to 07:00 same day
        adv.setHours(Math.floor(DAY_START / 60), DAY_START % 60, 0, 0);
      } else {
        adv.setHours(Math.floor(nextMins / 60), nextMins % 60, 0, 0);
      }
      cursor = adv;
      return slot;
    });

    const ids = new Set(dated.map(p => p.id));
    const next = [...dated, ...queuePosts.filter(p => !ids.has(p.id))];
    saveCalendarQueue(next);
    // Jump calendar to first new date
    const first = new Date(`${dated[0].date}T12:00:00`);
    setVisibleMonth(new Date(first.getFullYear(), first.getMonth(), 1, 12));
    setSelectedDate(dated[0].date);
    const lastDate = dated[dated.length - 1].date;
    setAutoDateStatus(`${dated.length} posts scheduled — first at ${dated[0].time} today, ~${ppd}/day through ${lastDate}.`);
    window.BoltKitToast?.({ msg: `${dated.length} posts scheduled · first at ${dated[0].time}, ~${ppd}/day.`, type: "success" });
  };

  // Reschedule all missed posts starting from now, 10 min apart, then push to server
  const rescheduleAllMissed = async () => {
    const pad = n => String(n).padStart(2, "0");
    const dStr = d => `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())}`;
    const tStr = d => `${pad(d.getHours())}:${pad(d.getMinutes())}`;
    const isPosted = s => ["posted","published","sent"].includes(String(s||"").toLowerCase());
    const now = new Date();
    const missed = queuePosts.filter(p => {
      if (!p.date || isPosted(p.status)) return false;
      return new Date(`${p.date}T${p.time||"09:00"}:00Z`) <= now;
    });
    if (!missed.length) { window.BoltKitToast?.({ msg: "No missed posts to reschedule.", type: "success" }); return; }
    const rescheduled = missed.map((p, i) => {
      const slot = new Date(now.getTime() + (i + 1) * 10 * 60 * 1000);
      return { ...p, date: dStr(slot), time: tStr(slot), status: "Scheduled" };
    });
    const missedIds = new Set(missed.map(p => p.id));
    const next = queuePosts.map(p => { const r = rescheduled.find(r => r.id === p.id); return r || p; });
    saveCalendarQueue(next);
    window.BoltKitToast?.({ msg: `${missed.length} missed post${missed.length !== 1 ? "s" : ""} queued — firing every 10 min from now.`, type: "success" });
    try {
      const proj = (() => { try { return JSON.parse(localStorage.getItem("boltkit.currentProject.v1") || "{}"); } catch { return {}; } })();
      await fetch("/api/supabase-scheduled-posts", {
        method: "POST", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ action: "save", project: proj, posts: next }),
      });
    } catch {}
  };

  // Push all posts to Supabase so the server cron can fire them even with the laptop closed
  const startServerAutopilot = async () => {
    const scheduled = queuePosts.filter(p => p.date && String(p.date).match(/\d{4}-\d{2}-\d{2}/));
    if (!scheduled.length) {
      window.BoltKitToast?.({ msg: "No dated posts to push. Use Auto-date first.", type: "error" }); return;
    }
    setAutopilotState("pushing");
    window.BoltKitToast?.({ msg: `Pushing ${scheduled.length} posts to server…`, type: "loading" });
    try {
      let projectData = {};
      try { projectData = JSON.parse(localStorage.getItem("boltkit.currentProject.v1") || "{}"); } catch {}
      const controller = new AbortController();
      const timer = setTimeout(() => controller.abort(), 30000); // 30s hard timeout
      const resp = await fetch("/api/supabase-scheduled-posts", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ action: "save", project: projectData, posts: queuePosts }),
        signal: controller.signal,
      });
      clearTimeout(timer);
      const data = await resp.json();
      if (data.ok) {
        setAutopilotState("on");
        window.BoltKitToast?.({ msg: `${data.count || scheduled.length} posts saved to server. Autopilot on — close your laptop.`, type: "success" });
      } else {
        throw new Error(data.error || "Save failed");
      }
    } catch (err) {
      setAutopilotState("error");
      const msg = err.name === "AbortError" ? "Server push timed out — check your connection." : "Server push failed: " + err.message;
      window.BoltKitToast?.({ msg, type: "error" });
    }
  };

  const publishNow = async (post) => {
    // Look up the raw queue post to get the original image URL (mapping may use publicUrl/imageUrl)
    const raw = queuePosts.find(p => p.id === post.id) || post;
    const mediaUrl = raw.publicUrl || raw.imageUrl || raw.remoteUrl || post.publicUrl || post.imageUrl;
    if (!mediaUrl) {
      window.BoltKitToast?.({ msg: "No image URL — upload the image to Supabase first.", type: "error" }); return;
    }
    if (mediaUrl.startsWith("blob:") || mediaUrl.startsWith("data:")) {
      window.BoltKitToast?.({ msg: "Image is still uploading to Supabase. Wait a moment then try again.", type: "error" }); return;
    }
    setPublishingId(post.id);
    window.BoltKitToast?.({ msg: "Sending to Instagram…", type: "loading" });
    try {
      const rawHashtags = raw.hashtags || post.hashtags || [];
      const hashtagStr = Array.isArray(rawHashtags) ? rawHashtags.map(h => h.startsWith("#") ? h : `#${h}`).join(" ") : String(rawHashtags);
      const captionText = [raw.hook || post.hook, raw.caption || post.caption, hashtagStr].filter(Boolean).join("\n\n");
      const resp = await fetch("/api/instagram-publish-now", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ mediaUrl, caption: captionText, mediaType: "IMAGE" }),
      });
      const data = await resp.json();
      if (data.ok && !data.skipped) {
        saveCalendarQueue(queuePosts.map(p => p.id === post.id ? { ...p, status: "Posted", postedAt: new Date().toISOString() } : p));
        window.BoltKitToast?.({ msg: "Posted to Instagram!", type: "success" });
      } else if (data.skipped) {
        window.BoltKitToast?.({ msg: "Publishing is disabled — set ENABLE_INSTAGRAM_PUBLISHING=true in Vercel.", type: "error" });
      } else {
        window.BoltKitToast?.({ msg: data.error || "Publish failed.", type: "error" });
      }
    } catch (err) {
      window.BoltKitToast?.({ msg: "Publish failed: " + err.message, type: "error" });
    }
    setPublishingId(null);
  };
  const saveCalendarQueue = (next) => {
    // If adding posts back, clear the "recently cleared" flag so Supabase sync resumes normally
    if (next.length > 0) { try { localStorage.removeItem("boltkit.igCalClearedAt"); } catch {} }
    setQueuePosts(next);
    try { localStorage.setItem("boltkit.scheduledPosts.v1", JSON.stringify(next)); } catch {}
    window.dispatchEvent(new Event("boltkit:saved"));
    // Always persist to Supabase so refreshing/closing tab never loses scheduled posts
    try {
      const proj = (() => { try { return JSON.parse(localStorage.getItem("boltkit.currentProject.v1") || "{}"); } catch { return {}; } })();
      fetch("/api/supabase-scheduled-posts", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ action: "save", project: proj, posts: next }),
      }).catch(() => {});
    } catch {}
  };
  const monthStart = new Date(visibleMonth.getFullYear(), visibleMonth.getMonth(), 1, 12);
  const monthLabel = monthStart.toLocaleDateString("en-GB", { month: "long", year: "numeric" });
  const daysInMonth = new Date(visibleMonth.getFullYear(), visibleMonth.getMonth() + 1, 0).getDate();
  const gridOffset = (monthStart.getDay() + 6) % 7;
  const posts = queuePosts.length ? queuePosts.map((post, index) => ({
    id: post.id || `scheduled-${index}`,
    label: post.title || post.mediaName || post.caption || `Scheduled post ${index + 1}`,
    type: post.storyEnabled ? "Feed + Story" : post.type || "Image",
    ready: String(post.status || "").toLowerCase() === "scheduled",
    date: post.date || "",
    time: post.time || "09:00",
    caption: post.caption || "",
    platforms: post.platforms || ["Instagram", ...(post.storyEnabled ? ["Facebook"] : [])],
    storyLink: post.storyLink || "",
    scheduleStarted: post.scheduleStarted,
    // Preserve image URLs so publishNow can use them
    imageUrl: post.publicUrl || post.imageUrl || post.remoteUrl || post.previewUrl || "",
    publicUrl: post.publicUrl || post.imageUrl || post.remoteUrl || "",
  })) : [];
  const platformsAll = [
    { name: "Instagram", icon: "insta", connected: false, note: "Feed, Reels, Stories" },
    { name: "Facebook", icon: "facebook", connected: false, note: "Page post + Story" },
    { name: "TikTok", icon: "tiktok", connected: false, note: "Short vertical video" },
    { name: "YouTube", icon: "youtube", connected: false, note: "Shorts" },
    { name: "LinkedIn", icon: "linkedin", connected: false, note: "Founder post" },
  ];
  const now = new Date();
  // Append Z — times are stored as UTC; without Z, Date() treats it as local time.
  const scheduledAt = (post = {}) => post.date ? new Date(`${post.date}T${post.time || "09:00"}:00Z`) : null;
  const isPastScheduled = (post = {}) => {
    const date = scheduledAt(post);
    const status = String(post.status || "").toLowerCase();
    return Boolean(date && date <= now && !["posted", "published", "sent"].includes(status));
  };
  const platformClass = (platform = "") => `platform-${String(platform).toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
  const postStage = (post = {}) => {
    if (isPastScheduled(post)) return "Missed time";
    if (post.scheduleStarted) return "Confirmed";
    if (post.date) return post.ready ? "Ready to post" : "Scheduled";
    return post.ready ? "Ready" : "Draft";
  };
  const postStageClass = (post = {}) => `stage-${postStage(post).toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
  const eventsByDate = posts.reduce((map, post, index) => {
    if (!post.date || !String(post.date).match(/\d{4}-\d{2}-\d{2}/)) return map;
    const key = post.date;
    const platformList = post.platforms?.length ? post.platforms : ["Instagram"];
    map[key] = [...(map[key] || []), ...platformList.map(platform => ({ label: `${platform.slice(0, 2).toUpperCase()} ${index + 1}`, platform, post, status: postStage(post) }))];
    return map;
  }, {});
  const selected = posts.find(post => post.id === selectedPost) || posts[0];
  React.useEffect(() => {
    if (!posts.length) return;
    if (!posts.some(post => post.id === selectedPost)) setSelectedPost(posts[0].id);
  }, [posts.length, selectedPost]);
  React.useEffect(() => {
    if (!selected) return;
    setTime(selected.time || "09:00");
    setPlatforms(selected.platforms?.length ? selected.platforms : ["Instagram", ...(selected.type === "Feed + Story" ? ["Facebook"] : [])]);
  }, [selected?.id]);
  const togglePlatform = (name) => {
    setPlatforms((current) => current.includes(name) ? current.filter(item => item !== name) : [...current, name]);
  };
  const scheduleSelectedPost = () => {
    if (!queuePosts.length || !selected?.id) {
      window.BoltKitToast?.("Import posts first, then schedule them here.");
      return;
    }
    const selectedTime = /^\d{2}:\d{2}$/.test(String(time || "")) ? time : "09:00";
    const scheduledAt = new Date(`${selectedDate}T${selectedTime}:00`);
    if (scheduledAt <= new Date()) {
      window.BoltKitToast?.("Choose a future date and time.");
      return;
    }
    const next = queuePosts.map((post, index) => {
      const postId = post.id || `scheduled-${index}`;
      if (postId !== selected.id) return post;
      return {
        ...post,
        date: selectedDate,
        time: selectedTime,
        status: "Scheduled",
        platforms: platforms.length ? platforms : ["Instagram"],
        storyStatus: post.storyEnabled ? "Scheduled" : post.storyStatus,
        scheduledBy: "Calendar day planner"
      };
    });
    saveCalendarQueue(next);
    window.BoltKitToast?.("Post scheduled on the calendar.");
  };
  const dateKey = (date) => {
    const safe = date instanceof Date && !Number.isNaN(date.getTime()) ? date : new Date();
    return `${safe.getFullYear()}-${String(safe.getMonth() + 1).padStart(2, "0")}-${String(safe.getDate()).padStart(2, "0")}`;
  };
  const moveMonth = (delta) => {
    const next = new Date(visibleMonth.getFullYear(), visibleMonth.getMonth() + delta, 1, 12);
    setVisibleMonth(next);
    setSelectedDate(dateKey(next));
  };
  const moveDay = (delta) => {
    const next = new Date(`${selectedDate}T12:00:00`);
    next.setDate(next.getDate() + delta);
    setSelectedDate(dateKey(next));
    setVisibleMonth(new Date(next.getFullYear(), next.getMonth(), 1, 12));
  };
  const jumpToday = () => {
    const today = new Date();
    const next = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 12);
    setSelectedDate(dateKey(next));
    setVisibleMonth(new Date(next.getFullYear(), next.getMonth(), 1, 12));
  };
  const jumpNextScheduled = () => {
    const future = posts
      .filter(post => post.date)
      .map(post => ({ post, at: scheduledAt(post) }))
      .filter(item => item.at && item.at >= new Date())
      .sort((a, b) => a.at - b.at)[0];
    if (!future) {
      window.BoltKitToast?.("No future scheduled posts yet.");
      return;
    }
    const next = dateKey(future.at);
    setSelectedDate(next);
    setSelectedPost(future.post.id);
    setVisibleMonth(new Date(future.at.getFullYear(), future.at.getMonth(), 1, 12));
  };
  // ── Calendar drag-and-drop ───────────────────────────────────────────────
  const [dragPostId,   setDragPostId]   = React.useState(null);
  const [dragOverDate, setDragOverDate] = React.useState(null);

  // ── Calendar block popup (click event chip → edit/delete/move) ──────────
  const [calPopup, setCalPopup] = React.useState(null); // { post, origId, x, y }
  const openCalPopup = (e, post) => {
    e.stopPropagation();
    const rect = e.currentTarget.getBoundingClientRect();
    const x = Math.min(rect.left, window.innerWidth - 310);
    const y = Math.min(rect.bottom + 6, window.innerHeight - 340);
    setCalPopup({ post: { ...post }, origId: post.id, x, y });
  };
  const updateCalPopupField = (field, value) => {
    setCalPopup(prev => prev ? { ...prev, post: { ...prev.post, [field]: value } } : prev);
  };
  const saveCalPopup = () => {
    if (!calPopup) return;
    const next = queuePosts.map(p => p.id === calPopup.origId ? { ...p, ...calPopup.post } : p);
    saveCalendarQueue(next);
    if (calPopup.post.date) {
      const d = new Date(`${calPopup.post.date}T12:00:00`);
      setVisibleMonth(new Date(d.getFullYear(), d.getMonth(), 1, 12));
      setSelectedDate(calPopup.post.date);
    }
    setCalPopup(null);
    window.BoltKitToast?.({ msg: "Post updated.", type: "success" });
  };
  const deleteCalPopup = () => {
    if (!calPopup) return;
    const next = queuePosts.filter(p => p.id !== calPopup.origId);
    saveCalendarQueue(next);
    setCalPopup(null);
    window.BoltKitToast?.({ msg: "Post removed.", type: "success" });
  };

  const movePostToDate = (postId, newDate) => {
    const next = queuePosts.map(p => {
      if (p.id !== postId) return p;
      const wasUnscheduled = !p.date;
      return { ...p, date: newDate, status: wasUnscheduled ? "Scheduled" : (p.status || "Scheduled") };
    });
    saveCalendarQueue(next);
    setSelectedDate(newDate);
    window.BoltKitToast?.({ msg: `Moved to ${newDate}.`, type: "success" });
  };

  // Nuclear clear — delete EVERYTHING the user wants a fresh start
  const clearCalendarSchedule = () => {
    const kept = []; // wipe all — status "Posted" here means missed/failed, not actually published
    try { localStorage.setItem("boltkit.igCalClearedAt", String(Date.now())); } catch {}
    try { localStorage.setItem("boltkit.scheduledPosts.v1", JSON.stringify(kept)); } catch {}
    setQueuePosts(kept);
    setAutopilotState("idle");
    setConfirmClearAll(false);
    window.BoltKitToast?.({ msg: "Clearing… please wait.", type: "success" });
    // Await Supabase confirmation (max 10s), THEN reload — so DB is empty when page re-mounts
    const project = (() => { try { return JSON.parse(localStorage.getItem("boltkit.currentProject.v1") || "{}"); } catch { return {}; } })();
    const supaDelete = typeof apiJson === "function"
      ? apiJson("/api/supabase-scheduled-posts", { action: "save", project, posts: kept })
          .then(() => "ok")
          .catch(err => { window.BoltKitToast?.({ msg: "DB clear failed — will reload anyway.", type: "error" }); return "error"; })
      : Promise.resolve("skip");
    Promise.race([supaDelete, new Promise(r => setTimeout(() => r("timeout"), 10000))]).then(result => {
      window.BoltKitToast?.({ msg: result === "ok" ? "Cleared — reloading." : "Reloading (DB may still clear).", type: "success" });
      setTimeout(() => window.location.reload(), 600);
    });
  };

  // Add a draft post from the tray to the calendar at a given date
  const addDraftToCalendar = (draft, date) => {
    if (queuePosts.some(p => p.id === draft.id)) { movePostToDate(draft.id, date); return; }
    const ppd = Math.max(1, igPerDay);
    const slotCount = (eventsByDate[date] || []).length;
    const genTimes = (n) => {
      if (n <= 1) return ["12:00"];
      const S = 7*60, E = 23*60 + 59; // 07:00 → 23:59 — match the auto-date window
      return Array.from({length: n}, (_, i) => { const m = Math.round(S + (E-S)*i/(n-1)); return `${String(Math.floor(m/60)).padStart(2,"0")}:${String(m%60).padStart(2,"0")}`; });
    };
    const TIMES = genTimes(ppd);
    const slotTime = TIMES[slotCount % TIMES.length];
    const next = [...queuePosts, { ...draft, date, time: slotTime, status: "Scheduled" }];
    saveCalendarQueue(next);
    setSelectedDate(date);
    window.BoltKitToast?.({ msg: `Added to ${date} at ${slotTime}.`, type: "success" });
  };

  const showFilterHint = () => {
    const active = platforms.length ? platforms.join(", ") : "no platforms";
    window.BoltKitToast?.(`Calendar filtered to ${active}. Use platform cards to change it.`);
  };
  const addToCalendarFromHero = () => {
    if (queuePosts.length) {
      scheduleSelectedPost();
      return;
    }
    window.BoltKitGo?.("instagram-scheduler", "Import or create posts first, then send them to the calendar.");
  };
  const selectedDateEvents = (eventsByDate[selectedDate] || []).sort((a, b) => String(a.post.time || "").localeCompare(String(b.post.time || "")));
  const missedCount = posts.filter(isPastScheduled).length;
  const hasStartedSchedule = posts.some(post => post.scheduleStarted && !isPastScheduled(post));
  const scheduledCount = posts.filter(post => post.date && String(post.date).match(/\d{4}-\d{2}-\d{2}/)).length;
  const confirmedCount = posts.filter(post => post.scheduleStarted && !isPastScheduled(post)).length;
  const readyCount = posts.filter(post => (post.ready || post.scheduleStarted) && !isPastScheduled(post)).length;
  const draftCount = Math.max(0, posts.length - scheduledCount);
  const cardState = missedCount ? "missed" : hasStartedSchedule ? "confirmed" : scheduledCount ? "ready" : "draft";
  return (
    <div className="ig-redesign-shell">
      <SidebarNav active="calendar" />
      <main className="ig-redesign-main">
        <TopBar project="Renoo · Notes" />
        <div className="ig-redesign-scroll">
          <section className="ig-hero-panel">
            <div>
              <div className="ig-eyebrow"><span />ALL-IN-ONE · SOCIAL CALENDAR</div>
              <h1>See exactly what is scheduled.</h1>
              <p>Drag posts onto any date. Drag missed posts to reschedule. Use the posts/day control to adjust your cadence.</p>
              <div className="ig-status-row">
                <SyncChip state="sync" />
                <Pill tone={missedCount ? "red" : hasStartedSchedule ? "green" : scheduledCount ? "yellow" : "amber"}>{missedCount ? "Needs reschedule" : hasStartedSchedule ? "Schedule confirmed" : scheduledCount ? "Ready to start" : "Not scheduled"}</Pill>
                <span>{confirmedCount} confirmed · {scheduledCount} scheduled · {missedCount} missed · {draftCount} draft</span>
              </div>
            </div>
            <div className="ig-hero-actions">
              <button data-bk-bound="true" className="btn btn-secondary" onClick={showFilterHint}><Icon name="filter" size={12} stroke={2} /> Filter</button>
              <button data-bk-bound="true" className="btn btn-primary" onClick={addToCalendarFromHero}><BoltMini size={13} color="#1A1813" /> Add to calendar</button>
            </div>
          </section>

          {/* Undated posts banner */}
          {(() => {
            const undatedCount = queuePosts.filter(p => !p.date || !String(p.date).match(/\d{4}-\d{2}-\d{2}/)).length;
            if (!undatedCount) return null;
            return (
              <div style={{ margin: "0 0 12px", padding: "14px 18px", background: "rgba(255,178,63,0.08)", border: "1px solid rgba(255,178,63,0.3)", display: "flex", alignItems: "center", gap: 14, flexWrap: "wrap" }}>
                <Icon name="alert" size={16} stroke={2.4} color="#FFB23F" />
                <div style={{ flex: 1, minWidth: 0 }}>
                  <b style={{ fontSize: 13, color: "#FFB23F", display: "block" }}>{undatedCount} post{undatedCount !== 1 ? "s" : ""} ready to schedule — set posts/day and auto-date them.</b>
                  <span style={{ fontSize: 11.5, color: "#8A8A8A" }}>{autoDateStatus || `${igPerDay}/day starting today — click to distribute across the calendar.`}</span>
                </div>
                <div style={{ display: "flex", alignItems: "center", gap: 4, flex: "none" }}>
                  <button data-bk-bound="true" onClick={() => setIgPerDay(n => Math.max(1, n - 1))} style={{ background: "none", border: "none", color: "#FFB23F", cursor: "pointer", fontSize: 15, padding: "0 3px", lineHeight: 1 }}>−</button>
                  <span style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 12, color: "#FFB23F", fontWeight: 700, minWidth: 22, textAlign: "center" }}>{igPerDay}</span>
                  <button data-bk-bound="true" onClick={() => setIgPerDay(n => Math.min(12, n + 1))} style={{ background: "none", border: "none", color: "#FFB23F", cursor: "pointer", fontSize: 15, padding: "0 3px", lineHeight: 1 }}>+</button>
                </div>
                <button data-bk-bound="true" className="btn btn-secondary btn-sm" style={{ borderColor: "#FFB23F", color: "#FFB23F", flex: "none" }} onClick={autoDateAllPosts}>
                  <BoltMini size={10} color="#FFB23F" /> Schedule {undatedCount} →
                </button>
              </div>
            );
          })()}

          {/* DRAFTS TRAY — unscheduled posts from generate screen, drag to calendar */}
          {(() => {
            const calIds = new Set(queuePosts.map(p => p.id));
            const unscheduled = draftPosts.filter(p => !calIds.has(p.id));
            if (!unscheduled.length) return null;
            return (
              <div style={{ margin: "0 0 8px", padding: "10px 14px", background: "rgba(239,255,0,0.05)", border: "1px solid rgba(239,255,0,0.2)", borderRadius: 6 }}>
                <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 8 }}>
                  <div style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 8.5, letterSpacing: "0.14em", color: "#EFFF00", fontWeight: 800 }}>READY TO SCHEDULE — {unscheduled.length} POSTS</div>
                  <span style={{ fontSize: 11, color: "#5C5C5C" }}>Drag onto a calendar date to schedule</span>
                </div>
                <div style={{ display: "flex", gap: 8, overflowX: "auto", paddingBottom: 4 }}>
                  {unscheduled.map(post => {
                    const img = post.publicUrl || post.imageUrl || post.previewUrl || post.remoteUrl;
                    const isDragging = dragDraftId === post.id;
                    return (
                      <div
                        key={post.id}
                        draggable
                        onDragStart={e => { e.dataTransfer.effectAllowed = "copy"; e.dataTransfer.setData("draftId", post.id); setDragDraftId(post.id); setDragOverDate(null); }}
                        onDragEnd={() => setDragDraftId(null)}
                        title={post.title || post.caption?.slice(0, 60) || "Draft post"}
                        style={{ flex: "none", width: 64, height: 64, borderRadius: 6, overflow: "hidden", border: `2px solid ${isDragging ? "#EFFF00" : "rgba(239,255,0,0.25)"}`, cursor: "grab", opacity: isDragging ? 0.4 : 1, transition: "opacity 0.15s, border-color 0.15s", position: "relative", background: "#26241E" }}
                      >
                        {img
                          ? <img src={img} alt="" style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }} />
                          : <div style={{ width: "100%", height: "100%", display: "flex", alignItems: "center", justifyContent: "center" }}><BoltMini size={14} color="#3A372F" /></div>}
                      </div>
                    );
                  })}
                </div>
              </div>
            );
          })()}

          {/* MASTER CONTROLS — autopilot */}
          <div style={{ display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap", padding: "10px 0 10px", marginBottom: 4, borderBottom: "1px solid #2C2A22" }}>
            {/* Autopilot status / start */}
            {autopilotState === "on" ? (
              <span style={{ display: "flex", alignItems: "center", gap: 5, fontFamily: "'JetBrains Mono', monospace", fontSize: 9.5, color: "#4ADE80", fontWeight: 700, letterSpacing: "0.1em" }}>
                <span style={{ width: 7, height: 7, borderRadius: "50%", background: "#4ADE80", display: "inline-block" }} />
                AUTOPILOT RUNNING
              </span>
            ) : scheduledCount > 0 ? (
              <button data-bk-bound="true" className="btn btn-sm" style={{ background: "#EFFF00", border: "none", color: "#1A1813", fontWeight: 700, whiteSpace: "nowrap" }} disabled={autopilotState === "pushing"} onClick={startServerAutopilot}>
                <BoltMini size={10} color="#1A1813" /> {autopilotState === "pushing" ? "Pushing…" : autopilotState === "error" ? "Retry" : `Push ${scheduledCount} to server`}
              </button>
            ) : null}
            {autopilotState === "error" && <span style={{ fontSize: 11, color: "#FF4D5E" }}>Push failed — retry.</span>}
            <div style={{ flex: 1 }} />
            {/* Stop */}
            {autopilotState === "on" && (
              <button data-bk-bound="true" className="btn btn-sm" style={{ background: "#26241E", border: "1px solid #3A372F", color: "#FFB23F", whiteSpace: "nowrap" }} onClick={() => setAutopilotState("idle")}>
                ⏸ Stop
              </button>
            )}
            {/* Resume */}
            {autopilotState !== "on" && scheduledCount > 0 && (
              <button data-bk-bound="true" className="btn btn-sm" style={{ background: "#26241E", border: "1px solid #3A372F", color: "#4ADE80", whiteSpace: "nowrap" }} onClick={startServerAutopilot} disabled={autopilotState === "pushing"}>
                ▶ Resume
              </button>
            )}
            {/* Clear — separated with a visible divider so it can't be accidentally tapped */}
            {scheduledCount > 0 && (
              <div style={{ display: "flex", alignItems: "center", gap: 6, marginLeft: 8, paddingLeft: 12, borderLeft: "1px solid #3A372F" }}>
                {!confirmClearAll ? (
                  <button data-bk-bound="true" className="btn btn-sm" style={{ background: "none", border: "1px dashed #3A372F", color: "#5C5C5C", whiteSpace: "nowrap" }} onClick={() => setConfirmClearAll(true)}>
                    ✕ Clear all
                  </button>
                ) : (
                  <div style={{ display: "flex", gap: 4, alignItems: "center" }}>
                    <span style={{ fontSize: 11, color: "#FF4D5E", fontWeight: 600 }}>Delete all {queuePosts.length} posts?</span>
                    <button data-bk-bound="true" className="btn btn-sm" style={{ background: "#FF4D5E", border: "none", color: "#FFF", whiteSpace: "nowrap" }} onClick={clearCalendarSchedule}>Yes, delete</button>
                    <button data-bk-bound="true" className="btn btn-sm" style={{ background: "#26241E", border: "1px solid #3A372F", color: "#A8A8A5" }} onClick={() => setConfirmClearAll(false)}>Cancel</button>
                  </div>
                )}
              </div>
            )}
          </div>

          <section className={"schedule-confirmation-card " + cardState}>
            <div className="schedule-confirmation-icon">
              <Icon name={missedCount ? "alert" : hasStartedSchedule ? "check" : "calendar"} size={18} stroke={2.5} />
            </div>
            <div>
              <b>{missedCount ? "Some posts have missed their scheduled time." : hasStartedSchedule ? "Schedule confirmed. Ready to post." : scheduledCount ? "Posts are scheduled. Press Start Schedule when ready." : "Nothing scheduled yet."}</b>
              <span>
                {missedCount
                  ? `${missedCount} post${missedCount === 1 ? " was" : "s were"} scheduled in the past. Reschedule them to fire every 10 minutes from now.`
                  : hasStartedSchedule
                  ? `${confirmedCount} post${confirmedCount === 1 ? "" : "s"} are confirmed in your calendar for manual posting/export. Auto-posting is disabled for the first MVP.`
                  : scheduledCount
                    ? `${scheduledCount} post${scheduledCount === 1 ? "" : "s"} have a date and time. Review the day planner, then start the schedule.`
                    : "Use the Instagram scheduler to bulk schedule your imported posts, then they will appear here."}
              </span>
            </div>
            <div className="schedule-confirmation-stats">
              <span><b>{readyCount}</b> ready</span>
              <span><b>{scheduledCount}</b> scheduled</span>
              <span><b>{confirmedCount}</b> confirmed</span>
              <span><b>{missedCount}</b> missed</span>
            </div>
            {missedCount > 0 && (
              <button
                data-bk-bound="true"
                className="btn btn-sm"
                style={{ background: "#FF4D5E", border: "none", color: "#FFF", fontWeight: 700, whiteSpace: "nowrap", flex: "none" }}
                onClick={rescheduleAllMissed}
              >
                ↻ Fire {missedCount}
              </button>
            )}
          </section>

          <section className="social-calendar-layout">
            <div className="social-calendar-card">
              <div className="ig-panel-head">
                <div>
                  <div className="ig-eyebrow small"><span />{monthLabel}</div>
                  <h2>{calendarMode} calendar</h2>
                </div>
                <div className="ig-chip-row compact calendar-nav">
                  <button className="ig-pill-button square" onClick={() => moveMonth(-1)} aria-label="Previous month"><Icon name="chevron_left" size={13} stroke={2.5} /></button>
                  {["Month", "Week", "Day"].map((label) => <button key={label} className={"ig-pill-button" + (calendarMode === label ? " active" : "")} onClick={() => setCalendarMode(label)}>{label}</button>)}
                  <button className="ig-pill-button square" onClick={() => moveMonth(1)} aria-label="Next month"><Icon name="chevron_right" size={13} stroke={2.5} /></button>
                  <button className="ig-pill-button" onClick={jumpToday}>Today</button>
                  <button className="ig-pill-button" onClick={jumpNextScheduled}>Next post</button>
                </div>
              </div>
              <div className="social-week-head">
                {["MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"].map(day => <span key={day}>{day}</span>)}
              </div>
              <div className="social-calendar-grid">
                {Array.from({ length: 42 }).map((_, index) => {
                  const day = index - gridOffset + 1;
                  const dim = day < 1 || day > daysInMonth;
                  const displayDate = new Date(visibleMonth.getFullYear(), visibleMonth.getMonth(), Math.max(1, Math.min(daysInMonth, day)), 12);
                  const cellDateKey = dateKey(displayDate);
                  const display = dim ? "" : day;
                  const active = cellDateKey === selectedDate && !dim;
                  const isDropTarget = !dim && (dragPostId || dragDraftId) && dragOverDate === cellDateKey;
                  const events = dim ? [] : (eventsByDate[cellDateKey] || []);
                  return (
                    <button
                      key={index}
                      className={(dim ? "dim " : "") + (active ? "active " : "")}
                      onClick={() => !dim && setSelectedDate(cellDateKey)}
                      onDragOver={e => { if (dim || (!dragPostId && !dragDraftId)) return; e.preventDefault(); e.dataTransfer.dropEffect = "move"; setDragOverDate(cellDateKey); }}
                      onDragLeave={e => { if (e.currentTarget.contains(e.relatedTarget)) return; setDragOverDate(null); }}
                      onDrop={e => { e.preventDefault(); const dId = e.dataTransfer.getData("draftId"); if (dId && !dim) { const d = draftPosts.find(p => p.id === dId); if (d) addDraftToCalendar(d, cellDateKey); } else if (dragPostId && !dim) { movePostToDate(dragPostId, cellDateKey); } setDragPostId(null); setDragDraftId(null); setDragOverDate(null); }}
                      style={isDropTarget ? { outline: "2px solid #EFFF00", outlineOffset: "-2px", background: "rgba(239,255,0,0.08)" } : undefined}
                    >
                      <b>{display}</b>
                      {events.slice(0, 4).map((event, eventIndex) => (
                        <span
                          key={`${event.label}-${eventIndex}`}
                          className={`${platformClass(event.platform)} ${postStageClass(event.post)}`}
                          draggable
                          onDragStart={e => { e.stopPropagation(); e.dataTransfer.effectAllowed = "move"; setDragPostId(event.post.id); setDragOverDate(null); }}
                          onDragEnd={() => { setDragPostId(null); setDragOverDate(null); }}
                          onClick={e => { if (!dragPostId) openCalPopup(e, event.post); }}
                          style={{ cursor: "pointer", opacity: dragPostId === event.post.id ? 0.35 : 1, transition: "opacity 0.15s" }}
                        >
                          {event.label} · {event.status}
                        </span>
                      ))}
                      {events.length > 4 && <span className="platform-more">+ {events.length - 4} more</span>}
                    </button>
                  );
                })}
              </div>
            </div>

            <aside className="day-planner-panel">
              <div className="ig-eyebrow small"><span />DAY PLANNER</div>
              <div className="calendar-day-nav">
                <button className="ig-pill-button square" onClick={() => moveDay(-1)} aria-label="Previous day"><Icon name="chevron_left" size={13} stroke={2.5} /></button>
                <h2>{new Date(`${selectedDate}T12:00:00`).toLocaleDateString("en-GB", { weekday: "short", day: "numeric", month: "short" })}</h2>
                <button className="ig-pill-button square" onClick={() => moveDay(1)} aria-label="Next day"><Icon name="chevron_right" size={13} stroke={2.5} /></button>
              </div>
              <div className="calendar-day-events">
                {selectedDateEvents.length ? selectedDateEvents.map((event, index) => (
                  <button key={`${event.label}-${index}`} className={`${platformClass(event.platform)} ${postStageClass(event.post)}`} onClick={() => setSelectedPost(event.post.id)}>
                    <b>{event.platform}</b>
                    <span>{event.post.label}</span>
                    <small>{event.post.time || "09:00"} · {postStage(event.post)}</small>
                  </button>
                )) : <p>No posts scheduled for this day yet.</p>}
              </div>
              {/* ── Post picker — visual thumbnails ── */}
              <div>
                <div style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 8, letterSpacing: "0.14em", color: "#5C5C5C", marginBottom: 6 }}>SELECT POST</div>
                <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 5, maxHeight: 180, overflowY: "auto" }}>
                  {posts.map(post => {
                    const isSel = post.id === selectedPost;
                    const img = post.imageUrl || post.publicUrl;
                    const isSchd = Boolean(post.date);
                    return (
                      <button key={post.id} data-bk-bound="true" onClick={() => setSelectedPost(post.id)}
                        style={{
                          position: "relative", aspectRatio: "4/5", borderRadius: 5, overflow: "hidden",
                          border: `2px solid ${isSel ? "#EFFF00" : isSchd ? "#4ADE80" : "#3A372F"}`,
                          background: "#1A1813", cursor: "pointer", padding: 0,
                        }}>
                        {img
                          ? <img src={img} alt="" style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }} />
                          : <div style={{ width: "100%", height: "100%", display: "flex", alignItems: "center", justifyContent: "center" }}><BoltMini size={12} color="#3A372F" /></div>}
                        {isSchd && (
                          <div style={{ position: "absolute", bottom: 0, left: 0, right: 0, background: "rgba(74,222,128,0.85)", fontFamily: "'JetBrains Mono', monospace", fontSize: 7, color: "#0a1a0a", fontWeight: 800, padding: "2px 3px", textAlign: "center" }}>
                            {post.date?.slice(5)}
                          </div>
                        )}
                        {isSel && (
                          <div style={{ position: "absolute", top: 3, right: 3, width: 12, height: 12, borderRadius: "50%", background: "#EFFF00", display: "flex", alignItems: "center", justifyContent: "center" }}>
                            <Icon name="check" size={7} stroke={3} color="#1A1813" />
                          </div>
                        )}
                      </button>
                    );
                  })}
                </div>
              </div>

              <div className="selected-post-preview">
                <div><BoltMini size={18} color="#1A1813" /></div>
                <section>
                  <b>{selected?.label || "No post selected"}</b>
                  <span>{selected?.type || "—"} · {selected ? postStage(selected) : "—"}</span>
                </section>
              </div>
              <label>
                <span>Time</span>
                <input type="time" value={time} onChange={(event) => setTime(event.target.value)} />
              </label>
              <label>
                <span>Caption note</span>
                <textarea defaultValue="Use founder voice, punchy hook, strong link-in-bio CTA. Reuse as Story where available." />
              </label>
              <button className="btn btn-primary" style={{ width: "100%", justifyContent: "center" }} onClick={scheduleSelectedPost}>
                <Icon name="calendar" size={12} stroke={2} /> Schedule post
              </button>

              {/* ── Instagram Publish ── */}
              <div style={{ marginTop: 12, paddingTop: 12, borderTop: "1px solid #2A2820" }}>
                {igAccount ? (
                  <div style={{ marginBottom: 8, display: "flex", alignItems: "center", gap: 6 }}>
                    <span style={{ width: 7, height: 7, borderRadius: "50%", background: "#4ADE80", display: "inline-block", flexShrink: 0 }} />
                    <span style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 9, color: "#4ADE80", letterSpacing: "0.1em", fontWeight: 700 }}>@{igAccount.username} · CONNECTED</span>
                  </div>
                ) : (
                  <div style={{ marginBottom: 8, display: "flex", alignItems: "center", gap: 6 }}>
                    <span style={{ width: 7, height: 7, borderRadius: "50%", background: "#FF4D5E", display: "inline-block", flexShrink: 0 }} />
                    <button data-bk-bound="true" onClick={() => window.BoltKitConnectInstagram?.()} style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 9, color: "#EFFF00", letterSpacing: "0.1em", fontWeight: 700, background: "none", border: "none", cursor: "pointer", padding: 0 }}>
                      CONNECT INSTAGRAM →
                    </button>
                  </div>
                )}
                <button
                  data-bk-bound="true"
                  className="btn btn-secondary"
                  style={{ width: "100%", justifyContent: "center", borderColor: igAccount ? "#4ADE80" : "#3A372F", color: igAccount ? "#4ADE80" : "#5C5C5C" }}
                  onClick={() => selected && publishNow(selected)}
                  disabled={!igAccount || publishingId === selected?.id}
                >
                  {publishingId === selected?.id
                    ? <><span className="bolt-thinking" style={{ display: "inline-block", lineHeight: 0 }}><BoltMini size={11} color="#EFFF00" /></span> Publishing…</>
                    : <><Icon name="insta" size={12} stroke={2} /> Publish now to Instagram</>}
                </button>
                {!igAccount && (
                  <p style={{ margin: "6px 0 0", fontSize: 10, color: "#5C5C5C", lineHeight: 1.5 }}>Connect Instagram above, then set <code style={{ color: "#EFFF00" }}>ENABLE_INSTAGRAM_PUBLISHING=true</code> in Vercel to activate publishing.</p>
                )}
              </div>
            </aside>
          </section>
        </div>
      </main>

      {/* Calendar block popup — Edit / Delete / Move */}
      {calPopup && (
        <>
          <div onClick={() => setCalPopup(null)} style={{ position: "fixed", inset: 0, zIndex: 9990 }} />
          <div style={{
            position: "fixed", left: calPopup.x, top: calPopup.y, width: 296, zIndex: 9999,
            background: "#1E1C16", border: "1px solid #3A372F", borderRadius: 10,
            boxShadow: "0 10px 40px rgba(0,0,0,0.6)", overflow: "hidden",
          }}>
            <div style={{ display: "flex", alignItems: "center", gap: 10, padding: "12px 14px 10px", borderBottom: "1px solid #2C2A22" }}>
              {(calPopup.post.imageUrl || calPopup.post.publicUrl) && (
                <img src={calPopup.post.imageUrl || calPopup.post.publicUrl} alt="" style={{ width: 40, height: 40, objectFit: "cover", borderRadius: 5, flex: "none" }} />
              )}
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 9, letterSpacing: "0.12em", color: "#EFFF00", fontWeight: 800, marginBottom: 2 }}>INSTAGRAM POST</div>
                <div style={{ fontSize: 11.5, color: "#CCC", fontWeight: 600, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{calPopup.post.label || "Untitled"}</div>
              </div>
              <button onClick={() => setCalPopup(null)} style={{ background: "none", border: "none", color: "#5C5C5C", cursor: "pointer", fontSize: 16, lineHeight: 1, padding: 2 }}>✕</button>
            </div>
            <div style={{ padding: "12px 14px", display: "flex", flexDirection: "column", gap: 10 }}>
              <label style={{ display: "flex", flexDirection: "column", gap: 4 }}>
                <span style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 8.5, letterSpacing: "0.12em", color: "#5C5C5C", fontWeight: 700 }}>CAPTION</span>
                <textarea
                  value={calPopup.post.caption || ""}
                  onChange={e => updateCalPopupField("caption", e.target.value)}
                  rows={3}
                  style={{ background: "#141210", border: "1px solid #3A372F", borderRadius: 5, color: "#E0E0E0", fontSize: 11.5, padding: "7px 9px", resize: "vertical", fontFamily: "inherit", lineHeight: 1.5 }}
                />
              </label>
              <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8 }}>
                <label style={{ display: "flex", flexDirection: "column", gap: 4 }}>
                  <span style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 8.5, letterSpacing: "0.12em", color: "#5C5C5C", fontWeight: 700 }}>DATE (MOVE)</span>
                  <input type="date" value={calPopup.post.date || ""} onChange={e => updateCalPopupField("date", e.target.value)}
                    style={{ background: "#141210", border: "1px solid #3A372F", borderRadius: 5, color: "#E0E0E0", fontSize: 11, padding: "6px 8px", fontFamily: "'JetBrains Mono', monospace" }} />
                </label>
                <label style={{ display: "flex", flexDirection: "column", gap: 4 }}>
                  <span style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 8.5, letterSpacing: "0.12em", color: "#5C5C5C", fontWeight: 700 }}>TIME</span>
                  <input type="time" value={calPopup.post.time || "09:00"} onChange={e => updateCalPopupField("time", e.target.value)}
                    style={{ background: "#141210", border: "1px solid #3A372F", borderRadius: 5, color: "#E0E0E0", fontSize: 11, padding: "6px 8px", fontFamily: "'JetBrains Mono', monospace" }} />
                </label>
              </div>
            </div>
            <div style={{ padding: "0 14px 14px", display: "flex", gap: 8 }}>
              <button onClick={deleteCalPopup} style={{ flex: "none", background: "rgba(255,77,94,0.12)", border: "1px solid rgba(255,77,94,0.3)", color: "#FF4D5E", borderRadius: 6, padding: "7px 12px", fontSize: 11.5, cursor: "pointer", fontWeight: 600 }}>
                Delete
              </button>
              <button onClick={saveCalPopup} style={{ flex: 1, background: "#EFFF00", border: "none", color: "#1A1813", borderRadius: 6, padding: "7px 12px", fontSize: 11.5, cursor: "pointer", fontWeight: 700 }}>
                <BoltMini size={11} color="#1A1813" /> Save changes
              </button>
            </div>
          </div>
        </>
      )}
    </div>
  );
};

/* ================================================================
   LIVE PUBLISHING DASHBOARD
   ================================================================ */
let PublishDashboard = () => {
  // Live Feed uses Supabase as primary source — no localStorage dependency.
  // localStorage is only used as a warm-start cache to avoid a blank flash on mount.
  const loadQueue = () => { try { return JSON.parse(localStorage.getItem("boltkit.scheduledPosts.v1") || "[]"); } catch { return []; } };
  const [queuePosts, setQueuePosts] = React.useState(loadQueue);
  const [loadedFromServer, setLoadedFromServer] = React.useState(false);
  const [publishing, setPublishing] = React.useState(false);
  const [currentlyPosting, setCurrentlyPosting] = React.useState(null);
  const [publishErrors, setPublishErrors] = React.useState([]);
  const [engagement, setEngagement] = React.useState({});
  const [igAccount, setIgAccount] = React.useState(null);
  const [now, setNow] = React.useState(new Date());
  const [autoPostEnabled, setAutoPostEnabled] = React.useState(true);
  const [engPolled,       setEngPolled]       = React.useState(false);
  const [showBulk,        setShowBulk]        = React.useState(false);
  const [confirmClear,    setConfirmClear]    = React.useState(false);
  const [postStory,       setPostStory]       = React.useState(() => { try { return JSON.parse(localStorage.getItem("boltkit.igPostStory") || "false"); } catch { return false; } });
  const publishingRef = React.useRef(false);

  // Second-by-second clock for countdown
  React.useEffect(() => {
    const t = setInterval(() => setNow(new Date()), 1000);
    return () => clearInterval(t);
  }, []);

  // Primary data source: Supabase. Load on mount then poll every 30s.
  // localStorage is only used as a warm-start cache and for mutations (publish/remove).
  React.useEffect(() => {
    const getProj = () => { try { return JSON.parse(localStorage.getItem("boltkit.currentProject.v1") || "{}"); } catch { return {}; } };
    const fetchFromServer = async () => {
      try {
        const resp = await fetch("/api/supabase-scheduled-posts", {
          method: "POST", headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ action: "load", project: getProj() }),
        });
        const data = await resp.json();
        if (!data.ok || !Array.isArray(data.posts)) return;
        // Always use the server's post list as truth — overwrite state and cache
        setQueuePosts(data.posts);
        try { localStorage.setItem("boltkit.scheduledPosts.v1", JSON.stringify(data.posts)); } catch {}
        setLoadedFromServer(true);
      } catch {}
    };
    fetchFromServer();
    const t = setInterval(fetchFromServer, 30000);
    return () => clearInterval(t);
  }, []);

  // IG account status
  React.useEffect(() => {
    const pid = (() => { try { return JSON.parse(localStorage.getItem("boltkit.currentProject.v1") || "{}").id || null; } catch { return null; } })();
    const url = pid ? `/api/meta/status?projectId=${encodeURIComponent(pid)}` : "/api/meta/status";
    fetch(url).then(r => r.json()).then(d => {
      if (d.accounts?.[0]) setIgAccount(d.accounts[0]);
    }).catch(() => {});
  }, []);

  const saveQueue = (next) => {
    setQueuePosts(next);
    try { localStorage.setItem("boltkit.scheduledPosts.v1", JSON.stringify(next)); } catch {}
    // Do NOT dispatch boltkit:saved — that event triggers other screens to re-read
    // localStorage, which caused the "flash then disappear" corruption loop.
  };

  const autoPublish = React.useCallback(async (post) => {
    if (publishingRef.current) return;
    publishingRef.current = true;
    setPublishing(true);
    setCurrentlyPosting(post);
    window.BoltKitToast?.({ msg: "Auto-posting to Instagram…", type: "loading" });
    // Supabase-loaded posts use previewUrl/remoteUrl; locally-created posts use publicUrl/imageUrl
    const mediaUrl = post.publicUrl || post.imageUrl || post.remoteUrl || post.previewUrl;
    try {
      const hashtags = Array.isArray(post.hashtags) ? post.hashtags.map(h => `#${h}`).join(" ") : (post.hashtags || "");
      const captionText = [post.hook, post.caption, hashtags].filter(Boolean).join("\n\n");
      const resp = await fetch("/api/instagram-publish-now", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ mediaUrl, caption: captionText, mediaType: "IMAGE" }),
      });
      const data = await resp.json();
      if (data.ok && !data.skipped) {
        const igMediaId = data.result?.media_id || data.mediaId || null;
        // PATCH Supabase immediately so the 30s poll doesn't revert back to "scheduled"
        const proj = (() => { try { return JSON.parse(localStorage.getItem("boltkit.currentProject.v1") || "{}"); } catch { return {}; } })();
        fetch("/api/supabase-scheduled-posts", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ action: "mark-published", project: proj, postClientId: post.id, igMediaId })
        }).catch(() => {});
        const queue = loadQueue();
        const next = queue.map(p => p.id === post.id ? { ...p, status: "Posted", postedAt: new Date().toISOString(), igMediaId } : p);
        saveQueue(next);
        window.BoltKitToast?.({ msg: "Posted to Instagram!", type: "success" });
      } else if (data.skipped) {
        setPublishErrors(prev => [...prev, { post, error: "Publishing disabled — set ENABLE_INSTAGRAM_PUBLISHING=true in Vercel", time: new Date() }]);
        window.BoltKitToast?.({ msg: "Publishing is disabled in Vercel.", type: "error" });
      } else {
        setPublishErrors(prev => [...prev, { post, error: data.error || "Publish failed", time: new Date() }]);
        window.BoltKitToast?.({ msg: data.error || "Publish failed.", type: "error" });
      }
    } catch (err) {
      setPublishErrors(prev => [...prev, { post, error: err.message || "Network error", time: new Date() }]);
      window.BoltKitToast?.({ msg: "Publish error: " + err.message, type: "error" });
    }
    publishingRef.current = false;
    setPublishing(false);
    setCurrentlyPosting(null);
  }, []);

  // Auto-post engine — check every 30s
  React.useEffect(() => {
    if (!autoPostEnabled) return;
    const check = () => {
      if (publishingRef.current) return;
      const queue = loadQueue();
      const nowMs = Date.now();
      const due = queue.find(p => {
        if (!p.date) return false;
        // date/time are UTC (rowToPost ISO slice) — append Z so Date parses as UTC not local
        const at = new Date(`${p.date}T${p.time || "09:00"}:00Z`).getTime();
        const s = String(p.status || "").toLowerCase();
        // Supabase-loaded posts use previewUrl/remoteUrl; locally-created posts use publicUrl/imageUrl
        const url = p.publicUrl || p.imageUrl || p.remoteUrl || p.previewUrl;
        return at <= nowMs && !["posted", "published", "sent", "failed"].includes(s) && url && !url.startsWith("blob:") && !url.startsWith("data:");
      });
      if (due) autoPublish(due);
    };
    check();
    const t = setInterval(check, 30000);
    return () => clearInterval(t);
  }, [autoPostEnabled, autoPublish]);

  // (Status sync replaced by the primary Supabase poll above — no separate sync needed)

  // Fetch engagement every 5 min
  React.useEffect(() => {
    const poll = async () => {
      const queue = loadQueue();
      const posted = queue.filter(p => ["Posted", "published", "sent"].includes(p.status) && p.igMediaId);
      if (!posted.length) return;
      try {
        const resp = await fetch("/api/instagram-insights", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ mediaIds: posted.map(p => p.igMediaId) })
        });
        const data = await resp.json();
        if (data.ok && data.insights) {
          setEngagement(prev => ({ ...prev, ...data.insights }));
          setEngPolled(true);
        }
      } catch {}
    };
    poll();
    const t = setInterval(poll, 5 * 60 * 1000);
    return () => clearInterval(t);
  }, []);

  const retryPost = async (errItem) => {
    setPublishErrors(prev => prev.filter(e => e !== errItem));
    await autoPublish(errItem.post);
  };

  // ── Reschedule all unposted posts from the next available slot ────────────
  const rescheduleFromNow = async () => {
    const queue      = loadQueue();
    const isPostedS  = s => ["posted","published","sent"].includes(String(s||"").toLowerCase());
    const posted     = queue.filter(p => isPostedS(p.status));
    const unposted   = queue.filter(p => !isPostedS(p.status));
    if (!unposted.length) {
      window.BoltKitToast?.({ msg: "Nothing to reschedule — all posts are done.", type: "success" }); return;
    }
    const PER_DAY = 2;
    const TIMES   = ["09:00", "18:00"];
    const nowMs   = new Date();
    // Find the first future slot today; if none, start tomorrow at 09:00
    let startDayOffset = 0, startTimeIdx = 0, foundToday = false;
    for (let ti = 0; ti < TIMES.length; ti++) {
      const [h, m] = TIMES[ti].split(":").map(Number);
      const slot = new Date(nowMs); slot.setHours(h, m, 0, 0);
      if (slot > nowMs) { startTimeIdx = ti; foundToday = true; break; }
    }
    if (!foundToday) { startDayOffset = 1; startTimeIdx = 0; }
    const rescheduled = unposted.map((post, i) => {
      const absSlot  = startTimeIdx + i;
      const dayOff   = startDayOffset + Math.floor(absSlot / PER_DAY);
      const timeIdx  = absSlot % PER_DAY;
      const d = new Date(nowMs); d.setDate(d.getDate() + dayOff);
      return { ...post, date: d.toISOString().slice(0, 10), time: TIMES[timeIdx], status: "Scheduled" };
    });
    const merged = [...posted, ...rescheduled];
    saveQueue(merged);
    window.BoltKitToast?.({ msg: `Rescheduling ${rescheduled.length} posts from ${rescheduled[0]?.date}…`, type: "loading" });
    try {
      const proj = (() => { try { return JSON.parse(localStorage.getItem("boltkit.currentProject.v1") || "{}"); } catch { return {}; } })();
      const resp = await fetch("/api/supabase-scheduled-posts", {
        method: "POST", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ action: "save", project: proj, posts: merged }),
      });
      const data = await resp.json();
      if (data.ok) {
        window.BoltKitToast?.({ msg: `${rescheduled.length} posts rescheduled from ${rescheduled[0]?.date} — live on server ✓`, type: "success" });
      } else throw new Error(data.error || "Server error");
    } catch (e) {
      window.BoltKitToast?.({ msg: `Rescheduled locally — server sync failed: ${e.message}`, type: "error" });
    }
  };

  // Move an upcoming post up (-1) or down (+1) by swapping its time-slot with its neighbour
  const moveUpcoming = async (id, dir) => {
    const all = loadQueue();
    const isPst = s => ["Posted", "posted", "published", "sent"].includes(String(s || "").toLowerCase());
    const sorted = all
      .filter(p => p.date && !isPst(p.status) && new Date(`${p.date}T${p.time || "09:00"}:00Z`) > new Date())
      .sort((a, b) => new Date(`${a.date}T${a.time || "09:00"}:00Z`) - new Date(`${b.date}T${b.time || "09:00"}:00Z`));
    const idx = sorted.findIndex(p => p.id === id);
    const swapIdx = idx + dir;
    if (idx < 0 || swapIdx < 0 || swapIdx >= sorted.length) return;
    const aSlot = { date: sorted[idx].date,      time: sorted[idx].time      || "09:00" };
    const bSlot = { date: sorted[swapIdx].date,  time: sorted[swapIdx].time  || "09:00" };
    const next = all.map(p => {
      if (p.id === sorted[idx].id)     return { ...p, date: bSlot.date, time: bSlot.time };
      if (p.id === sorted[swapIdx].id) return { ...p, date: aSlot.date, time: aSlot.time };
      return p;
    });
    saveQueue(next);
    try {
      let proj = {};
      try { proj = JSON.parse(localStorage.getItem("boltkit.currentProject.v1") || "{}"); } catch {}
      await fetch("/api/supabase-scheduled-posts", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ action: "save", project: proj, posts: next }),
      });
    } catch {}
  };

  // Remove a single post from the queue
  const removePost = async (id) => {
    const next = loadQueue().filter(p => p.id !== id);
    saveQueue(next);
    try {
      const proj = (() => { try { return JSON.parse(localStorage.getItem("boltkit.currentProject.v1") || "{}"); } catch { return {}; } })();
      await fetch("/api/supabase-scheduled-posts", {
        method: "POST", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ action: "save", project: proj, posts: next }),
      });
    } catch {}
  };

  // Toggle "also post as story" — updates all queued posts + syncs to server
  const toggleStory = async () => {
    const next = !postStory;
    setPostStory(next);
    localStorage.setItem("boltkit.igPostStory", JSON.stringify(next));
    const all = loadQueue();
    const updated = all.map(p => isPostedSt(p.status) ? p : { ...p, also_story: next });
    saveQueue(updated);
    try {
      const proj = (() => { try { return JSON.parse(localStorage.getItem("boltkit.currentProject.v1") || "{}"); } catch { return {}; } })();
      await fetch("/api/supabase-scheduled-posts", {
        method: "POST", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ action: "save", project: proj, posts: updated }),
      });
    } catch {}
  };

  // Clear all unposted posts
  const clearUnposted = async () => {
    const all  = loadQueue();
    const next = all.filter(p => isPostedSt(p.status));
    saveQueue(next);
    try {
      const proj = (() => { try { return JSON.parse(localStorage.getItem("boltkit.currentProject.v1") || "{}"); } catch { return {}; } })();
      await fetch("/api/supabase-scheduled-posts", {
        method: "POST", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ action: "save", project: proj, posts: next }),
      });
    } catch {}
    setConfirmClear(false);
  };

  // Post all missed posts now — publishes directly via instagram-publish-now, no reschedule
  const postMissedNow = async () => {
    const missed = missedPosts;
    if (!missed.length) { window.BoltKitToast?.({ msg: "No missed posts found.", type: "info" }); return; }
    const proj = (() => { try { return JSON.parse(localStorage.getItem("boltkit.currentProject.v1") || "{}"); } catch { return {}; } })();
    let published = 0;
    let firstError = null;
    for (const post of missed) {
      const mediaUrl = post.publicUrl || post.imageUrl || post.remoteUrl || post.previewUrl;
      if (!mediaUrl) { firstError = firstError || `No media URL for post "${post.title}"`; continue; }
      try {
        window.BoltKitToast?.({ msg: `Publishing post ${published + 1} of ${missed.length}…`, type: "loading" });
        const hashtags = Array.isArray(post.hashtags) ? post.hashtags.join(" ") : (post.hashtags || "");
        const captionText = [post.caption, hashtags].filter(Boolean).join("\n\n");
        const resp = await fetch("/api/instagram-publish-now", {
          method: "POST", headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ mediaUrl, caption: captionText, mediaType: "IMAGE", project_client_id: proj.id }),
        });
        const data = await resp.json();
        if (data.ok && !data.skipped) {
          const igMediaId = data.result?.media_id || null;
          // Mark published in Supabase so 30s poll reflects it
          fetch("/api/supabase-scheduled-posts", {
            method: "POST", headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ action: "mark-published", project: proj, postClientId: post.id, igMediaId }),
          }).catch(() => {});
          // Update local state
          setQueuePosts(prev => prev.map(p => p.id === post.id ? { ...p, status: "Posted", postedAt: new Date().toISOString(), igMediaId } : p));
          try { const q = loadQueue(); localStorage.setItem("boltkit.scheduledPosts.v1", JSON.stringify(q.map(p => p.id === post.id ? { ...p, status: "Posted" } : p))); } catch {}
          published++;
        } else if (data.skipped) {
          firstError = firstError || "Publishing is disabled — check ENABLE_INSTAGRAM_PUBLISHING=true in Vercel";
          break;
        } else {
          firstError = firstError || (data.error || data.message || "Publish failed");
        }
      } catch (err) {
        firstError = firstError || (err.message || "Network error");
      }
      // 10-second gap between posts to avoid Instagram rate limits
      if (published < missed.length) await new Promise(r => setTimeout(r, 10000));
    }
    if (published > 0) window.BoltKitToast?.({ msg: `Posted ${published} to Instagram!`, type: "success" });
    if (firstError) window.BoltKitToast?.({ msg: firstError, type: "error" });
  };

  // Derived state
  // Case-insensitive: rowToPost capitalises first letter ("Published"), UI sets "Posted"
  const isPosted = s => ["posted", "published", "sent"].includes(String(s || "").toLowerCase());
  const isPostedSt = isPosted; // alias — same logic
  const postedPosts = queuePosts.filter(p => isPosted(p.status)).sort((a, b) => new Date(b.postedAt || 0) - new Date(a.postedAt || 0));
  const missedPosts = queuePosts.filter(p => {
    if (!p.date || isPosted(p.status)) return false;
    // date/time are UTC (rowToPost uses ISO slice) — append Z so Date parses as UTC not local
    return new Date(`${p.date}T${p.time || "09:00"}:00Z`) <= now;
  });
  const upcomingPosts = queuePosts.filter(p => {
    if (!p.date) return false;
    const at = new Date(`${p.date}T${p.time || "09:00"}:00Z`);
    return at > now && !isPosted(p.status);
  }).sort((a, b) => new Date(`${a.date}T${a.time || "09:00"}Z`) - new Date(`${b.date}T${b.time || "09:00"}Z`));

  const nextPost = upcomingPosts[0];
  const nextAt = nextPost ? new Date(`${nextPost.date}T${nextPost.time || "09:00"}:00Z`) : null;
  const secsUntilNext = nextAt ? Math.max(0, Math.floor((nextAt - now) / 1000)) : null;

  const fmtCountdown = (secs) => {
    if (secs === null) return "—";
    if (secs === 0) return "NOW";
    const d = Math.floor(secs / 86400);
    const h = Math.floor((secs % 86400) / 3600);
    const m = Math.floor((secs % 3600) / 60);
    const s = secs % 60;
    if (d > 0) return `${d}d ${h}h ${String(m).padStart(2,"0")}m`;
    if (h > 0) return `${h}h ${String(m).padStart(2,"0")}m ${String(s).padStart(2,"0")}s`;
    return `${String(m).padStart(2,"0")}m ${String(s).padStart(2,"0")}s`;
  };

  const totalLikes = postedPosts.reduce((sum, p) => sum + (engagement[p.igMediaId]?.likes || 0), 0);
  const totalComments = postedPosts.reduce((sum, p) => sum + (engagement[p.igMediaId]?.comments || 0), 0);
  const isLive = autoPostEnabled && !!nextPost && !!igAccount;
  const dashStatus = publishing ? "POSTING" : publishErrors.length ? "ERROR" : isLive ? "LIVE" : "IDLE";

  const etaLabel = (secs) => {
    if (secs < 3600) return `${Math.floor(secs / 60)}m`;
    if (secs < 86400) return `${Math.floor(secs / 3600)}h`;
    return `${Math.floor(secs / 86400)}d`;
  };

  return (
    <div className="ig-redesign-shell">
      <SidebarNav active="publish" />
      <main className="ig-redesign-main">
        <TopBar project="SHIFT · Live Publishing" />
        <div className="ig-redesign-scroll">

          {/* Hero */}
          <section className="ig-hero-panel">
            <div>
              <div className="ig-eyebrow"><span />INSTAGRAM · LIVE PUBLISHING</div>
              <h1>Publishing dashboard.</h1>
              <p>Everything you need — what's live, what's posted, what's next and how each post is performing. Auto-post fires due posts the moment their time arrives.</p>
              <div className="ig-status-row">
                <span className={"publish-status-badge publish-status-" + dashStatus.toLowerCase()}>
                  <span className="publish-pulse" />
                  {dashStatus}
                </span>
                {igAccount
                  ? <Pill tone="green">@{igAccount.username} · connected</Pill>
                  : <Pill tone="amber">Instagram not connected</Pill>}
                <span>{postedPosts.length} posted · {upcomingPosts.length} upcoming · {missedPosts.length > 0 ? `${missedPosts.length} missed · ` : ""}{publishErrors.length} error{publishErrors.length !== 1 ? "s" : ""}</span>
              </div>
            </div>
            <div className="ig-hero-actions">
              <button className="btn btn-secondary" onClick={() => window.BoltKitGo?.("ig-calendar")}>
                <Icon name="calendar" size={12} stroke={2} /> Calendar
              </button>
              <button className="btn btn-secondary" onClick={() => window.BoltKitGo?.("ig-generate")}>
                <Icon name="sparkle" size={12} stroke={2} /> Generate
              </button>
            </div>
          </section>

          {/* Missed posts banner */}
          {missedPosts.length > 0 && (
            <div style={{ display: "flex", alignItems: "center", gap: 14, padding: "14px 18px", background: "rgba(255,77,94,0.07)", border: "1px solid rgba(255,77,94,0.3)", borderRadius: 10, flexWrap: "wrap" }}>
              <Icon name="alert" size={18} stroke={2.4} color="#FF4D5E" />
              <div style={{ flex: 1, minWidth: 0 }}>
                <b style={{ fontSize: 13, color: "#FF4D5E", display: "block" }}>
                  {missedPosts.length} post{missedPosts.length !== 1 ? "s" : ""} missed {missedPosts.length === 1 ? "its" : "their"} scheduled time
                </b>
                <span style={{ fontSize: 11.5, color: "#8A8A8A" }}>
                  They were skipped because the browser wasn't open or the engine was paused. Post them now in 10-minute intervals.
                </span>
              </div>
              <button className="btn btn-primary btn-sm" style={{ flex: "none", whiteSpace: "nowrap", background: "#FF4D5E", borderColor: "#FF4D5E" }} onClick={postMissedNow}>
                <Icon name="signal" size={11} stroke={2} /> Post all {missedPosts.length} now · 10-min intervals
              </button>
            </div>
          )}

          {/* Currently posting banner */}
          {publishing && currentlyPosting && (
            <div className="publish-now-card">
              <div className="publish-now-anim">
                <span className="publish-radar" />
                <span className="publish-radar-2" />
                <BoltMini size={22} color="#1A1813" />
              </div>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div className="ig-eyebrow small" style={{ marginBottom: 4 }}><span />POSTING NOW</div>
                <b>{currentlyPosting.title || (currentlyPosting.caption || "").slice(0, 60) || "Post"}</b>
                <span>Sending to Instagram @{igAccount?.username || "account"}…</span>
              </div>
              {(currentlyPosting.publicUrl || currentlyPosting.imageUrl) && !/^(blob:|data:)/.test(currentlyPosting.publicUrl || currentlyPosting.imageUrl) && (
                <img src={currentlyPosting.publicUrl || currentlyPosting.imageUrl} alt="" className="publish-now-thumb" />
              )}
            </div>
          )}

          {/* Stats strip */}
          <div className="publish-stats-strip">
            <div className="publish-stat">
              <b>{postedPosts.length}</b>
              <span>Posted</span>
            </div>
            <div className="publish-stat">
              <b>{upcomingPosts.length}</b>
              <span>Upcoming</span>
            </div>
            <div className={"publish-stat" + (secsUntilNext !== null ? " accent" : "")}>
              <b>{fmtCountdown(secsUntilNext)}</b>
              <span>Next post</span>
            </div>
            <div className="publish-stat">
              <b>{engPolled ? (totalLikes || "0") : "—"}</b>
              <span>Total likes</span>
            </div>
            <div className="publish-stat">
              <b>{engPolled ? (totalComments || "0") : "—"}</b>
              <span>Comments</span>
            </div>
            <div className={"publish-stat" + (publishErrors.length ? " publish-status-error" : "")}>
              <b style={publishErrors.length ? { color: "#FF4D5E" } : {}}>{publishErrors.length}</b>
              <span>Errors</span>
            </div>
          </div>

          {/* Main grid */}
          <div className="publish-main-grid">

            {/* Left — Posted feed */}
            <div>
              <div className="publish-feed-col">
                <div className="publish-feed-header">
                  <div>
                    <div className="card-title">Posted</div>
                    <div className="card-sub">Published to Instagram · engagement updates every 5 min</div>
                  </div>
                  <Pill tone={postedPosts.length ? "green" : "dark"}>{postedPosts.length}</Pill>
                </div>
                {postedPosts.length ? postedPosts.map(post => {
                  const eng = engagement[post.igMediaId] || {};
                  const img = post.publicUrl || post.imageUrl;
                  const hasEng = eng.likes !== undefined;
                  return (
                    <div key={post.id} className="publish-posted-row">
                      {img && !/^(blob:|data:)/.test(img)
                        ? <img src={img} alt="" className="publish-row-thumb" />
                        : <div className="publish-row-thumb publish-row-thumb-empty"><BoltMini size={12} color="#3A372F" /></div>}
                      <div className="publish-row-body">
                        <b>{post.title || (post.caption || "").slice(0, 55) || "Post"}</b>
                        <span>{post.postedAt ? new Date(post.postedAt).toLocaleString("en-GB", { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" }) : (post.date || "—")}</span>
                      </div>
                      <div className={"publish-row-eng" + (hasEng ? " has-data" : "")}>
                        <span><Icon name="heart" size={10} stroke={2} /> {hasEng ? eng.likes : "—"}</span>
                        <span><Icon name="comment" size={10} stroke={2} /> {hasEng ? eng.comments : "—"}</span>
                      </div>
                      <Pill tone="green">Posted</Pill>
                    </div>
                  );
                }) : (
                  <div className="ig-empty-queue" style={{ padding: 32 }}>
                    <Icon name="signal" size={28} stroke={1.5} color="#3A372F" />
                    <b>Nothing posted yet.</b>
                    <span>Posts appear here as they go live. Auto-post is {autoPostEnabled ? "on and watching" : "currently off"}.</span>
                  </div>
                )}
              </div>
            </div>

            {/* Right — Next post + upcoming + errors + controls */}
            <div className="publish-right-col">

              {/* Next post */}
              {nextPost ? (
                <div className="publish-next-card">
                  <div className="ig-eyebrow small"><span />NEXT POST</div>
                  <div className="publish-countdown">{fmtCountdown(secsUntilNext)}</div>
                  <div className="publish-next-details">
                    <b>{nextPost.title || (nextPost.caption || "").slice(0, 55) || "Scheduled post"}</b>
                    <span>{nextPost.date} at {nextPost.time || "09:00"}</span>
                  </div>
                  {(nextPost.publicUrl || nextPost.imageUrl) && !/^(blob:|data:)/.test(nextPost.publicUrl || nextPost.imageUrl) && (
                    <img src={nextPost.publicUrl || nextPost.imageUrl} alt="" className="publish-next-thumb" />
                  )}
                  <button
                    className="btn btn-secondary btn-sm"
                    style={{ width: "100%", justifyContent: "center", marginTop: 12 }}
                    disabled={publishing || !igAccount}
                    onClick={() => autoPublish(nextPost)}
                  >
                    <Icon name="insta" size={12} stroke={2} /> Post now
                  </button>
                </div>
              ) : (
                <div className="publish-next-card">
                  <div className="ig-eyebrow small"><span />NEXT POST</div>
                  <div className="publish-countdown" style={{ color: "#3A372F" }}>—</div>
                  <div className="publish-next-details">
                    <b style={{ color: "#5C5C5C" }}>No upcoming posts</b>
                    <span>Schedule posts in the calendar to see them here</span>
                  </div>
                  <button className="btn btn-secondary btn-sm" style={{ width: "100%", justifyContent: "center", marginTop: 12 }} onClick={() => window.BoltKitGo?.("ig-generate")}>
                    <Icon name="sparkle" size={12} stroke={2} /> Generate posts
                  </button>
                </div>
              )}

              {/* Upcoming list — reorderable */}
              <div className="publish-upcoming-section">
                <div className="ig-panel-head" style={{ marginBottom: 8 }}>
                  <div>
                    <div className="card-title">Coming up</div>
                    <div className="card-sub" style={{ fontSize: 10.5, marginTop: 2 }}>Use ↑↓ to change order — slots reassign automatically</div>
                  </div>
                  <Pill tone={upcomingPosts.length ? "yellow" : "dark"}>{upcomingPosts.length}</Pill>
                </div>
                {upcomingPosts.length ? upcomingPosts.map((post, idx) => {
                  const at = new Date(`${post.date}T${post.time || "09:00"}:00Z`);
                  const secs = Math.max(0, Math.floor((at - now) / 1000));
                  const img = post.publicUrl || post.imageUrl;
                  const isFirst = idx === 0;
                  const isLast  = idx === upcomingPosts.length - 1;
                  return (
                    <div key={post.id} className="publish-upcoming-row" style={{ gap: 6 }}>
                      {/* Reorder controls */}
                      <div style={{ display: "flex", flexDirection: "column", gap: 2, flex: "none" }}>
                        <button
                          onClick={() => moveUpcoming(post.id, -1)}
                          disabled={isFirst}
                          style={{ background: "none", border: "1px solid #3A372F", borderRadius: 3, width: 18, height: 17, display: "flex", alignItems: "center", justifyContent: "center", cursor: isFirst ? "default" : "pointer", opacity: isFirst ? 0.2 : 0.7, padding: 0, color: "#FFF" }}
                          title="Move earlier"
                        >
                          <Icon name="chevron_down" size={9} stroke={2.5} style={{ transform: "rotate(180deg)", display: "block" }} />
                        </button>
                        <button
                          onClick={() => moveUpcoming(post.id, 1)}
                          disabled={isLast}
                          style={{ background: "none", border: "1px solid #3A372F", borderRadius: 3, width: 18, height: 17, display: "flex", alignItems: "center", justifyContent: "center", cursor: isLast ? "default" : "pointer", opacity: isLast ? 0.2 : 0.7, padding: 0, color: "#FFF" }}
                          title="Move later"
                        >
                          <Icon name="chevron_down" size={9} stroke={2.5} />
                        </button>
                      </div>
                      {/* Thumbnail */}
                      {img && !/^(blob:|data:)/.test(img)
                        ? <img src={img} alt="" style={{ width: 30, height: 38, objectFit: "cover", borderRadius: 3, flex: "none", border: "1px solid #3A372F" }} />
                        : <div style={{ width: 30, height: 38, background: "#2C2A23", borderRadius: 3, flex: "none", display: "flex", alignItems: "center", justifyContent: "center" }}><BoltMini size={9} color="#5C5C5C" /></div>}
                      {/* Info */}
                      <div style={{ flex: 1, minWidth: 0 }}>
                        <div className="publish-upcoming-label">{post.title || (post.caption || "").slice(0, 38) || "Post"}</div>
                        <div className="publish-upcoming-meta">{post.date} · {post.time || "09:00"}</div>
                      </div>
                      {/* ETA */}
                      <div className="publish-upcoming-eta">{etaLabel(secs)}</div>
                      {/* Remove */}
                      <button
                        onClick={() => removePost(post.id)}
                        title="Remove from queue"
                        style={{ background: "none", border: "none", color: "#3A372F", cursor: "pointer", fontSize: 14, padding: "0 2px", lineHeight: 1, flex: "none", transition: "color 0.15s" }}
                        onMouseEnter={e => e.currentTarget.style.color = "#FF4D5E"}
                        onMouseLeave={e => e.currentTarget.style.color = "#3A372F"}
                      >✕</button>
                    </div>
                  );
                }) : (
                  <p style={{ fontSize: 12, color: "#5C5C5C", margin: 0 }}>No upcoming posts.</p>
                )}
              </div>

              {/* Errors */}
              {publishErrors.length > 0 && (
                <div className="publish-error-panel">
                  <div><Icon name="alert" size={14} stroke={2.5} color="#FF4D5E" />{publishErrors.length} ERROR{publishErrors.length !== 1 ? "S" : ""}</div>
                  {publishErrors.map((err, i) => (
                    <div key={i} className="publish-error-row">
                      <div style={{ flex: 1, minWidth: 0 }}>
                        <div style={{ fontSize: 11.5, color: "#FF4D5E", fontWeight: 600, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
                          {err.post.title || (err.post.caption || "").slice(0, 40) || "Post"}
                        </div>
                        <div style={{ fontSize: 10.5, color: "#8A8A8A", marginTop: 2, lineHeight: 1.4 }}>{err.error}</div>
                      </div>
                      <button className="btn btn-secondary btn-sm" style={{ flex: "none" }} onClick={() => retryPost(err)}>Retry</button>
                    </div>
                  ))}
                  <button className="btn btn-ghost btn-sm" style={{ marginTop: 8 }} onClick={() => setPublishErrors([])}>Clear all</button>
                </div>
              )}

              {/* Campaign Control */}
              <div className="publish-engine-card">
                <div style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 8.5, color: "#5C5C5C", letterSpacing: "0.14em", marginBottom: 12 }}>CAMPAIGN CONTROL</div>

                {/* Status row */}
                <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 14 }}>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 3 }}>
                      <span style={{ width: 8, height: 8, borderRadius: "50%", background: isLive ? "#4ADE80" : autoPostEnabled ? "#FFB23F" : "#5C5C5C", display: "inline-block", ...(isLive ? { animation: "pub-pulse-dot 1.4s infinite" } : {}) }} />
                      <span style={{ fontFamily: "'Anton', Impact, sans-serif", fontSize: 20, color: isLive ? "#4ADE80" : autoPostEnabled ? "#FFB23F" : "#8A8A8A", lineHeight: 1, letterSpacing: "0.02em" }}>
                        {isLive ? "RUNNING" : autoPostEnabled ? (igAccount ? "WATCHING" : "WAITING") : "PAUSED"}
                      </span>
                    </div>
                    <div style={{ fontSize: 11, color: "#5C5C5C" }}>
                      {upcomingPosts.length} post{upcomingPosts.length !== 1 ? "s" : ""} queued · {postedPosts.length} posted
                    </div>
                  </div>
                  <button
                    onClick={() => { setAutoPostEnabled(v => !v); setConfirmClear(false); }}
                    title={autoPostEnabled ? "Pause campaign" : "Resume campaign"}
                    style={{ width: 50, height: 50, borderRadius: 12, border: autoPostEnabled ? "2px solid #3A372F" : "none", background: autoPostEnabled ? "#26241E" : "#EFFF00", color: autoPostEnabled ? "#8A8A8A" : "#1A1813", display: "flex", alignItems: "center", justifyContent: "center", cursor: "pointer", flex: "none", fontSize: 22 }}
                  >
                    {autoPostEnabled ? "⏸" : "▶"}
                  </button>
                </div>

                {!igAccount && (
                  <div style={{ display: "flex", alignItems: "center", gap: 6, marginBottom: 10, padding: "8px 10px", background: "rgba(255,178,63,0.08)", borderRadius: 6, border: "1px solid rgba(255,178,63,0.2)" }}>
                    <span style={{ width: 5, height: 5, borderRadius: "50%", background: "#FFB23F", display: "inline-block", flex: "none" }} />
                    <span style={{ fontSize: 10.5, color: "#FFB23F", lineHeight: 1.5 }}>Connect Instagram to enable auto-posting.</span>
                  </div>
                )}

                <div style={{ display: "flex", flexDirection: "column", gap: 6, paddingTop: 12, borderTop: "1px solid #2C2A22" }}>
                  {/* Story toggle */}
                  <div
                    onClick={toggleStory}
                    style={{ display: "flex", alignItems: "center", gap: 10, padding: "9px 12px", background: postStory ? "rgba(239,255,0,0.06)" : "#26241E", borderRadius: 8, cursor: "pointer", border: postStory ? "1px solid rgba(239,255,0,0.2)" : "1px solid transparent", transition: "all 0.2s" }}
                  >
                    <div style={{ width: 36, height: 20, borderRadius: 10, background: postStory ? "#EFFF00" : "#3A372F", position: "relative", flex: "none", transition: "background 0.2s" }}>
                      <div style={{ width: 14, height: 14, borderRadius: "50%", background: postStory ? "#1A1813" : "#5C5C5C", position: "absolute", top: 3, left: postStory ? 18 : 3, transition: "left 0.2s" }} />
                    </div>
                    <div style={{ minWidth: 0 }}>
                      <div style={{ fontSize: 12, fontWeight: 600, color: postStory ? "#FFF" : "#8A8A8A", lineHeight: 1.2 }}>Also post as Story</div>
                      <div style={{ fontSize: 10, color: "#5C5C5C", marginTop: 1 }}>Reposts each image as an Instagram Story simultaneously</div>
                    </div>
                  </div>

                  <button className="btn btn-primary btn-sm" style={{ width: "100%", justifyContent: "center" }} onClick={() => setShowBulk(true)}>
                    <Icon name="calendar" size={11} stroke={2} /> Bulk Schedule
                  </button>
                  <button className="btn btn-secondary btn-sm" style={{ width: "100%", justifyContent: "center" }} onClick={rescheduleFromNow}>
                    <Icon name="refresh" size={11} stroke={2} /> Quick reschedule (2/day)
                  </button>
                  {!confirmClear
                    ? <button
                        className="btn btn-ghost btn-sm"
                        style={{ width: "100%", justifyContent: "center", color: upcomingPosts.length ? "#FF4D5E" : "#3A372F", borderColor: upcomingPosts.length ? "rgba(255,77,94,0.3)" : "#2C2A22" }}
                        onClick={() => setConfirmClear(true)}
                        disabled={!upcomingPosts.length}
                      >
                        <Icon name="trash" size={11} stroke={2} /> Clear queue ({upcomingPosts.length})
                      </button>
                    : <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
                        <button
                          className="btn btn-ghost btn-sm"
                          style={{ width: "100%", justifyContent: "center", background: "rgba(255,77,94,0.12)", color: "#FF4D5E", border: "1px solid rgba(255,77,94,0.5)", fontWeight: 700 }}
                          onClick={clearUnposted}
                        >
                          Confirm — delete all {upcomingPosts.length} unposted
                        </button>
                        <button className="btn btn-ghost btn-sm" style={{ width: "100%", justifyContent: "center" }} onClick={() => setConfirmClear(false)}>Cancel</button>
                      </div>
                  }
                </div>
              </div>
            </div>
          </div>
        </div>
      </main>
      {showBulk && window.BulkScheduleModal && React.createElement(window.BulkScheduleModal, {
        posts: queuePosts,
        storageKey: "boltkit.scheduledPosts.v1",
        apiEndpoint: "/api/supabase-scheduled-posts",
        postStory,
        onClose: () => { setShowBulk(false); setQueuePosts(loadQueue()); },
      })}
    </div>
  );
};

Object.assign(window, { InstagramScreen, CalendarScreen, PublishDashboard });
