<!DOCTYPE html>
<html lang="en">
  <head>
    <meta name="ffm-build" content="7468175a3368eda619c4092cba1808826ef41592" />
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
    <link rel="manifest" href="/manifest.json" />
    <link rel="apple-touch-icon" href="/icons/icon-192.png" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
    <meta name="theme-color" content="#0d0d0d" />
    <meta name="mobile-web-app-capable" content="yes" />
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
    <meta name="description" content="Make films, run your studio, and compete in Follywood. Write scripts, hire talent, release to cinemas and streaming." />
    <meta name="robots" content="index, follow" />
    <link rel="canonical" href="https://www.fantasyfilmmaker2.com/" />
    <title>FantasyFilmmaker 2</title>
    <script type="application/ld+json">
      {
        "@context": "https://schema.org",
        "@graph": [
          {
            "@type": "WebSite",
            "@id": "https://www.fantasyfilmmaker2.com/#website",
            "url": "https://www.fantasyfilmmaker2.com/",
            "name": "FantasyFilmmaker 2",
            "description": "Online film studio simulation — scripts, production, cinema, streaming, and Follywood market.",
            "publisher": { "@id": "https://www.fantasyfilmmaker2.com/#organization" },
            "inLanguage": "en"
          },
          {
            "@type": "Organization",
            "@id": "https://www.fantasyfilmmaker2.com/#organization",
            "name": "FantasyFilmmaker 2",
            "url": "https://www.fantasyfilmmaker2.com/"
          }
        ]
      }
    </script>
    <!-- Open Graph (Facebook, WhatsApp, etc.) - defaults; app can override per page -->
    <meta property="og:type" content="website" />
    <meta property="og:url" content="https://www.fantasyfilmmaker2.com/" />
    <meta property="og:title" content="FantasyFilmmaker 2" />
    <meta property="og:description" content="Make films, run your studio, and compete in Follywood. Write scripts, hire talent, release to cinemas and streaming." />
    <meta property="og:image" content="https://www.fantasyfilmmaker2.com/og-default.png" />
    <!-- Twitter Card - defaults; app can override per page -->
    <meta name="twitter:card" content="summary_large_image" />
    <meta name="twitter:title" content="FantasyFilmmaker 2" />
    <meta name="twitter:description" content="Make films, run your studio, and compete in Follywood. Write scripts, hire talent, release to cinemas and streaming." />
    <meta name="twitter:image" content="https://www.fantasyfilmmaker2.com/og-default.png" />
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link
      href="https://fonts.googleapis.com/css2?family=Cinzel:wght@400;600;700&family=Cormorant+Garamond:ital,wght@0,400;0,600;1,400&family=Crimson+Pro:ital,wght@0,400;0,600;1,400&family=Courier+Prime:wght@400;700&family=DM+Sans:ital,wght@0,400;0,600;0,700;1,400&family=Lora:ital,wght@0,400;0,600;1,400&family=Libre+Baskerville:ital,wght@0,400;0,700;1,400&family=Playfair+Display:ital,wght@0,500;0,600;0,700;1,400&family=Source+Sans+3:ital,wght@0,400;0,600;0,700;1,400&family=Source+Serif+4:ital,opsz,wght@0,8..60,400;0,8..60,600;0,8..60,700;1,8..60,400&family=Special+Elite&display=swap"
      rel="stylesheet"
    />
    <!-- Inline until main-*.css loads — hides sr-only SEO copy and powers the stale-cache recovery UI. -->
    <style id="ffm-critical-shell">
      .sr-only:not(:focus):not(:active) {
        position: absolute !important;
        width: 1px !important;
        height: 1px !important;
        padding: 0 !important;
        margin: -1px !important;
        overflow: hidden !important;
        clip: rect(0, 0, 0, 0) !important;
        white-space: nowrap !important;
        border: 0 !important;
      }
      .skip-to-main {
        position: absolute;
        left: -9999px;
        top: auto;
        width: 1px;
        height: 1px;
        overflow: hidden;
      }
      .skip-to-main:focus {
        position: fixed;
        left: 1rem;
        top: 1rem;
        z-index: 100000;
        width: auto;
        height: auto;
        padding: 0.5rem 0.75rem;
        background: #1c1917;
        color: #fafaf9;
        border-radius: 0.5rem;
      }
      #ffm-shell-recovery[hidden] {
        display: none !important;
      }
      #ffm-shell-recovery {
        position: fixed;
        inset: 0;
        z-index: 99999;
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 1.5rem;
        background: #0c0a09;
        color: #fafaf9;
        font-family: system-ui, -apple-system, Segoe UI, sans-serif;
      }
      #ffm-shell-recovery .ffm-shell-recovery-card {
        max-width: 26rem;
        text-align: center;
        line-height: 1.5;
      }
      #ffm-shell-recovery h1 {
        font-size: 1.25rem;
        margin: 0 0 0.75rem;
      }
      #ffm-shell-recovery p {
        margin: 0 0 1rem;
        color: #d6d3d1;
        font-size: 0.95rem;
      }
      #ffm-shell-recovery button {
        -webkit-appearance: none;
           -moz-appearance: none;
                appearance: none;
        border: 0;
        border-radius: 0.5rem;
        padding: 0.65rem 1rem;
        font-size: 0.95rem;
        font-weight: 600;
        background: #d97706;
        color: #0c0a09;
        cursor: pointer;
      }
      #ffm-shell-recovery small {
        display: block;
        margin-top: 0.75rem;
        color: #a8a29e;
        font-size: 0.8rem;
      }
    </style>
    <script type="module" crossorigin src="/assets/main-CrEaH0OL.js"></script>
    <link rel="modulepreload" crossorigin href="/assets/modulepreload-polyfill-B5Qt9EMX.js">
    <link rel="stylesheet" crossorigin href="/assets/main-Dj_KE3Lw.css?v=7468175a3368eda619c4092cba1808826ef41592">
      <link rel="preload" as="style" href="/assets/main-Dj_KE3Lw.css?v=7468175a3368eda619c4092cba1808826ef41592" crossorigin />
  </head>
  <body>
    <div id="ffm-shell-recovery" hidden role="alertdialog" aria-modal="true" aria-labelledby="ffm-shell-recovery-title">
      <div class="ffm-shell-recovery-card">
        <h1 id="ffm-shell-recovery-title">FantasyFilmmaker 2 needs a refresh</h1>
        <p>
          Your browser is probably showing an old cached copy of the site styles after an update. Load the latest
          version below, or hard-refresh (Ctrl+Shift+R).
        </p>
        <button type="button" id="ffm-shell-recovery-btn">Load latest version</button>
        <small>If this keeps happening, clear site data for fantasyfilmmaker2.com in your browser settings.</small>
      </div>
    </div>
    <div id="root"></div>
    <noscript>
      <p>
        FantasyFilmmaker 2 is a browser strategy simulation in the fictional world of Follywood. This dashboard is your studio HQ — scripts, films, market pulse, and community links below.
      </p>
    </noscript>
    <!-- Runs before the module bundle so a stale main-*.js (HTML 404) can hard-reload. Keep in sync with src/lib/chunkLoadRecovery.js -->
    <script>
      (function () {
        var COUNT_KEY = 'ffm-chunk-reload-count';
        var LEGACY_KEY = 'ffm-chunk-reload-once';
        var MAX = 5;
        var RECOVERY_ID = 'ffm-shell-recovery';
        var RECOVERY_BTN_ID = 'ffm-shell-recovery-btn';
        function readCount() {
          try {
            if (sessionStorage.getItem(LEGACY_KEY)) {
              sessionStorage.removeItem(LEGACY_KEY);
              return 1;
            }
            var n = Number(sessionStorage.getItem(COUNT_KEY));
            return n > 0 && n === n ? Math.floor(n) : 0;
          } catch (e) {
            return 0;
          }
        }
        function mainCssLink() {
          return document.querySelector('link[rel="stylesheet"][href*="/assets/main-"]');
        }
        /** Vite dev uses /src/main.jsx — skip stale-cache recovery (no /assets/main-*.css link). */
        function isProductionShell() {
          if (document.querySelector('script[type="module"][src*="/src/main"]')) return false;
          return !!mainCssLink() || !!document.querySelector('script[type="module"][src*="/assets/main-"]');
        }
        function cssApplied() {
          if (!isProductionShell()) return true;
          var link = mainCssLink();
          if (!link) return false;
          try {
            return !!link.sheet;
          } catch (e) {
            return false;
          }
        }
        function prefetchAssets() {
          var urls = [];
          var css = mainCssLink();
          if (css && css.href) urls.push(css.href);
          var script = document.querySelector('script[type="module"][src*="/assets/main-"]');
          if (script && script.src) urls.push(script.src);
          var bust = String(Date.now());
          for (var i = 0; i < urls.length; i++) {
            try {
              fetch(urls[i] + (urls[i].indexOf('?') === -1 ? '?' : '&') + '_=' + bust, {
                cache: 'reload',
                headers: { 'Cache-Control': 'no-cache', Pragma: 'no-cache' },
              }).catch(function () {});
            } catch (e) {}
          }
        }
        function showRecoveryOverlay() {
          var el = document.getElementById(RECOVERY_ID);
          if (el) el.hidden = false;
        }
        function wireRecoveryButton() {
          var btn = document.getElementById(RECOVERY_BTN_ID);
          if (!btn || btn.dataset.wired === '1') return;
          btn.dataset.wired = '1';
          btn.addEventListener('click', function () {
            try {
              sessionStorage.removeItem(COUNT_KEY);
              sessionStorage.removeItem(LEGACY_KEY);
            } catch (e) {}
            if (window.caches && caches.keys) {
              caches.keys().then(function (keys) {
                keys.forEach(function (k) {
                  caches.delete(k);
                });
              });
            }
            prefetchAssets();
            var target = new URL(location.origin);
            target.pathname = '/';
            target.search = '';
            target.searchParams.set('_ffm', String(Date.now()));
            target.searchParams.set('_nc', '1');
            location.replace(target.toString());
          });
        }
        function reloadOnce() {
          try {
            var count = readCount();
            if (count >= MAX) {
              showRecoveryOverlay();
              return;
            }
            sessionStorage.setItem(COUNT_KEY, String(count + 1));
            var target = new URL(location.origin);
            target.pathname = '/';
            target.search = '';
            target.searchParams.set('_ffm', String(Date.now()));
            target.searchParams.set('_nc', '1');
            var targetHref = target.toString();
            prefetchAssets();
            fetch(location.origin + '/index.html?_=' + Date.now(), {
              cache: 'reload',
              headers: { 'Cache-Control': 'no-cache', Pragma: 'no-cache' },
            })
              .catch(function () {})
              .finally(function () {
                location.replace(targetHref);
              });
          } catch (e) {
            try {
              location.reload();
            } catch (_) {}
          }
        }
        function scheduleCssHealthCheck() {
          if (!isProductionShell()) return;
          wireRecoveryButton();
          function check() {
            if (cssApplied()) return true;
            reloadOnce();
            return false;
          }
          setTimeout(function () {
            if (check()) return;
            setTimeout(check, 2200);
          }, 1600);
        }
        addEventListener(
          'error',
          function (e) {
            var t = e.target;
            if (t && (t.tagName === 'SCRIPT' || t.tagName === 'LINK')) {
              var s = t.src || t.href || '';
              if (
                /\.(js|css|mjs)(\?|$)/i.test(s) &&
                (s.indexOf('/assets/') !== -1 || /\/main-[\w-]+\.(js|css|mjs)/i.test(s))
              ) {
                reloadOnce();
                return;
              }
            }
            if (e.message) {
              var m = String(e.message);
              if (/Refused to apply style/i.test(m)) {
                try {
                  sessionStorage.removeItem(COUNT_KEY);
                  sessionStorage.removeItem(LEGACY_KEY);
                } catch (err) {}
              }
              if (
                /MIME type of "text\/html"|MIME type \(['"]text\/html['"]\)|JavaScript-or-Wasm module script|not a supported stylesheet MIME type|Refused to apply style|Minified React error #321|Invalid hook call|Unexpected token ['"]?</i.test(
                  m,
                )
              ) {
                reloadOnce();
              }
            }
          },
          true,
        );
        try {
          var buildMeta = document.querySelector('meta[name="ffm-build"]');
          var buildId = buildMeta && buildMeta.getAttribute('content');
            if (buildId && buildId !== '7468175a3368eda619c4092cba1808826ef41592') {
            var BUILD_KEY = 'ffm-last-build';
            var prevBuild = sessionStorage.getItem(BUILD_KEY);
            sessionStorage.setItem(BUILD_KEY, buildId);
            if (prevBuild && prevBuild !== buildId) {
              reloadOnce();
            }
          }
        } catch (_) {}
        var cssLinkEarly = mainCssLink();
        if (cssLinkEarly) {
          cssLinkEarly.addEventListener('error', function () {
            reloadOnce();
          });
        }
        scheduleCssHealthCheck();
      })();
    </script>
  </body>
</html>
