<?php
declare(strict_types=1);

require_once __DIR__ . '/../lib/db.php';
require_once __DIR__ . '/../lib/helpers.php';

require_login();

$c    = cfg();
$base = base_url_dir(); // örn: "/tahta"
?>
<!doctype html>
<html lang="tr">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
  <title>QR ile Tahta Aktifleştir</title>

  <meta name="theme-color" content="#121417">

  <!-- ✅ HTML5 QR -->
  <script defer src="/qr/minified/html5-qrcode.min.js?v=2"></script>

  <!-- ✅ Lucide Icons -->
  <script defer src="https://unpkg.com/lucide@latest"></script>

  <style>
    :root{
      --bg:#0f1114;
      --bg2:#121417;
      --line:rgba(255,255,255,.12);
      --line2:rgba(255,255,255,.08);
      --w:#fff;
      --m:rgba(255,255,255,.72);
      --m2:rgba(255,255,255,.56);
      --shadow: 0 18px 50px rgba(0,0,0,.58);
      --shadow2: 0 10px 22px rgba(0,0,0,.45);
      --radius:22px;
      --tap:52px;
      --ok:#a7ffcf;
      --bad:#ffb0bb;
    }

    *{box-sizing:border-box}
    html,body{height:100%}

    body{
      margin:0;
      font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial;
      color:var(--w);
      background:
        radial-gradient(1100px 700px at 18% 8%, rgba(255,255,255,.06), transparent 55%),
        radial-gradient(900px 600px at 92% 22%, rgba(255,255,255,.04), transparent 52%),
        linear-gradient(180deg, var(--bg), var(--bg2));
    }

    .screen{
      min-height:100vh;
      min-height:100svh;
      min-height:100dvh;
      padding:
        max(12px, env(safe-area-inset-top))
        12px
        max(12px, env(safe-area-inset-bottom))
        12px;
      display:flex;
    }

    input, textarea { font-size:16px; }

    .app{
      max-width:560px;
      margin:0 auto;
      width:100%;
      display:flex;
      flex-direction:column;
      gap:12px;
      flex:1;
      min-height:0;
    }

    .top{
      display:flex;
      align-items:center;
      justify-content:space-between;
      gap:12px;
      position:sticky;
      top:calc(env(safe-area-inset-top));
      z-index:10;

      padding:12px 12px;
      border-radius: var(--radius);
      border:1px solid var(--line2);
      background: rgba(255,255,255,.03);
      backdrop-filter: blur(12px);
      box-shadow: var(--shadow2);
      overflow:hidden;
    }
    .top::before{
      content:"";
      position:absolute; inset:-1px;
      background: radial-gradient(620px 220px at 10% 0%, rgba(255,255,255,.06), transparent 60%);
      pointer-events:none;
    }

    .brand{
      display:flex;align-items:center;gap:10px;
      position:relative; z-index:1;
      min-width:0;
    }
    .brand i{width:18px;height:18px;opacity:.9}
    .ttl{
      font-weight:950;
      letter-spacing:.14em;
      text-transform:uppercase;
      font-size:12px;
      white-space:nowrap;
    }
    .subttl{
      font-size:12px;
      color:var(--m2);
      font-weight:900;
      letter-spacing:.02em;
      overflow:hidden;
      text-overflow:ellipsis;
      white-space:nowrap;
      margin-top:2px;
    }

    .nav{display:flex;gap:10px;align-items:center; position:relative; z-index:1;}
    .link{
      display:inline-flex;
      align-items:center;
      gap:8px;
      padding:10px 12px;
      border-radius:999px;
      border:1px solid rgba(255,255,255,.14);
      background: rgba(0,0,0,.18);
      color:rgba(255,255,255,.88);
      text-decoration:none;
      font-weight:900;
      letter-spacing:.08em;
      text-transform:uppercase;
      font-size:11px;
      -webkit-tap-highlight-color: transparent;
      white-space:nowrap;
    }
    .link i{width:16px;height:16px;opacity:.88}
    .link:hover{border-color:rgba(255,255,255,.22); background: rgba(255,255,255,.06);}

    .card{
      border:1px solid var(--line2);
      background: linear-gradient(180deg, rgba(255,255,255,.05), rgba(255,255,255,.02));
      border-radius: var(--radius);
      padding:14px;
      box-shadow: var(--shadow);
      overflow:hidden;
      position:relative;

      display:flex;
      flex-direction:column;
      flex:1;
      min-height:0;
      gap:0;
    }
    .card::before{
      content:"";
      position:absolute; inset:-2px;
      background: radial-gradient(650px 280px at 18% 0%, rgba(255,255,255,.05), transparent 62%);
      pointer-events:none;
    }

    .label{
      display:flex; align-items:center; gap:8px;
      font-size:11px;
      opacity:.70;
      letter-spacing:.22em;
      font-weight:950;
      text-transform:uppercase;
      position:relative; z-index:1;
    }
    .label i{width:16px;height:16px;opacity:.9}

    .big{
      font-size:22px;
      font-weight:950;
      letter-spacing:-.01em;
      margin-top:6px;
      position:relative; z-index:1;
    }
    .sub{
      font-size:13px;
      color:var(--m);
      line-height:1.55;
      margin-top:6px;
      position:relative; z-index:1;
    }
    .mono{font-family:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace}

    .notice{
      margin-top:10px;
      padding:12px 12px;
      border-radius:18px;
      border:1px solid rgba(255,255,255,.14);
      background: rgba(0,0,0,.20);
      color:rgba(255,255,255,.82);
      position:relative; z-index:1;
      display:none;
    }
    .notice b{color:#fff}

    .scanStage{
      border-radius: 18px;
      overflow:hidden;
      border:1px solid rgba(255,255,255,.14);
      background: rgba(0,0,0,.26);
      margin-top:12px;
      position:relative; z-index:1;
      box-shadow: inset 0 0 0 1px rgba(255,255,255,.03);
      display:flex;
      flex-direction:column;
      min-height:320px;
    }

    #qrReader{
      width:100%;
      height:100%;
      flex:1 1 auto;
      min-height:0;
      position:relative;

      display:flex;
      align-items:center;
      justify-content:center;

      background:
        radial-gradient(420px 260px at 30% 20%, rgba(255,255,255,.06), transparent 60%),
        linear-gradient(180deg, rgba(255,255,255,.03), rgba(255,255,255,.01));
    }

    #qrReader__scan_region{
      display:block!important;
      overflow:hidden!important;
      border-radius:16px;
      width:min(100%, 420px)!important;
      aspect-ratio:1 / 1 !important;
      height:auto!important;
    }
    #qrReader__scan_region video,
    #qrReader__scan_region canvas{
      width:100%!important;
      height:100%!important;
      display:block!important;
      object-fit:cover!important;
    }

    #qrReader__dashboard{display:none!important}

    .hud{
      padding:10px 12px;
      border-top:1px solid rgba(255,255,255,.10);
      display:flex;
      align-items:center;
      justify-content:space-between;
      gap:10px;
      color:var(--m);
      font-size:13px;
      flex:0 0 auto;
      background: rgba(0,0,0,.18);
      backdrop-filter: blur(10px);
    }
    .hud .left{display:flex; align-items:center; gap:10px; min-width:0;}
    .hud i{width:16px;height:16px;opacity:.9}

    .row{
      display:flex;
      gap:10px;
      flex-wrap:wrap;
      margin-top:12px;
      position:relative; z-index:1;
      flex:0 0 auto;
    }

    button{
      border:1px solid rgba(255,255,255,.16);
      padding:14px 14px;
      border-radius: 18px;
      font-weight:950;
      letter-spacing:.08em;
      cursor:pointer;
      min-height: var(--tap);
      background: rgba(255,255,255,.06);
      color: var(--w);
      transition: transform .12s ease, background .12s ease, border-color .12s ease, opacity .12s ease;
      user-select:none;
      -webkit-tap-highlight-color: transparent;
      flex:1;
      display:flex;
      align-items:center;
      justify-content:center;
      gap:10px;
    }
    button i{width:18px;height:18px;opacity:.92}
    button:hover{ background: rgba(255,255,255,.085); border-color: rgba(255,255,255,.22); }
    button:active{ transform: translateY(1px) scale(.995); }

    button.primary{
      background: linear-gradient(180deg, rgba(255,255,255,.98), rgba(255,255,255,.88));
      color: #0f1114;
      border: 1px solid rgba(255,255,255,.35);
      box-shadow: 0 14px 26px rgba(0,0,0,.35);
    }
    button.primary:hover{
      background: linear-gradient(180deg, rgba(255,255,255,1), rgba(255,255,255,.92));
    }
    button:disabled{opacity:.45;cursor:not-allowed}

    .pill{
      display:flex;
      justify-content:space-between;
      align-items:center;
      padding:12px 14px;
      border-radius: 18px;
      background: rgba(0,0,0,.18);
      border:1px solid rgba(255,255,255,.10);
      margin-top:12px;
      font-size:13px;
      color:rgba(255,255,255,.82);
      position:relative; z-index:1;
      flex:0 0 auto;
    }
    .pill .left{
      display:flex;align-items:center;gap:10px;
      font-weight:900;letter-spacing:.08em;text-transform:uppercase;
      font-size:12px;color:rgba(255,255,255,.82)
    }
    .pill .left i{width:16px;height:16px;opacity:.9}
    .ok{color:var(--ok);font-weight:950}
    .bad{color:var(--bad);font-weight:950}

    .tiny{
      font-size:12px;
      opacity:.80;
      line-height:1.45;
      margin-top:10px;
      position:relative; z-index:1;
      flex:0 0 auto;
    }
    .err{
      color:var(--bad);
      display:none;
    }

    @media (min-width: 520px){
      .screen{ padding-left:16px; padding-right:16px; }
      .card{ padding:16px; }
      .top{ padding:14px 14px; }
    }
  </style>
</head>
<body>
  <div class="screen">
    <div class="app">

      <div class="top">
        <div class="brand">
          <i data-lucide="qr-code"></i>
          <div style="min-width:0">
            <div class="ttl">QR AKTİFLEŞTİR</div>
            <div class="subttl">Tahta kilidini uygulama gibi tek adımda aç</div>
          </div>
        </div>

        <div class="nav">
          <a class="link" href="<?= htmlspecialchars($base, ENT_QUOTES) ?>/controller.php">
            <i data-lucide="sliders-horizontal"></i>
            Kontrol
          </a>
        </div>
      </div>

      <div class="card" id="card">
        <div class="label"><i data-lucide="unlock"></i>Tahta Kilidini Aç</div>
        <div class="big">Tahtadaki QR’ı okut</div>
        <div class="sub">
          QR içeriği <span class="mono">unlock=1</span> (kilit açma) veya <span class="mono">pair=1</span> (eşleştirme) olabilir.
          Sistem otomatik tanır ve doğru adımı uygular.
        </div>

        <div class="notice" id="httpsNote">
          <b>HTTPS gerekli:</b> Kamera sadece SSL (https) ile açılır. Siteyi https üzerinden çalıştır.
        </div>

        <div class="scanStage" id="scanStage">
          <div id="qrReader"></div>
          <div class="hud">
            <div class="left">
              <i data-lucide="camera"></i>
              <span id="hud">Kamera hazırlanıyor…</span>
            </div>
            <span id="miniState" style="font-weight:900;letter-spacing:.08em;text-transform:uppercase;font-size:11px;opacity:.78">AUTO</span>
          </div>
        </div>

        <div class="row" id="rowBtns">
          <button id="startBtn" class="primary">
            <i data-lucide="camera"></i>
            TEKRAR DENE
          </button>
          <button id="stopBtn" disabled>
            <i data-lucide="square"></i>
            DURDUR
          </button>
        </div>

        <div class="pill" id="pillState">
          <div class="left"><i data-lucide="shield"></i>Durum</div>
          <div id="state" class="bad">Hazır</div>
        </div>

        <div id="info" class="tiny"></div>
        <div id="err" class="tiny err"></div>
      </div>

      <?php // include __DIR__ . '/sidebar.php'; ?>

    </div>
  </div>

<script>
(function(){
  const BASE = <?= json_encode($base) ?>;

  const hud   = document.getElementById('hud');
  const miniState = document.getElementById('miniState');
  const state = document.getElementById('state');
  const info  = document.getElementById('info');
  const err   = document.getElementById('err');
  const httpsNote = document.getElementById('httpsNote');

  const startBtn = document.getElementById('startBtn');
  const stopBtn  = document.getElementById('stopBtn');

  const card = document.getElementById('card');
  const scanStage = document.getElementById('scanStage');
  const rowBtns = document.getElementById('rowBtns');
  const pillState = document.getElementById('pillState');

  let qr = null;
  let running = false;

  function iconsRefresh(){
    try{
      if (window.lucide && typeof window.lucide.createIcons === 'function') {
        window.lucide.createIcons();
      }
    }catch(e){}
  }

  function setErr(msg){
    err.style.display = msg ? 'block' : 'none';
    err.textContent = msg || '';
  }

  function px(n){ return Number.isFinite(n) ? n : 0; }
  function outerH(el){
    if(!el) return 0;
    const s = getComputedStyle(el);
    return el.offsetHeight + px(parseFloat(s.marginTop)) + px(parseFloat(s.marginBottom));
  }

  function fitScanStage(){
    if(!card || !scanStage) return;

    const label = card.querySelector('.label');
    const big   = card.querySelector('.big');
    const sub   = card.querySelector('.sub');
    const note  = document.getElementById('httpsNote');

    const cardStyles = getComputedStyle(card);
    const padTop = px(parseFloat(cardStyles.paddingTop));
    const padBottom = px(parseFloat(cardStyles.paddingBottom));

    const cardTop = card.getBoundingClientRect().top;

    const below = outerH(rowBtns) + outerH(pillState) + outerH(info) + outerH(err);

    const above =
      padTop +
      outerH(label) +
      outerH(big) +
      outerH(sub) +
      (note && note.style.display !== 'none' ? outerH(note) : 0) +
      12;

    const vh = window.innerHeight;
    const available = vh - cardTop - above - below - padBottom - 12;

    const target = Math.max(320, Math.floor(available));
    scanStage.style.height = target + 'px';

    enforceSquareScanRegion();
  }

  function enforceSquareScanRegion(){
    const reader = document.getElementById('qrReader');
    const region = document.getElementById('qrReader__scan_region');
    if(!reader || !region) return;

    const rect = reader.getBoundingClientRect();
    const size = Math.max(220, Math.floor(Math.min(rect.width, rect.height)));

    region.style.width  = size + 'px';
    region.style.height = size + 'px';
    region.style.margin = '0 auto';
    region.style.borderRadius = '16px';
    region.style.overflow = 'hidden';
  }

  async function fetchJson(url, opts){
    const res = await fetch(url, Object.assign({
      cache:'no-store',
      credentials:'same-origin'
    }, opts||{}));

    const text = await res.text();
    try { return JSON.parse(text); }
    catch(e){ throw new Error(`JSON değil (404/redirect olabilir). URL=${url}\n${text.slice(0,240)}`); }
  }

  async function post(action, data){
    const form = new URLSearchParams(data||{});
    return await fetchJson(`${BASE}/api.php?action=${encodeURIComponent(action)}`,{
      method:'POST',
      headers:{'Content-Type':'application/x-www-form-urlencoded'},
      body: form.toString()
    });
  }

  // ✅ qr.php?d=... sarmalını çöz (1-2 kere decode)
  function unwrapIfQrPhp(urlStr){
    try{
      const u = new URL(urlStr, location.origin);
      const path = (u.pathname || '').toLowerCase();
      if (!path.endsWith('/qr.php')) return urlStr;

      const d = u.searchParams.get('d');
      if(!d) return urlStr;

      let inner = d;
      try { inner = decodeURIComponent(inner); } catch(_){}
      // bazen iki kere encode edilmiş geliyor
      if (/%[0-9A-Fa-f]{2}/.test(inner)) {
        try { inner = decodeURIComponent(inner); } catch(_){}
      }
      return inner.trim() || urlStr;
    }catch(e){
      return urlStr;
    }
  }

  // ✅ Hem unlock hem pair URL'lerini tanı
  function parseQrPayload(text){
    let t = (text||'').trim();
    if(!t) return null;

    // 1) Eğer qr.php?d=... ise içeriği çıkar
    t = unwrapIfQrPhp(t);

    // 2) URL parse et
    let u = null;
    try { u = new URL(t, location.origin); }
    catch(e){
      // bazen sadece query geliyor
      if (t.includes('board=')) {
        try { u = new URL(location.origin + '/x?' + t.replace(/^\?/, '')); }
        catch(_){ return null; }
      } else {
        return null;
      }
    }

    const board = (u.searchParams.get('board')||'').trim();
    if(!board) return null;

    const isUnlock = (u.searchParams.get('unlock')||'') === '1';
    const isPair   = (u.searchParams.get('pair')||'') === '1';

    if (isUnlock) {
      const code  = (u.searchParams.get('code') || u.searchParams.get('unlock_code') || '').trim();
      const nonce = (u.searchParams.get('nonce')||'').trim();
      if(!code) return null; // unlock için code şart
      return { type:'unlock', board, code, nonce, raw:u.toString() };
    }

    if (isPair) {
      const nonce = (u.searchParams.get('nonce')||'').trim();
      if(!nonce) return null; // pair için nonce şart
      return { type:'pair', board, nonce, raw:u.toString() };
    }

    // Eski formatlar (olursa)
    const codeFallback = (u.searchParams.get('code') || u.searchParams.get('unlock_code') || '').trim();
    if(codeFallback) return { type:'unlock', board, code: codeFallback, nonce:(u.searchParams.get('nonce')||'').trim(), raw:u.toString() };

    return null;
  }

  async function activateUnlock(payload){
    setErr('');
    state.textContent = 'İşleniyor…';
    state.className = 'bad';
    hud.textContent = 'Kilit açılıyor…';

    info.innerHTML = `
      <div><b>İşlem:</b> <span class="mono">UNLOCK</span></div>
      <div><b>Tahta:</b> <span class="mono">${payload.board}</span></div>
      <div><b>Kod:</b> <span class="mono">${payload.code}</span></div>
    `;

    const req = { board: payload.board, unlock_code: payload.code };
    // nonce boş da gelebilir, varsa gönder
    if (payload.nonce) req.nonce = payload.nonce;

    const j = await post('unfreeze', req);
    if(!j.ok){
      const e = j.error || 'unfreeze ok=false';
      if (e === 'unlock_required' || e === 'unlock_code_invalid') {
        throw new Error('Güvenlik kodu geçersiz / süresi dolmuş. Tahtadaki QR/kod yenilenmiş olabilir.');
      }
      throw new Error(e);
    }

    state.textContent = 'AÇIK ✅';
    state.className = 'ok';
    hud.textContent = 'Tahta aktifleştirildi ✅';

    setTimeout(()=>{ window.location.href = `${BASE}/controller.php`; }, 650);
  }

  function goPair(payload){
    setErr('');
    state.textContent = 'Yönlendiriliyor…';
    state.className = 'ok';
    hud.textContent = 'Eşleştirme sayfası açılıyor…';

    info.innerHTML = `
      <div><b>İşlem:</b> <span class="mono">PAIR</span></div>
      <div><b>Tahta:</b> <span class="mono">${payload.board}</span></div>
      <div><b>Nonce:</b> <span class="mono">${payload.nonce}</span></div>
    `;

    const url = `${BASE}/controller.php?pair=1&board=${encodeURIComponent(payload.board)}&nonce=${encodeURIComponent(payload.nonce)}`;
    setTimeout(()=>{ window.location.href = url; }, 350);
  }

  async function start(auto=false){
    setErr('');
    if (running) return;

    fitScanStage();

    if (!window.isSecureContext && location.hostname !== 'localhost' && location.hostname !== '127.0.0.1') {
      httpsNote.style.display = 'block';
      fitScanStage();

      hud.textContent = 'HTTPS gerekli (SSL).';
      state.textContent = 'HATA';
      state.className = 'bad';
      miniState.textContent = 'SSL';
      setErr('Kamera için HTTPS gerekli. Siteyi SSL (https) ile aç.');
      return;
    } else {
      httpsNote.style.display = 'none';
      fitScanStage();
    }

    if (!window.Html5Qrcode) {
      hud.textContent = 'QR modülü yok.';
      miniState.textContent = 'MODÜL';
      setErr('QR modülü bulunamadı. /qr/minified/html5-qrcode.min.js yolunu kontrol et.');
      return;
    }

    hud.textContent = auto ? 'Kamera otomatik açılıyor…' : 'Kamera hazırlanıyor…';
    miniState.textContent = 'AUTO';

    try{
      if(!qr) qr = new Html5Qrcode("qrReader");

      startBtn.disabled = true;
      stopBtn.disabled  = false;

      const config = {
        fps: 12,
        qrbox: (w, h) => {
          const s = Math.max(220, Math.floor(Math.min(w, h) * 0.55));
          return { width: s, height: s };
        },
        rememberLastUsedCamera: true
      };

      const onSuccess = async (decodedText) => {
        const payload = parseQrPayload(decodedText);

        if(!payload){
          hud.textContent = 'QR okundu ama format tanınmadı.';
          state.textContent = 'FORMAT';
          state.className = 'bad';
          // Debug görmek istersen:
          info.innerHTML = `<div><b>Okunan:</b> <span class="mono">${String(decodedText).slice(0,220)}</span></div>`;
          return;
        }

        hud.textContent = 'QR okundu ✅';
        await stop();

        try{
          if (payload.type === 'pair') {
            goPair(payload);
          } else {
            await activateUnlock(payload);
          }
        }catch(e){
          setErr(e && e.message ? e.message : String(e));
          state.textContent = 'HATA';
          state.className = 'bad';
          hud.textContent = 'İşlem başarısız.';
        }
      };

      const onFail = () => {};

      try{
        await qr.start({ facingMode: "environment" }, config, onSuccess, onFail);
      }catch(e1){
        let cams = [];
        try { cams = await Html5Qrcode.getCameras(); } catch(_){ cams = []; }

        const pick =
          cams.find(c => /back|rear|environment|arka|main/i.test((c.label||''))) ||
          cams[cams.length - 1] || cams[0];

        if(!pick) throw e1;
        await qr.start({ deviceId: { exact: pick.id } }, config, onSuccess, onFail);
      }

      running = true;
      hud.textContent = 'QR hedefe gelince otomatik okunacak…';
      state.textContent = 'Tarama açık';
      state.className = 'ok';
      miniState.textContent = 'ON';

      setTimeout(enforceSquareScanRegion, 60);
      setTimeout(enforceSquareScanRegion, 140);
      setTimeout(enforceSquareScanRegion, 260);
      setTimeout(fitScanStage, 260);

    }catch(e){
      running = false;
      startBtn.disabled = false;
      stopBtn.disabled  = true;

      const name = (e && (e.name || (e.constructor && e.constructor.name))) ? (e.name || e.constructor.name) : '';
      const msg  = e && e.message ? e.message : String(e);
      const blob = (name + ' ' + msg);

      let human = 'Kamera açılamadı.';
      if (/NotAllowedError|PermissionDeniedError/i.test(blob)) human = 'Kamera izni reddedildi. Tarayıcıdan izin ver.';
      else if (/NotFoundError|DevicesNotFoundError/i.test(blob)) human = 'Kamera bulunamadı.';
      else if (/NotReadableError|TrackStartError/i.test(blob)) human = 'Kamera başka uygulama tarafından kullanılıyor.';
      else if (/OverconstrainedError/i.test(blob)) human = 'Kamera kısıtı desteklenmiyor.';

      setErr(`${human} | ${name} | ${msg}`);
      hud.textContent = human;
      state.textContent = 'HATA';
      state.className = 'bad';
      miniState.textContent = 'ERR';
    }
  }

  async function stop(){
    try{
      if(qr && running){
        await qr.stop();
        await qr.clear();
      }
    }catch(e){}
    running = false;
    startBtn.disabled = false;
    stopBtn.disabled  = true;

    if (state.textContent === 'Tarama açık') {
      state.textContent = 'Hazır';
      state.className = 'bad';
      miniState.textContent = 'OFF';
    }
  }

  startBtn.addEventListener('click', ()=>start(false));
  stopBtn.addEventListener('click', stop);

  function bootAutoStart(){
    let tries = 0;
    const t = setInterval(()=>{
      tries++;
      if (window.Html5Qrcode) {
        clearInterval(t);
        start(true);
      } else if (tries > 80) {
        clearInterval(t);
        setErr('QR modülü yüklenemedi. /qr/minified/html5-qrcode.min.js yolunu kontrol et.');
        hud.textContent = 'QR modülü yüklenemedi.';
        miniState.textContent = 'MODÜL';
      }
    }, 50);
  }

  function relayout(){
    fitScanStage();
    requestAnimationFrame(fitScanStage);
    setTimeout(fitScanStage, 120);

    setTimeout(enforceSquareScanRegion, 60);
    setTimeout(enforceSquareScanRegion, 140);
  }

  window.addEventListener('resize', relayout, {passive:true});
  window.addEventListener('orientationchange', ()=>setTimeout(relayout, 200), {passive:true});

  iconsRefresh();
  setTimeout(iconsRefresh, 120);

  relayout();
  bootAutoStart();
})();
</script>

</body>
</html>
