| Current Path : /home/seto/888mirror.pm/ |
| Current File : /home/seto/888mirror.pm/index.php |
<?php
require_once __DIR__ . '/includes/db.php';
require_once __DIR__ . '/includes/helpers.php';
// GEO + язык через api.php (zTDS)
$z_country = $z_lang = $z_bot = '';
ob_start();
if (file_exists(__DIR__ . '/api.php')) {
include __DIR__ . '/api.php';
}
ob_end_clean();
$country = (!empty($z_country) && $z_country !== '-') ? strtolower($z_country) : '';
$lang = (!empty($z_lang) && $z_lang !== '-') ? strtolower(substr($z_lang, 0, 2)) : 'en';
$active_langs = db()->query("SELECT code FROM languages WHERE is_active=1")->fetchAll(PDO::FETCH_COLUMN);
if (!in_array($lang, $active_langs)) $lang = 'en';
// GET override (user choice > TDS)
if (!empty($_GET['lang'])) {
$get_lang = strtolower(substr(preg_replace('/[^a-zA-Z]/', '', $_GET['lang']), 0, 2));
if (in_array($get_lang, $active_langs)) $lang = $get_lang;
}
if (isset($_GET['country'])) {
$get_country = strtolower(substr(preg_replace('/[^a-zA-Z]/', '', $_GET['country']), 0, 10));
$country = $get_country;
}
// Выборка брендов
$db = db();
$geo_brands = [];
$global_brands = [];
if ($country) {
$stmt = $db->prepare("
SELECT DISTINCT b.*
FROM brands b
JOIN brand_countries bc ON bc.brand_id = b.id
WHERE b.status = 'active' AND bc.country_code = ?
ORDER BY b.is_featured DESC, b.weight DESC
");
$stmt->execute([$country]);
$geo_brands = $stmt->fetchAll();
}
// Глобальный блок — только бренды с 'en' или 'crypto', не показанные выше
$geo_ids = array_column($geo_brands, 'id');
if ($geo_ids) {
$not_in = implode(',', array_map('intval', $geo_ids));
$global_brands = $db->query("
SELECT DISTINCT b.* FROM brands b
JOIN brand_countries bc ON bc.brand_id = b.id
WHERE b.status = 'active' AND bc.country_code IN ('en','crypto') AND b.id NOT IN ($not_in)
ORDER BY b.is_featured DESC, b.weight DESC
")->fetchAll();
} else {
$global_brands = $db->query("
SELECT DISTINCT b.* FROM brands b
JOIN brand_countries bc ON bc.brand_id = b.id
WHERE b.status = 'active' AND bc.country_code IN ('en','crypto')
ORDER BY b.is_featured DESC, b.weight DESC
")->fetchAll();
}
$brands = array_merge($geo_brands, $global_brands);
$geo_brand_ids = array_flip(array_column($geo_brands, 'id'));
// Impressions
$is_bot = is_bot_request() || ($z_bot !== '' && $z_bot !== '-');
$db->prepare("INSERT INTO access_log (type, ip, country, language, user_agent, path) VALUES (?,?,?,?,?,?)")
->execute([
$is_bot ? 'bot' : 'real',
$_SERVER['REMOTE_ADDR'] ?? '',
$country ?: '-',
$lang,
$_SERVER['HTTP_USER_AGENT'] ?? '',
'/',
]);
$visitor_ip = $_SERVER['REMOTE_ADDR'] ?? '';
if (!empty($brands) && !$is_bot && !is_excluded_ip($visitor_ip) && !is_duplicate_visit($visitor_ip)) {
$visit_id = bin2hex(random_bytes(8));
$ref = referrer_domain() ?: null;
$stmt_imp = $db->prepare("INSERT INTO stats (brand_id, country, language, event_type, visit_id, referrer, ip) VALUES (?,?,?,'impression',?,?,?)");
foreach ($brands as $b) {
$stmt_imp->execute([$b['id'], $country ?: '-', $lang, $visit_id, $ref, $visitor_ip]);
}
}
// Переводы
$brand_ids = array_column($brands, 'id');
$translations = [];
if ($brand_ids) {
$in = implode(',', $brand_ids);
$rows = $db->prepare("SELECT * FROM brand_translations WHERE brand_id IN ($in) AND language_code = ?");
$rows->execute([$lang]);
foreach ($rows->fetchAll() as $t) {
$translations[$t['brand_id']] = $t;
}
$missing = array_diff($brand_ids, array_keys($translations));
if ($missing && $lang !== 'en') {
$in2 = implode(',', $missing);
$rows2 = $db->query("SELECT * FROM brand_translations WHERE brand_id IN ($in2) AND language_code = 'en'");
foreach ($rows2->fetchAll() as $t) {
$translations[$t['brand_id']] = $t;
}
}
}
// Страны для каждого бренда
$brand_countries = [];
if ($brand_ids) {
$in_c = implode(',', $brand_ids);
$rows_c = $db->query("SELECT brand_id, country_code FROM brand_countries WHERE brand_id IN ($in_c) ORDER BY country_code");
foreach ($rows_c->fetchAll() as $row) {
$brand_countries[$row['brand_id']][] = $row['country_code'];
}
}
$site_name = setting('site_name') ?: '888mirror.pm';
$site_title = $site_name . ' — Best Casino Bonuses';
$switcher_langs = db()->query("SELECT code, name FROM languages WHERE is_active=1 ORDER BY code")->fetchAll();
$lang_names = array_column($switcher_langs, 'name', 'code');
$current_lang_name = $lang_names[$lang] ?? strtoupper($lang);
$switcher_countries = db()->query("
SELECT DISTINCT bc.country_code
FROM brand_countries bc
JOIN brands b ON b.id = bc.brand_id
WHERE b.status = 'active' AND bc.country_code != 'en' AND length(bc.country_code) = 2
")->fetchAll(PDO::FETCH_COLUMN);
usort($switcher_countries, fn($a, $b) => strcasecmp(country_name($a), country_name($b)));
?>
<!DOCTYPE html>
<html lang="<?= h($lang) ?>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?= h($site_title) ?></title>
<meta name="description" content="Best casino bonuses and offers for <?= h(strtoupper($country ?: 'all regions')) ?>">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; }
body {
font-family: 'Inter', sans-serif;
background: #0f1117;
color: #e0e6f0;
min-height: 100vh;
}
/* ---- Header ---- */
.site-header {
padding: 18px 0 14px;
border-bottom: 1px solid rgba(255,255,255,.07);
margin-bottom: 28px;
}
.site-logo {
font-size: 1.25rem;
font-weight: 800;
color: #f0c040;
text-decoration: none;
letter-spacing: -.5px;
}
.site-logo span { color: #fff; }
/* ---- Switcher buttons ---- */
.sw-btn {
background: rgba(255,255,255,.06);
border: 1px solid rgba(255,255,255,.12);
color: #c8d0e0;
font-size: 12px;
font-weight: 500;
padding: 5px 12px;
border-radius: 20px;
white-space: nowrap;
cursor: pointer;
transition: border-color .2s, background .2s;
}
.sw-btn:hover { border-color: rgba(240,192,64,.5); background: rgba(240,192,64,.08); color: #f0c040; }
.sw-btn::after { content: ' ▾'; font-size: 10px; opacity: .6; }
.dropdown-menu {
background: #1e2235;
border: 1px solid rgba(255,255,255,.1);
border-radius: 10px;
box-shadow: 0 8px 32px rgba(0,0,0,.5);
padding: 6px;
max-height: 300px;
overflow-y: auto;
}
.dropdown-item {
color: #c8d0e0;
border-radius: 6px;
font-size: 13px;
padding: 6px 10px;
transition: background .15s;
}
.dropdown-item:hover, .dropdown-item:focus { background: rgba(240,192,64,.12); color: #f0c040; }
.dropdown-item.active, .dropdown-item:active { background: rgba(240,192,64,.2); color: #f0c040; }
.dropdown-divider { border-color: rgba(255,255,255,.08); margin: 4px 0; }
/* ---- Page title area ---- */
.page-heading {
font-size: 1.5rem;
font-weight: 800;
color: #fff;
margin: 0 0 4px;
line-height: 1.2;
}
.page-sub {
font-size: 13px;
color: #6b7a99;
}
/* ---- Brand card ---- */
.brand-card {
background: #1a1d2e;
border: 1px solid rgba(255,255,255,.07);
border-radius: 14px;
margin-bottom: 14px;
position: relative;
overflow: hidden;
transition: transform .22s ease, box-shadow .22s ease, border-color .22s ease;
}
.brand-card:hover {
transform: translateY(-5px);
box-shadow: 0 18px 52px rgba(0,0,0,.6), 0 0 0 1px rgba(240,192,64,.5);
border-color: rgba(240,192,64,.55);
}
/* shimmer sweep */
.brand-card::after {
content: '';
position: absolute;
top: 0; bottom: 0;
width: 45%;
background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,.04) 50%, transparent 100%);
animation: shimmer 3.5s ease-in-out infinite;
pointer-events: none;
}
@keyframes shimmer {
0% { left: -50%; }
100% { left: 130%; }
}
.card-inner { padding: 18px 18px 16px; position: relative; z-index: 1; }
/* Identity column: position + logo pill + name + stars */
.brand-identity {
width: 145px;
flex-shrink: 0;
}
.pos-num {
font-size: 11px;
font-weight: 700;
color: #3d4560;
line-height: 1;
margin-bottom: 7px;
}
.pos-num.top3 { color: #f0c040; }
/* White pill — fixes blending for any logo (white or dark) */
.logo-pill {
background: #ffffff;
border-radius: 10px;
padding: 8px 12px;
display: flex;
align-items: center;
justify-content: center;
width: 120px;
height: 80px;
}
.brand-logo {
max-width: 104px;
max-height: 64px;
width: auto;
height: auto;
object-fit: contain;
}
.logo-placeholder {
width: 120px;
height: 80px;
background: rgba(255,255,255,.06);
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 11px;
color: #6b7a99;
text-align: center;
padding: 6px;
}
.brand-name-row {
display: flex;
align-items: center;
gap: 6px;
margin-top: 8px;
flex-wrap: wrap;
}
.brand-name {
font-size: 13px;
font-weight: 700;
color: #e0e6f0;
}
.brand-year { font-size: 10px; color: #6b7a99; margin-top: 2px; }
/* Badge chip — inline next to name */
.badge-chip {
font-size: 10px;
padding: 2px 7px;
border-radius: 20px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: .5px;
white-space: nowrap;
line-height: 1.5;
}
.badge-chip.badge-best { background: linear-gradient(135deg, #f0c040, #e0a800); color: #1a1a00; }
.badge-chip.badge-new { background: linear-gradient(135deg, #4f46e5, #7c3aed); color: #fff; }
.badge-chip.badge-free { background: linear-gradient(135deg, #059669, #047857); color: #fff; }
/* Stars */
.star-rating { color: #f0c040; font-size: 12px; letter-spacing: 0; white-space: nowrap; margin-top: 4px; }
.star-off { color: #2e3350; }
/* Bonus block */
.bonus-block {
background: linear-gradient(135deg, #1d3a2a, #1a4030);
border: 1px solid rgba(52,211,153,.2);
border-radius: 10px;
padding: 10px 14px;
}
.bonus-label { font-size: 9px; font-weight: 600; text-transform: uppercase; color: #34d399; letter-spacing: 1px; }
.bonus-amount { font-size: 1.15rem; font-weight: 800; color: #f0c040; line-height: 1.2; }
.bonus-spins { font-size: 12px; color: #34d399; margin-top: 2px; }
.games-count { font-size: 12px; color: #6b7a99; margin-top: 6px; }
/* Advantages */
.adv-list { list-style: none; padding: 0; margin: 0; }
.adv-list li {
font-size: 12px;
color: #9aa3bd;
padding: 2px 0;
padding-left: 14px;
position: relative;
}
.adv-list li::before {
content: '✓';
position: absolute;
left: 0;
color: #34d399;
font-weight: 700;
}
/* License / withdraw */
.meta-item { font-size: 11px; color: #6b7a99; margin-top: 4px; }
/* CTA */
.get-bonus-btn {
display: inline-block;
background: linear-gradient(135deg, #ff6b00, #ff8c00);
color: #fff;
font-weight: 700;
font-size: 14px;
padding: 11px 22px;
border-radius: 10px;
text-decoration: none;
white-space: nowrap;
transition: transform .15s, box-shadow .15s;
box-shadow: 0 4px 16px rgba(255,107,0,.35);
border: none;
}
.get-bonus-btn:hover {
transform: scale(1.04);
box-shadow: 0 6px 24px rgba(255,107,0,.55);
color: #fff;
}
/* Badge overlay — top-right corner */
.badge-overlay {
position: absolute;
top: 10px;
right: 10px;
z-index: 5;
font-size: 10px;
padding: 3px 8px;
border-radius: 20px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: .5px;
}
.badge-best { background: linear-gradient(135deg, #f0c040, #e0a800); color: #1a1a00; }
.badge-new { background: linear-gradient(135deg, #4f46e5, #7c3aed); color: #fff; }
.badge-free { background: linear-gradient(135deg, #059669, #047857); color: #fff; }
/* Country flags strip */
.flags-strip {
display: flex;
align-items: center;
flex-wrap: nowrap;
gap: 3px;
padding: 8px 18px 10px;
border-top: 1px solid rgba(255,255,255,.05);
position: relative;
z-index: 1;
}
.flags-strip img {
border-radius: 2px;
opacity: .75;
transition: opacity .15s;
flex-shrink: 0;
}
.flags-strip img:hover { opacity: 1; }
.flags-more {
font-size: 10px;
color: #6b7a99;
white-space: nowrap;
margin-left: 2px;
cursor: pointer;
padding: 2px 5px;
border-radius: 4px;
border: 1px solid rgba(255,255,255,.1);
background: rgba(255,255,255,.04);
transition: background .15s, color .15s;
user-select: none;
}
.flags-more:hover { background: rgba(240,192,64,.1); color: #f0c040; border-color: rgba(240,192,64,.3); }
/* Popup with extra flags */
.flags-popup {
display: none;
position: absolute;
bottom: calc(100% + 6px);
left: 18px;
background: #1e2235;
border: 1px solid rgba(255,255,255,.12);
border-radius: 10px;
padding: 10px 12px;
box-shadow: 0 8px 32px rgba(0,0,0,.6);
z-index: 100;
max-width: 320px;
display: none;
flex-wrap: wrap;
gap: 4px;
}
.flags-popup.open { display: flex; }
/* Global separator — prominent */
.global-sep {
display: flex;
align-items: center;
gap: 14px;
margin: 28px 0 20px;
font-size: 13px;
font-weight: 600;
color: #9aa3bd;
letter-spacing: .2px;
}
.global-sep hr {
flex: 1;
border: none;
height: 1px;
background: linear-gradient(90deg, transparent, rgba(255,255,255,.18), transparent);
margin: 0;
opacity: 1;
}
.global-sep .sep-label {
background: rgba(255,255,255,.06);
border: 1px solid rgba(255,255,255,.13);
border-radius: 20px;
padding: 5px 16px;
white-space: nowrap;
}
/* Empty state */
.empty-state { color: #6b7a99; padding: 60px 0; text-align: center; }
/* Footer */
.site-footer {
border-top: 1px solid rgba(255,255,255,.06);
margin-top: 40px;
padding: 20px 0;
font-size: 12px;
color: #4a5568;
text-align: center;
}
/* ---- Mobile compact card ---- */
.mobile-pos { font-size: 10px; font-weight: 700; color: #3d4560; flex-shrink: 0; line-height: 1; }
.mobile-pos.top3 { color: #f0c040; }
@media (max-width: 767px) {
.brand-card { border-radius: 10px; margin-bottom: 10px; }
.card-inner { padding: 10px 12px; }
.desktop-card { display: none !important; }
.mobile-row { display: flex; align-items: center; gap: 10px; }
.flags-strip { display: none; }
.logo-pill { width: 80px; height: 54px; padding: 5px 8px; border-radius: 8px; flex-shrink: 0; }
.brand-logo { max-width: 68px; max-height: 44px; }
.logo-placeholder { width: 74px; height: 50px; border-radius: 8px; flex-shrink: 0; }
.mobile-name { font-size: 13px; font-weight: 700; color: #e0e6f0; flex: 1; min-width: 0; overflow: hidden; }
.mobile-bonus { font-size: 11px; color: #f0c040; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-top: 2px; }
.get-bonus-btn { font-size: 12px; padding: 9px 12px; border-radius: 8px; white-space: nowrap; flex-shrink: 0; }
.badge-overlay { font-size: 9px; padding: 2px 6px; top: 8px; right: 8px; }
.global-sep { margin: 16px 0 12px; font-size: 12px; }
.global-sep .sep-label { padding: 4px 12px; }
.page-heading { font-size: 1.25rem; }
}
@media (min-width: 768px) {
.mobile-row { display: none; }
.desktop-card { display: flex; align-items: center; gap: 16px; }
}
</style>
</head>
<body>
<!-- Header -->
<header class="site-header">
<div class="container" style="max-width:920px">
<div class="d-flex align-items-center justify-content-between gap-2 flex-wrap">
<a class="site-logo" href="?lang=<?= h($lang) ?>">888<span>mirror</span></a>
<div class="d-flex align-items-center gap-2 flex-wrap">
<!-- Lang switcher -->
<div class="dropdown">
<button class="sw-btn" data-bs-toggle="dropdown" aria-expanded="false"
style="max-width:130px;overflow:hidden;text-overflow:ellipsis">
Lang: <?= h($current_lang_name) ?>
</button>
<ul class="dropdown-menu dropdown-menu-end" style="min-width:160px">
<?php foreach ($switcher_langs as $sl): ?>
<li>
<a class="dropdown-item <?= $sl['code'] === $lang ? 'active' : '' ?>"
href="?lang=<?= h($sl['code']) ?><?= $country ? '&country='.h($country) : '' ?>">
<?= h($sl['name']) ?>
</a>
</li>
<?php endforeach ?>
</ul>
</div>
<!-- Country switcher -->
<div class="dropdown">
<button class="sw-btn d-flex align-items-center gap-1" data-bs-toggle="dropdown" aria-expanded="false">
<?php if ($country === 'crypto'): ?>
📍 💎 Crypto
<?php elseif ($country === 'en'): ?>
📍 🌐 Global
<?php elseif ($country): ?>
📍
<img src="https://flagicons.lipis.dev/flags/4x3/<?= h($country) ?>.svg"
width="16" height="12" style="border-radius:2px;vertical-align:middle"
data-cc-flag="<?= h($country) ?>" alt="<?= h(strtoupper($country)) ?>">
<?= h(strtoupper($country)) ?>
<?php else: ?>
📍 All regions
<?php endif ?>
</button>
<ul class="dropdown-menu dropdown-menu-end" style="min-width:170px">
<li>
<a class="dropdown-item <?= !$country ? 'active' : '' ?>"
href="?lang=<?= h($lang) ?>">🌍 All regions</a>
</li>
<li><hr class="dropdown-divider"></li>
<?php foreach ($switcher_countries as $cc): ?>
<li>
<a class="dropdown-item d-flex align-items-center gap-2 <?= $cc === $country ? 'active' : '' ?>"
href="?country=<?= h($cc) ?>&lang=<?= h($lang) ?>">
<img src="https://flagicons.lipis.dev/flags/4x3/<?= h($cc) ?>.svg"
width="18" height="14" style="border-radius:2px;flex-shrink:0"
alt="<?= h(country_name($cc)) ?>">
<span><?= h(country_name($cc)) ?></span>
</a>
</li>
<?php endforeach ?>
<li><hr class="dropdown-divider"></li>
<li>
<a class="dropdown-item <?= $country === 'en' ? 'active' : '' ?>"
href="?country=en&lang=<?= h($lang) ?>">🌐 Global</a>
</li>
<li>
<a class="dropdown-item <?= $country === 'crypto' ? 'active' : '' ?>"
href="?country=crypto&lang=<?= h($lang) ?>">💎 Crypto</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</header>
<div class="container" style="max-width:920px">
<!-- Page heading -->
<div class="mb-4">
<h1 class="page-heading">
Best Casino Bonuses
<?php if ($country === 'crypto'): ?>
<span style="font-size:1.2rem">💎</span>
<?php elseif ($country === 'en'): ?>
<span style="font-size:1.2rem">🌐</span>
<?php elseif ($country): ?>
<img src="https://flagicons.lipis.dev/flags/4x3/<?= h($country) ?>.svg"
width="26" height="20" style="border-radius:3px;box-shadow:0 0 4px rgba(0,0,0,.4);vertical-align:middle"
data-cc-flag="<?= h($country) ?>" alt="<?= h(strtoupper($country)) ?>">
<?php else: ?>
<span style="font-size:1.2rem">🌍</span>
<?php endif ?>
</h1>
<p class="page-sub">
<?php if ($country === 'crypto'): ?>
Showing crypto-friendly casinos
<?php elseif ($country): ?>
Showing offers for: <strong id="country-name" style="color:#c8d0e0">
<?php if ($country === 'en'): ?>Global<?php else: ?><?= h(strtoupper($country)) ?><?php endif ?>
</strong>
<?php else: ?>
All available offers
<?php endif ?>
</p>
</div>
<!-- Brand list -->
<?php
$showed_global_sep = false;
$card_index = 0;
foreach ($brands as $brand):
$t = $translations[$brand['id']] ?? [];
$adv = !empty($t['advantages']) ? json_decode_safe($t['advantages']) : [];
$pm = !empty($t['payment_methods']) ? json_decode_safe($t['payment_methods']) : [];
$is_global = !empty($geo_brands) && !isset($geo_brand_ids[$brand['id']]);
$card_index++;
$stars = max(1, min(5, (int)ceil($brand['weight'] / 20)));
$shimmer_delay = round(($card_index - 1) * 0.4, 1);
$badge_class = '';
if ($brand['badge'] === 'best') $badge_class = 'badge-best';
elseif ($brand['badge'] === 'new') $badge_class = 'badge-new';
elseif ($brand['badge'] === 'free') $badge_class = 'badge-free';
if ($is_global && !$showed_global_sep):
$showed_global_sep = true;
?>
<div class="global-sep">
<hr><span class="sep-label">🌍 Also available globally</span><hr>
</div>
<?php endif; ?>
<div class="brand-card" style="--shimmer-delay:<?= $shimmer_delay ?>s">
<?php if ($brand['badge'] && $badge_class): ?>
<span class="badge-overlay <?= $badge_class ?>">
<?php if ($brand['badge'] === 'best'): ?>⭐ Best
<?php elseif ($brand['badge'] === 'new'): ?>New
<?php elseif ($brand['badge'] === 'free'): ?>Free
<?php endif ?>
</span>
<?php endif ?>
<div class="card-inner">
<!-- DESKTOP layout -->
<div class="desktop-card">
<!-- Identity: position + logo pill + name + stars -->
<div class="brand-identity">
<div class="pos-num <?= $card_index <= 3 ? 'top3' : '' ?>">#<?= $card_index ?></div>
<?php if ($brand['logo']): ?>
<div class="logo-pill">
<img src="<?= h($brand['logo']) ?>" class="brand-logo" alt="<?= h($brand['name']) ?>">
</div>
<?php else: ?>
<div class="logo-placeholder"><?= h($brand['name']) ?></div>
<?php endif ?>
<div class="brand-name-row">
<span class="brand-name"><?= h($brand['name']) ?></span>
</div>
<div class="star-rating">
<?php for ($i = 1; $i <= 5; $i++): ?>
<span class="<?= $i <= $stars ? '' : 'star-off' ?>">★</span>
<?php endfor ?>
</div>
<?php if ($brand['year_founded']): ?>
<div class="brand-year">Est. <?= (int)$brand['year_founded'] ?></div>
<?php endif ?>
</div>
<!-- Bonus -->
<div style="width:200px;flex-shrink:0">
<?php if (!empty($t['bonus'])): ?>
<div class="bonus-block">
<div class="bonus-label">Welcome Bonus</div>
<div class="bonus-amount"><?= h($t['bonus']) ?></div>
<?php if (!empty($t['free_spins'])): ?>
<div class="bonus-spins">+ <?= h($t['free_spins']) ?></div>
<?php endif ?>
</div>
<?php endif ?>
<?php if (!empty($t['games_count'])): ?>
<div class="games-count">🎮 <?= h($t['games_count']) ?> games</div>
<?php endif ?>
</div>
<!-- Advantages + meta -->
<div style="flex:1;min-width:0">
<?php if ($adv): ?>
<ul class="adv-list">
<?php foreach (array_slice($adv, 0, 4) as $a): ?>
<li><?= h($a) ?></li>
<?php endforeach ?>
</ul>
<?php endif ?>
<?php if (!empty($t['license'])): ?>
<div class="meta-item">🔒 <?= h($t['license']) ?></div>
<?php endif ?>
<?php if (!empty($t['min_withdraw']) || !empty($t['max_withdraw'])): ?>
<div class="meta-item">
💳 <?= h($t['min_withdraw'] ?? '') ?>
<?php if (!empty($t['max_withdraw'])): ?>— <?= h($t['max_withdraw']) ?><?php endif ?>
</div>
<?php endif ?>
</div>
<!-- CTA -->
<div style="flex-shrink:0;text-align:center">
<a href="/click.php?id=<?= (int)$brand['id'] ?>"
class="get-bonus-btn" target="_blank" rel="noopener noreferrer">
Get Bonus →
</a>
<?php if (!empty($t['currencies'])): ?>
<div class="meta-item mt-2"><?= h($t['currencies']) ?></div>
<?php endif ?>
</div>
</div><!-- /desktop-card -->
<!-- MOBILE layout (compact row) -->
<div class="mobile-row">
<span class="mobile-pos <?= $card_index <= 3 ? 'top3' : '' ?>">#<?= $card_index ?></span>
<?php if ($brand['logo']): ?>
<div class="logo-pill">
<img src="<?= h($brand['logo']) ?>" class="brand-logo" alt="<?= h($brand['name']) ?>">
</div>
<?php else: ?>
<div class="logo-placeholder"><?= h(substr($brand['name'],0,3)) ?></div>
<?php endif ?>
<div class="mobile-name">
<div><?= h($brand['name']) ?></div>
<?php if (!empty($t['bonus'])): ?>
<div class="mobile-bonus"><?= h($t['bonus']) ?></div>
<?php endif ?>
</div>
<a href="/click.php?id=<?= (int)$brand['id'] ?>"
class="get-bonus-btn" target="_blank" rel="noopener noreferrer">
Bonus →
</a>
</div><!-- /mobile-row -->
</div><!-- /card-inner -->
<?php
$bcodes = $brand_countries[$brand['id']] ?? [];
if ($bcodes):
$real_flags = array_filter($bcodes, fn($c) => strlen($c) === 2 && $c !== 'en');
$has_crypto = in_array('crypto', $bcodes);
$has_global = in_array('en', $bcodes);
// Текущая страна — первой (если она есть в списке)
if ($country && strlen($country) === 2 && in_array($country, $real_flags)) {
$real_flags = array_merge([$country], array_filter($real_flags, fn($c) => $c !== $country));
}
$all_flags = array_values($real_flags);
$limit_desk = 20;
$limit_mob = 8;
$extra_desk = max(0, count($all_flags) - $limit_desk);
$extra_mob = max(0, count($all_flags) - $limit_mob);
?>
<div class="flags-strip">
<?php if ($has_crypto): ?>
<span title="Crypto" style="font-size:13px;line-height:1">💎</span>
<?php endif ?>
<?php if ($has_global): ?>
<span title="Global" style="font-size:13px;line-height:1">🌐</span>
<?php endif ?>
<?php
$popup_flags = [];
foreach ($all_flags as $i => $cc):
$is_active = ($cc === $country);
$active_sty = $is_active ? 'opacity:1;box-shadow:0 0 0 1px rgba(240,192,64,.6);border-radius:2px' : '';
// Visible on desktop (0-19), visible on mobile (0-7), rest go to popup
$desk_vis = $i < $limit_desk;
$mob_vis = $i < $limit_mob;
if (!$desk_vis) { $popup_flags[] = $cc; continue; }
$cls = $mob_vis ? '' : 'd-none d-md-inline';
?>
<img src="https://flagicons.lipis.dev/flags/4x3/<?= h($cc) ?>.svg"
width="16" height="12"
data-cc-flag="<?= h($cc) ?>"
alt="<?= h(strtoupper($cc)) ?>"
title="<?= h(strtoupper($cc)) ?>"
<?= $cls ? 'class="'.$cls.'"' : '' ?>
<?= $active_sty ? 'style="'.$active_sty.'"' : '' ?>>
<?php endforeach ?>
<?php $uid = 'fp_' . $brand['id'] ?>
<?php if ($extra_desk > 0): ?>
<span class="flags-more d-none d-md-inline" onclick="toggleFlagsPopup('<?= $uid ?>')">+<?= $extra_desk ?> more</span>
<?php endif ?>
<?php if ($extra_mob > 0): ?>
<span class="flags-more d-md-none" onclick="toggleFlagsPopup('<?= $uid ?>')">+<?= $extra_mob ?> more</span>
<?php endif ?>
<?php if ($popup_flags): ?>
<div class="flags-popup" id="<?= $uid ?>">
<?php foreach ($popup_flags as $cc): ?>
<img src="https://flagicons.lipis.dev/flags/4x3/<?= h($cc) ?>.svg"
width="16" height="12"
data-cc-flag="<?= h($cc) ?>"
alt="<?= h(strtoupper($cc)) ?>"
title="<?= h(strtoupper($cc)) ?>"
style="border-radius:2px;opacity:.8">
<?php endforeach ?>
</div>
<?php endif ?>
</div>
<?php endif ?>
</div><!-- /brand-card -->
<?php endforeach ?>
<?php if (empty($brands)): ?>
<div class="empty-state">
<p>No offers available at the moment.</p>
</div>
<?php endif ?>
</div><!-- /container -->
<footer class="site-footer">
© <?= date('Y') ?> <?= h($site_name) ?> — 18+ | Gamble responsibly
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
// Flags popup toggle
function toggleFlagsPopup(id) {
var popup = document.getElementById(id);
if (!popup) return;
popup.classList.toggle('open');
}
// Close popup when clicking outside
document.addEventListener('click', function(e) {
if (!e.target.classList.contains('flags-more')) {
document.querySelectorAll('.flags-popup.open').forEach(function(p) {
p.classList.remove('open');
});
}
});
// Apply staggered shimmer delays
document.querySelectorAll('.brand-card').forEach(function(card) {
var delay = card.style.getPropertyValue('--shimmer-delay') || '0s';
card.style.setProperty('--shimmer-delay', delay);
});
// Override shimmer animation-delay via inline style on ::after via CSS var
(function() {
var style = document.createElement('style');
document.querySelectorAll('.brand-card').forEach(function(card, i) {
card.dataset.idx = i;
});
var rules = '';
document.querySelectorAll('.brand-card').forEach(function(card, i) {
var delay = (i * 0.4).toFixed(1);
rules += '.brand-card[data-idx="' + i + '"]::after { animation-delay: ' + delay + 's; }\n';
});
style.textContent = rules;
document.head.appendChild(style);
})();
// Country name in header (native locale)
<?php if ($country && !in_array($country, ['en','crypto'])): ?>
try {
var el = document.getElementById('country-name');
if (el) {
var n = new Intl.DisplayNames([navigator.language || 'en'], {type: 'region'});
var v = n.of('<?= strtoupper($country) ?>');
if (v) el.textContent = v;
}
} catch(e) {}
<?php endif ?>
// Alt + title для флажков в карточках (на языке текущей страницы)
(function() {
var uiLang = navigator.language || 'en';
document.querySelectorAll('[data-cc-flag]').forEach(function(img) {
var cc = img.getAttribute('data-cc-flag');
try {
var n = new Intl.DisplayNames([uiLang], {type: 'region'});
var v = n.of(cc.toUpperCase());
if (v) { img.alt = v; img.title = v; }
} catch(e) {}
});
})();
</script>
<?= render_footer() ?>
</body>
</html>