<?php
error_reporting(0);
ini_set('display_errors', 0);
ini_set('memory_limit', '512M');
require_once __DIR__ . '/lang.php';
require_once __DIR__ . '/model_desc_i18n.php';

define('SITE_URL',  'https://sex-videochat.info');
define('USER_ID',   '492b59a9925d01876c059022ef32fa6f9949ef373e4179aa08008786798f6032');
define('API_KEY',   '22f4ea13b6dd0a08a0d9');
define('CACHE_DIR', __DIR__ . '/cache');
define('CACHE_TTL', 90); // max age before we consider a cached model "possibly offline now"
$_siteUrl = siteUrl();

$username = $_GET['u'] ?? '';
if (!$username) {
    $path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
    $username = trim(preg_replace('#^/model/#', '', $path), '/');
}
$username = preg_replace('/[^a-zA-Z0-9_\-]/', '', $username);

// Model data is populated by cron_update_models.php (runs every minute, fetches the
// full online-models list once and writes a small per-model cache file). This function
// only ever reads that cache — it never calls the live API itself, because the API has
// no real single-model lookup and a per-visit fetch of the full ~9,800-model list is what
// caused memory exhaustion / 500 errors under the web SAPI's memory_limit.
function fetchModel(string $name): ?array {
    if (!$name) return null;
    $cacheFile = CACHE_DIR . '/m_' . md5($name) . '.json';
    if (!file_exists($cacheFile)) return null;
    $d = json_decode(file_get_contents($cacheFile), true);
    if (!$d) return null;
    // Cron only rewrites a model's file while she's in the live online list. If the file
    // hasn't been touched recently, she's no longer online — show last-known profile data
    // but don't claim she's live.
    if ((time() - filemtime($cacheFile)) > CACHE_TTL) {
        $d['status'] = 'offline';
    }
    return $d;
}

function h(string $s): string {
    return htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}

function flagEmoji(string $code): string {
    if (strlen($code) !== 2) return '';
    $code = strtoupper($code);
    $out  = '';
    foreach (str_split($code) as $c) {
        $out .= mb_chr(0x1F1E6 + ord($c) - 65, 'UTF-8');
    }
    return $out;
}

function fmtNum(int $n): string {
    if ($n >= 1000000) return round($n / 1000000, 1) . 'M';
    if ($n >= 1000)    return round($n / 1000, 1) . 'K';
    return (string)$n;
}

function genderLbl(string $g): string {
    $map = [
        'female'     => t('detail_female', 'Female'),
        'males'      => t('detail_male',   'Male'),
        'maleFemale' => t('detail_couple', 'Couple'),
        'trans'      => t('detail_trans',  'Trans'),
        'females'    => t('detail_group',  'Female Group'),
    ];
    return $map[$g] ?? ucfirst($g);
}

function generateModelDescriptionHtmlDE(array $m, string $name): string {
    $tags    = $m['tags'] ?? [];
    $country = strtoupper($m['modelsCountry'] ?? '');
    $langs   = $m['languages'] ?? [];
    $isHD    = !empty($m['broadcastHD']);
    $isVR    = !empty($m['broadcastVR']);
    $viewers = (int)($m['viewersCount'] ?? 0);
    $has     = fn($tag) => (bool) array_filter($tags, fn($t) => str_contains($t, $tag));

    $hairMap = ['blondes'=>'blonde','brunettes'=>'brünette','redheads'=>'rothaarige','black-hair'=>'schwarzhaarige','colorful'=>'bunt gefärbte'];
    $hair = ''; foreach ($hairMap as $tag => $val) { if ($has($tag)) { $hair = $val; break; } }

    $bodyMap = ['petite'=>'zierliche','athletic'=>'sportliche','curvy'=>'kurvige','bbw'=>'vollbusige','big-tits'=>'vollbusige','big-ass'=>'vollbusige'];
    $body = 'heiße'; foreach ($bodyMap as $tag => $val) { if ($has($tag)) { $body = $val; break; } }

    $ethMap = ['asian'=>'Asiatische','ebony'=>'Ebony','latin'=>'Latina','russian'=>'Russische','indian'=>'Indische','arab'=>'Arabische','white'=>'Europäische','mixed'=>'Gemischte','ukrainian'=>'Ukrainische','colombian'=>'Kolumbianische','japanese'=>'Japanische','korean'=>'Koreanische','thai'=>'Thailändische','chinese'=>'Chinesische','pinay'=>'Filipinische'];
    $eth = ''; foreach ($ethMap as $tag => $val) { if ($has($tag)) { $eth = $val . ' '; break; } }

    $featList = [];
    if ($has('big-tits'))   $featList[] = 'große Titten';
    if ($has('big-ass'))    $featList[] = 'einen tollen großen Arsch';
    if ($has('small-tits')) $featList[] = 'süße kleine Titten';
    if ($has('tattoos'))    $featList[] = 'heiße Tattoos';
    if ($has('piercings'))  $featList[] = 'sexy Piercings';

    $actList = [];
    if ($has('lovense'))      $actList[] = 'interaktive Lovense-Shows';
    if ($has('squirt'))       $actList[] = 'Squirt';
    if ($has('anal'))         $actList[] = 'Anal';
    if ($has('lesbians'))     $actList[] = 'Lesbien-Shows';
    if ($has('masturbation')) $actList[] = 'Solo-Masturbation';
    if ($has('blowjob'))      $actList[] = 'Blowjob';
    if ($has('foot-fetish'))  $actList[] = 'Fußfetisch';
    if ($has('cosplay'))      $actList[] = 'Cosplay';
    if ($has('outdoor'))      $actList[] = 'Outdoor-Shows';

    $quality  = ($isHD && $isVR) ? 'HD und VR' : ($isHD ? 'HD' : ($isVR ? 'VR' : 'Live'));
    $hairPart = $hair ? $hair . ' Webcam-Model ' : 'Webcam-Model ';
    $fromPart = $country ? 'aus ' . $country . ' ' : '';

    $hn = h($name);
    $viewersTxt = $viewers > 0
        ? 'Bereits ' . fmtNum($viewers) . ' Zuschauer sehen ihr gerade zu — verpasse es nicht!'
        : 'Sie ist jetzt live und bereit für dich.';

    $p1 = "<p>Treffe <strong>{$hn} stripchat</strong> — eine {$eth}{$hairPart}{$fromPart}die gerade live in {$quality} sendet. {$viewersTxt}</p>";

    $p2Parts = [];
    if ($featList) {
        $last = array_pop($featList);
        $featStr = $featList ? implode(', ', $featList) . ' und ' . $last : $last;
        $p2Parts[] = "{$hn} zeigt dir {$featStr}";
    }
    if ($actList) {
        $last = array_pop($actList);
        $aStr = $actList ? implode(', ', $actList) . ' und ' . $last : $last;
        $p2Parts[] = "sie ist bekannt für {$aStr}";
    }

    $p2 = $p2Parts
        ? '<p>' . implode(', ', $p2Parts) . '. Sie liebt es zu verführen und gibt ihren Zuschauern genau das, wonach sie suchen.</p>'
        : "<p>{$hn} liefert jedes Mal eine unvergessliche Show. Sie weiß, wie sie ihr Publikum fesselt.</p>";

    $langMap = ['en'=>'Englisch','es'=>'Spanisch','ru'=>'Russisch','ko'=>'Koreanisch','ja'=>'Japanisch','fr'=>'Französisch','de'=>'Deutsch','pt'=>'Portugiesisch','it'=>'Italienisch','zh'=>'Chinesisch','ar'=>'Arabisch','tr'=>'Türkisch','pl'=>'Polnisch','ro'=>'Rumänisch','uk'=>'Ukrainisch'];
    $langNames = array_values(array_filter(array_map(fn($l) => $langMap[$l] ?? null, $langs)));
    $langSentence = $langNames ? "Sie spricht " . implode(' und ', array_slice($langNames,0,2)) . ", sodass der Chat persönlich und einfach ist. " : '';

    $p3 = "<p>Was auch immer deine Stimmung ist, {$hn} kann sie erfüllen — von sinnlich-langsam bis wild und explizit. {$langSentence}Sag ihr, was du möchtest, und sie macht es möglich. Private Shows sind jederzeit verfügbar.</p>";
    $p4 = "<p>Wenn {$hn} dich antörnt, schick ihr ein Trinkgeld — sie liest jede Nachricht und belohnt ihre großzügigsten Fans mit ganz besonderen Shows. Klicke jetzt auf Stripchat für ein volles privates Erlebnis mit ihr.</p>";

    return $p1 . $p2 . $p3 . $p4;
}

function generateModelDescriptionHtml(array $m, string $name): string {
    $tags    = $m['tags'] ?? [];
    $country = strtoupper($m['modelsCountry'] ?? '');
    $langs   = $m['languages'] ?? [];
    $isHD    = !empty($m['broadcastHD']);
    $isVR    = !empty($m['broadcastVR']);
    $viewers = (int)($m['viewersCount'] ?? 0);
    $has     = fn($tag) => (bool) array_filter($tags, fn($t) => str_contains($t, $tag));

    $hairMap = ['blondes'=>'blonde','brunettes'=>'brunette','redheads'=>'fiery red','black-hair'=>'dark','colorful'=>'colorful'];
    $hair = '';
    foreach ($hairMap as $tag => $val) { if ($has($tag)) { $hair = $val; break; } }

    $bodyMap = ['petite'=>'petite slim','athletic'=>'fit and toned','curvy'=>'curvy','bbw'=>'big beautiful','big-tits'=>'busty','big-ass'=>'booty'];
    $body = 'sexy';
    foreach ($bodyMap as $tag => $val) { if ($has($tag)) { $body = $val; break; } }

    $ethMap = ['asian'=>'Asian','ebony'=>'Ebony','latin'=>'Latina','russian'=>'Russian','indian'=>'Indian','arab'=>'Arab','white'=>'European','mixed'=>'mixed','ukrainian'=>'Ukrainian','colombian'=>'Colombian','japanese'=>'Japanese','korean'=>'Korean','thai'=>'Thai','chinese'=>'Chinese','pinay'=>'Filipina'];
    $eth = '';
    foreach ($ethMap as $tag => $val) { if ($has($tag)) { $eth = $val . ' '; break; } }

    $featList = [];
    if ($has('big-tits'))   $featList[] = 'gorgeous big tits';
    if ($has('big-ass'))    $featList[] = 'amazing big ass';
    if ($has('small-tits')) $featList[] = 'cute small tits';
    if ($has('tattoos'))    $featList[] = 'hot tattoos';
    if ($has('piercings'))  $featList[] = 'sexy piercings';

    $actList = [];
    if ($has('lovense'))      $actList[] = 'interactive Lovense toy shows';
    if ($has('squirt'))       $actList[] = 'squirt';
    if ($has('anal'))         $actList[] = 'anal';
    if ($has('lesbians'))     $actList[] = 'lesbian shows';
    if ($has('masturbation')) $actList[] = 'solo masturbation';
    if ($has('blowjob'))      $actList[] = 'blowjob';
    if ($has('foot-fetish'))  $actList[] = 'foot fetish';
    if ($has('cosplay'))      $actList[] = 'cosplay';
    if ($has('outdoor'))      $actList[] = 'outdoor shows';

    $interestMap = ['romantic'=>'romance and sensual vibes','gamers'=>'gaming and geek culture','glamour'=>'glamour and luxury aesthetics','bdsm'=>'BDSM and kink','goth'=>'goth aesthetics','student'=>'college life'];
    $interests = [];
    foreach ($interestMap as $tag => $val) { if ($has($tag)) $interests[] = $val; }

    $quality  = ($isHD && $isVR) ? 'HD and VR' : ($isHD ? 'HD' : ($isVR ? 'VR' : 'live'));
    $hairPart = $hair ? $hair . '-haired ' : '';
    $fromPart = $country ? 'from ' . $country . ' ' : '';
    $langPart = $langs ? 'speaks ' . implode(' and ', array_slice($langs, 0, 2)) : '';

    $hn = h($name);
    $viewersTxt = $viewers > 0
        ? 'Already ' . fmtNum($viewers) . ' people are watching her — don\'t miss out.'
        : 'She\'s live and ready for you right now.';

    $p1 = "<p>Meet <strong>{$hn} stripchat</strong> — a {$eth}{$hairPart}{$body} webcam model "
        . "{$fromPart}streaming live right now in {$quality}. {$viewersTxt}</p>";

    $featListCopy = $featList;
    $p2Parts = [];
    if ($featListCopy) {
        $last    = array_pop($featListCopy);
        $featStr = $featListCopy ? implode(', ', $featListCopy) . ' and ' . $last : $last;
        $p2Parts[] = "{$hn} will show off her {$featStr}";
    }
    $actListCopy = $actList;
    if ($actListCopy) {
        $last = array_pop($actListCopy);
        $aStr = $actListCopy ? implode(', ', $actListCopy) . ' and ' . $last : $last;
        $p2Parts[] = "she's known for {$aStr}";
    }
    if ($interests) $p2Parts[] = "she's into " . implode(' and ', $interests);

    $p2 = $p2Parts
        ? '<p>' . implode(', ', $p2Parts) . '. She loves to tease and always gives her viewers exactly what they came for.</p>'
        : "<p>{$hn} puts on an unforgettable show every single time she goes live. She knows how to keep her audience hooked.</p>";

    $langSentence = $langPart ? "She {$langPart}, so chatting with her is easy and personal. " : '';
    $p3 = "<p>Whatever mood you're in, {$hn} can match it — from slow and sensual to wild and explicit. "
        . "{$langSentence}Tell her what you want and she'll make it happen. Private shows are available anytime.</p>";

    $p4 = "<p>If {$hn} turns you on, send her a tip — she reads every message and rewards her most generous fans with extra-special performances. "
        . "Hit Watch on Stripchat for a full private experience with her right now.</p>";

    return $p1 . $p2 . $p3 . $p4;
}

// --- Build SEO values + server-rendered HTML blocks ---
$model = $username ? fetchModel($username) : null;

$avatarFallback = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 80 80'%3E%3Crect width='80' height='80' fill='%23181820'/%3E%3Ccircle cx='40' cy='32' r='16' fill='%23252535'/%3E%3Cpath d='M10 70c0-16.6 13.4-30 30-30s30 13.4 30 30' fill='%23252535'/%3E%3C/svg%3E";

if ($model) {
    $name    = $model['username'] ?? $username;
    $allTags = $model['tags'] ?? [];

    // --- Meta / SEO ---
    $country   = !empty($model['modelsCountry']) ? ' from ' . strtoupper($model['modelsCountry']) : '';
    $hdStr     = !empty($model['broadcastHD']) ? 'HD ' : '';
    $vrStr     = !empty($model['broadcastVR']) ? 'VR ' : '';
    $genderLabel = match($model['gender'] ?? 'female') {
        'males'                 => t('gender_desc_male',   'gay cam model'),
        'maleFemale', 'females' => t('gender_desc_couple', 'couples cam model'),
        'trans'                 => t('gender_desc_trans',  'trans cam model'),
        default                 => t('gender_desc_female', 'cam girl'),
    };
    $title = "{$name} stripchat";
    // Connector keys carry their own leading separator (space for Latin scripts,
    // a punctuation mark for CJK) so the template never needs to add spacing.
    $countryClause = !empty($model['modelsCountry'])
        ? tf('model_meta_from', ' from %s', strtoupper($model['modelsCountry']))
        : '';
    $qualityWord = trim($hdStr . $vrStr) ?: t('model_meta_live', 'live');
    $desc = tf('model_meta_desc',
        '%1$s stripchat is a %2$s%3$s. Watch %1$s in a %4$s webcam show%5$s. Free, no registration required.',
        $name, $genderLabel, $countryClause, $qualityWord, '');
    $imgUrl  = $model['snapshotUrl'] ?? $model['popularSnapshotUrl'] ?? $model['previewUrlThumbSmall'] ?? '';
    $pageUrl = $_siteUrl . '/model/' . rawurlencode($name);

    $primaryTag = 'girls';
    foreach (['girls','men','trans','couples'] as $t) {
        if (in_array($t, $allTags)) { $primaryTag = $t; break; }
    }
    $catLabels = ['girls'=>t('nav_girls','Girls'),'men'=>t('nav_men','Guys'),'trans'=>t('nav_trans','Trans'),'couples'=>t('nav_couples','Couples')];
    $catLabel  = $catLabels[$primaryTag] ?? 'Girls';

    // --- Info card: top block (avatar + name + status + stats) ---
    $mStatus      = $model['status'] ?? 'public';
    $isOnline     = in_array($mStatus, ['public','private','groupShow','p2p']);
    $statusLabels = [
        'public'    => t('status_live','Live Now'),
        'private'   => t('status_private','Private Show'),
        'groupShow' => t('status_group','Group Show'),
        'p2p'       => t('status_p2p','Private Chat'),
    ];
    $statusLabel  = $statusLabels[$mStatus] ?? 'Live';
    $avatarUrl    = $model['avatarUrl'] ?? '';
    $viewersCount  = (int)($model['viewersCount'] ?? 0);
    $favoritedCount = (int)($model['favoritedCount'] ?? 0);

    $minfoTopHtml = '
<div class="minfo-avatar-row">
  <img class="minfo-avatar" src="' . h($avatarUrl) . '" alt="' . h($name) . ' stripchat"
    onerror="this.src=\'' . $avatarFallback . '\'" width="60" height="60" loading="lazy">
  <div>
    <h1 class="minfo-name">' . h($name) . '</h1>'
    . ($isOnline
        ? '<div class="minfo-status"><span class="live-dot" style="width:7px;height:7px;" aria-hidden="true"></span>' . h($statusLabel) . '</div>'
        : '<div style="font-size:11px;color:var(--muted);margin-top:3px">Offline</div>')
    . '
  </div>
</div>
<div class="minfo-stats">
  <div class="mstat"><div class="mstat-val">' . fmtNum($viewersCount) . '</div><div class="mstat-lbl">' . t('stat_viewers','Viewers') . '</div></div>
  <div class="mstat"><div class="mstat-val">' . fmtNum($favoritedCount) . '</div><div class="mstat-lbl">' . t('stat_favorites','Favorites') . '</div></div>
</div>
<div style="display:flex;gap:5px;flex-wrap:wrap">'
    . (!empty($model['broadcastVR']) ? '<span class="badge b-vr" style="font-size:10px;padding:3px 8px">VR</span>' : '')
    . (!empty($model['isNew'])       ? '<span class="badge b-new" style="font-size:10px;padding:3px 8px">NEW MODEL</span>' : '')
    . '</div>';

    // --- Info card: details rows ---
    // Each row: ['label' => string, 'href' => string|null]
    $findTag = fn($kw) => (function() use ($allTags, $kw) {
        foreach ($allTags as $t) { if (str_contains(strtolower($t), $kw)) return $t; }
        return null;
    })();

    $infoRows = [];
    if (!empty($model['modelsCountry'])) $infoRows[] = ['label' => flagEmoji($model['modelsCountry']) . ' ' . strtoupper($model['modelsCountry']), 'href' => null];
    if (!empty($model['languages']))     $infoRows[] = ['label' => implode(', ', $model['languages']), 'href' => null];
    if (!empty($model['gender']))        $infoRows[] = ['label' => genderLbl($model['gender']), 'href' => '/' . $primaryTag . '/'];
    if (!empty($model['broadcastHD']))   $infoRows[] = ['label' => t('detail_hd','HD Stream Quality'), 'href' => '/' . $primaryTag . '/hd/'];
    if (!empty($model['broadcastVR']))   $infoRows[] = ['label' => t('detail_vr','VR Broadcast'), 'href' => '/' . $primaryTag . '/vr/'];

    foreach (['blondes','brunettes','redheads','black-hair','colorful'] as $kw) {
        $ft = $findTag($kw); if ($ft) { $infoRows[] = ['label' => subLabel($kw), 'href' => '/' . $ft . '/']; break; }
    }
    foreach (['petite','athletic','curvy','bbw'] as $kw) {
        $ft = $findTag($kw); if ($ft) { $infoRows[] = ['label' => subLabel($kw), 'href' => '/' . $ft . '/']; break; }
    }
    foreach (['asian','ebony','latin','russian','indian','arab','white','mixed','ukrainian','colombian','japanese','korean','thai','chinese','pinay'] as $kw) {
        $ft = $findTag($kw); if ($ft) { $infoRows[] = ['label' => subLabel($kw), 'href' => '/' . $ft . '/']; break; }
    }

    $minfoMetaHtml = '';
    $minfoMetaStyle = 'display:none';
    if ($infoRows) {
        $minfoMetaStyle = '';
        $rowsHtml = implode('', array_map(fn($r) =>
            '<div class="info-row">'
            . '<svg width="14" height="14" viewBox="0 0 20 20" fill="var(--muted)" aria-hidden="true"><circle cx="10" cy="10" r="9" opacity=".4"/></svg>'
            . ($r['href']
                ? '<a class="info-row-link" href="' . h($r['href']) . '">' . h($r['label']) . '</a>'
                : '<span>' . h($r['label']) . '</span>')
            . '</div>', $infoRows));
        $minfoMetaHtml = '<div class="minfo-sec-title">' . t('details_title','Details') . '</div><div class="info-rows" id="infoRows">' . $rowsHtml . '</div>';
    }

    // --- Goal ---
    $goalWrapStyle = 'display:none';
    $goalHtml = '';
    if (!empty($model['goalMessage']) || ($model['neededForGoal'] ?? 0) > 0) {
        $goalWrapStyle = '';
        $pct = ($model['neededForGoal'] ?? 0) > 0
            ? min(100, round(($model['earnedForGoal'] ?? 0) / $model['neededForGoal'] * 100))
            : 0;
        $goalHtml = '
<div class="minfo-section" style="padding-bottom:0"><div class="minfo-sec-title">' . t('goal_title','Current Goal') . '</div></div>
<div class="goal-card">
  <div class="goal-msg" id="goalMsg">' . h($model['goalMessage'] ?? 'Goal active') . '</div>
  <div class="goal-track"><div class="goal-progress" id="goalBar" style="width:' . $pct . '%"></div></div>
  <div class="goal-nums">
    <span id="goalEarned">' . (int)($model['earnedForGoal'] ?? 0) . ' tkn</span>
    <span id="goalNeeded">' . (int)($model['neededForGoal'] ?? 0) . ' tkn</span>
  </div>
</div>';
    }

    // --- Tags ---
    $modelTags = array_values(array_filter($allTags, fn($t) => $t !== $name && $t !== ($model['gender'] ?? '')));
    $minfoTagsStyle = 'display:none';
    $tagsHtml = '';
    if ($modelTags) {
        $minfoTagsStyle = '';
        $tagsHtml = '<div class="minfo-sec-title">Tags</div><div class="tag-list" id="tagList">'
            . implode('', array_map(fn($t) =>
                '<a class="tag-link" href="/' . h($t) . '/" title="Browse ' . h(explode('/', $t)[count(explode('/', $t))-1]) . ' models">'
                . h(explode('/', $t)[count(explode('/', $t))-1]) . '</a>',
                array_slice($modelTags, 0, 30)))
            . '</div>';
    }

    // --- Visit link ---
    $visitStyle = 'display:none';
    $visitHref  = '#';
    if (!empty($model['clickUrl'])) {
        $visitStyle = '';
        $visitHref  = $model['clickUrl'];
    }

    // --- Description (per-language; falls back to EN) ---
    $descHtml = match ($lang) {
        'de' => generateModelDescriptionHtmlDE($model, $name),
        'fr' => generateModelDescriptionHtmlFR($model, $name),
        'it' => generateModelDescriptionHtmlIT($model, $name),
        'es' => generateModelDescriptionHtmlES($model, $name),
        'pt' => generateModelDescriptionHtmlPT($model, $name),
        'ja' => generateModelDescriptionHtmlJP($model, $name),
        'zh' => generateModelDescriptionHtmlCN($model, $name),
        'ko' => generateModelDescriptionHtmlKR($model, $name),
        'hi' => generateModelDescriptionHtmlIN($model, $name),
        default => generateModelDescriptionHtml($model, $name),
    };

    // --- Schema.org ---
    $ldJson = json_encode([
        '@context' => 'https://schema.org',
        '@graph'   => [
            ['@type'=>'BreadcrumbList','itemListElement'=>[
                ['@type'=>'ListItem','position'=>1,'name'=>t('breadcrumb_home','Home'),'item'=>$_siteUrl.'/'],
                ['@type'=>'ListItem','position'=>2,'name'=>$catLabel,'item'=>$_siteUrl.'/'.$primaryTag.'/'],
                ['@type'=>'ListItem','position'=>3,'name'=>$name,'item'=>$pageUrl],
            ]],
            ['@type'=>'Person','name'=>$name,'description'=>"{$name} is a live webcam model streaming on sex-videochat.info.".($country?" Based in ".trim($country).".":""),'url'=>$pageUrl,'image'=>$imgUrl],
            array_filter([
                '@type'        => 'VideoObject',
                'name'         => "{$name} Live Webcam Stream",
                'description'  => $desc,
                'thumbnailUrl' => $imgUrl,
                'uploadDate'   => date('Y-m-d\TH:i:sP'),
                'embedUrl'     => 'https://creative.eiscprts.com/widgets/Player?modelName='.rawurlencode($name).'&userId='.USER_ID,
                'contentUrl'   => $model['clickUrl'] ?? $pageUrl,
                'interactionStatistic' => !empty($model['viewersCount']) ? [
                    '@type'=>'InteractionCounter',
                    'interactionType'=>'https://schema.org/WatchAction',
                    'userInteractionCount'=>(int)$model['viewersCount'],
                ] : null,
            ]),
        ],
    ], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);

} else {
    $name           = $username ?: 'Model';
    $title          = "{$name} stripchat";
    $desc           = "Watch {$name} live on webcam for free. Join the live show on sex-videochat.info.";
    $imgUrl         = '';
    $pageUrl        = SITE_URL . '/model/' . rawurlencode($name);
    $catLabel       = 'Girls';
    $primaryTag     = 'girls';
    $ldJson         = '{}';
    $minfoTopHtml   = '<div class="skeleton" style="height:80px;border-radius:8px;margin-bottom:10px"></div>'
                    . '<div class="skeleton" style="height:60px;border-radius:8px"></div>';
    $minfoMetaHtml  = '';
    $minfoMetaStyle = 'display:none';
    $goalWrapStyle  = 'display:none';
    $goalHtml       = '';
    $minfoTagsStyle = 'display:none';
    $tagsHtml       = '';
    $visitStyle     = 'display:none';
    $visitHref      = '#';
    $descHtml       = '';
}
?><!DOCTYPE html>
<html lang="<?= t('lang_attr','en') ?>">
<head>
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-NQM6FB2X');</script>
<!-- End Google Tag Manager -->
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XS6BT34GJP"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('config', 'G-XS6BT34GJP');
</script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">

<title id="pageTitle"><?= h($title) ?></title>
<meta name="description" id="pageDesc" content="<?= h($desc) ?>">
<meta name="robots" content="index, follow, max-image-preview:large">
<link rel="canonical" id="pageCanonical" href="<?= h($pageUrl) ?>">
<?= hreflangTags('/model/' . rawurlencode($name)) ?>

<meta property="og:type" content="video.other">
<meta property="og:site_name" content="<?= $_SERVER['HTTP_HOST'] ?>">
<meta property="og:title" id="ogTitle" content="<?= h($title) ?>">
<meta property="og:description" id="ogDesc" content="<?= h($desc) ?>">
<meta property="og:url" id="ogUrl" content="<?= h($pageUrl) ?>">
<meta property="og:image" id="ogImage" content="<?= h($imgUrl) ?>">
<meta property="og:image:width" content="1280">
<meta property="og:image:height" content="720">

<meta name="twitter:card" content="player">
<meta name="twitter:title" id="twTitle" content="<?= h($title) ?>">
<meta name="twitter:description" id="twDesc" content="<?= h($desc) ?>">
<meta name="twitter:image" id="twImage" content="<?= h($imgUrl) ?>">

<script type="application/ld+json" id="schemaLD"><?= $ldJson ?></script>

<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="stylesheet" href="/style.css">
<link rel="preconnect" href="https://creative.eiscprts.com">
</head>
<body>
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-NQM6FB2X"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

<header class="site-header" role="banner">
  <div class="header-row">
    <a href="/" class="logo" aria-label="sex-videochat.info - Home"><span class="logo-text">Sex-videochat<span class="logo-tld">.info</span></span></a>
    <div class="search-wrap" role="search" style="flex:1;max-width:320px">
      <svg class="search-icon" width="15" height="15" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" aria-hidden="true">
        <circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/>
      </svg>
      <input type="search" id="searchInput" placeholder="<?= t('search_placeholder','Search models…') ?>" aria-label="Search cam models"
        onkeydown="if(event.key==='Enter')window.location.href='/?q='+encodeURIComponent(this.value)">
    </div>
    <div class="hdr-actions">
      <?= langSwitcherHtml() ?>
    </div>
  </div>
</header>

<main class="model-page" id="main" role="main">

  <nav class="breadcrumb" aria-label="Breadcrumb" id="breadcrumb">
    <a href="/"><?= t('breadcrumb_home','Home') ?></a>
    <svg width="12" height="12" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="m9 18 6-6-6-6"/></svg>
    <span id="breadCat"><a href="/<?= h($primaryTag) ?>/"><?= h($catLabel) ?></a></span>
    <svg width="12" height="12" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="m9 18 6-6-6-6"/></svg>
    <span id="breadName" aria-current="page"><?= h($name) ?></span>
  </nav>

  <div class="model-layout">

    <div>
      <div class="player-wrap" id="playerWrap">
        <div id="scPlayerContainer"></div>
        <div class="player-overlay hidden" id="playerOverlay">
          <div class="player-bar">
            <span class="player-live-txt">LIVE</span>
            <div class="player-controls">
              <button class="player-ctrl-btn" aria-label="Mute" onclick="toggleVolume()">
                <svg id="volIcon" width="18" height="18" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
                  <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
                  <line x1="23" y1="9" x2="17" y2="15"/><line x1="17" y1="9" x2="23" y2="15"/>
                </svg>
              </button>
              <input type="range" class="player-vol-slider" id="volSlider" min="0" max="100" value="0"
                style="background:linear-gradient(to right,var(--primary) 0%,rgba(255,255,255,.35) 0%)"
                oninput="setVolume(this.value)" aria-label="Volume">
              <button class="player-ctrl-btn" aria-label="Fullscreen" onclick="togglePlayerFs()">
                <svg id="playerFsIcon" width="18" height="18" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
                  <path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/>
                </svg>
              </button>
            </div>
          </div>
        </div>
      </div>

      <div class="model-desc" id="modelDesc"<?= $descHtml ? '' : ' style="display:none"' ?>>
        <h2 id="descTitle"><?= $model ? h($name) . ' Stripchat' : '' ?></h2>
        <div id="descText"><?= $descHtml ?></div>
      </div>
    </div>

    <aside aria-label="Model information">
      <div class="model-info-card" id="modelCard">

        <div class="minfo-top" id="minfoTop">
          <?= $minfoTopHtml ?>
        </div>

        <div class="minfo-section" id="minfoMeta" style="<?= $minfoMetaStyle ?>">
          <?= $minfoMetaHtml ?>
        </div>

        <div id="goalWrap" style="<?= $goalWrapStyle ?>">
          <?= $goalHtml ?>
        </div>

        <div class="minfo-section" id="minfoTags" style="<?= $minfoTagsStyle ?>">
          <?= $tagsHtml ?>
        </div>

        <a class="visit-link" id="visitLink" href="<?= h($visitHref) ?>" target="_blank"
          rel="noopener noreferrer sponsored" style="<?= $visitStyle ?>">
          <svg width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" aria-hidden="true">
            <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/>
          </svg>
          <?= tf('visit_btn','Free Sex Chat with %s', h($name)) ?>
        </a>

      </div>
    </aside>

    <section class="similar-section" id="similarSection" style="display:none" aria-label="Similar live cam models">
      <div class="section-head">
        <h2 class="section-title"><?= t('similar_title','Similar Live Models') ?></h2>
      </div>
      <div class="model-grid" id="similarGrid"></div>
      <div class="load-more-wrap" id="similarLoadMoreWrap" style="display:none">
        <button class="load-more-btn" id="similarLoadMoreBtn"><?= t('load_more','Load More') ?></button>
      </div>
    </section>

  </div>

</main>

<!-- ===== FOOTER ===== -->
<footer class="site-footer" role="contentinfo">
  <div class="footer-glow"></div>
  <div class="footer-inner">

    <?php
    // Определение текущего языка по subdomain
    $__host = $_SERVER['HTTP_HOST'] ?? '';
    $__sub = '';
    if (preg_match('/^([a-z]+)\.sex-videochat\.info$/', $__host, $__m)) {
        $__sub = $__m[1];
    }
    $__currentLang = match($__sub) {
        'de' => 'de', 'fr' => 'fr', 'es' => 'es', 'it' => 'it',
        'pt' => 'pt', 'jp' => 'ja', 'cn' => 'zh', 'kr' => 'ko', 'in' => 'hi',
        default => 'en',
    };
    $__langActive = fn($code) => $__currentLang === $code ? ' active' : '';
    ?>

    <!-- ROW 1: Logo + Description + Socials -->
    <div class="footer-top">
      <a href="/" class="footer-logo" aria-label="Home">
        <span class="footer-logo-icon">
          <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
            <path d="M12 2C9 2 6 5 6 9C6 14.5 9.5 20 12 21.5C14.5 20 18 14.5 18 9C18 5 15 2 12 2Z" fill="#ff3d7f"/>
            <path d="M12 2C12 2 10.5 3.5 10.5 5C10.5 5 11.2 4.2 12 4.2C12.8 4.2 13.5 5 13.5 5C13.5 3.5 12 2 12 2Z" fill="#2a0a15"/>
            <circle cx="9.5" cy="10" r="0.9" fill="#fff" opacity="0.5"/>
            <circle cx="14.2" cy="11.5" r="0.9" fill="#fff" opacity="0.5"/>
            <circle cx="12" cy="15.5" r="0.9" fill="#fff" opacity="0.5"/>
          </svg>
        </span>
        <span class="footer-logo-text">Sex-videochat<span class="logo-tld">.info</span></span>
      </a>
      <p class="footer-tagline">Free live webcam shows with thousands of models streaming 24/7. HD quality, no registration required.</p>
      <div class="footer-socials">
        <a href="#" class="social-btn" aria-label="Twitter"><svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg></a>
        <a href="#" class="social-btn" aria-label="Instagram"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="2" width="20" height="20" rx="5"/><circle cx="12" cy="12" r="4"/><circle cx="17.5" cy="6.5" r="1" fill="currentColor"/></svg></a>
        <a href="#" class="social-btn" aria-label="Reddit"><svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0zm5.01 4.744c.688 0 1.25.561 1.25 1.249a1.25 1.25 0 0 1-2.498.056l-2.597-.547-.8 3.747c1.824.07 3.48.632 4.674 1.488.308-.309.73-.491 1.207-.491.968 0 1.754.786 1.754 1.754 0 .716-.435 1.333-1.01 1.614a3.111 3.111 0 0 1 .042.52c0 2.694-3.13 4.87-7.004 4.87-3.874 0-7.004-2.176-7.004-4.87 0-.183.015-.366.043-.534A1.748 1.748 0 0 1 4.028 12c0-.968.786-1.754 1.754-1.754.463 0 .898.196 1.207.49 1.207-.883 2.878-1.43 4.744-1.487l.885-4.182a.342.342 0 0 1 .14-.197.35.35 0 0 1 .238-.042l2.906.617a1.214 1.214 0 0 1 1.108-.701zM9.25 12C8.561 12 8 12.562 8 13.25c0 .687.561 1.248 1.25 1.248.687 0 1.248-.561 1.248-1.249 0-.688-.561-1.249-1.249-1.249zm5.5 0c-.687 0-1.248.561-1.248 1.25 0 .687.561 1.248 1.249 1.248.688 0 1.249-.561 1.249-1.249 0-.687-.562-1.249-1.25-1.249zm-5.466 3.991a3.234 3.234 0 0 0 2.723 0 .263.263 0 0 1 .346.1.263.263 0 0 1-.1.347 3.756 3.756 0 0 1-3.212 0 .262.262 0 0 1-.1-.347.263.263 0 0 1 .347-.1z"/></svg></a>
      </div>
    </div>

    <!-- ROW 2: Languages (теперь ПОД соцсетями) -->
    <div class="footer-langs">
      <div class="lang-title">🌐 Languages</div>
      <div class="lang-grid">
        <a href="https://sex-videochat.info/" class="lang-link<?= $__langActive('en') ?>">🇬🇧 English</a>
        <a href="https://de.sex-videochat.info/" class="lang-link<?= $__langActive('de') ?>">🇩🇪 Deutsch</a>
        <a href="https://fr.sex-videochat.info/" class="lang-link<?= $__langActive('fr') ?>">🇫🇷 Français</a>
        <a href="https://es.sex-videochat.info/" class="lang-link<?= $__langActive('es') ?>">🇪🇸 Español</a>
        <a href="https://it.sex-videochat.info/" class="lang-link<?= $__langActive('it') ?>">🇮🇹 Italiano</a>
        <a href="https://pt.sex-videochat.info/" class="lang-link<?= $__langActive('pt') ?>">🇵🇹 Português</a>
        <a href="https://jp.sex-videochat.info/" class="lang-link<?= $__langActive('ja') ?>">🇯🇵 日本語</a>
        <a href="https://cn.sex-videochat.info/" class="lang-link<?= $__langActive('zh') ?>">🇨🇳 中国人</a>
        <a href="https://kr.sex-videochat.info/" class="lang-link<?= $__langActive('ko') ?>">🇰🇷 한국인</a>
        <a href="https://in.sex-videochat.info/" class="lang-link<?= $__langActive('hi') ?>">🇮🇳 हिंदी</a>
      </div>
    </div>

    <!-- ROW 3: 4 columns -->
    <div class="footer-cols">
      <div class="footer-col">
        <h4 class="footer-col-title">Categories</h4>
        <a href="/">Girls</a>
        <a href="/men/">Guys</a>
        <a href="/couples/">Couples</a>
        <a href="/trans/">Trans</a>
        <a href="/girls/hd/">HD Cams</a>
        <a href="/girls/vr/">VR Cams</a>
      </div>
      <div class="footer-col">
        <h4 class="footer-col-title">Popular</h4>
        <a href="/girls/teens/">Teen 18+</a>
        <a href="/girls/milfs/">MILF</a>
        <a href="/girls/asian/">Asian</a>
        <a href="/girls/latin/">Latina</a>
        <a href="/girls/russian/">Russian</a>
        <a href="/girls/bbw/">BBW</a>
      </div>
      <div class="footer-col">
        <h4 class="footer-col-title">Information</h4>
        <a href="/privacy">Privacy Policy</a>
        <a href="/terms">Terms & Conditions</a>
        <a href="/2257">2257</a>
        <a href="/dmca">Profile Removal & DMCA</a>
        <a href="/age-verification">Age Verification</a>
        <a href="/contact">Contact</a>
      </div>
      <div class="footer-col">
        <h4 class="footer-col-title">Support</h4>
        <a href="/blog">Blog</a>
        <a href="/sitemap.xml">Sitemap</a>
        <a href="/models">Models Wanted</a>
        <a href="/affiliates">Affiliates</a>
        <a href="/faq">FAQ</a>
      </div>
    </div>

    <!-- Divider -->
    <div class="footer-divider"></div>

    <!-- ROW 4: Bottom (18+ + Copyright) -->
    <div class="footer-bottom">
      <div class="footer-18">
        <span class="age-badge">18+</span>
        <p class="age-text">This website contains age-restricted materials. By entering, you confirm you are at least 18 years old. All models were 18+ at the time of depiction.</p>
      </div>
      <div class="footer-legal">
        <span>© <span id="yr"></span> sex-videochat.info. All Rights Reserved.</span>
      </div>
    </div>

  </div>
</footer>


<div class="toast-stack" id="toasts" aria-live="assertive"></div>

<script src="https://creative.eiscprts.com/widgets/Player/lib.js" id="SCPlayerScript"></script>

<script>
const I18N = {
  viewers:      '<?= t('stat_viewers','Viewers') ?>',
  favorites:    '<?= t('stat_favorites','Favorites') ?>',
  offline:      '<?= t('offline_label','Offline') ?>',
  visitBtn:     '<?= str_replace(' %s', '', t('visit_btn', 'Free Sex Chat with %s')) ?>',
  loadMore:     '<?= t('load_more','Load More') ?>',
  statusLabels: {public:'<?= t('status_live','Live Now') ?>',private:'<?= t('status_private','Private Show') ?>',groupShow:'<?= t('status_group','Group Show') ?>',p2p:'<?= t('status_p2p','Private Chat') ?>'},
  catLabels:    {girls:'<?= t('nav_girls','Girls') ?>',men:'<?= t('nav_men','Guys') ?>',trans:'<?= t('nav_trans','Trans') ?>',couples:'<?= t('nav_couples','Couples') ?>'},
  genderLabels: {female:'<?= t('detail_female','Female') ?>',males:'<?= t('detail_male','Male') ?>',maleFemale:'<?= t('detail_couple','Couple') ?>',trans:'<?= t('detail_trans','Trans') ?>',females:'<?= t('detail_group','Female Group') ?>'},
  hdLabel:      '<?= t('detail_hd','HD Stream Quality') ?>',
  vrLabel:      '<?= t('detail_vr','VR Broadcast') ?>',
  detailsTitle: '<?= t('details_title','Details') ?>',
  goalTitle:    '<?= t('goal_title','Current Goal') ?>',
  tagsTitle:    'Tags',
  watchBtn:     '<?= t('watch_btn','Watch on Stripchat →') ?>',
  descTitle:    'Stripchat',
  hairLabels:   {blondes:'<?= subLabel('blondes') ?>',brunettes:'<?= subLabel('brunettes') ?>',redheads:'<?= subLabel('redheads') ?>','black-hair':'<?= subLabel('black-hair') ?>',colorful:'<?= subLabel('colorful') ?>'},
  bodyLabels:   {petite:'<?= subLabel('petite') ?>',athletic:'<?= subLabel('athletic') ?>',curvy:'<?= subLabel('curvy') ?>',bbw:'BBW'},
  ethLabels:    {asian:'<?= subLabel('asian') ?>',ebony:'Ebony',latin:'<?= subLabel('latin') ?>',russian:'<?= subLabel('russian') ?>',indian:'<?= subLabel('indian') ?>',arab:'<?= subLabel('arab') ?>',white:'<?= subLabel('white') ?>',mixed:'<?= subLabel('mixed') ?>',ukrainian:'<?= subLabel('ukrainian') ?>',colombian:'<?= subLabel('colombian') ?>',japanese:'<?= subLabel('japanese') ?>',korean:'<?= subLabel('korean') ?>',thai:'<?= subLabel('thai') ?>',chinese:'<?= subLabel('chinese') ?>',pinay:'<?= subLabel('pinay') ?>'},
};
const CFG = {
  userId:  '492b59a9925d01876c059022ef32fa6f9949ef373e4179aa08008786798f6032',
  apiKey:  '22f4ea13b6dd0a08a0d9',
  siteUrl: '<?= $_siteUrl ?>',
};

// True when PHP already rendered this exact model (the common, cached case). The
// server output is correctly localized; the JS updateSEO() below is English-only,
// so it must not overwrite server meta when this is true. It only takes over on a
// cold cache miss, where the server had no model data to localize.
const SERVER_RENDERED = <?= $model ? 'true' : 'false' ?>;

const username = new URLSearchParams(location.search).get('u')
  || decodeURIComponent(location.pathname.replace(/^\/model\//, '').replace(/\/$/, ''))
  || '';

let playerApp    = null;
let similarModels  = [];
let similarVisible = 48;

function updateSEO(m) {
  if (SERVER_RENDERED) return; // server already emitted correct localized meta + schema
  const name    = m.username || username;
  const country = m.modelsCountry ? ` from ${m.modelsCountry.toUpperCase()}` : '';
  const tags    = (m.tags||[]).filter(t=>!['girls','men','trans','couples'].includes(t)).slice(0,3).join(', ');
  const tagsStr = tags ? ` | ${tags}` : '';
  const title   = `${name} stripchat`;
  const langMap = {en:'English',es:'Spanish',ru:'Russian',ko:'Korean',ja:'Japanese',fr:'French',de:'German',pt:'Portuguese',it:'Italian',zh:'Chinese',ar:'Arabic',tr:'Turkish',pl:'Polish',ro:'Romanian',nl:'Dutch',uk:'Ukrainian',cs:'Czech',hu:'Hungarian',sv:'Swedish'};
  const langNames = (m.languages||[]).map(l=>langMap[l]).filter(Boolean);
  const langStr = langNames.length ? ' who speaks '+langNames.join(', ') : '';
  const subTags = [...new Set((m.tags||[]).filter(t=>t.includes('/')).map(t=>t.split('/')[1].replace(/-/g,' ')))].slice(0,5).map(t=>t[0].toUpperCase()+t.slice(1)).join(', ');
  const hdStr   = m.broadcastHD ? 'HD ' : '';
  const vrStr   = m.broadcastVR ? 'VR ' : '';
  const viewers = m.viewersCount ? ` ${m.viewersCount} viewers watching now!` : ' Join the live show!';
  const gLabel  = {males:'gay stripchat model',maleFemale:'couples stripchat cam',females:'couples stripchat cam',trans:'shemale stripchat model'}[m.gender] || 'cam girl on stripchat';
  const desc    = `${name} is a ${gLabel}${country}${langStr}. Watch ${name} — ${hdStr}${vrStr}live webcam show${subTags?' with '+subTags:''}.${viewers}`;
  const imgUrl  = m.snapshotUrl || m.popularSnapshotUrl || m.previewUrlThumbSmall || '';
  const pageUrl = CFG.siteUrl + '/model/' + encodeURIComponent(name);

  document.title = title;
  sm('pageTitle', title, true);
  sm('pageDesc',  desc);
  sm('ogTitle',   title);
  sm('ogDesc',    desc);
  sm('ogUrl',     pageUrl);
  sm('ogImage',   imgUrl);
  sm('twTitle',   title);
  sm('twDesc',    desc);
  sm('twImage',   imgUrl);
  document.getElementById('pageCanonical').href = pageUrl;
  document.getElementById('breadName').textContent = name;

  const primaryTag = (m.tags||[]).find(t => ['girls','men','trans','couples'].includes(t)) || 'girls';
  const catLabel   = I18N.catLabels[primaryTag] || 'Girls';
  document.getElementById('breadCat').innerHTML = `<a href="/${primaryTag}/">${esc(catLabel)}</a>`;

  const ld = {
    "@context": "https://schema.org",
    "@graph": [
      { "@type":"BreadcrumbList","itemListElement":[
        {"@type":"ListItem","position":1,"name":"Home","item":CFG.siteUrl+"/"},
        {"@type":"ListItem","position":2,"name":catLabel,"item":CFG.siteUrl+"/"+primaryTag+"/"},
        {"@type":"ListItem","position":3,"name":name,"item":pageUrl}
      ]},
      { "@type":"Person","name":name,"description":`${name} is a live webcam model streaming on sex-videochat.info.${country?' Based in '+country.trim()+'.':''} ${tagsStr}`,"url":pageUrl,"image":imgUrl,"sameAs":m.clickUrl||'' },
      { "@type":"VideoObject","name":`${name} Live Webcam Stream`,"description":desc,"thumbnailUrl":imgUrl,"uploadDate":new Date().toISOString(),
        "embedUrl":CFG.userId?`https://creative.eiscprts.com/widgets/Player?modelName=${encodeURIComponent(name)}&userId=${CFG.userId}`:pageUrl,
        "contentUrl":m.clickUrl||pageUrl,
        ...(m.viewersCount?{"interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/WatchAction","userInteractionCount":m.viewersCount}}:{})
      }
    ]
  };
  document.getElementById('schemaLD').textContent = JSON.stringify(ld);
}

function sm(id, val, isTitle) {
  const el = document.getElementById(id);
  if (!el) return;
  if (isTitle) el.textContent = val;
  else el.content !== undefined ? el.content = val : el.textContent = val;
}

function generateModelDescription(m, name, isOnline) {
  const tags    = m.tags || [];
  const country = (m.modelsCountry || '').toUpperCase();
  const langs   = m.languages || [];
  const isHD    = m.broadcastHD;
  const isVR    = m.broadcastVR;
  const viewers = m.viewersCount || 0;
  const has     = tag => tags.some(t => t.includes(tag));

  const hairMap = { blondes:'blonde', brunettes:'brunette', redheads:'fiery red', 'black-hair':'dark', colorful:'colorful' };
  let hair = '';
  for (const [tag, val] of Object.entries(hairMap)) if (has(tag)) { hair = val; break; }

  const bodyMap = { petite:'petite slim', athletic:'fit and toned', curvy:'curvy', bbw:'big beautiful', 'big-tits':'busty', 'big-ass':'booty' };
  let body = 'sexy';
  for (const [tag, val] of Object.entries(bodyMap)) if (has(tag)) { body = val; break; }

  const ethMap = { asian:'Asian', ebony:'Ebony', latin:'Latina', russian:'Russian', indian:'Indian', arab:'Arab', white:'European', mixed:'mixed', ukrainian:'Ukrainian', colombian:'Colombian', japanese:'Japanese', korean:'Korean', thai:'Thai', chinese:'Chinese', pinay:'Filipina' };
  let eth = '';
  for (const [tag, val] of Object.entries(ethMap)) if (has(tag)) { eth = val + ' '; break; }

  const featList = [];
  if (has('big-tits'))   featList.push('gorgeous big tits');
  if (has('big-ass'))    featList.push('amazing big ass');
  if (has('small-tits')) featList.push('cute small tits');
  if (has('tattoos'))    featList.push('hot tattoos');
  if (has('piercings'))  featList.push('sexy piercings');

  const actList = [];
  if (has('lovense'))       actList.push('interactive Lovense toy shows');
  if (has('squirt'))        actList.push('squirt');
  if (has('anal'))          actList.push('anal');
  if (has('lesbians'))      actList.push('lesbian shows');
  if (has('masturbation'))  actList.push('solo masturbation');
  if (has('blowjob'))       actList.push('blowjob');
  if (has('foot-fetish'))   actList.push('foot fetish');
  if (has('cosplay'))       actList.push('cosplay');
  if (has('outdoor'))       actList.push('outdoor shows');

  const interestMap = { romantic:'romance and sensual vibes', gamers:'gaming and geek culture', glamour:'glamour and luxury aesthetics', bdsm:'BDSM and kink', goth:'goth aesthetics', student:'college life' };
  const interests = [];
  for (const [tag, val] of Object.entries(interestMap)) if (has(tag)) interests.push(val);

  const quality   = isHD && isVR ? 'HD and VR' : isHD ? 'HD' : isVR ? 'VR' : 'live';
  const hairPart  = hair ? `${hair}-haired ` : '';
  const fromPart  = country ? `from ${country} ` : '';
  const langPart  = langs.length ? `speaks ${langs.slice(0,2).join(' and ')}` : '';

  const viewersTxt = viewers > 0 ? `Already ${fmtNum(viewers)} people are watching her — don't miss out.` : `She's live and ready for you right now.`;
  const p1 = `<p>Meet <strong>${esc(name)} stripchat</strong> — a ${eth}${hairPart}${body} webcam model ${fromPart}streaming live right now in ${quality}. ${viewersTxt}</p>`;

  const p2Parts = [];
  if (featList.length) { const last = featList.pop(); const featStr = featList.length ? featList.join(', ')+' and '+last : last; p2Parts.push(`${esc(name)} will show off her ${featStr}`); }
  if (actList.length)  { const last = actList.pop();  const aStr   = actList.length  ? actList.join(', ')+'  and '+last : last; p2Parts.push(`she's known for ${aStr}`); }
  if (interests.length) p2Parts.push(`she's into ${interests.join(' and ')}`);
  const p2 = p2Parts.length
    ? `<p>${p2Parts.join(', ')}. She loves to tease and always gives her viewers exactly what they came for.</p>`
    : `<p>${esc(name)} puts on an unforgettable show every single time she goes live. She knows how to keep her audience hooked.</p>`;

  const langSentence = langPart ? `She ${langPart}, so chatting with her is easy and personal. ` : '';
  const p3 = `<p>Whatever mood you're in, ${esc(name)} can match it — from slow and sensual to wild and explicit. ${langSentence}Tell her what you want and she'll make it happen. Private shows are available anytime.</p>`;
  const p4 = `<p>If ${esc(name)} turns you on, send her a tip — she reads every message and rewards her most generous fans with extra-special performances. Hit Watch on Stripchat for a full private experience with her right now.</p>`;

  return p1 + p2 + p3 + p4;
}

function renderModel(m) {
  updateSEO(m);

  const name     = m.username || username;
  const status   = m.status || 'public';
  const isOnline = ['public','private','groupShow','p2p'].includes(status);
  const statusLabels = I18N.statusLabels;
  const avatarFallback = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 80 80'%3E%3Crect width='80' height='80' fill='%23181820'/%3E%3Ccircle cx='40' cy='32' r='16' fill='%23252535'/%3E%3Cpath d='M10 70c0-16.6 13.4-30 30-30s30 13.4 30 30' fill='%23252535'/%3E%3C/svg%3E`;

  document.getElementById('minfoTop').innerHTML = `
    <div class="minfo-avatar-row">
      <img class="minfo-avatar" src="${esc(m.avatarUrl||'')}" alt="${esc(name)} stripchat"
        onerror="this.src='${avatarFallback}'" width="60" height="60" loading="lazy">
      <div>
        <h1 class="minfo-name">${esc(name)}</h1>
        ${isOnline
          ? `<div class="minfo-status"><span class="live-dot" style="width:7px;height:7px;" aria-hidden="true"></span>${esc(statusLabels[status]||'Live')}</div>`
          : `<div style="font-size:11px;color:var(--muted);margin-top:3px">${I18N.offline}</div>`}
      </div>
    </div>
    <div class="minfo-stats">
      <div class="mstat"><div class="mstat-val">${fmtNum(m.viewersCount||0)}</div><div class="mstat-lbl">${I18N.viewers}</div></div>
      <div class="mstat"><div class="mstat-val">${fmtNum(m.favoritedCount||0)}</div><div class="mstat-lbl">${I18N.favorites}</div></div>
    </div>
    <div style="display:flex;gap:5px;flex-wrap:wrap">
      ${m.broadcastVR ? '<span class="badge b-vr" style="font-size:10px;padding:3px 8px">VR</span>' : ''}
      ${m.isNew       ? '<span class="badge b-new" style="font-size:10px;padding:3px 8px">NEW MODEL</span>' : ''}
    </div>`;

  function findTag(tags, kw) {
    return (tags||[]).find(t => t.toLowerCase().includes(kw)) || null;
  }

  const genderToTag = {female:'girls',females:'girls',males:'men',maleFemale:'couples',trans:'trans'};
  const primaryTag  = genderToTag[m.gender] || 'girls';

  const rows = [];
  if (m.modelsCountry) rows.push({label: flag(m.modelsCountry) + ' ' + m.modelsCountry.toUpperCase(), href: null});
  if ((m.languages||[]).length) rows.push({label: m.languages.join(', '), href: null});
  if (m.gender) rows.push({label: (I18N.genderLabels[m.gender]||m.gender), href: '/'+primaryTag+'/'});
  if (m.broadcastHD) rows.push({label: I18N.hdLabel, href: '/'+primaryTag+'/hd/'});
  if (m.broadcastVR) rows.push({label: I18N.vrLabel, href: '/'+primaryTag+'/vr/'});

  for (const [kw, lbl] of Object.entries(I18N.hairLabels)) {
    const t = findTag(m.tags, kw); if (t) { rows.push({label: lbl, href: '/'+t+'/'}); break; }
  }
  for (const [kw, lbl] of Object.entries(I18N.bodyLabels)) {
    const t = findTag(m.tags, kw); if (t) { rows.push({label: lbl, href: '/'+t+'/'}); break; }
  }
  for (const [kw, lbl] of Object.entries(I18N.ethLabels)) {
    const t = findTag(m.tags, kw); if (t) { rows.push({label: lbl, href: '/'+t+'/'}); break; }
  }

  if (rows.length) {
    document.getElementById('infoRows').innerHTML = rows.map(r =>
      `<div class="info-row"><svg width="14" height="14" viewBox="0 0 20 20" fill="var(--muted)" aria-hidden="true"><circle cx="10" cy="10" r="9" opacity=".4"/></svg>${r.href ? `<a class="info-row-link" href="${r.href}">${esc(r.label)}</a>` : `<span>${esc(r.label)}</span>`}</div>`
    ).join('');
    document.getElementById('minfoMeta').style.display = '';
  }

  if (m.goalMessage || m.neededForGoal > 0) {
    const pct = m.neededForGoal > 0 ? Math.min(100, Math.round((m.earnedForGoal||0)/m.neededForGoal*100)) : 0;
    document.getElementById('goalMsg').textContent    = m.goalMessage || 'Goal active';
    document.getElementById('goalBar').style.width   = pct + '%';
    document.getElementById('goalEarned').textContent = (m.earnedForGoal||0) + ' tkn';
    document.getElementById('goalNeeded').textContent = (m.neededForGoal||0) + ' tkn';
    document.getElementById('goalWrap').style.display = '';
  }

  const modelTags = (m.tags||[]).filter(t => t !== name && t !== m.gender);
  if (modelTags.length) {
    document.getElementById('tagList').innerHTML = modelTags.slice(0,30).map(t =>
      `<a class="tag-link" href="/${t}/" title="Browse ${esc(t.split('/').pop())} models">${esc(t.split('/').pop())}</a>`
    ).join('');
    document.getElementById('minfoTags').style.display = '';
  }

  const visitEl = document.getElementById('visitLink');
  if (m.clickUrl) {
    visitEl.href = m.clickUrl;
    visitEl.style.display = '';
    visitEl.childNodes[visitEl.childNodes.length - 1].textContent = `${I18N.visitBtn} ${name}`;
  }

  showPlayerOverlay(isOnline);
  document.getElementById('descTitle').textContent = `${name} ${I18N.descTitle}`;
  // Only build the description client-side if the server didn't already render one
  // (e.g. model wasn't in cache yet at page load). The server-rendered version is
  // correctly localized; this JS fallback is English-only, so never let it clobber
  // a description that's already there.
  const descEl = document.getElementById('descText');
  if (!descEl.innerHTML.trim()) {
    descEl.innerHTML = generateModelDescription(m, name, isOnline);
  }
  document.getElementById('modelDesc').style.display = '';
}

function getPlayerVideo() {
  return document.querySelector('#scPlayerContainer video');
}
function toggleVolume() {
  const video = getPlayerVideo();
  const slider = document.getElementById('volSlider');
  const isMuted = !video || video.muted || +slider.value === 0;
  if (isMuted) {
    const vol = 80;
    if (video) { video.muted = false; video.volume = vol / 100; }
    slider.value = vol;
    slider.style.background = `linear-gradient(to right,var(--primary) ${vol}%,rgba(255,255,255,.35) ${vol}%)`;
    updateVolIcon(false);
  } else {
    if (video) video.muted = true;
    slider.value = 0;
    slider.style.background = `linear-gradient(to right,var(--primary) 0%,rgba(255,255,255,.35) 0%)`;
    updateVolIcon(true);
  }
}
function setVolume(val) {
  const video = getPlayerVideo();
  if (!video) return;
  video.volume = val / 100;
  video.muted  = val == 0;
  updateVolIcon(val == 0);
  const s = document.getElementById('volSlider');
  if (s) s.style.background = `linear-gradient(to right,var(--primary) ${val}%,rgba(255,255,255,.35) ${val}%)`;
}
function updateVolIcon(muted) {
  document.getElementById('volIcon').innerHTML = muted
    ? '<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><line x1="23" y1="9" x2="17" y2="15"/><line x1="17" y1="9" x2="23" y2="15"/>'
    : '<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07M19.07 4.93a10 10 0 0 1 0 14.14"/>';
}

function togglePlayerFs() {
  const wrap  = document.getElementById('playerWrap');
  const video = document.querySelector('#scPlayerContainer video');
  const isFs  = document.fullscreenElement || document.webkitFullscreenElement;

  if (!isFs) {
    // iOS Safari — только video поддерживает fullscreen
    if (video && video.webkitEnterFullscreen) {
      video.webkitEnterFullscreen();
    } else {
      const fn = wrap.requestFullscreen || wrap.webkitRequestFullscreen || wrap.mozRequestFullScreen;
      if (fn) fn.call(wrap);
    }
  } else {
    const fn = document.exitFullscreen || document.webkitExitFullscreen || document.mozCancelFullScreen;
    if (fn) fn.call(document);
  }
}

function updateFsIcon() {
  const icon = document.getElementById('playerFsIcon');
  if (!icon) return;
  const isFs = document.fullscreenElement || document.webkitFullscreenElement;
  icon.innerHTML = isFs
    ? '<path d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3"/>'
    : '<path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/>';
}
document.addEventListener('fullscreenchange', updateFsIcon);
document.addEventListener('webkitfullscreenchange', updateFsIcon);

function showPlayerOverlay(online) {
  const el = document.getElementById('playerOverlay');
  if (!el) return;
  if (online) el.classList.remove('hidden');
  else el.classList.add('hidden');
}

function mountPlayer(m) {
  const container = document.getElementById('scPlayerContainer');
  container.innerHTML = '';
  if (!CFG.userId) { renderOfflinePlayer(m, 'Stream is currently unavailable.'); return; }
  if (typeof StripchatPlayer !== 'undefined') {
    try {
      const player = new StripchatPlayer({ modelName: m.username, userId: CFG.userId, autoplay: 'all', mute: 0, volumeControl: 0, fullscreen: 0, showBadge: 0, strict: 1 });
      player.mount(container).then(app => { playerApp = app; }).catch(() => iframePlayer(m.username));
      return;
    } catch(e) {}
  }
  iframePlayer(m.username);
}

function iframePlayer(name) {
  if (!CFG.userId) return;
  const src = `https://creative.eiscprts.com/widgets/Player?modelName=${encodeURIComponent(name)}&userId=${encodeURIComponent(CFG.userId)}&autoplay=all&mute=0&volumeControl=0&fullscreen=0`;
  document.getElementById('scPlayerContainer').innerHTML =
    `<iframe src="${esc(src)}" title="${esc(name)} live stream" width="100%" height="100%"
      frameborder="0" allow="autoplay; fullscreen; encrypted-media" allowfullscreen></iframe>`;
}

function renderOfflinePlayer(m, msg) {
  const img = m.previewUrlThumbSmall || m.avatarUrl || '';
  document.getElementById('scPlayerContainer').innerHTML = `
    <div class="player-offline">
      ${img ? `<img src="${esc(img)}" alt="${esc(m.username)}" width="90" height="90">` : ''}
      <p style="color:var(--text2);font-size:14px">${esc(m.username||'')}</p>
      <p style="color:var(--muted);font-size:12px;max-width:260px;text-align:center">${esc(msg)}</p>
      ${m.clickUrl ? `<a href="${esc(m.clickUrl)}" target="_blank" rel="noopener noreferrer sponsored"
        style="margin-top:8px;padding:9px 20px;background:var(--primary);color:#fff;border-radius:7px;font-size:13px;font-weight:700">
        ${I18N.watchBtn}</a>` : ''}
    </div>`;
}

function showSimilar(models, currentUsername) {
  const filtered = models.filter(m => m.username !== currentUsername);
  if (!filtered.length) return false;
  similarModels  = filtered;
  similarVisible = 48;
  renderSimilarGrid();
  document.getElementById('similarSection').style.display = '';
  document.getElementById('similarGrid').addEventListener('click', e => {
    const card = e.target.closest('.model-card[data-u]');
    if (!card) return;
    const uname = decodeURIComponent(card.dataset.u);
    const found = similarModels.find(x => x.username === uname);
    if (found) try { sessionStorage.setItem('sc_m_' + uname, JSON.stringify(found)); } catch(ex) {}
  });
  document.getElementById('similarLoadMoreBtn').addEventListener('click', () => {
    const g = document.getElementById('similarGrid');
    const prev = similarVisible;
    similarVisible += 48;
    similarModels.slice(prev, similarVisible).forEach(m => g.insertAdjacentHTML('beforeend', simCard(m)));
    updateSimilarLoadMore();
  });
  return true;
}

function renderSimilarGrid() {
  document.getElementById('similarGrid').innerHTML = similarModels.slice(0, similarVisible).map(m => simCard(m)).join('');
  updateSimilarLoadMore();
}

function updateSimilarLoadMore() {
  document.getElementById('similarLoadMoreWrap').style.display = similarModels.length > similarVisible ? '' : 'none';
}

async function loadSimilar(currentModel) {
  const primaryTag = (currentModel.tags||[]).find(t => ['girls','men','trans','couples'].includes(t)) || 'girls';
  try {
    const raw = localStorage.getItem('sc_v1_' + primaryTag);
    if (raw) {
      const { ts, data } = JSON.parse(raw);
      if (data && Date.now() - ts < 30 * 60 * 1000) {
        if (showSimilar(data, currentModel.username)) return;
      }
    }
  } catch(e) {}
  if (!CFG.userId) return;
  try {
    const r = await fetch(`/api.php?_ep=ext&userId=${CFG.userId}&tag=${encodeURIComponent(primaryTag)}`, { headers: { 'Authorization': 'Bearer ' + CFG.apiKey } });
    if (!r.ok) return;
    const d = await r.json();
    showSimilar(Array.isArray(d) ? d : (d.models||d.data||[]), currentModel.username);
  } catch(e) {}
}

function simCard(m) {
  const img = m.snapshotUrl || m.popularSnapshotUrl || m.previewUrlThumbSmall || '';
  return `<a class="model-card" href="/model/${enc(m.username)}" data-u="${enc(m.username)}" aria-label="${esc(m.username)} live webcam">
    <div class="card-thumb">
      <img class="card-img" src="${esc(img)}" alt="${esc(m.username)} cam" loading="lazy" decoding="async" width="320" height="240"
        onerror="this.src='data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 80 80%22%3E%3Crect width=%2280%22 height=%2280%22 fill=%22%23181820%22/%3E%3C/svg%3E'">
      <div class="card-overlay" aria-hidden="true"></div>
      <div class="play-icon" aria-hidden="true"><svg viewBox="0 0 48 48" fill="none"><circle cx="24" cy="24" r="24" fill="rgba(0,0,0,.55)"/><polygon points="19,15 36,24 19,33" fill="white"/></svg></div>
      <div class="card-badges" aria-hidden="true">
        ${m.broadcastHD?'<span class="badge b-hd">HD</span>':''}
        ${m.broadcastVR?'<span class="badge b-vr">VR</span>':''}
      </div>
      <span class="card-online" aria-hidden="true"></span>
      ${(m.viewersCount||0)>0?`<span class="card-viewers">${fmtNum(m.viewersCount)}</span>`:''}
    </div>
    <div class="card-info">
      <div class="card-name">${esc(m.username)}</div>
      <div class="card-meta">
        ${m.modelsCountry?`<span>${flag(m.modelsCountry)}</span>`:''}
        ${(m.favoritedCount||0)>0?`<span>♥ ${fmtNum(m.favoritedCount)}</span>`:''}
      </div>
    </div>
  </a>`;
}

async function fetchModelJS(name) {
  const r = await fetch(`/model_data.php?u=${encodeURIComponent(name)}`);
  if (!r.ok) return null;
  return await r.json();
}

function enc(s) { return encodeURIComponent(s||'') }
function esc(s) {
  s = String(s||'');
  return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}
function fmtNum(n) {
  n = n||0;
  if(n>=1e6) return (n/1e6).toFixed(1)+'M';
  if(n>=1e3) return (n/1e3).toFixed(1)+'K';
  return String(n);
}
function flag(code) {
  if(!code||code.length!==2) return '';
  return [...code.toUpperCase()].map(c=>String.fromCodePoint(0x1F1E6+c.charCodeAt(0)-65)).join('');
}
function toast(msg, type) {
  const d = document.createElement('div');
  d.className = 'toast '+(type||'');
  d.textContent = msg;
  document.getElementById('toasts').appendChild(d);
  setTimeout(() => d.remove(), 4500);
}

async function init() {
  document.getElementById('yr').textContent = new Date().getFullYear();

  if (!username) {
    document.title = 'Model Not Found';
    document.getElementById('scPlayerContainer').innerHTML =
      `<div class="player-offline"><p style="color:var(--muted)">No model specified.</p><a href="/" style="color:var(--primary)">Browse Models →</a></div>`;
    return;
  }

  let model = null;
  try {
    const cached = sessionStorage.getItem('sc_m_' + username);
    if (cached) model = JSON.parse(cached);
  } catch(e) {}

  if (model) {
    renderModel(model);
    mountPlayer(model);
    loadSimilar(model);
  } else {
    document.getElementById('scPlayerContainer').innerHTML =
      `<div style="width:100%;height:100%;background:#000;display:flex;align-items:center;justify-content:center">
        <svg width="48" height="48" viewBox="0 0 48 48" fill="none"><circle cx="24" cy="24" r="23" stroke="var(--border2)" stroke-width="2"/><path d="M24 24m-6 0a6 6 0 1 0 12 0 6 6 0 1 0-12 0" fill="none" stroke="var(--muted)" stroke-width="2"/></svg>
      </div>`;
  }

  if (CFG.userId) {
    try {
      const fresh = await fetchModelJS(username);
      if (fresh) {
        model = fresh;
        renderModel(model);
        if (!playerApp) mountPlayer(model);
        try { sessionStorage.setItem('sc_m_' + username, JSON.stringify(model)); } catch(e) {}
      } else if (!model) {
        const offlineModel = { username, status:'offline', tags:[], viewersCount:0, favoritedCount:0 };
        renderModel(offlineModel);
        renderOfflinePlayer(offlineModel, `${username} is currently offline. Check back later or visit their profile on Stripchat.`);
      }
    } catch(e) {
      if (!model) {
        toast('Could not load model data.', 'err');
        renderOfflinePlayer({ username, clickUrl:'' }, 'Could not load stream. Please try again.');
      }
    }
  } else if (!model) {
    renderOfflinePlayer({ username, clickUrl:'' }, 'Please enter your User ID in Settings to watch live streams.');
  }

  if (model) loadSimilar(model);
}

init();
document.addEventListener('click', e => {
  if (!e.target.closest('.lang-switcher'))
    document.querySelectorAll('.lang-menu.open').forEach(m => m.classList.remove('open'));
});

(function(){
  const btn = document.createElement('button');
  btn.className = 'scroll-top';
  btn.setAttribute('aria-label', 'Scroll to top');
  btn.innerHTML = '<svg width="20" height="20" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path d="M18 15l-6-6-6 6"/></svg>';
  btn.onclick = () => window.scrollTo({ top: 0, behavior: 'smooth' });
  document.body.appendChild(btn);
  window.addEventListener('scroll', () => {
    btn.classList.toggle('visible', window.scrollY > 400);
  }, { passive: true });
})();
</script>

</body>
</html>
