| Current Path : /home/seto/888mirror.pm/ |
| Current File : /home/seto/888mirror.pm/widget-api.php |
<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET');
header('Content-Type: text/html; charset=UTF-8');
header('Cache-Control: no-store, no-cache, must-revalidate');
require_once __DIR__ . '/includes/db.php';
require_once __DIR__ . '/includes/helpers.php';
$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';
if (!empty($_GET['lang'])) {
$gl = strtolower(substr(preg_replace('/[^a-zA-Z]/', '', $_GET['lang']), 0, 2));
if (in_array($gl, $active_langs)) $lang = $gl;
}
if (isset($_GET['country'])) {
$country = strtolower(substr(preg_replace('/[^a-zA-Z]/', '', $_GET['country']), 0, 10));
}
// Загрузить настройки виджета из БД
$ws = [];
try {
foreach (db()->query("SELECT key, value FROM widget_settings")->fetchAll() as $r) {
$ws[$r['key']] = $r['value'];
}
} catch (Exception $e) {}
$layout = in_array($ws['default_layout'] ?? '', ['table','cards','ranked','liquid','random'])
? $ws['default_layout'] : 'table';
if (isset($_GET['layout']) && in_array($_GET['layout'], ['table','cards','ranked','liquid'])) {
$layout = $_GET['layout']; // data-layout в embed-коде переопределяет настройку
}
// Random: выбрать один из 4 случайно (на сервере, до записи в stats)
if ($layout === 'random') {
$layout = ['table','cards','ranked','liquid'][array_rand(['table','cards','ranked','liquid'])];
}
$brand_limit = max(0, (int)($ws['brand_limit'] ?? 0));
// Referrer hostname (передаётся widget.js)
$ref_host = '';
if (!empty($_GET['ref'])) {
$ref_host = substr(preg_replace('/[^a-zA-Z0-9.\-]/', '', $_GET['ref']), 0, 100);
}
// Автоматический язык по стране
$cc_to_lang = [
'ua'=>'uk','gb'=>'en','us'=>'en','au'=>'en','ca'=>'en','ie'=>'en','nz'=>'en','za'=>'en',
'br'=>'pt','pt'=>'pt','ch'=>'de','at'=>'de',
'be'=>'fr','mx'=>'es','ar'=>'es','cl'=>'es','co'=>'es','pe'=>'es','ve'=>'es',
'bo'=>'es','py'=>'es','uy'=>'es','ec'=>'es','cr'=>'es','gt'=>'es','hn'=>'es',
'ni'=>'es','pa'=>'es','sv'=>'es','do'=>'es',
'cn'=>'zh','tw'=>'zh','hk'=>'zh','jp'=>'ja','kr'=>'ko',
'no'=>'nb','se'=>'sv','dk'=>'da','cz'=>'cs','sk'=>'sk',
];
// Если язык не был задан вручную, определяем по стране
if (!isset($_GET['lang']) && $country && isset($cc_to_lang[$country])) {
$guessed = $cc_to_lang[$country];
if (in_array($guessed, $active_langs)) $lang = $guessed;
}
$db = db();
$geo_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();
}
$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='en' 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='en'
ORDER BY b.is_featured DESC, b.weight DESC
")->fetchAll();
}
// Применить лимит брендов (geo заполняется первым, остаток — global)
if ($brand_limit > 0) {
$geo_brands = array_slice($geo_brands, 0, $brand_limit);
$global_remain = $brand_limit - count($geo_brands);
$global_brands = $global_remain > 0 ? array_slice($global_brands, 0, $global_remain) : [];
}
$brands = array_merge($geo_brands, $global_brands);
// Записать показы виджета
if ($brands) {
$visit_id = bin2hex(random_bytes(8));
$stmt_wimp = $db->prepare("INSERT INTO widget_stats (brand_id, country, language, event_type, visit_id, ref_host, widget_layout) VALUES (?,?,?,'impression',?,?,?)");
foreach ($brands as $wb) {
$stmt_wimp->execute([$wb['id'], $country ?: '-', $lang, $visit_id, $ref_host, $layout]);
}
}
$translations = [];
if ($brands) {
$ids = implode(',', array_map(fn($b) => (int)$b['id'], $brands));
$rows = $db->query("SELECT * FROM brand_translations WHERE brand_id IN ($ids) AND language_code IN ('$lang','en')")->fetchAll();
foreach ($rows as $row) {
$bid = $row['brand_id']; $lc = $row['language_code'];
if (!isset($translations[$bid]) || $lc === $lang) $translations[$bid] = $row;
}
}
$brand_country_counts = [];
$brand_country_codes = [];
if ($brands) {
$ids = implode(',', array_map(fn($b) => (int)$b['id'], $brands));
$rows = $db->query("
SELECT brand_id, COUNT(*) as cnt FROM brand_countries
WHERE brand_id IN ($ids) AND country_code NOT IN ('en','crypto')
GROUP BY brand_id
")->fetchAll();
foreach ($rows as $row) $brand_country_counts[$row['brand_id']] = (int)$row['cnt'];
$rows2 = $db->query("
SELECT brand_id, country_code FROM brand_countries
WHERE brand_id IN ($ids) AND country_code NOT IN ('en','crypto')
ORDER BY brand_id
")->fetchAll();
foreach ($rows2 as $row) $brand_country_codes[$row['brand_id']][] = $row['country_code'];
}
$cta_map = [
'ru'=>'Получить бонус','de'=>'Bonus holen','fr'=>'Obtenir le bonus','es'=>'Obtener bono',
'pt'=>'Obter bônus','pl'=>'Odbierz bonus','tr'=>'Bonus al','uk'=>'Отримати бонус',
'it'=>'Ottieni bonus','ro'=>'Obține bonus','cs'=>'Získat bonus','fi'=>'Hae bonus',
'sv'=>'Hämta bonus','nb'=>'Få bonus','da'=>'Få bonus',
];
$cta_text = $cta_map[$lang] ?? 'Get Bonus';
$avail_map = [
'ru'=>'Доступно в','de'=>'Verfügbar in','fr'=>'Disponible en','es'=>'Disponible en',
'pt'=>'Disponível em','pl'=>'Dostępne w','tr'=>'Şurada mevcut','uk'=>'Доступно в',
'it'=>'Disponibile in','ro'=>'Disponibil în','cs'=>'Dostupné v','fi'=>'Saatavilla',
'sv'=>'Tillgänglig i','nb'=>'Tilgjengelig i','da'=>'Tilgängelig i',
];
$avail_label = $avail_map[$lang] ?? 'Available in';
// Countries with at least one active brand (for GEO selector)
$active_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 NOT IN ('en','crypto')
ORDER BY bc.country_code
")->fetchAll(PDO::FETCH_COLUMN);
function flag_emoji(string $cc): string {
$cc = strtoupper($cc);
$out = '';
for ($i = 0; $i < strlen($cc); $i++) {
$out .= mb_chr(0x1F1E6 + ord($cc[$i]) - ord('A'));
}
return $out;
}
function weight_stars(int $w): string {
$n = min(5, max(1, (int)ceil($w / 20)));
return str_repeat('★', $n) . str_repeat('☆', 5 - $n);
}
function geo_flag(string $cc): string {
if (!$cc) return '';
return '<img src="https://flagicons.lipis.dev/flags/4x3/' . h($cc) . '.svg"'
. ' width="18" height="13" style="border-radius:2px;vertical-align:middle"'
. ' title="' . h(country_name($cc)) . '">';
}
function logo_bg_gradient(int $id, string $custom = ''): string {
if ($custom) return $custom;
static $palette = [
'rgba(59,130,246,.85)', 'rgba(139,92,246,.85)', 'rgba(236,72,153,.85)', 'rgba(239,68,68,.85)',
'rgba(249,115,22,.85)', 'rgba(234,179,8,.85)', 'rgba(34,197,94,.85)', 'rgba(6,182,212,.85)',
'rgba(99,102,241,.85)', 'rgba(168,85,247,.85)', 'rgba(20,184,166,.85)', 'rgba(244,63,94,.85)',
'rgba(16,185,129,.85)', 'rgba(132,204,22,.85)', 'rgba(14,165,233,.85)', 'rgba(217,70,239,.85)',
];
return $palette[$id % count($palette)];
}
function make_brand_entry(array $b, int $idx, array $translations, array $brand_country_counts, array $brand_country_codes, string $country): array {
$tr = $translations[$b['id']] ?? [];
$adv = json_decode_safe($tr['advantages'] ?? '[]');
$country_count = $brand_country_counts[$b['id']] ?? 0;
$more_countries = max(0, $country_count - ($country ? 1 : 0));
$all_codes = $brand_country_codes[$b['id']] ?? [];
return [
'b' => $b,
'bonus' => $tr['bonus'] ?? '',
'free_spins' => $tr['free_spins'] ?? '',
'games_count' => $tr['games_count'] ?? '',
'license' => $tr['license'] ?? '',
'currencies' => $tr['currencies'] ?? '',
'adv' => $adv,
'stars' => weight_stars((int)$b['weight']),
'click' => 'https://888mirror.pm/click.php?id=' . (int)$b['id']
. '&src=widget&wl=' . urlencode($layout) . '&ref=' . urlencode($ref_host),
'logo' => $b['logo'] ? (strpos($b['logo'], 'http') === 0 ? $b['logo'] : 'https://888mirror.pm' . $b['logo']) : '',
'year_founded' => isset($b['year_founded']) && $b['year_founded'] ? (int)$b['year_founded'] : null,
'more_countries' => $more_countries,
'country_codes' => $all_codes,
'idx' => $idx,
];
}
$geo_brand_data = [];
foreach ($geo_brands as $i => $b) {
$geo_brand_data[] = make_brand_entry($b, $i, $translations, $brand_country_counts, $brand_country_codes, $country);
}
$global_brand_data = [];
$g_offset = count($geo_brand_data);
foreach ($global_brands as $i => $b) {
$global_brand_data[] = make_brand_entry($b, $g_offset + $i, $translations, $brand_country_counts, $brand_country_codes, $country);
}
$sections = [];
if ($geo_brand_data) $sections[] = ['data' => $geo_brand_data, 'header' => null];
if ($global_brand_data) $sections[] = ['data' => $global_brand_data, 'header' => '🌍 Also available globally'];
?>
<style>
/* ── CSS Variables ──────────────────────────────────────── */
.cw-widget {
--bg: #0e0e1a;
--surface: #181828;
--surface2: #1f1f35;
--border: rgba(255,255,255,0.08);
--border2: rgba(255,255,255,0.05);
--text: #e8e8f0;
--text2: rgba(255,255,255,0.5);
--text3: rgba(255,255,255,0.28);
--gold: #ffd700;
--gold-dim: rgba(255,215,0,0.15);
--gold-border: rgba(255,215,0,0.3);
--gold-text: #ffe566;
--green: #4ade80;
--accent: #ff2d55;
--logo-bg: #ffffff;
--shadow: 0 8px 32px rgba(0,0,0,0.4);
font-family: 'Inter', -apple-system, sans-serif;
background: var(--bg); color: var(--text);
padding: 24px 16px;
box-sizing: border-box;
}
.cw-widget.cw-light {
--bg: #f0f2f8;
--surface: #ffffff;
--surface2: #f7f8fc;
--border: rgba(0,0,0,0.08);
--border2: rgba(0,0,0,0.05);
--text: #1a1a2e;
--text2: rgba(0,0,0,0.5);
--text3: rgba(0,0,0,0.28);
--gold: #d4900a;
--gold-dim: rgba(212,144,10,0.08);
--gold-border: rgba(212,144,10,0.25);
--gold-text: #8a5c00;
--green: #16a34a;
--logo-bg: #f0f2f8;
--shadow: 0 4px 20px rgba(0,0,0,0.1);
}
.cw-widget *, .cw-widget *::before, .cw-widget *::after { box-sizing: border-box; }
/* ── Button ─────────────────────────────────────────────── */
.cw-widget .btn-cta {
display: inline-flex; align-items: center; justify-content: center; gap: 6px;
padding: 11px 22px; border-radius: 8px;
background: linear-gradient(135deg, #ff2d55 0%, #c026d3 100%);
color: #fff !important; font-size: 13px; font-weight: 700; letter-spacing: .3px;
text-decoration: none !important; border: none; cursor: pointer;
box-shadow: 0 3px 12px rgba(255,45,85,.35);
transition: transform .15s, box-shadow .15s;
position: relative; overflow: hidden; white-space: nowrap;
}
.cw-widget .btn-cta:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(255,45,85,.45); }
/* ── Logo pill ──────────────────────────────────────────── */
.cw-widget .logo-pill {
background: var(--logo-bg); border-radius: 8px;
display: flex; align-items: center; justify-content: center;
overflow: hidden; flex-shrink: 0;
}
.cw-widget .logo-pill img { object-fit: contain; display: block; }
.cw-widget .logo-pill-text { font-weight: 800; color: #1a1a2e; }
/* ── Stars / badge / pro ────────────────────────────────── */
.cw-widget .stars { color: var(--gold); letter-spacing: 1px; font-size: 13px; }
.cw-widget .badge {
display: inline-flex; align-items: center; gap: 3px;
font-size: 9px; font-weight: 800; text-transform: uppercase; letter-spacing: .9px;
padding: 3px 9px; border-radius: 20px; vertical-align: middle; white-space: nowrap;
}
.cw-widget .badge-best {
background: linear-gradient(135deg, #f59e0b, #f97316);
color: #fff;
box-shadow: 0 2px 10px rgba(249,115,22,.45);
}
.cw-widget .badge-new {
background: linear-gradient(135deg, #6366f1, #8b5cf6);
color: #fff;
box-shadow: 0 2px 10px rgba(139,92,246,.45);
}
.cw-widget .badge-free {
background: linear-gradient(135deg, #10b981, #06b6d4);
color: #fff;
box-shadow: 0 2px 10px rgba(16,185,129,.4);
}
.cw-widget .pro { font-size: 12px; color: var(--text2); display: flex; align-items: flex-start; gap: 5px; line-height: 1.4; }
.cw-widget .pro::before { content: '✓'; color: var(--green); font-weight: 700; flex-shrink: 0; font-size: 11px; margin-top: 1px; }
/* ════════════════ TABLE ════════════════════════════════════ */
@keyframes cwRowFadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.cw-widget .t-table { display: flex; flex-direction: column; gap: 8px; }
.cw-widget .t-row {
display: grid;
grid-template-columns: 180px 1fr 200px 150px 160px;
align-items: stretch;
background: var(--surface); border: 1px solid var(--border);
border-radius: 12px; overflow: hidden;
transition: transform .15s, box-shadow .15s, background .15s, opacity .1s; cursor: pointer;
animation: cwRowFadeIn .35s ease both;
animation-delay: calc(var(--row-idx, 0) * 55ms);
}
.cw-widget .t-row:hover { transform: translateY(-2px); box-shadow: var(--shadow); background: var(--surface2); }
.cw-widget .t-row:active { opacity: .75; transform: scale(0.995); transition: opacity .05s, transform .05s; }
/* Top-3 */
.cw-widget .t-row.rank-1 { border-left: 3px solid #fbbf24; }
.cw-widget .t-row.rank-2 { border-left: 3px solid #94a3b8; }
.cw-widget .t-row.rank-3 { border-left: 3px solid #cd7f32; }
/* Featured */
.cw-widget .t-row.t-featured {
border-color: rgba(99,102,241,.4);
background: linear-gradient(to right, rgba(99,102,241,.06), var(--surface));
}
.cw-widget .t-row.t-featured:hover { background: linear-gradient(to right, rgba(99,102,241,.1), var(--surface2)); }
/* Mobile GEO flag */
.cw-widget .t-brand-geo-mobile { display: none; }
.cw-widget .t-num { display: none; }
.cw-widget .t-logo { padding: 16px 12px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
border-right: 1px solid var(--border2); }
.cw-widget .t-logo-meta { font-size: 10px; color: var(--text3); letter-spacing: .3px; }
.cw-widget .t-brand { padding: 16px; display: flex; flex-direction: column; justify-content: center; gap: 5px; }
.cw-widget .t-brand-year { font-size: 10px; font-weight: 400; color: var(--text3); vertical-align: middle; }
.cw-widget .t-brand-name { font-size: 15px; font-weight: 700; }
.cw-widget .t-brand-row { display: flex; align-items: center; gap: 6px; }
.cw-widget .t-bonus { padding: 16px; display: flex; flex-direction: column; justify-content: center; gap: 6px;
border-left: 1px solid var(--border2); border-right: 1px solid var(--border2); }
.cw-widget .t-bonus-inner { background: var(--surface2); border: 1px solid var(--border); border-radius: 10px; padding: 10px 14px; }
.cw-widget .t-bonus-label { font-size: 9px; font-weight: 600; text-transform: uppercase; color: var(--green); letter-spacing: 1px; margin-bottom: 3px; }
.cw-widget .t-bonus-text { font-size: 15px; font-weight: 800; color: var(--accent); line-height: 1.2; }
.cw-widget .t-bonus-spins { font-size: 12px; color: var(--green); margin-top: 2px; }
.cw-widget .t-geo { padding: 14px 16px; display: flex; flex-direction: column; justify-content: center; gap: 8px;
border-right: 1px solid var(--border2); }
.cw-widget .t-geo-label { font-size: 9px; font-weight: 600; text-transform: uppercase; letter-spacing: .7px; color: var(--text3); }
.cw-widget .t-geo-flags { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; }
.cw-widget .t-geo-flags img { border-radius: 2px; }
.cw-widget .t-geo-country { font-size: 12px; font-weight: 500; }
.cw-widget .t-geo-more { font-size: 11px; color: var(--text3); }
.cw-widget .t-action { padding: 16px; display: flex; flex-direction: column;
align-items: center; justify-content: center; gap: 8px; }
.cw-widget .t-avail { display: flex; align-items: center; gap: 5px; font-size: 11px; color: var(--text3); }
.cw-widget .t-action .btn-cta { width: 100%; justify-content: center; }
/* ── Tablet ≤900px: лого | бренд | бонус | geo | кнопка (один ряд) ── */
@media (max-width: 900px) {
.cw-widget .t-row {
grid-template-columns: 136px 1fr 170px 120px 120px;
grid-template-rows: auto;
min-height: 88px;
}
.cw-widget .t-num { display: none; }
.cw-widget .t-logo {
grid-column: 1; grid-row: 1;
border-right: 1px solid var(--border2);
padding: 12px 10px;
justify-content: center;
}
.cw-widget .t-logo .logo-pill { width: 112px !important; height: 80px !important; }
.cw-widget .t-logo .logo-pill img { width: 112px !important; height: 64px !important; object-fit: contain; }
.cw-widget .t-brand {
grid-column: 2; grid-row: 1;
padding: 14px 14px;
border: none;
justify-content: center;
}
.cw-widget .t-brand-name { font-size: 14px; }
.cw-widget .t-brand .pro { display: none; }
.cw-widget .t-bonus {
grid-column: 3; grid-row: 1;
padding: 14px 12px;
border-left: 1px solid var(--border2);
border-right: none;
align-items: flex-start;
justify-content: center;
}
.cw-widget .t-bonus-text { font-size: 13px; }
.cw-widget .t-stats { display: none; }
.cw-widget .t-geo {
grid-column: 4; grid-row: 1;
display: flex;
padding: 14px 10px;
border-left: 1px solid var(--border2);
}
.cw-widget .t-geo-label { font-size: 8px; }
.cw-widget .t-geo-flags img { width: 20px !important; height: 15px !important; }
.cw-widget .t-geo-country { font-size: 11px; }
.cw-widget .t-action {
grid-column: 5; grid-row: 1;
border-left: 1px solid var(--border2);
padding: 14px 12px;
gap: 8px;
}
.cw-widget .t-avail { font-size: 10px; }
.cw-widget .t-action .btn-cta { font-size: 12px; padding: 11px 14px; width: 100%; }
}
/* ── Mobile ≤480px: лого сверху на всю ширину, контент стопкой ── */
@media (max-width: 480px) {
.cw-widget .t-row {
display: flex; flex-direction: column;
min-height: 0;
border-radius: 10px;
}
.cw-widget .t-num { display: none; }
.cw-widget .t-logo {
width: 100%; border-right: none; border-bottom: 1px solid var(--border2);
padding: 0; justify-content: center; align-items: stretch;
}
.cw-widget .t-logo .logo-pill { width: 100% !important; height: 90px !important; border-radius: 0 !important; }
.cw-widget .t-logo .logo-pill img { width: auto !important; max-width: 70% !important; height: 64px !important; object-fit: contain; }
.cw-widget .t-brand {
padding: 14px 16px 6px;
border: none;
align-items: center;
text-align: center;
}
.cw-widget .t-brand-name { font-size: 16px; }
.cw-widget .t-brand .pro { display: none; }
.cw-widget .t-brand-row { justify-content: center; }
.cw-widget .t-brand-row .stars { font-size: 14px; }
.cw-widget .t-brand-geo-mobile { display: flex; align-items: center; justify-content: center; gap: 5px; font-size: 11px; color: var(--text2); margin-top: 4px; }
.cw-widget .t-bonus {
padding: 8px 16px 12px;
border: none;
align-items: center;
}
.cw-widget .t-bonus-inner { padding: 10px 16px; text-align: center; width: 100%; }
.cw-widget .t-bonus-label { text-align: center; }
.cw-widget .t-bonus-text { font-size: 16px; text-align: center; }
.cw-widget .t-bonus-spins { text-align: center; }
.cw-widget .t-stats { display: none; }
.cw-widget .t-geo { display: none; }
.cw-widget .t-action {
border-top: 1px solid var(--border2);
padding: 12px 16px 16px;
flex-direction: column;
align-items: center;
gap: 8px;
}
.cw-widget .t-avail { font-size: 11px; flex-direction: row; gap: 4px; justify-content: center; }
.cw-widget .t-action .btn-cta { font-size: 14px; padding: 13px 24px; width: 100%; }
}
/* ════════════════ CARDS ════════════════════════════════════ */
.cw-widget .c-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(290px, 1fr)); gap: 20px; }
.cw-widget .c-card { background: var(--surface); border: 1px solid var(--border);
border-radius: 16px; overflow: hidden; display: flex; flex-direction: column;
transition: transform .2s, box-shadow .2s; }
.cw-widget .c-card:hover { transform: translateY(-4px); box-shadow: var(--shadow); }
.cw-widget .c-head { padding: 24px 20px 18px; display: flex; flex-direction: column; align-items: center; gap: 10px; position: relative; }
.cw-widget .c-head-badge { position: absolute; top: 14px; right: 14px; }
.cw-widget .c-name { font-size: 13px; font-weight: 600; color: var(--text2); }
.cw-widget .c-stars { font-size: 16px; letter-spacing: 2px; }
.cw-widget .c-bonus { margin: 0 16px 16px; background: var(--gold-dim); border: 1px solid var(--gold-border);
border-radius: 10px; padding: 12px 16px; text-align: center; }
.cw-widget .c-bonus-label { font-size: 10px; text-transform: uppercase; letter-spacing: .8px; color: var(--text3); margin-bottom: 4px; }
.cw-widget .c-bonus-text { font-size: 14px; font-weight: 700; color: var(--gold-text); line-height: 1.4; }
.cw-widget .c-stats { margin: 0 16px 14px; display: grid; grid-template-columns: 1fr 1fr; gap: 1px;
border: 1px solid var(--border); border-radius: 10px; overflow: hidden; }
.cw-widget .c-stat { background: var(--surface2); padding: 10px 12px; display: flex; flex-direction: column; gap: 2px; }
.cw-widget .c-stat-val { font-size: 12px; font-weight: 700; }
.cw-widget .c-stat-label { font-size: 10px; color: var(--text3); text-transform: uppercase; letter-spacing: .4px; }
.cw-widget .c-stat.full { grid-column: 1/-1; }
.cw-widget .c-pros { margin: 0 16px; flex: 1; display: flex; flex-direction: column; gap: 6px;
padding-bottom: 16px; border-bottom: 1px solid var(--border2); }
.cw-widget .c-foot { padding: 14px 16px 18px; display: flex; flex-direction: column; gap: 10px; align-items: center; }
.cw-widget .c-avail { display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--text3); }
.cw-widget .c-foot .btn-cta { width: 100%; justify-content: center; font-size: 14px; padding: 13px 20px; }
/* ════════════════ RANKED ═══════════════════════════════════ */
.cw-widget .r-list { display: flex; flex-direction: column; gap: 10px; }
.cw-widget .r-row { display: flex; align-items: center;
background: var(--surface); border: 1px solid var(--border);
border-radius: 14px; overflow: hidden; transition: transform .15s, box-shadow .15s; }
.cw-widget .r-row:hover { transform: translateX(4px); box-shadow: var(--shadow); }
.cw-widget .r-num { width: 52px; flex-shrink: 0; display: flex; align-items: center; justify-content: center;
font-size: 24px; font-weight: 900; color: var(--text3);
border-right: 1px solid var(--border2); align-self: stretch; }
.cw-widget .r-num.top { color: var(--gold); }
.cw-widget .r-logo { padding: 14px 16px; flex-shrink: 0; border-right: 1px solid var(--border2); }
.cw-widget .r-main { flex: 1; padding: 14px 18px; min-width: 0; display: flex; flex-direction: column; gap: 5px;
border-right: 1px solid var(--border2); }
.cw-widget .r-name { font-size: 15px; font-weight: 700; display: flex; align-items: center; gap: 6px; }
.cw-widget .r-bonus { font-size: 13px; color: var(--gold-text); font-weight: 500;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cw-widget .r-row2 { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.cw-widget .r-chip { font-size: 11px; color: var(--text2); background: var(--surface2);
padding: 2px 8px; border-radius: 20px; border: 1px solid var(--border2); white-space: nowrap; }
.cw-widget .r-stars { padding: 14px 16px; flex-shrink: 0; border-right: 1px solid var(--border2); }
.cw-widget .r-geo { padding: 14px 16px; flex-shrink: 0; border-right: 1px solid var(--border2);
display: flex; flex-direction: column; align-items: center; gap: 4px; }
.cw-widget .r-geo-label { font-size: 10px; color: var(--text3); text-transform: uppercase; letter-spacing: .4px; }
.cw-widget .r-action { padding: 14px 16px; flex-shrink: 0; }
@media (max-width: 700px) {
.cw-widget .r-row { flex-wrap: wrap; }
.cw-widget .r-num { display: none; }
.cw-widget .r-logo { border-right: none; padding: 14px; flex: 100%; justify-content: center; display: flex; }
.cw-widget .r-logo .logo-pill { width: 160px; height: 56px; }
.cw-widget .r-logo .logo-pill img { width: 160px; height: 56px; }
.cw-widget .r-main { flex: 100%; border-right: none; border-top: 1px solid var(--border2); padding: 10px 14px; }
.cw-widget .r-stars { display: none; }
.cw-widget .r-geo { flex: 100%; border-right: none; border-top: 1px solid var(--border2);
flex-direction: row; gap: 6px; padding: 8px 14px; justify-content: center; }
.cw-widget .r-geo-label { font-size: 11px; }
.cw-widget .r-action { flex: 100%; padding: 0 14px 14px; }
.cw-widget .r-action .btn-cta { width: 100%; justify-content: center; }
}
/* ════════════════ LIQUID GLASS ════════════════════════════ */
.cw-widget .lq-wrap {
background: linear-gradient(145deg, #08001f 0%, #0d1f4a 45%, #0a1a2e 100%);
border-radius: 16px; padding: 28px 20px;
position: relative; overflow: hidden;
}
.cw-widget .lq-wrap::before,
.cw-widget .lq-wrap::after {
content: ''; position: absolute; border-radius: 50%;
filter: blur(80px); opacity: .35; pointer-events: none;
}
.cw-widget .lq-wrap::before {
width: 500px; height: 500px;
background: radial-gradient(circle, #6e3aff, transparent 70%);
top: -150px; left: -100px;
}
.cw-widget .lq-wrap::after {
width: 400px; height: 400px;
background: radial-gradient(circle, #00b4ff, transparent 70%);
bottom: -100px; right: -80px;
}
.cw-widget .lq-list { display: flex; flex-direction: column; gap: 12px; position: relative; z-index: 1; }
.cw-widget .lq-card {
display: flex; align-items: center;
background: rgba(255,255,255,0.065);
backdrop-filter: blur(28px) saturate(160%);
-webkit-backdrop-filter: blur(28px) saturate(160%);
border-radius: 18px; border: 1px solid rgba(255,255,255,0.13);
box-shadow: 0 8px 32px rgba(0,0,0,0.35),
inset 0 1px 0 rgba(255,255,255,0.18),
inset 0 -1px 0 rgba(0,0,0,0.2);
overflow: hidden; transition: transform .2s, box-shadow .2s, background .2s;
position: relative;
}
.cw-widget .lq-card::before {
content: ''; position: absolute; inset: 0; border-radius: 18px;
background: linear-gradient(135deg, rgba(255,255,255,0.07) 0%, rgba(255,255,255,0.02) 40%, transparent 100%);
pointer-events: none;
}
.cw-widget .lq-card:hover {
transform: translateY(-3px) scale(1.005);
background: rgba(255,255,255,0.1);
box-shadow: 0 16px 48px rgba(0,0,0,0.45),
inset 0 1px 0 rgba(255,255,255,0.22),
0 0 0 1px rgba(120,80,255,0.25);
}
.cw-widget .lq-num {
width: 48px; flex-shrink: 0; align-self: stretch;
display: flex; align-items: center; justify-content: center;
font-size: 18px; font-weight: 900; color: rgba(255,255,255,0.25);
background: linear-gradient(180deg, rgba(255,255,255,0.06), rgba(255,255,255,0.02));
border-right: 1px solid rgba(255,255,255,0.08);
}
.cw-widget .lq-num.top {
color: #ffd700; text-shadow: 0 0 12px rgba(255,215,0,0.5);
background: linear-gradient(180deg, rgba(255,215,0,0.12), rgba(255,180,0,0.05));
}
.cw-widget .lq-logo {
padding: 14px 16px; flex-shrink: 0;
border-right: 1px solid rgba(255,255,255,0.07);
position: relative; z-index: 1;
}
.cw-widget .lq-logo .logo-pill {
background: rgba(255,255,255,0.92);
border-radius: 10px;
box-shadow: 0 2px 12px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,1);
}
.cw-widget .lq-main {
flex: 1; padding: 14px 18px; min-width: 0;
display: flex; flex-direction: column; gap: 4px;
border-right: 1px solid rgba(255,255,255,0.07);
}
.cw-widget .lq-name {
font-size: 15px; font-weight: 700; color: #fff;
display: flex; align-items: center; gap: 7px; letter-spacing: -.1px;
}
.cw-widget .lq-bonus {
font-size: 12px; font-weight: 500;
background: linear-gradient(90deg, #ffd700, #ffaa00);
-webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cw-widget .lq-chips { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 2px; }
.cw-widget .lq-chip {
font-size: 10px; color: rgba(255,255,255,0.55);
background: rgba(255,255,255,0.07); border: 1px solid rgba(255,255,255,0.1);
padding: 2px 8px; border-radius: 20px; backdrop-filter: blur(8px);
}
.cw-widget .lq-stars {
padding: 14px; flex-shrink: 0; border-right: 1px solid rgba(255,255,255,0.07);
display: flex; align-items: center;
}
.cw-widget .lq-stars .stars { font-size: 13px; letter-spacing: 2px; filter: drop-shadow(0 0 4px rgba(255,215,0,0.5)); }
.cw-widget .lq-geo {
padding: 14px; flex-shrink: 0; border-right: 1px solid rgba(255,255,255,0.07);
display: flex; flex-direction: column; align-items: center; gap: 4px;
}
.cw-widget .lq-geo-label { font-size: 9px; color: rgba(255,255,255,0.3); text-transform: uppercase; letter-spacing: .6px; }
.cw-widget .lq-action { padding: 14px 16px; flex-shrink: 0; }
.cw-widget .lq-btn {
display: inline-flex; align-items: center; justify-content: center;
padding: 10px 20px; border-radius: 100px;
background: linear-gradient(135deg, rgba(120,80,255,0.9) 0%, rgba(60,140,255,0.9) 100%);
backdrop-filter: blur(10px);
border: 1px solid rgba(255,255,255,0.25);
box-shadow: 0 4px 20px rgba(100,60,255,0.45), inset 0 1px 0 rgba(255,255,255,0.3);
color: #fff; font-size: 13px; font-weight: 700;
text-decoration: none; white-space: nowrap;
transition: transform .15s, box-shadow .15s; position: relative; overflow: hidden;
}
.cw-widget .lq-btn::after {
content: ''; position: absolute; inset: 0; border-radius: 100px;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.18), transparent);
transform: translateX(-100%); transition: transform .5s;
}
.cw-widget .lq-btn:hover { transform: translateY(-2px); color: #fff; text-decoration: none;
box-shadow: 0 8px 30px rgba(100,60,255,0.6), inset 0 1px 0 rgba(255,255,255,0.35); }
.cw-widget .lq-btn:hover::after { transform: translateX(100%); }
/* Light overrides */
.cw-widget.cw-light .lq-wrap {
background: linear-gradient(145deg, #e8eeff 0%, #f0f4ff 50%, #e4f0ff 100%);
}
.cw-widget.cw-light .lq-wrap::before { background: radial-gradient(circle, rgba(110,58,255,0.15), transparent 70%); }
.cw-widget.cw-light .lq-wrap::after { background: radial-gradient(circle, rgba(0,140,255,0.15), transparent 70%); }
.cw-widget.cw-light .lq-card {
background: rgba(255,255,255,0.7); border-color: rgba(0,0,0,0.08);
box-shadow: 0 4px 20px rgba(0,0,0,0.1), inset 0 1px 0 rgba(255,255,255,0.9);
}
.cw-widget.cw-light .lq-card::before { background: linear-gradient(135deg, rgba(255,255,255,0.6) 0%, transparent 60%); }
.cw-widget.cw-light .lq-card:hover { background: rgba(255,255,255,0.85); box-shadow: 0 8px 32px rgba(0,0,0,0.15), 0 0 0 1px rgba(100,60,255,0.15); }
.cw-widget.cw-light .lq-num { color: rgba(0,0,0,0.2); background: rgba(0,0,0,0.03); border-right-color: rgba(0,0,0,0.06); }
.cw-widget.cw-light .lq-num.top { color: #b8860b; background: rgba(255,180,0,0.08); text-shadow: none; }
.cw-widget.cw-light .lq-logo { border-right-color: rgba(0,0,0,0.06); }
.cw-widget.cw-light .lq-logo .logo-pill { background: rgba(255,255,255,0.95); box-shadow: 0 2px 8px rgba(0,0,0,0.12); }
.cw-widget.cw-light .lq-main { border-right-color: rgba(0,0,0,0.06); }
.cw-widget.cw-light .lq-name { color: #1a1a2e; }
.cw-widget.cw-light .lq-bonus { background: linear-gradient(90deg, #b8860b, #d4a017); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
.cw-widget.cw-light .lq-chip { color: rgba(0,0,0,0.5); background: rgba(0,0,0,0.05); border-color: rgba(0,0,0,0.08); }
.cw-widget.cw-light .lq-stars { border-right-color: rgba(0,0,0,0.06); }
.cw-widget.cw-light .lq-stars .stars { filter: none; }
.cw-widget.cw-light .lq-geo { border-right-color: rgba(0,0,0,0.06); }
.cw-widget.cw-light .lq-geo-label { color: rgba(0,0,0,0.35); }
@media (max-width: 700px) {
.cw-widget .lq-wrap { padding: 16px 12px; }
.cw-widget .lq-card { flex-wrap: wrap; }
.cw-widget .lq-num { display: none; }
.cw-widget .lq-logo { border-right: none; padding: 12px; flex: 100%; justify-content: center; display: flex; }
.cw-widget .lq-logo .logo-pill { width: 160px; height: 56px; }
.cw-widget .lq-logo .logo-pill img { width: 160px; height: 56px; }
.cw-widget .lq-main { flex: 100%; border-right: none; border-top: 1px solid rgba(255,255,255,0.07); padding: 10px 14px; }
.cw-widget .lq-stars, .cw-widget .lq-geo { display: none; }
.cw-widget .lq-action { flex: 100%; padding: 0 14px 14px; }
.cw-widget .lq-action .lq-btn { width: 100%; justify-content: center; }
}
/* ════════════════ TOOLBAR (layout + geo) ═══════════════════ */
.cw-widget .cw-geo-bar {
display: flex; align-items: center; gap: 8px;
justify-content: space-between; margin-bottom: 14px; flex-wrap: wrap;
}
.cw-widget .cw-layout-bar { display: flex; align-items: center; gap: 3px; }
.cw-widget .cw-lb {
width: 30px; height: 30px; border-radius: 6px; padding: 0;
border: 1px solid var(--border); background: var(--surface);
color: var(--text2); cursor: pointer; font-size: 14px;
display: inline-flex; align-items: center; justify-content: center;
transition: border-color .15s, color .15s, background .15s;
font-family: inherit;
}
.cw-widget .cw-lb:hover { border-color: rgba(255,255,255,0.22); color: var(--text); }
.cw-widget .cw-lb.active { background: var(--gold-dim); border-color: var(--gold-border); color: var(--gold-text); }
.cw-widget.cw-light .cw-lb:hover { border-color: rgba(0,0,0,0.22); }
.cw-widget.cw-light .cw-lb.active { background: var(--gold-dim); border-color: var(--gold-border); color: var(--gold-text); }
.cw-widget .cw-geo-right { display: flex; align-items: center; gap: 8px; }
.cw-widget .cw-geo-bar label {
font-size: 12px; color: var(--text3); white-space: nowrap;
}
.cw-widget .cw-geo-select {
-webkit-appearance: none; appearance: none;
background: var(--surface);
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%23888' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 10px center;
background-size: 10px 7px;
border: 1px solid var(--border); border-radius: 8px;
color: var(--text); font-size: 13px; font-family: inherit;
padding: 7px 30px 7px 10px; cursor: pointer; min-width: 140px;
transition: border-color .15s;
}
.cw-widget .cw-geo-select:hover { border-color: rgba(255,255,255,0.22); }
.cw-widget .cw-geo-select:focus { outline: none; border-color: var(--gold-border); }
.cw-widget.cw-light .cw-geo-select:hover { border-color: rgba(0,0,0,0.22); }
.cw-widget .cw-geo-select option { background: #1a1a2e; color: #e8e8f0; }
.cw-widget.cw-light .cw-geo-select option { background: #ffffff; color: #1a1a2e; }
/* ════════════════ SECTION DIVIDER ══════════════════════════ */
.cw-widget .cw-divider {
display: flex; align-items: center; gap: 10px;
font-size: 11px; font-weight: 600; letter-spacing: .7px; text-transform: uppercase;
color: var(--text3); margin: 14px 0 6px; grid-column: 1 / -1;
}
.cw-widget .cw-divider::before,
.cw-widget .cw-divider::after {
content: ''; flex: 1; height: 1px; background: var(--border);
}
.cw-widget .cw-divider::before { max-width: 20px; }
/* ════════════════ SHIMMER ══════════════════════════════════ */
@keyframes cw-shine {
0% { transform: translateX(-160%) skewX(-18deg); }
28% { transform: translateX(160%) skewX(-18deg); }
100% { transform: translateX(160%) skewX(-18deg); }
}
.cw-widget .t-row,
.cw-widget .c-card,
.cw-widget .r-row,
.cw-widget .lq-card { position: relative; }
.cw-widget .t-row::after,
.cw-widget .c-card::after,
.cw-widget .r-row::after,
.cw-widget .lq-card::after {
content: '';
position: absolute; top: 0; left: 0;
width: 45%; height: 100%;
background: linear-gradient(90deg,
transparent 0%,
rgba(255,255,255,0.055) 45%,
rgba(255,255,255,0.09) 50%,
rgba(255,255,255,0.055) 55%,
transparent 100%);
animation: cw-shine 3.6s ease-in-out infinite;
pointer-events: none;
z-index: 1;
}
/* staggered delays for each row */
.cw-widget .t-row:nth-child(1)::after,
.cw-widget .c-card:nth-child(1)::after,
.cw-widget .r-row:nth-child(1)::after,
.cw-widget .lq-card:nth-child(1)::after { animation-delay: 0.0s; }
.cw-widget .t-row:nth-child(2)::after,
.cw-widget .c-card:nth-child(2)::after,
.cw-widget .r-row:nth-child(2)::after,
.cw-widget .lq-card:nth-child(2)::after { animation-delay: 0.35s; }
.cw-widget .t-row:nth-child(3)::after,
.cw-widget .c-card:nth-child(3)::after,
.cw-widget .r-row:nth-child(3)::after,
.cw-widget .lq-card:nth-child(3)::after { animation-delay: 0.7s; }
.cw-widget .t-row:nth-child(4)::after,
.cw-widget .c-card:nth-child(4)::after,
.cw-widget .r-row:nth-child(4)::after,
.cw-widget .lq-card:nth-child(4)::after { animation-delay: 1.05s; }
.cw-widget .t-row:nth-child(5)::after,
.cw-widget .c-card:nth-child(5)::after,
.cw-widget .r-row:nth-child(5)::after,
.cw-widget .lq-card:nth-child(5)::after { animation-delay: 1.4s; }
.cw-widget .t-row:nth-child(6)::after,
.cw-widget .c-card:nth-child(6)::after,
.cw-widget .r-row:nth-child(6)::after,
.cw-widget .lq-card:nth-child(6)::after { animation-delay: 1.75s; }
.cw-widget .t-row:nth-child(7)::after,
.cw-widget .c-card:nth-child(7)::after,
.cw-widget .r-row:nth-child(7)::after,
.cw-widget .lq-card:nth-child(7)::after { animation-delay: 2.1s; }
.cw-widget .t-row:nth-child(8)::after,
.cw-widget .c-card:nth-child(8)::after,
.cw-widget .r-row:nth-child(8)::after,
.cw-widget .lq-card:nth-child(8)::after { animation-delay: 2.45s; }
/* light theme: slightly more visible */
.cw-widget.cw-light .t-row::after,
.cw-widget.cw-light .c-card::after,
.cw-widget.cw-light .r-row::after,
.cw-widget.cw-light .lq-card::after {
background: linear-gradient(90deg,
transparent 0%,
rgba(255,255,255,0.3) 45%,
rgba(255,255,255,0.5) 50%,
rgba(255,255,255,0.3) 55%,
transparent 100%);
}
</style>
<div class="cw-widget" id="cw-widget">
<div class="cw-geo-bar">
<div class="cw-layout-bar">
<button class="cw-lb <?= $layout==='table' ? 'active':'' ?>" data-layout="table" title="Table">⊞</button>
<button class="cw-lb <?= $layout==='cards' ? 'active':'' ?>" data-layout="cards" title="Cards">▦</button>
<button class="cw-lb <?= $layout==='ranked' ? 'active':'' ?>" data-layout="ranked" title="Ranked">☰</button>
<button class="cw-lb <?= $layout==='liquid' ? 'active':'' ?>" data-layout="liquid" title="Liquid">◈</button>
</div>
<?php if (!empty($active_countries)): ?>
<div class="cw-geo-right">
<label for="cw-geo-sel">📍</label>
<select class="cw-geo-select" id="cw-geo-sel">
<option value="" <?= $country === '' ? 'selected' : '' ?>>🌍 Global</option>
<?php foreach ($active_countries as $cc): ?>
<option value="<?= h($cc) ?>" <?= $country === $cc ? 'selected' : '' ?>><?= flag_emoji($cc) ?> <?= h(country_name($cc)) ?></option>
<?php endforeach ?>
</select>
</div>
<?php endif ?>
</div>
<?php if (empty($sections)): ?>
<div style="text-align:center;padding:60px 20px;opacity:.3;font-size:14px">No brands available</div>
<?php elseif ($layout === 'table'): ?>
<div class="t-table">
<?php foreach ($sections as $sec): ?>
<?php if ($sec['header']): ?><div class="cw-divider"><?= $sec['header'] ?></div><?php endif ?>
<?php foreach ($sec['data'] as $d): $is_top = $d['idx'] < 3;
$row_cls = 't-row';
if ($d['idx'] === 0) $row_cls .= ' rank-1';
elseif ($d['idx'] === 1) $row_cls .= ' rank-2';
elseif ($d['idx'] === 2) $row_cls .= ' rank-3';
if (!empty($d['b']['is_featured'])) $row_cls .= ' t-featured';
?>
<div class="<?= $row_cls ?>" style="--row-idx:<?= $d['idx'] ?>" onclick="window.open('<?= h($d['click']) ?>','_blank')">
<div class="t-num <?= $is_top ? 'top' : '' ?>"><?= $d['idx']+1 ?></div>
<div class="t-logo">
<div class="logo-pill" style="width:164px;height:84px;background:<?= logo_bg_gradient((int)$d['b']['id'], $d['b']['logo_bg'] ?? '') ?>">
<?php if ($d['logo']): ?>
<img src="<?= h($d['logo']) ?>" alt="<?= h($d['b']['name']) ?>" style="width:164px;height:64px;object-fit:contain">
<?php else: ?>
<span class="logo-pill-text" style="font-size:15px;padding:8px"><?= h($d['b']['name']) ?></span>
<?php endif ?>
</div>
</div>
<div class="t-brand">
<div class="t-brand-name">
<?= h($d['b']['name']) ?>
<?php if ($d['b']['badge']): ?><span class="badge badge-<?= h($d['b']['badge']) ?>"><?= badge_label($d['b']['badge']) ?></span><?php endif ?>
</div>
<div class="t-brand-row"><span class="stars"><?= $d['stars'] ?></span></div>
<?php foreach (array_slice($d['adv'], 0, 2) as $item): ?><div class="pro"><?= h($item) ?></div><?php endforeach ?>
<?php if ($country): ?>
<div class="t-brand-geo-mobile">
<img src="https://flagicons.lipis.dev/flags/4x3/<?= h($country) ?>.svg" width="16" height="12" style="border-radius:2px">
<span><?= h(country_name($country)) ?></span>
</div>
<?php endif ?>
</div>
<div class="t-bonus">
<?php if ($d['bonus']): ?>
<div class="t-bonus-inner">
<div class="t-bonus-label">Welcome Bonus</div>
<div class="t-bonus-text"><?= h($d['bonus']) ?></div>
<?php if ($d['free_spins']): ?>
<div class="t-bonus-spins">+ <?= h($d['free_spins']) ?></div>
<?php endif ?>
</div>
<?php else: ?><span style="color:var(--text3);font-size:12px">—</span><?php endif ?>
</div>
<div class="t-geo">
<div class="t-geo-label">Accepts players from</div>
<div class="t-geo-flags">
<?php if ($country): ?>
<img src="https://flagicons.lipis.dev/flags/4x3/<?= h($country) ?>.svg" width="28" height="21" title="<?= h(country_name($country)) ?>">
<span class="t-geo-country"><?= h(country_name($country)) ?></span>
<?php else:
$show = array_slice($d['country_codes'], 0, 4);
$rest = count($d['country_codes']) - count($show);
foreach ($show as $cc): ?>
<img src="https://flagicons.lipis.dev/flags/4x3/<?= h($cc) ?>.svg" width="22" height="16" title="<?= h(country_name($cc)) ?>">
<?php endforeach;
if ($rest > 0): ?><span class="t-geo-more">+<?= $rest ?></span><?php endif ?>
<?php endif ?>
</div>
</div>
<div class="t-action" onclick="event.stopPropagation()">
<a href="<?= h($d['click']) ?>" class="btn-cta" target="_blank" rel="noopener"><?= h($cta_text) ?></a>
</div>
</div>
<?php endforeach ?>
<?php endforeach ?>
</div>
<?php elseif ($layout === 'cards'): ?>
<div class="c-grid">
<?php foreach ($sections as $sec): ?>
<?php if ($sec['header']): ?><div class="cw-divider"><?= $sec['header'] ?></div><?php endif ?>
<?php foreach ($sec['data'] as $d): ?>
<div class="c-card">
<div class="c-head">
<?php if ($d['b']['badge']): ?><div class="c-head-badge"><span class="badge badge-<?= h($d['b']['badge']) ?>"><?= badge_label($d['b']['badge']) ?></span></div><?php endif ?>
<div class="logo-pill" style="width:200px;height:72px;background:<?= logo_bg_gradient((int)$d['b']['id'], $d['b']['logo_bg'] ?? '') ?>">
<?php if ($d['logo']): ?>
<img src="<?= h($d['logo']) ?>" alt="<?= h($d['b']['name']) ?>" style="width:200px;height:72px;object-fit:contain">
<?php else: ?>
<span class="logo-pill-text" style="font-size:18px;padding:10px"><?= h($d['b']['name']) ?></span>
<?php endif ?>
</div>
<div class="c-stars stars"><?= $d['stars'] ?></div>
<div class="c-name"><?= h($d['b']['name']) ?></div>
</div>
<?php if ($d['bonus']): ?>
<div class="c-bonus"><div class="c-bonus-label">Welcome Bonus</div><div class="c-bonus-text"><?= h($d['bonus']) ?></div></div>
<?php endif ?>
<?php
$stats = [];
if ($d['year_founded']) $stats[] = ['Est.', $d['year_founded'], false];
if ($d['games_count']) $stats[] = ['Games', $d['games_count'], false];
if ($d['license']) $stats[] = ['License', $d['license'], false];
if ($d['currencies']) $stats[] = ['Currency', $d['currencies'], false];
if ($d['more_countries']>0) $stats[] = ['+Countries', '+' . $d['more_countries'] . ' more', true];
?>
<?php if ($stats): ?>
<div class="c-stats">
<?php foreach ($stats as [$label, $val, $full]): ?>
<div class="c-stat <?= $full ? 'full' : '' ?>"><span class="c-stat-val"><?= h($val) ?></span><span class="c-stat-label"><?= h($label) ?></span></div>
<?php endforeach ?>
</div>
<?php endif ?>
<?php if ($d['adv']): ?>
<div class="c-pros"><?php foreach (array_slice($d['adv'], 0, 3) as $item): ?><div class="pro"><?= h($item) ?></div><?php endforeach ?></div>
<?php endif ?>
<div class="c-foot">
<?php if ($country): ?><div class="c-avail"><?= geo_flag($country) ?><span><?= h($avail_label) ?> <?= h(country_name($country)) ?></span></div><?php endif ?>
<a href="<?= h($d['click']) ?>" class="btn-cta" target="_blank" rel="noopener"><?= h($cta_text) ?></a>
</div>
</div>
<?php endforeach ?>
<?php endforeach ?>
</div>
<?php elseif ($layout === 'liquid'): ?>
<div class="lq-wrap">
<div class="lq-list">
<?php foreach ($sections as $sec): ?>
<?php if ($sec['header']): ?><div class="cw-divider" style="color:rgba(255,255,255,0.35)"><?= $sec['header'] ?></div><?php endif ?>
<?php foreach ($sec['data'] as $d): $is_top = $d['idx'] < 3; ?>
<div class="lq-card">
<div class="lq-num <?= $is_top ? 'top' : '' ?>"><?= $d['idx']+1 ?></div>
<div class="lq-logo">
<div class="logo-pill" style="width:140px;height:52px">
<?php if ($d['logo']): ?>
<img src="<?= h($d['logo']) ?>" alt="<?= h($d['b']['name']) ?>" style="width:140px;height:52px;object-fit:contain">
<?php else: ?>
<span class="logo-pill-text" style="font-size:13px;padding:6px"><?= h($d['b']['name']) ?></span>
<?php endif ?>
</div>
</div>
<div class="lq-main">
<div class="lq-name"><?= h($d['b']['name']) ?>
<?php if ($d['b']['badge']): ?><span class="badge badge-<?= h($d['b']['badge']) ?>"><?= badge_label($d['b']['badge']) ?></span><?php endif ?>
</div>
<?php if ($d['bonus']): ?><div class="lq-bonus"><?= h($d['bonus']) ?></div><?php endif ?>
<div class="lq-chips">
<?php if ($d['games_count']): ?><span class="lq-chip">🎮 <?= h($d['games_count']) ?></span><?php endif ?>
<?php if ($d['license']): ?><span class="lq-chip">🪪 <?= h($d['license']) ?></span><?php endif ?>
<?php if ($d['year_founded']): ?><span class="lq-chip">📅 <?= $d['year_founded'] ?></span><?php endif ?>
<?php if ($d['more_countries'] > 0): ?><span class="lq-chip">🌍 +<?= $d['more_countries'] ?></span><?php endif ?>
</div>
</div>
<div class="lq-stars"><span class="stars"><?= $d['stars'] ?></span></div>
<?php if ($country): ?>
<div class="lq-geo">
<span class="lq-geo-label"><?= h($avail_label) ?></span>
<?= geo_flag($country) ?>
<span style="font-size:10px;color:rgba(255,255,255,.45)"><?= h(strtoupper($country)) ?></span>
</div>
<?php endif ?>
<div class="lq-action">
<a href="<?= h($d['click']) ?>" class="lq-btn" target="_blank" rel="noopener"><?= h($cta_text) ?></a>
</div>
</div>
<?php endforeach ?>
<?php endforeach ?>
</div>
</div>
<?php else: // ranked ?>
<div class="r-list">
<?php foreach ($sections as $sec): ?>
<?php if ($sec['header']): ?><div class="cw-divider"><?= $sec['header'] ?></div><?php endif ?>
<?php foreach ($sec['data'] as $d): $is_top = $d['idx'] < 3; ?>
<div class="r-row">
<div class="r-num <?= $is_top ? 'top' : '' ?>"><?= $d['idx']+1 ?></div>
<div class="r-logo">
<div class="logo-pill" style="width:160px;height:56px;background:<?= logo_bg_gradient((int)$d['b']['id'], $d['b']['logo_bg'] ?? '') ?>">
<?php if ($d['logo']): ?>
<img src="<?= h($d['logo']) ?>" alt="<?= h($d['b']['name']) ?>" style="width:160px;height:56px;object-fit:contain">
<?php else: ?>
<span class="logo-pill-text" style="font-size:13px;padding:6px"><?= h($d['b']['name']) ?></span>
<?php endif ?>
</div>
</div>
<div class="r-main">
<div class="r-name"><?= h($d['b']['name']) ?>
<?php if ($d['b']['badge']): ?><span class="badge badge-<?= h($d['b']['badge']) ?>"><?= badge_label($d['b']['badge']) ?></span><?php endif ?>
</div>
<?php if ($d['bonus']): ?><div class="r-bonus"><?= h($d['bonus']) ?></div><?php endif ?>
<div class="r-row2">
<?php if ($d['games_count']): ?><span class="r-chip">🎮 <?= h($d['games_count']) ?></span><?php endif ?>
<?php if ($d['license']): ?><span class="r-chip">🪪 <?= h($d['license']) ?></span><?php endif ?>
<?php if ($d['year_founded']): ?><span class="r-chip">📅 <?= $d['year_founded'] ?></span><?php endif ?>
<?php if ($d['more_countries'] > 0): ?><span class="r-chip">🌍 +<?= $d['more_countries'] ?> countries</span><?php endif ?>
</div>
</div>
<div class="r-stars"><span class="stars"><?= $d['stars'] ?></span></div>
<?php if ($country): ?>
<div class="r-geo">
<span class="r-geo-label"><?= h($avail_label) ?></span>
<?= geo_flag($country) ?>
<span style="font-size:11px;color:var(--text2)"><?= h(strtoupper($country)) ?></span>
</div>
<?php endif ?>
<div class="r-action">
<a href="<?= h($d['click']) ?>" class="btn-cta" target="_blank" rel="noopener"><?= h($cta_text) ?></a>
</div>
</div>
<?php endforeach ?>
<?php endforeach ?>
</div>
<?php endif ?>
</div><!-- /cw-widget -->
<script>
(function () {
function detectLight(el) {
var bg = '';
while (el && (!bg || bg === 'rgba(0, 0, 0, 0)' || bg === 'transparent')) {
el = el.parentElement;
if (el) bg = getComputedStyle(el).backgroundColor;
}
var m = bg && bg.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
return m && (parseInt(m[1])*299 + parseInt(m[2])*587 + parseInt(m[3])*114) / 1000 >= 128;
}
function loadWidget(layout, country) {
var w = document.getElementById('cw-widget');
if (!w) return;
var url = 'https://888mirror.pm/widget-api.php?layout=' + encodeURIComponent(layout)
+ '&country=' + encodeURIComponent(country || '');
fetch(url)
.then(function (r) { return r.text(); })
.then(function (html) {
var tmp = document.createElement('div');
tmp.innerHTML = html;
var newW = tmp.querySelector('#cw-widget');
if (!newW) return;
var cur = document.getElementById('cw-widget');
if (!cur) return;
cur.innerHTML = newW.innerHTML;
cur.className = newW.className;
initCwWidget();
})
.catch(function (e) { console.error('CW error:', e); });
}
function initCwWidget() {
var w = document.getElementById('cw-widget');
if (!w) return;
// Текущий layout читаем из активной кнопки (обновляется при каждом рендере)
var activeBtn = w.querySelector('.cw-lb.active');
var curLayout = activeBtn ? activeBtn.getAttribute('data-layout') : '<?= h($layout) ?>';
var curCountry = '';
if (detectLight(w)) w.classList.add('cw-light');
var sel = w.querySelector('.cw-geo-select');
if (sel) { curCountry = sel.value; sel.disabled = false; }
// Восстановить сохранённый layout из localStorage (только при первом рендере)
var savedLayout = localStorage.getItem('cw_layout_v2');
if (savedLayout && savedLayout !== curLayout) {
loadWidget(savedLayout, curCountry);
return;
}
// GEO switcher
if (sel) {
sel.addEventListener('change', function () {
sel.disabled = true;
loadWidget(curLayout, this.value);
});
}
// Layout switcher
w.querySelectorAll('.cw-lb').forEach(function (btn) {
btn.addEventListener('click', function () {
var newLayout = this.getAttribute('data-layout');
localStorage.setItem('cw_layout_v2', newLayout);
loadWidget(newLayout, curCountry);
});
});
}
initCwWidget();
})();
</script>