openvibers/openvibe.live Commits
Lite mode for phones / weak GPUs: no shader, canvases, blur filters, blend modes, backdrop-filter or animated shadows (flicker on low-end Android); CSS safety net ≤ 820px
Changes
@@@ -37,3 +37,7 @@.hfx-shimmer, .hfx-glow, #page-home .hero-cta .btn-primary, #page-home .section-header h2 .live-dot { animation: none !important; }.hfx-reveal { opacity: 1; transform: none; transition: none; }}++++.rs-lite .hfx-sparks, .rs-lite .hfx-spot { display: none; }++.rs-lite .hfx-shimmer, .rs-lite .hfx-glow, .rs-lite #page-home .hero-cta .btn-primary, .rs-lite #page-home .section-header h2 .live-dot { animation: none !important; text-shadow: none; }++.rs-lite #page-home .stream-card { transition: none; }@@@ -210,3 +210,11 @@.chat-msg-hoverable:hover .chat-translate-btn { display: inline-flex; }.chat-msg-hoverable .chat-translate-btn:hover { color: var(--accent-light); border-color: var(--accent); }.chat-translation[hidden] { display: none; }++++/* Lite mode (phones / weak GPUs): no blur, no filters, no shadow animations */++.rs-lite .star-card-border::before { animation: none; background: linear-gradient(135deg, #fbbf24, #f472b6, var(--accent)); inset: 0; }++.rs-lite .star-avatar-glow, .rs-lite .star-petals { display: none; }++.rs-lite .star-avatar-ring { animation: none; }++.rs-lite .star-btn-watch::after, .rs-lite .star-kicker i, .rs-lite .star-live-pill { animation: none; }++.rs-lite .star-btn-watch { box-shadow: none; }++.rs-lite .ch-captions-dot, .rs-lite .chat-translate-hint { animation: none; box-shadow: none; }@@@ -795,3 +795,36 @@ body.rs-ticker-on .rs-modal, body.rs-ticker-on .rs-takeover { --rs-ticker-h: 0px.rs-board-row { opacity: 1; transform: none; } .rs-yes { transform: none; }.rs-money { background: none; -webkit-text-fill-color: var(--rs-gold); color: var(--rs-gold); }}++++/* ═══════════════════════════════════════════════════════════════++ LITE MODE — html.rs-lite (phones / weak GPUs / Data Saver) and any screen ≤ 820px.++ No blur filters, blend modes, backdrop-filter, canvases or animated shadows: those are what++ make low-end Android Chrome flicker. Transform/opacity animations stay.++ ═══════════════════════════════════════════════════════════════ */++.rs-lite .rs-tk-aurora, .rs-lite .rs-tk-scan, .rs-lite .rs-tk-gl, .rs-lite .rs-tk-canvas, .rs-lite .rs-tk-burst, .rs-lite .rs-card-foil, .rs-lite .rs-card-glare, .rs-lite .rs-bills, .rs-lite .rs-vip-bubbles, .rs-lite .rs-ticker::before, .rs-lite .rs-btn-gold::after, .rs-lite .rs-glitch::before, .rs-lite .rs-glitch::after { display: none !important; }++.rs-lite .rs-takeover { background: radial-gradient(120% 80% at 50% 0%, #2e1065 0%, #1e1b4b 40%, #06060a 80%); }++.rs-lite .rs-tk-grid { animation: none; transform: perspective(600px) rotateX(64deg); opacity: 0.5; }++.rs-lite .rs-tk-content, .rs-lite .rs-tk-amount, .rs-lite .rs-tk-amount .rs-money { transform: none !important; animation: none !important; }++.rs-lite .rs-money { animation: none; filter: none; background: none; -webkit-text-fill-color: var(--rs-gold); color: var(--rs-gold); text-shadow: 0 2px 0 rgba(0,0,0,0.35); }++.rs-lite .rs-tk-amount .rs-money { filter: none; }++.rs-lite .rs-ticker { animation: rsTickerDrop 0.5s ease both; background: linear-gradient(90deg, #4c1d95, #1d4ed8, #7c3aed); }++.rs-lite .rs-ticker-item .rs-ticker-cta, .rs-lite .rs-tk-eyebrow, .rs-lite .rs-flow-node.rs-to, .rs-lite .rs-btn-gold, .rs-lite .rs-stream-alert, .rs-lite .rs-sa-siren, .rs-lite .rs-flow2-hub, .rs-lite .rs-chip, .rs-lite .rs-flow2-node, .rs-lite .rs-odo b, .rs-lite .rs-yes, .rs-lite .rs-star-avatar-glow { animation: none !important; box-shadow: none !important; }++.rs-lite .rs-chip, .rs-lite .rs-flow-node, .rs-lite .rs-flow2-node, .rs-lite .rs-btn-ghost, .rs-lite .rs-tk-close, .rs-lite .rs-modal, .rs-lite .rs-tk-tour { backdrop-filter: none !important; -webkit-backdrop-filter: none !important; }++.rs-lite .rs-modal { background: rgba(4,4,10,0.9); }++.rs-lite .rs-card-border::before { animation: none; background: linear-gradient(135deg, var(--rs-gold), var(--accent), var(--rs-blue)); inset: 0; }++.rs-lite .rs-hero-card { transform: none !important; transition: none; box-shadow: 0 10px 30px rgba(0,0,0,0.4); }++.rs-lite .rs-coin { animation: none; transform: rotateY(0); } .rs-lite .rs-coin-face { box-shadow: inset 0 0 0 5px rgba(180,83,9,0.55); }++.rs-lite .rs-vip-star { filter: none; } .rs-lite .rs-vip { box-shadow: 0 10px 30px rgba(0,0,0,0.4); }++.rs-lite .rs-vip-sparkle { animation: none; opacity: 0.8; }++.rs-lite .rs-stream-alert { box-shadow: 0 0 0 2px rgba(251,191,36,0.6); }++.rs-lite .rs-sa-stripe { animation: none; }++.rs-lite .rs-packet { box-shadow: none; }++.rs-lite .rs-board::before { animation: none; }++.rs-lite .rs-tk-title { text-shadow: none; }++@media (max-width: 820px) {++ /* CSS-only safety net for the most expensive effects even before JS decides on lite mode. */++ .rs-tk-aurora { filter: none; opacity: 0.55; } .rs-tk-aurora span { mix-blend-mode: normal; animation: none; }++ .rs-tk-scan, .rs-card-foil { display: none; }++ .rs-tk-amount .rs-money { filter: drop-shadow(0 4px 0 rgba(120,53,15,0.6)); }++ .rs-stream-alert, .rs-sa-siren, .rs-btn-gold { animation: none; }++}@@@ -67,9 +67,9 @@<link rel="stylesheet" href="/css/broadcast.css?v=11"><link rel="stylesheet" href="/css/cosmetics.css?v=2"><link rel="stylesheet" href="/css/messenger.css?v=1">-- <link rel="stylesheet" href="/css/rs-promo.css?v=8">-- <link rel="stylesheet" href="/css/home-fx.css?v=2">-- <link rel="stylesheet" href="/css/i18n-star.css?v=2">++ <link rel="stylesheet" href="/css/rs-promo.css?v=9">++ <link rel="stylesheet" href="/css/home-fx.css?v=3">++ <link rel="stylesheet" href="/css/i18n-star.css?v=3"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"><!-- JSON-LD Structured Data -->@@@ -5126,8 +5126,8 @@ curl -s https://openvibe.media/live/@Goosely/chat-insight.json | jq ".chat_insig<script src="/js/pastes.js?v=15" defer></script><script src="/js/draggable-fab.js?v=2" defer></script><script src="/js/messenger.js?v=2" defer></script>--<script src="/js/rs-promo.js?v=11" defer></script>--<script src="/js/home-fx.js?v=1" defer></script>++<script src="/js/rs-promo.js?v=12" defer></script>++<script src="/js/home-fx.js?v=2" defer></script><!-- ── OpenVibe shared libs (notifications, account sync, themes) ── --><script src="/shared/theme-loader.js" defer></script>@@@ -16,6 +16,7 @@const REDUCED = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;const COARSE = window.matchMedia && window.matchMedia('(pointer: coarse)').matches;if (REDUCED) return;++ if (COARSE || window.innerWidth <= 820 || (navigator.deviceMemory && navigator.deviceMemory <= 4)) { document.documentElement.classList.add('rs-lite'); return; }const onHome = () => { const p = document.getElementById('page-home'); return !!(p && p.classList.contains('active')); };@@@ -21,11 +21,24 @@(function () {'use strict';-- const PROMO_VERSION = 'rs-desperate-v7'; // bump to re-show the takeover to everyone++ const PROMO_VERSION = 'rs-desperate-v8'; // bump to re-show the takeover to everyoneconst SEEN_KEY = 'ov_rs_promo_seen';const TICKER_KEY = 'ov_rs_ticker_hidden'; // sessionStorage — comes back next visitconst REDUCED = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;const COARSE = window.matchMedia && window.matchMedia('(pointer: coarse)').matches;++ // Lite mode: phones and weak devices get the copy and the cheap transform animations, but no++ // canvases, shaders, blur filters, blend modes or animated shadows (they flicker and stutter on++ // low-end Android GPUs). Also honours Data Saver.++ const LITE = (() => {++ try {++ const narrow = window.innerWidth <= 820;++ const weak = (navigator.hardwareConcurrency && navigator.hardwareConcurrency <= 4) || (navigator.deviceMemory && navigator.deviceMemory <= 4);++ const saver = navigator.connection && navigator.connection.saveData;++ const ua = /Android|iPhone|iPad|Mobile/i.test(navigator.userAgent);++ return !!(saver || (COARSE && (narrow || ua)) || (narrow && weak) || (ua && weak));++ } catch { return COARSE; }++ })();++ if (LITE) document.documentElement.classList.add('rs-lite');const cfg = { enabled: true, amount: 50, amountMin: 25, referral: 10, vip: null, totals: null, github: 'https://github.com/OpenVibers/OpenVibe.Live', owner: 'admin', discord: 'https://discord.gg/M6MuRUaeJj' };const STREAM_ALERT_KEY = 'ov_rs_stream_alert_min'; // sessionStorage — collapsed (never hidden) state@@@ -205,11 +218,15 @@document.body.style.overflow = 'hidden';store.set(SEEN_KEY, PROMO_VERSION);if (!REDUCED) {-- rainStop = startMoneyRain(takeoverEl.querySelector('.rs-tk-canvas'));-- glStop = startShader(takeoverEl.querySelector('.rs-tk-gl'));-- attachParallax(takeoverEl);++ if (!LITE) {++ rainStop = startMoneyRain(takeoverEl.querySelector('.rs-tk-canvas'));++ glStop = startShader(takeoverEl.querySelector('.rs-tk-gl'));++ attachParallax(takeoverEl);++ } else {++ takeoverEl.querySelectorAll('.rs-tk-canvas, .rs-tk-gl, .rs-tk-burst, .rs-tk-scan').forEach(c => c.remove());++ }typewriter(takeoverEl.querySelector('.rs-tk-type-text'), PLEAS);-- countUp(takeoverEl.querySelector('[data-countup]'), cfg.amount, 1500, 500, () => { shake(takeoverEl); coinBurst(takeoverEl.querySelector('.rs-tk-burst')); });++ countUp(takeoverEl.querySelector('[data-countup]'), cfg.amount, 1500, 500, () => { if (!LITE) { shake(takeoverEl); coinBurst(takeoverEl.querySelector('.rs-tk-burst')); } });takeoverEl.querySelectorAll('[data-odo]').forEach(el => odometer(el, 900));} else {takeoverEl.querySelector('[data-countup]').textContent = money();@@@ -639,7 +656,7 @@document.body.appendChild(modalEl);if (loggedIn) loadCashout().catch(() => { const c = document.getElementById('rs-cashout'); if (c) c.innerHTML = '<p class="rs-modal-fine">Could not load your payouts right now.</p>'; });document.body.style.overflow = 'hidden';-- if (!REDUCED) confettiBurst();++ if (!REDUCED && !LITE) confettiBurst();setTimeout(() => modalEl?.querySelector('.rs-modal-actions .rs-btn')?.focus({ preventScroll: true }), 400);}const _spinner = (t) => `<div class="rs-cashout-loading"><i class="fa-solid fa-circle-notch fa-spin"></i> ${esc(t)}</div>`;@@@ -675,7 +692,7 @@const body = Object.fromEntries(new FormData(f).entries());body.method = modalEl?.querySelector('.rs-toggle')?.dataset.method || 'zelle';btn.disabled = true; btn.innerHTML = '<i class="fa-solid fa-circle-notch fa-spin"></i> Filing…';-- try { const r = await api('/promo/claims', { method: 'POST', body }); say(`Claim #${r.claim.id} filed — you'll get a notification when it's approved`, 'success'); if (!REDUCED) confettiBurst(); await loadCashout(); }++ try { const r = await api('/promo/claims', { method: 'POST', body }); say(`Claim #${r.claim.id} filed — you'll get a notification when it's approved`, 'success'); if (!REDUCED && !LITE) confettiBurst(); await loadCashout(); }catch (err) { say(err?.message || 'Could not file the claim', 'error'); btn.disabled = false; btn.innerHTML = `<i class="fa-solid fa-sack-dollar"></i> File claim for ${money()}`; }});box.querySelectorAll('.rs-payout-form').forEach(f => f.addEventListener('submit', async (e) => {
@@ -37,3 +37,7 @@
@@ -37,3 +37,7 @@
.hfx-shimmer, .hfx-glow, #page-home .hero-cta .btn-primary, #page-home .section-header h2 .live-dot { animation: none !important; }
.hfx-shimmer, .hfx-glow, #page-home .hero-cta .btn-primary, #page-home .section-header h2 .live-dot { animation: none !important; }
.hfx-reveal { opacity: 1; transform: none; transition: none; }
.hfx-reveal { opacity: 1; transform: none; transition: none; }
}
}
+
+.rs-lite .hfx-sparks, .rs-lite .hfx-spot { display: none; }
+.rs-lite .hfx-shimmer, .rs-lite .hfx-glow, .rs-lite #page-home .hero-cta .btn-primary, .rs-lite #page-home .section-header h2 .live-dot { animation: none !important; text-shadow: none; }
+.rs-lite #page-home .stream-card { transition: none; }
@@ -210,3 +210,11 @@
@@ -210,3 +210,11 @@
.chat-msg-hoverable:hover .chat-translate-btn { display: inline-flex; }
.chat-msg-hoverable:hover .chat-translate-btn { display: inline-flex; }
.chat-msg-hoverable .chat-translate-btn:hover { color: var(--accent-light); border-color: var(--accent); }
.chat-msg-hoverable .chat-translate-btn:hover { color: var(--accent-light); border-color: var(--accent); }
.chat-translation[hidden] { display: none; }
.chat-translation[hidden] { display: none; }
+
+/* Lite mode (phones / weak GPUs): no blur, no filters, no shadow animations */
+.rs-lite .star-card-border::before { animation: none; background: linear-gradient(135deg, #fbbf24, #f472b6, var(--accent)); inset: 0; }
+.rs-lite .star-avatar-glow, .rs-lite .star-petals { display: none; }
+.rs-lite .star-avatar-ring { animation: none; }
+.rs-lite .star-btn-watch::after, .rs-lite .star-kicker i, .rs-lite .star-live-pill { animation: none; }
+.rs-lite .star-btn-watch { box-shadow: none; }
+.rs-lite .ch-captions-dot, .rs-lite .chat-translate-hint { animation: none; box-shadow: none; }
@@ -795,3 +795,36 @@ body.rs-ticker-on .rs-modal, body.rs-ticker-on .rs-takeover { --rs-ticker-h: 0px
@@ -795,3 +795,36 @@ body.rs-ticker-on .rs-modal, body.rs-ticker-on .rs-takeover { --rs-ticker-h: 0px
.rs-board-row { opacity: 1; transform: none; } .rs-yes { transform: none; }
.rs-board-row { opacity: 1; transform: none; } .rs-yes { transform: none; }
.rs-money { background: none; -webkit-text-fill-color: var(--rs-gold); color: var(--rs-gold); }
.rs-money { background: none; -webkit-text-fill-color: var(--rs-gold); color: var(--rs-gold); }
}
}
+
+/* ═══════════════════════════════════════════════════════════════
+ LITE MODE — html.rs-lite (phones / weak GPUs / Data Saver) and any screen ≤ 820px.
+ No blur filters, blend modes, backdrop-filter, canvases or animated shadows: those are what
+ make low-end Android Chrome flicker. Transform/opacity animations stay.
+ ═══════════════════════════════════════════════════════════════ */
+.rs-lite .rs-tk-aurora, .rs-lite .rs-tk-scan, .rs-lite .rs-tk-gl, .rs-lite .rs-tk-canvas, .rs-lite .rs-tk-burst, .rs-lite .rs-card-foil, .rs-lite .rs-card-glare, .rs-lite .rs-bills, .rs-lite .rs-vip-bubbles, .rs-lite .rs-ticker::before, .rs-lite .rs-btn-gold::after, .rs-lite .rs-glitch::before, .rs-lite .rs-glitch::after { display: none !important; }
+.rs-lite .rs-takeover { background: radial-gradient(120% 80% at 50% 0%, #2e1065 0%, #1e1b4b 40%, #06060a 80%); }
+.rs-lite .rs-tk-grid { animation: none; transform: perspective(600px) rotateX(64deg); opacity: 0.5; }
+.rs-lite .rs-tk-content, .rs-lite .rs-tk-amount, .rs-lite .rs-tk-amount .rs-money { transform: none !important; animation: none !important; }
+.rs-lite .rs-money { animation: none; filter: none; background: none; -webkit-text-fill-color: var(--rs-gold); color: var(--rs-gold); text-shadow: 0 2px 0 rgba(0,0,0,0.35); }
+.rs-lite .rs-tk-amount .rs-money { filter: none; }
+.rs-lite .rs-ticker { animation: rsTickerDrop 0.5s ease both; background: linear-gradient(90deg, #4c1d95, #1d4ed8, #7c3aed); }
+.rs-lite .rs-ticker-item .rs-ticker-cta, .rs-lite .rs-tk-eyebrow, .rs-lite .rs-flow-node.rs-to, .rs-lite .rs-btn-gold, .rs-lite .rs-stream-alert, .rs-lite .rs-sa-siren, .rs-lite .rs-flow2-hub, .rs-lite .rs-chip, .rs-lite .rs-flow2-node, .rs-lite .rs-odo b, .rs-lite .rs-yes, .rs-lite .rs-star-avatar-glow { animation: none !important; box-shadow: none !important; }
+.rs-lite .rs-chip, .rs-lite .rs-flow-node, .rs-lite .rs-flow2-node, .rs-lite .rs-btn-ghost, .rs-lite .rs-tk-close, .rs-lite .rs-modal, .rs-lite .rs-tk-tour { backdrop-filter: none !important; -webkit-backdrop-filter: none !important; }
+.rs-lite .rs-modal { background: rgba(4,4,10,0.9); }
+.rs-lite .rs-card-border::before { animation: none; background: linear-gradient(135deg, var(--rs-gold), var(--accent), var(--rs-blue)); inset: 0; }
+.rs-lite .rs-hero-card { transform: none !important; transition: none; box-shadow: 0 10px 30px rgba(0,0,0,0.4); }
+.rs-lite .rs-coin { animation: none; transform: rotateY(0); } .rs-lite .rs-coin-face { box-shadow: inset 0 0 0 5px rgba(180,83,9,0.55); }
+.rs-lite .rs-vip-star { filter: none; } .rs-lite .rs-vip { box-shadow: 0 10px 30px rgba(0,0,0,0.4); }
+.rs-lite .rs-vip-sparkle { animation: none; opacity: 0.8; }
+.rs-lite .rs-stream-alert { box-shadow: 0 0 0 2px rgba(251,191,36,0.6); }
+.rs-lite .rs-sa-stripe { animation: none; }
+.rs-lite .rs-packet { box-shadow: none; }
+.rs-lite .rs-board::before { animation: none; }
+.rs-lite .rs-tk-title { text-shadow: none; }
+@media (max-width: 820px) {
+ /* CSS-only safety net for the most expensive effects even before JS decides on lite mode. */
+ .rs-tk-aurora { filter: none; opacity: 0.55; } .rs-tk-aurora span { mix-blend-mode: normal; animation: none; }
+ .rs-tk-scan, .rs-card-foil { display: none; }
+ .rs-tk-amount .rs-money { filter: drop-shadow(0 4px 0 rgba(120,53,15,0.6)); }
+ .rs-stream-alert, .rs-sa-siren, .rs-btn-gold { animation: none; }
+}
@@ -67,9 +67,9 @@
@@ -67,9 +67,9 @@
<link rel="stylesheet" href="/css/broadcast.css?v=11">
<link rel="stylesheet" href="/css/broadcast.css?v=11">
<link rel="stylesheet" href="/css/cosmetics.css?v=2">
<link rel="stylesheet" href="/css/cosmetics.css?v=2">
<link rel="stylesheet" href="/css/messenger.css?v=1">
<link rel="stylesheet" href="/css/messenger.css?v=1">
- <link rel="stylesheet" href="/css/rs-promo.css?v=8">
- <link rel="stylesheet" href="/css/home-fx.css?v=2">
- <link rel="stylesheet" href="/css/i18n-star.css?v=2">
+ <link rel="stylesheet" href="/css/rs-promo.css?v=9">
+ <link rel="stylesheet" href="/css/home-fx.css?v=3">
+ <link rel="stylesheet" href="/css/i18n-star.css?v=3">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<!-- JSON-LD Structured Data -->
<!-- JSON-LD Structured Data -->
@@ -5126,8 +5126,8 @@ curl -s https://openvibe.media/live/@Goosely/chat-insight.json | jq ".chat_insig
@@ -5126,8 +5126,8 @@ curl -s https://openvibe.media/live/@Goosely/chat-insight.json | jq ".chat_insig
<script src="/js/pastes.js?v=15" defer></script>
<script src="/js/pastes.js?v=15" defer></script>
<script src="/js/draggable-fab.js?v=2" defer></script>
<script src="/js/draggable-fab.js?v=2" defer></script>
<script src="/js/messenger.js?v=2" defer></script>
<script src="/js/messenger.js?v=2" defer></script>
-<script src="/js/rs-promo.js?v=11" defer></script>
-<script src="/js/home-fx.js?v=1" defer></script>
+<script src="/js/rs-promo.js?v=12" defer></script>
+<script src="/js/home-fx.js?v=2" defer></script>
<!-- ── OpenVibe shared libs (notifications, account sync, themes) ── -->
<!-- ── OpenVibe shared libs (notifications, account sync, themes) ── -->
<script src="/shared/theme-loader.js" defer></script>
<script src="/shared/theme-loader.js" defer></script>
@@ -16,6 +16,7 @@
@@ -16,6 +16,7 @@
const REDUCED = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
const REDUCED = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
const COARSE = window.matchMedia && window.matchMedia('(pointer: coarse)').matches;
const COARSE = window.matchMedia && window.matchMedia('(pointer: coarse)').matches;
if (REDUCED) return;
if (REDUCED) return;
+ if (COARSE || window.innerWidth <= 820 || (navigator.deviceMemory && navigator.deviceMemory <= 4)) { document.documentElement.classList.add('rs-lite'); return; }
const onHome = () => { const p = document.getElementById('page-home'); return !!(p && p.classList.contains('active')); };
const onHome = () => { const p = document.getElementById('page-home'); return !!(p && p.classList.contains('active')); };
@@ -21,11 +21,24 @@
@@ -21,11 +21,24 @@
(function () {
(function () {
'use strict';
'use strict';
- const PROMO_VERSION = 'rs-desperate-v7'; // bump to re-show the takeover to everyone
+ const PROMO_VERSION = 'rs-desperate-v8'; // bump to re-show the takeover to everyone
const SEEN_KEY = 'ov_rs_promo_seen';
const SEEN_KEY = 'ov_rs_promo_seen';
const TICKER_KEY = 'ov_rs_ticker_hidden'; // sessionStorage — comes back next visit
const TICKER_KEY = 'ov_rs_ticker_hidden'; // sessionStorage — comes back next visit
const REDUCED = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
const REDUCED = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
const COARSE = window.matchMedia && window.matchMedia('(pointer: coarse)').matches;
const COARSE = window.matchMedia && window.matchMedia('(pointer: coarse)').matches;
+ // Lite mode: phones and weak devices get the copy and the cheap transform animations, but no
+ // canvases, shaders, blur filters, blend modes or animated shadows (they flicker and stutter on
+ // low-end Android GPUs). Also honours Data Saver.
+ const LITE = (() => {
+ try {
+ const narrow = window.innerWidth <= 820;
+ const weak = (navigator.hardwareConcurrency && navigator.hardwareConcurrency <= 4) || (navigator.deviceMemory && navigator.deviceMemory <= 4);
+ const saver = navigator.connection && navigator.connection.saveData;
+ const ua = /Android|iPhone|iPad|Mobile/i.test(navigator.userAgent);
+ return !!(saver || (COARSE && (narrow || ua)) || (narrow && weak) || (ua && weak));
+ } catch { return COARSE; }
+ })();
+ if (LITE) document.documentElement.classList.add('rs-lite');
const cfg = { enabled: true, amount: 50, amountMin: 25, referral: 10, vip: null, totals: null, github: 'https://github.com/OpenVibers/OpenVibe.Live', owner: 'admin', discord: 'https://discord.gg/M6MuRUaeJj' };
const cfg = { enabled: true, amount: 50, amountMin: 25, referral: 10, vip: null, totals: null, github: 'https://github.com/OpenVibers/OpenVibe.Live', owner: 'admin', discord: 'https://discord.gg/M6MuRUaeJj' };
const STREAM_ALERT_KEY = 'ov_rs_stream_alert_min'; // sessionStorage — collapsed (never hidden) state
const STREAM_ALERT_KEY = 'ov_rs_stream_alert_min'; // sessionStorage — collapsed (never hidden) state
@@ -205,11 +218,15 @@
@@ -205,11 +218,15 @@
document.body.style.overflow = 'hidden';
document.body.style.overflow = 'hidden';
store.set(SEEN_KEY, PROMO_VERSION);
store.set(SEEN_KEY, PROMO_VERSION);
if (!REDUCED) {
if (!REDUCED) {
- rainStop = startMoneyRain(takeoverEl.querySelector('.rs-tk-canvas'));
- glStop = startShader(takeoverEl.querySelector('.rs-tk-gl'));
- attachParallax(takeoverEl);
+ if (!LITE) {
+ rainStop = startMoneyRain(takeoverEl.querySelector('.rs-tk-canvas'));
+ glStop = startShader(takeoverEl.querySelector('.rs-tk-gl'));
+ attachParallax(takeoverEl);
+ } else {
+ takeoverEl.querySelectorAll('.rs-tk-canvas, .rs-tk-gl, .rs-tk-burst, .rs-tk-scan').forEach(c => c.remove());
+ }
typewriter(takeoverEl.querySelector('.rs-tk-type-text'), PLEAS);
typewriter(takeoverEl.querySelector('.rs-tk-type-text'), PLEAS);
- countUp(takeoverEl.querySelector('[data-countup]'), cfg.amount, 1500, 500, () => { shake(takeoverEl); coinBurst(takeoverEl.querySelector('.rs-tk-burst')); });
+ countUp(takeoverEl.querySelector('[data-countup]'), cfg.amount, 1500, 500, () => { if (!LITE) { shake(takeoverEl); coinBurst(takeoverEl.querySelector('.rs-tk-burst')); } });
takeoverEl.querySelectorAll('[data-odo]').forEach(el => odometer(el, 900));
takeoverEl.querySelectorAll('[data-odo]').forEach(el => odometer(el, 900));
} else {
} else {
takeoverEl.querySelector('[data-countup]').textContent = money();
takeoverEl.querySelector('[data-countup]').textContent = money();
@@ -639,7 +656,7 @@
@@ -639,7 +656,7 @@
document.body.appendChild(modalEl);
document.body.appendChild(modalEl);
if (loggedIn) loadCashout().catch(() => { const c = document.getElementById('rs-cashout'); if (c) c.innerHTML = '<p class="rs-modal-fine">Could not load your payouts right now.</p>'; });
if (loggedIn) loadCashout().catch(() => { const c = document.getElementById('rs-cashout'); if (c) c.innerHTML = '<p class="rs-modal-fine">Could not load your payouts right now.</p>'; });
document.body.style.overflow = 'hidden';
document.body.style.overflow = 'hidden';
- if (!REDUCED) confettiBurst();
+ if (!REDUCED && !LITE) confettiBurst();
setTimeout(() => modalEl?.querySelector('.rs-modal-actions .rs-btn')?.focus({ preventScroll: true }), 400);
setTimeout(() => modalEl?.querySelector('.rs-modal-actions .rs-btn')?.focus({ preventScroll: true }), 400);
}
}
const _spinner = (t) => `<div class="rs-cashout-loading"><i class="fa-solid fa-circle-notch fa-spin"></i> ${esc(t)}</div>`;
const _spinner = (t) => `<div class="rs-cashout-loading"><i class="fa-solid fa-circle-notch fa-spin"></i> ${esc(t)}</div>`;
@@ -675,7 +692,7 @@
@@ -675,7 +692,7 @@
const body = Object.fromEntries(new FormData(f).entries());
const body = Object.fromEntries(new FormData(f).entries());
body.method = modalEl?.querySelector('.rs-toggle')?.dataset.method || 'zelle';
body.method = modalEl?.querySelector('.rs-toggle')?.dataset.method || 'zelle';
btn.disabled = true; btn.innerHTML = '<i class="fa-solid fa-circle-notch fa-spin"></i> Filing…';
btn.disabled = true; btn.innerHTML = '<i class="fa-solid fa-circle-notch fa-spin"></i> Filing…';
- try { const r = await api('/promo/claims', { method: 'POST', body }); say(`Claim #${r.claim.id} filed — you'll get a notification when it's approved`, 'success'); if (!REDUCED) confettiBurst(); await loadCashout(); }
+ try { const r = await api('/promo/claims', { method: 'POST', body }); say(`Claim #${r.claim.id} filed — you'll get a notification when it's approved`, 'success'); if (!REDUCED && !LITE) confettiBurst(); await loadCashout(); }
catch (err) { say(err?.message || 'Could not file the claim', 'error'); btn.disabled = false; btn.innerHTML = `<i class="fa-solid fa-sack-dollar"></i> File claim for ${money()}`; }
catch (err) { say(err?.message || 'Could not file the claim', 'error'); btn.disabled = false; btn.innerHTML = `<i class="fa-solid fa-sack-dollar"></i> File claim for ${money()}`; }
});
});
box.querySelectorAll('.rs-payout-form').forEach(f => f.addEventListener('submit', async (e) => {
box.querySelectorAll('.rs-payout-form').forEach(f => f.addEventListener('submit', async (e) => {