mirror of
https://gitlab.com/vyla-entertainment/home-frontend.git
synced 2026-11-13 12:07:20 -05:00
updated UI
This commit is contained in:
24
index.html
24
index.html
@@ -84,8 +84,11 @@
|
||||
|
||||
<link rel="apple-touch-icon" href="favicon.ico">
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;900&display=swap">
|
||||
|
||||
<!-- Critical CSS - loaded immediately -->
|
||||
<link rel="stylesheet" href="src/styles/base/tokens.css">
|
||||
<link rel="stylesheet" href="src/styles/base/reset.css">
|
||||
<link rel="stylesheet" href="src/styles/base/typography.css">
|
||||
@@ -96,6 +99,7 @@
|
||||
<link rel="stylesheet" href="src/styles/sections/hero-atmosphere.css">
|
||||
<link rel="stylesheet" href="src/styles/sections/hero-content.css">
|
||||
|
||||
<!-- Non-critical CSS - deferred loading -->
|
||||
<link rel="stylesheet" href="src/styles/layout/footer.css" media="print" onload="this.media='all'">
|
||||
<link rel="stylesheet" href="src/styles/components/buttons.css" media="print" onload="this.media='all'">
|
||||
<link rel="stylesheet" href="src/styles/components/pills.css" media="print" onload="this.media='all'">
|
||||
@@ -267,12 +271,14 @@
|
||||
<main role="main">
|
||||
<section class="hero" aria-labelledby="hero-title">
|
||||
<div class="hero-atmosphere" aria-hidden="true">
|
||||
<div id="hero-prism" class="hero-prism"></div>
|
||||
<video class="hero-atmosphere-video" autoplay muted loop playsinline preload="auto">
|
||||
<source src="https://www.pexels.com/download/video/39333459/" type="video/mp4">
|
||||
</video>
|
||||
</div>
|
||||
<div class="container hero-container">
|
||||
<div class="logo-image">
|
||||
<img src="assets/images/header_logo.png" alt="Vyla Home Logo" width="100" height="100"
|
||||
fetchpriority="high"
|
||||
fetchpriority="high" loading="eager" decoding="async"
|
||||
srcset="assets/images/header_logo.png 100w, assets/images/header_logo.png 200w" sizes="100px">
|
||||
</div>
|
||||
<h1 id="hero-title" class="hero-title">The Local-First Desktop Media Center</h1>
|
||||
@@ -479,7 +485,7 @@
|
||||
<figure class="feature-media">
|
||||
<img src="assets/images/features/1.png"
|
||||
alt="Diagram showing datacenter IP throttling bypass with residential network connection"
|
||||
width="800" height="450" loading="lazy" decoding="async"
|
||||
width="800" height="450" loading="lazy" decoding="async" fetchpriority="low"
|
||||
srcset="assets/images/features/1.png 800w, assets/images/features/1.png 400w"
|
||||
sizes="(max-width: 768px) 400px, 800px">
|
||||
</figure>
|
||||
@@ -494,7 +500,7 @@
|
||||
<figure class="feature-media">
|
||||
<img src="assets/images/features/2.png"
|
||||
alt="Diagram showing decentralized architecture eliminating single point of failure"
|
||||
width="800" height="450" loading="lazy" decoding="async"
|
||||
width="800" height="450" loading="lazy" decoding="async" fetchpriority="low"
|
||||
srcset="assets/images/features/2.png 800w, assets/images/features/2.png 400w"
|
||||
sizes="(max-width: 768px) 400px, 800px">
|
||||
</figure>
|
||||
@@ -508,7 +514,7 @@
|
||||
<figure class="feature-media">
|
||||
<img src="assets/images/features/3.png"
|
||||
alt="Illustration of encrypted local vault with hardware profile key" width="800"
|
||||
height="450" loading="lazy" decoding="async"
|
||||
height="450" loading="lazy" decoding="async" fetchpriority="low"
|
||||
srcset="assets/images/features/3.png 800w, assets/images/features/3.png 400w"
|
||||
sizes="(max-width: 768px) 400px, 800px">
|
||||
</figure>
|
||||
@@ -522,7 +528,7 @@
|
||||
<figure class="feature-media">
|
||||
<img src="assets/images/features/4.png"
|
||||
alt="Interface showing automated over-the-air update process" width="800" height="450"
|
||||
loading="lazy" decoding="async"
|
||||
loading="lazy" decoding="async" fetchpriority="low"
|
||||
srcset="assets/images/features/4.png 800w, assets/images/features/4.png 400w"
|
||||
sizes="(max-width: 768px) 400px, 800px">
|
||||
</figure>
|
||||
@@ -536,7 +542,7 @@
|
||||
<figure class="feature-media">
|
||||
<img src="assets/images/features/5.png"
|
||||
alt="Interface showing rich media discovery with curated collections and 4K content"
|
||||
width="800" height="450" loading="lazy" decoding="async"
|
||||
width="800" height="450" loading="lazy" decoding="async" fetchpriority="low"
|
||||
srcset="assets/images/features/5.png 800w, assets/images/features/5.png 400w"
|
||||
sizes="(max-width: 768px) 400px, 800px">
|
||||
</figure>
|
||||
@@ -550,7 +556,7 @@
|
||||
<figure class="feature-media">
|
||||
<img src="assets/images/features/6.png"
|
||||
alt="Interface showing customizable theme and layout options" width="800" height="450"
|
||||
loading="lazy" decoding="async"
|
||||
loading="lazy" decoding="async" fetchpriority="low"
|
||||
srcset="assets/images/features/6.png 800w, assets/images/features/6.png 400w"
|
||||
sizes="(max-width: 768px) 400px, 800px">
|
||||
</figure>
|
||||
@@ -750,7 +756,7 @@
|
||||
</div>
|
||||
<img src="https://app.greenweb.org/api/v3/greencheckimage/vyla.cc"
|
||||
alt="This website runs on green hosting - verified by thegreenwebfoundation.org" width="200"
|
||||
height="95" loading="lazy" decoding="async">
|
||||
height="95" loading="lazy" decoding="async" fetchpriority="low">
|
||||
</div>
|
||||
<div class="footer-bottom">
|
||||
<a href="/fuckinggodihatethisprojectijustwantoenditall" class="secret-footer">
|
||||
|
||||
@@ -153,7 +153,15 @@ class GlassSurface {
|
||||
}
|
||||
|
||||
applyGlassEffect() {
|
||||
if (this.svgSupported && this.backdropSupported) {
|
||||
const useBackdropOnly = this.config.useBackdropOnly === true;
|
||||
|
||||
if (useBackdropOnly || !this.svgSupported) {
|
||||
if (this.backdropSupported) {
|
||||
this.applyBackdropFallback();
|
||||
} else {
|
||||
this.applyBasicFallback();
|
||||
}
|
||||
} else if (this.svgSupported && this.backdropSupported) {
|
||||
this.applySVGFilter();
|
||||
} else if (this.backdropSupported) {
|
||||
this.applyBackdropFallback();
|
||||
@@ -394,7 +402,7 @@ function destroyGlassSurface(el) {
|
||||
delete el.dataset.hasGlass;
|
||||
}
|
||||
|
||||
function initLiquidGlass() {
|
||||
function initLiquidGlass(configOverride = {}) {
|
||||
const config = {
|
||||
width: 200,
|
||||
height: 25,
|
||||
@@ -402,7 +410,7 @@ function initLiquidGlass() {
|
||||
borderWidth: 0.07,
|
||||
brightness: 50,
|
||||
opacity: 0.93,
|
||||
blur: 11,
|
||||
blur: configOverride.reducedBlur ? 6 : 11,
|
||||
displace: 0,
|
||||
backgroundOpacity: 0,
|
||||
saturation: 1,
|
||||
@@ -418,7 +426,6 @@ function initLiquidGlass() {
|
||||
const eagerSelectors = [
|
||||
'.nav-links',
|
||||
'.discord-copy-bar',
|
||||
'.platform-pill',
|
||||
'.hero-cta-unsure',
|
||||
'#navIslandMain',
|
||||
'#navbarSearchToggle',
|
||||
|
||||
@@ -3,6 +3,8 @@ import { Renderer, Triangle, Program, Mesh } from 'https://esm.sh/ogl@1.0.11';
|
||||
export function initPrism(container, options = {}) {
|
||||
if (!container) return () => { };
|
||||
|
||||
const stepMultiplier = options.stepMultiplier ?? 1.0;
|
||||
|
||||
const H = Math.max(0.001, options.height ?? 3.5);
|
||||
const BW = Math.max(0.001, options.baseWidth ?? 5.5);
|
||||
const BASE_HALF = BW * 0.5;
|
||||
@@ -76,6 +78,7 @@ export function initPrism(container, options = {}) {
|
||||
uniform float uMinAxis;
|
||||
uniform float uPxScale;
|
||||
uniform float uTimeScale;
|
||||
uniform float uStepMultiplier;
|
||||
|
||||
vec4 tanh4(vec4 x){
|
||||
vec4 e2x = exp(2.0*x);
|
||||
@@ -139,8 +142,12 @@ export function initPrism(container, options = {}) {
|
||||
wob = mat2(c0, c1, c2, c0);
|
||||
}
|
||||
|
||||
const int STEPS = 100;
|
||||
for (int i = 0; i < STEPS; i++) {
|
||||
const int MAX_STEPS = 100;
|
||||
float stepMultiplier = uStepMultiplier;
|
||||
int actualSteps = int(floor(float(MAX_STEPS) * stepMultiplier));
|
||||
|
||||
for (int i = 0; i < MAX_STEPS; i++) {
|
||||
if (i >= actualSteps) break;
|
||||
p = vec3(f, z);
|
||||
p.xz = p.xz * wob;
|
||||
p = uRot * p;
|
||||
@@ -198,7 +205,8 @@ export function initPrism(container, options = {}) {
|
||||
uPxScale: {
|
||||
value: 1 / ((gl.drawingBufferHeight || 1) * 0.1 * SCALE)
|
||||
},
|
||||
uTimeScale: { value: TS }
|
||||
uTimeScale: { value: TS },
|
||||
uStepMultiplier: { value: stepMultiplier }
|
||||
}
|
||||
});
|
||||
const mesh = new Mesh(gl, { geometry, program });
|
||||
|
||||
@@ -5,15 +5,21 @@ import { initTestimonialsSection } from './sections/testimonials.js';
|
||||
import { initPartnersSection } from './sections/partners.js';
|
||||
import { initFaqSection } from './sections/faq.js';
|
||||
import { initStatsSection } from './sections/stats.js';
|
||||
import { getDeviceCapability, getAnimationConfig } from './utils/device-capability.js';
|
||||
|
||||
function initApp() {
|
||||
const deviceCapability = getDeviceCapability();
|
||||
const animationConfig = getAnimationConfig();
|
||||
|
||||
observeReveals();
|
||||
|
||||
initLiquidGlass();
|
||||
if (animationConfig.glassSurface.enabled) {
|
||||
initLiquidGlass(animationConfig.glassSurface);
|
||||
}
|
||||
|
||||
initHeroSection();
|
||||
initHeroSection(animationConfig);
|
||||
initStatsSection();
|
||||
initTestimonialsSection();
|
||||
initTestimonialsSection(animationConfig);
|
||||
initPartnersSection();
|
||||
initFaqSection();
|
||||
}
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
import { getPartners } from '../services/partners-service.js';
|
||||
import { createElement } from '../utils/dom.js';
|
||||
import { observeReveals } from '../animations/reveal-observer.js';
|
||||
import { shouldReduceEffects } from '../utils/device-capability.js';
|
||||
|
||||
function createPartnerItem(partner) {
|
||||
const item = createElement('div', 'partner-item reveal');
|
||||
const item = createElement('div', 'partner-item');
|
||||
let imgHTML;
|
||||
|
||||
if (partner.logo) {
|
||||
if (partner.logoSmall) {
|
||||
imgHTML = `<img src="${partner.logoSmall}" alt="${partner.name}" width="128" height="128" loading="lazy" decoding="async"
|
||||
fetchpriority="low"
|
||||
srcset="${partner.logoSmall} 64w, ${partner.logo} 128w" sizes="64px">`;
|
||||
} else {
|
||||
imgHTML = `<img src="${partner.logo}" alt="${partner.name}" width="128" height="128" loading="lazy" decoding="async">`;
|
||||
imgHTML = `<img src="${partner.logo}" alt="${partner.name}" width="128" height="128" loading="lazy" decoding="async"
|
||||
fetchpriority="low">`;
|
||||
}
|
||||
} else {
|
||||
imgHTML = '';
|
||||
@@ -32,7 +35,12 @@ export async function initPartnersSection() {
|
||||
const partners = await getPartners();
|
||||
grid.innerHTML = '';
|
||||
partners.forEach((partner) => grid.appendChild(createPartnerItem(partner)));
|
||||
observeReveals(grid);
|
||||
|
||||
if (shouldReduceEffects()) {
|
||||
grid.classList.add('in-view');
|
||||
} else {
|
||||
observeReveals(grid);
|
||||
}
|
||||
} catch (error) {
|
||||
grid.innerHTML = '';
|
||||
}
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
import { getDiscordStats } from '../services/discord-service.js';
|
||||
import { getScripts } from '../services/scripts-service.js';
|
||||
import { animateCountOnVisible } from '../animations/counter.js';
|
||||
import { shouldReduceEffects } from '../utils/device-capability.js';
|
||||
|
||||
async function initDiscordStats() {
|
||||
const membersEl = document.getElementById('stat-members');
|
||||
const onlineEl = document.getElementById('stat-online');
|
||||
if (!membersEl || !onlineEl) return;
|
||||
|
||||
if (shouldReduceEffects()) {
|
||||
membersEl.textContent = '1K+';
|
||||
onlineEl.textContent = '100+';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { memberCount, onlineCount } = await getDiscordStats();
|
||||
const statsGrid = document.getElementById('stats-grid');
|
||||
@@ -38,6 +45,11 @@ async function initScriptCountStat() {
|
||||
const scriptsEl = document.getElementById('stat-scripts');
|
||||
if (!scriptsEl) return;
|
||||
|
||||
if (shouldReduceEffects()) {
|
||||
scriptsEl.textContent = '50+';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const scripts = await getScripts();
|
||||
animateCountOnVisible(scriptsEl, scripts.length);
|
||||
@@ -47,6 +59,15 @@ async function initScriptCountStat() {
|
||||
}
|
||||
|
||||
export function initStatsSection() {
|
||||
initDiscordStats();
|
||||
initScriptCountStat();
|
||||
if ('requestIdleCallback' in window) {
|
||||
window.requestIdleCallback(() => {
|
||||
initDiscordStats();
|
||||
initScriptCountStat();
|
||||
}, { timeout: 2000 });
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
initDiscordStats();
|
||||
initScriptCountStat();
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { getReviews } from '../services/reviews-service.js';
|
||||
import { createTestimonialCard } from '../components/testimonial-card.js';
|
||||
import { createMarqueeLoop } from '../animations/marquee-loop.js';
|
||||
|
||||
const AVG_TESTIMONIAL_WIDTH_PX = 260;
|
||||
|
||||
@@ -16,7 +17,7 @@ function fillMarqueeTrack(trackEl, reviews) {
|
||||
firstPass.forEach((card) => trackEl.appendChild(card));
|
||||
}
|
||||
|
||||
export async function initTestimonialsSection() {
|
||||
export async function initTestimonialsSection(animationConfig) {
|
||||
const track1 = document.getElementById('marquee-track-1');
|
||||
const track2 = document.getElementById('marquee-track-2');
|
||||
if (!track1 || !track2) return;
|
||||
@@ -29,6 +30,23 @@ export async function initTestimonialsSection() {
|
||||
|
||||
fillMarqueeTrack(track1, rowA.length ? rowA : reviews);
|
||||
fillMarqueeTrack(track2, rowB.length ? rowB : reviews);
|
||||
|
||||
if (animationConfig.marquee.enabled) {
|
||||
const baseSpeed = animationConfig.marquee.reducedSpeed ? 0.3 : 0.6;
|
||||
const boostedSpeed = animationConfig.marquee.reducedSpeed ? 0.6 : 1.2;
|
||||
|
||||
createMarqueeLoop(track1, '--marquee-x-1', {
|
||||
baseSpeed,
|
||||
boostedSpeed,
|
||||
minSetsFor: 'screen'
|
||||
});
|
||||
|
||||
createMarqueeLoop(track2, '--marquee-x-2', {
|
||||
baseSpeed: -baseSpeed,
|
||||
boostedSpeed: -boostedSpeed,
|
||||
minSetsFor: 'screen'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
const wrap = document.querySelector('.marquee-wrap');
|
||||
if (wrap) {
|
||||
|
||||
142
src/scripts/utils/device-capability.js
Normal file
142
src/scripts/utils/device-capability.js
Normal file
@@ -0,0 +1,142 @@
|
||||
export class DeviceCapability {
|
||||
constructor() {
|
||||
this.lowEndDevice = this.detectLowEndDevice();
|
||||
this.reducedMotion = this.detectReducedMotion();
|
||||
this.lowMemory = this.detectLowMemory();
|
||||
this.slowConnection = this.detectSlowConnection();
|
||||
this.mobileViewport = this.detectMobileViewport();
|
||||
this.shouldReduceEffects = this.calculateShouldReduceEffects();
|
||||
}
|
||||
|
||||
detectLowEndDevice() {
|
||||
const ua = navigator.userAgent.toLowerCase();
|
||||
const mobileUA = /android|iphone|ipod|ipad|blackberry|iemobile|opera mini/i.test(ua);
|
||||
|
||||
const cores = navigator.hardwareConcurrency || 4;
|
||||
const lowCores = cores <= 2;
|
||||
|
||||
const memory = (navigator.deviceMemory || 8);
|
||||
const lowMemory = memory <= 4;
|
||||
|
||||
return mobileUA && (lowCores || lowMemory);
|
||||
}
|
||||
|
||||
detectReducedMotion() {
|
||||
return window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
}
|
||||
|
||||
detectLowMemory() {
|
||||
const memory = navigator.deviceMemory;
|
||||
if (memory && memory <= 4) return true;
|
||||
|
||||
const ua = navigator.userAgent.toLowerCase();
|
||||
const lowEndUA = /android.*gt-[a-z0-9]+/i.test(ua) ||
|
||||
/iphone.*os [0-9]+_[0-9]+/i.test(ua);
|
||||
return lowEndUA;
|
||||
}
|
||||
|
||||
detectSlowConnection() {
|
||||
const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
|
||||
if (!connection) return false;
|
||||
|
||||
const slowTypes = ['2g', '3g'];
|
||||
const effectiveType = connection.effectiveType;
|
||||
const saveData = connection.saveData;
|
||||
|
||||
return slowTypes.includes(effectiveType) || saveData;
|
||||
}
|
||||
|
||||
detectMobileViewport() {
|
||||
return window.innerWidth < 768;
|
||||
}
|
||||
|
||||
calculateShouldReduceEffects() {
|
||||
return this.reducedMotion ||
|
||||
this.lowEndDevice ||
|
||||
this.lowMemory ||
|
||||
this.slowConnection;
|
||||
}
|
||||
|
||||
getAnimationConfig() {
|
||||
if (this.shouldReduceEffects) {
|
||||
return {
|
||||
lightRays: {
|
||||
enabled: false,
|
||||
raysSpeed: 0.5,
|
||||
followMouse: false
|
||||
},
|
||||
glassSurface: {
|
||||
enabled: true,
|
||||
useBackdropOnly: true,
|
||||
reducedBlur: true
|
||||
},
|
||||
marquee: {
|
||||
enabled: true,
|
||||
reducedSpeed: true
|
||||
},
|
||||
fluid: {
|
||||
enabled: false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (this.mobileViewport) {
|
||||
return {
|
||||
lightRays: {
|
||||
enabled: true,
|
||||
raysSpeed: 1.0,
|
||||
followMouse: false
|
||||
},
|
||||
glassSurface: {
|
||||
enabled: true,
|
||||
useBackdropOnly: false,
|
||||
reducedBlur: false
|
||||
},
|
||||
marquee: {
|
||||
enabled: true,
|
||||
reducedSpeed: false
|
||||
},
|
||||
fluid: {
|
||||
enabled: false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
lightRays: {
|
||||
enabled: true,
|
||||
raysSpeed: 1.8,
|
||||
followMouse: true
|
||||
},
|
||||
glassSurface: {
|
||||
enabled: true,
|
||||
useBackdropOnly: false,
|
||||
reducedBlur: false
|
||||
},
|
||||
marquee: {
|
||||
enabled: true,
|
||||
reducedSpeed: false
|
||||
},
|
||||
fluid: {
|
||||
enabled: true
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let deviceCapabilityInstance = null;
|
||||
|
||||
export function getDeviceCapability() {
|
||||
if (!deviceCapabilityInstance) {
|
||||
deviceCapabilityInstance = new DeviceCapability();
|
||||
}
|
||||
return deviceCapabilityInstance;
|
||||
}
|
||||
|
||||
export function shouldReduceEffects() {
|
||||
return getDeviceCapability().shouldReduceEffects;
|
||||
}
|
||||
|
||||
export function getAnimationConfig() {
|
||||
return getDeviceCapability().getAnimationConfig();
|
||||
}
|
||||
@@ -38,8 +38,9 @@
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.7rem 1.35rem;
|
||||
padding-left: 1rem;
|
||||
height: 50px !important;
|
||||
width: 160px;
|
||||
border-radius: var(--radius-pill) !important;
|
||||
font-family: var(--font-body);
|
||||
font-size: 0.8125rem;
|
||||
@@ -109,11 +110,7 @@
|
||||
display: inline-block;
|
||||
background: #000;
|
||||
border-radius: var(--radius-pill);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.download-expandable[aria-expanded="true"] {
|
||||
border-radius: 10px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.download-expandable .hero-cta {
|
||||
@@ -142,16 +139,23 @@
|
||||
}
|
||||
|
||||
.download-options {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.3s ease, opacity 0.3s ease;
|
||||
position: absolute;
|
||||
top: calc(100% + 8px);
|
||||
left: 0;
|
||||
right: 0;
|
||||
min-width: 100%;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transform: translateY(-10px);
|
||||
transition: opacity 0.3s ease, transform 0.3s ease, visibility 0.3s ease;
|
||||
background: #000;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.download-options.show {
|
||||
max-height: 200px;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.download-option {
|
||||
|
||||
@@ -36,42 +36,24 @@
|
||||
transform: translateZ(0);
|
||||
backface-visibility: hidden;
|
||||
will-change: transform;
|
||||
contain: layout style paint;
|
||||
}
|
||||
|
||||
.marquee-left .marquee-track {
|
||||
animation: marquee-scroll-left 60s linear infinite;
|
||||
transform: translateX(var(--marquee-x-1, 0));
|
||||
}
|
||||
|
||||
.marquee-right .marquee-track {
|
||||
animation: marquee-scroll-right 60s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes marquee-scroll-left {
|
||||
from {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translate3d(-50%, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes marquee-scroll-right {
|
||||
from {
|
||||
transform: translate3d(-50%, 0, 0);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
transform: translateX(var(--marquee-x-2, 0));
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
|
||||
.marquee-track,
|
||||
.executors-marquee-track,
|
||||
.showcase-track {
|
||||
animation: none !important;
|
||||
.marquee-track {
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Performance optimization: GPU acceleration */
|
||||
.marquee-row {
|
||||
will-change: transform;
|
||||
}
|
||||
@@ -4,6 +4,8 @@
|
||||
align-items: center;
|
||||
gap: 0.85rem;
|
||||
padding-bottom: 3rem;
|
||||
position: relative;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.partners-label {
|
||||
@@ -46,4 +48,18 @@
|
||||
.partner-item a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.partners-inline {
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
|
||||
.partners-grid {
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.partner-item img {
|
||||
height: 24px;
|
||||
}
|
||||
}
|
||||
@@ -101,19 +101,27 @@
|
||||
|
||||
.platform-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem 1rem;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: var(--radius-pill);
|
||||
font-size: 0.8125rem;
|
||||
padding: 0.45rem 1.1rem;
|
||||
border-radius: 50px;
|
||||
font-size: 0.72rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-fg-dim);
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.platform-pill svg {
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
color: var(--color-fg);
|
||||
fill: currentColor;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.platform-pill span {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.platform-divider {
|
||||
color: var(--color-fg-dim);
|
||||
opacity: 0.5;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
padding: 32px 24px 24px;
|
||||
overflow: visible;
|
||||
background: transparent;
|
||||
z-index: 2;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
|
||||
@@ -1 +1 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;900&display=swap');
|
||||
/* Font loading is handled in HTML with preconnect for better performance */
|
||||
@@ -70,6 +70,7 @@
|
||||
|
||||
.cta-banner-content .download-options {
|
||||
background: #fff;
|
||||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.cta-banner-content .download-option {
|
||||
|
||||
@@ -32,4 +32,12 @@
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.hero-atmosphere-video {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
@@ -67,6 +67,7 @@
|
||||
margin: 1rem auto 0;
|
||||
line-height: 1.5;
|
||||
width: 50%;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.hero-description a {
|
||||
@@ -116,35 +117,6 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.platform-pill {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.45rem 1.1rem;
|
||||
border-radius: 50px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
margin-bottom: 1.25rem;
|
||||
white-space: nowrap;
|
||||
width: auto;
|
||||
max-width: max-content;
|
||||
}
|
||||
|
||||
.platform-pill svg {
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
color: #ffffff;
|
||||
fill: currentColor;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.platform-pill span {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.hero-screen-mockup {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
Reference in New Issue
Block a user