templates/index.html.twig line 1

Open in your IDE?
  1. <!doctype html>
  2. <html lang="ru">
  3.   <head>
  4.     <meta charset="UTF-8" />
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
  6.     <title>PlayPapa</title>
  7.     <script>
  8.       (() => {
  9.         window.__preloaderStart = performance.now();
  10.         try {
  11.           const requestedTheme = new URLSearchParams(window.location.search).get("theme");
  12.           const savedTheme = localStorage.getItem("playpapa-theme");
  13.           const theme = requestedTheme || savedTheme;
  14.           document.documentElement.dataset.theme = theme === "dark" ? "dark" : "light";
  15.         } catch {
  16.           document.documentElement.dataset.theme = "light";
  17.         }
  18.         try {
  19.           if ("scrollRestoration" in history) history.scrollRestoration = "manual";
  20.           if (!window.location.hash || window.location.hash === "#") {
  21.             window.addEventListener("load", () => requestAnimationFrame(() => window.scrollTo(0, 0)), { once: true });
  22.           }
  23.         } catch {}
  24.       })();
  25.     </script>
  26.     <script>
  27.       window.playpapaCatalog = {{ catalog_payload_json|default('{"products":[],"total":0}')|raw }};
  28.     </script>
  29.     <script>
  30.       tailwind = {
  31.         config: {
  32.           theme: {
  33.             extend: {
  34.               colors: {
  35.                 papa: "#268bf3",
  36.                 ink: "#202838",
  37.                 soft: "#f2f4f8",
  38.               },
  39.             },
  40.           },
  41.         },
  42.       };
  43.     </script>
  44.     <script src="https://cdn.tailwindcss.com" defer></script>
  45.     <link rel="preconnect" href="https://fonts.googleapis.com" />
  46.     <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
  47.     <link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@500;600;700;800;900&display=swap" rel="stylesheet" />
  48.     <link rel="icon" type="image/svg+xml" href="./assets/blogger-playpapa-logo.svg" />
  49.     <link rel="stylesheet" href="./frame.css" />
  50.     <link rel="stylesheet" href="./styles.css" />
  51.     <link rel="stylesheet" href="/auth-local.css" />
  52.   </head>
  53.   <body class="preloader-active">
  54.     <div class="page-preloader" id="pagePreloader" role="status" aria-label="Загрузка">
  55.       <div class="preloader-inner">
  56.         <div class="preloader-dots" aria-hidden="true">
  57.           <span></span>
  58.           <span></span>
  59.           <span></span>
  60.         </div>
  61.         <div class="preloader-text">Загрузка...</div>
  62.       </div>
  63.     </div>
  64.     <header class="site-header">
  65.       <div class="shell header-inner">
  66.         <a class="logo-link" href="/">
  67.           <img src="./assets/logo.svg" alt="PlayPapa" />
  68.         </a>
  69.         <div class="theme-switch" aria-label="Переключение темы">
  70.           <button class="theme-btn is-active" type="button" data-theme="light" aria-label="Светлая тема" aria-pressed="true">
  71.             <img src="./assets/white-theme.svg" alt="" />
  72.           </button>
  73.           <button class="theme-btn" type="button" data-theme="dark" aria-label="Темная тема" aria-pressed="false">
  74.             <img src="./assets/dark-theme.svg" alt="" />
  75.           </button>
  76.         </div>
  77.         <button class="catalog-btn" type="button" aria-label="Каталог" aria-expanded="false" aria-controls="catalogMegaMenu">
  78.           <img src="./assets/catalog.svg" alt="" />
  79.           <span>Каталог</span>
  80.         </button>
  81.         <form class="search-form" action="#">
  82.           <input type="search" placeholder="Игра, приложение или услуга..." />
  83.           <button class="search-favorite" type="button" aria-label="Избранное">
  84.             <img src="./assets/favorite.svg" alt="" />
  85.           </button>
  86.           <button class="search-submit" type="submit" aria-label="Найти">
  87.             <svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
  88.               <path d="m21 21-4.35-4.35m2.35-5.65a8 8 0 1 1-16 0 8 8 0 0 1 16 0Z" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" />
  89.             </svg>
  90.           </button>
  91.         </form>
  92.         <button class="bonus-btn" aria-label="Бонусы">
  93.           <img src="./assets/bonus.svg" alt="Бонусы" />
  94.         </button>
  95.         <div class="profile-menu">
  96.           <button class="profile-btn" type="button" aria-label="Профиль" aria-expanded="false" aria-controls="profilePopover">
  97.             <img src="./assets/profile.svg" alt="" />
  98.             <span class="profile-toggle-indicator" aria-hidden="true">
  99.               <svg viewBox="0 0 12 8" fill="none">
  100.                 <path d="m2 2 4 4 4-4" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" />
  101.               </svg>
  102.             </span>
  103.           </button>
  104.           <section class="profile-popover" id="profilePopover" aria-hidden="true" aria-label="Меню профиля" inert>
  105.             <div class="profile-popover-heading">
  106.               <h2>Вы не авторизованы</h2>
  107.               <p>
  108.                 <span>Войдите, чтобы видеть бонусы и</span>
  109.                 <span>рефералов</span>
  110.               </p>
  111.             </div>
  112.             <nav class="profile-popover-nav" aria-label="Разделы профиля">
  113.               <button class="profile-menu-item" type="button" disabled>
  114.                 <img class="profile-menu-icon" src="./assets/profile-menu-profile.svg" alt="" />
  115.                 <span>Профиль</span>
  116.               </button>
  117.               <button class="profile-menu-item" type="button" disabled>
  118.                 <img class="profile-menu-icon" src="./assets/profile-menu-referrals.svg" alt="" />
  119.                 <span>Рефералы</span>
  120.               </button>
  121.               <button class="profile-menu-item" type="button" disabled>
  122.                 <img class="profile-menu-icon" src="./assets/profile-menu-bonus.svg" alt="" />
  123.                 <span>Бонусы</span>
  124.               </button>
  125.               <button class="profile-menu-item" type="button" disabled>
  126.                 <img class="profile-menu-icon" src="./assets/profile-menu-topup.svg" alt="" />
  127.                 <span>Пополнить</span>
  128.               </button>
  129.               <button class="profile-menu-item profile-help" type="button">
  130.                 <img class="profile-menu-icon" src="./assets/profile-menu-help.svg" alt="" />
  131.                 <span>Помощь</span>
  132.               </button>
  133.               <button class="profile-menu-item profile-login" type="button" data-auth-open="login">
  134.                 <img class="profile-menu-icon" src="./assets/profile-menu-login.svg" alt="" />
  135.                 <span>Войти</span>
  136.               </button>
  137.             </nav>
  138.           </section>
  139.         </div>
  140.       </div>
  141.       <div class="catalog-layer" id="catalogMegaMenu" aria-hidden="true" inert>
  142.         <div class="shell catalog-mega" role="dialog" aria-label="Каталог товаров">
  143.           <aside class="catalog-sidebar" aria-label="Разделы каталога">
  144.             <button class="catalog-side-link is-active" type="button">
  145.               <svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M7.5 8.2h9a4.5 4.5 0 0 1 4.2 6.1l-1.1 2.8a2.2 2.2 0 0 1-3.7.7l-1.2-1.5H9.3l-1.2 1.5a2.2 2.2 0 0 1-3.7-.7l-1.1-2.8a4.5 4.5 0 0 1 4.2-6.1Z" stroke="currentColor" stroke-width="1.8"/><path d="M8 11v3m-1.5-1.5h3M16.5 11.7h.01m2 2h.01" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
  146.               <span>Игры и игровые сервисы</span>
  147.               <span class="catalog-chevron" aria-hidden="true">›</span>
  148.             </button>
  149.             <button class="catalog-side-link" type="button">
  150.               <svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="m12 2.8 8.1 5.5L12 21.2 3.9 8.3 12 2.8Z" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="m3.9 8.3 5.1.2 3-5.7 3 5.7 5.1-.2M9 8.5l3 12.7 3-12.7" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/></svg>
  151.               <span>Игровые ценности</span>
  152.               <span class="catalog-chevron" aria-hidden="true">›</span>
  153.             </button>
  154.             <button class="catalog-side-link" type="button">
  155.               <svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><rect x="6.2" y="2.5" width="11.6" height="19" rx="2.2" stroke="currentColor" stroke-width="1.8"/><path d="M10 5h4m-2 13.5h.01" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
  156.               <span>Мобильные игры</span>
  157.               <span class="catalog-chevron" aria-hidden="true">›</span>
  158.             </button>
  159.             <button class="catalog-side-link" type="button">
  160.               <svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><circle cx="12" cy="12" r="9" stroke="currentColor" stroke-width="1.8"/><path d="M3.4 12h17.2M12 3c2.2 2.5 3.3 5.5 3.3 9S14.2 18.5 12 21c-2.2-2.5-3.3-5.5-3.3-9S9.8 5.5 12 3Z" stroke="currentColor" stroke-width="1.5"/></svg>
  161.               <span>Сервисы и соцсети</span>
  162.               <span class="catalog-chevron" aria-hidden="true">›</span>
  163.             </button>
  164.             <button class="catalog-side-link" type="button">
  165.               <svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><rect x="3" y="4.2" width="18" height="12.3" rx="1.8" stroke="currentColor" stroke-width="1.8"/><path d="M8.5 20h7M12 16.5V20" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
  166.               <span>Программы</span>
  167.               <span class="catalog-chevron" aria-hidden="true">›</span>
  168.             </button>
  169.           </aside>
  170.           <div class="catalog-content">
  171.             <section class="catalog-showcase" aria-labelledby="catalogFeaturedTitle">
  172.               <h2 id="catalogFeaturedTitle">Игры и игровые сервисы <span aria-hidden="true">›</span></h2>
  173.               <div class="catalog-showcase-row" id="catalogFeatured"></div>
  174.             </section>
  175.             <section class="catalog-showcase catalog-new" aria-labelledby="catalogNewTitle">
  176.               <h2 id="catalogNewTitle">Новинки и предзаказы <span aria-hidden="true">›</span></h2>
  177.               <div class="catalog-showcase-row" id="catalogNew"></div>
  178.             </section>
  179.             <div class="catalog-platforms">
  180.               <section class="catalog-link-group">
  181.                 <h3>Steam <span aria-hidden="true">›</span></h3>
  182.                 <a href="#">Игры и DLC</a>
  183.                 <a href="#">Пополнение баланса</a>
  184.                 <a href="#">Подарочные карты</a>
  185.                 <a href="#">Коллекционные карточки</a>
  186.                 <a href="#">Смена региона</a>
  187.               </section>
  188.               <section class="catalog-link-group">
  189.                 <h3>PlayStation <span aria-hidden="true">›</span></h3>
  190.                 <a href="#">Игры и DLC</a>
  191.                 <a href="#">Пополнение баланса</a>
  192.                 <a href="#">Новые аккаунты</a>
  193.                 <a href="#">PS Plus</a>
  194.                 <a href="#">EA Play</a>
  195.               </section>
  196.               <section class="catalog-link-group">
  197.                 <h3>Xbox <span aria-hidden="true">›</span></h3>
  198.                 <a href="#">Игры и DLC</a>
  199.                 <a href="#">Пополнение баланса</a>
  200.                 <a href="#">Новые аккаунты</a>
  201.                 <a href="#">Xbox Game Pass</a>
  202.                 <a href="#">Услуги</a>
  203.               </section>
  204.               <section class="catalog-link-group">
  205.                 <h3>Nintendo <span aria-hidden="true">›</span></h3>
  206.                 <a href="#">Игры и DLC</a>
  207.                 <a href="#">Подарочные карты</a>
  208.                 <a href="#">Новые аккаунты</a>
  209.                 <a href="#">NS Online</a>
  210.               </section>
  211.               <section class="catalog-link-group">
  212.                 <h3>Battle.net <span aria-hidden="true">›</span></h3>
  213.                 <a href="#">World of Warcraft</a>
  214.                 <a href="#">Подарочные карты</a>
  215.                 <a href="#">Прямое пополнение</a>
  216.                 <a href="#">Новые аккаунты</a>
  217.                 <a href="#">Смена региона</a>
  218.               </section>
  219.             </div>
  220.           </div>
  221.         </div>
  222.       </div>
  223.     </header>
  224.     <main>
  225.       <section class="shell hero-section">
  226.         <picture class="hero-picture">
  227.           <source media="(max-width: 767px)" srcset="./assets/hero-steam-mobile.jpg" />
  228.           <img class="hero-img" src="./assets/hero-steam-cutout.jpg" alt="Пополнение Steam" />
  229.         </picture>
  230.         <div class="hero-arrows">
  231.           <button aria-label="Назад">
  232.             <svg viewBox="0 0 24 24" fill="none"><path d="M19 12H6m5-5-5 5 5 5" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" /></svg>
  233.           </button>
  234.           <button aria-label="Вперед">
  235.             <svg viewBox="0 0 24 24" fill="none"><path d="M5 12h13m-5-5 5 5-5 5" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" /></svg>
  236.           </button>
  237.         </div>
  238.         <div class="hero-dots" aria-hidden="true"><span></span><span></span><span></span><span></span><span></span><span></span></div>
  239.       </section>
  240.       <section class="shell services-card">
  241.         <div class="services-row" id="servicesRow"></div>
  242.         <div class="topup-row">
  243.           <div class="topup-info">
  244.             <img src="./assets/steam-topup.png" alt="" />
  245.             <div>
  246.               <div class="topup-title">
  247.                 Пополнение Steam
  248.                 <span>5%</span>
  249.               </div>
  250.               <button class="promo-small" type="button" aria-expanded="false" aria-controls="topupPromoSection">
  251.                 <span class="promo-small-label">Ввести промокод</span>
  252.                 <svg viewBox="0 0 16 16" fill="none"><path d="m4 6 4 4 4-4" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" /></svg>
  253.               </button>
  254.             </div>
  255.           </div>
  256.           <label class="form-field login-field">
  257.             <img src="./assets/profile-gray.svg" alt="" />
  258.             <input type="text" name="steam-login" placeholder="Логин Steam" />
  259.             <span class="field-info" aria-hidden="true"><img src="./assets/info-i.svg" alt="" /></span>
  260.           </label>
  261.           <div class="form-field amount-field">
  262.             <div class="amount-field-shell">
  263.               <span class="currency-mark"><span class="currency-icon currency-icon-ruble" aria-hidden="true"></span></span>
  264.               <label class="amount-control">
  265.                 <small>Сумма</small>
  266.                 <span class="amount-value">
  267.                   <input type="text" name="amount" inputmode="numeric" value="500" maxlength="6" aria-label="Сумма" aria-expanded="false" aria-controls="topupQuickPopover" />
  268.                   <span class="currency-icon currency-icon-ruble" aria-hidden="true"></span>
  269.                 </span>
  270.               </label>
  271.               <span class="currency-tabs" role="group" aria-label="Валюта">
  272.                 <button type="button" aria-label="Доллар"><span class="currency-icon currency-icon-dollar" aria-hidden="true"></span></button>
  273.                 <button type="button" aria-label="Тенге"><span class="currency-icon currency-icon-tenge" aria-hidden="true"></span></button>
  274.                 <button class="is-active" type="button" aria-label="Рубль"><span class="currency-icon currency-icon-ruble" aria-hidden="true"></span></button>
  275.               </span>
  276.             </div>
  277.             <div class="topup-quick-popover" id="topupQuickPopover" aria-hidden="true" inert>
  278.               <span class="topup-quick-popover-label">Быстрый выбор суммы</span>
  279.               <div class="topup-quick-popover-buttons" aria-label="Быстрый выбор суммы">
  280.                 <button class="is-active" type="button" data-quick-amount="500">500 ₽</button>
  281.                 <button type="button" data-quick-amount="1000">1 000 ₽</button>
  282.                 <button type="button" data-quick-amount="2500">2 500 ₽</button>
  283.                 <button type="button" data-quick-amount="5000">5 000 ₽</button>
  284.               </div>
  285.             </div>
  286.           </div>
  287.           <button class="pay-btn" type="button" aria-expanded="false" aria-controls="topupPaymentSection">Оплатить 500<span class="currency-icon currency-icon-ruble" aria-hidden="true"></span></button>
  288.           <div class="topup-expand" id="topupDetails" aria-hidden="true" inert>
  289.           <div class="topup-expand-clip">
  290.             <div class="topup-expand-content">
  291.               <section class="topup-expand-section topup-promo-section" id="topupPromoSection" aria-hidden="true" inert>
  292.                 <div class="topup-expand-section-inner">
  293.                   <label class="topup-detail-label" for="topupPromoInput">Промокод</label>
  294.                   <div class="topup-promo-row">
  295.                     <label class="topup-promo-input">
  296.                       <svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
  297.                         <path d="M4 10h16v10H4V10Zm0-4h16v4H4V6Zm8 0v14M12 6c-1.7-3.1-5.2-2.8-5.2-.5C6.8 7.2 9 7.5 12 6Zm0 0c1.7-3.1 5.2-2.8 5.2-.5C17.2 7.2 15 7.5 12 6Z" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round" />
  298.                       </svg>
  299.                       <input id="topupPromoInput" type="text" placeholder="Введите промокод" autocomplete="off" />
  300.                     </label>
  301.                     <button class="topup-promo-apply" type="button">Применить</button>
  302.                   </div>
  303.                 </div>
  304.               </section>
  305.               <div class="topup-promo-benefit" aria-live="polite" aria-hidden="true">
  306.                 <img src="./assets/discount-percent.png" alt="" />
  307.                 <strong>Скидка <span>5%</span> — вы экономите <b data-promo-saving>25 ₽</b></strong>
  308.                 <small><span>Вы платите примерно на 12% меньше,</span><span>чем на других площадках</span></small>
  309.               </div>
  310.               <section class="topup-expand-section topup-payment-section" id="topupPaymentSection" aria-hidden="true" inert>
  311.                 <div class="topup-expand-section-inner">
  312.                   <h3 class="topup-detail-label topup-payment-title">Способ оплаты</h3>
  313.                   <div class="topup-payment-methods" role="radiogroup" aria-label="Способ оплаты">
  314.                     <button class="topup-payment-method is-selected" type="button" role="radio" aria-checked="true" data-fee="0">
  315.                       <span class="topup-payment-icon"><img src="./assets/payment-sbp.svg" alt="" /></span>
  316.                       <span class="topup-payment-copy"><b>СБП</b><small>Без комиссии</small></span>
  317.                       <span class="topup-payment-check" aria-hidden="true"><svg viewBox="0 0 20 20" fill="none"><path d="m5 10 3 3 7-7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /></svg></span>
  318.                     </button>
  319.                     <button class="topup-payment-method" type="button" role="radio" aria-checked="false" data-fee="2">
  320.                       <span class="topup-payment-icon"><img src="./assets/payment-mir-option.svg" alt="" /></span>
  321.                       <span class="topup-payment-copy"><b>Карта МИР</b><small>+2% комиссия</small></span>
  322.                       <span class="topup-payment-check" aria-hidden="true"><svg viewBox="0 0 20 20" fill="none"><path d="m5 10 3 3 7-7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /></svg></span>
  323.                     </button>
  324.                     <button class="topup-payment-method" type="button" role="radio" aria-checked="false" data-fee="0">
  325.                       <span class="topup-payment-icon"><img src="./assets/payment-btc.svg" alt="" /></span>
  326.                       <span class="topup-payment-copy"><b>Криптовалюта</b><small>Без комиссии</small></span>
  327.                       <span class="topup-payment-check" aria-hidden="true"><svg viewBox="0 0 20 20" fill="none"><path d="m5 10 3 3 7-7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /></svg></span>
  328.                     </button>
  329.                   </div>
  330.                   <div class="topup-total-row">
  331.                     <div class="topup-total-copy">
  332.                       <small>К зачислению на Steam</small>
  333.                       <span><strong data-topup-credit>500 ₽</strong><b data-topup-fee-badge>Без комиссии</b></span>
  334.                     </div>
  335.                     <button class="topup-final-pay" type="button">Оплатить 500₽</button>
  336.                   </div>
  337.                 </div>
  338.               </section>
  339.             </div>
  340.           </div>
  341.         </div>
  342.         </div>
  343.       </section>
  344.       <section class="shell product-section popular-section">
  345.         <div class="section-head compact-head">
  346.           <h2><img src="./assets/icon-popular.svg" alt="" />Популярные товары</h2>
  347.           <nav class="chip-row" aria-label="Категории">
  348.             <button class="chip is-active"><img src="./assets/cat-donate.svg" alt="" />Донат</button>
  349.             <button class="chip"><img src="./assets/cat-subscribes.svg" alt="" />Подписки</button>
  350.             <button class="chip"><img src="./assets/cat-items.svg" alt="" />Предметы</button>
  351.             <button class="chip"><img src="./assets/cat-accounts.svg" alt="" />Аккаунты</button>
  352.             <button class="chip"><img src="./assets/cat-keys.svg" alt="" />Ключи</button>
  353.             <button class="chip"><img src="./assets/cat-currency.svg" alt="" />Steam Gift</button>
  354.             <button class="chip"><img src="./assets/cat-other.svg" alt="" />Другое</button>
  355.           </nav>
  356.         </div>
  357.         <div class="product-grid" data-products="popular"></div>
  358.         <a class="show-all mobile-show-all" href="/catalog">Показать все</a>
  359.       </section>
  360.       <section class="shell product-section">
  361.         <div class="section-head">
  362.           <h2><img src="./assets/icon-recommended.svg" alt="" />Рекомендованные товары</h2>
  363.           <a class="show-all" href="/catalog">Показать все</a>
  364.         </div>
  365.         <div class="product-grid" data-products="recommended"></div>
  366.       </section>
  367.       <section class="shell blogger-section">
  368.         <div class="section-head">
  369.           <h2><img src="./assets/icon-recommended-alt.svg" alt="" />Подборка от блогера</h2>
  370.           <a class="show-all" href="/catalog">Показать все</a>
  371.         </div>
  372.         <div class="blogger-layout">
  373.           <article class="blogger-card">
  374.             <div class="blogger-card-head">
  375.               <div class="blogger-author">
  376.                 <img src="./assets/polka.png" alt="" />
  377.                 <strong>Рекомендует Polk Zone</strong>
  378.               </div>
  379.               <button class="promo-inside">
  380.                 <img src="./assets/blogger-promo-blob.svg" alt="" />
  381.                 <span>Промокод внутри</span>
  382.               </button>
  383.             </div>
  384.             <img class="blogger-banner" src="./assets/blogger-promo.png" alt="Личная подборка от блогера" />
  385.             <div class="blogger-stats">
  386.               <div class="coupon-stat">
  387.                 <span class="stat-icon"><img src="./assets/blogger-icon-coupon.svg" alt="" /></span>
  388.                 <span class="coupon-text">Промокод<br /><b>BLOGGER5</b></span>
  389.                 <span class="coupon-copy" aria-hidden="true"><img src="./assets/blogger-icon-copy.svg" alt="" /></span>
  390.               </div>
  391.               <div class="stat-item"><span class="stat-icon"><img src="./assets/blogger-icon-cart.svg" alt="" /></span><span class="stat-text"><b>12 товаров</b><small>в подборке</small></span></div>
  392.               <div class="stat-item wide"><span class="stat-icon"><img src="./assets/blogger-icon-services.svg" alt="" /></span><span class="stat-text"><b>Steam · Roblox · PUBG</b><small>и другие сервисы</small></span></div>
  393.             </div>
  394.           </article>
  395.           <aside class="blogger-side">
  396.             <article class="side-card steam-side">
  397.               <div class="side-title">
  398.                 <img src="./assets/steam-topup.png" alt="" />
  399.                 <div><b>Пополнение Steam</b><span>Без комиссии</span></div>
  400.               </div>
  401.               <div class="side-features">
  402.                 <div><img src="./assets/blogger-icon-wallet.svg" alt="" /><b>от 500 ₽</b></div>
  403.                 <div><img src="./assets/blogger-icon-lightning.svg" alt="" /><b>Моментально</b></div>
  404.               </div>
  405.               <button>Пополнить</button>
  406.             </article>
  407.             <article class="side-card keys-side">
  408.               <div class="side-title">
  409.                 <img src="./assets/blogger-playpapa-logo.svg" alt="" />
  410.                 <div><b>Топ ключи игр</b><span>Выбор подписчиков</span></div>
  411.               </div>
  412.               <div class="thumb-row">
  413.                 <img src="./assets/thumb-game-1.jpg" alt="" />
  414.                 <img src="./assets/thumb-game-2.png" alt="" />
  415.                 <img src="./assets/thumb-game-3.png" alt="" />
  416.                 <span>Ещё 7</span>
  417.               </div>
  418.               <button>Смотреть</button>
  419.             </article>
  420.           </aside>
  421.         </div>
  422.       </section>
  423.       <section class="shell product-section other-products">
  424.         <div class="section-head">
  425.           <h2><img src="./assets/icon-recommended-alt.svg" alt="" />Другие товары</h2>
  426.           <a class="show-all" href="/catalog">Показать все</a>
  427.         </div>
  428.         <div class="product-grid" data-products="other"></div>
  429.       </section>
  430.       <section class="shell articles-section">
  431.         <div class="section-head">
  432.           <h2><img src="./assets/icon-articles.svg" alt="" />Интересные статьи</h2>
  433.           <a class="show-all" href="/catalog">Показать все</a>
  434.         </div>
  435.         <div class="article-grid">
  436.           <article class="article-card">
  437.             <div class="article-image"><img src="./assets/article-paradise.png" alt="" /><span>Статьи</span></div>
  438.             <h3>Тайные игры EGS: Paradise Killer — детектив, который можно забрать прямо сейчас</h3>
  439.             <div class="article-meta"><span><img src="./assets/article-calendar.svg" alt="" />13 июня 2026</span><span><img src="./assets/article-eye.svg" alt="" />4 892</span></div>
  440.           </article>
  441.           <article class="article-card">
  442.             <div class="article-image"><img src="./assets/article-sale.png" alt="" /><span>Статьи</span></div>
  443.             <h3>Тайные игры EGS: Paradise Killer — детектив, который можно забрать прямо сейчас</h3>
  444.             <div class="article-meta"><span><img src="./assets/article-calendar.svg" alt="" />13 июня 2026</span><span><img src="./assets/article-eye.svg" alt="" />4 892</span></div>
  445.           </article>
  446.           <article class="article-card">
  447.             <div class="article-image"><img src="./assets/article-mobile.png" alt="" /><span>Статьи</span></div>
  448.             <h3>Тайные игры EGS: Paradise Killer — детектив, который можно забрать прямо сейчас</h3>
  449.             <div class="article-meta"><span><img src="./assets/article-calendar.svg" alt="" />13 июня 2026</span><span><img src="./assets/article-eye.svg" alt="" />4 892</span></div>
  450.           </article>
  451.         </div>
  452.       </section>
  453.       <section class="shell reviews-section">
  454.         <div class="section-head reviews-head">
  455.           <div>
  456.             <h2><img src="./assets/icon-reviews.svg" alt="" />Последние отзывы</h2>
  457.             <p>Все отзывы взяты с независимой площадки</p>
  458.           </div>
  459.           <a class="show-all" href="/catalog">Показать все</a>
  460.         </div>
  461.         <div class="review-grid">
  462.           <article class="review-card">
  463.             <div class="review-top">
  464.               <img src="./assets/review-avatar.png" alt="" />
  465.               <div><b>Bizidin</b><span>★★★★★ <strong>5.0</strong></span></div>
  466.               <time>Сегодня в 11:48</time>
  467.             </div>
  468.             <p class="review-text">Отзывчивый и приятный продавец, помог не только с товаром но и с другим вопросом. Рекомендую!</p>
  469.             <div class="review-product">
  470.               <img src="./assets/product-pubg.png" alt="" />
  471.               <b>🌸 FunTime | Полностью готовый сервер под ключ ⚡</b>
  472.               <span>139₽</span>
  473.             </div>
  474.           </article>
  475.           <article class="review-card">
  476.             <div class="review-top">
  477.               <img src="./assets/review-avatar.png" alt="" />
  478.               <div><b>Bizidin</b><span>★★★★★ <strong>5.0</strong></span></div>
  479.               <time>Сегодня в 11:48</time>
  480.             </div>
  481.             <p class="review-text">Отзывчивый и приятный продавец, помог не только с товаром но и с другим вопросом. Рекомендую!</p>
  482.             <div class="review-product">
  483.               <img src="./assets/product-pubg.png" alt="" />
  484.               <b>🌸 FunTime | Полностью готовый сервер под ключ ⚡</b>
  485.               <span>139₽</span>
  486.             </div>
  487.           </article>
  488.           <article class="review-card">
  489.             <div class="review-top">
  490.               <img src="./assets/review-avatar.png" alt="" />
  491.               <div><b>Bizidin</b><span>★★★★★ <strong>5.0</strong></span></div>
  492.               <time>Сегодня в 11:48</time>
  493.             </div>
  494.             <p class="review-text">Отзывчивый и приятный продавец, помог не только с товаром но и с другим вопросом. Рекомендую!</p>
  495.             <div class="review-product">
  496.               <img src="./assets/product-pubg.png" alt="" />
  497.               <b>🌸 FunTime | Полностью готовый сервер под ключ ⚡</b>
  498.               <span>139₽</span>
  499.             </div>
  500.           </article>
  501.         </div>
  502.       </section>
  503.     <footer class="shell site-footer">
  504.       <div class="footer-top">
  505.         <img src="./assets/logo.svg" alt="PlayPapa" />
  506.         <nav>
  507.           <a href="#">Стать продавцом</a>
  508.           <a href="#">Бонусы</a>
  509.           <a href="#">Поддержка</a>
  510.           <a href="#">Гарантии</a>
  511.           <a href="#">Отзывы</a>
  512.         </nav>
  513.       </div>
  514.       <div class="footer-mid">
  515.         <div class="footer-social">
  516.           <b>PlayPapa</b>
  517.           <a class="social-icon" href="#" aria-label="VK"><img src="./assets/footer-vk.svg" alt="" /></a>
  518.           <a class="social-icon" href="#" aria-label="Telegram"><img src="./assets/footer-telegram.svg" alt="" /></a>
  519.           <a class="footer-channel" href="#">Polk Zone</a>
  520.           <a class="social-icon" href="#" aria-label="TikTok"><img src="./assets/footer-tiktok.svg" alt="" /></a>
  521.           <a class="social-icon" href="#" aria-label="YouTube"><img src="./assets/footer-youtube.svg" alt="" /></a>
  522.         </div>
  523.         <div class="payments">
  524.           <span><img src="./assets/payment-visa.svg" alt="VISA" /></span>
  525.           <span><img src="./assets/payment-mir.svg" alt="МИР" /></span>
  526.           <span><img src="./assets/payment-mastercard.svg" alt="Mastercard" /></span>
  527.         </div>
  528.       </div>
  529.       <div class="footer-bottom">
  530.         <nav>
  531.           <a href="#">Политика конфиденциальности</a>
  532.           <a href="#">Соглашение</a>
  533.           <a href="#">Договор-оферта</a>
  534.         </nav>
  535.         <span>© 2026 PlayPapa</span>
  536.       </div>
  537.     </footer>
  538.     </main>
  539.     <section class="mobile-search-layer" id="mobileSearch" role="dialog" aria-modal="true" aria-label="Мобильный поиск" aria-hidden="true" inert>
  540.       <div class="mobile-search-page">
  541.         <header class="mobile-search-topbar">
  542.           <button class="mobile-search-back" id="mobileSearchBack" type="button" aria-label="Назад">
  543.             <svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
  544.               <path d="M19 12H5m6-6-6 6 6 6" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" />
  545.             </svg>
  546.           </button>
  547.           <form class="mobile-search-form" role="search">
  548.             <svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
  549.               <circle cx="10.8" cy="10.8" r="6.8" stroke="currentColor" stroke-width="2" />
  550.               <path d="m16 16 4.2 4.2" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
  551.             </svg>
  552.             <input type="search" name="mobile-query" placeholder="Найти товар" aria-label="Найти товар" autocomplete="off" />
  553.           </form>
  554.         </header>
  555.         <section class="mobile-search-section mobile-search-services-section">
  556.           <h2>Игры и игровые сервисы</h2>
  557.           <div class="mobile-search-cards" aria-label="Игры и игровые сервисы">
  558.             <a class="mobile-search-card" href="#"><img src="./assets/service-steam.png" alt="" /><strong>Steam</strong></a>
  559.             <a class="mobile-search-card" href="#"><img src="./assets/service-telegram.png" alt="" /><strong>Telegram</strong></a>
  560.             <a class="mobile-search-card" href="#"><img src="./assets/service-roblox.png" alt="" /><strong>Roblox</strong></a>
  561.             <a class="mobile-search-card" href="#"><img src="./assets/service-brawl.png" alt="" /><strong>Brawl...</strong></a>
  562.             <a class="mobile-search-card mobile-search-more" href="#">
  563.               <span><img src="./assets/more-service.svg" alt="" /></span>
  564.               <strong>еще 841</strong>
  565.             </a>
  566.           </div>
  567.         </section>
  568.         <section class="mobile-search-section mobile-search-new-section">
  569.           <h2>Новинки и предзаказы</h2>
  570.           <div class="mobile-search-cards mobile-search-game-cards" aria-label="Новинки и предзаказы">
  571.             <a class="mobile-search-card" href="#"><img src="./assets/article-paradise.png" alt="" /><span>Gothic 1<br />Remake</span></a>
  572.             <a class="mobile-search-card" href="#"><img src="./assets/article-sale.png" alt="" /><span>Forza<br />Horizon 6</span></a>
  573.             <a class="mobile-search-card" href="#"><img src="./assets/product-pubg.png" alt="" /><span>Assassin's<br />Creed:...</span></a>
  574.             <a class="mobile-search-card" href="#"><img src="./assets/article-mobile.png" alt="" /><span>007 First<br />Light</span></a>
  575.             <a class="mobile-search-card" href="#"><img src="./assets/article-sale.png" alt="" /><span>Call of Duty:<br />Modern...</span></a>
  576.           </div>
  577.         </section>
  578.         <nav class="mobile-search-platforms" aria-label="Разделы каталога">
  579.           <a href="#"><span>Steam</span><span aria-hidden="true">›</span></a>
  580.           <a href="#"><span>PlayStation</span><span aria-hidden="true">›</span></a>
  581.           <a href="#"><span>Xbox</span><span aria-hidden="true">›</span></a>
  582.           <a href="#"><span>Nintendo</span><span aria-hidden="true">›</span></a>
  583.           <a href="#"><span>Battle.net</span><span aria-hidden="true">›</span></a>
  584.           <a href="#"><span>Подборки</span><span aria-hidden="true">›</span></a>
  585.         </nav>
  586.       </div>
  587.     </section>
  588.     <section class="auth-modal-layer" id="authModal" data-auth-screen="login" aria-hidden="true" inert>
  589.       <div class="auth-modal-dialog" role="dialog" aria-modal="true" aria-label="Авторизация">
  590.         <div class="auth-toast auth-toast-success" aria-live="polite">
  591.           <img src="./assets/auth-check.svg" alt="" />
  592.           <span>Письмо отправлено на почту</span>
  593.         </div>
  594.         <div class="auth-toast auth-toast-error" aria-live="polite">
  595.           <img src="./assets/auth-error.svg" alt="" />
  596.           <span>Ошибка отправки письма</span>
  597.         </div>
  598.         <button class="auth-close" type="button" aria-label="Закрыть" data-auth-close>
  599.           <svg viewBox="0 0 16 16" fill="none" aria-hidden="true">
  600.             <path d="m4.5 4.5 7 7m0-7-7 7" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
  601.           </svg>
  602.         </button>
  603.         <img class="auth-logo" src="./assets/logo.svg" alt="PlayPapa" />
  604.         <section class="auth-view auth-view-login" aria-label="Вход">
  605.           <h2>С возвращением!</h2>
  606.           <div class="auth-tabs" role="tablist" aria-label="Авторизация">
  607.             <button class="auth-tab" type="button" role="tab" data-auth-screen-target="login">Вход</button>
  608.             <button class="auth-tab" type="button" role="tab" data-auth-screen-target="register">Регистрация</button>
  609.           </div>
  610.           <form class="auth-form" data-auth-form="login">
  611.             <label class="auth-field">
  612.               <img src="./assets/auth-user.svg" alt="" />
  613.               <input type="text" name="login" placeholder="Логин" autocomplete="username" />
  614.             </label>
  615.             <label class="auth-field">
  616.               <img src="./assets/auth-lock.svg" alt="" />
  617.               <input type="password" name="password" placeholder="Пароль" autocomplete="current-password" />
  618.             </label>
  619.             <button class="auth-link auth-forgot" type="button" data-auth-screen-target="recover">Забыли пароль?</button>
  620.             <button class="auth-primary" type="submit">Войти</button>
  621.           </form>
  622.           <div class="auth-social-block">
  623.             <div class="auth-divider"><span>Другие методы входа</span></div>
  624.             <div class="auth-socials" aria-label="Другие методы входа">
  625.               <button class="auth-social auth-social-telegram" type="button" aria-label="Telegram"><img src="./assets/footer-telegram.svg" alt="" /></button>
  626.               <button class="auth-social auth-social-vk" type="button" aria-label="VK"><img src="./assets/footer-vk.svg" alt="" /></button>
  627.               <button class="auth-social auth-social-steam" type="button" aria-label="Steam"><img src="./assets/auth-steam.svg" alt="" /></button>
  628.               <button class="auth-social auth-social-yandex" type="button" aria-label="Яндекс"><img src="./assets/auth-yandex.svg" alt="" /></button>
  629.             </div>
  630.             <p class="auth-recaptcha">Защищено reCAPTCHA · <a href="#">Политика</a> и <a href="#">Условия</a></p>
  631.           </div>
  632.         </section>
  633.         <section class="auth-view auth-view-register" aria-label="Регистрация">
  634.           <h2>Создание аккаунта</h2>
  635.           <div class="auth-tabs" role="tablist" aria-label="Авторизация">
  636.             <button class="auth-tab" type="button" role="tab" data-auth-screen-target="login">Вход</button>
  637.             <button class="auth-tab" type="button" role="tab" data-auth-screen-target="register">Регистрация</button>
  638.           </div>
  639.           <form class="auth-form" data-auth-form="register">
  640.             <label class="auth-field">
  641.               <img src="./assets/auth-user.svg" alt="" />
  642.               <input type="text" name="register-login" placeholder="Логин" autocomplete="username" />
  643.             </label>
  644.             <label class="auth-field">
  645.               <img src="./assets/auth-mail.svg" alt="" />
  646.               <input type="email" name="register-email" placeholder="E-mail" autocomplete="email" />
  647.             </label>
  648.             <label class="auth-field">
  649.               <img src="./assets/auth-lock.svg" alt="" />
  650.               <input type="password" name="register-password" placeholder="Пароль" autocomplete="new-password" />
  651.             </label>
  652.             <label class="auth-field">
  653.               <img src="./assets/auth-lock.svg" alt="" />
  654.               <input type="password" name="register-password-repeat" placeholder="Повторите пароль" autocomplete="new-password" />
  655.             </label>
  656.             <label class="auth-consent">
  657.               <input type="checkbox" checked />
  658.               <span aria-hidden="true"></span>
  659.               <b>Я принимаю <a href="#">условия</a> и <a href="#">политику конфиденциальности</a>.</b>
  660.             </label>
  661.             <button class="auth-primary" type="submit">Создать аккаунт</button>
  662.           </form>
  663.           <div class="auth-social-block">
  664.             <div class="auth-divider"><span>Другие методы входа</span></div>
  665.             <div class="auth-socials" aria-label="Другие методы входа">
  666.               <button class="auth-social auth-social-telegram" type="button" aria-label="Telegram"><img src="./assets/footer-telegram.svg" alt="" /></button>
  667.               <button class="auth-social auth-social-vk" type="button" aria-label="VK"><img src="./assets/footer-vk.svg" alt="" /></button>
  668.               <button class="auth-social auth-social-steam" type="button" aria-label="Steam"><img src="./assets/auth-steam.svg" alt="" /></button>
  669.               <button class="auth-social auth-social-yandex" type="button" aria-label="Яндекс"><img src="./assets/auth-yandex.svg" alt="" /></button>
  670.             </div>
  671.             <p class="auth-recaptcha">Защищено reCAPTCHA · <a href="#">Политика</a> и <a href="#">Условия</a></p>
  672.           </div>
  673.         </section>
  674.         <section class="auth-view auth-view-recover" aria-label="Восстановление пароля">
  675.           <button class="auth-back" type="button" data-auth-screen-target="login">
  676.             <svg viewBox="0 0 16 16" fill="none" aria-hidden="true">
  677.               <path d="M10.5 3.5 6 8l4.5 4.5" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />
  678.             </svg>
  679.             <span>Назад ко входу</span>
  680.           </button>
  681.           <h2>Восстановление пароля</h2>
  682.           <p class="auth-subtitle">Введите e-mail - пришлем ссылку для сброса пароля.</p>
  683.           <form class="auth-form" data-auth-form="recover">
  684.             <label class="auth-field">
  685.               <img src="./assets/auth-mail.svg" alt="" />
  686.               <input type="email" name="recover-email" placeholder="you@mail.ru" autocomplete="email" />
  687.             </label>
  688.             <button class="auth-primary" type="submit">Отправить ссылку</button>
  689.           </form>
  690.           <div class="auth-social-block">
  691.             <div class="auth-divider"><span>Другие методы входа</span></div>
  692.             <div class="auth-socials" aria-label="Другие методы входа">
  693.               <button class="auth-social auth-social-telegram" type="button" aria-label="Telegram"><img src="./assets/footer-telegram.svg" alt="" /></button>
  694.               <button class="auth-social auth-social-vk" type="button" aria-label="VK"><img src="./assets/footer-vk.svg" alt="" /></button>
  695.               <button class="auth-social auth-social-steam" type="button" aria-label="Steam"><img src="./assets/auth-steam.svg" alt="" /></button>
  696.               <button class="auth-social auth-social-yandex" type="button" aria-label="Яндекс"><img src="./assets/auth-yandex.svg" alt="" /></button>
  697.             </div>
  698.             <p class="auth-recaptcha">Защищено reCAPTCHA · <a href="#">Политика</a> и <a href="#">Условия</a></p>
  699.           </div>
  700.         </section>
  701.         <section class="auth-view auth-view-success" aria-label="Письмо отправлено">
  702.           <div class="auth-status auth-status-success"><img src="./assets/auth-check.svg" alt="" /></div>
  703.           <h2>Проверьте почту</h2>
  704.           <p class="auth-message">Мы отправили ссылку для сброса пароля на <strong>you@mail.ru</strong>. Перейдите по ней, чтобы задать новый пароль.</p>
  705.           <button class="auth-text-action" type="button" data-auth-screen-target="recover">Не пришло письмо? Отправить повторно</button>
  706.           <button class="auth-secondary" type="button" data-auth-screen-target="login">Вернуться ко входу</button>
  707.         </section>
  708.         <section class="auth-view auth-view-error" aria-label="Ошибка отправки">
  709.           <div class="auth-status auth-status-error"><img src="./assets/auth-error.svg" alt="" /></div>
  710.           <h2>Не удалось отправить</h2>
  711.           <p class="auth-message">Письмо не отправлено - возможно, неверный адрес или проблема со связью. Проверьте e-mail и попробуйте снова.</p>
  712.           <button class="auth-primary" type="button" data-auth-screen-target="recover">Попробовать снова</button>
  713.           <button class="auth-support" type="button">Написать в поддержку</button>
  714.         </section>
  715.       </div>
  716.     </section>
  717.     <div data-mobile-panel></div>
  718.     <script src="./assets/mobile-panel.js"></script>
  719.     <script>
  720.       const root = document.documentElement;
  721.       const themeSwitch = document.querySelector(".theme-switch");
  722.       const themeButtons = document.querySelectorAll(".theme-btn[data-theme]");
  723.       const preloader = document.getElementById("pagePreloader");
  724.       if (preloader) {
  725.         const preloaderStart = window.__preloaderStart || performance.now();
  726.         const minPreloaderTime = 600;
  727.         const maxPreloaderTime = 3000;
  728.         let preloaderHidden = false;
  729.         const hidePreloader = () => {
  730.           if (preloaderHidden) return;
  731.           preloaderHidden = true;
  732.           preloader.classList.add("is-hiding");
  733.           document.body.classList.remove("preloader-active");
  734.           window.setTimeout(() => preloader.remove(), 260);
  735.         };
  736.         const requestPreloaderHide = () => {
  737.           const elapsed = performance.now() - preloaderStart;
  738.           window.setTimeout(hidePreloader, Math.max(minPreloaderTime - elapsed, 0));
  739.         };
  740.         window.setTimeout(hidePreloader, Math.max(maxPreloaderTime - (performance.now() - preloaderStart), 0));
  741.         if (document.readyState === "complete") {
  742.           requestPreloaderHide();
  743.         } else {
  744.           window.addEventListener("load", requestPreloaderHide, { once: true });
  745.         }
  746.       }
  747.       const setTheme = (theme, animate = false) => {
  748.         if (animate) {
  749.           document.body.classList.remove("theme-animating");
  750.           void document.body.offsetWidth;
  751.           document.body.classList.add("theme-animating");
  752.         }
  753.         root.dataset.theme = theme;
  754.         try {
  755.           localStorage.setItem("playpapa-theme", theme);
  756.         } catch {}
  757.         themeButtons.forEach((button) => {
  758.           const active = button.dataset.theme === theme;
  759.           button.classList.toggle("is-active", active);
  760.           button.setAttribute("aria-pressed", active ? "true" : "false");
  761.         });
  762.         if (animate) window.setTimeout(() => document.body.classList.remove("theme-animating"), 560);
  763.       };
  764.       setTheme(root.dataset.theme === "dark" ? "dark" : "light");
  765.       themeButtons.forEach((button) => {
  766.         button.addEventListener("click", () => setTheme(button.dataset.theme, true));
  767.       });
  768.       themeSwitch?.addEventListener("click", (event) => {
  769.         if (event.target.closest(".theme-btn")) return;
  770.         setTheme(root.dataset.theme === "dark" ? "light" : "dark", true);
  771.       });
  772.       const mobileSearchLayer = document.getElementById("mobileSearch");
  773.       const mobileSearchTrigger = document.querySelector("[data-mobile-search-trigger]");
  774.       const mobileSearchBack = document.getElementById("mobileSearchBack");
  775.       const setMobileSearchOpen = (open) => {
  776.         if (!mobileSearchLayer) return;
  777.         mobileSearchLayer.classList.toggle("is-open", open);
  778.         mobileSearchLayer.setAttribute("aria-hidden", open ? "false" : "true");
  779.         mobileSearchLayer.toggleAttribute("inert", !open);
  780.         mobileSearchTrigger?.setAttribute("aria-expanded", open ? "true" : "false");
  781.         document.body.classList.toggle("mobile-search-open", open);
  782.         if (open) mobileSearchLayer.scrollTop = 0;
  783.       };
  784.       mobileSearchTrigger?.addEventListener("click", (event) => {
  785.         event.preventDefault();
  786.         setProfileOpen(false);
  787.         setCatalogOpen(false);
  788.         setMobileSearchOpen(true);
  789.       });
  790.       mobileSearchBack?.addEventListener("click", () => {
  791.         setMobileSearchOpen(false);
  792.         mobileSearchTrigger?.focus();
  793.       });
  794.       mobileSearchLayer?.querySelector(".mobile-search-form")?.addEventListener("submit", (event) => event.preventDefault());
  795.       mobileSearchLayer?.querySelectorAll('a[href="#"]').forEach((link) => {
  796.         link.addEventListener("click", (event) => event.preventDefault());
  797.       });
  798.       document.addEventListener("keydown", (event) => {
  799.         if (event.key !== "Escape" || !mobileSearchLayer?.classList.contains("is-open")) return;
  800.         setMobileSearchOpen(false);
  801.         mobileSearchTrigger?.focus();
  802.       });
  803.       const profileMenu = document.querySelector(".profile-menu");
  804.       const profileButton = profileMenu?.querySelector(".profile-btn");
  805.       const profilePopover = document.getElementById("profilePopover");
  806.       const setProfileOpen = (open, returnFocus = false) => {
  807.         if (!profileMenu || !profilePopover || !profileButton) return;
  808.         profileMenu.classList.toggle("is-open", open);
  809.         profileButton.setAttribute("aria-expanded", open ? "true" : "false");
  810.         profilePopover.setAttribute("aria-hidden", open ? "false" : "true");
  811.         profilePopover.toggleAttribute("inert", !open);
  812.         document.body.classList.toggle("profile-open", open);
  813.         if (returnFocus) profileButton.focus();
  814.       };
  815.       profileButton?.addEventListener("click", (event) => {
  816.         event.stopPropagation();
  817.         const shouldOpen = !profileMenu?.classList.contains("is-open");
  818.         setProfileOpen(shouldOpen);
  819.       });
  820.       profilePopover?.querySelectorAll(".profile-menu-item:not(:disabled)").forEach((item) => {
  821.         item.addEventListener("click", () => setProfileOpen(false));
  822.       });
  823.       document.addEventListener("pointerdown", (event) => {
  824.         if (!profileMenu?.classList.contains("is-open") || profileMenu.contains(event.target)) return;
  825.         setProfileOpen(false);
  826.       });
  827.       document.addEventListener("keydown", (event) => {
  828.         if (event.key !== "Escape" || !profileMenu?.classList.contains("is-open")) return;
  829.         setProfileOpen(false, true);
  830.       });
  831.       const catalogLayer = document.getElementById("catalogMegaMenu");
  832.       const catalogButton = document.querySelector(".catalog-btn:not([href])");
  833.       const catalogTriggers = [catalogButton, ...document.querySelectorAll("[data-catalog-trigger]")].filter(Boolean);
  834.       const catalogSideLinks = document.querySelectorAll(".catalog-side-link");
  835.       const catalogFeaturedItems = [
  836.         ["service-steam.png", "Пополнение Steam"],
  837.         ["service-brawl.png", "Game Pass"],
  838.         ["service-steam.png", "Пополнение PSN"],
  839.         ["service-brawl.png", "PS Plus"],
  840.         ["service-pubg.png", "Grand Theft Auto V"],
  841.         ["service-roblox.png", "Minecraft"],
  842.         ["service-playstation.png", "Diablo 4"],
  843.         ["service-mobile.png", "ARC Raiders"],
  844.         ["service-chatgpt.png", "Dead by Daylight"],
  845.       ];
  846.       const catalogNewItems = [
  847.         ["article-paradise.png", "Gothic 1 Remake"],
  848.         ["article-sale.png", "Forza Horizon 6"],
  849.         ["product-pubg.png", "Assassin's Creed: Black..."],
  850.         ["article-mobile.png", "007 First Light"],
  851.         ["article-sale.png", "Call of Duty: Modern..."],
  852.         ["product-pubg.png", "Marvel's Wolverine"],
  853.         ["article-paradise.png", "UFC 6"],
  854.         ["article-sale.png", "Subnautica 2"],
  855.         ["product-pubg.png", "SILENT HILL: Townfall"],
  856.       ];
  857.       const catalogItemMarkup = ([image, label]) => `
  858.         <a class="catalog-showcase-item" href="#" aria-label="${label}">
  859.           <img src="./assets/${image}" alt="">
  860.           <span>${label}</span>
  861.         </a>
  862.       `;
  863.       const catalogFeatured = document.getElementById("catalogFeatured");
  864.       const catalogNew = document.getElementById("catalogNew");
  865.       if (catalogFeatured) catalogFeatured.innerHTML = catalogFeaturedItems.map(catalogItemMarkup).join("");
  866.       if (catalogNew) catalogNew.innerHTML = catalogNewItems.map(catalogItemMarkup).join("");
  867.       const setCatalogOpen = (open) => {
  868.         if (!catalogLayer) return;
  869.         catalogLayer.classList.toggle("is-open", open);
  870.         catalogLayer.setAttribute("aria-hidden", open ? "false" : "true");
  871.         catalogLayer.toggleAttribute("inert", !open);
  872.         document.body.classList.toggle("catalog-open", open);
  873.         catalogTriggers.forEach((trigger) => trigger.setAttribute("aria-expanded", open ? "true" : "false"));
  874.       };
  875.       catalogTriggers.forEach((trigger) => {
  876.         trigger.addEventListener("click", (event) => {
  877.           event.preventDefault();
  878.           setProfileOpen(false);
  879.           setMobileSearchOpen(false);
  880.           setCatalogOpen(!catalogLayer?.classList.contains("is-open"));
  881.         });
  882.       });
  883.       catalogSideLinks.forEach((link) => {
  884.         link.addEventListener("click", () => {
  885.           catalogSideLinks.forEach((item) => item.classList.toggle("is-active", item === link));
  886.         });
  887.       });
  888.       catalogLayer?.querySelectorAll('a[href="#"]').forEach((link) => {
  889.         link.addEventListener("click", (event) => event.preventDefault());
  890.       });
  891.       document.addEventListener("pointerdown", (event) => {
  892.         if (!catalogLayer?.classList.contains("is-open")) return;
  893.         if (catalogLayer.contains(event.target) || catalogTriggers.some((trigger) => trigger.contains(event.target))) return;
  894.         setCatalogOpen(false);
  895.       });
  896.       document.addEventListener("keydown", (event) => {
  897.         if (event.key !== "Escape" || !catalogLayer?.classList.contains("is-open")) return;
  898.         setCatalogOpen(false);
  899.         catalogButton?.focus();
  900.       });
  901.       const authModal = document.getElementById("authModal");
  902.       const authDialog = authModal?.querySelector(".auth-modal-dialog");
  903.       const authScreens = new Set(["login", "register", "recover", "success", "error"]);
  904.       let authReturnFocus = null;
  905.       const syncAuthScreenState = (screen = "login") => {
  906.         if (!authModal || !authScreens.has(screen)) return;
  907.         authModal.dataset.authScreen = screen;
  908.         authModal.querySelectorAll(".auth-tab").forEach((tab) => {
  909.           const active = tab.dataset.authScreenTarget === screen;
  910.           tab.setAttribute("aria-selected", active ? "true" : "false");
  911.           tab.tabIndex = active ? 0 : -1;
  912.         });
  913.         authModal.querySelectorAll(".auth-view").forEach((view) => {
  914.           const active = view.classList.contains(`auth-view-${screen}`);
  915.           view.setAttribute("aria-hidden", active ? "false" : "true");
  916.         });
  917.       };
  918.       const setAuthScreen = (screen = "login") => {
  919.         if (!authModal || !authScreens.has(screen)) return;
  920.         syncAuthScreenState(screen);
  921.       };
  922.       const setAuthOpen = (open, screen = "login", trigger = null) => {
  923.         if (!authModal) return;
  924.         if (open) {
  925.           authReturnFocus = trigger || document.activeElement;
  926.           setProfileOpen(false);
  927.           setCatalogOpen(false);
  928.           setMobileSearchOpen(false);
  929.           setAuthScreen(screen);
  930.         }
  931.         authModal.classList.toggle("is-open", open);
  932.         authModal.setAttribute("aria-hidden", open ? "false" : "true");
  933.         authModal.toggleAttribute("inert", !open);
  934.         document.body.classList.toggle("auth-modal-open", open);
  935.         if (open) {
  936.           window.setTimeout(() => authModal.querySelector("[data-auth-close]")?.focus(), 80);
  937.         } else if (authReturnFocus && typeof authReturnFocus.focus === "function") {
  938.           authReturnFocus.focus();
  939.           authReturnFocus = null;
  940.         }
  941.       };
  942.       document.querySelectorAll("[data-auth-open]").forEach((trigger) => {
  943.         trigger.addEventListener("click", (event) => {
  944.           event.preventDefault();
  945.           setAuthOpen(true, trigger.dataset.authOpen || "login", trigger);
  946.         });
  947.       });
  948.       authModal?.querySelectorAll("[data-auth-screen-target]").forEach((control) => {
  949.         control.addEventListener("click", () => setAuthScreen(control.dataset.authScreenTarget || "login"));
  950.       });
  951.       authModal?.querySelectorAll("[data-auth-close]").forEach((control) => {
  952.         control.addEventListener("click", () => setAuthOpen(false));
  953.       });
  954.       authModal?.addEventListener("pointerdown", (event) => {
  955.         if (event.target !== authModal || authDialog?.contains(event.target)) return;
  956.         setAuthOpen(false);
  957.       });
  958.       authModal?.querySelectorAll(".auth-form").forEach((form) => {
  959.         form.addEventListener("submit", (event) => {
  960.           event.preventDefault();
  961.           if (form.dataset.authForm === "recover") {
  962.             const email = form.querySelector('input[type="email"]')?.value.trim().toLowerCase() || "";
  963.             setAuthScreen(email.includes("error") || email.includes("fail") ? "error" : "success");
  964.           }
  965.         });
  966.       });
  967.       document.addEventListener("keydown", (event) => {
  968.         if (event.key !== "Escape" || !authModal?.classList.contains("is-open")) return;
  969.         setAuthOpen(false);
  970.       });
  971.       const initialAuthScreen = (() => {
  972.         const param = new URLSearchParams(window.location.search).get("auth");
  973.         const hash = window.location.hash.startsWith("#auth-") ? window.location.hash.replace("#auth-", "") : "";
  974.         return authScreens.has(param) ? param : authScreens.has(hash) ? hash : "";
  975.       })();
  976.       setAuthScreen("login");
  977.       if (initialAuthScreen) setAuthOpen(true, initialAuthScreen);
  978.       if (window.matchMedia("(pointer: coarse)").matches) {
  979.         let lastTouchEnd = 0;
  980.         const stopZoom = (event) => {
  981.           event.preventDefault();
  982.         };
  983.         const stopMultiTouch = (event) => {
  984.           if (event.touches && event.touches.length > 1) event.preventDefault();
  985.         };
  986.         document.addEventListener("gesturestart", stopZoom, { passive: false });
  987.         document.addEventListener("gesturechange", stopZoom, { passive: false });
  988.         document.addEventListener("gestureend", stopZoom, { passive: false });
  989.         document.addEventListener("touchstart", stopMultiTouch, { passive: false });
  990.         document.addEventListener("touchmove", stopMultiTouch, { passive: false });
  991.         document.addEventListener(
  992.           "touchend",
  993.           (event) => {
  994.             const now = Date.now();
  995.             if (now - lastTouchEnd < 320) event.preventDefault();
  996.             lastTouchEnd = now;
  997.           },
  998.           { passive: false }
  999.         );
  1000.       }
  1001.       const amountInput = document.querySelector('input[name="amount"]');
  1002.       const amountField = document.querySelector(".amount-field");
  1003.       const servicesCard = document.querySelector(".services-card");
  1004.       const topupQuickPopover = document.getElementById("topupQuickPopover");
  1005.       const payButton = document.querySelector(".pay-btn");
  1006.       const payCurrencyIcon = payButton?.querySelector(".currency-icon");
  1007.       const promoToggleButton = document.querySelector(".promo-small");
  1008.       const promoToggleLabel = promoToggleButton?.querySelector(".promo-small-label");
  1009.       const topupDetails = document.getElementById("topupDetails");
  1010.       const topupPromoSection = document.getElementById("topupPromoSection");
  1011.       const topupPaymentSection = document.getElementById("topupPaymentSection");
  1012.       const topupPromoInput = document.getElementById("topupPromoInput");
  1013.       const topupPromoApply = document.querySelector(".topup-promo-apply");
  1014.       const topupPromoBenefit = document.querySelector(".topup-promo-benefit");
  1015.       const topupPromoSaving = document.querySelector("[data-promo-saving]");
  1016.       const topupQuickAmounts = document.querySelectorAll("[data-quick-amount]");
  1017.       const topupPaymentMethods = document.querySelectorAll(".topup-payment-method");
  1018.       const topupCredit = document.querySelector("[data-topup-credit]");
  1019.       const topupFeeBadge = document.querySelector("[data-topup-fee-badge]");
  1020.       const topupFinalPay = document.querySelector(".topup-final-pay");
  1021.       const maxTopupAmount = 100000;
  1022.       let promoSectionOpen = false;
  1023.       let paymentSectionOpen = false;
  1024.       let promoApplied = false;
  1025.       let selectedPaymentFee = 0;
  1026.       let quickPopoverOpen = false;
  1027.       const formatRubles = (value) => new Intl.NumberFormat("ru-RU").format(Math.max(0, Math.round(value)));
  1028.       const syncQuickPopover = () => {
  1029.         topupQuickPopover?.classList.toggle("is-open", quickPopoverOpen);
  1030.         topupQuickPopover?.setAttribute("aria-hidden", quickPopoverOpen ? "false" : "true");
  1031.         topupQuickPopover?.toggleAttribute("inert", !quickPopoverOpen);
  1032.         amountInput?.setAttribute("aria-expanded", quickPopoverOpen ? "true" : "false");
  1033.         servicesCard?.classList.toggle("has-quick-popover", quickPopoverOpen);
  1034.       };
  1035.       const syncTopupExpansion = () => {
  1036.         const detailsOpen = promoSectionOpen || paymentSectionOpen;
  1037.         topupDetails?.classList.toggle("is-open", detailsOpen);
  1038.         topupDetails?.setAttribute("aria-hidden", detailsOpen ? "false" : "true");
  1039.         topupDetails?.toggleAttribute("inert", !detailsOpen);
  1040.         topupPromoSection?.classList.toggle("is-visible", promoSectionOpen);
  1041.         topupPromoSection?.setAttribute("aria-hidden", promoSectionOpen ? "false" : "true");
  1042.         topupPromoSection?.toggleAttribute("inert", !promoSectionOpen);
  1043.         promoToggleButton?.setAttribute("aria-expanded", promoSectionOpen ? "true" : "false");
  1044.         if (promoToggleLabel) promoToggleLabel.textContent = promoSectionOpen ? "Скрыть промокод" : "Ввести промокод";
  1045.         topupPaymentSection?.classList.toggle("is-visible", paymentSectionOpen);
  1046.         topupPaymentSection?.setAttribute("aria-hidden", paymentSectionOpen ? "false" : "true");
  1047.         topupPaymentSection?.toggleAttribute("inert", !paymentSectionOpen);
  1048.         payButton?.setAttribute("aria-expanded", paymentSectionOpen ? "true" : "false");
  1049.         servicesCard?.classList.toggle("is-payment-open", paymentSectionOpen);
  1050.       };
  1051.       const syncAmount = () => {
  1052.         if (!amountInput) return;
  1053.         let value = amountInput.value.replace(/\D/g, "");
  1054.         if (value) value = String(Math.min(Number(value), maxTopupAmount));
  1055.         amountInput.value = value;
  1056.         amountInput.style.setProperty("--amount-digits", String(Math.max(value.length, 3)));
  1057.         const numericAmount = Number(value || 0);
  1058.         if (payButton && payCurrencyIcon) {
  1059.           payButton.textContent = `Оплатить ${value || 0}`;
  1060.           payButton.append(payCurrencyIcon);
  1061.         }
  1062.         topupQuickAmounts.forEach((button) => {
  1063.           button.classList.toggle("is-active", Number(button.dataset.quickAmount) === numericAmount);
  1064.         });
  1065.         if (topupCredit) topupCredit.textContent = `${formatRubles(numericAmount)} ₽`;
  1066.         const amountToPay = numericAmount * (1 + selectedPaymentFee / 100);
  1067.         if (topupFinalPay) topupFinalPay.textContent = `Оплатить ${formatRubles(amountToPay)}₽`;
  1068.         if (topupFeeBadge) {
  1069.           topupFeeBadge.textContent = selectedPaymentFee ? `+${selectedPaymentFee}% комиссия` : "Без комиссии";
  1070.           topupFeeBadge.classList.toggle("has-fee", selectedPaymentFee > 0);
  1071.         }
  1072.         if (topupPromoSaving) topupPromoSaving.textContent = `${formatRubles(numericAmount * 0.05)} ₽`;
  1073.       };
  1074.       amountInput?.addEventListener("input", syncAmount);
  1075.       amountInput?.addEventListener("focus", () => {
  1076.         quickPopoverOpen = true;
  1077.         syncQuickPopover();
  1078.       });
  1079.       amountInput?.addEventListener("click", () => {
  1080.         quickPopoverOpen = true;
  1081.         syncQuickPopover();
  1082.       });
  1083.       amountInput?.addEventListener("keydown", (event) => {
  1084.         if (event.key !== "Escape") return;
  1085.         quickPopoverOpen = false;
  1086.         syncQuickPopover();
  1087.         amountInput.blur();
  1088.       });
  1089.       amountInput?.addEventListener("blur", () => {
  1090.         if (!amountInput.value) amountInput.value = "500";
  1091.         syncAmount();
  1092.       });
  1093.       promoToggleButton?.addEventListener("click", () => {
  1094.         promoSectionOpen = !promoSectionOpen;
  1095.         syncTopupExpansion();
  1096.       });
  1097.       payButton?.addEventListener("click", () => {
  1098.         quickPopoverOpen = false;
  1099.         syncQuickPopover();
  1100.         paymentSectionOpen = true;
  1101.         syncTopupExpansion();
  1102.       });
  1103.       topupQuickAmounts.forEach((button) => {
  1104.         button.addEventListener("click", () => {
  1105.           if (!amountInput) return;
  1106.           amountInput.value = button.dataset.quickAmount || "500";
  1107.           syncAmount();
  1108.           if (button.closest(".topup-quick-popover") && (!paymentSectionOpen || window.matchMedia("(min-width: 768px)").matches)) {
  1109.             quickPopoverOpen = false;
  1110.             syncQuickPopover();
  1111.           }
  1112.         });
  1113.       });
  1114.       document.addEventListener("pointerdown", (event) => {
  1115.         if (!quickPopoverOpen || amountField?.contains(event.target)) return;
  1116.         if (paymentSectionOpen && window.matchMedia("(max-width: 767px)").matches) return;
  1117.         quickPopoverOpen = false;
  1118.         syncQuickPopover();
  1119.       });
  1120.       topupPaymentMethods.forEach((method) => {
  1121.         method.addEventListener("click", () => {
  1122.           selectedPaymentFee = Number(method.dataset.fee || 0);
  1123.           topupPaymentMethods.forEach((item) => {
  1124.             const selected = item === method;
  1125.             item.classList.toggle("is-selected", selected);
  1126.             item.setAttribute("aria-checked", selected ? "true" : "false");
  1127.           });
  1128.           syncAmount();
  1129.         });
  1130.       });
  1131.       topupPromoApply?.addEventListener("click", () => {
  1132.         if (!topupPromoInput?.value.trim()) {
  1133.           topupPromoInput?.focus();
  1134.           return;
  1135.         }
  1136.         promoApplied = true;
  1137.         topupPromoBenefit?.classList.add("is-visible");
  1138.         topupPromoBenefit?.setAttribute("aria-hidden", "false");
  1139.         topupPromoApply.textContent = "Применено";
  1140.         syncAmount();
  1141.       });
  1142.       topupPromoInput?.addEventListener("input", () => {
  1143.         if (!promoApplied) return;
  1144.         promoApplied = false;
  1145.         topupPromoBenefit?.classList.remove("is-visible");
  1146.         topupPromoBenefit?.setAttribute("aria-hidden", "true");
  1147.         if (topupPromoApply) topupPromoApply.textContent = "Применить";
  1148.       });
  1149.       syncTopupExpansion();
  1150.       syncQuickPopover();
  1151.       syncAmount();
  1152.       const services = [
  1153.         ["service-steam.png", "Steam"],
  1154.         ["service-telegram.png", "Telegram"],
  1155.         ["service-roblox.png", "Roblox"],
  1156.         ["service-brawl.png", "Brawl Stars"],
  1157.         ["service-pubg.png", "PUBG Mob..."],
  1158.         ["service-appstore.png", "App Store"],
  1159.         ["service-chatgpt.png", "ChatGPT"],
  1160.         ["service-playstation.png", "PlayStation"],
  1161.         ["service-tiktok.png", "TikTok"],
  1162.         ["service-mobile.png", "Mobile Leg..."],
  1163.       ];
  1164.       const servicesRow = document.getElementById("servicesRow");
  1165.       if (servicesRow) {
  1166.         services.forEach(([icon, label, active]) => {
  1167.           servicesRow.insertAdjacentHTML(
  1168.             "beforeend",
  1169.             `<button class="service-item ${active ? "is-active" : ""}"><img src="./assets/${icon}" alt=""><span>${label}</span></button>`
  1170.           );
  1171.         });
  1172.         servicesRow.insertAdjacentHTML(
  1173.           "beforeend",
  1174.           `<button class="service-item more-service"><span class="more-icon"><img class="more-icon-img" src="./assets/more-service.svg" alt=""></span><span>еще 841</span></button>`
  1175.         );
  1176.       }
  1177.       const discountPattern = ["red", "orange", "red", "green", "red"];
  1178.       const escapeHtml = (value) =>
  1179.         String(value ?? "")
  1180.           .replaceAll("&", "&amp;")
  1181.           .replaceAll("<", "&lt;")
  1182.           .replaceAll(">", "&gt;")
  1183.           .replaceAll('"', "&quot;")
  1184.           .replaceAll("'", "&#039;");
  1185.       const catalogProducts = Array.isArray(window.playpapaCatalog?.products) ? window.playpapaCatalog.products : [];
  1186.       const fallbackProducts = [
  1187.         {
  1188.           name: "DOOM 2016 STEAM KEY РФ+СНГ",
  1189.           image_url: "./assets/product-pubg.png",
  1190.           price_label: "990 ₽",
  1191.           url: "/product",
  1192.         },
  1193.       ];
  1194.       const renderProducts = catalogProducts.length ? catalogProducts : fallbackProducts;
  1195.       const productMarkup = (product, color, index) => `
  1196.         <article class="product-card">
  1197.           <div class="product-cover">
  1198.             <img src="${escapeHtml(product.image_url || "./assets/product-pubg.png")}" alt="">
  1199.             ${product.discount_label ? `<span class="discount ${color}">${escapeHtml(product.discount_label)}</span>` : ""}
  1200.           </div>
  1201.           <div class="product-body">
  1202.             <h3>${escapeHtml(product.name || "Товар PlayPapa")}</h3>
  1203.             <div class="price-line"><strong>${escapeHtml(product.price_label || "Цена уточняется")}</strong></div>
  1204.             <a href="${escapeHtml(product.url || "/product")}">Купить</a>
  1205.           </div>
  1206.         </article>
  1207.       `;
  1208.       document.querySelectorAll("[data-products]").forEach((grid) => {
  1209.         const items = Array.from({ length: 5 }, (_, i) =>
  1210.           productMarkup(renderProducts[i % renderProducts.length], discountPattern[i % discountPattern.length], i)
  1211.         ).join("");
  1212.         grid.innerHTML = items;
  1213.       });
  1214.     </script>
  1215.     <script src="/assets/playpapa-auth.js"></script>
  1216.   </body>
  1217. </html>