/* APP SHELL */

/* ---------- PERFORMANCE TIER DETECTION ----------
   Runs synchronously when app.jsx loads (after all other scripts, before React mounts).
   Sets document.body.dataset.perf = 'high' | 'low'.
   Components (CursorFX, Scutoid3D) read this on mount and degrade gracefully.
   CSS rules under `body[data-perf="low"]` pause non-essential animations.

   Two-stage approach:
   1) Synchronous heuristic from cheap browser signals (cores, optional
      deviceMemory, saveData, touch+narrow, software WebGL renderer).
      Picks the right tier BEFORE React mounts — avoids the flash of
      heavy hero on Safari/Mac that the previous all-or-nothing detector caused.
   2) Runtime FPS probe over the first ~1.5s of paint. If the page is
      already on a weak machine that slipped past stage 1 (e.g. budget
      Windows desktop with discrete-looking GPU strings but actually
      throttled), we downgrade live and tear down WebGL.

   Downgrade is one-way: low never flips back to high. Tearing down a
   running scene + remounting CSS animations would flash worse than just
   staying on the heavy version. */
function downgradeToLow() {
  if (document.body.dataset.perf === 'low') return;
  document.body.dataset.perf = 'low';
  document.body.classList.add('native-cursor');
  // Notify mounted components (Scutoid3D) so they can release WebGL.
  try { window.dispatchEvent(new CustomEvent('perfdowngrade')); } catch (_) {}
}

function detectSoftwareGpu() {
  try {
    const canvas = document.createElement('canvas');
    const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
    if (!gl) return true; // no WebGL at all — definitely low
    const ext = gl.getExtension('WEBGL_debug_renderer_info');
    if (!ext) return false;
    const renderer = String(gl.getParameter(ext.UNMASKED_RENDERER_WEBGL) || '');
    // Known software / fallback renderers
    return /SwiftShader|llvmpipe|Microsoft Basic Render|software/i.test(renderer);
  } catch (_) { return false; }
}

function startFpsProbe() {
  // Skip probe if we're already low — nothing to downgrade further.
  if (document.body.dataset.perf === 'low') return;
  let frames = 0;
  const start = performance.now();
  const DURATION = 1500;
  const MIN_FPS = 35;
  const tick = () => {
    frames++;
    const elapsed = performance.now() - start;
    if (elapsed >= DURATION) {
      const fps = (frames * 1000) / elapsed;
      if (fps < MIN_FPS) downgradeToLow();
      return;
    }
    requestAnimationFrame(tick);
  };
  requestAnimationFrame(tick);
}

(function setPerfTier() {
  if (typeof document === 'undefined' || !document.body) return;
  try {
    // URL override for manual testing: /?perf=low or /?perf=high
    const urlOverride = new URLSearchParams(location.search).get('perf');
    if (urlOverride === 'low' || urlOverride === 'high') {
      document.body.dataset.perf = urlOverride;
      if (urlOverride === 'low') document.body.classList.add('native-cursor');
      return;
    }
    const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    // Chrome/Edge expose deviceMemory; Safari/Firefox don't. Use the signal ONLY
    // when present, so absence never lowers the tier (that bug penalized Safari).
    const mem = typeof navigator.deviceMemory === 'number' ? navigator.deviceMemory : null;
    const cores = navigator.hardwareConcurrency || 8;
    const saveData = !!(navigator.connection && navigator.connection.saveData);
    const coarse = window.matchMedia('(hover: none) and (pointer: coarse)').matches;
    const narrow = window.matchMedia('(max-width: 768px)').matches;
    const softGpu = detectSoftwareGpu();

    const low =
      reduced ||
      cores <= 2 ||
      (mem !== null && mem <= 4) ||
      saveData ||
      softGpu ||
      (coarse && narrow);

    document.body.dataset.perf = low ? 'low' : 'high';
    if (low) document.body.classList.add('native-cursor');
    else {
      // Stage 2: dynamic downgrade if real-world FPS turns out poor.
      // Defer until after the first frames so we measure steady-state.
      requestAnimationFrame(() => requestAnimationFrame(startFpsProbe));
    }
  } catch (_) {
    document.body.dataset.perf = 'high';
  }
})();

function Services() {
  const items = [
    { ico: 'web', t: 'Web‑платформы', d: 'Продуктовые SaaS, кабинеты, маркетплейсы. Next.js, Node, Go, PostgreSQL.' },
    { ico: 'mob', t: 'Mobile', d: 'iOS / Android / React Native. Real‑time, offline‑first, мобильный AI.' },
    { ico: 'ai',  t: 'AI инжиниринг', d: 'LLM‑агенты, RAG‑поиск, прод‑классификаторы. Eval‑first процесс.' },
    { ico: 'des', t: 'Дизайн и&nbsp;UX', d: 'Брендинг, продуктовый UX, моушн, дизайн‑системы для продуктовых команд.' },
  ];
  const Ico = ({ k }) => {
    const common = { width:22, height:22, viewBox:'0 0 24 24', fill:'none', stroke:'currentColor', strokeWidth:1.6, strokeLinecap:'round', strokeLinejoin:'round' };
    if (k === 'web') return (<svg {...common}><rect x="3" y="4" width="18" height="14" rx="2"/><path d="M3 9h18M8 14h2M8 4v5"/></svg>);
    if (k === 'mob') return (<svg {...common}><rect x="7" y="3" width="10" height="18" rx="2"/><path d="M11 18h2"/></svg>);
    if (k === 'ai')  return (<svg {...common}><path d="M12 3v3M12 18v3M3 12h3M18 12h3M5.6 5.6l2.1 2.1M16.3 16.3l2.1 2.1M5.6 18.4l2.1-2.1M16.3 7.7l2.1-2.1"/><circle cx="12" cy="12" r="3"/></svg>);
    if (k === 'des') return (<svg {...common}><circle cx="13.5" cy="6.5" r="2.5"/><circle cx="17.5" cy="10.5" r="2.5"/><circle cx="9.5" cy="10.5" r="2.5"/><circle cx="13.5" cy="14.5" r="2.5"/><path d="M5 19h12"/></svg>);
  };
  const refs = React.useRef([]);
  React.useEffect(() => {
    const cleanups = refs.current.map(el => {
      if (!el) return null;
      const onMove = (e) => {
        const r = el.getBoundingClientRect();
        el.style.setProperty('--lx', ((e.clientX - r.left)/r.width*100) + '%');
        el.style.setProperty('--ly', ((e.clientY - r.top)/r.height*100) + '%');
      };
      el.addEventListener('mousemove', onMove);
      return () => el.removeEventListener('mousemove', onMove);
    });
    return () => cleanups.forEach(c => c && c());
  }, []);
  return (
    <section className="section container" id="services">
      <div className="section-head">
        <h2>Что мы делаем. <span className="italic">Полный цикл.</span></h2>
        <div className="num">01 / УСЛУГИ</div>
      </div>
      <div className="services">
        {items.map((s, i) => (
          <div className="svc" key={i} ref={el => refs.current[i] = el}>
            <div className="ico"><Ico k={s.ico}/></div>
            <h3 dangerouslySetInnerHTML={{__html: s.t}}/>
            <p dangerouslySetInnerHTML={{__html: s.d}}/>
            <div className="index">/ 0{i+1}</div>
          </div>
        ))}
      </div>
    </section>
  );
}

function Process() {
  const steps = [
    { n: '01', t: 'Брифинг и оценка',     d: 'Созвон 60 минут, технический бриф, состав команды, оценка. NDA до&nbsp;созвона&nbsp;— по&nbsp;запросу.', dur: '3–5 раб. дней' },
    { n: '02', t: 'UX и дизайн',          d: 'Карта сценариев, прототипы, дизайн‑система. Утверждаем визуал и поведение до&nbsp;кода.', dur: '2–4 недели' },
    { n: '03', t: 'Архитектура и спринты', d: 'Двухнедельные спринты, демо в&nbsp;конце каждого. CI/CD с&nbsp;первого дня, код‑ревью, автотесты.', dur: '6–14 недель' },
    { n: '04', t: 'QA и релиз',           d: 'Автоматическое и ручное QA, нагрузочные сценарии, выкладка в&nbsp;App&nbsp;Store, Google&nbsp;Play или прод.', dur: '1–3 недели' },
  ];
  return (
    <section className="section container" id="process">
      <div className="section-head">
        <h2>Процесс. <span className="italic">От первого созвона до&nbsp;релиза.</span></h2>
        <div className="num">03 / ПРОЦЕСС</div>
      </div>
      <div className="process">
        {steps.map((s, i) => (
          <div className="step" key={i}>
            <div className="num">{s.n}</div>
            <h4>{s.t}</h4>
            <p dangerouslySetInnerHTML={{__html: s.d}}/>
            <div className="dur">{s.dur}</div>
          </div>
        ))}
      </div>
    </section>
  );
}

function Marquee() {
  const items = ['React · Next.js', 'Node · Go · Rust', 'Postgres · ClickHouse', 'AWS · GCP · K8s', 'LLM · RAG · Vector', 'Swift · Kotlin', 'Tailwind · Motion', 'WebGL · Three.js', 'TypeScript', 'gRPC · GraphQL'];
  const sep = <span className="sep">◆</span>;
  const renderItems = (key) => items.map((t, i) => <React.Fragment key={key + i}>{sep}<span>{t}</span></React.Fragment>);
  return (
    <div className="marquee">
      <div className="marquee-track">
        <span>{renderItems('a')}</span>
        <span>{renderItems('b')}</span>
      </div>
    </div>
  );
}

function Nav() {
  const [open, setOpen] = React.useState(false);
  const close = () => setOpen(false);

  React.useEffect(() => {
    const onResize = () => { if (window.innerWidth > 900) setOpen(false); };
    const onKey = (e) => { if (e.key === 'Escape') setOpen(false); };
    window.addEventListener('resize', onResize);
    window.addEventListener('keydown', onKey);
    return () => {
      window.removeEventListener('resize', onResize);
      window.removeEventListener('keydown', onKey);
    };
  }, []);

  React.useEffect(() => {
    document.body.style.overflow = open ? 'hidden' : '';
    return () => { document.body.style.overflow = ''; };
  }, [open]);

  const links = [
    ['#services', 'Услуги'],
    ['#cases', 'Кейсы'],
    ['#pricing', 'Цены'],
    ['#faq', 'FAQ'],
    ['#contact', 'Контакт'],
  ];

  return (
    <React.Fragment>
      <nav className={"nav " + (open ? "is-open" : "")}>
        <a href="#top" className="nav-logo" onClick={close}>
          <span className="mark"><img src="assets/scutoid.png" alt="D-one studio" width="28" height="28" decoding="async"/></span>
          <span>D-one <span style={{color:'var(--ink-2)'}}>studio</span></span>
        </a>
        <div className="nav-links">
          {links.map(([href, label]) => <a key={href} href={href}>{label}</a>)}
        </div>
        <a href="#contact" className="nav-cta">
          <span style={{width:6, height:6, borderRadius:'50%', background:'#46f3a8', boxShadow:'0 0 8px #46f3a8'}}></span>
          Запустить проект
        </a>
        <button
          className="nav-burger"
          onClick={() => setOpen(o => !o)}
          aria-label={open ? 'Закрыть меню' : 'Открыть меню'}
          aria-expanded={open}
        >
          <span></span><span></span><span></span>
        </button>
      </nav>

      <div className={"mobile-menu " + (open ? "open" : "")} onClick={(e) => { if (e.target === e.currentTarget) close(); }}>
        <div className="mobile-menu-panel">
          {links.map(([href, label]) => (
            <a key={href} href={href} onClick={close}>{label}</a>
          ))}
          <a href="#contact" className="mobile-cta" onClick={close}>
            <span style={{width:6, height:6, borderRadius:'50%', background:'#46f3a8', boxShadow:'0 0 8px #46f3a8'}}></span>
            Запустить проект
          </a>
        </div>
      </div>
    </React.Fragment>
  );
}

function Footer() {
  return (
    <footer className="footer container">
      <div className="footer-grid">
        <div>
          <div className="nav-logo" style={{marginBottom: 14}}>
            <span className="mark"><img src="assets/scutoid.png" alt="D-one studio" width="28" height="28" loading="lazy" decoding="async"/></span>
            <span style={{fontSize:18}}>D-one <span style={{color:'var(--ink-2)'}}>studio</span></span>
          </div>
          <p style={{color:'var(--ink-1)', fontSize:14, maxWidth: 320, lineHeight: 1.55}}>
            Продуктовая инженерная студия. Помогаем командам запускать сложные цифровые продукты.
          </p>
        </div>
        <div>
          <h5>Контакт</h5>
          <a href="mailto:ipavmakeev@yandex.ru">ipavmakeev@yandex.ru</a>
          <a href="https://t.me/skuzhi" target="_blank" rel="noopener noreferrer">@skuzhi</a>
          <a href="tel:+79688968887">+7 (968) 896-88-87</a>
        </div>
        <div>
          <h5>Студия</h5>
          <a href="#cases">Кейсы</a>
          <a href="#process">Процесс</a>
          <a href="#contact">Связаться</a>
        </div>
        <div>
          <h5>Прочее</h5>
          <a href="/blog">Инженерный блог</a>
          <a href="/admin">Админка</a>
          <a href="#">Карьера</a>
          <a href="#">Privacy</a>
        </div>
      </div>
      <div className="footer-bottom">
        <span>© 2026 D-one studio. Все права защищены.</span>
        <span>v.4.2 — fra‑02 — uptime 99.98%</span>
      </div>
    </footer>
  );
}

function App() {
  React.useEffect(() => {
    // scroll reveal
    const io = new IntersectionObserver((entries) => {
      entries.forEach(e => {
        if (e.isIntersecting) {
          e.target.classList.add('in');
          io.unobserve(e.target);
        }
      });
    }, { threshold: 0.12, rootMargin: '0px 0px -8% 0px' });
    document.querySelectorAll('.section, .marquee').forEach(el => {
      el.classList.add('fade-up');
      io.observe(el);
    });

    // magnetic buttons
    const buttons = document.querySelectorAll('[data-magnetic]');
    const cleanups = [];
    buttons.forEach(btn => {
      const onMove = (e) => {
        const r = btn.getBoundingClientRect();
        const dx = (e.clientX - (r.left + r.width/2)) * 0.15;
        const dy = (e.clientY - (r.top + r.height/2)) * 0.15;
        btn.style.transform = `translate(${dx}px, ${dy}px)`;
      };
      const onLeave = () => { btn.style.transform = ''; };
      btn.addEventListener('mousemove', onMove);
      btn.addEventListener('mouseleave', onLeave);
      cleanups.push(() => { btn.removeEventListener('mousemove', onMove); btn.removeEventListener('mouseleave', onLeave); });
    });

    // keyboard shortcut to jump to /admin
    const onKey = (e) => {
      if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key.toLowerCase() === 'a') {
        e.preventDefault();
        window.location.href = '/admin';
      }
    };
    window.addEventListener('keydown', onKey);

    return () => {
      cleanups.forEach(c => c());
      window.removeEventListener('keydown', onKey);
    };
  }, []);

  return (
    <React.Fragment>
      <div className="bg-stage"/>
      <div className="bg-grid"/>
      <div className="bg-noise"/>
      <CursorFX/>

      <div className="site" id="top" data-screen-label="01 Сайт">
        <Nav/>
        <Hero/>
        <Marquee/>
        <Services/>
        <Cases/>
        <Process/>
        <Engagement/>
        <Pricing/>
        {/* <Team/> — temporarily hidden, still editable in /admin */}
        <FAQ/>
        <Contact/>
        <Footer/>
      </div>
    </React.Fragment>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
