<!doctype html>
<html lang="tr" class="dark">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
  <title>Akademik Asistan | Öğrenci & Akademisyen Portalı</title>
  
  <!-- PWA Meta Tags -->
  <meta name="description" content="Yapay zeka destekli akademik yönetim platformu. Ders programı, sınavlar, notlar, ödevler ve daha fazlası." />
  <meta name="theme-color" content="#062a63" media="(prefers-color-scheme: light)" />
  <meta name="theme-color" content="#0f172a" media="(prefers-color-scheme: dark)" />
  <meta name="apple-mobile-web-app-capable" content="yes" />
  <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
  <meta name="apple-mobile-web-app-title" content="Akademik" />
  <meta name="mobile-web-app-capable" content="yes" />
  <meta name="application-name" content="Akademik Asistan" />
  <meta name="msapplication-TileColor" content="#062a63" />
  <meta name="msapplication-tap-highlight" content="no" />
  
  <!-- Open Graph / Social -->
  <meta property="og:type" content="website" />
  <meta property="og:title" content="Akademik Asistan | Öğrenci & Akademisyen Portalı" />
  <meta property="og:description" content="Yapay zeka destekli akademik yönetim platformu. Ders programı, sınavlar, notlar, ödevler ve daha fazlası." />
  <meta property="og:site_name" content="Akademik Asistan" />
  <meta property="og:locale" content="tr_TR" />
  <meta property="og:url" content="https://akademikasistan.com/" />
  <meta property="og:image" content="https://akademikasistan.com/og-demo.png" />
  <meta name="twitter:image" content="https://akademikasistan.com/og-demo.png" />
  
  <!-- Twitter Card -->
  <meta name="twitter:card" content="summary_large_image" />
  <meta name="twitter:title" content="Akademik Asistan" />
  <meta name="twitter:description" content="Yapay zeka destekli akademik yönetim platformu" />
  
  <!-- Icons -->
  <link rel="icon" href="/favicon.ico" sizes="any" />
  <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
  <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
  <link rel="icon" type="image/png" sizes="192x192" href="/icon-192x192.png" />
  <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
  <link rel="apple-touch-icon" sizes="152x152" href="/icon-152x152.png" />
  <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
  <link rel="apple-touch-icon" sizes="167x167" href="/icon-167x167.png" />
  
  <!-- PWA Manifest -->
  <link rel="manifest" href="/manifest.json" />
  
  <!-- DNS Prefetch for external resources -->
  <link rel="dns-prefetch" href="https://fonts.googleapis.com" />
  <link rel="dns-prefetch" href="https://fonts.gstatic.com" />
  
  <!-- Preconnect to Supabase for faster API calls -->
  <link rel="preconnect" href="https://egbjtfktnrrwfmkckkwz.supabase.co" crossorigin />
  <!-- Preconnect to Cloudflare Turnstile (demo auto-login captcha) -->
  <link rel="preconnect" href="https://challenges.cloudflare.com" crossorigin />

  <script>
    // Anonymous visitors land on the demo auto-signin, which needs a Turnstile
    // token before it can log in. Start downloading the Turnstile script during
    // HTML parse (in parallel with the app bundle) so the token resolves sooner.
    // loadTurnstile() reuses this same #turnstile-script tag.
    (function () {
      try {
        var hasSession = false;
        for (var i = 0; i < localStorage.length; i++) {
          var k = localStorage.key(i);
          if (k && k.indexOf('sb-') === 0 && k.indexOf('-auth-token') !== -1) { hasSession = true; break; }
        }

        // This global pre-warm widget only feeds the demo auto-signin (root "/")
        // and the /demo entry pages. Every other route — notably /login, which
        // renders its own in-form Turnstile — must NEVER get this full-screen
        // background widget: on a flagged IP (Cloudflare WARP / VPN) it escalates
        // to an interactive challenge and the opaque backdrop hijacks the whole
        // page before the user has even tried to sign in.
        var tsPath = location.pathname || '/';
        tsPath = tsPath.length > 1 ? tsPath.replace(/\/+$/, '') : tsPath;
        var isDemoEntryPath = (tsPath === '/' || tsPath === '/demo' || tsPath.indexOf('/demo/') === 0);

        // Initialize global Turnstile placeholder variables
        window.globalTurnstileToken = undefined;
        window.globalTurnstileSolvedAt = undefined;
        window.globalTurnstileError = false;

        // Shared opaque backdrop toggle. When an interactive challenge is
        // actually shown it must block the whole screen (centered widget on
        // top) so it's impossible to miss — demo data stays empty until it's
        // solved. Exposed on window so the React fallback (turnstile.ts) reuses
        // the exact same overlay instead of rolling its own.
        function setTurnstileBackdrop(show) {
          var bd = document.getElementById('turnstile-backdrop');
          if (bd) bd.style.display = show ? 'flex' : 'none';
        }
        window.__setTurnstileBackdrop = setTurnstileBackdrop;

        if (!hasSession && isDemoEntryPath) {
          window.onloadTurnstileCallback = function () {
            if (window.turnstile) {
              try {
                var siteKey = '0x4AAAAAACCeu9iTYebNZAVO' || '0x4AAAAAACCeu9iTYebNZAVO';
                if (siteKey.indexOf('%') === 0) {
                  siteKey = '0x4AAAAAACCeu9iTYebNZAVO';
                }

                window.globalTurnstileWidgetId = window.turnstile.render('#global-turnstile-container', {
                  sitekey: siteKey,
                  appearance: 'interaction-only',
                  execution: 'render',
                  // Don't auto-retry a failed challenge: on a flagged IP (WARP/VPN)
                  // auto-retry re-opens the interactive backdrop in a loop. One
                  // attempt only — the demo flow's React fallback handles a real
                  // failure and ultimately routes to /login.
                  retry: 'never',
                  // Once solved we never need this background widget again, so don't let
                  // it auto-refresh and pop back up in the center after the token
                  // expires (especially annoying during an active demo session).
                  'refresh-expired': 'never',
                  // Open the opaque blocking overlay only when CF actually shows
                  // an interactive challenge; close it the moment it's done.
                  'before-interactive-callback': function () { setTurnstileBackdrop(true); },
                  'after-interactive-callback': function () { setTurnstileBackdrop(false); },
                  callback: function (token) {
                    window.globalTurnstileToken = token;
                    window.globalTurnstileSolvedAt = Date.now();
                    window.globalTurnstileError = false;
                    // Success: hide the centered widget + overlay so they don't
                    // linger (interaction-only can still flash a success badge).
                    setTurnstileBackdrop(false);
                    var solvedContainer = document.getElementById('global-turnstile-container');
                    if (solvedContainer) solvedContainer.style.display = 'none';
                    window.dispatchEvent(new CustomEvent('global-turnstile-solved', { detail: token }));
                  },
                  'error-callback': function () {
                    setTurnstileBackdrop(false);
                    window.globalTurnstileError = true;
                    window.dispatchEvent(new CustomEvent('global-turnstile-error'));
                  },
                  'expired-callback': function () {
                    setTurnstileBackdrop(false);
                    window.globalTurnstileToken = undefined;
                    window.globalTurnstileSolvedAt = undefined;
                    window.dispatchEvent(new CustomEvent('global-turnstile-expired'));
                  },
                  'timeout-callback': function () {
                    setTurnstileBackdrop(false);
                    window.globalTurnstileToken = undefined;
                    window.globalTurnstileSolvedAt = undefined;
                    window.dispatchEvent(new CustomEvent('global-turnstile-timeout'));
                  }
                });
              } catch (e) {
                window.globalTurnstileError = true;
              }
            }
          };

          if (!document.getElementById('turnstile-script')) {
            var s = document.createElement('script');
            s.id = 'turnstile-script';
            s.src = 'https://challenges.cloudflare.com/turnstile/v0/api.js?onload=onloadTurnstileCallback';
            s.async = true; s.defer = true; s.crossOrigin = 'anonymous';
            document.head.appendChild(s);
          }
        }
      } catch (e) { /* ignore */ }
    })();
  </script>
  
  <script>
    // Prevent theme flash on load
    (function () {
      var theme = localStorage.getItem('aa_web_theme');
      if (theme === 'light') {
        document.documentElement.classList.remove('dark');
        document.body && document.body.classList.add('theme-light');
      }
    })();
  </script>
  <style>
    /* Prevent FOUC (Flash of Unstyled Content) and white screen */
    html,
    body,
    #root {
      margin: 0;
      padding: 0;
      min-height: 100vh;
    }

    html.dark body,
    html.dark #root {
      background: #050507;
    }

    html:not(.dark) body,
    html:not(.dark) #root {
      background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 50%, #e2e8f0 100%);
      background-attachment: fixed;
    }

    /* Fully opaque blocking overlay for an interactive Turnstile challenge.
       Covers the entire viewport on every breakpoint (inset:0 + fixed), so on
       mobile and desktop alike only the centered widget is interactable. */
    #turnstile-backdrop {
      background: #050507;
    }

    html:not(.dark) #turnstile-backdrop {
      background: #f1f5f9;
    }
  </style>
  <script type="module" crossorigin src="/assets/index-DN3w2PCK.js"></script>
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-C42WewQR.js">
  <link rel="modulepreload" crossorigin href="/assets/vendor-jspdf-DlGW7Nx4.js">
  <link rel="modulepreload" crossorigin href="/assets/vendor-motion-0zzrcNji.js">
  <link rel="modulepreload" crossorigin href="/assets/vendor-supabase-CHb5Xv48.js">
  <link rel="modulepreload" crossorigin href="/assets/vendor-icons-ESv8xirF.js">
  <link rel="modulepreload" crossorigin href="/assets/vendor-router-JhIKHLnE.js">
  <link rel="modulepreload" crossorigin href="/assets/vendor-utils-CEdPFdu2.js">
  <link rel="modulepreload" crossorigin href="/assets/vendor-radix-CwBnN_F5.js">
  <link rel="modulepreload" crossorigin href="/assets/vendor-query-D1pc2dLN.js">
  <link rel="stylesheet" crossorigin href="/assets/index-BgiC7szM.css">
</head>

<body>
  <div id="turnstile-backdrop" style="position: fixed; inset: 0; z-index: 9998; display: none; align-items: center; justify-content: center;"></div>
  <div id="global-turnstile-container" style="position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 9999; pointer-events: auto;"></div>
  <div id="root"></div>
  
  <!-- Service Worker Registration -->
  <script>
    (function () {
      if (window.desktopApp && window.desktopApp.isDesktopApp) return;
      if (!('serviceWorker' in navigator)) return;

      var isLocalhost = ['localhost', '127.0.0.1', '0.0.0.0'].includes(window.location.hostname);
      if (isLocalhost) {
        // Unregister all SWs on localhost so dev is never blocked
        navigator.serviceWorker.getRegistrations().then(function (regs) {
          regs.forEach(function (r) { r.unregister(); });
        });
        return;
      }

      // ── 1. PROACTIVELY DELETE OLD-VERSION CACHES ──────────────────────────
      // Inline scripts run even when an old SW is serving a cached index.html,
      // because SWs cannot intercept their own host page's inline scripts.
      // Keep only the current cache version; purge every older akademik-* cache
      // so a stuck client self-heals. KEEP_CACHE must match CACHE_VERSION in /sw.js.
      if ('caches' in window) {
        var KEEP_CACHE = 'v1.13.11';
        caches.keys().then(function (keys) {
          keys.forEach(function (key) {
            if (key.startsWith('akademik-') && key.indexOf(KEEP_CACHE) === -1) {
              caches.delete(key);
            }
          });
        });
      }

      // ── 2. CONTROLLERCHANGE → RELOAD ──────────────────────────────────────
      // The old code used sessionStorage to prevent double-reloads.
      // On mobile, background tabs can keep sessionStorage alive for WEEKS,
      // so once the key was set by an earlier update attempt the reload would
      // never fire again. Replace the guard with a simple in-memory debounce
      // (safe: after reload the new SW is already active → no further event).
      var reloadPending = false;
      navigator.serviceWorker.addEventListener('controllerchange', function () {
        if (reloadPending) return;
        reloadPending = true;
        window.location.reload();
      });

      // ── 3. REGISTER AND FORCE-UPDATE SW ───────────────────────────────────
      window.addEventListener('load', function () {
        navigator.serviceWorker.register('/sw.js', { scope: '/', updateViaCache: 'none' })
          .then(function (reg) {
            // Immediately check for a newer sw.js on the server
            reg.update().catch(function () {});

            // If a new SW is already waiting (stuck from a previous visit), activate it now
            if (reg.waiting) {
              reg.waiting.postMessage({ type: 'SKIP_WAITING' });
            }

            // Also watch for updates found during this session
            reg.addEventListener('updatefound', function () {
              var newWorker = reg.installing;
              if (!newWorker) return;
              newWorker.addEventListener('statechange', function () {
                if (newWorker.state === 'installed' && reg.waiting) {
                  reg.waiting.postMessage({ type: 'SKIP_WAITING' });
                }
              });
            });
          })
          .catch(function (err) {
            console.warn('[PWA] SW registration failed:', err);
          });
      });
    })();
  </script>
<!-- Cloudflare Pages Analytics --><script defer src='https://static.cloudflareinsights.com/beacon.min.js' data-cf-beacon='{"token": "70d18e9e70f1409197d8ea649f92fb0b"}'></script><!-- Cloudflare Pages Analytics --></body>

</html>
