mirror of
https://github.com/Shik3i/KoalaSync.git
synced 2026-07-26 12:08:15 +00:00
Split landing CSS by render priority
This commit is contained in:
+2
-2
@@ -22,7 +22,7 @@ KoalaSync is a specialized tool for **synchronized video playback** across multi
|
||||
- `server/`: Node.js Relay Server using Socket.IO (WebSocket-only).
|
||||
- `website/`: **Landing Page** & Invitation Bridge (Marketing, Tutorials, and Downloads).
|
||||
- **`build.cjs`**: Zero-dependency static site compiler. Translates `template.html` + `locales/*.json` → `www/`. Also minifies CSS/JS automatically.
|
||||
- **`www/` is auto-generated**: Never edit files in `www/` directly. Always edit source files (`template.html`, `style.css`, `app.js`, `lang-init.js`, `locales/*.json`) and run `node website/build.cjs` to regenerate. CSS/JS are output as `.min.*` files — a built-in cleanup step removes stale artifacts on each build.
|
||||
- **`www/` is auto-generated**: Never edit files in `www/` directly. Always edit source files (`template.html`, `style.css`, `styles/*.css`, `app.js`, `lang-init.js`, `locales/*.json`) and run `node website/build.cjs` to regenerate. `style.css` is the development manifest; `style.legacy.css` is an unloaded, byte-identical migration reference. Production creates page-specific and render-priority CSS bundles from `styles/*.css`; CSS/JS are output as `.min.*` files and stale generated assets are removed on each build.
|
||||
- `shared/`: **Single Source of Truth** for protocol constants, event names, blacklist data, and generated usernames.
|
||||
- `scripts/`: Development utilities (e.g., `build-extension.cjs`).
|
||||
- `docker-compose.yml`: Root-level orchestration for the relay server.
|
||||
@@ -149,7 +149,7 @@ Before starting any task, committing, or pushing, you **MUST** run `git pull --r
|
||||
3. Implement the handler in `server/index.js` and `background.js`.
|
||||
|
||||
### Making Website Changes
|
||||
1. Edit source files in `website/` (`template.html`, `style.css`, `app.js`, `lang-init.js`, or `locales/*.json`).
|
||||
1. Edit source files in `website/` (`template.html`, `style.css`, `styles/*.css`, `app.js`, `lang-init.js`, or `locales/*.json`).
|
||||
2. Run the compiler: `node website/build.cjs`. This generates the multilingual pages in `www/` and minifies CSS/JS.
|
||||
3. Verify the output: `node --check website/www/app.min.js && node --check website/www/lang-init.min.js`.
|
||||
4. Test locally: `npx serve website/www` or `python3 -m http.server 8080 -d website/www`.
|
||||
|
||||
+2
-1
@@ -4,7 +4,7 @@ This directory contains the static KoalaSync website. It is both the public mark
|
||||
|
||||
## Where You Are
|
||||
|
||||
- `template.html`, `style.css`, `app.js`, `lang-init.js`, and `locales/*.json` are source files.
|
||||
- `template.html`, `style.css`, `styles/*.css`, `app.js`, `lang-init.js`, and `locales/*.json` are source files. `style.css` is the local-development manifest; production concatenates the modules in the order declared by `website/build.cjs`.
|
||||
- `build.cjs` compiles the static site into `website/www/`.
|
||||
- `website/www/` is generated output. Do not edit files there directly.
|
||||
- `join.html` handles room-invite links and communicates with the extension through `bridge.js`.
|
||||
@@ -33,6 +33,7 @@ The website is 100% static HTML, CSS, and JavaScript.
|
||||
|
||||
- **Static i18n compiler**: `build.cjs` combines `template.html` with dictionaries in `locales/`.
|
||||
- **Build-time minification**: Source CSS/JS stays readable; generated output uses `.min.css` and `.min.js`.
|
||||
- **Strangler CSS architecture**: `styles/*.css` supplies page/runtime bundles; `style.legacy.css` is the byte-identical, unloaded reference monolith. The landing ships a critical shell, a desktop/on-demand demo bundle, and idle-loaded below-fold styles. Legal, join, and alternatives rules stay out of landing bundles.
|
||||
- **Zero backend**: The compiled site can be hosted by any static file server.
|
||||
- **Zero external assets**: Fonts, icons, scripts, and images must remain self-hosted.
|
||||
- **Generated SEO/runtime files**: `version.json`, sitemap, robots, clean URLs, localized pages, and minified assets are copied or generated into `www/`.
|
||||
|
||||
@@ -1,6 +1,27 @@
|
||||
// KoalaSync Landing Page Logic
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const deferredCss = document.body.dataset.deferredCss;
|
||||
if (deferredCss) {
|
||||
const loadDeferredCss = () => {
|
||||
if (document.querySelector('link[data-landing-deferred]')) return;
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = deferredCss;
|
||||
link.dataset.landingDeferred = '';
|
||||
const integrity = document.body.dataset.deferredCssIntegrity;
|
||||
if (integrity) {
|
||||
link.integrity = integrity;
|
||||
link.crossOrigin = 'anonymous';
|
||||
}
|
||||
document.head.appendChild(link);
|
||||
};
|
||||
if ('requestIdleCallback' in window) {
|
||||
window.requestIdleCallback(loadDeferredCss, { timeout: 1500 });
|
||||
} else {
|
||||
window.setTimeout(loadDeferredCss, 250);
|
||||
}
|
||||
}
|
||||
// Mockup Video Title Randomization on Load
|
||||
const SERIES_NAMES = [
|
||||
'Stranger Things',
|
||||
@@ -1311,6 +1332,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
let previousFocus = null;
|
||||
|
||||
const openModal = () => {
|
||||
const demoStyles = document.querySelector('link[data-landing-demo]');
|
||||
if (demoStyles) demoStyles.media = 'all';
|
||||
previousFocus = document.activeElement && typeof document.activeElement.focus === 'function'
|
||||
? document.activeElement
|
||||
: null;
|
||||
|
||||
+67
-1
@@ -132,11 +132,62 @@ async function compile() {
|
||||
|
||||
// ── 1. Minify CSS/JS (must happen first so hashes go into HTML) ──
|
||||
console.log('Minifying CSS/JS...');
|
||||
const styleRaw = fs.readFileSync(path.join(websiteDir, 'style.css'), 'utf8');
|
||||
const styleModules = [
|
||||
'styles/foundation.css',
|
||||
'styles/hero.css',
|
||||
'styles/landing-primary.css',
|
||||
'styles/legal.css',
|
||||
'styles/landing-controls.css',
|
||||
'styles/join-spinner.css',
|
||||
'styles/landing-sections.css',
|
||||
'styles/join.css',
|
||||
'styles/landing-faq.css',
|
||||
'styles/alternatives.css',
|
||||
'styles/demo.css'
|
||||
];
|
||||
// Landing CSS is split by actual render dependency, not by the historical
|
||||
// section comments in style.legacy.css. The initial bundle contains every
|
||||
// selector used by the background, navigation, hero, interactive mockup,
|
||||
// responsive shell, and accessibility states. Below-fold sections load
|
||||
// after first paint from app.js. Utility-page rules never reach the landing.
|
||||
const landingCriticalModules = [
|
||||
'styles/foundation.css',
|
||||
'styles/hero.css',
|
||||
'styles/legal.css',
|
||||
'styles/landing-controls.css',
|
||||
'styles/landing-demo-gate.css'
|
||||
];
|
||||
const landingDemoModules = ['styles/demo.css'];
|
||||
const landingDeferredModules = [
|
||||
'styles/landing-primary.css',
|
||||
'styles/landing-sections.css',
|
||||
'styles/landing-faq.css'
|
||||
];
|
||||
const styleRaw = styleModules
|
||||
.map(relativePath => fs.readFileSync(path.join(websiteDir, relativePath), 'utf8'))
|
||||
.join('');
|
||||
const styleMin = minifyCSS(styleRaw);
|
||||
const styleHash = sha8(styleMin);
|
||||
const styleName = `style.${styleHash}.min.css`;
|
||||
const styleSRI = sha384(styleMin);
|
||||
const landingStyleRaw = landingCriticalModules
|
||||
.map(relativePath => fs.readFileSync(path.join(websiteDir, relativePath), 'utf8'))
|
||||
.join('');
|
||||
const landingStyleMin = minifyCSS(landingStyleRaw);
|
||||
const landingStyleName = `landing.${sha8(landingStyleMin)}.min.css`;
|
||||
const landingStyleSRI = sha384(landingStyleMin);
|
||||
const landingDemoRaw = landingDemoModules
|
||||
.map(relativePath => fs.readFileSync(path.join(websiteDir, relativePath), 'utf8'))
|
||||
.join('');
|
||||
const landingDemoMin = minifyCSS(landingDemoRaw);
|
||||
const landingDemoName = `landing-demo.${sha8(landingDemoMin)}.min.css`;
|
||||
const landingDemoSRI = sha384(landingDemoMin);
|
||||
const landingDeferredRaw = landingDeferredModules
|
||||
.map(relativePath => fs.readFileSync(path.join(websiteDir, relativePath), 'utf8'))
|
||||
.join('');
|
||||
const landingDeferredMin = minifyCSS(landingDeferredRaw);
|
||||
const landingDeferredName = `landing-deferred.${sha8(landingDeferredMin)}.min.css`;
|
||||
const landingDeferredSRI = sha384(landingDeferredMin);
|
||||
|
||||
const appRaw = fs.readFileSync(path.join(websiteDir, 'app.js'), 'utf8');
|
||||
const appMin = await minifyJS(appRaw);
|
||||
@@ -154,6 +205,9 @@ async function compile() {
|
||||
const appPct = ((1 - appMin.length / appRaw.length) * 100).toFixed(0);
|
||||
const langPct = ((1 - langMin.length / langRaw.length) * 100).toFixed(0);
|
||||
console.log(` CSS: ${styleName} (${(styleMin.length/1024).toFixed(1)} KB, -${stylePct}%)`);
|
||||
console.log(` Landing CSS: ${landingStyleName} (${(landingStyleMin.length/1024).toFixed(1)} KB)`);
|
||||
console.log(` Desktop/modal demo CSS: ${landingDemoName} (${(landingDemoMin.length/1024).toFixed(1)} KB)`);
|
||||
console.log(` Deferred landing CSS: ${landingDeferredName} (${(landingDeferredMin.length/1024).toFixed(1)} KB)`);
|
||||
console.log(` App: ${appName} (${(appMin.length/1024).toFixed(1)} KB, -${appPct}%)`);
|
||||
console.log(` Lang: ${langName} (${(langMin.length/1024).toFixed(1)} KB, -${langPct}%)`);
|
||||
|
||||
@@ -167,6 +221,9 @@ async function compile() {
|
||||
|
||||
// Write minified files
|
||||
fs.writeFileSync(path.join(wwwDir, styleName), styleMin);
|
||||
fs.writeFileSync(path.join(wwwDir, landingStyleName), landingStyleMin);
|
||||
fs.writeFileSync(path.join(wwwDir, landingDemoName), landingDemoMin);
|
||||
fs.writeFileSync(path.join(wwwDir, landingDeferredName), landingDeferredMin);
|
||||
fs.writeFileSync(path.join(wwwDir, appName), appMin);
|
||||
fs.writeFileSync(path.join(wwwDir, langName), langMin);
|
||||
|
||||
@@ -463,6 +520,15 @@ async function compile() {
|
||||
html = html.replace(/(<link\b[^>]*?\bhref=")((?:\.\.\/)*\/?)style\.min\.css"/g, (m, before, prefix) => {
|
||||
return `${before}${prefix}${styleName}" integrity="${styleSRI}" crossorigin="anonymous"`;
|
||||
});
|
||||
html = html.replace(/(<link\b[^>]*?\bhref=")((?:\.\.\/)*\/?)landing\.min\.css"/g, (m, before, prefix) => {
|
||||
return `${before}${prefix}${landingStyleName}" integrity="${landingStyleSRI}" crossorigin="anonymous"`;
|
||||
});
|
||||
html = html.replace(/(<link\b[^>]*?\bhref=")((?:\.\.\/)*\/?)landing-demo\.min\.css"/g, (m, before, prefix) => {
|
||||
return `${before}${prefix}${landingDemoName}" integrity="${landingDemoSRI}" crossorigin="anonymous"`;
|
||||
});
|
||||
html = html.replace(/data-deferred-css="((?:\.\.\/)*\/?)landing-deferred\.min\.css"/g, (m, prefix) => {
|
||||
return `data-deferred-css="${prefix}${landingDeferredName}" data-deferred-css-integrity="${landingDeferredSRI}"`;
|
||||
});
|
||||
html = html.replace(/(<script\b[^>]*?\bsrc=")((?:\.\.\/)*\/?)app\.min\.js"/g, (m, before, prefix) => {
|
||||
return `${before}${prefix}${appName}" integrity="${appSRI}" crossorigin="anonymous"`;
|
||||
});
|
||||
|
||||
+18
-6576
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,23 @@
|
||||
/* --- Alternatives Hub Styles --- */
|
||||
.guide-card {
|
||||
transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1), background-color 0.25s ease, border-color 0.25s ease !important;
|
||||
}
|
||||
.guide-card:hover {
|
||||
transform: translateY(-4px);
|
||||
background-color: rgba(255, 255, 255, 0.07) !important;
|
||||
border-color: rgba(255, 255, 255, 0.22) !important;
|
||||
box-shadow: 0 12px 24px -10px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
.guide-card .btn-primary {
|
||||
transition: background-color 0.2s ease, transform 0.1s ease;
|
||||
}
|
||||
.guide-card .btn-primary:active {
|
||||
transform: scale(0.97);
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.guides-grid {
|
||||
grid-template-columns: 1fr !important;
|
||||
gap: 1.25rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Vendored
+1418
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,911 @@
|
||||
/* --- Hero Section --- */
|
||||
.hero {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding-top: clamp(8rem, 12vh, 10rem);
|
||||
padding-bottom: clamp(2rem, 5vh, 4rem);
|
||||
}
|
||||
|
||||
.hero-grid {
|
||||
display: grid;
|
||||
/* Single column until the interactive demo appears (1024px). Below that the
|
||||
headline gets the full width and the install CTAs stay prominent while the
|
||||
demo stacks underneath, matching the demo's own 1024px breakpoint. */
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
align-content: center;
|
||||
/* Keep the whole hero vertically centered. Do not use grid alignment to fix
|
||||
the demo top edge; that moves the headline and breaks the hero balance. */
|
||||
align-items: center;
|
||||
gap: 3rem;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
min-height: calc(100svh - 12.5rem);
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
}
|
||||
.hero-grid {
|
||||
grid-template-columns: 1.35fr 1fr;
|
||||
gap: 3.5rem;
|
||||
}
|
||||
.cta-group {
|
||||
gap: 0.75rem;
|
||||
}
|
||||
.cta-group .btn {
|
||||
padding: 0.8rem 1.35rem;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-text h1 {
|
||||
font-size: clamp(2.9rem, 4.1vw, 4rem);
|
||||
font-weight: 800;
|
||||
line-height: 1.12;
|
||||
margin-bottom: 1.4rem;
|
||||
padding-bottom: 0.1em;
|
||||
color: var(--text);
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.hero-text h1 .hero-highlight {
|
||||
background: linear-gradient(90deg, var(--accent-green), var(--accent-terracotta), var(--accent-green));
|
||||
background-size: 300% 100%;
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
white-space: nowrap;
|
||||
animation: hero-gradient-pan 10s linear infinite;
|
||||
/* background-clip: text only paints inside the layout box; with the
|
||||
negative h1 letter-spacing the last glyph overhangs it and gets cut
|
||||
off. Invisible padding extends the paint area without moving text. */
|
||||
padding-right: 0.08em;
|
||||
margin-right: -0.08em;
|
||||
}
|
||||
|
||||
@keyframes hero-gradient-pan {
|
||||
from { background-position: 0% 50%; }
|
||||
to { background-position: 300% 50%; }
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.hero-text h1 .hero-highlight {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-text p, .hero-subtitle {
|
||||
font-size: 1.25rem;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 1.9rem;
|
||||
max-width: 600px;
|
||||
font-weight: 400;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.hero-mascot-container {
|
||||
position: relative;
|
||||
display: block;
|
||||
margin-bottom: -0.5rem;
|
||||
margin-left: 2.2rem;
|
||||
}
|
||||
|
||||
.hero-lookdown-mascot {
|
||||
display: block;
|
||||
width: 90px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* Changelog link as the koala's comic speech bubble. Absolutely positioned
|
||||
so it never moves the koala out of its spot above the install buttons. */
|
||||
.koala-speech-bubble {
|
||||
position: absolute;
|
||||
left: 108px;
|
||||
top: 0.2rem;
|
||||
width: max-content;
|
||||
max-width: min(240px, calc(100% - 120px));
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
text-align: left;
|
||||
background: oklch(0.27 0.035 130 / 0.95);
|
||||
border: 1px solid oklch(0.68 0.13 150 / 0.45);
|
||||
border-radius: 14px;
|
||||
border-bottom-left-radius: 4px;
|
||||
padding: 0.6rem 0.9rem;
|
||||
line-height: 1.35;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
|
||||
animation: bubble-float 4s ease-in-out infinite;
|
||||
transition: border-color 0.25s ease, box-shadow 0.25s ease;
|
||||
}
|
||||
|
||||
/* Tail: outline triangle + fill triangle so it reads as part of the bubble */
|
||||
.koala-speech-bubble::before,
|
||||
.koala-speech-bubble::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.koala-speech-bubble::before {
|
||||
left: -11px;
|
||||
bottom: 10px;
|
||||
border-top: 8px solid transparent;
|
||||
border-bottom: 4px solid transparent;
|
||||
border-right: 11px solid oklch(0.68 0.13 150 / 0.45);
|
||||
}
|
||||
|
||||
.koala-speech-bubble::after {
|
||||
left: -9px;
|
||||
bottom: 11px;
|
||||
border-top: 7px solid transparent;
|
||||
border-bottom: 3px solid transparent;
|
||||
border-right: 10px solid oklch(0.27 0.035 130 / 0.95);
|
||||
}
|
||||
|
||||
.koala-speech-bubble:hover {
|
||||
border-color: oklch(0.68 0.13 150 / 0.85);
|
||||
box-shadow: 0 8px 28px oklch(0.68 0.13 150 / 0.35);
|
||||
}
|
||||
|
||||
.koala-speech-bubble .version-text-en,
|
||||
.koala-speech-bubble .version-text-de {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.koala-speech-bubble .bubble-title {
|
||||
display: block;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 800;
|
||||
color: var(--text);
|
||||
letter-spacing: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.koala-speech-bubble .bubble-sub {
|
||||
display: block;
|
||||
font-size: 0.72rem;
|
||||
font-weight: 600;
|
||||
color: var(--accent);
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
html.theme-light .koala-speech-bubble {
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
box-shadow: 0 8px 24px oklch(0.20 0.025 140 / 0.12);
|
||||
}
|
||||
|
||||
html.theme-light .koala-speech-bubble::after {
|
||||
border-right-color: rgba(255, 255, 255, 0.96);
|
||||
}
|
||||
|
||||
@keyframes bubble-float {
|
||||
0%, 100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-4px); }
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.koala-speech-bubble {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
/* The koala keeps standing on the active install CTA; app.js updates the
|
||||
offset from the real button geometry when the row wraps. */
|
||||
.hero-mascot-container {
|
||||
--hero-mascot-offset: 0px;
|
||||
margin-left: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.hero-lookdown-mascot {
|
||||
transform: translateX(var(--hero-mascot-offset));
|
||||
}
|
||||
.koala-speech-bubble {
|
||||
left: calc(50% + var(--hero-mascot-offset) + 50px);
|
||||
right: 0.75rem;
|
||||
top: 0.3rem;
|
||||
max-width: 240px;
|
||||
padding: 0.5rem 0.75rem;
|
||||
}
|
||||
.koala-speech-bubble .bubble-title {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.koala-speech-bubble .bubble-sub {
|
||||
font-size: 0.68rem;
|
||||
}
|
||||
/* The relative release time is too much text for the small mobile bubble */
|
||||
.koala-speech-bubble .bubble-ago {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.cta-group {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
/* Quiet trust line under the install buttons; answers "is it free, is it
|
||||
legit, do I need an account" right at the moment of the click decision. */
|
||||
.hero-text .cta-trust {
|
||||
margin-top: 0.85rem;
|
||||
margin-bottom: 0;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
color: var(--text-muted);
|
||||
opacity: 0.75;
|
||||
letter-spacing: 0.01em;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 0.85rem 1.75rem;
|
||||
border-radius: 12px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.3s cubic-bezier(0.4, 0, 0.2, 1), color 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1), border-color 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, oklch(0.48 0.13 150), oklch(0.39 0.10 145));
|
||||
color: #fff;
|
||||
border: 1px solid oklch(0.72 0.11 95 / 0.24);
|
||||
box-shadow: 0 12px 24px -8px oklch(0.38 0.11 148 / 0.65), 0 0 0 1px oklch(0.80 0.08 90 / 0.06) inset;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: linear-gradient(135deg, oklch(0.54 0.14 150), oklch(0.44 0.11 145));
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 18px 32px -8px oklch(0.38 0.11 148 / 0.75), 0 0 18px oklch(0.68 0.13 150 / 0.18);
|
||||
}
|
||||
|
||||
.btn-firefox {
|
||||
background: oklch(0.62 0.14 45);
|
||||
color: white;
|
||||
box-shadow: 0 10px 15px -3px oklch(0.62 0.14 45 / 0.3);
|
||||
}
|
||||
|
||||
.btn-firefox:hover {
|
||||
background: oklch(0.68 0.14 45);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 20px 25px -5px oklch(0.62 0.14 45 / 0.4);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: var(--card);
|
||||
color: var(--text);
|
||||
border: 1px solid var(--glass-border);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: oklch(0.30 0.03 125);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.btn-demo-mobile {
|
||||
display: none;
|
||||
border: 1px solid oklch(0.68 0.13 150 / 0.34);
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
.btn-demo-mobile svg {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Install buttons: compact by default (just the browser name) so all three
|
||||
CTAs fit one line; the detected browser expands to the full call to action.
|
||||
The GitHub button stays compact always. */
|
||||
.btn-label-full { display: none; }
|
||||
|
||||
.btn-install:not(.is-detected),
|
||||
.btn-compact {
|
||||
padding: 0.62rem 1.05rem;
|
||||
font-size: 0.9rem;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.btn-install.is-detected .btn-label-short { display: none; }
|
||||
.btn-install.is-detected .btn-label-full { display: inline; }
|
||||
|
||||
.version-badge {
|
||||
display: inline-block;
|
||||
background: oklch(0.68 0.13 150 / 0.1);
|
||||
color: var(--accent);
|
||||
padding: 0.4rem 1rem;
|
||||
border-radius: 99px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 1.5rem;
|
||||
border: 1px solid oklch(0.68 0.13 150 / 0.3);
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s, box-shadow 0.2s, background 0.2s;
|
||||
}
|
||||
|
||||
.version-badge-below {
|
||||
margin-bottom: 0;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.version-badge:hover {
|
||||
background: oklch(0.68 0.13 150 / 0.2);
|
||||
box-shadow: 0 0 16px oklch(0.68 0.13 150 / 0.3);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* --- Interactive CSS Extension Mockup --- */
|
||||
.hero-mockup-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.extension-mockup {
|
||||
width: 100%;
|
||||
max-width: 320px;
|
||||
height: 528px;
|
||||
background: oklch(0.20 0.025 140);
|
||||
border-radius: 20px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4), 0 0 20px oklch(0.68 0.13 150 / 0.15);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
user-select: none;
|
||||
padding: 14px;
|
||||
padding-bottom: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.mock-header-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.mock-header-title {
|
||||
font-size: 18px;
|
||||
margin: 0;
|
||||
color: var(--accent);
|
||||
letter-spacing: 1px;
|
||||
text-transform: uppercase;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.mock-header-title picture {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
.mock-header-title img {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
object-fit: contain;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.mock-version-link {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: 10px;
|
||||
color: var(--text-muted);
|
||||
text-decoration: none;
|
||||
opacity: 0.5;
|
||||
transition: opacity 0.2s, color 0.2s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mock-version-link:hover {
|
||||
opacity: 1;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
/* Close button for the hero demo popup (hidden in the static mobile mockup) */
|
||||
.mock-close-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
margin-left: 8px;
|
||||
padding: 0;
|
||||
flex-shrink: 0;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
transition: color 0.2s, background 0.2s;
|
||||
}
|
||||
|
||||
.mock-close-btn:hover {
|
||||
color: #fff;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.mock-close-btn {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.mock-tabs {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
background: oklch(0.27 0.035 130);
|
||||
padding: 4px;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 14px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.mock-tab {
|
||||
flex: 1;
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
padding: 7px 4px;
|
||||
cursor: pointer;
|
||||
border-radius: 8px;
|
||||
transition: color 0.2s, background-color 0.2s;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mock-tab:hover {
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.mock-tab.active {
|
||||
background: var(--accent);
|
||||
color: var(--text-on-green);
|
||||
}
|
||||
|
||||
.mock-body {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 0;
|
||||
padding-bottom: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 0.8rem;
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
|
||||
.mock-body::-webkit-scrollbar {
|
||||
display: none; /* Chrome, Safari, Opera */
|
||||
}
|
||||
|
||||
.mock-screen {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.mock-screen.active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* Common Mockup Components */
|
||||
.mock-card {
|
||||
background: oklch(0.27 0.035 130);
|
||||
border: 1px solid oklch(0.32 0.03 125);
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.mock-label {
|
||||
font-size: 0.65rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
.mock-section-label {
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 4px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.mock-input {
|
||||
background: oklch(0.27 0.035 130);
|
||||
border: 1px solid oklch(0.32 0.03 125);
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
padding: 8px 10px;
|
||||
font-size: 0.75rem;
|
||||
width: 100%;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.mock-btn {
|
||||
background: var(--accent);
|
||||
color: var(--text-on-green);
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
padding: 0.5rem;
|
||||
font-weight: 600;
|
||||
font-size: 0.75rem;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mock-btn:hover {
|
||||
background: oklch(0.60 0.12 150);
|
||||
}
|
||||
|
||||
/* Room Tab Details */
|
||||
.mock-joined-room {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background: oklch(0.68 0.13 150 / 0.1);
|
||||
border: 1px solid oklch(0.68 0.13 150 / 0.2);
|
||||
border-radius: 8px;
|
||||
padding: 0.6rem 0.75rem;
|
||||
}
|
||||
|
||||
.mock-room-name {
|
||||
font-weight: 700;
|
||||
color: var(--accent);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.mock-server-badge {
|
||||
background: oklch(0.68 0.13 150 / 0.2);
|
||||
color: var(--accent);
|
||||
font-size: 0.6rem;
|
||||
font-weight: 700;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.mock-invite-box {
|
||||
display: flex;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.mock-invite-box input {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.demo-room-empty {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
padding: 24px 10px;
|
||||
text-align: center;
|
||||
}
|
||||
.demo-room-empty img {
|
||||
border-radius: 10px;
|
||||
opacity: 0.85;
|
||||
box-shadow: 0 4px 12px oklch(0.68 0.13 150 / 0.2);
|
||||
}
|
||||
.demo-room-empty-text {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
font-weight: 600;
|
||||
}
|
||||
.demo-create-room-btn {
|
||||
background: var(--accent);
|
||||
border: none;
|
||||
color: var(--text-on-green);
|
||||
font-weight: 700;
|
||||
padding: 10px 18px;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
.demo-create-room-btn:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px oklch(0.68 0.13 150 / 0.4);
|
||||
}
|
||||
.demo-create-room-btn:active {
|
||||
transform: scale(0.96);
|
||||
}
|
||||
|
||||
.mock-peer-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.mock-peer-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
border: 1px solid rgba(255, 255, 255, 0.03);
|
||||
border-radius: 8px;
|
||||
padding: 0.45rem 0.55rem;
|
||||
}
|
||||
|
||||
.mock-peer-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 0.2rem;
|
||||
}
|
||||
|
||||
.mock-peer-name {
|
||||
font-weight: 700;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.mock-peer-meta {
|
||||
font-size: 0.65rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.mock-peer-tab {
|
||||
font-size: 0.7rem;
|
||||
color: var(--accent);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.mock-peer-status {
|
||||
font-size: 0.65rem;
|
||||
color: var(--text-muted);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
/* Sync Tab Details */
|
||||
.mock-target-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
background: oklch(0.62 0.14 45 / 0.1);
|
||||
border: 1px solid oklch(0.62 0.14 45 / 0.2);
|
||||
color: oklch(0.72 0.13 50);
|
||||
padding: 0.5rem 0.6rem;
|
||||
border-radius: 8px;
|
||||
font-weight: 600;
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
.mock-ctrl-buttons {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.mock-btn-play { background: oklch(0.68 0.13 150); }
|
||||
.mock-btn-play:hover { background: oklch(0.60 0.12 150); }
|
||||
.mock-btn-pause { background: oklch(0.55 0.15 25); color: #fff; }
|
||||
.mock-btn-pause:hover { background: oklch(0.48 0.14 25); }
|
||||
.mock-btn-force { background: var(--accent); grid-column: span 2; }
|
||||
|
||||
/* Contrast-safe palette for the decorative hero demo. Lighthouse/axe audits
|
||||
sighted text contrast even inside aria-hidden, so every walkthrough state
|
||||
must meet WCAG AA. Accent shown as text gets a lighter indigo; buttons that
|
||||
put white text on a colour get a darker shade (these beat inline styles). */
|
||||
.hero-demo-scene {
|
||||
--accent: oklch(0.80 0.10 150);
|
||||
--demo-sky-top: oklch(0.22 0.055 152);
|
||||
--demo-sky-mid: oklch(0.145 0.035 150);
|
||||
--demo-sky-bottom: oklch(0.075 0.018 145);
|
||||
--demo-glow: oklch(0.76 0.13 150 / 0.34);
|
||||
--demo-stars: oklch(0.94 0.025 95);
|
||||
--demo-moon: oklch(0.97 0.025 92);
|
||||
--demo-mountain: oklch(0.18 0.055 148 / 0.9);
|
||||
--demo-midground: oklch(0.22 0.065 145 / 0.88);
|
||||
--demo-foreground: oklch(0.065 0.025 145);
|
||||
--demo-card: oklch(0.18 0.032 142);
|
||||
--demo-toolbar: oklch(0.12 0.025 142);
|
||||
--demo-control-fade: oklch(0.055 0.015 145 / 0.94);
|
||||
--demo-track: oklch(0.92 0.02 95 / 0.2);
|
||||
}
|
||||
.hero-demo-scene .mock-tab.active,
|
||||
.hero-demo-scene .mock-btn-force,
|
||||
.hero-demo-scene #demo-force-sync,
|
||||
.hero-demo-scene .demo-create-room-btn {
|
||||
background: oklch(0.42 0.09 150) !important;
|
||||
color: #fff;
|
||||
}
|
||||
.hero-demo-scene .mock-btn-play {
|
||||
background: oklch(0.50 0.11 150) !important;
|
||||
}
|
||||
.hero-demo-scene .mock-btn-pause,
|
||||
.hero-demo-scene #demo-room-joined > button.mock-btn {
|
||||
background: oklch(0.45 0.14 25) !important;
|
||||
}
|
||||
.hero-demo-scene .mock-version-link {
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
html.theme-light .hero-demo-scene {
|
||||
--accent: oklch(0.60 0.12 150);
|
||||
--demo-sky-top: oklch(0.86 0.085 78);
|
||||
--demo-sky-mid: oklch(0.76 0.095 112);
|
||||
--demo-sky-bottom: oklch(0.54 0.10 150);
|
||||
--demo-glow: oklch(0.92 0.13 72 / 0.44);
|
||||
--demo-stars: oklch(0.98 0.025 90);
|
||||
--demo-moon: oklch(0.99 0.025 88);
|
||||
--demo-mountain: oklch(0.48 0.095 150 / 0.82);
|
||||
--demo-midground: oklch(0.40 0.10 148 / 0.8);
|
||||
--demo-foreground: oklch(0.24 0.075 145);
|
||||
--demo-card: oklch(0.985 0.008 95);
|
||||
--demo-toolbar: oklch(0.955 0.014 100);
|
||||
--demo-control-fade: oklch(0.16 0.035 145 / 0.88);
|
||||
--demo-track: oklch(0.98 0.01 95 / 0.34);
|
||||
}
|
||||
|
||||
|
||||
html.theme-light .hero-demo-scene .demo-tab-card {
|
||||
background: var(--demo-card);
|
||||
border-color: oklch(0.20 0.025 140 / 0.1);
|
||||
box-shadow:
|
||||
0 18px 38px oklch(0.20 0.025 140 / 0.16),
|
||||
0 0 28px oklch(0.82 0.10 85 / 0.14),
|
||||
inset 0 1px rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
html.theme-light .hero-demo-scene .demo-tab-titlebar {
|
||||
background: var(--demo-toolbar);
|
||||
border-bottom-color: oklch(0.88 0.015 90);
|
||||
color: oklch(0.45 0.02 110);
|
||||
}
|
||||
|
||||
html.theme-light .hero-demo-scene .demo-tab-dots i {
|
||||
background: oklch(0.78 0.02 90);
|
||||
}
|
||||
|
||||
html.theme-light .hero-demo-scene .demo-ext-launcher {
|
||||
background: #ffffff;
|
||||
border-color: oklch(0.78 0.02 90);
|
||||
}
|
||||
|
||||
html.theme-light .hero-demo-scene .demo-sync-chip {
|
||||
background: oklch(0.55 0.12 150 / 0.1);
|
||||
border-color: oklch(0.55 0.12 150 / 0.26);
|
||||
color: oklch(0.40 0.10 150);
|
||||
}
|
||||
|
||||
.mock-status-display {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.mock-status-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.3rem;
|
||||
background: oklch(0.68 0.13 150 / 0.1);
|
||||
border: 1px solid oklch(0.68 0.13 150 / 0.2);
|
||||
color: oklch(0.68 0.13 150);
|
||||
padding: 2px 8px;
|
||||
border-radius: 99px;
|
||||
font-size: 0.65rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.mock-log-item {
|
||||
font-size: 0.7rem;
|
||||
color: var(--text-muted);
|
||||
border-left: 2px solid var(--accent);
|
||||
padding-left: 0.4rem;
|
||||
margin-top: 0.4rem;
|
||||
}
|
||||
|
||||
/* Settings Tab Details */
|
||||
.mock-settings-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.6rem;
|
||||
}
|
||||
|
||||
.mock-settings-row input[type="checkbox"] {
|
||||
margin-top: 0.2rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mock-settings-row label {
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.mock-settings-row p {
|
||||
font-size: 0.65rem;
|
||||
color: var(--text-muted);
|
||||
margin-top: 0.1rem;
|
||||
}
|
||||
|
||||
/* Mock Toggle Switch Details */
|
||||
.mock-toggle-switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 36px;
|
||||
height: 20px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.mock-toggle-switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.mock-slider {
|
||||
position: absolute;
|
||||
cursor: default;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background-color: oklch(0.32 0.03 125);
|
||||
transition: .3s;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.mock-slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
left: 3px;
|
||||
bottom: 3px;
|
||||
background-color: oklch(0.68 0.02 100);
|
||||
transition: .3s;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
input:checked + .mock-slider {
|
||||
background-color: var(--accent);
|
||||
}
|
||||
|
||||
input:checked + .mock-slider:before {
|
||||
transform: translateX(16px);
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
/* Dev Tab Details */
|
||||
.mock-diagnostic-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.mock-diag-box {
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
border: 1px solid rgba(255, 255, 255, 0.04);
|
||||
border-radius: 6px;
|
||||
padding: 0.4rem;
|
||||
}
|
||||
|
||||
.mock-diag-box .diag-val {
|
||||
font-family: monospace;
|
||||
font-weight: 700;
|
||||
color: var(--accent);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/* --- Join Page Loading Spinner --- */
|
||||
.join-spinner {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border: 3px solid oklch(0.68 0.13 150 / 0.2);
|
||||
border-top-color: var(--accent);
|
||||
border-radius: 50%;
|
||||
animation: spin 0.8s linear infinite;
|
||||
margin: 0 auto 0.75rem;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* --- Reduced Motion --- */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*, *::before, *::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
[data-reveal] {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
}
|
||||
.illus-typing-text::after {
|
||||
animation: none;
|
||||
}
|
||||
.illus-timeline-progress {
|
||||
animation: none;
|
||||
width: 60%;
|
||||
}
|
||||
.illus-sync-pulse {
|
||||
animation: none;
|
||||
}
|
||||
.illus-extension-icon {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
/* --- Join & Invitation Page Visuals --- */
|
||||
.join-status-visual {
|
||||
margin-bottom: 2rem;
|
||||
position: relative;
|
||||
height: 200px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.join-status-mascot {
|
||||
display: block;
|
||||
width: 140px;
|
||||
height: auto;
|
||||
z-index: 2;
|
||||
filter: drop-shadow(0 4px 12px rgba(0,0,0,0.15));
|
||||
}
|
||||
|
||||
.join-status-mascot.searching-mascot {
|
||||
width: 175px; /* Slightly wider due to antenna to keep main body scale identical */
|
||||
}
|
||||
|
||||
.join-status-pulse {
|
||||
animation: mascot-gentle-pulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes mascot-gentle-pulse {
|
||||
0% {
|
||||
transform: scale(0.96);
|
||||
filter: drop-shadow(0 4px 10px oklch(0.68 0.13 150 / 0.15));
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.04);
|
||||
filter: drop-shadow(0 8px 20px oklch(0.68 0.13 150 / 0.35));
|
||||
}
|
||||
100% {
|
||||
transform: scale(0.96);
|
||||
filter: drop-shadow(0 4px 10px oklch(0.68 0.13 150 / 0.15));
|
||||
}
|
||||
}
|
||||
|
||||
.legal-mascot {
|
||||
display: block;
|
||||
width: 180px;
|
||||
height: auto;
|
||||
margin: -0.75rem auto 0.4rem auto;
|
||||
filter: drop-shadow(0 4px 12px rgba(0,0,0,0.15));
|
||||
transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
|
||||
.legal-mascot:hover {
|
||||
transform: scale(1.06) translateY(-4px) rotate(1.5deg);
|
||||
}
|
||||
|
||||
.selfhost-mascot {
|
||||
display: block;
|
||||
width: 300px;
|
||||
height: auto;
|
||||
margin: 0 auto;
|
||||
filter: drop-shadow(0 4px 12px rgba(0,0,0,0.15));
|
||||
transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
|
||||
.selfhost-mascot:hover {
|
||||
transform: scale(1.05) translateY(-4px) rotate(-1deg);
|
||||
}
|
||||
|
||||
.status-ring {
|
||||
position: absolute;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
border-radius: 50%;
|
||||
background: oklch(0.68 0.13 150 / 0.05);
|
||||
border: 2px dashed oklch(0.68 0.13 150 / 0.25);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.status-ring.active-pulse {
|
||||
animation: radar-pulse 3s cubic-bezier(0.25, 0.8, 0.25, 1) infinite;
|
||||
}
|
||||
|
||||
@keyframes radar-pulse {
|
||||
0% {
|
||||
transform: scale(0.85);
|
||||
opacity: 0.9;
|
||||
border-color: oklch(0.68 0.13 150 / 0.3);
|
||||
box-shadow: 0 0 0 0px oklch(0.68 0.13 150 / 0.15);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.18);
|
||||
opacity: 0.35;
|
||||
border-color: oklch(0.75 0.10 150 / 0.2);
|
||||
box-shadow: 0 0 0 15px oklch(0.75 0.10 150 / 0);
|
||||
}
|
||||
100% {
|
||||
transform: scale(0.85);
|
||||
opacity: 0.9;
|
||||
border-color: oklch(0.68 0.13 150 / 0.3);
|
||||
box-shadow: 0 0 0 0px oklch(0.68 0.13 150 / 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
.join-card-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.join-card-actions .btn {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
padding: 1.1rem;
|
||||
font-size: 0.88rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.join-card-actions .btn svg {
|
||||
margin-right: 0.3rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.join-card-actions .btn img {
|
||||
margin-right: 0.3rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background: var(--accent-green);
|
||||
color: var(--text-on-green) !important;
|
||||
box-shadow: 0 10px 15px -3px oklch(0.68 0.13 150 / 0.3);
|
||||
}
|
||||
|
||||
.btn-success:hover {
|
||||
background: oklch(0.60 0.12 150);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 20px 25px -5px oklch(0.68 0.13 150 / 0.4);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,332 @@
|
||||
/* --- Language Toggle --- */
|
||||
html.lang-de [lang="en"] {
|
||||
display: none !important;
|
||||
}
|
||||
html:not(.lang-de) [lang="de"] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* --- Modern Glassmorphic Language Selector --- */
|
||||
.lang-select-container {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background: oklch(0.27 0.035 130 / 0.4);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: 9999px;
|
||||
padding: 6px 14px;
|
||||
transition: background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1), border-color 0.3s cubic-bezier(0.4, 0, 0.2, 1), color 0.3s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.lang-select-container:hover {
|
||||
background: oklch(0.27 0.035 130 / 0.6);
|
||||
border-color: oklch(0.68 0.13 150 / 0.3);
|
||||
color: var(--text);
|
||||
box-shadow: 0 0 15px oklch(0.68 0.13 150 / 0.1);
|
||||
}
|
||||
|
||||
.lang-select-container .globe-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
flex-shrink: 0;
|
||||
opacity: 0.8;
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
|
||||
.lang-select-container:hover .globe-icon {
|
||||
transform: rotate(15deg);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.lang-select-container .chevron-icon {
|
||||
position: absolute;
|
||||
right: 14px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
pointer-events: none;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.lang-select-container:hover .chevron-icon {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.lang-dropdown {
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
background: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
font-family: inherit;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
color: currentColor;
|
||||
cursor: pointer;
|
||||
padding: 0 28px 0 0;
|
||||
margin: 0;
|
||||
width: auto;
|
||||
min-width: 110px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@supports (appearance: base-select) {
|
||||
.lang-dropdown::picker(select) {
|
||||
appearance: base-select;
|
||||
background-color: oklch(0.20 0.025 140);
|
||||
border: 1px solid oklch(0.32 0.03 125);
|
||||
border-radius: 8px;
|
||||
padding: 4px;
|
||||
font-family: 'Twemoji Country Flags', inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.lang-dropdown option {
|
||||
background: oklch(0.20 0.025 140);
|
||||
color: white;
|
||||
font-family: inherit;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* Light theme: the pill was designed on the dark glass recipe — by day it
|
||||
becomes frosted white so it sits in the same family as the buttons. */
|
||||
html.theme-light .lang-select-container {
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
border-color: oklch(0.28 0.035 140 / 0.14);
|
||||
}
|
||||
|
||||
html.theme-light .lang-select-container:hover {
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
border-color: oklch(0.56 0.13 150 / 0.35);
|
||||
box-shadow: 0 0 15px oklch(0.56 0.13 150 / 0.12);
|
||||
}
|
||||
|
||||
html.theme-light .lang-dropdown option {
|
||||
background: #ffffff;
|
||||
color: oklch(0.21 0.03 140);
|
||||
}
|
||||
|
||||
@supports (appearance: base-select) {
|
||||
html.theme-light .lang-dropdown::picker(select) {
|
||||
background-color: #ffffff;
|
||||
border-color: oklch(0.28 0.035 140 / 0.18);
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Hamburger Menu --- */
|
||||
.hamburger {
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text);
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
padding: 0.25rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* --- Feature Cards: subtle differentiation --- */
|
||||
.feature-card:nth-child(1) { --card-accent: oklch(0.68 0.13 150 / 0.06); }
|
||||
.feature-card:nth-child(2) { --card-accent: oklch(0.62 0.14 45 / 0.06); }
|
||||
.feature-card:nth-child(3) { --card-accent: oklch(0.68 0.13 150 / 0.06); }
|
||||
.feature-card:nth-child(4) { --card-accent: oklch(0.75 0.10 150 / 0.06); }
|
||||
.feature-card:nth-child(5) { --card-accent: oklch(0.68 0.14 45 / 0.06); }
|
||||
.feature-card:nth-child(6) { --card-accent: oklch(0.62 0.14 45 / 0.06); }
|
||||
.feature-card:nth-child(7) { --card-accent: oklch(0.62 0.14 45 / 0.06); }
|
||||
.feature-card:nth-child(8) { --card-accent: oklch(0.68 0.13 150 / 0.06); }
|
||||
|
||||
.feature-card {
|
||||
background: linear-gradient(135deg, var(--card-accent, transparent), oklch(0.27 0.035 130 / 0.45));
|
||||
}
|
||||
|
||||
/* --- Compatibility Section --- */
|
||||
.compat-section {
|
||||
padding: 0.5rem 0 2.5rem 0;
|
||||
text-align: center;
|
||||
background: transparent;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.compat-mascot {
|
||||
display: block;
|
||||
margin: 0 auto 0.3rem auto;
|
||||
max-width: 270px;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
|
||||
.compat-mascot:hover {
|
||||
transform: scale(1.05) translateY(-5px) rotate(1deg);
|
||||
}
|
||||
|
||||
.compat-heading {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-muted);
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 1.4rem;
|
||||
}
|
||||
|
||||
.compat-row {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 2.4rem 3rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.compat-logo {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-muted);
|
||||
transition: opacity 0.3s;
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.compat-logo:hover {
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.compat-logo img {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.compat-logo img.compat-logo-wide {
|
||||
width: 52px;
|
||||
height: 28px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.compat-logo img.compat-logo-disneyplus-mark {
|
||||
width: 60px;
|
||||
height: 30px;
|
||||
object-fit: contain;
|
||||
filter: brightness(0) saturate(100%) invert(63%) sepia(12%) saturate(621%) hue-rotate(203deg) brightness(92%) contrast(88%);
|
||||
}
|
||||
|
||||
html.theme-light .compat-logo img.compat-logo-disneyplus-mark {
|
||||
filter: none;
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
.compat-section {
|
||||
padding: 0 0 1.6rem;
|
||||
}
|
||||
|
||||
.compat-mascot {
|
||||
max-width: 220px;
|
||||
margin-bottom: 0.1rem;
|
||||
}
|
||||
|
||||
.compat-heading {
|
||||
font-size: 0.92rem;
|
||||
line-height: 1.55;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.compat-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 1.55rem 0.75rem;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.compat-logo {
|
||||
gap: 0.35rem;
|
||||
font-size: 0.72rem;
|
||||
}
|
||||
|
||||
.compat-logo img {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
}
|
||||
|
||||
.compat-logo img.compat-logo-wide {
|
||||
width: 48px;
|
||||
height: 26px;
|
||||
}
|
||||
|
||||
.compat-logo img.compat-logo-disneyplus-mark {
|
||||
width: 56px;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.compat-logo-jellyfin {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.compat-more {
|
||||
grid-column: 1 / -1;
|
||||
justify-self: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.compat-more {
|
||||
opacity: 0.4;
|
||||
cursor: help;
|
||||
flex-direction: row;
|
||||
gap: 0.35rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.compat-more:hover {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.compat-more-icon {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
color: var(--accent);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.compat-tooltip {
|
||||
display: none;
|
||||
position: absolute;
|
||||
bottom: calc(100% + 10px);
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: var(--card);
|
||||
color: var(--text-muted);
|
||||
padding: 0.6rem 0.9rem;
|
||||
border-radius: 8px;
|
||||
font-size: 0.72rem;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
border: 1px solid var(--glass-border);
|
||||
box-shadow: 0 8px 20px rgba(0,0,0,0.4);
|
||||
z-index: 10;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.compat-tooltip::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
border: 6px solid transparent;
|
||||
border-top-color: var(--glass-border);
|
||||
}
|
||||
|
||||
.compat-more:hover .compat-tooltip {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/* Initial mobile shell for the deferred interactive hero demo. */
|
||||
@media (max-width: 900px) {
|
||||
.hero-grid > .hero-mockup-wrapper {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.hero-github-cta {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.btn-demo-mobile {
|
||||
display: inline-flex;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
/* --- Section collapse (FAQ, self-hosting): closed by default to keep the page short --- */
|
||||
.section-collapse {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.faq-section-collapse {
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.collapse-summary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.6rem;
|
||||
list-style: none;
|
||||
cursor: pointer;
|
||||
font-weight: 700;
|
||||
font-size: 1rem;
|
||||
color: var(--accent);
|
||||
background: oklch(0.68 0.13 150 / 0.08);
|
||||
border: 1px solid oklch(0.68 0.13 150 / 0.3);
|
||||
border-radius: 99px;
|
||||
padding: 0.85rem 2rem;
|
||||
max-width: 360px;
|
||||
margin: 0 auto;
|
||||
transition: background 0.3s, box-shadow 0.3s, transform 0.3s;
|
||||
}
|
||||
|
||||
.collapse-summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.collapse-summary:hover {
|
||||
background: oklch(0.68 0.13 150 / 0.16);
|
||||
box-shadow: 0 0 20px oklch(0.68 0.13 150 / 0.25);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.collapse-summary .collapse-chevron {
|
||||
flex-shrink: 0;
|
||||
transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
.section-collapse[open] .collapse-chevron {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.collapse-label-hide { display: none; }
|
||||
.section-collapse[open] .collapse-label-show { display: none; }
|
||||
.section-collapse[open] .collapse-label-hide { display: inline; }
|
||||
|
||||
.section-collapse[open] .collapse-summary {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.section-collapse[open] .collapse-content {
|
||||
animation: collapse-content-in 0.4s ease-out;
|
||||
}
|
||||
|
||||
@keyframes collapse-content-in {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.section-collapse[open] .collapse-content {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- FAQ Section --- */
|
||||
|
||||
.faq-grid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.faq-item {
|
||||
background: oklch(0.27 0.035 130 / 0.45);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: 16px;
|
||||
padding: 1.5rem 2rem;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.faq-item:hover {
|
||||
border-color: oklch(0.68 0.13 150 / 0.3);
|
||||
box-shadow: 0 8px 20px oklch(0.68 0.13 150 / 0.1);
|
||||
}
|
||||
|
||||
.faq-item[open] {
|
||||
border-color: oklch(0.68 0.13 150 / 0.4);
|
||||
background: oklch(0.27 0.035 130 / 0.65);
|
||||
}
|
||||
|
||||
.faq-item summary {
|
||||
font-weight: 700;
|
||||
font-size: 1.05rem;
|
||||
color: var(--text);
|
||||
list-style: none;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.faq-item summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.faq-item summary::after {
|
||||
content: '+';
|
||||
font-size: 1.4rem;
|
||||
color: var(--accent);
|
||||
font-weight: 300;
|
||||
transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
flex-shrink: 0;
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
.faq-item[open] summary::after {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.faq-item p {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.6;
|
||||
margin-top: 1rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
/* --- Focus states for interactive cards --- */
|
||||
.feature-card:focus-visible,
|
||||
.use-case-card:focus-visible,
|
||||
.compat-logo:focus-visible {
|
||||
outline: 2px solid var(--accent);
|
||||
outline-offset: 4px;
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
.feature-card,
|
||||
.use-case-card,
|
||||
.compat-logo,
|
||||
section[id],
|
||||
header[id] {
|
||||
scroll-margin-top: 100px;
|
||||
}
|
||||
|
||||
/* --- Screen-reader-only (also crawlable by search engines) --- */
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/* --- Skip-to-content accessibility link --- */
|
||||
.skip-link {
|
||||
position: absolute;
|
||||
left: -9999px;
|
||||
top: 0;
|
||||
background: var(--accent);
|
||||
color: var(--text-on-green);
|
||||
padding: 0.75rem 1.25rem;
|
||||
border-radius: 0 0 8px 0;
|
||||
font-weight: 700;
|
||||
z-index: 9999;
|
||||
text-decoration: none;
|
||||
}
|
||||
.skip-link:focus {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
/* --- Reduced motion support --- */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*, *::before, *::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
.join-status-pulse { animation: none; }
|
||||
.status-ring.active-pulse { animation: none; }
|
||||
}
|
||||
|
||||
/* --- will-change hints for animated elements --- */
|
||||
.join-status-pulse,
|
||||
.status-ring.active-pulse,
|
||||
.hero-mockup-wrapper,
|
||||
.cta-group .btn {
|
||||
will-change: transform, opacity;
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.join-status-pulse,
|
||||
.status-ring.active-pulse,
|
||||
.hero-mockup-wrapper,
|
||||
.cta-group .btn {
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,462 @@
|
||||
/* --- Use Cases / Anwendungsszenarien Section --- */
|
||||
.use-cases-section {
|
||||
padding: 6rem 0;
|
||||
position: relative;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.use-cases-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
||||
gap: 2rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.use-cases-feature-grid {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.use-cases-feature-grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.use-cases-feature-grid {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
gap: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.use-case-card {
|
||||
background: oklch(0.27 0.035 130 / 0.4);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 16px;
|
||||
padding: 1.2rem 1.6rem 1.7rem 1.6rem;
|
||||
text-align: left;
|
||||
transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.3s ease;
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.8rem;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.use-case-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 3px;
|
||||
background: linear-gradient(90deg, transparent, var(--card-accent-border, var(--accent)), transparent);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.use-case-card:nth-child(1) { --card-accent-border: oklch(0.68 0.13 150); }
|
||||
.use-case-card:nth-child(2) { --card-accent-border: oklch(0.62 0.14 45); }
|
||||
.use-case-card:nth-child(3) { --card-accent-border: oklch(0.75 0.10 150); }
|
||||
|
||||
|
||||
.use-case-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.35);
|
||||
border-color: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
.use-case-card:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.use-case-image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 272px;
|
||||
height: 150px;
|
||||
object-fit: contain;
|
||||
border-radius: 8px;
|
||||
margin: 0 auto 0 auto;
|
||||
}
|
||||
|
||||
.cta-github-mascot {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 250px;
|
||||
height: auto;
|
||||
margin: 1.5rem auto;
|
||||
}
|
||||
|
||||
.features-questions-mascot {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 250px;
|
||||
height: auto;
|
||||
margin: -1.5rem auto 0.2rem auto;
|
||||
transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
|
||||
.features-questions-mascot:hover {
|
||||
transform: scale(1.05) translateY(-4px) rotate(-1.5deg);
|
||||
}
|
||||
|
||||
.comparison-mascot {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 260px;
|
||||
height: auto;
|
||||
margin: -2.2rem auto 0.2rem auto;
|
||||
transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
|
||||
.comparison-mascot:hover {
|
||||
transform: scale(1.05) translateY(-4px) rotate(1deg);
|
||||
}
|
||||
|
||||
.use-case-icon {
|
||||
font-size: 1.6rem;
|
||||
margin-right: 0.6rem;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
filter: drop-shadow(0 2px 8px rgba(0,0,0,0.2));
|
||||
}
|
||||
|
||||
.use-case-card h3 {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
margin: 0;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.use-case-card p {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.6;
|
||||
color: var(--text-muted);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* --- Comparison Section --- */
|
||||
.comparison-section {
|
||||
padding: 6rem 0;
|
||||
background: transparent;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
#self-hosting,
|
||||
#faq,
|
||||
#faq + section,
|
||||
footer {
|
||||
background: var(--section-tint-strong);
|
||||
}
|
||||
|
||||
#self-hosting {
|
||||
border-top: 1px solid var(--glass-border);
|
||||
}
|
||||
|
||||
.comparison-table-wrapper {
|
||||
overflow-x: auto;
|
||||
background: oklch(0.27 0.035 130 / 0.35);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.comparison-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
text-align: left;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.comparison-table th,
|
||||
.comparison-table td {
|
||||
padding: 1.25rem 1.5rem;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
.comparison-table th {
|
||||
background: oklch(0.20 0.025 140 / 0.6);
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.8rem;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
|
||||
.comparison-table th.highlight,
|
||||
.comparison-table td.highlight {
|
||||
background: oklch(0.68 0.13 150 / 0.08);
|
||||
}
|
||||
|
||||
.comparison-table tbody tr:hover {
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
}
|
||||
|
||||
.comparison-table tbody tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.feat-name {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.feat-name strong {
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.feat-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.check {
|
||||
color: oklch(0.68 0.13 150);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.cross {
|
||||
color: oklch(0.68 0.17 25);
|
||||
font-weight: 600;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.comparison-table th,
|
||||
.comparison-table td {
|
||||
padding: 1rem;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* On phones the 3-column table is turned into stacked cards so there is
|
||||
no horizontal scrolling — one card per feature, KoalaSync above Teleparty. */
|
||||
@media (max-width: 600px) {
|
||||
.comparison-table-wrapper {
|
||||
overflow-x: visible;
|
||||
background: transparent;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
backdrop-filter: none;
|
||||
-webkit-backdrop-filter: none;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.comparison-table {
|
||||
display: block;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
/* Visually hide the header row but keep it for screen readers. */
|
||||
.comparison-table thead {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.comparison-table tbody,
|
||||
.comparison-table tr,
|
||||
.comparison-table td {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.comparison-table tbody tr {
|
||||
background: oklch(0.27 0.035 130 / 0.35);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 14px;
|
||||
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
|
||||
margin-bottom: 1rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.comparison-table tbody tr:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.comparison-table td {
|
||||
padding: 0.85rem 1.1rem;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
.comparison-table tbody tr td:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* Feature name acts as the card header. */
|
||||
.comparison-table td.feat-name {
|
||||
background: oklch(0.20 0.025 140 / 0.55);
|
||||
padding: 0.9rem 1.1rem;
|
||||
}
|
||||
|
||||
/* Room is back on the card, so show the description again. */
|
||||
.feat-desc {
|
||||
display: block;
|
||||
margin-top: 0.15rem;
|
||||
}
|
||||
|
||||
/* Label each value cell with the product it belongs to. */
|
||||
.comparison-table td.check,
|
||||
.comparison-table td.cross {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.comparison-table td.check::before,
|
||||
.comparison-table td.cross::before {
|
||||
flex: 0 0 5.5rem;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
color: var(--text-muted);
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.comparison-table td.check::before,
|
||||
.comparison-table td.cross::before {
|
||||
content: attr(data-label);
|
||||
}
|
||||
|
||||
/* Keep the KoalaSync row subtly highlighted, like on desktop. */
|
||||
.comparison-table td.check.highlight {
|
||||
background: oklch(0.68 0.13 150 / 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
html.theme-light .comparison-section {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
html.theme-light .comparison-table tbody tr {
|
||||
background: #ffffff;
|
||||
border-color: oklch(0.20 0.025 140 / 0.1);
|
||||
box-shadow: 0 8px 20px oklch(0.20 0.025 140 / 0.08);
|
||||
}
|
||||
|
||||
html.theme-light .comparison-table tbody tr:hover {
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
html.theme-light .comparison-table td {
|
||||
border-bottom-color: oklch(0.20 0.025 140 / 0.08);
|
||||
}
|
||||
|
||||
html.theme-light .comparison-table td.feat-name {
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
html.theme-light .comparison-table td.check.highlight {
|
||||
background: oklch(0.52 0.12 150 / 0.07);
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Footnotes & Source Links --- */
|
||||
.source-link {
|
||||
color: var(--accent);
|
||||
text-decoration: none;
|
||||
font-size: 0.72rem;
|
||||
font-weight: 700;
|
||||
margin-left: 0.25rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
vertical-align: super;
|
||||
opacity: 0.82;
|
||||
transition: opacity 0.2s ease, color 0.2s ease, transform 0.2s ease;
|
||||
}
|
||||
|
||||
.source-link:hover {
|
||||
opacity: 1;
|
||||
color: oklch(0.75 0.10 150);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.table-footnotes {
|
||||
padding: 0.8rem 1.25rem;
|
||||
background: oklch(0.20 0.025 140 / 0.45);
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
||||
font-size: 0.72rem;
|
||||
color: var(--text-muted);
|
||||
line-height: 1.5;
|
||||
opacity: 0.65;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
/* Link from the comparison table to the fuller guides/alternatives hub */
|
||||
.comparison-guides-link {
|
||||
text-align: center;
|
||||
margin: 2rem 0 0;
|
||||
}
|
||||
|
||||
.comparison-guides-link a {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.45rem;
|
||||
color: var(--accent);
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
transition: gap 0.2s ease, opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.comparison-guides-link a:hover {
|
||||
gap: 0.7rem;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.table-footnotes:hover {
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
.table-footnotes p {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.table-footnotes p:not(:last-child) {
|
||||
margin-bottom: 0.4rem;
|
||||
}
|
||||
|
||||
.table-footnotes a {
|
||||
color: var(--accent);
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
transition: color 0.2s ease, text-decoration 0.2s ease;
|
||||
}
|
||||
|
||||
.table-footnotes a:hover {
|
||||
color: oklch(0.75 0.10 150);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.table-footnotes sup {
|
||||
font-weight: 700;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.table-footnotes {
|
||||
padding: 0.75rem 1rem;
|
||||
font-size: 0.68rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
/* --- Legal Pages --- */
|
||||
.legal-content {
|
||||
max-width: 800px;
|
||||
margin: 8rem auto 4rem auto;
|
||||
padding: 0 2rem;
|
||||
}
|
||||
|
||||
.legal-card {
|
||||
background: var(--card);
|
||||
padding: 3rem;
|
||||
border-radius: 24px;
|
||||
border: 1px solid var(--glass-border);
|
||||
}
|
||||
|
||||
.legal-card h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.legal-card h2 {
|
||||
font-size: 1.25rem;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.legal-card p {
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
/* Reset global section padding for legal sections specifically */
|
||||
.legal-card section {
|
||||
padding: 0 !important;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
/* --- Join Page Specifics --- */
|
||||
.join-card {
|
||||
max-width: 450px;
|
||||
margin: 6rem auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.room-badge {
|
||||
display: inline-block;
|
||||
background: oklch(0.68 0.13 150 / 0.1);
|
||||
color: var(--accent);
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 99px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 1rem;
|
||||
border: 1px solid oklch(0.68 0.13 150 / 0.2);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.hero {
|
||||
align-items: center;
|
||||
padding-top: 5.5rem;
|
||||
padding-bottom: 3.5rem;
|
||||
min-height: 100svh;
|
||||
}
|
||||
|
||||
.hero-grid {
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.hero-grid {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
text-align: center;
|
||||
}
|
||||
.step {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
text-align: left;
|
||||
}
|
||||
.hero-text h1 {
|
||||
font-size: 2.5rem;
|
||||
max-width: none;
|
||||
}
|
||||
.cta-group {
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.nav-links {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
flex-direction: column;
|
||||
background: oklch(0.20 0.025 140 / 0.95);
|
||||
backdrop-filter: blur(12px);
|
||||
padding: 1rem 2rem;
|
||||
gap: 1rem;
|
||||
border-bottom: 1px solid var(--glass-border);
|
||||
margin-left: 0;
|
||||
}
|
||||
.nav-links.open {
|
||||
display: flex;
|
||||
}
|
||||
.nav-right {
|
||||
margin-left: auto;
|
||||
}
|
||||
.hamburger {
|
||||
display: inline-flex !important;
|
||||
}
|
||||
nav .container {
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
.logo-area {
|
||||
gap: 0.25rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
.logo-area img {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
margin: 0;
|
||||
}
|
||||
.lang-select-container {
|
||||
padding: 4px 10px;
|
||||
gap: 4px;
|
||||
}
|
||||
.lang-select-container .globe-icon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
.lang-dropdown {
|
||||
font-size: 0.7rem;
|
||||
padding: 0 6px 0 0;
|
||||
}
|
||||
.lang-select-container .chevron-icon {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
.legal-card {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 769px) and (max-height: 920px) {
|
||||
.hero {
|
||||
padding-top: clamp(7.5rem, 10vh, 9rem);
|
||||
padding-bottom: clamp(1.5rem, 3vh, 2.75rem);
|
||||
}
|
||||
|
||||
.hero-grid {
|
||||
min-height: calc(100svh - 9.5rem);
|
||||
}
|
||||
}
|
||||
|
||||
/* Extra-narrow phones: make sure logo, language picker and the menu button
|
||||
never crowd each other in the header (kept comfortable down to ~320px). */
|
||||
@media (max-width: 380px) {
|
||||
nav .container {
|
||||
padding: 0 0.4rem;
|
||||
}
|
||||
.nav-right {
|
||||
gap: 0.35rem;
|
||||
}
|
||||
.logo-area {
|
||||
font-size: 1rem;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
<link rel="dns-prefetch" href="https://addons.mozilla.org">
|
||||
<link rel="dns-prefetch" href="https://github.com">
|
||||
|
||||
<link rel="preload" href="{{ASSET_PATH}}style.min.css" as="style">
|
||||
<link rel="stylesheet" href="{{ASSET_PATH}}landing.min.css">
|
||||
<link rel="stylesheet" href="{{ASSET_PATH}}landing-demo.min.css" media="(min-width: 769px)" data-landing-demo>
|
||||
<link rel="preload" as="image" href="{{ASSET_PATH}}assets/LookDownKoala.avif" imagesrcset="{{ASSET_PATH}}assets/LookDownKoala-1x.avif 90w, {{ASSET_PATH}}assets/LookDownKoala.avif 205w" imagesizes="90px" type="image/avif" fetchpriority="high">
|
||||
<link rel="stylesheet" href="{{ASSET_PATH}}style.min.css">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="{{ASSET_PATH}}assets/favicon-16x16.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="{{ASSET_PATH}}assets/favicon-32x32.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="{{ASSET_PATH}}assets/icon-192x192.png">
|
||||
@@ -209,7 +209,7 @@
|
||||
</style>
|
||||
</noscript>
|
||||
</head>
|
||||
<body>
|
||||
<body data-deferred-css="{{ASSET_PATH}}landing-deferred.min.css">
|
||||
<div class="scroll-progress-bar"></div>
|
||||
|
||||
<a class="skip-link" href="#main-content">Skip to main content</a>
|
||||
|
||||
Reference in New Issue
Block a user