/* ========================================================================
   data.jsx — Example data + simple global state store
   ====================================================================== */

const NAV = [
  { section: "Workspace", items: [
    { id: "home", label: "Landing", icon: "globe" },
    { id: "dashboard", label: "Dashboard", icon: "grid" },
    { id: "projects", label: "Projects", icon: "folder", count: 6 },
  ]},
  { section: "Project", items: [
    { id: "overview", label: "Project Overview", icon: "briefcase" },
    { id: "brand", label: "Brand Packs", icon: "palette" },
    { id: "assets", label: "Assets", icon: "image", count: 24 },
    { id: "analysis", label: "AI Analysis", icon: "brain" },
  ]},
  { section: "Generate", items: [
    { id: "generate", label: "Generate Packs", icon: "sparkle" },
    { id: "outputs", label: "Outputs", icon: "layers", count: 12 },
    { id: "exports", label: "Exports", icon: "download" },
  ]},
  { section: "System", items: [
    { id: "settings", label: "Settings", icon: "settings" },
  ]},
];

// secondary destinations (open via cards/links rather than top-level nav)
const SECONDARY_PAGES = [
  "wizard", "social", "video", "appstore", "ads", "landing", "seo", "claude-prompts", "codex-prompts"
];

const PROJECTS = [
  {
    id: "renoo",
    name: "Renoo",
    tagline: "Mobile-first renovation companion",
    icon: { letter: "R", gradient: "linear-gradient(135deg, oklch(0.65 0.16 200), oklch(0.55 0.18 250))" },
    url: "renoo.app",
    status: "Generated",
    statusVariant: "yellow",
    readiness: 87,
    missing: 2,
    lastPack: "Full Launch Pack",
    lastGen: "12 min ago",
    packs: 8,
    audience: "Homeowners planning renovations, landlords managing maintenance, and small builders organising quotes and photos.",
    pain: "Renovation projects spiral. Quotes get lost in WhatsApp, paint codes vanish, receipts pile up in glove boxes, and three months in nobody remembers what was decided in the kitchen.",
    tone: "Helpful, practical, calm, trustworthy and lightly premium.",
    summary: "Renoo is a mobile-first renovation companion that keeps every quote, receipt, paint code and decision in one calm place. AI checks quotes for fairness, scans receipts and paint codes, generates snagging lists from photos, and remembers what was decided in every room.",
    features: [
      "AI quote checker", "Receipt scanner", "Paint code scanner", "Smart alerts",
      "AI snagging list", "Room Memory AI", "Renovation Copilot", "Cost tracker",
      "Shopping list", "Project timeline", "Weekly summary"
    ],
    benefits: [
      { t: "Never overpay for a quote again", s: "Quote Checker cross-references your trades against thousands of recent local jobs in seconds." },
      { t: "Every paint colour, saved for life", s: "Snap a tin, Renoo remembers the brand, code and which room it was used in." },
      { t: "A single source of truth", s: "Receipts, photos, snags and decisions — all in one place, searchable by room." },
      { t: "Built for couples and contractors", s: "Shared rooms with comments mean fewer arguments and zero lost decisions." },
    ],
    angles: [
      "The renovation copilot that pays for itself in one quote",
      "Stop fighting about paint codes",
      "Your kitchen, your rules — and your receipts",
      "What if Notion knew about your skirting boards?",
    ],
    missingAssets: [
      { name: "Press kit photos", required: true },
      { name: "App Store hero video", required: false },
    ],
    colors: ["#FFE600", "#0B0B0B", "#F5F4F0", "#1E90FF"],
    fonts: { heading: "Söhne Halbfett", body: "Söhne Buch" },
  },
  {
    id: "loopnest",
    name: "Loopnest",
    tagline: "Calm focus timer for ADHD brains",
    icon: { letter: "L", gradient: "linear-gradient(135deg, oklch(0.7 0.16 150), oklch(0.55 0.18 200))" },
    url: "loopnest.io",
    status: "Needs Analysis",
    statusVariant: "amber",
    readiness: 42,
    missing: 6,
    lastPack: "—",
    lastGen: "Never",
    packs: 0,
  },
  {
    id: "ferment",
    name: "Ferment",
    tagline: "Sourdough log + bake timer",
    icon: { letter: "F", gradient: "linear-gradient(135deg, oklch(0.78 0.13 70), oklch(0.6 0.16 35))" },
    url: "ferment.bake",
    status: "Ready",
    statusVariant: "blue",
    readiness: 71,
    missing: 3,
    lastPack: "App Store Pack",
    lastGen: "3 days ago",
    packs: 4,
  },
  {
    id: "wayfound",
    name: "Wayfound",
    tagline: "Indoor wayfinding for venues",
    icon: { letter: "W", gradient: "linear-gradient(135deg, oklch(0.55 0.18 290), oklch(0.4 0.18 270))" },
    url: "wayfound.co",
    status: "Exported",
    statusVariant: "green",
    readiness: 94,
    missing: 0,
    lastPack: "Feature Update Pack",
    lastGen: "Yesterday",
    packs: 11,
  },
  {
    id: "sprout",
    name: "Sprout Studio",
    tagline: "AI growth coach for makers",
    icon: { letter: "S", gradient: "linear-gradient(135deg, oklch(0.72 0.17 145), oklch(0.6 0.15 165))" },
    url: "sprout.studio",
    status: "Generated",
    statusVariant: "yellow",
    readiness: 79,
    missing: 1,
    lastPack: "Social Content Pack",
    lastGen: "Yesterday",
    packs: 6,
  },
  {
    id: "tideboard",
    name: "Tideboard",
    tagline: "Surf forecast for the UK coast",
    icon: { letter: "T", gradient: "linear-gradient(135deg, oklch(0.7 0.14 220), oklch(0.45 0.16 235))" },
    url: "tideboard.uk",
    status: "Needs Analysis",
    statusVariant: "amber",
    readiness: 28,
    missing: 9,
    lastPack: "—",
    lastGen: "Never",
    packs: 0,
  },
];

const RECENT_PACKS = [
  { id: 1, project: "Renoo",     pack: "Full Launch Pack",     items: 132, when: "12 min ago",  by: "AI" },
  { id: 2, project: "Sprout",    pack: "Social Content Pack",  items: 48,  when: "Yesterday",   by: "AI" },
  { id: 3, project: "Wayfound",  pack: "Feature Update Pack",  items: 27,  when: "Yesterday",   by: "AI" },
  { id: 4, project: "Renoo",     pack: "Claude Design Prompts",items: 7,   when: "2 days ago",  by: "AI" },
  { id: 5, project: "Ferment",   pack: "App Store Pack",       items: 14,  when: "3 days ago",  by: "AI" },
];

const PACK_TYPES = [
  { id: "full",      name: "Full Launch Pack",          time: "~4 min", items: "120–160 assets", tone: "comprehensive", icon: "rocket", featured: true,
    desc: "Everything from app store copy to a 30-day social calendar, ads, scripts, prompts and a press kit." },
  { id: "feature",   name: "Feature Update Pack",       time: "~90s",  items: "24–32 assets", tone: "iterative", icon: "spark2",
    desc: "Announce a new feature across socials, app store updates, email and changelog." },
  { id: "social",    name: "Social Content Pack",       time: "~2 min", items: "30–60 posts",   tone: "creative", icon: "insta",
    desc: "Instagram, Reels, TikTok, LinkedIn, Facebook + a 30-day content calendar." },
  { id: "video",     name: "Video Script Pack",         time: "~2 min", items: "10–20 scripts", tone: "narrative", icon: "video",
    desc: "YouTube, Shorts, Reels and TikTok scripts with shot lists, overlays and thumbnail text." },
  { id: "appstore",  name: "App Store Pack",            time: "~1 min", items: "App Store + Play",tone: "concise", icon: "shop",
    desc: "Title, subtitle, description, keywords, screenshot captions and what's new copy." },
  { id: "ads",       name: "Paid Ads Pack",             time: "~2 min", items: "30+ ad variants",tone: "performance", icon: "target",
    desc: "Meta, Instagram, TikTok, Google, YouTube, LinkedIn — with A/B variants and CTAs." },
  { id: "landing",   name: "Landing Page Copy",         time: "~1 min", items: "12 sections",   tone: "persuasive", icon: "layout",
    desc: "Hero, value prop, features, benefits, FAQ, CTA + SEO title and meta description." },
  { id: "seo",       name: "SEO Pack",                  time: "~90s",  items: "20+ ideas",     tone: "discoverable", icon: "hash",
    desc: "Page titles, meta descriptions, blog ideas, keyword clusters, comparison articles." },
  { id: "instagram", name: "Instagram Pack",            time: "~90s",  items: "20 posts",       tone: "visual", icon: "insta",
    desc: "Single posts, carousels, story frames — captions and hashtags included." },
  { id: "claude",    name: "Claude Design Prompts",     time: "~1 min", items: "7 prompts",     tone: "design", icon: "sparkle",
    desc: "Ready-to-paste prompts for dashboards, landing pages, app screens and brand systems." },
  { id: "codex",     name: "Codex Prompt Pack",         time: "~1 min", items: "9 prompts",     tone: "engineering", icon: "code",
    desc: "Spec-style prompts for frontend, backend, Supabase, API routes, mobile polish, QA." },
  { id: "qa",        name: "QA / Testing Prompts",      time: "~45s",  items: "6 prompts",     tone: "rigorous", icon: "check_circle",
    desc: "Edge cases, accessibility, mobile responsiveness and pre-launch checklist prompts." },
];

const SAMPLE_INSTAGRAM = [
  { kind: "single", hook: "The quote you almost signed.", body: "We ran 12 real renovation quotes through Renoo this week. 9 were over-priced by an average of £4,300.\n\nIf you're getting quotes for a kitchen, bathroom or extension — send them to Renoo first. Two taps, real numbers, no negotiating gut feelings.", hashtags: "#renovation #ukhomes #renovating #homerenovation #renoapp", saves: 412, score: 92 },
  { kind: "carousel", hook: "Six things every renovation forgets.", slides: ["Skirting board height", "Socket positions", "Tile grout colour", "Tap finish", "Door handles", "The fan in the bathroom"], body: "Save this carousel for when you're 40% in and 60% confused.", hashtags: "#renovationtips #firsttimerenovation #renoo", saves: 1208, score: 88 },
  { kind: "single", hook: "Your paint code, for life.", body: "Renoo remembers the exact paint in every room — brand, code, finish, where it was used, when it was last touched up. No more guessing on touch-ups three years later.", hashtags: "#homedecor #paintideas #renoo", saves: 287, score: 89 },
];

const SAMPLE_VIDEOS = [
  { kind: "yt-short", title: "I sent a £42k kitchen quote to AI", duration: "0:38", scenes: 4, score: 91 },
  { kind: "reel",     title: "Three taps every renovation regret",  duration: "0:24", scenes: 3, score: 86 },
  { kind: "tiktok",   title: "The receipt graveyard in your car",   duration: "0:42", scenes: 5, score: 84 },
  { kind: "yt-long",  title: "How we built Renoo in 8 weekends",    duration: "11:20", scenes: 9, score: 78 },
  { kind: "fb",       title: "For couples doing their first reno",  duration: "0:55", scenes: 6, score: 81 },
];

const SAMPLE_ADS = {
  meta: [
    { head: "Stop guessing what your kitchen should cost.", body: "Renoo cross-references your quote against thousands of recent UK jobs in seconds. Free for your first three quotes.", cta: "Try Renoo", v: "A" },
    { head: "Your renovation, finally organised.", body: "Receipts, paint codes, snags and decisions — all in one place, searchable by room.", cta: "Get Renoo", v: "B" },
  ],
  google: [
    { head1: "Renovation Quote Check", head2: "AI Cross-References UK Trades", desc: "Free for your first 3 quotes. Receipts and paint codes saved per room.", url: "renoo.app/quote-check" },
    { head1: "Renovation App for Couples", head2: "End the WhatsApp Renovation", desc: "Shared rooms, decisions and receipts. Calm, premium, mobile-first.", url: "renoo.app/couples" },
  ],
  tiktok: [
    { hook: "I sent my plumber's quote to an AI", body: "What it spotted in 4 seconds saved us £1,800. This is Renoo.", cta: "Try Renoo free" },
  ],
};

const SAMPLE_LANDING = {
  hero_h: "Your renovation, finally calm.",
  hero_sub: "Renoo is a mobile-first renovation companion. It checks quotes, scans receipts, remembers paint codes and writes your snagging list from photos.",
  hero_cta: "Start your first room — free",
  vp: "Renovating is fragmented across WhatsApp, glove boxes, and the back of envelopes. Renoo gives every room one calm source of truth.",
  features: [
    { t: "AI Quote Checker", b: "Paste any trade quote. Get a fairness score and the three things to push back on." },
    { t: "Receipt Scanner", b: "Snap a receipt — Renoo files it by room, project and trade automatically." },
    { t: "Room Memory", b: "Every paint code, tile spec and decision, saved against the room it belongs to." },
    { t: "Snagging Co-pilot", b: "Walk the room with your phone. Renoo generates a punch list from your photos." },
  ],
  faq: [
    { q: "Is Renoo just for first-time renovators?", a: "No. Landlords use Renoo to track maintenance, and small builders use it to organise quotes, photos and snags by job." },
    { q: "Does my builder need the app?", a: "No. You can share a read-only room link with anyone, including trades who don't sign up." },
    { q: "How is this different from Notion?", a: "Renoo is mobile-first and renovation-shaped. The AI knows what a snagging list looks like — Notion doesn't." },
  ],
};

const SAMPLE_APPSTORE = {
  title: "Renoo — Renovation Companion",
  subtitle: "Quotes, paint codes, snags, calm",
  description: "Renoo is the calm renovation companion your project deserves.\n\nQuote Checker cross-references trade quotes against thousands of UK jobs in seconds. Receipt Scanner files every paper trail by room. Paint Code Scanner remembers the exact finish in every space — for life. Room Memory keeps every decision, photo and spec in one searchable place.\n\nBuilt for homeowners, couples, landlords and small builders. Mobile-first, lightly premium.",
  keywords: ["renovation", "home renovation", "snagging", "paint code", "quote checker", "renovation app", "home improvement", "diy", "couples renovating", "landlord"],
  whats_new: "v1.4 — Room Memory now exports as a PDF spec sheet. Snagging photos can be shared as a read-only room link.",
  captions: [
    "Quote Checker — paste any quote, get a fairness score",
    "Room Memory — every paint code, saved for life",
    "Snagging Copilot — write your punch list from photos",
    "Weekly summary — what got done, what's next",
    "Shared rooms — your partner, your builder, in one place",
  ],
};

const SAMPLE_SEO = {
  titles: [
    "Renoo — Renovation Companion App (UK)",
    "Renovation Quote Checker — Is Your Quote Fair? | Renoo",
    "Save Every Paint Code You'll Ever Use | Renoo",
    "Snagging List App for First Time Renovators | Renoo",
  ],
  meta: "Renoo is a mobile-first renovation companion. Check trade quotes, scan receipts, remember paint codes, and generate a snagging list from photos. Free for your first room.",
  blogs: [
    "12 things your kitchen quote should include (and what's a red flag)",
    "How much does a 4×3m bathroom really cost in the UK in 2026?",
    "The 9-minute walk-through that finds £1,800 of snags",
    "Notion vs Renoo for renovations: why room-shape matters",
    "The renovation receipts checklist your accountant wishes you had",
    "First-time renovator? Save these 7 paint codes before you forget",
  ],
  clusters: [
    { topic: "Quote checker", terms: ["renovation quote checker", "is my builder's quote fair", "compare trade quotes uk", "average kitchen cost uk"] },
    { topic: "Snagging",      terms: ["snagging list new build", "snagging app", "punch list renovation", "snagging template pdf"] },
    { topic: "Paint codes",   terms: ["save paint codes app", "farrow and ball code", "remember paint colour", "paint scanner app"] },
  ],
};

const CLAUDE_PROMPTS = [
  { name: "Full app dashboard", purpose: "Generate the cleaned-up dashboard for Renoo with Rooms grid, Quote inbox, Snagging tray.", target: "Claude Design", words: 412 },
  { name: "Landing page redesign", purpose: "Redesign the renoo.app landing using the Calm-Premium aesthetic from the brand pack.", target: "Claude Design", words: 388 },
  { name: "Social media asset design", purpose: "Generate 6 Instagram post designs + 3 carousels for the Quote Checker feature.", target: "Claude Design", words: 264 },
  { name: "App Store screenshot layouts", purpose: "5 App Store screenshot frames with overlay copy from the captions pack.", target: "Claude Design", words: 318 },
  { name: "Feature update design",  purpose: "Design the in-app announcement modal for v1.4 (Room Memory PDF export).", target: "Claude Design", words: 196 },
  { name: "Mobile-first polish",     purpose: "Pass over all primary surfaces for thumb-zone, tap targets and safe-area.", target: "Claude Design", words: 244 },
  { name: "Brand system creation",   purpose: "Lock the Renoo brand into a tokens + components system for handoff.", target: "Claude Design", words: 502 },
];

const CODEX_PROMPTS = [
  { name: "Full app implementation",   stack: "Expo / RN / Supabase", lines: 612 },
  { name: "Frontend build",             stack: "Next.js / shadcn / Tailwind", lines: 380 },
  { name: "Backend / Supabase setup",   stack: "Supabase / Postgres / RLS", lines: 290 },
  { name: "API routes",                 stack: "tRPC / Zod", lines: 240 },
  { name: "QA / testing",               stack: "Playwright / Vitest", lines: 198 },
  { name: "Mobile responsiveness",      stack: "Tailwind / RN", lines: 156 },
  { name: "Export systems",             stack: "PDF / CSV / ZIP",  lines: 220 },
  { name: "Bug fixing",                 stack: "—",  lines: 180 },
  { name: "Production cleanup",         stack: "—",  lines: 142 },
];

const PROMPT_SAMPLE = `<span class="com">// Claude Design — Renoo full dashboard</span>
<span class="kw">role</span>:    senior product designer, calm-premium fintech sensibility
<span class="kw">audience</span>: UK homeowners and small builders, mobile-first
<span class="kw">scope</span>:   one full desktop dashboard frame + one mobile frame

<span class="kw">do</span>:
  - lead with a <span class="str">"Rooms"</span> grid, not a calendar
  - foreground the Quote Checker inbox at the top right
  - use the brand palette tokens from <span class="id">renoo.brand.v1</span>
  - keep all type in <span class="id">Söhne</span> + <span class="id">JetBrains Mono</span> for receipts
  - default copy from <span class="id">renoo.copy.dashboard.v3</span>

<span class="kw">don't</span>:
  - introduce stock SaaS sidebars
  - use emoji
  - generate hero illustrations — placeholder slots only

<span class="kw">deliver</span>:
  one HTML file, two artboards, design tokens panel,
  inline notes on three decisions you made.`;

/* ------- tiny global store using useState + context ------- */
const AppCtx = React.createContext(null);

function useAppStore() {
  const routeIds = React.useMemo(() => new Set([
    "home", "pricing", "features", "demo", "login", "signup", "forgot", "terms", "privacy",
    "dashboard", "projects", "wizard", "overview", "brand", "assets", "analysis",
    "generate", "instagram-scheduler", "outputs", "social", "video", "appstore", "ads", "landing", "seo",
    "claude-prompts", "codex-prompts", "exports", "settings", "account", "billing", "usage", "health"
  ]), []);
  const readRoute = React.useCallback(() => {
    if (typeof window === "undefined") return "home";
    const fromHash = window.location.hash.replace(/^#\/?/, "");
    return routeIds.has(fromHash) ? fromHash : "home";
  }, [routeIds]);
  const [route, setRouteState] = React.useState(readRoute);
  const [currentProjectId, setCurrentProjectIdState] = React.useState(() => {
    try { return localStorage.getItem("boltkit.currentProjectId") || "renoo"; }
    catch (e) { return "renoo"; }
  });
  const [navOpen, setNavOpen] = React.useState(false);
  const [toast, setToast] = React.useState(null);
  const [generating, setGenerating] = React.useState(null); // {packName, progress}
  const [generatedSet, setGeneratedSetState] = React.useState(() => {
    try {
      const saved = JSON.parse(localStorage.getItem("boltkit.generatedPacks") || "[]");
      return new Set(saved);
    } catch (e) {
      return new Set();
    }
  });
  const [selectedPackForOutput, setSelectedPackForOutput] = React.useState(() => {
    try { return localStorage.getItem("boltkit.selectedPack") || "full"; }
    catch (e) { return "full"; }
  });

  React.useEffect(() => {
    const sync = () => setRouteState(readRoute());
    window.addEventListener("hashchange", sync);
    window.addEventListener("popstate", sync);
    const id = window.setInterval(sync, 500);
    return () => {
      window.removeEventListener("hashchange", sync);
      window.removeEventListener("popstate", sync);
      window.clearInterval(id);
    };
  }, [readRoute]);

  React.useEffect(() => {
    try { localStorage.setItem("boltkit.currentProjectId", currentProjectId); } catch (e) {}
  }, [currentProjectId]);

  React.useEffect(() => {
    try { localStorage.setItem("boltkit.generatedPacks", JSON.stringify([...generatedSet])); } catch (e) {}
  }, [generatedSet]);

  React.useEffect(() => {
    try { localStorage.setItem("boltkit.selectedPack", selectedPackForOutput); } catch (e) {}
  }, [selectedPackForOutput]);

  const showToast = React.useCallback((msg) => {
    setToast(msg);
    setTimeout(() => setToast(null), 2200);
  }, []);

  const go = React.useCallback((id) => {
    const next = routeIds.has(id) ? id : "dashboard";
    setRouteState(next);
    if (typeof window !== "undefined" && window.location.hash !== `#${next}`) {
      window.history.pushState(null, "", `#${next}`);
    }
    setNavOpen(false);
    if (typeof window !== "undefined") window.scrollTo({ top: 0, behavior: "instant" });
  }, [routeIds]);

  const setRoute = React.useCallback((id) => go(id), [go]);

  const setCurrentProjectId = React.useCallback((id) => {
    setCurrentProjectIdState(id);
    try { localStorage.setItem("boltkit.currentProjectId", id); } catch (e) {}
  }, []);

  const setGeneratedSet = React.useCallback((value) => {
    setGeneratedSetState(prev => {
      const next = typeof value === "function" ? value(prev) : value;
      try { localStorage.setItem("boltkit.generatedPacks", JSON.stringify([...next])); } catch (e) {}
      return next;
    });
  }, []);

  const currentProject = PROJECTS.find(p => p.id === currentProjectId) || PROJECTS[0];

  const runGeneration = React.useCallback((pack) => {
    setGenerating({ pack, progress: 0 });
    let p = 0;
    const t = setInterval(() => {
      p += 8 + Math.random() * 9;
      if (p >= 100) {
        clearInterval(t);
        setGenerating({ pack, progress: 100 });
        setTimeout(async () => {
          let mode = "local";
          try {
            const controller = new AbortController();
            const timeout = setTimeout(() => controller.abort(), 30000);
            const res = await fetch("/api/generate-pack", {
              method: "POST",
              headers: { "Content-Type": "application/json" },
              body: JSON.stringify({
                project: {
                  ...currentProject,
                  assets: (() => { try { return JSON.parse(localStorage.getItem("boltkit.assets") || "[]"); } catch (e) { return []; } })(),
                  brandPack: {
                    source: currentProject.brandSource || "/brand-pack/brand-pack.html",
                    colours: currentProject.colors || ["#EFFF00", "#050505", "#F4F4F0"],
                    fonts: currentProject.fonts || { heading: "Anton", body: "Inter", mono: "JetBrains Mono" },
                    rules: currentProject.brandRules || []
                  }
                },
                packType: pack.id
              }),
              signal: controller.signal
            });
            clearTimeout(timeout);
            const data = await res.json();
            if (data.ok && data.pack) {
              mode = data.mode || "api";
              const existing = JSON.parse(localStorage.getItem("boltkit.generatedContent") || "{}");
              existing[pack.id] = { ...data.pack, mode, savedAt: new Date().toISOString() };
              localStorage.setItem("boltkit.generatedContent", JSON.stringify(existing));
            } else {
              const existing = JSON.parse(localStorage.getItem("boltkit.generatedContent") || "{}");
              existing[pack.id] = {
                title: `${currentProject.name} ${pack.name}`,
                mode: "api error",
                savedAt: new Date().toISOString(),
                content_markdown: `# ${currentProject.name} ${pack.name}\n\nThe API route returned an error: ${data.error || "Unknown error"}.\n\nProject context used:\n- URL: ${currentProject.url}\n- Audience: ${currentProject.audience}\n- Tone: ${currentProject.tone}`,
                quality_score: 0,
                brand_match_score: 0
              };
              localStorage.setItem("boltkit.generatedContent", JSON.stringify(existing));
              mode = "api error";
            }
          } catch (e) {
            const existing = JSON.parse(localStorage.getItem("boltkit.generatedContent") || "{}");
            existing[pack.id] = {
              title: `${currentProject.name} ${pack.name}`,
              mode: "api timeout",
              savedAt: new Date().toISOString(),
              content_markdown: `# ${currentProject.name} ${pack.name}\n\nThe OpenAI request did not complete within the local 30 second safety window. Try regenerating this pack, or switch to a faster model in .env.local.\n\nProject context used:\n- URL: ${currentProject.url}\n- Audience: ${currentProject.audience}\n- Tone: ${currentProject.tone}`,
              quality_score: 0,
              brand_match_score: 0
            };
            localStorage.setItem("boltkit.generatedContent", JSON.stringify(existing));
            mode = "api timeout";
          }
          setGeneratedSet(prev => new Set([...prev, pack.id]));
          setGenerating(null);
          setSelectedPackForOutput(pack.id);
          go("outputs");
          showToast(`✓  ${pack.name} ready · ${mode}`);
        }, 500);
      } else {
        setGenerating({ pack, progress: p });
      }
    }, 220);
  }, [showToast, setGeneratedSet, go, currentProject]);

  return {
    route, setRoute, go,
    currentProjectId, setCurrentProjectId, currentProject,
    navOpen, setNavOpen,
    toast, showToast,
    generating, runGeneration, generatedSet, setGeneratedSet,
    selectedPackForOutput, setSelectedPackForOutput,
  };
}

Object.assign(window, {
  NAV, SECONDARY_PAGES, PROJECTS, RECENT_PACKS, PACK_TYPES,
  SAMPLE_INSTAGRAM, SAMPLE_VIDEOS, SAMPLE_ADS, SAMPLE_LANDING,
  SAMPLE_APPSTORE, SAMPLE_SEO, CLAUDE_PROMPTS, CODEX_PROMPTS, PROMPT_SAMPLE,
  AppCtx, useAppStore,
});
