diff --git a/docs/AI_INIT.md b/docs/AI_INIT.md index 8a6674c..13b4593 100644 --- a/docs/AI_INIT.md +++ b/docs/AI_INIT.md @@ -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`. diff --git a/website/README.md b/website/README.md index d9b640c..b982136 100644 --- a/website/README.md +++ b/website/README.md @@ -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/`. diff --git a/website/app.js b/website/app.js index f11d24a..6de2394 100644 --- a/website/app.js +++ b/website/app.js @@ -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; diff --git a/website/build.cjs b/website/build.cjs index 748a142..cc09301 100644 --- a/website/build.cjs +++ b/website/build.cjs @@ -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(/(]*?\bhref=")((?:\.\.\/)*\/?)style\.min\.css"/g, (m, before, prefix) => { return `${before}${prefix}${styleName}" integrity="${styleSRI}" crossorigin="anonymous"`; }); + html = html.replace(/(]*?\bhref=")((?:\.\.\/)*\/?)landing\.min\.css"/g, (m, before, prefix) => { + return `${before}${prefix}${landingStyleName}" integrity="${landingStyleSRI}" crossorigin="anonymous"`; + }); + html = html.replace(/(]*?\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(/(]*?\bsrc=")((?:\.\.\/)*\/?)app\.min\.js"/g, (m, before, prefix) => { return `${before}${prefix}${appName}" integrity="${appSRI}" crossorigin="anonymous"`; }); diff --git a/website/style.css b/website/style.css index ad55856..a5eebf3 100644 --- a/website/style.css +++ b/website/style.css @@ -1,6576 +1,18 @@ -/* KoalaSync Design System - Privacy-Focused (No External Assets) */ - -@font-face { - font-family: 'Twemoji Country Flags'; - unicode-range: U+1F1E6-1F1FF, U+1F3F4, U+E0062-E0063, U+E0065, U+E0067, U+E006C, U+E006E, U+E0073-E0074, U+E0077, U+E007F; - src: url('assets/TwemojiCountryFlags.woff2') format('woff2'); - font-display: swap; -} - -:root { - /* v3.0.0 "The Greens Update" nature palette */ - --bg-base: oklch(0.20 0.025 140); - --surface: oklch(0.27 0.035 130); - --surface-deep: oklch(0.16 0.025 135); - --surface-alt: oklch(0.23 0.03 130); - --border-soft: rgba(255, 255, 255, 0.06); - --border-strong: oklch(0.32 0.03 125); - --text-primary: oklch(0.96 0.01 90); - --text-secondary: oklch(0.78 0.02 90); - --accent-green: oklch(0.68 0.13 150); - --accent-green-hover: oklch(0.75 0.13 150); - --accent-terracotta: oklch(0.62 0.14 45); - --danger: oklch(0.55 0.15 25); - --text-on-green: oklch(0.14 0.02 90); - - /* Legacy aliases used throughout the stylesheet */ - --bg: var(--bg-base); - --card: var(--surface); - --accent: var(--accent-green); - --accent-glow: oklch(0.68 0.13 150 / 0.3); - --text: var(--text-primary); - --text-muted: var(--text-secondary); - --success: var(--accent-green); - --glass: oklch(0.27 0.035 130 / 0.4); - --glass-border: var(--border-soft); - --section-tint: transparent; - --section-tint-strong: oklch(0.20 0.025 140 / 0.28); - --card-surface: oklch(0.27 0.035 130 / 0.45); - --card-border: rgba(255, 255, 255, 0.08); - color-scheme: dark; -} - -.extension-mockup, -.illus-popup-card, -.illus-player-card { - --bg: oklch(0.20 0.025 140) !important; - --card: oklch(0.27 0.035 130) !important; - --accent: oklch(0.68 0.13 150) !important; - --accent-glow: oklch(0.68 0.13 150 / 0.3) !important; - --text: oklch(0.96 0.01 90) !important; - --text-muted: oklch(0.78 0.02 90) !important; - --success: oklch(0.68 0.13 150) !important; - --error: oklch(0.55 0.15 25) !important; - --glass: oklch(0.27 0.035 130 / 0.4) !important; - --glass-border: rgba(255, 255, 255, 0.05) !important; - --section-tint: rgba(255, 255, 255, 0.02) !important; - --section-tint-strong: oklch(0.20 0.025 140 / 0.4) !important; - --card-surface: oklch(0.27 0.035 130 / 0.45) !important; - --card-border: rgba(255, 255, 255, 0.08) !important; -} - -.invite-banner { - background: var(--accent); - color: var(--text-on-green); - padding: 0.75rem 0; - text-align: center; - font-weight: 600; - font-size: 0.9rem; - position: relative; - z-index: 2000; - box-shadow: 0 4px 12px rgba(0,0,0,0.3); -} - -.btn-banner { - background: white; - color: var(--accent); - padding: 4px 12px; - border-radius: 6px; - text-decoration: none; - font-size: 0.8rem; - font-weight: 700; - margin-left: 12px; - cursor: pointer; - border: none; - transition: transform 0.2s; -} - -.btn-banner:hover { - transform: scale(1.05); -} - -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; - border-radius: 6px; -} - -html { - scroll-behavior: smooth; -} - -body { - font-family: 'Twemoji Country Flags', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; - background-color: var(--bg); - color: var(--text); - line-height: 1.6; - overflow-x: hidden; - position: relative; -} - -/* --- Animated Bamboo Forest Background (v3 Greens Update) --- */ -/* Registered so wind gusts can TRANSITION these instead of jumping */ -@property --sway { - syntax: ''; - initial-value: 1.2deg; - inherits: true; -} - -@property --leaf-drift { - syntax: ''; - initial-value: 26px; - inherits: true; -} - -.bg-nature { - --sway: 1.2deg; - --leaf-drift: 26px; - transition: --sway 1.6s ease-in-out, --leaf-drift 1.6s ease-in-out; -} - -.bg-nature.gusting { - --sway: 3.4deg; - --leaf-drift: 68px; -} - -.bg-nature { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: -1; - overflow: hidden; - pointer-events: none; - background: - radial-gradient(circle at 18% 10%, oklch(0.26 0.05 145 / 0.35), transparent 34rem), - radial-gradient(circle at 78% 68%, oklch(0.24 0.04 140 / 0.3), transparent 38rem), - linear-gradient(180deg, oklch(0.20 0.025 140) 0%, oklch(0.17 0.03 142) 45%, oklch(0.19 0.03 140) 100%); -} - -/* (a) Distant bamboo layer: blurred, scroll-parallaxed by app.js */ -#bamboo-far { - position: absolute; - inset: 0; - will-change: transform; -} - -#bamboo-far .bamboo-stalk { - position: absolute; - /* 200% covers the upward parallax drift (max 0.55vh on the near layer) - with margin while keeping the blurred layer smaller than the old - 240% overscan. Spare coverage sits below, where the drift needs it. */ - top: -40%; - height: 200%; - border-radius: 999px; - filter: blur(4.5px); - transition: filter 1.2s ease; - background: - repeating-linear-gradient(180deg, - transparent 0, transparent 108px, - oklch(0.40 0.06 145 / 0.22) 108px, oklch(0.40 0.06 145 / 0.22) 112px), - linear-gradient(180deg, oklch(0.34 0.05 145 / 0.4), oklch(0.24 0.04 140 / 0.16)); -} - -#bamboo-far .far-1 { left: 2%; width: 26px; } -#bamboo-far .far-2 { left: 7.5%; width: 18px; } -#bamboo-far .far-3 { right: 4%; width: 30px; } -#bamboo-far .far-4 { left: 13%; width: 20px; } -#bamboo-far .far-5 { right: 10.5%; width: 16px; } -/* Two whisper-quiet trunks soften the empty centre without touching the - content: wide apart, thin, and at the lowest opacity in the scene. */ -#bamboo-far .far-6 { left: 39%; width: 12px; opacity: 0.55; } -#bamboo-far .far-7 { right: 42%; width: 14px; opacity: 0.6; } - -/* Two barely-there trunks break up the empty middle without competing with - the content. They share the far layer's single parallax transform. */ -#bamboo-far::before, -#bamboo-far::after { - content: ''; - position: absolute; - top: -40%; - height: 200%; - border-radius: 999px; - filter: blur(6.5px); - opacity: 0.3; - background: linear-gradient(180deg, oklch(0.34 0.05 145 / 0.3), oklch(0.23 0.04 140 / 0.1)); -} - -#bamboo-far::before { left: 24%; width: 13px; } -#bamboo-far::after { right: 25%; width: 17px; } - -/* Depth of field: the far layer blurs further as you descend (stepped by - JS on threshold crossings, never per frame) */ -.bg-nature.depth-mid #bamboo-far .bamboo-stalk { filter: blur(5.5px); } -.bg-nature.depth-deep #bamboo-far .bamboo-stalk { filter: blur(6.5px); } - -/* (a2) Mid bamboo layer: sits between far and near with its own parallax - rate (app.js) and half the near layer's sway, so the three layers drift - apart subtly instead of moving as one sheet. Landing page only — the - static pages keep the lighter two-layer scene. */ -#bamboo-mid { - position: absolute; - inset: 0; - will-change: transform; -} - -#bamboo-mid .bamboo-stalk { - position: absolute; - top: -40%; - height: 200%; - border-radius: 999px; - filter: blur(2px); - transition: filter 1.2s ease; - /* Segment rings + a soft left-of-centre sheen give the stalks culm - structure without any extra elements; the blur keeps both faint. */ - background: - repeating-linear-gradient(180deg, - transparent 0, transparent 84px, - oklch(0.46 0.075 145 / 0.28) 84px, oklch(0.46 0.075 145 / 0.28) 87px), - linear-gradient(90deg, transparent 14%, oklch(0.58 0.07 145 / 0.14) 38%, transparent 68%), - linear-gradient(180deg, oklch(0.36 0.06 145 / 0.45), oklch(0.25 0.045 140 / 0.2)); - transform-origin: bottom center; - animation: bambooSwayHalf 13s ease-in-out infinite; -} - -#bamboo-mid .mid-1 { left: 17%; width: 15px; animation-duration: 12s; } -#bamboo-mid .mid-2 { left: 31%; width: 9px; animation-delay: -5s; opacity: 0.55; } -#bamboo-mid .mid-3 { right: 15.5%; width: 17px; animation-duration: 14.5s; animation-delay: -8s; } -#bamboo-mid .mid-4 { right: 30%; width: 8px; animation-duration: 11.5s; animation-delay: -2.5s; opacity: 0.5; } - -/* A few leaves sprout from the mid stalks (pseudo-elements, so they ride - the stalk's sway for free). The slim centre stalks stay bare. */ -#bamboo-mid .bamboo-stalk::before, -#bamboo-mid .bamboo-stalk::after { - content: ''; - position: absolute; - width: 24px; - height: 11px; - border-radius: 0 100% 0 100%; - background: oklch(0.5 0.1 145 / 0.32); -} - -#bamboo-mid .mid-1::before { top: 31%; left: 13px; transform: rotate(20deg); } -#bamboo-mid .mid-1::after { top: 55%; left: -26px; transform: rotate(-158deg); width: 20px; height: 9px; } -#bamboo-mid .mid-3::before { top: 26%; left: -27px; transform: rotate(-156deg); } -#bamboo-mid .mid-3::after { top: 49%; left: 15px; transform: rotate(24deg); width: 21px; height: 10px; } -#bamboo-mid .mid-2::before, -#bamboo-mid .mid-2::after, -#bamboo-mid .mid-4::before, -#bamboo-mid .mid-4::after { - display: none; -} - -.bg-nature.depth-mid #bamboo-mid .bamboo-stalk { filter: blur(3px); } -.bg-nature.depth-deep #bamboo-mid .bamboo-stalk { filter: blur(4px); } - -@keyframes bambooSwayHalf { - 0%, 100% { transform: rotate(0); } - 50% { transform: rotate(calc(var(--sway, 1.2deg) * 0.5)); } -} - -/* (b) Near bamboo layer: sharp stalks with nodes and leaves, gentle sway */ -#bamboo-near { - position: absolute; - inset: 0; - will-change: transform; -} - -#bamboo-near .bamboo-stalk { - position: absolute; - top: -40%; - height: 200%; - border-radius: 999px; - /* Vertical sheen over the culm gradient: reads as light from the - canopy catching the rounded stalk instead of a flat strip. */ - background: - linear-gradient(90deg, transparent 12%, oklch(0.64 0.08 145 / 0.22) 36%, transparent 64%), - linear-gradient(180deg, oklch(0.42 0.075 145 / 0.6), oklch(0.27 0.05 140 / 0.3)); - transform-origin: bottom center; - animation: bambooSway 10s ease-in-out infinite; -} - -#bamboo-near .near-1 { left: 4.5%; width: 15px; animation-duration: 9.2s; } -#bamboo-near .near-2 { right: 7%; width: 13px; animation-duration: 11s; animation-delay: -3.5s; } -#bamboo-near .near-3 { left: 10%; width: 10px; animation-duration: 12.5s; animation-delay: -6s; opacity: 0.75; } - -#bamboo-near .bamboo-node { - position: absolute; - left: -1px; - right: -1px; - height: 3px; - border-radius: 2px; - background: oklch(0.52 0.085 145 / 0.6); -} - -#bamboo-near .node-1 { top: 28%; } -#bamboo-near .node-2 { top: 48%; } -#bamboo-near .node-3 { top: 68%; } - -#bamboo-near .bamboo-leaf { - position: absolute; - width: 26px; - height: 12px; - border-radius: 0 100% 0 100%; - background: oklch(0.55 0.11 145 / 0.4); -} - -#bamboo-near .near-1 .leaf-1 { top: 27.5%; left: 12px; transform: rotate(18deg); } -#bamboo-near .near-1 .leaf-2 { top: 47.5%; left: -28px; transform: rotate(-160deg); width: 22px; height: 10px; } -#bamboo-near .near-2 .leaf-1 { top: 44%; left: -26px; transform: rotate(-155deg); } -#bamboo-near .near-2 .leaf-2 { top: 61%; left: 11px; transform: rotate(21deg); width: 20px; height: 9px; } -#bamboo-near .near-3 .leaf-1 { top: 33%; left: 8px; transform: rotate(24deg); width: 18px; height: 9px; } - -@keyframes bambooSway { - 0%, 100% { transform: rotate(0); } - 50% { transform: rotate(var(--sway, 1.2deg)); } -} - -/* (d) Falling leaves: outer element falls + rotates, inner leaf sways */ -.fall-leaf { - position: absolute; - top: 0; - animation: leafFall 17s linear infinite; - opacity: 0; -} - -.fall-leaf i { - display: block; - width: 10px; - height: 14px; - border-radius: 0 70% 0 70%; - background: oklch(0.68 0.13 150 / 0.55); - animation: leafSway 3.2s ease-in-out infinite; -} - -.fall-leaf:nth-child(even) i { - background: oklch(0.62 0.14 45 / 0.55); -} - -.fall-leaf-1 { left: 6%; animation-duration: 15s; animation-delay: -2s; } -.fall-leaf-2 { left: 16%; animation-duration: 19s; animation-delay: -9s; } -.fall-leaf-2 i { animation-duration: 2.7s; background: oklch(0.62 0.14 45 / 0.6); } -.fall-leaf-3 { left: 28%; animation-duration: 14s; animation-delay: -5s; } -.fall-leaf-3 i { background: oklch(0.68 0.13 150 / 0.5); } -.fall-leaf-4 { left: 41%; animation-duration: 21s; animation-delay: -13s; } -.fall-leaf-4 i { animation-duration: 4s; } -.fall-leaf-5 { left: 55%; animation-duration: 16s; animation-delay: -7s; } -.fall-leaf-5 i { background: oklch(0.68 0.13 150 / 0.6); } -.fall-leaf-6 { left: 68%; animation-duration: 20s; animation-delay: -16s; } -.fall-leaf-6 i { animation-duration: 3.6s; background: oklch(0.62 0.14 45 / 0.5); } -.fall-leaf-7 { left: 81%; animation-duration: 14.5s; animation-delay: -3s; } -.fall-leaf-7 i { animation-duration: 2.9s; } -.fall-leaf-8 { left: 92%; animation-duration: 18s; animation-delay: -11s; } - -@keyframes leafFall { - 0% { transform: translateY(-40px) rotate(0); opacity: 0; } - 8% { opacity: 0.65; } - 92% { opacity: 0.5; } - 100% { transform: translateY(110vh) translateX(120px) rotate(280deg); opacity: 0; } -} - -@keyframes leafSway { - 0%, 100% { margin-left: 0; } - 50% { margin-left: var(--leaf-drift, 26px); } -} - -/* Scroll journey: JS drives the opacity of these two groups so the page - travels from sunlit canopy (top) into misty dusk (bottom). Static - defaults below keep the scene balanced without JS. */ -.bg-depth-day, -.bg-depth-dusk { - position: absolute; - inset: 0; -} - -.bg-depth-dusk { - opacity: 0.6; -} - -/* Dusk tint: darkens and warms the whole scene as you descend */ -.bg-dusk-tint { - position: absolute; - inset: 0; - opacity: 0; - background: linear-gradient(180deg, - oklch(0.10 0.03 145 / 0.15) 0%, - oklch(0.12 0.045 142 / 0.5) 65%, - oklch(0.16 0.07 55 / 0.35) 100%); -} - -/* God rays: soft light shafts falling through the canopy */ -.bg-godrays { - position: absolute; - top: -10%; - left: 0; - right: 0; - height: 75%; -} - -.godray { - position: absolute; - top: 0; - height: 115%; - background: linear-gradient(180deg, oklch(0.85 0.06 130 / 0.10), transparent 78%); - filter: blur(10px); - transform: rotate(14deg); - transform-origin: top center; - animation: rayShimmer 9s ease-in-out infinite alternate; -} - -.godray-1 { left: 16%; width: 120px; animation-duration: 8s; } -.godray-2 { left: 33%; width: 70px; animation-delay: -3s; background: linear-gradient(180deg, oklch(0.85 0.06 130 / 0.07), transparent 78%); } -.godray-3 { left: 56%; width: 140px; animation-duration: 13s; animation-delay: -6s; background: linear-gradient(180deg, oklch(0.85 0.05 110 / 0.08), transparent 75%); } - -@keyframes rayShimmer { - 0% { opacity: 0.45; } - 100% { opacity: 1; } -} - -/* (e) Canopy: dark vignette at the top plus hanging leaf arcs */ -.bg-canopy { - position: absolute; - top: 0; - left: 0; - right: 0; - height: 150px; - background: - radial-gradient(ellipse 70% 100% at 30% 0%, oklch(0.14 0.03 145 / 0.55), transparent 70%), - radial-gradient(ellipse 60% 90% at 80% 0%, oklch(0.14 0.03 145 / 0.45), transparent 70%); -} - -.canopy-arc { - position: absolute; - border-radius: 0 0 100% 100%; - background: oklch(0.32 0.06 145 / 0.45); -} - -.canopy-arc-1 { top: -3px; left: 12%; width: 52px; height: 16px; transform: rotate(-6deg); background: oklch(0.30 0.06 145 / 0.5); } -.canopy-arc-2 { top: -2px; left: 34%; width: 38px; height: 12px; transform: rotate(4deg); background: oklch(0.34 0.06 145 / 0.35); } -.canopy-arc-3 { top: -4px; left: 61%; width: 46px; height: 14px; transform: rotate(-3deg); } -.canopy-arc-4 { top: -2px; left: 84%; width: 30px; height: 10px; transform: rotate(7deg); background: oklch(0.33 0.06 145 / 0.4); } - -/* (f) Undergrowth: grass blades in the lower corners. Each blade carries a - shorter companion blade (::before) so the corners read as small tufts. */ -.bg-grass { - position: absolute; - bottom: -4px; - transform-origin: bottom; -} - -.bg-grass::before { - content: ''; - position: absolute; - bottom: 0; - width: 72%; - height: 55%; - border-radius: inherit; - background: inherit; - opacity: 0.7; - transform-origin: bottom; -} - -.bg-grass-1::before { left: -5px; transform: rotate(-13deg); } -.bg-grass-2::before { left: 4px; transform: rotate(11deg); } -.bg-grass-3::before { right: -5px; transform: rotate(12deg); } -.bg-grass-4::before { right: 4px; transform: rotate(-9deg); } - -.bg-grass-1 { left: 1.5%; width: 6px; height: 52px; border-radius: 100% 0 0 0; background: oklch(0.38 0.07 145 / 0.4); transform: rotate(-9deg); } -.bg-grass-2 { left: 3.2%; width: 5px; height: 38px; border-radius: 100% 0 0 0; background: oklch(0.35 0.07 145 / 0.3); transform: rotate(-12deg); } -.bg-grass-3 { right: 2%; width: 6px; height: 56px; border-radius: 0 100% 0 0; background: oklch(0.4 0.07 145 / 0.4); transform: rotate(8deg); } -.bg-grass-4 { right: 3.8%; width: 5px; height: 40px; border-radius: 0 100% 0 0; background: oklch(0.36 0.07 145 / 0.32); transform: rotate(11deg); } - -/* (g) Ground mist: soft drifting ellipses at the bottom edge */ -.bg-mist { - position: absolute; - bottom: -40px; - height: 130px; - filter: blur(19px); - will-change: transform; -} - -.bg-mist-1 { - left: -6%; - width: 75%; - background: radial-gradient(ellipse, oklch(0.85 0.03 145 / 0.07), transparent 70%); - animation: mistDrift 22s ease-in-out infinite alternate; -} - -.bg-mist-2 { - right: -6%; - width: 70%; - background: radial-gradient(ellipse, oklch(0.85 0.03 145 / 0.06), transparent 70%); - animation: mistDrift 27s ease-in-out infinite alternate-reverse; -} - -@keyframes mistDrift { - 0% { transform: translateX(-8%); } - 100% { transform: translateX(8%); } -} - -/* (h) Breathing horizon glow */ -.bg-horizon { - position: absolute; - inset: 0; - background: radial-gradient(ellipse 60% 100% at 50% 100%, oklch(0.62 0.14 45 / 0.14), transparent 70%); - animation: horizonBreathe 10s ease-in-out infinite; -} - -@keyframes horizonBreathe { - 0%, 100% { opacity: 0.5; } - 50% { opacity: 0.85; } -} - -/* Theme switch to light: the horizon flares up once like a sunrise */ -.bg-nature.sunrise .bg-horizon { - animation: - horizonBreathe 10s ease-in-out infinite, - sunriseFlash 1.6s ease-out; -} - -@keyframes sunriseFlash { - 0% { opacity: 0.5; } - 30% { opacity: 1; } - 100% { opacity: 0.5; } -} - -/* (i) Fireflies: the wrapper holds the base position and is what the JS - pushes away from the cursor; the inner dot pulses (transform) and - wanders (translate) independently. */ -.firefly-wrap { - position: absolute; - width: 4px; - height: 4px; -} - -.firefly { - display: block; - width: 4px; - height: 4px; - border-radius: 50%; - background: oklch(0.68 0.13 150); - box-shadow: 0 0 8px oklch(0.68 0.13 150); - animation: - fireflyPulse 3.8s ease-in-out infinite, - fireflyWander 21s ease-in-out infinite alternate; -} - -.fw-2 .firefly, -.fw-4 .firefly, -.fw-6 .firefly { - width: 3px; - height: 3px; - background: oklch(0.62 0.14 45); - box-shadow: 0 0 8px oklch(0.62 0.14 45); -} - -.fw-1 { top: 32%; left: 12%; } -.fw-2 { top: 58%; left: 22%; } -.fw-3 { top: 44%; left: 46%; } -.fw-4 { top: 70%; left: 64%; } -.fw-5 { top: 26%; left: 78%; } -.fw-6 { top: 62%; left: 90%; } - -.fw-1 .firefly { animation-delay: -0.4s, -4s; } -.fw-2 .firefly { animation-duration: 4.4s, 26s; animation-delay: -2.1s, -11s; } -.fw-3 .firefly { animation-duration: 3.4s, 18s; animation-delay: -1.2s, -7s; } -.fw-4 .firefly { animation-duration: 4.1s, 29s; animation-delay: -3s, -19s; } -.fw-5 .firefly { animation-duration: 3.6s, 23s; animation-delay: -1.8s, -14s; } -.fw-6 .firefly { animation-duration: 4.2s, 17s; animation-delay: -0.9s, -2s; } - -/* One firefly greets you when a new section scrolls into view */ -.firefly-wrap.firefly-hello { - animation: fireflyHello 1.2s ease-out; -} - -@keyframes fireflyHello { - 0%, 100% { transform: scale(1); filter: brightness(1); } - 35% { transform: scale(2.4); filter: brightness(1.7); } -} - -@keyframes fireflyPulse { - 0%, 100% { opacity: 0.2; transform: scale(1); } - 50% { opacity: 0.9; transform: scale(1.35); } -} - -@keyframes fireflyWander { - 0% { translate: 0 0; } - 25% { translate: 38px -26px; } - 50% { translate: -20px 16px; } - 75% { translate: 26px 30px; } - 100% { translate: -14px -18px; } -} - -/* Day/night: fireflies belong to the dark theme. By daylight the same six - spots are visited by small butterflies instead — the wrapper (and with it - the cursor-shy push and the section greeting) is shared, only the inner - dot is redressed. Only animation-name is swapped so the per-firefly - duration/delay variety above keeps applying to the butterflies. */ -html.theme-light .firefly { - position: relative; - width: 2px; - height: 9px; - border-radius: 1px; - background: oklch(0.35 0.05 140 / 0.8); - box-shadow: none; - opacity: 0.85; - animation-name: butterflyBob, fireflyWander; -} - -html.theme-light .firefly::before, -html.theme-light .firefly::after { - content: ''; - position: absolute; - top: -2px; - width: 7px; - height: 10px; - background: oklch(0.62 0.14 45 / 0.7); - animation: wingFlap 0.5s ease-in-out infinite alternate; -} - -html.theme-light .firefly::before { - left: -6px; - border-radius: 90% 10% 60% 40%; - transform-origin: 100% 55%; -} - -html.theme-light .firefly::after { - right: -6px; - border-radius: 10% 90% 40% 60%; - transform-origin: 0% 55%; - animation-name: wingFlapR; -} - -/* Every other butterfly wears the sage of the canopy and flaps lazier */ -html.theme-light .fw-2 .firefly::before, -html.theme-light .fw-2 .firefly::after, -html.theme-light .fw-4 .firefly::before, -html.theme-light .fw-4 .firefly::after, -html.theme-light .fw-6 .firefly::before, -html.theme-light .fw-6 .firefly::after { - background: oklch(0.52 0.12 150 / 0.55); - animation-duration: 0.62s; -} - -@keyframes butterflyBob { - 0%, 100% { transform: translateY(0) rotate(-4deg); } - 50% { transform: translateY(-8px) rotate(5deg); } -} - -@keyframes wingFlap { - from { transform: scaleX(1) rotate(-6deg); } - to { transform: scaleX(0.3) rotate(8deg); } -} - -@keyframes wingFlapR { - from { transform: scaleX(1) rotate(6deg); } - to { transform: scaleX(0.3) rotate(-8deg); } -} - -/* By day the dusk layer must not dim the butterflies with scroll depth - (the JS crossfade keeps writing inline opacity for the dark theme), and - the warm horizon glow steps back so it reads as haze, not dusk. */ -html.theme-light .bg-depth-dusk { - opacity: 1 !important; -} - -html.theme-light .bg-horizon { - background: radial-gradient(ellipse 60% 100% at 50% 100%, oklch(0.8 0.08 60 / 0.1), transparent 70%); -} - -/* (j) Wandering light sweep */ -.bg-light-sweep { - position: absolute; - top: -30%; - left: -30%; - width: 160%; - height: 160%; - background: linear-gradient(115deg, transparent 42%, oklch(0.68 0.13 150 / 0.05) 50%, transparent 58%); - animation: lightSweep 26s ease-in-out infinite alternate; - will-change: transform; -} - -@keyframes lightSweep { - 0% { transform: translate(-20%, -20%) rotate(8deg); } - 100% { transform: translate(20%, 20%) rotate(8deg); } -} - -/* (j2) Corner vignette: pulls the eye toward the content and grounds the - scene — the forest darkens gently toward the edges like real depth. */ -.bg-nature::after { - content: ''; - position: absolute; - inset: 0; - pointer-events: none; - background: radial-gradient(ellipse 120% 90% at 50% 38%, transparent 55%, oklch(0.10 0.025 142 / 0.4) 100%); -} - -html.theme-light .bg-nature::after { - background: radial-gradient(ellipse 120% 90% at 50% 38%, transparent 62%, oklch(0.78 0.03 120 / 0.3) 100%); -} - -/* (k) Film grain (SVG feTurbulence, no external assets) */ -.bg-grain { - position: absolute; - inset: 0; - opacity: 0.05; - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); - background-size: 160px 160px; -} - -@media (max-width: 768px) { - /* Half the leaves, no god rays / light sweep / extra stalks: the scene - stays recognisable while the animation count roughly halves. */ - .fall-leaf-5, - .fall-leaf-6, - .fall-leaf-7, - .fall-leaf-8, - .bg-godrays, - .bg-light-sweep, - #bamboo-mid, - #bamboo-far .far-4, - #bamboo-far .far-5, - #bamboo-far .far-6, - #bamboo-far .far-7, - #bamboo-near .near-3 { - display: none; - } -} - -/* --- UI/UX cohesion pass ------------------------------------------------- - Shared rhythm and interaction rules for the landing page. Component-specific - responsive refinements are repeated at the end of the cascade. */ -html { - scroll-padding-top: 96px; -} - -body { - text-rendering: optimizeLegibility; -} - -.footer-disclaimer { - color: oklch(0.72 0.015 90); -} - -html.theme-light .footer-disclaimer { - color: var(--text-muted); -} - -.logo-area { - text-decoration: none; - border-radius: 12px; -} - -.logo-area:focus-visible, -.nav-links a:focus-visible, -.theme-toggle:focus-visible, -.lang-dropdown:focus-visible, -.hamburger:focus-visible, -.btn:focus-visible, -.faq-item summary:focus-visible { - outline: 3px solid var(--accent); - outline-offset: 4px; -} - -.nav-links a { - position: relative; - padding: 0.55rem 0; -} - -.nav-links a::after { - content: ""; - position: absolute; - right: 0; - bottom: 0.25rem; - left: 0; - height: 2px; - border-radius: 999px; - background: var(--accent); - transform: scaleX(0); - transform-origin: right; - transition: transform 0.25s ease; -} - -.nav-links a:hover::after, -.nav-links a:focus-visible::after { - transform: scaleX(1); - transform-origin: left; -} - -.hero-text { - max-width: 42rem; -} - -.hero-text h1 { - text-wrap: balance; -} - -.hero-subtitle, -.section-subtitle, -.use-cases-subtitle { - text-wrap: pretty; -} - -.cta-group { - align-items: stretch; -} - -.cta-group .btn { - min-height: 48px; - box-sizing: border-box; -} - -.cta-trust { - max-width: 44rem; - line-height: 1.55; -} - -section[id] { - scroll-margin-top: 96px; -} - -.section-title, -#how-it-works h2, -#self-hosting h2, -#faq h2 { - text-wrap: balance; - letter-spacing: -0.035em; -} - -.use-case-card, -.feature-card, -.faq-item, -.comparison-table-wrapper { - box-shadow: 0 18px 46px rgba(0, 0, 0, 0.12); -} - -html.theme-light :is(.use-case-card, .feature-card, .faq-item, .comparison-table-wrapper) { - box-shadow: 0 18px 46px oklch(0.22 0.035 140 / 0.09); -} - -.use-case-card, -.feature-card { - height: 100%; -} - -.use-case-card p, -.feature-card p, -.faq-item p { - line-height: 1.7; -} - -.faq-item summary { - min-height: 52px; - display: flex; - align-items: center; -} - -.comparison-table tbody tr { - transition: background-color 0.2s ease; -} - -@media (max-width: 768px) { - html { - scroll-padding-top: 76px; - } - - .container { - padding-inline: 1.15rem; - } - - section { - padding-block: 4.25rem; - } - - .hero-text { - max-width: none; - } - - .hero-text h1, - .section-title, - #how-it-works h2, - #self-hosting h2, - #faq h2 { - font-size: clamp(2rem, 9vw, 2.65rem) !important; - } - - .cta-group { - display: grid; - grid-template-columns: 1fr 1fr; - width: 100%; - } - - .cta-group .btn { - width: 100%; - justify-content: center; - } - - .cta-group .btn-primary, - .cta-group .btn-firefox { - grid-column: span 1; - } - - .hero-github-cta, - .btn-demo-mobile { - grid-column: span 1; - } - - .use-case-card, - .feature-card, - .faq-item, - .comparison-table-wrapper { - box-shadow: 0 12px 30px rgba(0, 0, 0, 0.1); - } -} - -@media (max-width: 430px) { - .cta-group { - grid-template-columns: 1fr; - } - - .cta-group .btn-primary, - .cta-group .btn-firefox, - .hero-github-cta, - .btn-demo-mobile { - grid-column: 1; - } -} - -@media (prefers-reduced-motion: reduce) { - .nav-links a::after, - .use-case-card, - .feature-card, - .faq-item, - .btn { - transition-duration: 0.01ms !important; - } -} - -/* (l) Click confetti: a small handful of eucalyptus leaves bursts from - wherever the visitor presses (spawned by app.js). Built like the falling - leaves above — the outer span owns the flight path (fast burst that - brakes at its peak, then sinks), the inner leaf rocks around its stem - the whole way down. Deliberately subtle: few, tiny, short-lived. */ -.click-leaf { - position: fixed; - z-index: 3000; - margin: -6px 0 0 -4px; - pointer-events: none; - opacity: 0; - animation: leafBurst var(--leaf-dur, 1.1s) forwards; - will-change: transform, opacity; -} - -.click-leaf i { - display: block; - width: 8px; - height: 12px; - border-radius: 0 70% 0 70%; - background: linear-gradient(135deg, oklch(0.62 0.12 150 / 0.95), oklch(0.45 0.09 145 / 0.85)); - transform: rotate(var(--leaf-rot, 0deg)); - animation: leafRock var(--flutter-dur, 0.6s) ease-in-out infinite alternate; -} - -.click-leaf-sage i { - background: linear-gradient(135deg, oklch(0.72 0.09 140 / 0.9), oklch(0.55 0.08 145 / 0.8)); -} - -.click-leaf-amber i { - background: linear-gradient(135deg, oklch(0.68 0.14 55 / 0.95), oklch(0.55 0.13 40 / 0.85)); -} - -/* Two-phase flight: --leaf-x/--leaf-y is the burst PEAK; from there each - leaf loses momentum and sinks past it while fading. Per-segment timing - functions give the fast pop and the gentle fall. */ -@keyframes leafBurst { - 0% { - opacity: 0; - transform: translate(0, 0) scale(var(--leaf-scale, 1)); - animation-timing-function: cubic-bezier(0.16, 0.84, 0.44, 1); - } - 10% { opacity: 1; } - 45% { - opacity: 1; - transform: translate(var(--leaf-x, 0), var(--leaf-y, -20px)) scale(var(--leaf-scale, 1)); - animation-timing-function: cubic-bezier(0.55, 0.06, 0.68, 0.19); - } - 100% { - opacity: 0; - transform: translate(calc(var(--leaf-x, 0) * 1.4), calc(var(--leaf-y, -20px) + 44px)) scale(calc(var(--leaf-scale, 1) * 0.92)); - } -} - -@keyframes leafRock { - from { transform: rotate(calc(var(--leaf-rot, 0deg) - 24deg)); } - to { transform: rotate(calc(var(--leaf-rot, 0deg) + 24deg)); } -} - -@media (prefers-reduced-motion: reduce) { - .click-leaf { display: none; } - .bg-nature *, - .bg-nature *::before, - .bg-nature *::after { - animation: none !important; - } - .fall-leaf { opacity: 0; } -} - -/* --- Layout --- */ -.container { - max-width: 1100px; - margin: 0 auto; - padding: 0 2rem; -} - -section { - padding: 6rem 0; -} - -#how-it-works { - padding: 4.5rem 0; -} - -/* Content-visibility for offscreen sections to improve INP */ -.use-cases-section, -#self-hosting, -.comparison-section { - content-visibility: auto; - contain-intrinsic-size: auto 1400px; -} - -/* --- Navigation --- */ -nav { - position: fixed; - top: 0; - width: 100%; - z-index: 1000; - background: var(--glass); - backdrop-filter: blur(12px); - border-bottom: 1px solid var(--glass-border); - padding: 1rem 0; - transition: padding 0.3s ease, background 0.3s ease; -} - -nav.nav-scrolled { - padding: 0.75rem 0; - background: oklch(0.20 0.025 140 / 0.9); -} - -html.theme-light nav.nav-scrolled { - background: oklch(0.95 0.01 100 / 0.92); -} - -.nav-content { - display: flex; - align-items: center; -} - -.logo-area { - display: flex; - align-items: center; - gap: 0.75rem; - font-weight: 800; - font-size: 1.5rem; - letter-spacing: -0.5px; -} - -/* Logo can be a plain ; without this it falls back to the UA link blue, - which clashed silently with the old indigo theme and visibly with green. */ -a.logo-area { - color: var(--text); -} - -.logo-area picture { - display: contents; -} - -.logo-area img { - height: 64px; - width: 64px; - object-fit: contain; - border-radius: 8px; - margin: -4px 0; -} - -.nav-ext-status { - display: inline-flex; - align-items: center; - gap: 4px; - padding: 3px 8px; - background: oklch(0.68 0.13 150 / 0.15); - border: 1px solid oklch(0.68 0.13 150 / 0.3); - color: oklch(0.68 0.13 150); - border-radius: 12px; - font-size: 0.55rem; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.05em; - box-shadow: 0 0 10px oklch(0.68 0.13 150 / 0.1); - animation: fade-in 0.4s ease-out; - margin-left: 0.2rem; -} - -@keyframes fade-in { - from { opacity: 0; transform: translateY(-5px); } - to { opacity: 1; transform: translateY(0); } -} - -.nav-links { - display: flex; - gap: 2rem; - margin-left: auto; -} - -.nav-links a { - color: var(--text-muted); - text-decoration: none; - font-weight: 500; - transition: color 0.3s; -} - -.nav-links a:hover { - color: var(--accent); -} - -.nav-right { - display: flex; - align-items: center; - gap: 0.5rem; - margin-left: 2rem; - flex-shrink: 0; -} - -/* --- Theme Toggle (sun/moon) --- */ -.theme-toggle { - position: relative; - display: inline-flex; - align-items: center; - justify-content: center; - width: 36px; - height: 36px; - padding: 0; - background: transparent; - border: none; - border-radius: 10px; - color: var(--text-muted); - cursor: pointer; - flex-shrink: 0; - transition: color 0.3s, transform 0.3s; - overflow: hidden; -} - -.theme-toggle:hover { - color: var(--accent); - transform: scale(1.15); -} - -.theme-toggle:active { - transform: scale(0.9); -} - -.theme-toggle svg { - position: absolute; - width: 18px; - height: 18px; - transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.4s ease; -} - -/* Default (Dark mode): Sun visible, Moon hidden & rotated */ -.theme-toggle .theme-icon-sun { - opacity: 1; - transform: scale(1) rotate(0deg); -} - -.theme-toggle .theme-icon-moon { - opacity: 0; - transform: scale(0) rotate(-90deg); -} - -/* Light mode: Moon visible, Sun hidden & rotated */ -html.theme-light .theme-toggle .theme-icon-sun { - opacity: 0; - transform: scale(0) rotate(90deg); -} - -html.theme-light .theme-toggle .theme-icon-moon { - opacity: 1; - transform: scale(1) rotate(0deg); -} - -/* --- Light Theme --- */ -html.theme-light { - --accent-green: oklch(0.52 0.12 150); - --accent-green-hover: oklch(0.58 0.12 150); - --text-on-green: #ffffff; - --bg: oklch(0.95 0.01 100); - --card: #ffffff; - --accent: var(--accent-green); - --accent-glow: oklch(0.52 0.12 150 / 0.25); - --text: oklch(0.20 0.025 140); - --text-muted: oklch(0.45 0.02 110); - --glass: rgba(255, 255, 255, 0.7); - --glass-border: oklch(0.20 0.025 140 / 0.08); - --section-tint: transparent; - --section-tint-strong: oklch(0.88 0.015 90 / 0.5); - --card-surface: rgba(255, 255, 255, 0.75); - --card-border: oklch(0.20 0.025 140 / 0.08); - color-scheme: light; -} - -html.theme-light .bg-nature { - background: - radial-gradient(circle at 16% 10%, oklch(0.85 0.06 150 / 0.3), transparent 34rem), - radial-gradient(circle at 78% 68%, oklch(0.85 0.06 60 / 0.2), transparent 38rem), - linear-gradient(180deg, oklch(0.96 0.01 90) 0%, oklch(0.93 0.025 140) 46%, oklch(0.96 0.01 90) 100%); -} - -html.theme-light .bg-canopy, -html.theme-light .canopy-arc, -html.theme-light .bg-grass { - opacity: 0.45; -} - -/* By day the stalks get their own fresh-green recipes — the dark-theme - gradients faded to 45% read as grey smears on the light backdrop and - stopped looking like bamboo at all. */ -html.theme-light #bamboo-far .bamboo-stalk { - opacity: 0.55; - background: - repeating-linear-gradient(180deg, - transparent 0, transparent 108px, - oklch(0.55 0.09 145 / 0.4) 108px, oklch(0.55 0.09 145 / 0.4) 112px), - linear-gradient(180deg, oklch(0.68 0.11 145 / 0.6), oklch(0.62 0.09 140 / 0.35)); -} - -html.theme-light #bamboo-far::before, -html.theme-light #bamboo-far::after { - background: linear-gradient(180deg, oklch(0.7 0.1 145 / 0.5), oklch(0.64 0.08 140 / 0.25)); -} - -html.theme-light #bamboo-mid .bamboo-stalk { - opacity: 0.65; - background: - repeating-linear-gradient(180deg, - transparent 0, transparent 84px, - oklch(0.5 0.1 145 / 0.45) 84px, oklch(0.5 0.1 145 / 0.45) 87px), - linear-gradient(90deg, transparent 14%, oklch(0.85 0.08 130 / 0.35) 38%, transparent 68%), - linear-gradient(180deg, oklch(0.63 0.115 145 / 0.65), oklch(0.57 0.09 140 / 0.4)); -} - -html.theme-light #bamboo-mid .bamboo-stalk::before, -html.theme-light #bamboo-mid .bamboo-stalk::after { - background: oklch(0.55 0.13 145 / 0.5); -} - -html.theme-light #bamboo-near .bamboo-stalk { - opacity: 0.75; - background: - linear-gradient(90deg, transparent 12%, oklch(0.88 0.07 130 / 0.4) 36%, transparent 64%), - linear-gradient(180deg, oklch(0.59 0.12 145 / 0.75), oklch(0.52 0.1 140 / 0.5)); -} - -html.theme-light #bamboo-near .bamboo-node { - background: oklch(0.45 0.1 145 / 0.65); -} - -html.theme-light #bamboo-near .bamboo-leaf { - background: oklch(0.55 0.13 145 / 0.6); -} - -/* The centre trunks stay whisper-quiet by daylight too */ -html.theme-light #bamboo-far .far-6, -html.theme-light #bamboo-far .far-7 { - opacity: 0.26; -} - -html.theme-light .bg-mist { - display: none; -} - -html.theme-light .bg-dusk-tint { - display: none; -} - -html.theme-light .godray { - filter: blur(12px); - opacity: 0.5; -} - -html.theme-light .bg-grain { - opacity: 0.035; -} - -html.theme-light .feature-card, -html.theme-light .faq-item { - background: var(--card-surface); - border-color: var(--card-border); - box-shadow: 0 8px 24px oklch(0.20 0.025 140 / 0.06); -} - -html.theme-light .feature-card::before { - background: linear-gradient(to bottom right, oklch(0.20 0.025 140 / 0.08), transparent); -} - -html.theme-light .use-case-card { - background: var(--card-surface); -} - -html.theme-light .use-case-card h3 { - color: var(--text); -} - -html.theme-light .use-case-card:hover { - box-shadow: 0 12px 30px oklch(0.20 0.025 140 / 0.12); - border-color: oklch(0.20 0.025 140 / 0.15); -} - -html.theme-light .faq-item[open] { - background: #ffffff; -} - -html.theme-light .faq-item p { - border-top-color: oklch(0.20 0.025 140 / 0.08); -} - -html.theme-light .btn-secondary { - background: #ffffff; - border-color: oklch(0.20 0.025 140 / 0.12); -} - -html.theme-light .btn-secondary:hover { - background: oklch(0.88 0.015 90); -} - -html.theme-light .btn-firefox { - background: oklch(0.62 0.14 45); - color: #ffffff; - box-shadow: 0 10px 18px oklch(0.62 0.14 45 / 0.24); -} - -html.theme-light .btn-firefox:hover { - background: oklch(0.55 0.13 45); - box-shadow: 0 18px 26px oklch(0.62 0.14 45 / 0.28); -} - -html.theme-light .btn-firefox img { - filter: brightness(0) saturate(100%) invert(13%) sepia(38%) saturate(1084%) hue-rotate(177deg) brightness(89%) contrast(96%); -} - -html.theme-light .btn-firefox-secondary img { - filter: brightness(0) saturate(100%) invert(13%) sepia(38%) saturate(1084%) hue-rotate(177deg) brightness(89%) contrast(96%); -} - -html.theme-light .comparison-section { - background: transparent; -} - -html.theme-light .comparison-table-wrapper { - background: rgba(255, 255, 255, 0.75); - box-shadow: 0 10px 30px oklch(0.20 0.025 140 / 0.08); -} - -html.theme-light .comparison-table th { - background: oklch(0.20 0.025 140 / 0.06); - color: var(--text); -} - -html.theme-light .comparison-table th, -html.theme-light .comparison-table td { - border-bottom-color: oklch(0.20 0.025 140 / 0.08); -} - -html.theme-light .comparison-table tbody tr:hover { - background: oklch(0.20 0.025 140 / 0.03); -} - -html.theme-light .feat-name strong { - color: var(--text); -} - -html.theme-light .table-footnotes { - background: oklch(0.20 0.025 140 / 0.04); - border-top-color: oklch(0.20 0.025 140 / 0.06); -} - -@media (max-width: 900px) { - html.theme-light .comparison-table tbody tr { - background: rgba(255, 255, 255, 0.85); - box-shadow: 0 6px 18px oklch(0.20 0.025 140 / 0.08); - } - html.theme-light .comparison-table td.feat-name { - background: oklch(0.20 0.025 140 / 0.05); - } - html.theme-light .comparison-table td { - border-bottom-color: oklch(0.20 0.025 140 / 0.08); - } -} - -html.theme-light .compat-logo img, -html.theme-light .compat-logo { - filter: none; -} - -/* --- 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; -} - -/* --- Features --- */ -/* Bento Grid Layout Configuration */ -.features-grid { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 1.5rem; - margin-top: 4rem; -} - -.feature-card.feature-card-compact { - padding: 1.8rem; - border-radius: 16px; -} - -.feature-card.feature-card-compact::before { - border-radius: 16px; -} - -.feature-card.feature-card-compact h3 { - font-size: 1.1rem; - margin-bottom: 0.75rem; -} - -.feature-card.feature-card-compact p { - font-size: 0.9rem; -} - -.feature-card { - background: oklch(0.27 0.035 130 / 0.45); - backdrop-filter: blur(16px) saturate(120%); - -webkit-backdrop-filter: blur(16px) saturate(120%); - padding: 2.5rem; - border-radius: 24px; - border: 1px solid rgba(255, 255, 255, 0.08); - box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.3); - transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.3s cubic-bezier(0.4, 0, 0.2, 1), border-color 0.3s cubic-bezier(0.4, 0, 0.2, 1); - position: relative; - overflow: hidden; -} - -.feature-card::before { - content: ''; - position: absolute; - top: 0; left: 0; right: 0; bottom: 0; - border-radius: 24px; - padding: 1px; - background: linear-gradient(to bottom right, rgba(255, 255, 255, 0.12), transparent); - -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); - -webkit-mask-composite: xor; - mask-composite: exclude; - pointer-events: none; -} - -.feature-card:hover { - transform: translateY(-5px); - box-shadow: 0 15px 30px oklch(0.68 0.13 150 / 0.2); - border-color: oklch(0.68 0.13 150 / 0.3); -} - -.feature-card:hover::before { - background: linear-gradient(to bottom right, oklch(0.68 0.13 150 / 0.3), transparent); -} - -/* Feature Icon with Inline SVG Wrapper */ -.feature-icon-svg { - display: flex; - flex-shrink: 0; - align-items: center; - justify-content: center; - width: 44px; - height: 44px; - background: oklch(0.68 0.13 150 / 0.12); - color: var(--accent); - border-radius: 12px; - margin-right: 14px; - transition: background 0.3s ease, color 0.3s ease, box-shadow 0.3s ease, transform 0.3s ease; -} - -.feature-card:hover .feature-icon-svg { - background: var(--accent); - color: var(--text-on-green); - box-shadow: 0 0 15px var(--accent-glow); - transform: scale(1.05); -} - -.bento-icon { - width: 22px; - height: 22px; - display: block; - flex-shrink: 0; -} - -.feature-card h3 { - margin-bottom: 1rem; - font-size: 1.25rem; - font-weight: 700; - display: flex; - align-items: center; -} - -.feature-card p { - color: var(--text-muted); - font-size: 0.95rem; - line-height: 1.6; -} - -/* Bento Asymmetric Spanning */ -.feature-card.bento-large { - grid-column: span 2; - background: linear-gradient(135deg, oklch(0.27 0.035 130 / 0.45) 0%, oklch(0.68 0.13 150 / 0.08) 100%); - padding: 3rem 3.5rem; - display: grid; - grid-template-columns: 1fr 2fr; - gap: 3rem; - align-items: center; -} - -.feature-card.bento-large h3 { - flex-direction: column; - align-items: center; - text-align: center; - gap: 1rem; - margin-bottom: 0; - font-size: 1.25rem; -} - -.feature-card.bento-large .feature-icon, -.feature-card.bento-large .feature-icon-svg { - width: 52px; - height: 52px; - border-radius: 14px; - margin-right: 0; - flex-shrink: 0; -} - -.feature-card.bento-large .bento-icon { - width: 24px; - height: 24px; -} - -.feature-card.bento-large p { - font-size: 1rem; - line-height: 1.7; - margin: 0; -} - -.feature-card.bento-large::after { - content: ''; - position: absolute; - top: 0; - left: 2.5rem; - right: 2.5rem; - height: 2px; - background: linear-gradient(90deg, transparent, oklch(0.68 0.13 150 / 0.4), transparent); - opacity: 0; - transition: opacity 0.3s ease; -} - -.feature-card.bento-large:hover::after { - opacity: 1; -} - -@media (max-width: 900px) { - .features-grid { - /* Keep the Why KoalaSync grid at either three columns or one column. - The two-column in-between makes compact cards stretch unevenly. */ - grid-template-columns: 1fr; - gap: 1.25rem; - } - .feature-card.bento-large { - grid-column: span 1; - padding: 2.5rem; - gap: 2rem; - } - .feature-card.bento-large h3 { - font-size: 1.15rem; - } -} - -@media (max-width: 600px) { - .features-grid { - grid-template-columns: 1fr; - gap: 1rem; - } - .feature-card.bento-large { - grid-column: span 1; - display: flex; - flex-direction: column; - padding: 1.75rem; - gap: 1rem; - } - .feature-card.bento-large h3 { - flex-direction: row; - align-items: center; - text-align: left; - gap: 0.75rem; - margin-bottom: 0; - font-size: 1.15rem; - } - .feature-card.bento-large .feature-icon, - .feature-card.bento-large .feature-icon-svg { - width: 40px; - height: 40px; - border-radius: 10px; - margin-right: 0; - } - .feature-card.bento-large .bento-icon { - width: 18px; - height: 18px; - } - .feature-card.bento-large p { - font-size: 0.9rem; - line-height: 1.6; - } - .feature-card.bento-large::after { - display: none; - } - .feature-card { - padding: 1.75rem; - } - .feature-card h3 { - font-size: 1.1rem; - } - .feature-icon-svg { - width: 40px; - height: 40px; - border-radius: 10px; - margin-right: 12px; - } - .bento-icon { - width: 18px; - height: 18px; - } -} - -/* --- How it works --- */ -.steps { - display: flex; - flex-direction: column; - gap: 4rem; -} - -.step { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 3rem; - align-items: center; -} - -.step:nth-child(even) .step-text { - order: 2; -} - -.step-num { - font-size: 3.25rem; - font-weight: 900; - background: linear-gradient(135deg, oklch(0.68 0.13 150 / 0.5), oklch(0.62 0.14 45 / 0.15)); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - line-height: 1; - margin-bottom: 0.5rem; - font-family: inherit; - letter-spacing: 0; -} - -.step-text h3 { - font-size: 1.65rem; - margin-bottom: 0.75rem; - font-weight: 850; - color: var(--text-main); - letter-spacing: 0; -} - -.step-text p { - color: var(--text-muted); - font-size: 1rem; - line-height: 1.55; -} - -/* Custom CSS Mockups for step illustrations */ -.step-illustration-1, .step-illustration-2, .step-illustration-3 { - background: radial-gradient(circle at 10% 10%, oklch(0.20 0.04 145), oklch(0.10 0.015 138)); - height: 240px; - border-radius: 18px; - border: 1px solid rgba(255, 255, 255, 0.06); - position: relative; - overflow: hidden; - display: flex; - align-items: center; - justify-content: center; - box-shadow: 0 20px 45px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.05); - transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1), border-color 0.4s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.4s cubic-bezier(0.16, 1, 0.3, 1); -} - -@media (max-width: 900px) { - #how-it-works { - padding: 3.5rem 0; - } - - .steps { - gap: 2.75rem; - } - - .step { - grid-template-columns: minmax(0, 1fr); - gap: 1.1rem; - text-align: left; - } - - #how-it-works .step .step-text { - order: 1; - } - - #how-it-works .step-illustration-1, - #how-it-works .step-illustration-2, - #how-it-works .step-illustration-3 { - order: 2; - height: 220px; - } - - .step-num { - font-size: 2.5rem; - } - - .step-text h3 { - font-size: 1.45rem; - } -} - -.step-illustration-1:hover, .step-illustration-2:hover, .step-illustration-3:hover { - transform: translateY(-6px) scale(1.02); - border-color: oklch(0.68 0.13 150 / 0.25); - box-shadow: 0 25px 50px oklch(0.68 0.13 150 / 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.1); -} - -/* ========================================================================== - Step 1 Illustration: Browser & Installed Success Popup - ========================================================================== */ -.illus-browser-container { - width: 90%; - height: 80%; - background: oklch(0.11 0.015 138); - border: 1px solid rgba(255, 255, 255, 0.06); - border-radius: 12px; - overflow: hidden; - box-shadow: 0 15px 35px rgba(0, 0, 0, 0.6); - display: flex; - flex-direction: column; -} - -.illus-browser-header { - background: oklch(0.20 0.025 140 / 0.8); - backdrop-filter: blur(8px); - -webkit-backdrop-filter: blur(8px); - padding: 0.6rem 0.8rem; - display: flex; - align-items: center; - border-bottom: 1px solid rgba(255, 255, 255, 0.06); -} - -.illus-browser-dots { - display: flex; - gap: 6px; - margin-right: 1.5rem; -} - -.illus-browser-dot { - width: 8px; - height: 8px; - border-radius: 50%; - opacity: 0.6; -} -.illus-browser-dot:nth-child(1) { background: oklch(0.55 0.15 25); } -.illus-browser-dot:nth-child(2) { background: oklch(0.68 0.14 45); } -.illus-browser-dot:nth-child(3) { background: oklch(0.68 0.13 150); } - -.illus-browser-address-bar { - background: oklch(0.13 0.02 138); - border: 1px solid rgba(255, 255, 255, 0.08); - border-radius: 6px; - color: var(--text-muted); - font-size: 0.68rem; - padding: 3px 12px; - flex-grow: 1; - max-width: 60%; - text-align: center; - font-family: monospace; - letter-spacing: 0.2px; -} - -.illus-browser-address-bar .url-protocol { - color: oklch(0.68 0.13 150); - font-weight: 600; -} - -.illus-browser-toolbar { - display: flex; - margin-left: auto; -} - -.illus-extension-btn { - width: 22px; - height: 22px; - border-radius: 6px; - display: flex; - align-items: center; - justify-content: center; - transition: opacity 0.3s, box-shadow 0.3s; -} - -.illus-extension-btn.active { - background: oklch(0.68 0.13 150 / 0.2); - border: 1px solid var(--accent); - box-shadow: 0 0 10px oklch(0.68 0.13 150 / 0.4); - animation: active-pulse 2s infinite alternate; -} - -@keyframes active-pulse { - 0% { transform: scale(1); box-shadow: 0 0 5px oklch(0.68 0.13 150 / 0.4); } - 100% { transform: scale(1.08); box-shadow: 0 0 12px oklch(0.68 0.13 150 / 0.7); } -} - -.illus-browser-content { - flex-grow: 1; - padding: 1rem; - position: relative; - display: flex; - align-items: flex-start; -} - -.illus-store-card { - background: rgba(255, 255, 255, 0.02); - border: 1px solid rgba(255, 255, 255, 0.04); - border-radius: 12px; - padding: 0.8rem; - width: 65%; - display: flex; - flex-direction: column; - gap: 10px; -} - -.illus-store-card-header { - display: flex; - align-items: center; - gap: 8px; -} - -.illus-large-logo { - width: 30px; - height: 30px; - object-fit: contain; - border-radius: 6px; - box-shadow: 0 0 10px oklch(0.68 0.13 150 / 0.2); -} - -.illus-store-info { - display: flex; - flex-direction: column; -} - -.illus-store-title { - font-size: 0.88rem; - font-weight: 800; - color: var(--text); - line-height: 1.1; -} - -.illus-store-desc { - font-size: 0.58rem; - color: var(--text-muted); - line-height: 1.2; - margin-top: 2px; -} - -.illus-store-buttons { - display: flex; - flex-direction: column; - gap: 6px; -} - -.illus-store-btn { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 5px 8px; - border-radius: 8px; - font-size: 0.58rem; - font-weight: 700; - color: white; - border: 1px solid rgba(255, 255, 255, 0.05); - background: rgba(255, 255, 255, 0.03); - box-shadow: 0 2px 5px rgba(0,0,0,0.2); - cursor: default; - transition: opacity 0.2s, transform 0.2s; -} - -.illus-store-btn img { - width: 11px; - height: 11px; - display: block; -} - -.illus-store-btn.chrome { - border-color: oklch(0.68 0.13 150 / 0.25); - background: oklch(0.68 0.13 150 / 0.08); - box-shadow: 0 0 10px oklch(0.68 0.13 150 / 0.05); -} - -.illus-store-btn.firefox { - border-color: oklch(0.62 0.14 45 / 0.25); - background: oklch(0.62 0.14 45 / 0.08); - box-shadow: 0 0 10px oklch(0.62 0.14 45 / 0.05); -} - -.install-breathe { - animation: install-breathe 2.5s ease-in-out infinite; - z-index: 2; - position: relative; -} - -@keyframes install-breathe { - 0%, 100% { - transform: scale(1); - box-shadow: 0 0 15px oklch(0.68 0.13 150 / 0.2); - } - 50% { - transform: scale(1.05); - box-shadow: 0 0 25px oklch(0.68 0.13 150 / 0.5); - } -} - -.install-breathe.firefox, -.btn-firefox.install-breathe { - animation-name: install-breathe-ff; -} - -@keyframes install-breathe-ff { - 0%, 100% { - transform: scale(1); - box-shadow: 0 0 15px oklch(0.62 0.14 45 / 0.2); - } - 50% { - transform: scale(1.05); - box-shadow: 0 0 25px oklch(0.62 0.14 45 / 0.5); - } -} - -/* Glassmorphic Dropping Success Card */ -.illus-extension-popup { - position: absolute; - top: 0.6rem; - right: 0.6rem; - width: 145px; - background: oklch(0.20 0.025 140 / 0.85); - backdrop-filter: blur(12px); - -webkit-backdrop-filter: blur(12px); - border: 1px solid oklch(0.68 0.13 150 / 0.25); - border-radius: 12px; - padding: 0.6rem 0.8rem; - box-shadow: 0 10px 25px rgba(0, 0, 0, 0.55), 0 0 15px oklch(0.68 0.13 150 / 0.15); - animation: popup-float 4s ease-in-out infinite; - z-index: 5; -} - -@keyframes popup-float { - 0%, 100% { transform: translateY(0); } - 50% { transform: translateY(-4px); } -} - -.illus-extension-popup .popup-title { - display: flex; - align-items: center; - gap: 5px; - font-size: 0.72rem; - font-weight: 700; - color: var(--text); - margin-bottom: 6px; - border-bottom: 1px solid rgba(255, 255, 255, 0.08); - padding-bottom: 4px; -} - -.illus-extension-popup .popup-title img { - width: 12px; - height: 12px; -} - -.illus-extension-popup .popup-status { - margin-bottom: 4px; -} - -.status-badge-success { - background: oklch(0.68 0.13 150 / 0.12); - color: oklch(0.68 0.13 150); - font-size: 0.6rem; - font-weight: 700; - padding: 2px 6px; - border-radius: 4px; - border: 1px solid oklch(0.68 0.13 150 / 0.2); - display: inline-flex; - align-items: center; - gap: 3px; - animation: status-glow 2s infinite alternate; -} - -@keyframes status-glow { - 0% { box-shadow: 0 0 0 oklch(0.68 0.13 150 / 0); } - 100% { box-shadow: 0 0 6px oklch(0.68 0.13 150 / 0.3); } -} - -.illus-extension-popup .popup-quick-start { - font-size: 0.55rem; - color: var(--text-muted); - font-weight: 500; - line-height: 1.2; -} - - -/* ========================================================================== - Step 2 Illustration: Highly Realistic Extension Popup (Create Room View) - ========================================================================== */ -.illus-popup-card { - width: 220px; - background: oklch(0.20 0.025 140 / 0.85); - backdrop-filter: blur(16px); - -webkit-backdrop-filter: blur(16px); - border: 1px solid rgba(255, 255, 255, 0.12); - border-radius: 16px; - padding: 0.8rem; - box-shadow: 0 18px 40px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.1); - display: flex; - flex-direction: column; - gap: 0.55rem; - animation: popup-float 4s ease-in-out infinite; - animation-delay: -1s; -} - -.illus-popup-header { - display: flex; - justify-content: space-between; - align-items: center; - border-bottom: 1px solid rgba(255, 255, 255, 0.06); - padding-bottom: 8px; -} - -.illus-popup-brand { - display: flex; - align-items: center; - gap: 6px; - font-size: 0.82rem; /* Scaled up font */ - font-weight: 800; - letter-spacing: -0.2px; -} - -.illus-popup-version { - font-size: 0.58rem; - color: var(--text-muted); -} - -.illus-popup-tabs { - display: flex; - gap: 4px; - background: oklch(0.12 0.015 135 / 0.4); - padding: 3px; - border-radius: 8px; - border: 1px solid rgba(255, 255, 255, 0.03); -} - -.illus-popup-tab { - flex: 1; - text-align: center; - font-size: 0.62rem; /* Scaled up font */ - padding: 4px 0; - border-radius: 6px; - color: var(--text-muted); - font-weight: 600; -} - -.illus-popup-tab.active { - background: var(--card); - color: var(--accent); - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); -} - -.illus-popup-body { - display: flex; - flex-direction: column; - gap: 0.55rem; - position: relative; -} - -.illus-popup-btn { - background: linear-gradient(135deg, var(--accent-green), var(--accent-terracotta)); - border-radius: 8px; - padding: 7px 9px; - color: var(--text-on-green); - font-weight: 700; - font-size: 0.68rem; - text-align: center; - box-shadow: 0 4px 12px oklch(0.68 0.13 150 / 0.3); - position: relative; - overflow: hidden; - cursor: default; - animation: create-btn-pulse 2s infinite alternate; -} - -@keyframes create-btn-pulse { - 0% { transform: scale(1); box-shadow: 0 4px 12px oklch(0.68 0.13 150 / 0.3); } - 100% { transform: scale(1.03); box-shadow: 0 6px 16px oklch(0.68 0.13 150 / 0.5); } -} - -.illus-btn-shine { - position: absolute; - top: 0; - left: -100%; - width: 50%; - height: 100%; - background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.25), transparent); - transform: skewX(-20deg); - animation: shimmer-sweep 3s infinite ease-in-out; -} - -@keyframes shimmer-sweep { - 0% { left: -100%; } - 50%, 100% { left: 150%; } -} - -.illus-collapsed-details { - background: oklch(0.12 0.015 135 / 0.4); - border: 1px solid rgba(255, 255, 255, 0.05); - border-radius: 8px; - padding: 6px 11px; - font-size: 0.54rem; - color: var(--text-muted); - font-weight: 700; - text-transform: uppercase; - display: flex; - align-items: center; - gap: 6px; - letter-spacing: 0.2px; -} - -.details-arrow { - font-size: 0.48rem; - color: var(--text-muted); - opacity: 0.7; -} - -/* Floating Success Badge */ -.illus-floating-success { - position: absolute; - /* Fully below the card so the toast never straddles its border */ - bottom: -2.4rem; - left: 50%; - transform: translateX(-50%); - background: oklch(0.12 0.015 135 / 0.92); - border: 1px solid oklch(0.68 0.13 150 / 0.35); - border-radius: 20px; - padding: 3px 10px; - display: flex; - align-items: center; - gap: 4px; - box-shadow: 0 6px 14px rgba(0, 0, 0, 0.45), 0 0 12px oklch(0.68 0.13 150 / 0.18); - animation: floating-success-fade 5s infinite; - white-space: nowrap; - z-index: 10; -} - -@keyframes floating-success-fade { - 0%, 100%, 75% { opacity: 0; transform: translate(-50%, 5px) scale(0.9); } - 15%, 60% { opacity: 1; transform: translate(-50%, 0) scale(1); } -} - -.illus-floating-success .success-icon { - font-size: 0.65rem; -} - -.illus-floating-success span[lang] { - font-size: 0.58rem; - color: oklch(0.68 0.13 150); - font-weight: 700; -} - - -/* ========================================================================== - Step 3 Illustration: Dual Screen Theater Sync Setup - ========================================================================= */ -.illus-sync-theater { - width: 95%; - display: flex; - align-items: center; - justify-content: center; - position: relative; - gap: 0.6rem; -} - -.illus-player-card { - width: 145px; - background: oklch(0.20 0.025 140 / 0.85); - backdrop-filter: blur(12px); - -webkit-backdrop-filter: blur(12px); - border: 1px solid rgba(255, 255, 255, 0.08); - border-radius: 12px; - padding: 6px; - box-shadow: 0 12px 30px rgba(0,0,0,0.5); - display: flex; - flex-direction: column; - gap: 4px; - position: relative; - z-index: 2; -} - -.illus-player-card.player-a { - animation: popup-float 4s ease-in-out infinite; - animation-delay: -2s; -} - -.illus-player-card.player-b { - animation: popup-float 4s ease-in-out infinite; - animation-delay: -3s; -} - -.player-header { - display: flex; - justify-content: space-between; - align-items: center; - font-size: 0.55rem; -} - -.player-user { - font-weight: 700; - color: var(--text); -} - -.player-badge { - font-size: 0.42rem; - font-weight: 800; - padding: 1px 3px; - border-radius: 3px; -} - -.player-badge.active { - background: oklch(0.68 0.13 150 / 0.15); - color: oklch(0.68 0.13 150); - border: 1px solid oklch(0.68 0.13 150 / 0.25); -} - -.player-video-canvas { - height: 70px; - border-radius: 8px; - background: linear-gradient(135deg, oklch(0.20 0.04 145), oklch(0.22 0.05 45)); - position: relative; - overflow: hidden; - display: flex; - align-items: center; - justify-content: center; - border: 1px solid rgba(255, 255, 255, 0.02); -} - -.player-video-overlay { - position: absolute; - top: 0; left: 0; width: 100%; height: 100%; - background: linear-gradient(to bottom, transparent, oklch(0.68 0.13 150 / 0.15), transparent); - background-size: 100% 200%; - animation: playerScrollScanline 3s linear infinite; -} - -@keyframes playerScrollScanline { - 0% { background-position: 0% 0%; } - 100% { background-position: 0% 200%; } -} - -.player-play-pulse { - width: 24px; - height: 24px; - border-radius: 50%; - background: oklch(0.68 0.13 150 / 0.85); - color: var(--text-on-green); - font-size: 0.65rem; - display: flex; - align-items: center; - justify-content: center; - box-shadow: 0 0 10px oklch(0.68 0.13 150 / 0.5); - z-index: 3; - animation: play-pulse 2s infinite alternate; -} - -@keyframes play-pulse { - 0% { transform: scale(1); box-shadow: 0 0 6px oklch(0.68 0.13 150 / 0.4); } - 100% { transform: scale(1.15); box-shadow: 0 0 15px oklch(0.68 0.13 150 / 0.8); } -} - -.player-controls { - display: flex; - align-items: center; - gap: 4px; - padding: 2px 2px 0 2px; -} - -.control-btn { - font-size: 0.58rem; - color: var(--accent); -} - -.player-timeline { - flex-grow: 1; - height: 3px; - background: rgba(255, 255, 255, 0.1); - border-radius: 2px; - position: relative; -} - -.timeline-fill { - position: absolute; - top: 0; left: 0; - height: 100%; - background: var(--accent); - border-radius: 2px; - width: 60%; - animation: theater-grow 6s infinite linear; -} - -.timeline-knob { - position: absolute; - top: -2px; - left: 60%; - width: 7px; - height: 7px; - border-radius: 50%; - background: white; - box-shadow: 0 0 4px rgba(0,0,0,0.5); - transform: translateX(-50%); - animation: theater-knob-grow 6s infinite linear; -} - -@keyframes theater-grow { - 0% { width: 0%; } - 100% { width: 100%; } -} - -@keyframes theater-knob-grow { - 0% { left: 0%; } - 100% { left: 100%; } -} - -.control-time { - font-size: 0.5rem; - color: var(--text-muted); - font-family: monospace; -} - -/* Glowing Connection Sync Bridge */ -.illus-sync-bridge { - flex-grow: 1; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 6px; - position: relative; - z-index: 1; -} - -.illus-sync-bridge .sync-line { - width: 100%; - height: 2px; - background: rgba(255,255,255,0.06); - position: relative; - overflow: hidden; -} - -.illus-sync-bridge .sync-beam { - position: absolute; - top: 0; - left: -100%; - width: 50px; - height: 100%; - background: linear-gradient(90deg, transparent, var(--accent), var(--success), transparent); - animation: sync-flow 2.5s infinite linear; -} - -@keyframes sync-flow { - 0% { left: -100%; } - 100% { left: 100%; } -} - -.sync-status-badge { - background: oklch(0.68 0.13 150 / 0.12); - border: 1px solid oklch(0.68 0.13 150 / 0.25); - color: oklch(0.68 0.13 150); - font-size: 0.5rem; - font-weight: 800; - letter-spacing: 0.5px; - padding: 2px 6px; - border-radius: 6px; - white-space: nowrap; - animation: active-pulse-green 2s infinite alternate; -} - -@keyframes active-pulse-green { - 0% { box-shadow: 0 0 3px oklch(0.68 0.13 150 / 0.1); } - 100% { box-shadow: 0 0 8px oklch(0.68 0.13 150 / 0.4); } -} - -/* --- Self Hosters Terminal --- */ -.terminal-container { - max-width: 850px; - margin: 3rem auto 0 auto; - background: oklch(0.20 0.025 140); - border: 1px solid rgba(255, 255, 255, 0.08); - border-radius: 16px; - overflow: hidden; - box-shadow: 0 20px 45px rgba(0,0,0,0.5); - text-align: left; -} - -.terminal-header { - background: oklch(0.27 0.035 130); - padding: 0.85rem 1.25rem; - display: flex; - justify-content: space-between; - align-items: center; - border-bottom: 1px solid rgba(255,255,255,0.06); -} - -.terminal-dots { - display: flex; - gap: 6px; -} - -.terminal-dot { - width: 10px; - height: 10px; - border-radius: 50%; -} - -.terminal-dot.red { background: oklch(0.55 0.15 25); } -.terminal-dot.yellow { background: oklch(0.68 0.14 45); } -.terminal-dot.green { background: oklch(0.68 0.13 150); } - -.terminal-tabs { - display: flex; - gap: 0.5rem; -} - -.terminal-tab-btn { - background: none; - border: none; - color: var(--text-muted); - font-weight: 600; - font-size: 0.8rem; - padding: 0.35rem 0.75rem; - border-radius: 6px; - cursor: pointer; - transition: color 0.2s, background-color 0.2s; -} - -.terminal-tab-btn:hover { - color: var(--text); - background: rgba(255, 255, 255, 0.03); -} - -.terminal-tab-btn.active { - color: var(--accent); - background: oklch(0.68 0.13 150 / 0.1); - border: 1px solid oklch(0.68 0.13 150 / 0.2); -} - -.terminal-body { - position: relative; - padding: 1.5rem; -} - -.terminal-pane { - display: none; - margin: 0; -} - -.terminal-pane.active { - display: block; -} - -.terminal-pane pre { - margin: 0; - overflow-x: auto; -} - -.terminal-pane code { - font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; - font-size: 0.85rem; - line-height: 1.6; - color: oklch(0.88 0.015 90); -} - -/* Syntax Highlighting */ -.t-comment { color: oklch(0.55 0.02 110); font-style: italic; } -.t-key { color: oklch(0.72 0.12 45); } -.t-val { color: oklch(0.75 0.10 150); } -.t-str { color: oklch(0.80 0.12 155); } -.t-num { color: oklch(0.75 0.12 50); } - -.terminal-copy-btn { - position: absolute; - top: 1rem; - right: 1.5rem; - background: rgba(255, 255, 255, 0.04); - border: 1px solid rgba(255, 255, 255, 0.08); - color: var(--text-muted); - padding: 0.35rem 0.75rem; - font-size: 0.75rem; - font-weight: 600; - border-radius: 6px; - cursor: pointer; - transition: background-color 0.2s, color 0.2s, opacity 0.2s; - display: flex; - align-items: center; - gap: 0.3rem; -} - -.terminal-copy-btn:hover { - background: rgba(255, 255, 255, 0.08); - color: var(--text); - border-color: rgba(255, 255, 255, 0.15); -} - -.terminal-copy-btn:active { - transform: scale(0.97); -} - -/* --- Footer --- */ -footer { - padding: 4rem 0; - border-top: 0; - text-align: center; - color: var(--text-muted); -} - -footer a { - color: var(--text-muted); - text-decoration: none; - transition: color 0.2s ease; -} - -footer a:hover { - color: var(--text); -} - - -/* --- Animations --- */ -[data-reveal] { - opacity: 0; - transform: translateY(30px); - transition: opacity 0.8s cubic-bezier(0.4, 0, 0.2, 1), transform 0.8s cubic-bezier(0.4, 0, 0.2, 1); -} - -[data-reveal].revealed { - opacity: 1; - transform: translateY(0); -} - -html.theme-light .hero-demo-scene .demo-user-a, -html.theme-light .hero-demo-scene .demo-user-b { - color: oklch(0.36 0.105 150); - background: oklch(0.60 0.12 150 / 0.13); -} - -html.theme-light .hero-demo-scene .extension-mockup { - --bg: oklch(0.935 0.018 110) !important; - --card: oklch(0.995 0.006 100) !important; - --accent: oklch(0.56 0.13 150) !important; - --accent-glow: oklch(0.56 0.13 150 / 0.22) !important; - --text: oklch(0.21 0.03 140) !important; - --text-muted: oklch(0.43 0.03 135) !important; - --success: oklch(0.56 0.13 150) !important; - --error: oklch(0.57 0.17 25) !important; - --glass-border: oklch(0.28 0.035 140 / 0.10) !important; - background: oklch(0.935 0.018 110) !important; - border-color: oklch(0.28 0.035 140 / 0.12); - box-shadow: - 0 20px 42px oklch(0.20 0.025 140 / 0.16), - 0 0 26px oklch(0.82 0.10 85 / 0.12); -} - -html.theme-light .hero-demo-scene .mock-tabs, -html.theme-light .hero-demo-scene .mock-card, -html.theme-light .hero-demo-scene .mock-input { - background: oklch(0.995 0.006 100); - border-color: oklch(0.28 0.035 140 / 0.14); - color: oklch(0.21 0.03 140); -} - -html.theme-light .hero-demo-scene .mock-peer-item { - background: oklch(0.28 0.035 140 / 0.035); - border-color: oklch(0.28 0.035 140 / 0.09); -} - -html.theme-light .hero-demo-scene .mock-joined-room, -html.theme-light .hero-demo-scene .mock-status-pill { - background: oklch(0.56 0.13 150 / 0.10); - border-color: oklch(0.56 0.13 150 / 0.24); -} - -html.theme-light .hero-demo-scene .mock-close-btn:hover { - color: oklch(0.21 0.03 140); - background: oklch(0.28 0.035 140 / 0.08); -} - -/* A direct hash destination is content, not an entrance animation. Keep it - readable from the first paint even before app.js has initialized. */ -section:target [data-reveal], -section:target[data-reveal] { - opacity: 1; - transform: translateY(0); -} - - -/* --- 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; - } -} - -/* --- 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; -} - -/* --- 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; - } -} - -/* --- 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; - } -} - -/* --- 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); -} - -/* --- 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 { - } -} - -/* --- 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; - } -} - -/* --- Hero Live Demo Scene (two synced tabs + extension popup) --- */ -.hero-mockup-wrapper { - flex-direction: column; - gap: 0.9rem; - container-type: inline-size; -} - -/* Fixed height that already fits the open popup, so toggling it never changes - layout (the vertically centered hero never jumps). The empty lower area is - the invisible room the popup drops into. - - Do not move .hero-mockup-wrapper down to align the headline and demo tops: - the extension popup is bottom-anchored inside this scene, so moving the whole - wrapper destroys the vertical balance and leaves no breathing room below. */ -.hero-demo-scene { - --demo-content-y: 0px; - position: relative; - width: 100%; - height: max(calc(59cqw + 100px), 548px); -} - -.demo-tab-stack { - position: relative; - width: 100%; - height: calc(59cqw + 100px + var(--demo-content-y)); -} - -@media (min-width: 1025px) { - .hero-demo-scene { - /* Balance the demo inside the visible hero area below the fixed navbar. - Shift the scene content, not the hero text or grid. */ - --demo-content-y: 2.75rem; - } -} - -/* While initializing, suppress all transitions so the popup can start closed - without a visible open->closed animation on page load. */ -.hero-demo-scene.demo-no-anim, -.hero-demo-scene.demo-no-anim * { - transition-duration: 0s !important; -} - -/* Sync status chip: green "in sync" at rest, flashes the broadcast event */ -.demo-sync-chip { - position: absolute; - top: var(--demo-content-y); - left: 2px; - z-index: 8; - display: inline-flex; - align-items: center; - gap: 7px; - padding: 5px 12px; - border-radius: 99px; - background: oklch(0.68 0.13 150 / 0.12); - border: 1px solid oklch(0.68 0.13 150 / 0.35); - color: #86efac; - font-family: inherit; - font-size: 11px; - font-weight: 800; - letter-spacing: 0.06em; - text-transform: uppercase; - cursor: default; - transition: background 0.3s, border-color 0.3s, color 0.3s; -} - -.demo-chip-dot { - width: 8px; - height: 8px; - border-radius: 50%; - background: oklch(0.68 0.13 150); - box-shadow: 0 0 8px oklch(0.68 0.13 150); - transition: background 0.3s, box-shadow 0.3s; -} - -.demo-sync-chip .demo-chip-event { display: none; } - -.demo-sync-chip.event { - background: oklch(0.68 0.13 150 / 0.14); - border-color: oklch(0.68 0.13 150 / 0.45); - color: oklch(0.85 0.07 150); -} - -.demo-sync-chip.event .demo-chip-dot { - background: oklch(0.75 0.13 150); - box-shadow: 0 0 8px oklch(0.75 0.13 150); -} - -.demo-sync-chip.event .demo-chip-label-sync { display: none; } -.demo-sync-chip.event .demo-chip-event { display: inline; } - -@keyframes demoBlink { - 50% { opacity: 0.35; } -} - -/* Video tab cards: a clean window cascade — the right one is in front */ -.demo-tab-card { - position: absolute; - width: 310px; - width: 70cqw; - background: var(--demo-card); - border: 1px solid rgba(255, 255, 255, 0.09); - border-radius: 14px; - box-shadow: 0 18px 36px rgba(0, 0, 0, 0.45), inset 0 1px rgba(255, 255, 255, 0.05); - overflow: hidden; - cursor: pointer; - user-select: none; - transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1), border-color 0.25s; -} - -.demo-tab-card:hover, -.demo-tab-card:focus-visible { - border-color: oklch(0.68 0.13 150 / 0.55); -} - -/* Card A = the foreground window at the top right ("your" browser, - hosts the extension icon in its toolbar) */ -.demo-tab-a { - top: calc(49px + var(--demo-content-y)); - right: -3%; - transform: rotate(1.2deg); - transform-origin: top right; - z-index: 3; -} - -.demo-tab-a:hover { - z-index: 4; - transform: rotate(1.2deg) scale(1.02); - box-shadow: 0 25px 50px rgba(0, 0, 0, 0.65), 0 0 20px oklch(0.68 0.13 150 / 0.2); -} - -/* Card B = the friend's window, lower left, tucked behind */ -.demo-tab-b { - top: 142px; - top: calc(20cqw + 67px + var(--demo-content-y)); - left: -3%; - transform: rotate(-1.6deg); - transform-origin: top left; - z-index: 2; -} - -.demo-tab-b:hover { - z-index: 4; - transform: rotate(-1.6deg) scale(1.02); - box-shadow: 0 25px 50px rgba(0, 0, 0, 0.65), 0 0 20px oklch(0.68 0.13 150 / 0.2); -} - -/* When the popup slides in from the right edge, the tabs step aside */ -.hero-demo-scene.popup-open .demo-tab-a { - top: calc(22px + var(--demo-content-y)); - transform: rotate(1deg) translate(-44%, 20%) scale(0.88); -} - -.hero-demo-scene.popup-open .demo-tab-a:hover { - z-index: 4; - transform: rotate(1deg) translate(-44%, 20%) scale(0.92); - box-shadow: 0 25px 50px rgba(0, 0, 0, 0.65), 0 0 20px oklch(0.68 0.13 150 / 0.2); -} - -.hero-demo-scene.popup-open .demo-tab-b { - top: calc(20cqw + 40px + var(--demo-content-y)); - transform: rotate(-1.6deg) translate(0, 14%) scale(0.94); -} - -.hero-demo-scene.popup-open .demo-tab-b:hover { - z-index: 4; - transform: rotate(-1.6deg) translate(0, 14%) scale(0.98); - box-shadow: 0 25px 50px rgba(0, 0, 0, 0.65), 0 0 20px oklch(0.68 0.13 150 / 0.2); -} - -.demo-tab-titlebar { - display: flex; - align-items: center; - gap: 8px; - padding: 6px 8px 6px 10px; - background: var(--demo-toolbar); - border-bottom: 1px solid rgba(255, 255, 255, 0.06); - font-size: 10.5px; - color: var(--text-muted); - font-weight: 600; -} - -.demo-tab-dots { - display: flex; - gap: 4px; - flex-shrink: 0; -} - -.demo-tab-dots i { - display: block; - width: 7px; - height: 7px; - border-radius: 50%; - background: oklch(0.32 0.03 125); -} - -.demo-tab-title { - display: flex; - align-items: center; - gap: 5px; - flex: 1; - min-width: 0; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.demo-tab-title svg { flex-shrink: 0; } - -.demo-tab-user { - flex-shrink: 0; - font-size: 9.5px; - font-weight: 700; - padding: 2px 7px; - border-radius: 99px; -} - -.demo-user-a { - color: oklch(0.80 0.10 150); - background: oklch(0.68 0.13 150 / 0.15); -} - -.demo-user-b { - color: #86efac; - background: oklch(0.68 0.13 150 / 0.12); -} - -/* Extension launcher pinned in the foreground window's toolbar */ -.demo-ext-launcher { - position: relative; - flex-shrink: 0; - display: flex; - align-items: center; - justify-content: center; - width: 24px; - height: 24px; - padding: 0; - border-radius: 7px; - background: oklch(0.30 0.03 130); - border: 1px solid rgba(255, 255, 255, 0.12); - cursor: pointer; - transition: border-color 0.25s, box-shadow 0.25s, transform 0.15s; -} - -.demo-ext-launcher img { - display: block; - width: 16px; - height: 16px; - border-radius: 4px; -} - -.demo-ext-launcher:hover { - border-color: var(--accent); - transform: translateY(-1px); -} - -.demo-ext-launcher:active { - transform: scale(0.94); -} - -.hero-demo-scene.popup-open .demo-ext-launcher { - border-color: oklch(0.68 0.13 150 / 0.7); - box-shadow: 0 0 10px oklch(0.68 0.13 150 / 0.45); -} - -.demo-ext-launcher::after { - content: ''; - position: absolute; - top: -3px; - right: -3px; - width: 8px; - height: 8px; - border-radius: 50%; - background: oklch(0.68 0.13 150); - box-shadow: 0 0 8px oklch(0.68 0.13 150); - opacity: 0; - transition: opacity 0.3s; -} - -.hero-demo-scene:not(.popup-open) .demo-ext-launcher:not(.demo-ext-static)::after { - opacity: 1; - animation: demoBlink 1.4s ease-in-out infinite; -} - -/* Card B shows the same extension icon, purely to signal the friend has it - installed too. It is not interactive and does not blink or glow. */ -.demo-ext-static, -.demo-ext-static:hover { - cursor: default; - border-color: rgba(255, 255, 255, 0.12); - transform: none; - box-shadow: none; -} - -.demo-ext-static::after { display: none; } - -/* Abstract "video" artwork with a slow cinematic pan/zoom while playing */ -.demo-video { - position: relative; - aspect-ratio: 16 / 9; - background: var(--demo-sky-bottom); - overflow: hidden; -} - -.demo-video::after { - content: ''; - position: absolute; - inset: 0; - z-index: 1; - pointer-events: none; - /* Cinematic vignette: soft edge falloff + heavier floor for the controls */ - box-shadow: - inset 0 0 0 1px rgba(255, 255, 255, 0.05), - inset 0 0 34px oklch(0.04 0.015 145 / 0.32), - inset 0 -26px 40px oklch(0.04 0.015 145 / 0.26); -} - -html.theme-light .hero-demo-scene .demo-video::after { - box-shadow: - inset 0 0 0 1px oklch(0.30 0.04 145 / 0.13), - inset 0 18px 32px oklch(0.98 0.03 90 / 0.1), - inset 0 -22px 34px oklch(0.18 0.05 145 / 0.16); -} - -.demo-video-art { - position: absolute; - inset: 0; - background: - radial-gradient(30% 42% at 76% 29%, var(--demo-glow), transparent 64%), - radial-gradient(90% 85% at 18% 5%, var(--demo-glow), transparent 66%), - linear-gradient(160deg, var(--demo-sky-top) 0%, var(--demo-sky-mid) 52%, var(--demo-sky-bottom) 100%); - transform-origin: 60% 40%; - animation: - demoKenBurns 9s ease-in-out infinite alternate, - demoArtShift 7s ease-in-out infinite alternate; - animation-play-state: paused; -} - -/* Soft light drifting through the scene */ -.demo-video-art::before { - content: ''; - position: absolute; - top: -25%; - left: -12%; - width: 70%; - aspect-ratio: 1; - border-radius: 50%; - background: radial-gradient(circle, var(--demo-glow), transparent 65%); - animation: demoGlowDrift 7s ease-in-out infinite alternate; - animation-play-state: paused; -} - -.demo-video-art::after { - content: ''; - position: absolute; - inset: 0; - background: linear-gradient(105deg, transparent 30%, oklch(0.75 0.13 150 / 0.14) 45%, oklch(0.75 0.10 150 / 0.1) 55%, transparent 70%); - transform: translateX(-100%); - animation: demoSheen 2.8s linear infinite; - animation-play-state: paused; -} - -/* Playing runs the animations; pausing freezes them mid-frame, so a synced - pause is visible at a glance */ -.demo-tab-card.playing .demo-video-art, -.demo-tab-card.playing .demo-video-art::before, -.demo-tab-card.playing .demo-video-art::after { - animation-play-state: running; -} - -@keyframes demoKenBurns { - from { transform: scale(1) translate(0, 0); } - to { transform: scale(1.18) translate(-3%, 2%); } -} - -@keyframes demoArtShift { - from { filter: hue-rotate(0deg) brightness(1); } - to { filter: hue-rotate(40deg) brightness(1.15); } -} - -@keyframes demoGlowDrift { - from { transform: translate(0, 0); } - to { transform: translate(65%, 30%); } -} - -@keyframes demoSheen { - to { transform: translateX(100%); } -} - -/* Play / pause overlay */ -.demo-play-overlay { - position: absolute; - inset: 0; - display: flex; - align-items: center; - justify-content: center; - background: oklch(0.10 0.01 135 / 0.35); - transition: opacity 0.25s, background 0.25s; -} - -.demo-icon-wrap { - display: flex; - align-items: center; - justify-content: center; - width: 46px; - height: 46px; - border-radius: 50%; - background: oklch(0.20 0.025 140 / 0.75); - border: 1px solid rgba(255, 255, 255, 0.25); - color: white; - box-shadow: 0 4px 18px rgba(0, 0, 0, 0.5); - backdrop-filter: blur(4px); - transition: transform 0.2s; -} - -.demo-tab-card:hover .demo-icon-wrap { transform: scale(1.1); } - -.demo-play-overlay .icon-pause { display: none; } -.demo-tab-card.playing .icon-play { display: none; } -.demo-tab-card.playing .icon-pause { display: block; } - -.demo-tab-card.playing .demo-play-overlay { - opacity: 0; - background: transparent; -} - -.demo-tab-card.playing:hover .demo-play-overlay, -.demo-tab-card.playing:focus-visible .demo-play-overlay { - opacity: 1; -} - -/* Timestamp + seekable progress bar */ -.demo-video-controls { - position: absolute; - left: 0; - right: 0; - bottom: 0; - display: flex; - align-items: center; - gap: 8px; - padding: 14px 10px 8px; - background: linear-gradient(transparent, var(--demo-control-fade)); - font-size: 9.5px; - color: oklch(0.88 0.015 90); - z-index: 1; -} - -.demo-time, -.demo-duration { - flex-shrink: 0; - font-family: ui-monospace, SFMono-Regular, Menlo, monospace; - font-weight: 700; - letter-spacing: 0.03em; - font-variant-numeric: tabular-nums; -} - -.demo-duration { - font-weight: 600; - opacity: 0.7; -} - -/* Decorative player chrome: play state glyph, volume, HD, fullscreen */ -.demo-ctrl-icon { - display: flex; - align-items: center; - flex-shrink: 0; - opacity: 0.85; -} - -.demo-ctrl-play .icon-pause { display: none; } -.demo-tab-card.playing .demo-ctrl-play .icon-play { display: none; } -.demo-tab-card.playing .demo-ctrl-play .icon-pause { display: block; } - -.demo-ctrl-badge { - flex-shrink: 0; - font-size: 7px; - font-weight: 800; - letter-spacing: 0.06em; - line-height: 1; - padding: 2px 3px; - border: 1px solid currentColor; - border-radius: 3px; - opacity: 0.65; -} - -.demo-progress { - position: relative; - flex: 1; - height: 5px; - border-radius: 99px; - background: var(--demo-track); - cursor: pointer; -} - -/* Generous invisible hit area for seeking */ -.demo-progress::before { - content: ''; - position: absolute; - inset: -8px 0; -} - -/* Buffered range behind the playhead, like a real player */ -.demo-progress-buffer { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: 56%; - border-radius: 99px; - background: oklch(0.92 0.02 95 / 0.14); - pointer-events: none; -} - -.demo-progress-fill { - position: relative; - height: 100%; - width: 30%; - border-radius: 99px; - background: linear-gradient(90deg, oklch(0.68 0.13 150), oklch(0.62 0.14 45)); - pointer-events: none; -} - -/* Scrub thumb, revealed on hover */ -.demo-progress-fill::after { - content: ''; - position: absolute; - right: -5px; - top: 50%; - width: 11px; - height: 11px; - margin-top: -5.5px; - border-radius: 50%; - background: oklch(0.88 0.015 90); - box-shadow: 0 0 6px oklch(0.68 0.13 150 / 0.8); - opacity: 0; - transform: scale(0.6); - transition: opacity 0.2s, transform 0.2s; -} - -.demo-tab-card:hover .demo-progress-fill::after { - opacity: 1; - transform: scale(1); -} - -/* Sync pulse ring on both cards */ -.demo-tab-card.sync-pulse { - animation: demoCardPulse 0.9s ease-out; -} - -@keyframes demoCardPulse { - 0% { box-shadow: 0 18px 36px rgba(0, 0, 0, 0.45), 0 0 0 0 oklch(0.68 0.13 150 / 0.65); } - 100% { box-shadow: 0 18px 36px rgba(0, 0, 0, 0.45), 0 0 0 22px oklch(0.68 0.13 150 / 0); } -} - -/* The existing popup mockup slides in at the right edge, dropping out of - the toolbar icon of the foreground window */ -.hero-demo-scene .extension-mockup { - position: absolute; - bottom: 0; - right: 0; - z-index: 5; - width: 292px; - width: min(320px, 66cqw); - margin: 0; - transform-origin: top right; - transition: transform 0.45s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.35s, visibility 0.45s; -} - -@media (min-width: 1025px) { - .hero-demo-scene .extension-mockup { - /* Counter the content shift so the opened panel is balanced below nav. */ - bottom: 0.625rem; - } -} - -.hero-demo-scene:not(.popup-open) .extension-mockup { - transform: translate(2%, -3%) scale(0.5); - opacity: 0; - visibility: hidden; - pointer-events: none; -} - -/* Ghost cursor for the automated walkthrough */ -.demo-cursor { - position: absolute; - top: 52%; - left: 46%; - z-index: 20; - width: 22px; - height: 22px; - opacity: 0; - pointer-events: none; - filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.6)); - transition: top 0.65s cubic-bezier(0.45, 0.05, 0.25, 1), left 0.65s cubic-bezier(0.45, 0.05, 0.25, 1), opacity 0.35s; -} - -.demo-cursor.visible { opacity: 1; } - -.demo-cursor svg { - display: block; - width: 100%; - height: 100%; -} - -.demo-cursor::before { - content: ''; - position: absolute; - top: -6px; - left: -8px; - width: 32px; - height: 32px; - border-radius: 50%; - border: 2px solid oklch(0.75 0.13 150); - opacity: 0; - transform: scale(0.35); -} - -.demo-cursor.clicking::before { - animation: demoClickRipple 0.45s ease-out; -} - -@keyframes demoClickRipple { - 0% { opacity: 0.9; transform: scale(0.35); } - 100% { opacity: 0; transform: scale(1.25); } -} - -/* Caption below the scene */ -.demo-hint { - margin: 0.75rem auto 0; - max-width: min(100%, 34rem); - text-align: center; - font-size: 0.82rem; - line-height: 1.45; - color: var(--text-muted); - opacity: 0; - transition: opacity 0.6s; -} - -.demo-hint.show { opacity: 0.75; } - -/* Tablet/Small Desktop: fall back to the classic static popup mockup */ -@media (max-width: 1023px) { - .hero-demo-scene, - .hero-demo-scene.popup-open { - height: auto; - max-width: 320px; - margin: 0 auto; - } - - .demo-tab-card, - .demo-tab-stack, - .demo-sync-chip, - .demo-cursor, - .demo-hint { - display: none !important; - } - - .hero-demo-scene .extension-mockup, - .hero-demo-scene:not(.popup-open) .extension-mockup { - position: static; - width: 100%; - transform: none; - opacity: 1; - visibility: visible; - pointer-events: auto; - } -} - -@media (prefers-reduced-motion: reduce) { - .hero-demo-scene:not(.popup-open) .demo-ext-launcher::after { - animation: none; - } -} - -/* --- Story walkthrough additions: room creation, invite flight, film --- */ - -/* The stick-figure film carries the motion now — keep only the color drift - on the backdrop (the animation shorthand resets play-state, so re-pause) */ -.demo-video-art { - animation: demoArtShift 7s ease-in-out infinite alternate; - animation-play-state: paused; -} - -.demo-film { - position: absolute; - inset: 0; - width: 100%; - height: 100%; -} - -.hero-demo-scene .film-stars { fill: var(--demo-stars) !important; } -.hero-demo-scene .film-clouds { fill: var(--demo-stars) !important; } -.hero-demo-scene .film-mist-scroll { fill: var(--demo-stars) !important; } -.hero-demo-scene .film-shooting-star { stroke: var(--demo-stars) !important; } -.hero-demo-scene .film-moon { - fill: var(--demo-moon) !important; - filter: drop-shadow(0 0 6px var(--demo-moon)); -} -.hero-demo-scene .film-mtn-scroll { fill: var(--demo-mountain) !important; } -.hero-demo-scene .film-mid-scroll > * { fill: var(--demo-midground) !important; } -.hero-demo-scene .film-fore-scroll > * { fill: var(--demo-foreground) !important; } - -/* By day the birds read as silhouettes, not chalk marks */ -html.theme-light .hero-demo-scene .film-birds { stroke: oklch(0.32 0.06 145 / 0.65); } -html.theme-light :is(.hero-demo-scene, .step-illustration-3) .film-hero-bird { stroke: oklch(0.32 0.06 145 / 0.75); } - -/* Stars: staggered twinkle so the sky feels alive */ -.film-star { - transform-box: fill-box; - transform-origin: center; - animation: filmTwinkle 2.8s ease-in-out infinite; -} -.film-star:nth-child(2) { animation-duration: 3.4s; animation-delay: 0.5s; } -.film-star:nth-child(3) { animation-duration: 2.4s; animation-delay: 1.1s; } -.film-star:nth-child(4) { animation-duration: 3.6s; animation-delay: 0.3s; } -.film-star:nth-child(5) { animation-duration: 2.6s; animation-delay: 1.6s; } -.film-star:nth-child(6) { animation-duration: 3.2s; animation-delay: 0.8s; } -.film-star:nth-child(7) { animation-duration: 2.2s; animation-delay: 1.3s; } - -@keyframes filmTwinkle { - 0%, 100% { opacity: 0.25; transform: scale(0.85); } - 50% { opacity: 1; transform: scale(1.15); } -} - -/* Moon: gentle glow pulse + slow arc drift. The drift lives on the wrapper - group so the craters travel with the disc; the glow stays on the circle. - (Step-3 uses a bare .film-moon without wrapper — filmMoon still covers it.) */ -.film-moon-g { - transform-box: fill-box; - transform-origin: center; - animation: filmMoonDrift 6s ease-in-out infinite alternate; - animation-play-state: paused; -} - -.film-moon-g .film-moon { - animation-name: filmMoonGlow; -} - -@keyframes filmMoonDrift { - from { transform: translate(0, 0); } - to { transform: translate(-6px, 3px); } -} - -@keyframes filmMoonGlow { - from { filter: drop-shadow(0 0 4px oklch(0.95 0.01 100 / 0.5)); } - to { filter: drop-shadow(0 0 10px oklch(0.95 0.01 100 / 0.95)); } -} - -.film-moon { - transform-box: fill-box; - transform-origin: center; - animation: filmMoon 6s ease-in-out infinite alternate; - animation-play-state: paused; -} - -@keyframes filmMoon { - from { transform: translate(0, 0); filter: drop-shadow(0 0 4px oklch(0.95 0.01 100 / 0.5)); } - to { transform: translate(-6px, 3px); filter: drop-shadow(0 0 10px oklch(0.95 0.01 100 / 0.95)); } -} - -.film-mtn-scroll { - transform: translateX(var(--scroll-back, 0px)); -} -.film-mid-scroll { - transform: translateX(var(--scroll-mid, 0px)); -} -.film-fore-scroll { - transform: translateX(var(--scroll-fore, 0px)); -} -/* A single gliding bird carries the scene: the outer group rides the - JS-driven drift vars, the inner group flaps its wings via scaleY. */ -.film-hero-bird-drift { - transform: translate(var(--lantern-x, 0px), var(--bounce-y, 0px)); -} - -.film-hero-bird { - transform-box: fill-box; - transform-origin: center; - animation: filmWingFlap 0.85s ease-in-out infinite alternate; - animation-play-state: paused; -} - -@keyframes filmWingFlap { - from { transform: scaleY(1); } - to { transform: scaleY(0.5); } -} - -/* Twinkle & Glow play state bindings */ -.film-star { - transform-box: fill-box; - transform-origin: center; - animation: filmTwinkle 2.8s ease-in-out infinite; - animation-play-state: paused; -} -.demo-tab-card.playing :is(.film-star, .film-moon, .film-moon-g, .film-cloud, .film-shooting-star, .film-birds, .film-hero-bird) { - animation-play-state: running; -} - -/* Drifting clouds: gentle back-and-forth so there is no wrap pop */ -.film-cloud { - transform-box: fill-box; - animation: filmCloudDrift 26s ease-in-out infinite alternate; - animation-play-state: paused; -} - -.film-cloud-2 { - animation-duration: 34s; - animation-delay: -12s; -} - -@keyframes filmCloudDrift { - from { transform: translateX(-6px); } - to { transform: translateX(10px); } -} - -/* A shooting star streaks across the sky every few loops */ -.film-shooting-star { - opacity: 0; - animation: filmShoot 11s linear infinite; - animation-play-state: paused; -} - -@keyframes filmShoot { - 0%, 87% { opacity: 0; transform: translate(0, 0); } - 89% { opacity: 0.9; } - 95%, 100% { opacity: 0; transform: translate(-46px, 20px); } -} - -/* A distant flock crosses the valley, fading out mid-flight */ -.film-birds { - animation: filmBirds 16s linear infinite; - animation-play-state: paused; -} - -@keyframes filmBirds { - 0% { transform: translate(0, 0); opacity: 0; } - 4% { opacity: 0.75; } - 42% { opacity: 0.75; } - 48%, 100% { transform: translate(185px, -12px); opacity: 0; } -} - -/* Valley mist rides the slow background parallax between tree layers */ -.film-mist-scroll { - transform: translateX(var(--scroll-back, 0px)); -} - -/* Play button pulsing indicator at the end of the walkthrough */ -.hero-demo-scene.demo-complete #demo-tab-a:not(.playing) .demo-icon-wrap { - animation: playButtonPulse 1.6s ease-in-out infinite; - border-color: oklch(0.68 0.13 150); - box-shadow: 0 0 15px oklch(0.68 0.13 150 / 0.8); -} -@keyframes playButtonPulse { - 0%, 100% { transform: scale(1); box-shadow: 0 0 10px oklch(0.68 0.13 150 / 0.5); } - 50% { transform: scale(1.12); box-shadow: 0 0 20px oklch(0.68 0.13 150 / 0.9); } -} - -/* Seek-reset: scrubbing restarts animations from frame zero */ -.demo-film.demo-reset * { - animation: none !important; -} - -/* Seek-break: scrubbing the timeline visibly glitches both videos so the - jump reads as a real cut instead of an invisible time update. A scanline - sweep + brightness pop, and the film animations restart mid-stride. */ -.demo-video-art { transition: filter 0.12s ease-out; } - -.demo-tab-card.demo-seeking .demo-video-art { - animation-play-state: paused; - filter: brightness(1.9) saturate(1.4) blur(0.6px); -} - -.demo-tab-card .demo-seek-sweep { - position: absolute; - inset: 0; - z-index: 4; - pointer-events: none; - opacity: 0; - background: linear-gradient(90deg, - transparent 0%, - oklch(0.75 0.13 150 / 0.35) 20%, - rgba(255, 255, 255, 0.55) 50%, - oklch(0.75 0.13 150 / 0.35) 80%, - transparent 100%); - mix-blend-mode: screen; - transform: translateX(-100%); -} - -.demo-tab-card.demo-seeking .demo-seek-sweep { - animation: demoSeekSweep 0.34s ease-out; -} - -@keyframes demoSeekSweep { - 0% { opacity: 0; transform: translateX(-100%); } - 20% { opacity: 1; } - 100% { opacity: 0; transform: translateX(100%); } -} - -/* The sync chip only appears once the room is connected (story progression) */ -.demo-sync-chip { - opacity: 0; - transform: translateY(-4px); - pointer-events: none; - transition: background 0.3s, border-color 0.3s, color 0.3s, opacity 0.4s, transform 0.4s; -} - -.hero-demo-scene.connected .demo-sync-chip { - opacity: 1; - transform: none; - pointer-events: auto; -} - -/* Toast on the friend's window ("Room joined", "Tab selected") */ -.demo-tab-toast { - position: absolute; - top: 36px; - left: 50%; - transform: translate(-50%, -6px); - z-index: 2; - padding: 4px 10px; - border-radius: 99px; - background: oklch(0.20 0.025 140 / 0.92); - border: 1px solid oklch(0.68 0.13 150 / 0.5); - color: oklch(0.85 0.07 150); - font-size: 9.5px; - font-weight: 700; - white-space: nowrap; - opacity: 0; - pointer-events: none; - transition: opacity 0.3s, transform 0.3s; -} - -.demo-tab-toast.show { - opacity: 1; - transform: translate(-50%, 0); -} - -/* Invite link pill flying from the popup to the friend's window */ -.demo-invite-fly { - position: absolute; - z-index: 30; - display: inline-flex; - align-items: center; - gap: 5px; - padding: 4px 10px; - border-radius: 99px; - background: var(--accent); - color: var(--text-on-green); - font-size: 9.5px; - font-weight: 700; - white-space: nowrap; - box-shadow: 0 6px 18px oklch(0.68 0.13 150 / 0.5); - opacity: 0; - pointer-events: none; - transition: left 0.75s cubic-bezier(0.3, 0.7, 0.4, 1), top 0.75s cubic-bezier(0.3, 0.7, 0.4, 1), opacity 0.3s; -} - -/* Attention flash (e.g. on the video select while "choosing the tab") */ -.demo-attn { - animation: demoAttn 0.7s ease-in-out 2; -} - -@keyframes demoAttn { - 50% { box-shadow: 0 0 0 3px oklch(0.68 0.13 150 / 0.75); } -} - - -/* --- Bento Card Host Control Mockup Additions --- */ - -.bento-content-wrap { - display: flex; - flex-direction: column; - gap: 1.2rem; - width: 100%; -} - -.bento-host-control-mockup { - background: oklch(0.20 0.025 140 / 0.6); - border: 1px solid rgba(255, 255, 255, 0.05); - border-radius: 12px; - padding: 0.75rem 1rem; - display: flex; - flex-direction: column; - gap: 0.5rem; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2); - width: 100%; - box-sizing: border-box; -} - -.bento-mockup-header { - font-size: 0.65rem; - text-transform: uppercase; - letter-spacing: 0.5px; - color: var(--text-muted); - font-weight: 700; -} - -.bento-mockup-row { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - font-size: 0.75rem; - color: white; - font-weight: 600; - margin-top: 2px; -} - -/* Custom switch toggle representation */ -.bento-switch-wrap { - position: relative; - display: inline-block; - width: 28px; - height: 16px; - flex-shrink: 0; -} - -.bento-switch-wrap input { - opacity: 0; - width: 0; - height: 0; -} - -.bento-switch-slider { - position: absolute; - cursor: default; - top: 0; left: 0; right: 0; bottom: 0; - background-color: rgba(255, 255, 255, 0.1); - border: 1px solid rgba(255, 255, 255, 0.15); - border-radius: 16px; - transition: .3s; -} - -.bento-switch-slider:before { - position: absolute; - content: ""; - height: 10px; - width: 10px; - left: 2px; - bottom: 2px; - background-color: oklch(0.68 0.02 100); - border-radius: 50%; - transition: .3s; -} - -.bento-switch-wrap input:checked + .bento-switch-slider { - background-color: oklch(0.68 0.13 150 / 0.25); - border-color: oklch(0.68 0.13 150 / 0.6); - box-shadow: 0 0 8px oklch(0.68 0.13 150 / 0.3); -} - -.bento-switch-wrap input:checked + .bento-switch-slider:before { - transform: translateX(12px); - background-color: var(--accent); - box-shadow: 0 0 6px var(--accent-glow); -} - -/* --- Step 2 & Step 3 Visual Redesign Styles --- */ - -/* Step 2 Browser video player mock */ -.illus-video-player-mock { - width: 85%; - height: 65%; - background: linear-gradient(135deg, oklch(0.20 0.04 145), oklch(0.12 0.02 138)); - border: 1px solid rgba(255, 255, 255, 0.05); - border-radius: 8px; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - position: relative; - margin-top: 15px; -} - -.video-mock-play-button { - width: 32px; - height: 32px; - border-radius: 50%; - background: rgba(255, 255, 255, 0.1); - border: 1px solid rgba(255, 255, 255, 0.2); - display: flex; - align-items: center; - justify-content: center; - color: white; - font-size: 0.8rem; - cursor: default; - box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); -} - -.video-mock-timeline { - position: absolute; - bottom: 8px; - left: 8px; - right: 8px; - height: 3px; - background: rgba(255, 255, 255, 0.15); - border-radius: 2px; -} - -.video-mock-timeline-fill { - width: 35%; - height: 100%; - background: var(--accent); - border-radius: 2px; -} - -/* Step 2 extension popup dropdown alignment */ -.illus-extension-popup.drop-down { - top: 2.2rem; - right: 0.6rem; - width: 145px; - animation: popup-dropdown-float 4s ease-in-out infinite; -} - -@keyframes popup-dropdown-float { - 0%, 100% { transform: translateY(0); } - 50% { transform: translateY(-3px); } -} - -.status-badge-select { - background: oklch(0.68 0.13 150 / 0.12); - color: oklch(0.85 0.07 150); - font-size: 0.58rem; - font-weight: 700; - padding: 2px 6px; - border-radius: 4px; - border: 1px solid oklch(0.68 0.13 150 / 0.2); - display: inline-flex; - align-items: center; - text-transform: uppercase; - letter-spacing: 0.2px; -} - -.popup-form-group { - margin-top: 6px; - width: 100%; - text-align: left; -} - -.popup-form-label { - display: block; - font-size: 0.6rem; - color: var(--text-muted); - margin-bottom: 4px; - font-weight: 700; -} - -.popup-select-wrapper { - position: relative; - width: 100%; -} - -.popup-select-mock { - width: 100%; - background: var(--card); - border: 1px solid rgba(255, 255, 255, 0.12); - color: white; - padding: 6px; - border-radius: 6px; - font-family: inherit; - font-size: 0.62rem; - outline: none; - appearance: none; - -webkit-appearance: none; - font-weight: 600; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - cursor: default; - display: block; - box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.2); -} - -.popup-cursor-pointer { - position: absolute; - bottom: -15px; - right: 15px; - font-size: 1.1rem; - pointer-events: none; - z-index: 10; - animation: pointer-click 1.6s infinite ease-in-out; -} - -@keyframes pointer-click { - 0%, 100% { transform: translate(0, 0) scale(1); } - 50% { transform: translate(-3px, -3px) scale(0.9); } -} - -/* Step 3 forest movie continuous animation bindings */ -.step-illustration-3 .film-mtn-scroll { - animation: scrollBack 12s linear infinite !important; -} -.step-illustration-3 .film-mid-scroll { - animation: scrollMid 6s linear infinite !important; -} -.step-illustration-3 .film-fore-scroll { - animation: scrollFore 3s linear infinite !important; -} -.step-illustration-3 .film-hero-bird { - animation: filmWingFlap 0.85s ease-in-out infinite !important; - animation-direction: alternate !important; -} - -@keyframes scrollBack { - 0% { transform: translateX(0); } - 100% { transform: translateX(-160px); } -} -@keyframes scrollMid { - 0% { transform: translateX(0); } - 100% { transform: translateX(-160px); } -} -@keyframes scrollFore { - 0% { transform: translateX(0); } - 100% { transform: translateX(-160px); } -} - -/* --- High Fidelity Micro-Animations & Identity additions --- */ - -/* Scroll progress bar glowing line at the top */ -.scroll-progress-bar { - position: fixed; - top: 0; - left: 0; - height: 3px; - background: linear-gradient(90deg, var(--accent), oklch(0.62 0.14 45), oklch(0.62 0.14 45)); - z-index: 9999; - width: 0%; - transition: width 0.1s ease-out; - box-shadow: 0 0 8px var(--accent-glow); -} - -.scroll-progress-bar::after { - content: ''; - position: absolute; - right: -3px; - top: 50%; - width: 10px; - height: 7px; - border-radius: 0 70% 0 70%; - background: var(--accent-green); - box-shadow: 0 0 6px var(--accent-glow); - transform: translateY(-50%) rotate(24deg); -} - -/* Navigation logo pop & spin */ -.nav-logo-img { - animation: logo-entrance 1.2s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; - transform-origin: center; - transition: transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1); - will-change: transform, opacity; -} - -.nav-logo-img:hover { - transform: scale(1.15) rotate(360deg); -} - -@keyframes logo-entrance { - 0% { - transform: scale(0) rotate(-180deg); - opacity: 0; - } - 100% { - transform: scale(1) rotate(0deg); - opacity: 1; - } -} - -/* Glowing reflection sweep on buttons */ -.btn-primary, .btn-firefox, .btn-secondary { - position: relative; - overflow: hidden; -} - -.btn-primary::after, .btn-firefox::after, .btn-secondary::after { - content: ''; - position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 100%; - background: linear-gradient( - 90deg, - rgba(255, 255, 255, 0) 0%, - rgba(255, 255, 255, 0.25) 50%, - rgba(255, 255, 255, 0) 100% - ); - transform: skewX(-25deg); - transition: none; - pointer-events: none; -} - -.btn-primary:hover::after, .btn-firefox:hover::after, .btn-secondary:hover::after { - left: 100%; - transition: left 0.8s ease-in-out; -} - -/* Step 2 mockup control panel styles */ -.popup-mock-controls { - display: flex; - gap: 8px; - margin-top: 4px; - width: 100%; -} - -.popup-mock-btn { - flex: 1; - text-align: center; - padding: 6px; - border-radius: 6px; - font-size: 0.62rem; - font-weight: 700; - color: white; - cursor: default; - opacity: 0.75; -} - -.popup-mock-btn.play { - background: oklch(0.68 0.13 150); - color: var(--text-on-green); -} - -.popup-mock-btn.pause { - background: oklch(0.55 0.15 25); -} - -/* Phone and portrait-tablet widths: keep the full hero demo out of the page - flow. The demo modal mounts the same node in its own fixed layer. */ -@media (max-width: 900px) { - .hero-grid > .hero-mockup-wrapper { - display: none !important; - } -} - -@media (max-width: 900px) { - .hero-github-cta { - display: none; - } - - .btn-demo-mobile { - display: inline-flex; - } -} - -body.mobile-demo-open { - overflow: hidden; -} - -.mobile-demo-modal[hidden] { - display: none; -} - -.mobile-demo-modal { - position: fixed; - inset: 0; - z-index: 2400; - display: flex; - flex-direction: column; - padding: 0 !important; -} - -.mobile-demo-backdrop { - position: absolute; - inset: 0; - background: oklch(0.10 0.01 135 / 0.25); - backdrop-filter: blur(16px); - -webkit-backdrop-filter: blur(16px); -} - -.mobile-demo-panel { - position: relative; - z-index: 1; - width: 100% !important; - height: 100% !important; - max-height: none !important; - display: flex; - flex-direction: column; - overflow: hidden; - background: transparent !important; - backdrop-filter: none !important; - -webkit-backdrop-filter: none !important; - border: none !important; - border-radius: 0 !important; - box-shadow: none !important; -} - -.mobile-demo-header { - display: flex; - align-items: center; - justify-content: space-between; - gap: 1rem; - padding: 0.85rem 0.95rem; - border-bottom: none !important; - flex-shrink: 0; -} - -.mobile-demo-header h2 { - margin: 0; - font-size: 0.95rem; - line-height: 1.2; - color: oklch(0.96 0.01 90); -} - -.mobile-demo-close { - display: inline-flex; - align-items: center; - justify-content: center; - width: 38px; - height: 38px; - padding: 0; - border: 1px solid rgba(255, 255, 255, 0.12); - border-radius: 10px; - background: oklch(0.27 0.035 130 / 0.72); - color: oklch(0.88 0.015 90); - cursor: pointer; - flex-shrink: 0; -} - -.mobile-demo-stage { - position: relative; - flex: 1; - min-height: 0; - overflow: hidden !important; - padding: 1rem 0.75rem 1.25rem; - overscroll-behavior: contain; - display: flex !important; - flex-direction: column !important; - justify-content: center !important; - align-items: center !important; -} - -/* Ensure the demo scene canvas is transparent inside the modal to let the glass backdrop blend */ -.mobile-demo-stage .hero-demo-scene { - background: transparent !important; - border: none !important; - box-shadow: none !important; -} - -/* Ensure the close button hover stays consistent in dark glass theme */ -.mobile-demo-close:hover { - background: rgba(51, 65, 85, 0.85) !important; -} - -/* Light Mode Frosted Glass overrides */ -html.theme-light .mobile-demo-backdrop { - background: oklch(0.95 0.01 100 / 0.2) !important; -} - -html.theme-light .mobile-demo-panel { - background: transparent !important; - border: none !important; - box-shadow: none !important; -} - -html.theme-light .mobile-demo-header { - border-bottom: none !important; -} - -html.theme-light .mobile-demo-header h2 { - color: var(--text) !important; -} - -html.theme-light .mobile-demo-close { - background: rgba(255, 255, 255, 0.4) !important; - border-color: oklch(0.20 0.025 140 / 0.1) !important; - color: var(--text) !important; -} - -html.theme-light .mobile-demo-close:hover { - background: rgba(255, 255, 255, 0.75) !important; -} - -.mobile-demo-stage .hero-mockup-wrapper { - display: flex !important; - position: relative; - width: min(100%, 560px); - margin: 0 auto; - will-change: transform; -} - -/* Mobile S / short screens */ -@media (max-height: 600px) { - .mobile-demo-stage .hero-mockup-wrapper { - transform: scale(0.68) !important; - transform-origin: top center !important; - height: 380px !important; - } -} - -/* Mobile M/L / medium screens */ -@media (min-height: 601px) and (max-height: 740px) { - .mobile-demo-stage .hero-mockup-wrapper { - transform: scale(0.82) !important; - transform-origin: top center !important; - height: 460px !important; - } -} - -/* Default / tall screens */ -@media (min-height: 741px) { - .mobile-demo-stage .hero-mockup-wrapper { - transform: scale(1) !important; - transform-origin: top center !important; - height: 550px !important; - } -} - -.mobile-demo-stage .hero-demo-scene, -.mobile-demo-stage .hero-demo-scene.popup-open { - position: relative; - width: 100%; - height: 100% !important; - max-width: none; - margin: 0 auto; -} - -.mobile-demo-stage .demo-tab-stack { - display: block !important; - position: static !important; - height: auto !important; -} - -.mobile-demo-stage .demo-tab-card { - display: block !important; -} - -.mobile-demo-stage .demo-sync-chip { - display: inline-flex !important; -} - -.mobile-demo-stage .demo-cursor { - display: block !important; -} - -.mobile-demo-stage .demo-hint { - display: block !important; - position: absolute !important; - left: 0 !important; - right: 0 !important; - top: auto !important; - bottom: 12px !important; - margin: 0 auto !important; - padding-inline: 1rem; - max-width: 90%; - z-index: 10; -} - -.mobile-demo-stage .hero-demo-scene .extension-mockup { - position: absolute; - bottom: 0 !important; - right: 0; - z-index: 15 !important; - width: min(320px, 66cqw); - transform: none !important; - transform-origin: top right !important; - transition: transform 0.45s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.35s, visibility 0.45s; -} - -.mobile-demo-stage .hero-demo-scene:not(.popup-open) .extension-mockup { - bottom: 0 !important; - transform: translate(2%, -3%) scale(0.5) !important; - opacity: 0; - visibility: hidden; - pointer-events: none; -} - -@media (max-width: 380px) { - .mobile-demo-modal { - padding: 0 !important; - } - - .mobile-demo-stage { - padding-inline: 0.45rem !important; - } -} - -/* Performance optimization for off-screen sections (below the fold) */ -.lazy-section { - content-visibility: auto; -} - -.use-cases-section { - contain-intrinsic-block-size: auto 600px; -} - -.comparison-section { - contain-intrinsic-block-size: auto 800px; -} - -#self-hosting { - contain-intrinsic-block-size: auto 400px; -} - -#faq { - contain-intrinsic-block-size: auto 450px; -} - -@media (max-width: 768px) { - .use-cases-section { - contain-intrinsic-block-size: auto 900px; - } - - .comparison-section { - contain-intrinsic-block-size: auto 1100px; - } - - #self-hosting { - contain-intrinsic-block-size: auto 550px; - } - - #faq { - contain-intrinsic-block-size: auto 700px; - } -} - -/* Final responsive hierarchy overrides. Keep these last: the landing page has - several component-level media blocks above that intentionally own geometry. */ -@media (max-width: 768px) { - .container { - padding-inline: 1.15rem; - } - - section { - padding-block: 4.25rem; - } - - .hero-text h1, - .section-title, - #how-it-works h2, - #self-hosting h2, - #faq h2 { - font-size: clamp(2rem, 9vw, 2.65rem) !important; - text-wrap: balance; - } - - .cta-group { - display: grid; - grid-template-columns: 1fr 1fr; - align-items: stretch; - width: 100%; - } - - .cta-group .btn { - width: 100%; - min-height: 48px; - justify-content: center; - } -} - -@media (max-width: 430px) { - .cta-group { - grid-template-columns: 1fr; - } -} +/* + * KoalaSync website CSS manifest for local development. + * + * Production builds concatenate these modules in this exact order before + * minification, hashing, and SRI generation. Keep the order synchronized with + * STYLE_MODULES in build.cjs; it preserves the original cascade byte-for-byte. + */ +@import url('./styles/foundation.css'); +@import url('./styles/hero.css'); +@import url('./styles/landing-primary.css'); +@import url('./styles/legal.css'); +@import url('./styles/landing-controls.css'); +@import url('./styles/join-spinner.css'); +@import url('./styles/landing-sections.css'); +@import url('./styles/join.css'); +@import url('./styles/landing-faq.css'); +@import url('./styles/alternatives.css'); +@import url('./styles/demo.css'); diff --git a/website/style.legacy.css b/website/style.legacy.css new file mode 100644 index 0000000..ad55856 --- /dev/null +++ b/website/style.legacy.css @@ -0,0 +1,6576 @@ +/* KoalaSync Design System - Privacy-Focused (No External Assets) */ + +@font-face { + font-family: 'Twemoji Country Flags'; + unicode-range: U+1F1E6-1F1FF, U+1F3F4, U+E0062-E0063, U+E0065, U+E0067, U+E006C, U+E006E, U+E0073-E0074, U+E0077, U+E007F; + src: url('assets/TwemojiCountryFlags.woff2') format('woff2'); + font-display: swap; +} + +:root { + /* v3.0.0 "The Greens Update" nature palette */ + --bg-base: oklch(0.20 0.025 140); + --surface: oklch(0.27 0.035 130); + --surface-deep: oklch(0.16 0.025 135); + --surface-alt: oklch(0.23 0.03 130); + --border-soft: rgba(255, 255, 255, 0.06); + --border-strong: oklch(0.32 0.03 125); + --text-primary: oklch(0.96 0.01 90); + --text-secondary: oklch(0.78 0.02 90); + --accent-green: oklch(0.68 0.13 150); + --accent-green-hover: oklch(0.75 0.13 150); + --accent-terracotta: oklch(0.62 0.14 45); + --danger: oklch(0.55 0.15 25); + --text-on-green: oklch(0.14 0.02 90); + + /* Legacy aliases used throughout the stylesheet */ + --bg: var(--bg-base); + --card: var(--surface); + --accent: var(--accent-green); + --accent-glow: oklch(0.68 0.13 150 / 0.3); + --text: var(--text-primary); + --text-muted: var(--text-secondary); + --success: var(--accent-green); + --glass: oklch(0.27 0.035 130 / 0.4); + --glass-border: var(--border-soft); + --section-tint: transparent; + --section-tint-strong: oklch(0.20 0.025 140 / 0.28); + --card-surface: oklch(0.27 0.035 130 / 0.45); + --card-border: rgba(255, 255, 255, 0.08); + color-scheme: dark; +} + +.extension-mockup, +.illus-popup-card, +.illus-player-card { + --bg: oklch(0.20 0.025 140) !important; + --card: oklch(0.27 0.035 130) !important; + --accent: oklch(0.68 0.13 150) !important; + --accent-glow: oklch(0.68 0.13 150 / 0.3) !important; + --text: oklch(0.96 0.01 90) !important; + --text-muted: oklch(0.78 0.02 90) !important; + --success: oklch(0.68 0.13 150) !important; + --error: oklch(0.55 0.15 25) !important; + --glass: oklch(0.27 0.035 130 / 0.4) !important; + --glass-border: rgba(255, 255, 255, 0.05) !important; + --section-tint: rgba(255, 255, 255, 0.02) !important; + --section-tint-strong: oklch(0.20 0.025 140 / 0.4) !important; + --card-surface: oklch(0.27 0.035 130 / 0.45) !important; + --card-border: rgba(255, 255, 255, 0.08) !important; +} + +.invite-banner { + background: var(--accent); + color: var(--text-on-green); + padding: 0.75rem 0; + text-align: center; + font-weight: 600; + font-size: 0.9rem; + position: relative; + z-index: 2000; + box-shadow: 0 4px 12px rgba(0,0,0,0.3); +} + +.btn-banner { + background: white; + color: var(--accent); + padding: 4px 12px; + border-radius: 6px; + text-decoration: none; + font-size: 0.8rem; + font-weight: 700; + margin-left: 12px; + cursor: pointer; + border: none; + transition: transform 0.2s; +} + +.btn-banner:hover { + transform: scale(1.05); +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; + border-radius: 6px; +} + +html { + scroll-behavior: smooth; +} + +body { + font-family: 'Twemoji Country Flags', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; + background-color: var(--bg); + color: var(--text); + line-height: 1.6; + overflow-x: hidden; + position: relative; +} + +/* --- Animated Bamboo Forest Background (v3 Greens Update) --- */ +/* Registered so wind gusts can TRANSITION these instead of jumping */ +@property --sway { + syntax: ''; + initial-value: 1.2deg; + inherits: true; +} + +@property --leaf-drift { + syntax: ''; + initial-value: 26px; + inherits: true; +} + +.bg-nature { + --sway: 1.2deg; + --leaf-drift: 26px; + transition: --sway 1.6s ease-in-out, --leaf-drift 1.6s ease-in-out; +} + +.bg-nature.gusting { + --sway: 3.4deg; + --leaf-drift: 68px; +} + +.bg-nature { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: -1; + overflow: hidden; + pointer-events: none; + background: + radial-gradient(circle at 18% 10%, oklch(0.26 0.05 145 / 0.35), transparent 34rem), + radial-gradient(circle at 78% 68%, oklch(0.24 0.04 140 / 0.3), transparent 38rem), + linear-gradient(180deg, oklch(0.20 0.025 140) 0%, oklch(0.17 0.03 142) 45%, oklch(0.19 0.03 140) 100%); +} + +/* (a) Distant bamboo layer: blurred, scroll-parallaxed by app.js */ +#bamboo-far { + position: absolute; + inset: 0; + will-change: transform; +} + +#bamboo-far .bamboo-stalk { + position: absolute; + /* 200% covers the upward parallax drift (max 0.55vh on the near layer) + with margin while keeping the blurred layer smaller than the old + 240% overscan. Spare coverage sits below, where the drift needs it. */ + top: -40%; + height: 200%; + border-radius: 999px; + filter: blur(4.5px); + transition: filter 1.2s ease; + background: + repeating-linear-gradient(180deg, + transparent 0, transparent 108px, + oklch(0.40 0.06 145 / 0.22) 108px, oklch(0.40 0.06 145 / 0.22) 112px), + linear-gradient(180deg, oklch(0.34 0.05 145 / 0.4), oklch(0.24 0.04 140 / 0.16)); +} + +#bamboo-far .far-1 { left: 2%; width: 26px; } +#bamboo-far .far-2 { left: 7.5%; width: 18px; } +#bamboo-far .far-3 { right: 4%; width: 30px; } +#bamboo-far .far-4 { left: 13%; width: 20px; } +#bamboo-far .far-5 { right: 10.5%; width: 16px; } +/* Two whisper-quiet trunks soften the empty centre without touching the + content: wide apart, thin, and at the lowest opacity in the scene. */ +#bamboo-far .far-6 { left: 39%; width: 12px; opacity: 0.55; } +#bamboo-far .far-7 { right: 42%; width: 14px; opacity: 0.6; } + +/* Two barely-there trunks break up the empty middle without competing with + the content. They share the far layer's single parallax transform. */ +#bamboo-far::before, +#bamboo-far::after { + content: ''; + position: absolute; + top: -40%; + height: 200%; + border-radius: 999px; + filter: blur(6.5px); + opacity: 0.3; + background: linear-gradient(180deg, oklch(0.34 0.05 145 / 0.3), oklch(0.23 0.04 140 / 0.1)); +} + +#bamboo-far::before { left: 24%; width: 13px; } +#bamboo-far::after { right: 25%; width: 17px; } + +/* Depth of field: the far layer blurs further as you descend (stepped by + JS on threshold crossings, never per frame) */ +.bg-nature.depth-mid #bamboo-far .bamboo-stalk { filter: blur(5.5px); } +.bg-nature.depth-deep #bamboo-far .bamboo-stalk { filter: blur(6.5px); } + +/* (a2) Mid bamboo layer: sits between far and near with its own parallax + rate (app.js) and half the near layer's sway, so the three layers drift + apart subtly instead of moving as one sheet. Landing page only — the + static pages keep the lighter two-layer scene. */ +#bamboo-mid { + position: absolute; + inset: 0; + will-change: transform; +} + +#bamboo-mid .bamboo-stalk { + position: absolute; + top: -40%; + height: 200%; + border-radius: 999px; + filter: blur(2px); + transition: filter 1.2s ease; + /* Segment rings + a soft left-of-centre sheen give the stalks culm + structure without any extra elements; the blur keeps both faint. */ + background: + repeating-linear-gradient(180deg, + transparent 0, transparent 84px, + oklch(0.46 0.075 145 / 0.28) 84px, oklch(0.46 0.075 145 / 0.28) 87px), + linear-gradient(90deg, transparent 14%, oklch(0.58 0.07 145 / 0.14) 38%, transparent 68%), + linear-gradient(180deg, oklch(0.36 0.06 145 / 0.45), oklch(0.25 0.045 140 / 0.2)); + transform-origin: bottom center; + animation: bambooSwayHalf 13s ease-in-out infinite; +} + +#bamboo-mid .mid-1 { left: 17%; width: 15px; animation-duration: 12s; } +#bamboo-mid .mid-2 { left: 31%; width: 9px; animation-delay: -5s; opacity: 0.55; } +#bamboo-mid .mid-3 { right: 15.5%; width: 17px; animation-duration: 14.5s; animation-delay: -8s; } +#bamboo-mid .mid-4 { right: 30%; width: 8px; animation-duration: 11.5s; animation-delay: -2.5s; opacity: 0.5; } + +/* A few leaves sprout from the mid stalks (pseudo-elements, so they ride + the stalk's sway for free). The slim centre stalks stay bare. */ +#bamboo-mid .bamboo-stalk::before, +#bamboo-mid .bamboo-stalk::after { + content: ''; + position: absolute; + width: 24px; + height: 11px; + border-radius: 0 100% 0 100%; + background: oklch(0.5 0.1 145 / 0.32); +} + +#bamboo-mid .mid-1::before { top: 31%; left: 13px; transform: rotate(20deg); } +#bamboo-mid .mid-1::after { top: 55%; left: -26px; transform: rotate(-158deg); width: 20px; height: 9px; } +#bamboo-mid .mid-3::before { top: 26%; left: -27px; transform: rotate(-156deg); } +#bamboo-mid .mid-3::after { top: 49%; left: 15px; transform: rotate(24deg); width: 21px; height: 10px; } +#bamboo-mid .mid-2::before, +#bamboo-mid .mid-2::after, +#bamboo-mid .mid-4::before, +#bamboo-mid .mid-4::after { + display: none; +} + +.bg-nature.depth-mid #bamboo-mid .bamboo-stalk { filter: blur(3px); } +.bg-nature.depth-deep #bamboo-mid .bamboo-stalk { filter: blur(4px); } + +@keyframes bambooSwayHalf { + 0%, 100% { transform: rotate(0); } + 50% { transform: rotate(calc(var(--sway, 1.2deg) * 0.5)); } +} + +/* (b) Near bamboo layer: sharp stalks with nodes and leaves, gentle sway */ +#bamboo-near { + position: absolute; + inset: 0; + will-change: transform; +} + +#bamboo-near .bamboo-stalk { + position: absolute; + top: -40%; + height: 200%; + border-radius: 999px; + /* Vertical sheen over the culm gradient: reads as light from the + canopy catching the rounded stalk instead of a flat strip. */ + background: + linear-gradient(90deg, transparent 12%, oklch(0.64 0.08 145 / 0.22) 36%, transparent 64%), + linear-gradient(180deg, oklch(0.42 0.075 145 / 0.6), oklch(0.27 0.05 140 / 0.3)); + transform-origin: bottom center; + animation: bambooSway 10s ease-in-out infinite; +} + +#bamboo-near .near-1 { left: 4.5%; width: 15px; animation-duration: 9.2s; } +#bamboo-near .near-2 { right: 7%; width: 13px; animation-duration: 11s; animation-delay: -3.5s; } +#bamboo-near .near-3 { left: 10%; width: 10px; animation-duration: 12.5s; animation-delay: -6s; opacity: 0.75; } + +#bamboo-near .bamboo-node { + position: absolute; + left: -1px; + right: -1px; + height: 3px; + border-radius: 2px; + background: oklch(0.52 0.085 145 / 0.6); +} + +#bamboo-near .node-1 { top: 28%; } +#bamboo-near .node-2 { top: 48%; } +#bamboo-near .node-3 { top: 68%; } + +#bamboo-near .bamboo-leaf { + position: absolute; + width: 26px; + height: 12px; + border-radius: 0 100% 0 100%; + background: oklch(0.55 0.11 145 / 0.4); +} + +#bamboo-near .near-1 .leaf-1 { top: 27.5%; left: 12px; transform: rotate(18deg); } +#bamboo-near .near-1 .leaf-2 { top: 47.5%; left: -28px; transform: rotate(-160deg); width: 22px; height: 10px; } +#bamboo-near .near-2 .leaf-1 { top: 44%; left: -26px; transform: rotate(-155deg); } +#bamboo-near .near-2 .leaf-2 { top: 61%; left: 11px; transform: rotate(21deg); width: 20px; height: 9px; } +#bamboo-near .near-3 .leaf-1 { top: 33%; left: 8px; transform: rotate(24deg); width: 18px; height: 9px; } + +@keyframes bambooSway { + 0%, 100% { transform: rotate(0); } + 50% { transform: rotate(var(--sway, 1.2deg)); } +} + +/* (d) Falling leaves: outer element falls + rotates, inner leaf sways */ +.fall-leaf { + position: absolute; + top: 0; + animation: leafFall 17s linear infinite; + opacity: 0; +} + +.fall-leaf i { + display: block; + width: 10px; + height: 14px; + border-radius: 0 70% 0 70%; + background: oklch(0.68 0.13 150 / 0.55); + animation: leafSway 3.2s ease-in-out infinite; +} + +.fall-leaf:nth-child(even) i { + background: oklch(0.62 0.14 45 / 0.55); +} + +.fall-leaf-1 { left: 6%; animation-duration: 15s; animation-delay: -2s; } +.fall-leaf-2 { left: 16%; animation-duration: 19s; animation-delay: -9s; } +.fall-leaf-2 i { animation-duration: 2.7s; background: oklch(0.62 0.14 45 / 0.6); } +.fall-leaf-3 { left: 28%; animation-duration: 14s; animation-delay: -5s; } +.fall-leaf-3 i { background: oklch(0.68 0.13 150 / 0.5); } +.fall-leaf-4 { left: 41%; animation-duration: 21s; animation-delay: -13s; } +.fall-leaf-4 i { animation-duration: 4s; } +.fall-leaf-5 { left: 55%; animation-duration: 16s; animation-delay: -7s; } +.fall-leaf-5 i { background: oklch(0.68 0.13 150 / 0.6); } +.fall-leaf-6 { left: 68%; animation-duration: 20s; animation-delay: -16s; } +.fall-leaf-6 i { animation-duration: 3.6s; background: oklch(0.62 0.14 45 / 0.5); } +.fall-leaf-7 { left: 81%; animation-duration: 14.5s; animation-delay: -3s; } +.fall-leaf-7 i { animation-duration: 2.9s; } +.fall-leaf-8 { left: 92%; animation-duration: 18s; animation-delay: -11s; } + +@keyframes leafFall { + 0% { transform: translateY(-40px) rotate(0); opacity: 0; } + 8% { opacity: 0.65; } + 92% { opacity: 0.5; } + 100% { transform: translateY(110vh) translateX(120px) rotate(280deg); opacity: 0; } +} + +@keyframes leafSway { + 0%, 100% { margin-left: 0; } + 50% { margin-left: var(--leaf-drift, 26px); } +} + +/* Scroll journey: JS drives the opacity of these two groups so the page + travels from sunlit canopy (top) into misty dusk (bottom). Static + defaults below keep the scene balanced without JS. */ +.bg-depth-day, +.bg-depth-dusk { + position: absolute; + inset: 0; +} + +.bg-depth-dusk { + opacity: 0.6; +} + +/* Dusk tint: darkens and warms the whole scene as you descend */ +.bg-dusk-tint { + position: absolute; + inset: 0; + opacity: 0; + background: linear-gradient(180deg, + oklch(0.10 0.03 145 / 0.15) 0%, + oklch(0.12 0.045 142 / 0.5) 65%, + oklch(0.16 0.07 55 / 0.35) 100%); +} + +/* God rays: soft light shafts falling through the canopy */ +.bg-godrays { + position: absolute; + top: -10%; + left: 0; + right: 0; + height: 75%; +} + +.godray { + position: absolute; + top: 0; + height: 115%; + background: linear-gradient(180deg, oklch(0.85 0.06 130 / 0.10), transparent 78%); + filter: blur(10px); + transform: rotate(14deg); + transform-origin: top center; + animation: rayShimmer 9s ease-in-out infinite alternate; +} + +.godray-1 { left: 16%; width: 120px; animation-duration: 8s; } +.godray-2 { left: 33%; width: 70px; animation-delay: -3s; background: linear-gradient(180deg, oklch(0.85 0.06 130 / 0.07), transparent 78%); } +.godray-3 { left: 56%; width: 140px; animation-duration: 13s; animation-delay: -6s; background: linear-gradient(180deg, oklch(0.85 0.05 110 / 0.08), transparent 75%); } + +@keyframes rayShimmer { + 0% { opacity: 0.45; } + 100% { opacity: 1; } +} + +/* (e) Canopy: dark vignette at the top plus hanging leaf arcs */ +.bg-canopy { + position: absolute; + top: 0; + left: 0; + right: 0; + height: 150px; + background: + radial-gradient(ellipse 70% 100% at 30% 0%, oklch(0.14 0.03 145 / 0.55), transparent 70%), + radial-gradient(ellipse 60% 90% at 80% 0%, oklch(0.14 0.03 145 / 0.45), transparent 70%); +} + +.canopy-arc { + position: absolute; + border-radius: 0 0 100% 100%; + background: oklch(0.32 0.06 145 / 0.45); +} + +.canopy-arc-1 { top: -3px; left: 12%; width: 52px; height: 16px; transform: rotate(-6deg); background: oklch(0.30 0.06 145 / 0.5); } +.canopy-arc-2 { top: -2px; left: 34%; width: 38px; height: 12px; transform: rotate(4deg); background: oklch(0.34 0.06 145 / 0.35); } +.canopy-arc-3 { top: -4px; left: 61%; width: 46px; height: 14px; transform: rotate(-3deg); } +.canopy-arc-4 { top: -2px; left: 84%; width: 30px; height: 10px; transform: rotate(7deg); background: oklch(0.33 0.06 145 / 0.4); } + +/* (f) Undergrowth: grass blades in the lower corners. Each blade carries a + shorter companion blade (::before) so the corners read as small tufts. */ +.bg-grass { + position: absolute; + bottom: -4px; + transform-origin: bottom; +} + +.bg-grass::before { + content: ''; + position: absolute; + bottom: 0; + width: 72%; + height: 55%; + border-radius: inherit; + background: inherit; + opacity: 0.7; + transform-origin: bottom; +} + +.bg-grass-1::before { left: -5px; transform: rotate(-13deg); } +.bg-grass-2::before { left: 4px; transform: rotate(11deg); } +.bg-grass-3::before { right: -5px; transform: rotate(12deg); } +.bg-grass-4::before { right: 4px; transform: rotate(-9deg); } + +.bg-grass-1 { left: 1.5%; width: 6px; height: 52px; border-radius: 100% 0 0 0; background: oklch(0.38 0.07 145 / 0.4); transform: rotate(-9deg); } +.bg-grass-2 { left: 3.2%; width: 5px; height: 38px; border-radius: 100% 0 0 0; background: oklch(0.35 0.07 145 / 0.3); transform: rotate(-12deg); } +.bg-grass-3 { right: 2%; width: 6px; height: 56px; border-radius: 0 100% 0 0; background: oklch(0.4 0.07 145 / 0.4); transform: rotate(8deg); } +.bg-grass-4 { right: 3.8%; width: 5px; height: 40px; border-radius: 0 100% 0 0; background: oklch(0.36 0.07 145 / 0.32); transform: rotate(11deg); } + +/* (g) Ground mist: soft drifting ellipses at the bottom edge */ +.bg-mist { + position: absolute; + bottom: -40px; + height: 130px; + filter: blur(19px); + will-change: transform; +} + +.bg-mist-1 { + left: -6%; + width: 75%; + background: radial-gradient(ellipse, oklch(0.85 0.03 145 / 0.07), transparent 70%); + animation: mistDrift 22s ease-in-out infinite alternate; +} + +.bg-mist-2 { + right: -6%; + width: 70%; + background: radial-gradient(ellipse, oklch(0.85 0.03 145 / 0.06), transparent 70%); + animation: mistDrift 27s ease-in-out infinite alternate-reverse; +} + +@keyframes mistDrift { + 0% { transform: translateX(-8%); } + 100% { transform: translateX(8%); } +} + +/* (h) Breathing horizon glow */ +.bg-horizon { + position: absolute; + inset: 0; + background: radial-gradient(ellipse 60% 100% at 50% 100%, oklch(0.62 0.14 45 / 0.14), transparent 70%); + animation: horizonBreathe 10s ease-in-out infinite; +} + +@keyframes horizonBreathe { + 0%, 100% { opacity: 0.5; } + 50% { opacity: 0.85; } +} + +/* Theme switch to light: the horizon flares up once like a sunrise */ +.bg-nature.sunrise .bg-horizon { + animation: + horizonBreathe 10s ease-in-out infinite, + sunriseFlash 1.6s ease-out; +} + +@keyframes sunriseFlash { + 0% { opacity: 0.5; } + 30% { opacity: 1; } + 100% { opacity: 0.5; } +} + +/* (i) Fireflies: the wrapper holds the base position and is what the JS + pushes away from the cursor; the inner dot pulses (transform) and + wanders (translate) independently. */ +.firefly-wrap { + position: absolute; + width: 4px; + height: 4px; +} + +.firefly { + display: block; + width: 4px; + height: 4px; + border-radius: 50%; + background: oklch(0.68 0.13 150); + box-shadow: 0 0 8px oklch(0.68 0.13 150); + animation: + fireflyPulse 3.8s ease-in-out infinite, + fireflyWander 21s ease-in-out infinite alternate; +} + +.fw-2 .firefly, +.fw-4 .firefly, +.fw-6 .firefly { + width: 3px; + height: 3px; + background: oklch(0.62 0.14 45); + box-shadow: 0 0 8px oklch(0.62 0.14 45); +} + +.fw-1 { top: 32%; left: 12%; } +.fw-2 { top: 58%; left: 22%; } +.fw-3 { top: 44%; left: 46%; } +.fw-4 { top: 70%; left: 64%; } +.fw-5 { top: 26%; left: 78%; } +.fw-6 { top: 62%; left: 90%; } + +.fw-1 .firefly { animation-delay: -0.4s, -4s; } +.fw-2 .firefly { animation-duration: 4.4s, 26s; animation-delay: -2.1s, -11s; } +.fw-3 .firefly { animation-duration: 3.4s, 18s; animation-delay: -1.2s, -7s; } +.fw-4 .firefly { animation-duration: 4.1s, 29s; animation-delay: -3s, -19s; } +.fw-5 .firefly { animation-duration: 3.6s, 23s; animation-delay: -1.8s, -14s; } +.fw-6 .firefly { animation-duration: 4.2s, 17s; animation-delay: -0.9s, -2s; } + +/* One firefly greets you when a new section scrolls into view */ +.firefly-wrap.firefly-hello { + animation: fireflyHello 1.2s ease-out; +} + +@keyframes fireflyHello { + 0%, 100% { transform: scale(1); filter: brightness(1); } + 35% { transform: scale(2.4); filter: brightness(1.7); } +} + +@keyframes fireflyPulse { + 0%, 100% { opacity: 0.2; transform: scale(1); } + 50% { opacity: 0.9; transform: scale(1.35); } +} + +@keyframes fireflyWander { + 0% { translate: 0 0; } + 25% { translate: 38px -26px; } + 50% { translate: -20px 16px; } + 75% { translate: 26px 30px; } + 100% { translate: -14px -18px; } +} + +/* Day/night: fireflies belong to the dark theme. By daylight the same six + spots are visited by small butterflies instead — the wrapper (and with it + the cursor-shy push and the section greeting) is shared, only the inner + dot is redressed. Only animation-name is swapped so the per-firefly + duration/delay variety above keeps applying to the butterflies. */ +html.theme-light .firefly { + position: relative; + width: 2px; + height: 9px; + border-radius: 1px; + background: oklch(0.35 0.05 140 / 0.8); + box-shadow: none; + opacity: 0.85; + animation-name: butterflyBob, fireflyWander; +} + +html.theme-light .firefly::before, +html.theme-light .firefly::after { + content: ''; + position: absolute; + top: -2px; + width: 7px; + height: 10px; + background: oklch(0.62 0.14 45 / 0.7); + animation: wingFlap 0.5s ease-in-out infinite alternate; +} + +html.theme-light .firefly::before { + left: -6px; + border-radius: 90% 10% 60% 40%; + transform-origin: 100% 55%; +} + +html.theme-light .firefly::after { + right: -6px; + border-radius: 10% 90% 40% 60%; + transform-origin: 0% 55%; + animation-name: wingFlapR; +} + +/* Every other butterfly wears the sage of the canopy and flaps lazier */ +html.theme-light .fw-2 .firefly::before, +html.theme-light .fw-2 .firefly::after, +html.theme-light .fw-4 .firefly::before, +html.theme-light .fw-4 .firefly::after, +html.theme-light .fw-6 .firefly::before, +html.theme-light .fw-6 .firefly::after { + background: oklch(0.52 0.12 150 / 0.55); + animation-duration: 0.62s; +} + +@keyframes butterflyBob { + 0%, 100% { transform: translateY(0) rotate(-4deg); } + 50% { transform: translateY(-8px) rotate(5deg); } +} + +@keyframes wingFlap { + from { transform: scaleX(1) rotate(-6deg); } + to { transform: scaleX(0.3) rotate(8deg); } +} + +@keyframes wingFlapR { + from { transform: scaleX(1) rotate(6deg); } + to { transform: scaleX(0.3) rotate(-8deg); } +} + +/* By day the dusk layer must not dim the butterflies with scroll depth + (the JS crossfade keeps writing inline opacity for the dark theme), and + the warm horizon glow steps back so it reads as haze, not dusk. */ +html.theme-light .bg-depth-dusk { + opacity: 1 !important; +} + +html.theme-light .bg-horizon { + background: radial-gradient(ellipse 60% 100% at 50% 100%, oklch(0.8 0.08 60 / 0.1), transparent 70%); +} + +/* (j) Wandering light sweep */ +.bg-light-sweep { + position: absolute; + top: -30%; + left: -30%; + width: 160%; + height: 160%; + background: linear-gradient(115deg, transparent 42%, oklch(0.68 0.13 150 / 0.05) 50%, transparent 58%); + animation: lightSweep 26s ease-in-out infinite alternate; + will-change: transform; +} + +@keyframes lightSweep { + 0% { transform: translate(-20%, -20%) rotate(8deg); } + 100% { transform: translate(20%, 20%) rotate(8deg); } +} + +/* (j2) Corner vignette: pulls the eye toward the content and grounds the + scene — the forest darkens gently toward the edges like real depth. */ +.bg-nature::after { + content: ''; + position: absolute; + inset: 0; + pointer-events: none; + background: radial-gradient(ellipse 120% 90% at 50% 38%, transparent 55%, oklch(0.10 0.025 142 / 0.4) 100%); +} + +html.theme-light .bg-nature::after { + background: radial-gradient(ellipse 120% 90% at 50% 38%, transparent 62%, oklch(0.78 0.03 120 / 0.3) 100%); +} + +/* (k) Film grain (SVG feTurbulence, no external assets) */ +.bg-grain { + position: absolute; + inset: 0; + opacity: 0.05; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); + background-size: 160px 160px; +} + +@media (max-width: 768px) { + /* Half the leaves, no god rays / light sweep / extra stalks: the scene + stays recognisable while the animation count roughly halves. */ + .fall-leaf-5, + .fall-leaf-6, + .fall-leaf-7, + .fall-leaf-8, + .bg-godrays, + .bg-light-sweep, + #bamboo-mid, + #bamboo-far .far-4, + #bamboo-far .far-5, + #bamboo-far .far-6, + #bamboo-far .far-7, + #bamboo-near .near-3 { + display: none; + } +} + +/* --- UI/UX cohesion pass ------------------------------------------------- + Shared rhythm and interaction rules for the landing page. Component-specific + responsive refinements are repeated at the end of the cascade. */ +html { + scroll-padding-top: 96px; +} + +body { + text-rendering: optimizeLegibility; +} + +.footer-disclaimer { + color: oklch(0.72 0.015 90); +} + +html.theme-light .footer-disclaimer { + color: var(--text-muted); +} + +.logo-area { + text-decoration: none; + border-radius: 12px; +} + +.logo-area:focus-visible, +.nav-links a:focus-visible, +.theme-toggle:focus-visible, +.lang-dropdown:focus-visible, +.hamburger:focus-visible, +.btn:focus-visible, +.faq-item summary:focus-visible { + outline: 3px solid var(--accent); + outline-offset: 4px; +} + +.nav-links a { + position: relative; + padding: 0.55rem 0; +} + +.nav-links a::after { + content: ""; + position: absolute; + right: 0; + bottom: 0.25rem; + left: 0; + height: 2px; + border-radius: 999px; + background: var(--accent); + transform: scaleX(0); + transform-origin: right; + transition: transform 0.25s ease; +} + +.nav-links a:hover::after, +.nav-links a:focus-visible::after { + transform: scaleX(1); + transform-origin: left; +} + +.hero-text { + max-width: 42rem; +} + +.hero-text h1 { + text-wrap: balance; +} + +.hero-subtitle, +.section-subtitle, +.use-cases-subtitle { + text-wrap: pretty; +} + +.cta-group { + align-items: stretch; +} + +.cta-group .btn { + min-height: 48px; + box-sizing: border-box; +} + +.cta-trust { + max-width: 44rem; + line-height: 1.55; +} + +section[id] { + scroll-margin-top: 96px; +} + +.section-title, +#how-it-works h2, +#self-hosting h2, +#faq h2 { + text-wrap: balance; + letter-spacing: -0.035em; +} + +.use-case-card, +.feature-card, +.faq-item, +.comparison-table-wrapper { + box-shadow: 0 18px 46px rgba(0, 0, 0, 0.12); +} + +html.theme-light :is(.use-case-card, .feature-card, .faq-item, .comparison-table-wrapper) { + box-shadow: 0 18px 46px oklch(0.22 0.035 140 / 0.09); +} + +.use-case-card, +.feature-card { + height: 100%; +} + +.use-case-card p, +.feature-card p, +.faq-item p { + line-height: 1.7; +} + +.faq-item summary { + min-height: 52px; + display: flex; + align-items: center; +} + +.comparison-table tbody tr { + transition: background-color 0.2s ease; +} + +@media (max-width: 768px) { + html { + scroll-padding-top: 76px; + } + + .container { + padding-inline: 1.15rem; + } + + section { + padding-block: 4.25rem; + } + + .hero-text { + max-width: none; + } + + .hero-text h1, + .section-title, + #how-it-works h2, + #self-hosting h2, + #faq h2 { + font-size: clamp(2rem, 9vw, 2.65rem) !important; + } + + .cta-group { + display: grid; + grid-template-columns: 1fr 1fr; + width: 100%; + } + + .cta-group .btn { + width: 100%; + justify-content: center; + } + + .cta-group .btn-primary, + .cta-group .btn-firefox { + grid-column: span 1; + } + + .hero-github-cta, + .btn-demo-mobile { + grid-column: span 1; + } + + .use-case-card, + .feature-card, + .faq-item, + .comparison-table-wrapper { + box-shadow: 0 12px 30px rgba(0, 0, 0, 0.1); + } +} + +@media (max-width: 430px) { + .cta-group { + grid-template-columns: 1fr; + } + + .cta-group .btn-primary, + .cta-group .btn-firefox, + .hero-github-cta, + .btn-demo-mobile { + grid-column: 1; + } +} + +@media (prefers-reduced-motion: reduce) { + .nav-links a::after, + .use-case-card, + .feature-card, + .faq-item, + .btn { + transition-duration: 0.01ms !important; + } +} + +/* (l) Click confetti: a small handful of eucalyptus leaves bursts from + wherever the visitor presses (spawned by app.js). Built like the falling + leaves above — the outer span owns the flight path (fast burst that + brakes at its peak, then sinks), the inner leaf rocks around its stem + the whole way down. Deliberately subtle: few, tiny, short-lived. */ +.click-leaf { + position: fixed; + z-index: 3000; + margin: -6px 0 0 -4px; + pointer-events: none; + opacity: 0; + animation: leafBurst var(--leaf-dur, 1.1s) forwards; + will-change: transform, opacity; +} + +.click-leaf i { + display: block; + width: 8px; + height: 12px; + border-radius: 0 70% 0 70%; + background: linear-gradient(135deg, oklch(0.62 0.12 150 / 0.95), oklch(0.45 0.09 145 / 0.85)); + transform: rotate(var(--leaf-rot, 0deg)); + animation: leafRock var(--flutter-dur, 0.6s) ease-in-out infinite alternate; +} + +.click-leaf-sage i { + background: linear-gradient(135deg, oklch(0.72 0.09 140 / 0.9), oklch(0.55 0.08 145 / 0.8)); +} + +.click-leaf-amber i { + background: linear-gradient(135deg, oklch(0.68 0.14 55 / 0.95), oklch(0.55 0.13 40 / 0.85)); +} + +/* Two-phase flight: --leaf-x/--leaf-y is the burst PEAK; from there each + leaf loses momentum and sinks past it while fading. Per-segment timing + functions give the fast pop and the gentle fall. */ +@keyframes leafBurst { + 0% { + opacity: 0; + transform: translate(0, 0) scale(var(--leaf-scale, 1)); + animation-timing-function: cubic-bezier(0.16, 0.84, 0.44, 1); + } + 10% { opacity: 1; } + 45% { + opacity: 1; + transform: translate(var(--leaf-x, 0), var(--leaf-y, -20px)) scale(var(--leaf-scale, 1)); + animation-timing-function: cubic-bezier(0.55, 0.06, 0.68, 0.19); + } + 100% { + opacity: 0; + transform: translate(calc(var(--leaf-x, 0) * 1.4), calc(var(--leaf-y, -20px) + 44px)) scale(calc(var(--leaf-scale, 1) * 0.92)); + } +} + +@keyframes leafRock { + from { transform: rotate(calc(var(--leaf-rot, 0deg) - 24deg)); } + to { transform: rotate(calc(var(--leaf-rot, 0deg) + 24deg)); } +} + +@media (prefers-reduced-motion: reduce) { + .click-leaf { display: none; } + .bg-nature *, + .bg-nature *::before, + .bg-nature *::after { + animation: none !important; + } + .fall-leaf { opacity: 0; } +} + +/* --- Layout --- */ +.container { + max-width: 1100px; + margin: 0 auto; + padding: 0 2rem; +} + +section { + padding: 6rem 0; +} + +#how-it-works { + padding: 4.5rem 0; +} + +/* Content-visibility for offscreen sections to improve INP */ +.use-cases-section, +#self-hosting, +.comparison-section { + content-visibility: auto; + contain-intrinsic-size: auto 1400px; +} + +/* --- Navigation --- */ +nav { + position: fixed; + top: 0; + width: 100%; + z-index: 1000; + background: var(--glass); + backdrop-filter: blur(12px); + border-bottom: 1px solid var(--glass-border); + padding: 1rem 0; + transition: padding 0.3s ease, background 0.3s ease; +} + +nav.nav-scrolled { + padding: 0.75rem 0; + background: oklch(0.20 0.025 140 / 0.9); +} + +html.theme-light nav.nav-scrolled { + background: oklch(0.95 0.01 100 / 0.92); +} + +.nav-content { + display: flex; + align-items: center; +} + +.logo-area { + display: flex; + align-items: center; + gap: 0.75rem; + font-weight: 800; + font-size: 1.5rem; + letter-spacing: -0.5px; +} + +/* Logo can be a plain ; without this it falls back to the UA link blue, + which clashed silently with the old indigo theme and visibly with green. */ +a.logo-area { + color: var(--text); +} + +.logo-area picture { + display: contents; +} + +.logo-area img { + height: 64px; + width: 64px; + object-fit: contain; + border-radius: 8px; + margin: -4px 0; +} + +.nav-ext-status { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 3px 8px; + background: oklch(0.68 0.13 150 / 0.15); + border: 1px solid oklch(0.68 0.13 150 / 0.3); + color: oklch(0.68 0.13 150); + border-radius: 12px; + font-size: 0.55rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.05em; + box-shadow: 0 0 10px oklch(0.68 0.13 150 / 0.1); + animation: fade-in 0.4s ease-out; + margin-left: 0.2rem; +} + +@keyframes fade-in { + from { opacity: 0; transform: translateY(-5px); } + to { opacity: 1; transform: translateY(0); } +} + +.nav-links { + display: flex; + gap: 2rem; + margin-left: auto; +} + +.nav-links a { + color: var(--text-muted); + text-decoration: none; + font-weight: 500; + transition: color 0.3s; +} + +.nav-links a:hover { + color: var(--accent); +} + +.nav-right { + display: flex; + align-items: center; + gap: 0.5rem; + margin-left: 2rem; + flex-shrink: 0; +} + +/* --- Theme Toggle (sun/moon) --- */ +.theme-toggle { + position: relative; + display: inline-flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + padding: 0; + background: transparent; + border: none; + border-radius: 10px; + color: var(--text-muted); + cursor: pointer; + flex-shrink: 0; + transition: color 0.3s, transform 0.3s; + overflow: hidden; +} + +.theme-toggle:hover { + color: var(--accent); + transform: scale(1.15); +} + +.theme-toggle:active { + transform: scale(0.9); +} + +.theme-toggle svg { + position: absolute; + width: 18px; + height: 18px; + transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.4s ease; +} + +/* Default (Dark mode): Sun visible, Moon hidden & rotated */ +.theme-toggle .theme-icon-sun { + opacity: 1; + transform: scale(1) rotate(0deg); +} + +.theme-toggle .theme-icon-moon { + opacity: 0; + transform: scale(0) rotate(-90deg); +} + +/* Light mode: Moon visible, Sun hidden & rotated */ +html.theme-light .theme-toggle .theme-icon-sun { + opacity: 0; + transform: scale(0) rotate(90deg); +} + +html.theme-light .theme-toggle .theme-icon-moon { + opacity: 1; + transform: scale(1) rotate(0deg); +} + +/* --- Light Theme --- */ +html.theme-light { + --accent-green: oklch(0.52 0.12 150); + --accent-green-hover: oklch(0.58 0.12 150); + --text-on-green: #ffffff; + --bg: oklch(0.95 0.01 100); + --card: #ffffff; + --accent: var(--accent-green); + --accent-glow: oklch(0.52 0.12 150 / 0.25); + --text: oklch(0.20 0.025 140); + --text-muted: oklch(0.45 0.02 110); + --glass: rgba(255, 255, 255, 0.7); + --glass-border: oklch(0.20 0.025 140 / 0.08); + --section-tint: transparent; + --section-tint-strong: oklch(0.88 0.015 90 / 0.5); + --card-surface: rgba(255, 255, 255, 0.75); + --card-border: oklch(0.20 0.025 140 / 0.08); + color-scheme: light; +} + +html.theme-light .bg-nature { + background: + radial-gradient(circle at 16% 10%, oklch(0.85 0.06 150 / 0.3), transparent 34rem), + radial-gradient(circle at 78% 68%, oklch(0.85 0.06 60 / 0.2), transparent 38rem), + linear-gradient(180deg, oklch(0.96 0.01 90) 0%, oklch(0.93 0.025 140) 46%, oklch(0.96 0.01 90) 100%); +} + +html.theme-light .bg-canopy, +html.theme-light .canopy-arc, +html.theme-light .bg-grass { + opacity: 0.45; +} + +/* By day the stalks get their own fresh-green recipes — the dark-theme + gradients faded to 45% read as grey smears on the light backdrop and + stopped looking like bamboo at all. */ +html.theme-light #bamboo-far .bamboo-stalk { + opacity: 0.55; + background: + repeating-linear-gradient(180deg, + transparent 0, transparent 108px, + oklch(0.55 0.09 145 / 0.4) 108px, oklch(0.55 0.09 145 / 0.4) 112px), + linear-gradient(180deg, oklch(0.68 0.11 145 / 0.6), oklch(0.62 0.09 140 / 0.35)); +} + +html.theme-light #bamboo-far::before, +html.theme-light #bamboo-far::after { + background: linear-gradient(180deg, oklch(0.7 0.1 145 / 0.5), oklch(0.64 0.08 140 / 0.25)); +} + +html.theme-light #bamboo-mid .bamboo-stalk { + opacity: 0.65; + background: + repeating-linear-gradient(180deg, + transparent 0, transparent 84px, + oklch(0.5 0.1 145 / 0.45) 84px, oklch(0.5 0.1 145 / 0.45) 87px), + linear-gradient(90deg, transparent 14%, oklch(0.85 0.08 130 / 0.35) 38%, transparent 68%), + linear-gradient(180deg, oklch(0.63 0.115 145 / 0.65), oklch(0.57 0.09 140 / 0.4)); +} + +html.theme-light #bamboo-mid .bamboo-stalk::before, +html.theme-light #bamboo-mid .bamboo-stalk::after { + background: oklch(0.55 0.13 145 / 0.5); +} + +html.theme-light #bamboo-near .bamboo-stalk { + opacity: 0.75; + background: + linear-gradient(90deg, transparent 12%, oklch(0.88 0.07 130 / 0.4) 36%, transparent 64%), + linear-gradient(180deg, oklch(0.59 0.12 145 / 0.75), oklch(0.52 0.1 140 / 0.5)); +} + +html.theme-light #bamboo-near .bamboo-node { + background: oklch(0.45 0.1 145 / 0.65); +} + +html.theme-light #bamboo-near .bamboo-leaf { + background: oklch(0.55 0.13 145 / 0.6); +} + +/* The centre trunks stay whisper-quiet by daylight too */ +html.theme-light #bamboo-far .far-6, +html.theme-light #bamboo-far .far-7 { + opacity: 0.26; +} + +html.theme-light .bg-mist { + display: none; +} + +html.theme-light .bg-dusk-tint { + display: none; +} + +html.theme-light .godray { + filter: blur(12px); + opacity: 0.5; +} + +html.theme-light .bg-grain { + opacity: 0.035; +} + +html.theme-light .feature-card, +html.theme-light .faq-item { + background: var(--card-surface); + border-color: var(--card-border); + box-shadow: 0 8px 24px oklch(0.20 0.025 140 / 0.06); +} + +html.theme-light .feature-card::before { + background: linear-gradient(to bottom right, oklch(0.20 0.025 140 / 0.08), transparent); +} + +html.theme-light .use-case-card { + background: var(--card-surface); +} + +html.theme-light .use-case-card h3 { + color: var(--text); +} + +html.theme-light .use-case-card:hover { + box-shadow: 0 12px 30px oklch(0.20 0.025 140 / 0.12); + border-color: oklch(0.20 0.025 140 / 0.15); +} + +html.theme-light .faq-item[open] { + background: #ffffff; +} + +html.theme-light .faq-item p { + border-top-color: oklch(0.20 0.025 140 / 0.08); +} + +html.theme-light .btn-secondary { + background: #ffffff; + border-color: oklch(0.20 0.025 140 / 0.12); +} + +html.theme-light .btn-secondary:hover { + background: oklch(0.88 0.015 90); +} + +html.theme-light .btn-firefox { + background: oklch(0.62 0.14 45); + color: #ffffff; + box-shadow: 0 10px 18px oklch(0.62 0.14 45 / 0.24); +} + +html.theme-light .btn-firefox:hover { + background: oklch(0.55 0.13 45); + box-shadow: 0 18px 26px oklch(0.62 0.14 45 / 0.28); +} + +html.theme-light .btn-firefox img { + filter: brightness(0) saturate(100%) invert(13%) sepia(38%) saturate(1084%) hue-rotate(177deg) brightness(89%) contrast(96%); +} + +html.theme-light .btn-firefox-secondary img { + filter: brightness(0) saturate(100%) invert(13%) sepia(38%) saturate(1084%) hue-rotate(177deg) brightness(89%) contrast(96%); +} + +html.theme-light .comparison-section { + background: transparent; +} + +html.theme-light .comparison-table-wrapper { + background: rgba(255, 255, 255, 0.75); + box-shadow: 0 10px 30px oklch(0.20 0.025 140 / 0.08); +} + +html.theme-light .comparison-table th { + background: oklch(0.20 0.025 140 / 0.06); + color: var(--text); +} + +html.theme-light .comparison-table th, +html.theme-light .comparison-table td { + border-bottom-color: oklch(0.20 0.025 140 / 0.08); +} + +html.theme-light .comparison-table tbody tr:hover { + background: oklch(0.20 0.025 140 / 0.03); +} + +html.theme-light .feat-name strong { + color: var(--text); +} + +html.theme-light .table-footnotes { + background: oklch(0.20 0.025 140 / 0.04); + border-top-color: oklch(0.20 0.025 140 / 0.06); +} + +@media (max-width: 900px) { + html.theme-light .comparison-table tbody tr { + background: rgba(255, 255, 255, 0.85); + box-shadow: 0 6px 18px oklch(0.20 0.025 140 / 0.08); + } + html.theme-light .comparison-table td.feat-name { + background: oklch(0.20 0.025 140 / 0.05); + } + html.theme-light .comparison-table td { + border-bottom-color: oklch(0.20 0.025 140 / 0.08); + } +} + +html.theme-light .compat-logo img, +html.theme-light .compat-logo { + filter: none; +} + +/* --- 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; +} + +/* --- Features --- */ +/* Bento Grid Layout Configuration */ +.features-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1.5rem; + margin-top: 4rem; +} + +.feature-card.feature-card-compact { + padding: 1.8rem; + border-radius: 16px; +} + +.feature-card.feature-card-compact::before { + border-radius: 16px; +} + +.feature-card.feature-card-compact h3 { + font-size: 1.1rem; + margin-bottom: 0.75rem; +} + +.feature-card.feature-card-compact p { + font-size: 0.9rem; +} + +.feature-card { + background: oklch(0.27 0.035 130 / 0.45); + backdrop-filter: blur(16px) saturate(120%); + -webkit-backdrop-filter: blur(16px) saturate(120%); + padding: 2.5rem; + border-radius: 24px; + border: 1px solid rgba(255, 255, 255, 0.08); + box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.3); + transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.3s cubic-bezier(0.4, 0, 0.2, 1), border-color 0.3s cubic-bezier(0.4, 0, 0.2, 1); + position: relative; + overflow: hidden; +} + +.feature-card::before { + content: ''; + position: absolute; + top: 0; left: 0; right: 0; bottom: 0; + border-radius: 24px; + padding: 1px; + background: linear-gradient(to bottom right, rgba(255, 255, 255, 0.12), transparent); + -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + pointer-events: none; +} + +.feature-card:hover { + transform: translateY(-5px); + box-shadow: 0 15px 30px oklch(0.68 0.13 150 / 0.2); + border-color: oklch(0.68 0.13 150 / 0.3); +} + +.feature-card:hover::before { + background: linear-gradient(to bottom right, oklch(0.68 0.13 150 / 0.3), transparent); +} + +/* Feature Icon with Inline SVG Wrapper */ +.feature-icon-svg { + display: flex; + flex-shrink: 0; + align-items: center; + justify-content: center; + width: 44px; + height: 44px; + background: oklch(0.68 0.13 150 / 0.12); + color: var(--accent); + border-radius: 12px; + margin-right: 14px; + transition: background 0.3s ease, color 0.3s ease, box-shadow 0.3s ease, transform 0.3s ease; +} + +.feature-card:hover .feature-icon-svg { + background: var(--accent); + color: var(--text-on-green); + box-shadow: 0 0 15px var(--accent-glow); + transform: scale(1.05); +} + +.bento-icon { + width: 22px; + height: 22px; + display: block; + flex-shrink: 0; +} + +.feature-card h3 { + margin-bottom: 1rem; + font-size: 1.25rem; + font-weight: 700; + display: flex; + align-items: center; +} + +.feature-card p { + color: var(--text-muted); + font-size: 0.95rem; + line-height: 1.6; +} + +/* Bento Asymmetric Spanning */ +.feature-card.bento-large { + grid-column: span 2; + background: linear-gradient(135deg, oklch(0.27 0.035 130 / 0.45) 0%, oklch(0.68 0.13 150 / 0.08) 100%); + padding: 3rem 3.5rem; + display: grid; + grid-template-columns: 1fr 2fr; + gap: 3rem; + align-items: center; +} + +.feature-card.bento-large h3 { + flex-direction: column; + align-items: center; + text-align: center; + gap: 1rem; + margin-bottom: 0; + font-size: 1.25rem; +} + +.feature-card.bento-large .feature-icon, +.feature-card.bento-large .feature-icon-svg { + width: 52px; + height: 52px; + border-radius: 14px; + margin-right: 0; + flex-shrink: 0; +} + +.feature-card.bento-large .bento-icon { + width: 24px; + height: 24px; +} + +.feature-card.bento-large p { + font-size: 1rem; + line-height: 1.7; + margin: 0; +} + +.feature-card.bento-large::after { + content: ''; + position: absolute; + top: 0; + left: 2.5rem; + right: 2.5rem; + height: 2px; + background: linear-gradient(90deg, transparent, oklch(0.68 0.13 150 / 0.4), transparent); + opacity: 0; + transition: opacity 0.3s ease; +} + +.feature-card.bento-large:hover::after { + opacity: 1; +} + +@media (max-width: 900px) { + .features-grid { + /* Keep the Why KoalaSync grid at either three columns or one column. + The two-column in-between makes compact cards stretch unevenly. */ + grid-template-columns: 1fr; + gap: 1.25rem; + } + .feature-card.bento-large { + grid-column: span 1; + padding: 2.5rem; + gap: 2rem; + } + .feature-card.bento-large h3 { + font-size: 1.15rem; + } +} + +@media (max-width: 600px) { + .features-grid { + grid-template-columns: 1fr; + gap: 1rem; + } + .feature-card.bento-large { + grid-column: span 1; + display: flex; + flex-direction: column; + padding: 1.75rem; + gap: 1rem; + } + .feature-card.bento-large h3 { + flex-direction: row; + align-items: center; + text-align: left; + gap: 0.75rem; + margin-bottom: 0; + font-size: 1.15rem; + } + .feature-card.bento-large .feature-icon, + .feature-card.bento-large .feature-icon-svg { + width: 40px; + height: 40px; + border-radius: 10px; + margin-right: 0; + } + .feature-card.bento-large .bento-icon { + width: 18px; + height: 18px; + } + .feature-card.bento-large p { + font-size: 0.9rem; + line-height: 1.6; + } + .feature-card.bento-large::after { + display: none; + } + .feature-card { + padding: 1.75rem; + } + .feature-card h3 { + font-size: 1.1rem; + } + .feature-icon-svg { + width: 40px; + height: 40px; + border-radius: 10px; + margin-right: 12px; + } + .bento-icon { + width: 18px; + height: 18px; + } +} + +/* --- How it works --- */ +.steps { + display: flex; + flex-direction: column; + gap: 4rem; +} + +.step { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 3rem; + align-items: center; +} + +.step:nth-child(even) .step-text { + order: 2; +} + +.step-num { + font-size: 3.25rem; + font-weight: 900; + background: linear-gradient(135deg, oklch(0.68 0.13 150 / 0.5), oklch(0.62 0.14 45 / 0.15)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + line-height: 1; + margin-bottom: 0.5rem; + font-family: inherit; + letter-spacing: 0; +} + +.step-text h3 { + font-size: 1.65rem; + margin-bottom: 0.75rem; + font-weight: 850; + color: var(--text-main); + letter-spacing: 0; +} + +.step-text p { + color: var(--text-muted); + font-size: 1rem; + line-height: 1.55; +} + +/* Custom CSS Mockups for step illustrations */ +.step-illustration-1, .step-illustration-2, .step-illustration-3 { + background: radial-gradient(circle at 10% 10%, oklch(0.20 0.04 145), oklch(0.10 0.015 138)); + height: 240px; + border-radius: 18px; + border: 1px solid rgba(255, 255, 255, 0.06); + position: relative; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 20px 45px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.05); + transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1), border-color 0.4s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.4s cubic-bezier(0.16, 1, 0.3, 1); +} + +@media (max-width: 900px) { + #how-it-works { + padding: 3.5rem 0; + } + + .steps { + gap: 2.75rem; + } + + .step { + grid-template-columns: minmax(0, 1fr); + gap: 1.1rem; + text-align: left; + } + + #how-it-works .step .step-text { + order: 1; + } + + #how-it-works .step-illustration-1, + #how-it-works .step-illustration-2, + #how-it-works .step-illustration-3 { + order: 2; + height: 220px; + } + + .step-num { + font-size: 2.5rem; + } + + .step-text h3 { + font-size: 1.45rem; + } +} + +.step-illustration-1:hover, .step-illustration-2:hover, .step-illustration-3:hover { + transform: translateY(-6px) scale(1.02); + border-color: oklch(0.68 0.13 150 / 0.25); + box-shadow: 0 25px 50px oklch(0.68 0.13 150 / 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.1); +} + +/* ========================================================================== + Step 1 Illustration: Browser & Installed Success Popup + ========================================================================== */ +.illus-browser-container { + width: 90%; + height: 80%; + background: oklch(0.11 0.015 138); + border: 1px solid rgba(255, 255, 255, 0.06); + border-radius: 12px; + overflow: hidden; + box-shadow: 0 15px 35px rgba(0, 0, 0, 0.6); + display: flex; + flex-direction: column; +} + +.illus-browser-header { + background: oklch(0.20 0.025 140 / 0.8); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + padding: 0.6rem 0.8rem; + display: flex; + align-items: center; + border-bottom: 1px solid rgba(255, 255, 255, 0.06); +} + +.illus-browser-dots { + display: flex; + gap: 6px; + margin-right: 1.5rem; +} + +.illus-browser-dot { + width: 8px; + height: 8px; + border-radius: 50%; + opacity: 0.6; +} +.illus-browser-dot:nth-child(1) { background: oklch(0.55 0.15 25); } +.illus-browser-dot:nth-child(2) { background: oklch(0.68 0.14 45); } +.illus-browser-dot:nth-child(3) { background: oklch(0.68 0.13 150); } + +.illus-browser-address-bar { + background: oklch(0.13 0.02 138); + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 6px; + color: var(--text-muted); + font-size: 0.68rem; + padding: 3px 12px; + flex-grow: 1; + max-width: 60%; + text-align: center; + font-family: monospace; + letter-spacing: 0.2px; +} + +.illus-browser-address-bar .url-protocol { + color: oklch(0.68 0.13 150); + font-weight: 600; +} + +.illus-browser-toolbar { + display: flex; + margin-left: auto; +} + +.illus-extension-btn { + width: 22px; + height: 22px; + border-radius: 6px; + display: flex; + align-items: center; + justify-content: center; + transition: opacity 0.3s, box-shadow 0.3s; +} + +.illus-extension-btn.active { + background: oklch(0.68 0.13 150 / 0.2); + border: 1px solid var(--accent); + box-shadow: 0 0 10px oklch(0.68 0.13 150 / 0.4); + animation: active-pulse 2s infinite alternate; +} + +@keyframes active-pulse { + 0% { transform: scale(1); box-shadow: 0 0 5px oklch(0.68 0.13 150 / 0.4); } + 100% { transform: scale(1.08); box-shadow: 0 0 12px oklch(0.68 0.13 150 / 0.7); } +} + +.illus-browser-content { + flex-grow: 1; + padding: 1rem; + position: relative; + display: flex; + align-items: flex-start; +} + +.illus-store-card { + background: rgba(255, 255, 255, 0.02); + border: 1px solid rgba(255, 255, 255, 0.04); + border-radius: 12px; + padding: 0.8rem; + width: 65%; + display: flex; + flex-direction: column; + gap: 10px; +} + +.illus-store-card-header { + display: flex; + align-items: center; + gap: 8px; +} + +.illus-large-logo { + width: 30px; + height: 30px; + object-fit: contain; + border-radius: 6px; + box-shadow: 0 0 10px oklch(0.68 0.13 150 / 0.2); +} + +.illus-store-info { + display: flex; + flex-direction: column; +} + +.illus-store-title { + font-size: 0.88rem; + font-weight: 800; + color: var(--text); + line-height: 1.1; +} + +.illus-store-desc { + font-size: 0.58rem; + color: var(--text-muted); + line-height: 1.2; + margin-top: 2px; +} + +.illus-store-buttons { + display: flex; + flex-direction: column; + gap: 6px; +} + +.illus-store-btn { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 5px 8px; + border-radius: 8px; + font-size: 0.58rem; + font-weight: 700; + color: white; + border: 1px solid rgba(255, 255, 255, 0.05); + background: rgba(255, 255, 255, 0.03); + box-shadow: 0 2px 5px rgba(0,0,0,0.2); + cursor: default; + transition: opacity 0.2s, transform 0.2s; +} + +.illus-store-btn img { + width: 11px; + height: 11px; + display: block; +} + +.illus-store-btn.chrome { + border-color: oklch(0.68 0.13 150 / 0.25); + background: oklch(0.68 0.13 150 / 0.08); + box-shadow: 0 0 10px oklch(0.68 0.13 150 / 0.05); +} + +.illus-store-btn.firefox { + border-color: oklch(0.62 0.14 45 / 0.25); + background: oklch(0.62 0.14 45 / 0.08); + box-shadow: 0 0 10px oklch(0.62 0.14 45 / 0.05); +} + +.install-breathe { + animation: install-breathe 2.5s ease-in-out infinite; + z-index: 2; + position: relative; +} + +@keyframes install-breathe { + 0%, 100% { + transform: scale(1); + box-shadow: 0 0 15px oklch(0.68 0.13 150 / 0.2); + } + 50% { + transform: scale(1.05); + box-shadow: 0 0 25px oklch(0.68 0.13 150 / 0.5); + } +} + +.install-breathe.firefox, +.btn-firefox.install-breathe { + animation-name: install-breathe-ff; +} + +@keyframes install-breathe-ff { + 0%, 100% { + transform: scale(1); + box-shadow: 0 0 15px oklch(0.62 0.14 45 / 0.2); + } + 50% { + transform: scale(1.05); + box-shadow: 0 0 25px oklch(0.62 0.14 45 / 0.5); + } +} + +/* Glassmorphic Dropping Success Card */ +.illus-extension-popup { + position: absolute; + top: 0.6rem; + right: 0.6rem; + width: 145px; + background: oklch(0.20 0.025 140 / 0.85); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border: 1px solid oklch(0.68 0.13 150 / 0.25); + border-radius: 12px; + padding: 0.6rem 0.8rem; + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.55), 0 0 15px oklch(0.68 0.13 150 / 0.15); + animation: popup-float 4s ease-in-out infinite; + z-index: 5; +} + +@keyframes popup-float { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-4px); } +} + +.illus-extension-popup .popup-title { + display: flex; + align-items: center; + gap: 5px; + font-size: 0.72rem; + font-weight: 700; + color: var(--text); + margin-bottom: 6px; + border-bottom: 1px solid rgba(255, 255, 255, 0.08); + padding-bottom: 4px; +} + +.illus-extension-popup .popup-title img { + width: 12px; + height: 12px; +} + +.illus-extension-popup .popup-status { + margin-bottom: 4px; +} + +.status-badge-success { + background: oklch(0.68 0.13 150 / 0.12); + color: oklch(0.68 0.13 150); + font-size: 0.6rem; + font-weight: 700; + padding: 2px 6px; + border-radius: 4px; + border: 1px solid oklch(0.68 0.13 150 / 0.2); + display: inline-flex; + align-items: center; + gap: 3px; + animation: status-glow 2s infinite alternate; +} + +@keyframes status-glow { + 0% { box-shadow: 0 0 0 oklch(0.68 0.13 150 / 0); } + 100% { box-shadow: 0 0 6px oklch(0.68 0.13 150 / 0.3); } +} + +.illus-extension-popup .popup-quick-start { + font-size: 0.55rem; + color: var(--text-muted); + font-weight: 500; + line-height: 1.2; +} + + +/* ========================================================================== + Step 2 Illustration: Highly Realistic Extension Popup (Create Room View) + ========================================================================== */ +.illus-popup-card { + width: 220px; + background: oklch(0.20 0.025 140 / 0.85); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + border: 1px solid rgba(255, 255, 255, 0.12); + border-radius: 16px; + padding: 0.8rem; + box-shadow: 0 18px 40px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.1); + display: flex; + flex-direction: column; + gap: 0.55rem; + animation: popup-float 4s ease-in-out infinite; + animation-delay: -1s; +} + +.illus-popup-header { + display: flex; + justify-content: space-between; + align-items: center; + border-bottom: 1px solid rgba(255, 255, 255, 0.06); + padding-bottom: 8px; +} + +.illus-popup-brand { + display: flex; + align-items: center; + gap: 6px; + font-size: 0.82rem; /* Scaled up font */ + font-weight: 800; + letter-spacing: -0.2px; +} + +.illus-popup-version { + font-size: 0.58rem; + color: var(--text-muted); +} + +.illus-popup-tabs { + display: flex; + gap: 4px; + background: oklch(0.12 0.015 135 / 0.4); + padding: 3px; + border-radius: 8px; + border: 1px solid rgba(255, 255, 255, 0.03); +} + +.illus-popup-tab { + flex: 1; + text-align: center; + font-size: 0.62rem; /* Scaled up font */ + padding: 4px 0; + border-radius: 6px; + color: var(--text-muted); + font-weight: 600; +} + +.illus-popup-tab.active { + background: var(--card); + color: var(--accent); + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); +} + +.illus-popup-body { + display: flex; + flex-direction: column; + gap: 0.55rem; + position: relative; +} + +.illus-popup-btn { + background: linear-gradient(135deg, var(--accent-green), var(--accent-terracotta)); + border-radius: 8px; + padding: 7px 9px; + color: var(--text-on-green); + font-weight: 700; + font-size: 0.68rem; + text-align: center; + box-shadow: 0 4px 12px oklch(0.68 0.13 150 / 0.3); + position: relative; + overflow: hidden; + cursor: default; + animation: create-btn-pulse 2s infinite alternate; +} + +@keyframes create-btn-pulse { + 0% { transform: scale(1); box-shadow: 0 4px 12px oklch(0.68 0.13 150 / 0.3); } + 100% { transform: scale(1.03); box-shadow: 0 6px 16px oklch(0.68 0.13 150 / 0.5); } +} + +.illus-btn-shine { + position: absolute; + top: 0; + left: -100%; + width: 50%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.25), transparent); + transform: skewX(-20deg); + animation: shimmer-sweep 3s infinite ease-in-out; +} + +@keyframes shimmer-sweep { + 0% { left: -100%; } + 50%, 100% { left: 150%; } +} + +.illus-collapsed-details { + background: oklch(0.12 0.015 135 / 0.4); + border: 1px solid rgba(255, 255, 255, 0.05); + border-radius: 8px; + padding: 6px 11px; + font-size: 0.54rem; + color: var(--text-muted); + font-weight: 700; + text-transform: uppercase; + display: flex; + align-items: center; + gap: 6px; + letter-spacing: 0.2px; +} + +.details-arrow { + font-size: 0.48rem; + color: var(--text-muted); + opacity: 0.7; +} + +/* Floating Success Badge */ +.illus-floating-success { + position: absolute; + /* Fully below the card so the toast never straddles its border */ + bottom: -2.4rem; + left: 50%; + transform: translateX(-50%); + background: oklch(0.12 0.015 135 / 0.92); + border: 1px solid oklch(0.68 0.13 150 / 0.35); + border-radius: 20px; + padding: 3px 10px; + display: flex; + align-items: center; + gap: 4px; + box-shadow: 0 6px 14px rgba(0, 0, 0, 0.45), 0 0 12px oklch(0.68 0.13 150 / 0.18); + animation: floating-success-fade 5s infinite; + white-space: nowrap; + z-index: 10; +} + +@keyframes floating-success-fade { + 0%, 100%, 75% { opacity: 0; transform: translate(-50%, 5px) scale(0.9); } + 15%, 60% { opacity: 1; transform: translate(-50%, 0) scale(1); } +} + +.illus-floating-success .success-icon { + font-size: 0.65rem; +} + +.illus-floating-success span[lang] { + font-size: 0.58rem; + color: oklch(0.68 0.13 150); + font-weight: 700; +} + + +/* ========================================================================== + Step 3 Illustration: Dual Screen Theater Sync Setup + ========================================================================= */ +.illus-sync-theater { + width: 95%; + display: flex; + align-items: center; + justify-content: center; + position: relative; + gap: 0.6rem; +} + +.illus-player-card { + width: 145px; + background: oklch(0.20 0.025 140 / 0.85); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 12px; + padding: 6px; + box-shadow: 0 12px 30px rgba(0,0,0,0.5); + display: flex; + flex-direction: column; + gap: 4px; + position: relative; + z-index: 2; +} + +.illus-player-card.player-a { + animation: popup-float 4s ease-in-out infinite; + animation-delay: -2s; +} + +.illus-player-card.player-b { + animation: popup-float 4s ease-in-out infinite; + animation-delay: -3s; +} + +.player-header { + display: flex; + justify-content: space-between; + align-items: center; + font-size: 0.55rem; +} + +.player-user { + font-weight: 700; + color: var(--text); +} + +.player-badge { + font-size: 0.42rem; + font-weight: 800; + padding: 1px 3px; + border-radius: 3px; +} + +.player-badge.active { + background: oklch(0.68 0.13 150 / 0.15); + color: oklch(0.68 0.13 150); + border: 1px solid oklch(0.68 0.13 150 / 0.25); +} + +.player-video-canvas { + height: 70px; + border-radius: 8px; + background: linear-gradient(135deg, oklch(0.20 0.04 145), oklch(0.22 0.05 45)); + position: relative; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; + border: 1px solid rgba(255, 255, 255, 0.02); +} + +.player-video-overlay { + position: absolute; + top: 0; left: 0; width: 100%; height: 100%; + background: linear-gradient(to bottom, transparent, oklch(0.68 0.13 150 / 0.15), transparent); + background-size: 100% 200%; + animation: playerScrollScanline 3s linear infinite; +} + +@keyframes playerScrollScanline { + 0% { background-position: 0% 0%; } + 100% { background-position: 0% 200%; } +} + +.player-play-pulse { + width: 24px; + height: 24px; + border-radius: 50%; + background: oklch(0.68 0.13 150 / 0.85); + color: var(--text-on-green); + font-size: 0.65rem; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 0 10px oklch(0.68 0.13 150 / 0.5); + z-index: 3; + animation: play-pulse 2s infinite alternate; +} + +@keyframes play-pulse { + 0% { transform: scale(1); box-shadow: 0 0 6px oklch(0.68 0.13 150 / 0.4); } + 100% { transform: scale(1.15); box-shadow: 0 0 15px oklch(0.68 0.13 150 / 0.8); } +} + +.player-controls { + display: flex; + align-items: center; + gap: 4px; + padding: 2px 2px 0 2px; +} + +.control-btn { + font-size: 0.58rem; + color: var(--accent); +} + +.player-timeline { + flex-grow: 1; + height: 3px; + background: rgba(255, 255, 255, 0.1); + border-radius: 2px; + position: relative; +} + +.timeline-fill { + position: absolute; + top: 0; left: 0; + height: 100%; + background: var(--accent); + border-radius: 2px; + width: 60%; + animation: theater-grow 6s infinite linear; +} + +.timeline-knob { + position: absolute; + top: -2px; + left: 60%; + width: 7px; + height: 7px; + border-radius: 50%; + background: white; + box-shadow: 0 0 4px rgba(0,0,0,0.5); + transform: translateX(-50%); + animation: theater-knob-grow 6s infinite linear; +} + +@keyframes theater-grow { + 0% { width: 0%; } + 100% { width: 100%; } +} + +@keyframes theater-knob-grow { + 0% { left: 0%; } + 100% { left: 100%; } +} + +.control-time { + font-size: 0.5rem; + color: var(--text-muted); + font-family: monospace; +} + +/* Glowing Connection Sync Bridge */ +.illus-sync-bridge { + flex-grow: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 6px; + position: relative; + z-index: 1; +} + +.illus-sync-bridge .sync-line { + width: 100%; + height: 2px; + background: rgba(255,255,255,0.06); + position: relative; + overflow: hidden; +} + +.illus-sync-bridge .sync-beam { + position: absolute; + top: 0; + left: -100%; + width: 50px; + height: 100%; + background: linear-gradient(90deg, transparent, var(--accent), var(--success), transparent); + animation: sync-flow 2.5s infinite linear; +} + +@keyframes sync-flow { + 0% { left: -100%; } + 100% { left: 100%; } +} + +.sync-status-badge { + background: oklch(0.68 0.13 150 / 0.12); + border: 1px solid oklch(0.68 0.13 150 / 0.25); + color: oklch(0.68 0.13 150); + font-size: 0.5rem; + font-weight: 800; + letter-spacing: 0.5px; + padding: 2px 6px; + border-radius: 6px; + white-space: nowrap; + animation: active-pulse-green 2s infinite alternate; +} + +@keyframes active-pulse-green { + 0% { box-shadow: 0 0 3px oklch(0.68 0.13 150 / 0.1); } + 100% { box-shadow: 0 0 8px oklch(0.68 0.13 150 / 0.4); } +} + +/* --- Self Hosters Terminal --- */ +.terminal-container { + max-width: 850px; + margin: 3rem auto 0 auto; + background: oklch(0.20 0.025 140); + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 16px; + overflow: hidden; + box-shadow: 0 20px 45px rgba(0,0,0,0.5); + text-align: left; +} + +.terminal-header { + background: oklch(0.27 0.035 130); + padding: 0.85rem 1.25rem; + display: flex; + justify-content: space-between; + align-items: center; + border-bottom: 1px solid rgba(255,255,255,0.06); +} + +.terminal-dots { + display: flex; + gap: 6px; +} + +.terminal-dot { + width: 10px; + height: 10px; + border-radius: 50%; +} + +.terminal-dot.red { background: oklch(0.55 0.15 25); } +.terminal-dot.yellow { background: oklch(0.68 0.14 45); } +.terminal-dot.green { background: oklch(0.68 0.13 150); } + +.terminal-tabs { + display: flex; + gap: 0.5rem; +} + +.terminal-tab-btn { + background: none; + border: none; + color: var(--text-muted); + font-weight: 600; + font-size: 0.8rem; + padding: 0.35rem 0.75rem; + border-radius: 6px; + cursor: pointer; + transition: color 0.2s, background-color 0.2s; +} + +.terminal-tab-btn:hover { + color: var(--text); + background: rgba(255, 255, 255, 0.03); +} + +.terminal-tab-btn.active { + color: var(--accent); + background: oklch(0.68 0.13 150 / 0.1); + border: 1px solid oklch(0.68 0.13 150 / 0.2); +} + +.terminal-body { + position: relative; + padding: 1.5rem; +} + +.terminal-pane { + display: none; + margin: 0; +} + +.terminal-pane.active { + display: block; +} + +.terminal-pane pre { + margin: 0; + overflow-x: auto; +} + +.terminal-pane code { + font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; + font-size: 0.85rem; + line-height: 1.6; + color: oklch(0.88 0.015 90); +} + +/* Syntax Highlighting */ +.t-comment { color: oklch(0.55 0.02 110); font-style: italic; } +.t-key { color: oklch(0.72 0.12 45); } +.t-val { color: oklch(0.75 0.10 150); } +.t-str { color: oklch(0.80 0.12 155); } +.t-num { color: oklch(0.75 0.12 50); } + +.terminal-copy-btn { + position: absolute; + top: 1rem; + right: 1.5rem; + background: rgba(255, 255, 255, 0.04); + border: 1px solid rgba(255, 255, 255, 0.08); + color: var(--text-muted); + padding: 0.35rem 0.75rem; + font-size: 0.75rem; + font-weight: 600; + border-radius: 6px; + cursor: pointer; + transition: background-color 0.2s, color 0.2s, opacity 0.2s; + display: flex; + align-items: center; + gap: 0.3rem; +} + +.terminal-copy-btn:hover { + background: rgba(255, 255, 255, 0.08); + color: var(--text); + border-color: rgba(255, 255, 255, 0.15); +} + +.terminal-copy-btn:active { + transform: scale(0.97); +} + +/* --- Footer --- */ +footer { + padding: 4rem 0; + border-top: 0; + text-align: center; + color: var(--text-muted); +} + +footer a { + color: var(--text-muted); + text-decoration: none; + transition: color 0.2s ease; +} + +footer a:hover { + color: var(--text); +} + + +/* --- Animations --- */ +[data-reveal] { + opacity: 0; + transform: translateY(30px); + transition: opacity 0.8s cubic-bezier(0.4, 0, 0.2, 1), transform 0.8s cubic-bezier(0.4, 0, 0.2, 1); +} + +[data-reveal].revealed { + opacity: 1; + transform: translateY(0); +} + +html.theme-light .hero-demo-scene .demo-user-a, +html.theme-light .hero-demo-scene .demo-user-b { + color: oklch(0.36 0.105 150); + background: oklch(0.60 0.12 150 / 0.13); +} + +html.theme-light .hero-demo-scene .extension-mockup { + --bg: oklch(0.935 0.018 110) !important; + --card: oklch(0.995 0.006 100) !important; + --accent: oklch(0.56 0.13 150) !important; + --accent-glow: oklch(0.56 0.13 150 / 0.22) !important; + --text: oklch(0.21 0.03 140) !important; + --text-muted: oklch(0.43 0.03 135) !important; + --success: oklch(0.56 0.13 150) !important; + --error: oklch(0.57 0.17 25) !important; + --glass-border: oklch(0.28 0.035 140 / 0.10) !important; + background: oklch(0.935 0.018 110) !important; + border-color: oklch(0.28 0.035 140 / 0.12); + box-shadow: + 0 20px 42px oklch(0.20 0.025 140 / 0.16), + 0 0 26px oklch(0.82 0.10 85 / 0.12); +} + +html.theme-light .hero-demo-scene .mock-tabs, +html.theme-light .hero-demo-scene .mock-card, +html.theme-light .hero-demo-scene .mock-input { + background: oklch(0.995 0.006 100); + border-color: oklch(0.28 0.035 140 / 0.14); + color: oklch(0.21 0.03 140); +} + +html.theme-light .hero-demo-scene .mock-peer-item { + background: oklch(0.28 0.035 140 / 0.035); + border-color: oklch(0.28 0.035 140 / 0.09); +} + +html.theme-light .hero-demo-scene .mock-joined-room, +html.theme-light .hero-demo-scene .mock-status-pill { + background: oklch(0.56 0.13 150 / 0.10); + border-color: oklch(0.56 0.13 150 / 0.24); +} + +html.theme-light .hero-demo-scene .mock-close-btn:hover { + color: oklch(0.21 0.03 140); + background: oklch(0.28 0.035 140 / 0.08); +} + +/* A direct hash destination is content, not an entrance animation. Keep it + readable from the first paint even before app.js has initialized. */ +section:target [data-reveal], +section:target[data-reveal] { + opacity: 1; + transform: translateY(0); +} + + +/* --- 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; + } +} + +/* --- 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; +} + +/* --- 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; + } +} + +/* --- 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; + } +} + +/* --- 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); +} + +/* --- 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 { + } +} + +/* --- 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; + } +} + +/* --- Hero Live Demo Scene (two synced tabs + extension popup) --- */ +.hero-mockup-wrapper { + flex-direction: column; + gap: 0.9rem; + container-type: inline-size; +} + +/* Fixed height that already fits the open popup, so toggling it never changes + layout (the vertically centered hero never jumps). The empty lower area is + the invisible room the popup drops into. + + Do not move .hero-mockup-wrapper down to align the headline and demo tops: + the extension popup is bottom-anchored inside this scene, so moving the whole + wrapper destroys the vertical balance and leaves no breathing room below. */ +.hero-demo-scene { + --demo-content-y: 0px; + position: relative; + width: 100%; + height: max(calc(59cqw + 100px), 548px); +} + +.demo-tab-stack { + position: relative; + width: 100%; + height: calc(59cqw + 100px + var(--demo-content-y)); +} + +@media (min-width: 1025px) { + .hero-demo-scene { + /* Balance the demo inside the visible hero area below the fixed navbar. + Shift the scene content, not the hero text or grid. */ + --demo-content-y: 2.75rem; + } +} + +/* While initializing, suppress all transitions so the popup can start closed + without a visible open->closed animation on page load. */ +.hero-demo-scene.demo-no-anim, +.hero-demo-scene.demo-no-anim * { + transition-duration: 0s !important; +} + +/* Sync status chip: green "in sync" at rest, flashes the broadcast event */ +.demo-sync-chip { + position: absolute; + top: var(--demo-content-y); + left: 2px; + z-index: 8; + display: inline-flex; + align-items: center; + gap: 7px; + padding: 5px 12px; + border-radius: 99px; + background: oklch(0.68 0.13 150 / 0.12); + border: 1px solid oklch(0.68 0.13 150 / 0.35); + color: #86efac; + font-family: inherit; + font-size: 11px; + font-weight: 800; + letter-spacing: 0.06em; + text-transform: uppercase; + cursor: default; + transition: background 0.3s, border-color 0.3s, color 0.3s; +} + +.demo-chip-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: oklch(0.68 0.13 150); + box-shadow: 0 0 8px oklch(0.68 0.13 150); + transition: background 0.3s, box-shadow 0.3s; +} + +.demo-sync-chip .demo-chip-event { display: none; } + +.demo-sync-chip.event { + background: oklch(0.68 0.13 150 / 0.14); + border-color: oklch(0.68 0.13 150 / 0.45); + color: oklch(0.85 0.07 150); +} + +.demo-sync-chip.event .demo-chip-dot { + background: oklch(0.75 0.13 150); + box-shadow: 0 0 8px oklch(0.75 0.13 150); +} + +.demo-sync-chip.event .demo-chip-label-sync { display: none; } +.demo-sync-chip.event .demo-chip-event { display: inline; } + +@keyframes demoBlink { + 50% { opacity: 0.35; } +} + +/* Video tab cards: a clean window cascade — the right one is in front */ +.demo-tab-card { + position: absolute; + width: 310px; + width: 70cqw; + background: var(--demo-card); + border: 1px solid rgba(255, 255, 255, 0.09); + border-radius: 14px; + box-shadow: 0 18px 36px rgba(0, 0, 0, 0.45), inset 0 1px rgba(255, 255, 255, 0.05); + overflow: hidden; + cursor: pointer; + user-select: none; + transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1), border-color 0.25s; +} + +.demo-tab-card:hover, +.demo-tab-card:focus-visible { + border-color: oklch(0.68 0.13 150 / 0.55); +} + +/* Card A = the foreground window at the top right ("your" browser, + hosts the extension icon in its toolbar) */ +.demo-tab-a { + top: calc(49px + var(--demo-content-y)); + right: -3%; + transform: rotate(1.2deg); + transform-origin: top right; + z-index: 3; +} + +.demo-tab-a:hover { + z-index: 4; + transform: rotate(1.2deg) scale(1.02); + box-shadow: 0 25px 50px rgba(0, 0, 0, 0.65), 0 0 20px oklch(0.68 0.13 150 / 0.2); +} + +/* Card B = the friend's window, lower left, tucked behind */ +.demo-tab-b { + top: 142px; + top: calc(20cqw + 67px + var(--demo-content-y)); + left: -3%; + transform: rotate(-1.6deg); + transform-origin: top left; + z-index: 2; +} + +.demo-tab-b:hover { + z-index: 4; + transform: rotate(-1.6deg) scale(1.02); + box-shadow: 0 25px 50px rgba(0, 0, 0, 0.65), 0 0 20px oklch(0.68 0.13 150 / 0.2); +} + +/* When the popup slides in from the right edge, the tabs step aside */ +.hero-demo-scene.popup-open .demo-tab-a { + top: calc(22px + var(--demo-content-y)); + transform: rotate(1deg) translate(-44%, 20%) scale(0.88); +} + +.hero-demo-scene.popup-open .demo-tab-a:hover { + z-index: 4; + transform: rotate(1deg) translate(-44%, 20%) scale(0.92); + box-shadow: 0 25px 50px rgba(0, 0, 0, 0.65), 0 0 20px oklch(0.68 0.13 150 / 0.2); +} + +.hero-demo-scene.popup-open .demo-tab-b { + top: calc(20cqw + 40px + var(--demo-content-y)); + transform: rotate(-1.6deg) translate(0, 14%) scale(0.94); +} + +.hero-demo-scene.popup-open .demo-tab-b:hover { + z-index: 4; + transform: rotate(-1.6deg) translate(0, 14%) scale(0.98); + box-shadow: 0 25px 50px rgba(0, 0, 0, 0.65), 0 0 20px oklch(0.68 0.13 150 / 0.2); +} + +.demo-tab-titlebar { + display: flex; + align-items: center; + gap: 8px; + padding: 6px 8px 6px 10px; + background: var(--demo-toolbar); + border-bottom: 1px solid rgba(255, 255, 255, 0.06); + font-size: 10.5px; + color: var(--text-muted); + font-weight: 600; +} + +.demo-tab-dots { + display: flex; + gap: 4px; + flex-shrink: 0; +} + +.demo-tab-dots i { + display: block; + width: 7px; + height: 7px; + border-radius: 50%; + background: oklch(0.32 0.03 125); +} + +.demo-tab-title { + display: flex; + align-items: center; + gap: 5px; + flex: 1; + min-width: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.demo-tab-title svg { flex-shrink: 0; } + +.demo-tab-user { + flex-shrink: 0; + font-size: 9.5px; + font-weight: 700; + padding: 2px 7px; + border-radius: 99px; +} + +.demo-user-a { + color: oklch(0.80 0.10 150); + background: oklch(0.68 0.13 150 / 0.15); +} + +.demo-user-b { + color: #86efac; + background: oklch(0.68 0.13 150 / 0.12); +} + +/* Extension launcher pinned in the foreground window's toolbar */ +.demo-ext-launcher { + position: relative; + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + padding: 0; + border-radius: 7px; + background: oklch(0.30 0.03 130); + border: 1px solid rgba(255, 255, 255, 0.12); + cursor: pointer; + transition: border-color 0.25s, box-shadow 0.25s, transform 0.15s; +} + +.demo-ext-launcher img { + display: block; + width: 16px; + height: 16px; + border-radius: 4px; +} + +.demo-ext-launcher:hover { + border-color: var(--accent); + transform: translateY(-1px); +} + +.demo-ext-launcher:active { + transform: scale(0.94); +} + +.hero-demo-scene.popup-open .demo-ext-launcher { + border-color: oklch(0.68 0.13 150 / 0.7); + box-shadow: 0 0 10px oklch(0.68 0.13 150 / 0.45); +} + +.demo-ext-launcher::after { + content: ''; + position: absolute; + top: -3px; + right: -3px; + width: 8px; + height: 8px; + border-radius: 50%; + background: oklch(0.68 0.13 150); + box-shadow: 0 0 8px oklch(0.68 0.13 150); + opacity: 0; + transition: opacity 0.3s; +} + +.hero-demo-scene:not(.popup-open) .demo-ext-launcher:not(.demo-ext-static)::after { + opacity: 1; + animation: demoBlink 1.4s ease-in-out infinite; +} + +/* Card B shows the same extension icon, purely to signal the friend has it + installed too. It is not interactive and does not blink or glow. */ +.demo-ext-static, +.demo-ext-static:hover { + cursor: default; + border-color: rgba(255, 255, 255, 0.12); + transform: none; + box-shadow: none; +} + +.demo-ext-static::after { display: none; } + +/* Abstract "video" artwork with a slow cinematic pan/zoom while playing */ +.demo-video { + position: relative; + aspect-ratio: 16 / 9; + background: var(--demo-sky-bottom); + overflow: hidden; +} + +.demo-video::after { + content: ''; + position: absolute; + inset: 0; + z-index: 1; + pointer-events: none; + /* Cinematic vignette: soft edge falloff + heavier floor for the controls */ + box-shadow: + inset 0 0 0 1px rgba(255, 255, 255, 0.05), + inset 0 0 34px oklch(0.04 0.015 145 / 0.32), + inset 0 -26px 40px oklch(0.04 0.015 145 / 0.26); +} + +html.theme-light .hero-demo-scene .demo-video::after { + box-shadow: + inset 0 0 0 1px oklch(0.30 0.04 145 / 0.13), + inset 0 18px 32px oklch(0.98 0.03 90 / 0.1), + inset 0 -22px 34px oklch(0.18 0.05 145 / 0.16); +} + +.demo-video-art { + position: absolute; + inset: 0; + background: + radial-gradient(30% 42% at 76% 29%, var(--demo-glow), transparent 64%), + radial-gradient(90% 85% at 18% 5%, var(--demo-glow), transparent 66%), + linear-gradient(160deg, var(--demo-sky-top) 0%, var(--demo-sky-mid) 52%, var(--demo-sky-bottom) 100%); + transform-origin: 60% 40%; + animation: + demoKenBurns 9s ease-in-out infinite alternate, + demoArtShift 7s ease-in-out infinite alternate; + animation-play-state: paused; +} + +/* Soft light drifting through the scene */ +.demo-video-art::before { + content: ''; + position: absolute; + top: -25%; + left: -12%; + width: 70%; + aspect-ratio: 1; + border-radius: 50%; + background: radial-gradient(circle, var(--demo-glow), transparent 65%); + animation: demoGlowDrift 7s ease-in-out infinite alternate; + animation-play-state: paused; +} + +.demo-video-art::after { + content: ''; + position: absolute; + inset: 0; + background: linear-gradient(105deg, transparent 30%, oklch(0.75 0.13 150 / 0.14) 45%, oklch(0.75 0.10 150 / 0.1) 55%, transparent 70%); + transform: translateX(-100%); + animation: demoSheen 2.8s linear infinite; + animation-play-state: paused; +} + +/* Playing runs the animations; pausing freezes them mid-frame, so a synced + pause is visible at a glance */ +.demo-tab-card.playing .demo-video-art, +.demo-tab-card.playing .demo-video-art::before, +.demo-tab-card.playing .demo-video-art::after { + animation-play-state: running; +} + +@keyframes demoKenBurns { + from { transform: scale(1) translate(0, 0); } + to { transform: scale(1.18) translate(-3%, 2%); } +} + +@keyframes demoArtShift { + from { filter: hue-rotate(0deg) brightness(1); } + to { filter: hue-rotate(40deg) brightness(1.15); } +} + +@keyframes demoGlowDrift { + from { transform: translate(0, 0); } + to { transform: translate(65%, 30%); } +} + +@keyframes demoSheen { + to { transform: translateX(100%); } +} + +/* Play / pause overlay */ +.demo-play-overlay { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + background: oklch(0.10 0.01 135 / 0.35); + transition: opacity 0.25s, background 0.25s; +} + +.demo-icon-wrap { + display: flex; + align-items: center; + justify-content: center; + width: 46px; + height: 46px; + border-radius: 50%; + background: oklch(0.20 0.025 140 / 0.75); + border: 1px solid rgba(255, 255, 255, 0.25); + color: white; + box-shadow: 0 4px 18px rgba(0, 0, 0, 0.5); + backdrop-filter: blur(4px); + transition: transform 0.2s; +} + +.demo-tab-card:hover .demo-icon-wrap { transform: scale(1.1); } + +.demo-play-overlay .icon-pause { display: none; } +.demo-tab-card.playing .icon-play { display: none; } +.demo-tab-card.playing .icon-pause { display: block; } + +.demo-tab-card.playing .demo-play-overlay { + opacity: 0; + background: transparent; +} + +.demo-tab-card.playing:hover .demo-play-overlay, +.demo-tab-card.playing:focus-visible .demo-play-overlay { + opacity: 1; +} + +/* Timestamp + seekable progress bar */ +.demo-video-controls { + position: absolute; + left: 0; + right: 0; + bottom: 0; + display: flex; + align-items: center; + gap: 8px; + padding: 14px 10px 8px; + background: linear-gradient(transparent, var(--demo-control-fade)); + font-size: 9.5px; + color: oklch(0.88 0.015 90); + z-index: 1; +} + +.demo-time, +.demo-duration { + flex-shrink: 0; + font-family: ui-monospace, SFMono-Regular, Menlo, monospace; + font-weight: 700; + letter-spacing: 0.03em; + font-variant-numeric: tabular-nums; +} + +.demo-duration { + font-weight: 600; + opacity: 0.7; +} + +/* Decorative player chrome: play state glyph, volume, HD, fullscreen */ +.demo-ctrl-icon { + display: flex; + align-items: center; + flex-shrink: 0; + opacity: 0.85; +} + +.demo-ctrl-play .icon-pause { display: none; } +.demo-tab-card.playing .demo-ctrl-play .icon-play { display: none; } +.demo-tab-card.playing .demo-ctrl-play .icon-pause { display: block; } + +.demo-ctrl-badge { + flex-shrink: 0; + font-size: 7px; + font-weight: 800; + letter-spacing: 0.06em; + line-height: 1; + padding: 2px 3px; + border: 1px solid currentColor; + border-radius: 3px; + opacity: 0.65; +} + +.demo-progress { + position: relative; + flex: 1; + height: 5px; + border-radius: 99px; + background: var(--demo-track); + cursor: pointer; +} + +/* Generous invisible hit area for seeking */ +.demo-progress::before { + content: ''; + position: absolute; + inset: -8px 0; +} + +/* Buffered range behind the playhead, like a real player */ +.demo-progress-buffer { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 56%; + border-radius: 99px; + background: oklch(0.92 0.02 95 / 0.14); + pointer-events: none; +} + +.demo-progress-fill { + position: relative; + height: 100%; + width: 30%; + border-radius: 99px; + background: linear-gradient(90deg, oklch(0.68 0.13 150), oklch(0.62 0.14 45)); + pointer-events: none; +} + +/* Scrub thumb, revealed on hover */ +.demo-progress-fill::after { + content: ''; + position: absolute; + right: -5px; + top: 50%; + width: 11px; + height: 11px; + margin-top: -5.5px; + border-radius: 50%; + background: oklch(0.88 0.015 90); + box-shadow: 0 0 6px oklch(0.68 0.13 150 / 0.8); + opacity: 0; + transform: scale(0.6); + transition: opacity 0.2s, transform 0.2s; +} + +.demo-tab-card:hover .demo-progress-fill::after { + opacity: 1; + transform: scale(1); +} + +/* Sync pulse ring on both cards */ +.demo-tab-card.sync-pulse { + animation: demoCardPulse 0.9s ease-out; +} + +@keyframes demoCardPulse { + 0% { box-shadow: 0 18px 36px rgba(0, 0, 0, 0.45), 0 0 0 0 oklch(0.68 0.13 150 / 0.65); } + 100% { box-shadow: 0 18px 36px rgba(0, 0, 0, 0.45), 0 0 0 22px oklch(0.68 0.13 150 / 0); } +} + +/* The existing popup mockup slides in at the right edge, dropping out of + the toolbar icon of the foreground window */ +.hero-demo-scene .extension-mockup { + position: absolute; + bottom: 0; + right: 0; + z-index: 5; + width: 292px; + width: min(320px, 66cqw); + margin: 0; + transform-origin: top right; + transition: transform 0.45s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.35s, visibility 0.45s; +} + +@media (min-width: 1025px) { + .hero-demo-scene .extension-mockup { + /* Counter the content shift so the opened panel is balanced below nav. */ + bottom: 0.625rem; + } +} + +.hero-demo-scene:not(.popup-open) .extension-mockup { + transform: translate(2%, -3%) scale(0.5); + opacity: 0; + visibility: hidden; + pointer-events: none; +} + +/* Ghost cursor for the automated walkthrough */ +.demo-cursor { + position: absolute; + top: 52%; + left: 46%; + z-index: 20; + width: 22px; + height: 22px; + opacity: 0; + pointer-events: none; + filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.6)); + transition: top 0.65s cubic-bezier(0.45, 0.05, 0.25, 1), left 0.65s cubic-bezier(0.45, 0.05, 0.25, 1), opacity 0.35s; +} + +.demo-cursor.visible { opacity: 1; } + +.demo-cursor svg { + display: block; + width: 100%; + height: 100%; +} + +.demo-cursor::before { + content: ''; + position: absolute; + top: -6px; + left: -8px; + width: 32px; + height: 32px; + border-radius: 50%; + border: 2px solid oklch(0.75 0.13 150); + opacity: 0; + transform: scale(0.35); +} + +.demo-cursor.clicking::before { + animation: demoClickRipple 0.45s ease-out; +} + +@keyframes demoClickRipple { + 0% { opacity: 0.9; transform: scale(0.35); } + 100% { opacity: 0; transform: scale(1.25); } +} + +/* Caption below the scene */ +.demo-hint { + margin: 0.75rem auto 0; + max-width: min(100%, 34rem); + text-align: center; + font-size: 0.82rem; + line-height: 1.45; + color: var(--text-muted); + opacity: 0; + transition: opacity 0.6s; +} + +.demo-hint.show { opacity: 0.75; } + +/* Tablet/Small Desktop: fall back to the classic static popup mockup */ +@media (max-width: 1023px) { + .hero-demo-scene, + .hero-demo-scene.popup-open { + height: auto; + max-width: 320px; + margin: 0 auto; + } + + .demo-tab-card, + .demo-tab-stack, + .demo-sync-chip, + .demo-cursor, + .demo-hint { + display: none !important; + } + + .hero-demo-scene .extension-mockup, + .hero-demo-scene:not(.popup-open) .extension-mockup { + position: static; + width: 100%; + transform: none; + opacity: 1; + visibility: visible; + pointer-events: auto; + } +} + +@media (prefers-reduced-motion: reduce) { + .hero-demo-scene:not(.popup-open) .demo-ext-launcher::after { + animation: none; + } +} + +/* --- Story walkthrough additions: room creation, invite flight, film --- */ + +/* The stick-figure film carries the motion now — keep only the color drift + on the backdrop (the animation shorthand resets play-state, so re-pause) */ +.demo-video-art { + animation: demoArtShift 7s ease-in-out infinite alternate; + animation-play-state: paused; +} + +.demo-film { + position: absolute; + inset: 0; + width: 100%; + height: 100%; +} + +.hero-demo-scene .film-stars { fill: var(--demo-stars) !important; } +.hero-demo-scene .film-clouds { fill: var(--demo-stars) !important; } +.hero-demo-scene .film-mist-scroll { fill: var(--demo-stars) !important; } +.hero-demo-scene .film-shooting-star { stroke: var(--demo-stars) !important; } +.hero-demo-scene .film-moon { + fill: var(--demo-moon) !important; + filter: drop-shadow(0 0 6px var(--demo-moon)); +} +.hero-demo-scene .film-mtn-scroll { fill: var(--demo-mountain) !important; } +.hero-demo-scene .film-mid-scroll > * { fill: var(--demo-midground) !important; } +.hero-demo-scene .film-fore-scroll > * { fill: var(--demo-foreground) !important; } + +/* By day the birds read as silhouettes, not chalk marks */ +html.theme-light .hero-demo-scene .film-birds { stroke: oklch(0.32 0.06 145 / 0.65); } +html.theme-light :is(.hero-demo-scene, .step-illustration-3) .film-hero-bird { stroke: oklch(0.32 0.06 145 / 0.75); } + +/* Stars: staggered twinkle so the sky feels alive */ +.film-star { + transform-box: fill-box; + transform-origin: center; + animation: filmTwinkle 2.8s ease-in-out infinite; +} +.film-star:nth-child(2) { animation-duration: 3.4s; animation-delay: 0.5s; } +.film-star:nth-child(3) { animation-duration: 2.4s; animation-delay: 1.1s; } +.film-star:nth-child(4) { animation-duration: 3.6s; animation-delay: 0.3s; } +.film-star:nth-child(5) { animation-duration: 2.6s; animation-delay: 1.6s; } +.film-star:nth-child(6) { animation-duration: 3.2s; animation-delay: 0.8s; } +.film-star:nth-child(7) { animation-duration: 2.2s; animation-delay: 1.3s; } + +@keyframes filmTwinkle { + 0%, 100% { opacity: 0.25; transform: scale(0.85); } + 50% { opacity: 1; transform: scale(1.15); } +} + +/* Moon: gentle glow pulse + slow arc drift. The drift lives on the wrapper + group so the craters travel with the disc; the glow stays on the circle. + (Step-3 uses a bare .film-moon without wrapper — filmMoon still covers it.) */ +.film-moon-g { + transform-box: fill-box; + transform-origin: center; + animation: filmMoonDrift 6s ease-in-out infinite alternate; + animation-play-state: paused; +} + +.film-moon-g .film-moon { + animation-name: filmMoonGlow; +} + +@keyframes filmMoonDrift { + from { transform: translate(0, 0); } + to { transform: translate(-6px, 3px); } +} + +@keyframes filmMoonGlow { + from { filter: drop-shadow(0 0 4px oklch(0.95 0.01 100 / 0.5)); } + to { filter: drop-shadow(0 0 10px oklch(0.95 0.01 100 / 0.95)); } +} + +.film-moon { + transform-box: fill-box; + transform-origin: center; + animation: filmMoon 6s ease-in-out infinite alternate; + animation-play-state: paused; +} + +@keyframes filmMoon { + from { transform: translate(0, 0); filter: drop-shadow(0 0 4px oklch(0.95 0.01 100 / 0.5)); } + to { transform: translate(-6px, 3px); filter: drop-shadow(0 0 10px oklch(0.95 0.01 100 / 0.95)); } +} + +.film-mtn-scroll { + transform: translateX(var(--scroll-back, 0px)); +} +.film-mid-scroll { + transform: translateX(var(--scroll-mid, 0px)); +} +.film-fore-scroll { + transform: translateX(var(--scroll-fore, 0px)); +} +/* A single gliding bird carries the scene: the outer group rides the + JS-driven drift vars, the inner group flaps its wings via scaleY. */ +.film-hero-bird-drift { + transform: translate(var(--lantern-x, 0px), var(--bounce-y, 0px)); +} + +.film-hero-bird { + transform-box: fill-box; + transform-origin: center; + animation: filmWingFlap 0.85s ease-in-out infinite alternate; + animation-play-state: paused; +} + +@keyframes filmWingFlap { + from { transform: scaleY(1); } + to { transform: scaleY(0.5); } +} + +/* Twinkle & Glow play state bindings */ +.film-star { + transform-box: fill-box; + transform-origin: center; + animation: filmTwinkle 2.8s ease-in-out infinite; + animation-play-state: paused; +} +.demo-tab-card.playing :is(.film-star, .film-moon, .film-moon-g, .film-cloud, .film-shooting-star, .film-birds, .film-hero-bird) { + animation-play-state: running; +} + +/* Drifting clouds: gentle back-and-forth so there is no wrap pop */ +.film-cloud { + transform-box: fill-box; + animation: filmCloudDrift 26s ease-in-out infinite alternate; + animation-play-state: paused; +} + +.film-cloud-2 { + animation-duration: 34s; + animation-delay: -12s; +} + +@keyframes filmCloudDrift { + from { transform: translateX(-6px); } + to { transform: translateX(10px); } +} + +/* A shooting star streaks across the sky every few loops */ +.film-shooting-star { + opacity: 0; + animation: filmShoot 11s linear infinite; + animation-play-state: paused; +} + +@keyframes filmShoot { + 0%, 87% { opacity: 0; transform: translate(0, 0); } + 89% { opacity: 0.9; } + 95%, 100% { opacity: 0; transform: translate(-46px, 20px); } +} + +/* A distant flock crosses the valley, fading out mid-flight */ +.film-birds { + animation: filmBirds 16s linear infinite; + animation-play-state: paused; +} + +@keyframes filmBirds { + 0% { transform: translate(0, 0); opacity: 0; } + 4% { opacity: 0.75; } + 42% { opacity: 0.75; } + 48%, 100% { transform: translate(185px, -12px); opacity: 0; } +} + +/* Valley mist rides the slow background parallax between tree layers */ +.film-mist-scroll { + transform: translateX(var(--scroll-back, 0px)); +} + +/* Play button pulsing indicator at the end of the walkthrough */ +.hero-demo-scene.demo-complete #demo-tab-a:not(.playing) .demo-icon-wrap { + animation: playButtonPulse 1.6s ease-in-out infinite; + border-color: oklch(0.68 0.13 150); + box-shadow: 0 0 15px oklch(0.68 0.13 150 / 0.8); +} +@keyframes playButtonPulse { + 0%, 100% { transform: scale(1); box-shadow: 0 0 10px oklch(0.68 0.13 150 / 0.5); } + 50% { transform: scale(1.12); box-shadow: 0 0 20px oklch(0.68 0.13 150 / 0.9); } +} + +/* Seek-reset: scrubbing restarts animations from frame zero */ +.demo-film.demo-reset * { + animation: none !important; +} + +/* Seek-break: scrubbing the timeline visibly glitches both videos so the + jump reads as a real cut instead of an invisible time update. A scanline + sweep + brightness pop, and the film animations restart mid-stride. */ +.demo-video-art { transition: filter 0.12s ease-out; } + +.demo-tab-card.demo-seeking .demo-video-art { + animation-play-state: paused; + filter: brightness(1.9) saturate(1.4) blur(0.6px); +} + +.demo-tab-card .demo-seek-sweep { + position: absolute; + inset: 0; + z-index: 4; + pointer-events: none; + opacity: 0; + background: linear-gradient(90deg, + transparent 0%, + oklch(0.75 0.13 150 / 0.35) 20%, + rgba(255, 255, 255, 0.55) 50%, + oklch(0.75 0.13 150 / 0.35) 80%, + transparent 100%); + mix-blend-mode: screen; + transform: translateX(-100%); +} + +.demo-tab-card.demo-seeking .demo-seek-sweep { + animation: demoSeekSweep 0.34s ease-out; +} + +@keyframes demoSeekSweep { + 0% { opacity: 0; transform: translateX(-100%); } + 20% { opacity: 1; } + 100% { opacity: 0; transform: translateX(100%); } +} + +/* The sync chip only appears once the room is connected (story progression) */ +.demo-sync-chip { + opacity: 0; + transform: translateY(-4px); + pointer-events: none; + transition: background 0.3s, border-color 0.3s, color 0.3s, opacity 0.4s, transform 0.4s; +} + +.hero-demo-scene.connected .demo-sync-chip { + opacity: 1; + transform: none; + pointer-events: auto; +} + +/* Toast on the friend's window ("Room joined", "Tab selected") */ +.demo-tab-toast { + position: absolute; + top: 36px; + left: 50%; + transform: translate(-50%, -6px); + z-index: 2; + padding: 4px 10px; + border-radius: 99px; + background: oklch(0.20 0.025 140 / 0.92); + border: 1px solid oklch(0.68 0.13 150 / 0.5); + color: oklch(0.85 0.07 150); + font-size: 9.5px; + font-weight: 700; + white-space: nowrap; + opacity: 0; + pointer-events: none; + transition: opacity 0.3s, transform 0.3s; +} + +.demo-tab-toast.show { + opacity: 1; + transform: translate(-50%, 0); +} + +/* Invite link pill flying from the popup to the friend's window */ +.demo-invite-fly { + position: absolute; + z-index: 30; + display: inline-flex; + align-items: center; + gap: 5px; + padding: 4px 10px; + border-radius: 99px; + background: var(--accent); + color: var(--text-on-green); + font-size: 9.5px; + font-weight: 700; + white-space: nowrap; + box-shadow: 0 6px 18px oklch(0.68 0.13 150 / 0.5); + opacity: 0; + pointer-events: none; + transition: left 0.75s cubic-bezier(0.3, 0.7, 0.4, 1), top 0.75s cubic-bezier(0.3, 0.7, 0.4, 1), opacity 0.3s; +} + +/* Attention flash (e.g. on the video select while "choosing the tab") */ +.demo-attn { + animation: demoAttn 0.7s ease-in-out 2; +} + +@keyframes demoAttn { + 50% { box-shadow: 0 0 0 3px oklch(0.68 0.13 150 / 0.75); } +} + + +/* --- Bento Card Host Control Mockup Additions --- */ + +.bento-content-wrap { + display: flex; + flex-direction: column; + gap: 1.2rem; + width: 100%; +} + +.bento-host-control-mockup { + background: oklch(0.20 0.025 140 / 0.6); + border: 1px solid rgba(255, 255, 255, 0.05); + border-radius: 12px; + padding: 0.75rem 1rem; + display: flex; + flex-direction: column; + gap: 0.5rem; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2); + width: 100%; + box-sizing: border-box; +} + +.bento-mockup-header { + font-size: 0.65rem; + text-transform: uppercase; + letter-spacing: 0.5px; + color: var(--text-muted); + font-weight: 700; +} + +.bento-mockup-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + font-size: 0.75rem; + color: white; + font-weight: 600; + margin-top: 2px; +} + +/* Custom switch toggle representation */ +.bento-switch-wrap { + position: relative; + display: inline-block; + width: 28px; + height: 16px; + flex-shrink: 0; +} + +.bento-switch-wrap input { + opacity: 0; + width: 0; + height: 0; +} + +.bento-switch-slider { + position: absolute; + cursor: default; + top: 0; left: 0; right: 0; bottom: 0; + background-color: rgba(255, 255, 255, 0.1); + border: 1px solid rgba(255, 255, 255, 0.15); + border-radius: 16px; + transition: .3s; +} + +.bento-switch-slider:before { + position: absolute; + content: ""; + height: 10px; + width: 10px; + left: 2px; + bottom: 2px; + background-color: oklch(0.68 0.02 100); + border-radius: 50%; + transition: .3s; +} + +.bento-switch-wrap input:checked + .bento-switch-slider { + background-color: oklch(0.68 0.13 150 / 0.25); + border-color: oklch(0.68 0.13 150 / 0.6); + box-shadow: 0 0 8px oklch(0.68 0.13 150 / 0.3); +} + +.bento-switch-wrap input:checked + .bento-switch-slider:before { + transform: translateX(12px); + background-color: var(--accent); + box-shadow: 0 0 6px var(--accent-glow); +} + +/* --- Step 2 & Step 3 Visual Redesign Styles --- */ + +/* Step 2 Browser video player mock */ +.illus-video-player-mock { + width: 85%; + height: 65%; + background: linear-gradient(135deg, oklch(0.20 0.04 145), oklch(0.12 0.02 138)); + border: 1px solid rgba(255, 255, 255, 0.05); + border-radius: 8px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: relative; + margin-top: 15px; +} + +.video-mock-play-button { + width: 32px; + height: 32px; + border-radius: 50%; + background: rgba(255, 255, 255, 0.1); + border: 1px solid rgba(255, 255, 255, 0.2); + display: flex; + align-items: center; + justify-content: center; + color: white; + font-size: 0.8rem; + cursor: default; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); +} + +.video-mock-timeline { + position: absolute; + bottom: 8px; + left: 8px; + right: 8px; + height: 3px; + background: rgba(255, 255, 255, 0.15); + border-radius: 2px; +} + +.video-mock-timeline-fill { + width: 35%; + height: 100%; + background: var(--accent); + border-radius: 2px; +} + +/* Step 2 extension popup dropdown alignment */ +.illus-extension-popup.drop-down { + top: 2.2rem; + right: 0.6rem; + width: 145px; + animation: popup-dropdown-float 4s ease-in-out infinite; +} + +@keyframes popup-dropdown-float { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-3px); } +} + +.status-badge-select { + background: oklch(0.68 0.13 150 / 0.12); + color: oklch(0.85 0.07 150); + font-size: 0.58rem; + font-weight: 700; + padding: 2px 6px; + border-radius: 4px; + border: 1px solid oklch(0.68 0.13 150 / 0.2); + display: inline-flex; + align-items: center; + text-transform: uppercase; + letter-spacing: 0.2px; +} + +.popup-form-group { + margin-top: 6px; + width: 100%; + text-align: left; +} + +.popup-form-label { + display: block; + font-size: 0.6rem; + color: var(--text-muted); + margin-bottom: 4px; + font-weight: 700; +} + +.popup-select-wrapper { + position: relative; + width: 100%; +} + +.popup-select-mock { + width: 100%; + background: var(--card); + border: 1px solid rgba(255, 255, 255, 0.12); + color: white; + padding: 6px; + border-radius: 6px; + font-family: inherit; + font-size: 0.62rem; + outline: none; + appearance: none; + -webkit-appearance: none; + font-weight: 600; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + cursor: default; + display: block; + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.2); +} + +.popup-cursor-pointer { + position: absolute; + bottom: -15px; + right: 15px; + font-size: 1.1rem; + pointer-events: none; + z-index: 10; + animation: pointer-click 1.6s infinite ease-in-out; +} + +@keyframes pointer-click { + 0%, 100% { transform: translate(0, 0) scale(1); } + 50% { transform: translate(-3px, -3px) scale(0.9); } +} + +/* Step 3 forest movie continuous animation bindings */ +.step-illustration-3 .film-mtn-scroll { + animation: scrollBack 12s linear infinite !important; +} +.step-illustration-3 .film-mid-scroll { + animation: scrollMid 6s linear infinite !important; +} +.step-illustration-3 .film-fore-scroll { + animation: scrollFore 3s linear infinite !important; +} +.step-illustration-3 .film-hero-bird { + animation: filmWingFlap 0.85s ease-in-out infinite !important; + animation-direction: alternate !important; +} + +@keyframes scrollBack { + 0% { transform: translateX(0); } + 100% { transform: translateX(-160px); } +} +@keyframes scrollMid { + 0% { transform: translateX(0); } + 100% { transform: translateX(-160px); } +} +@keyframes scrollFore { + 0% { transform: translateX(0); } + 100% { transform: translateX(-160px); } +} + +/* --- High Fidelity Micro-Animations & Identity additions --- */ + +/* Scroll progress bar glowing line at the top */ +.scroll-progress-bar { + position: fixed; + top: 0; + left: 0; + height: 3px; + background: linear-gradient(90deg, var(--accent), oklch(0.62 0.14 45), oklch(0.62 0.14 45)); + z-index: 9999; + width: 0%; + transition: width 0.1s ease-out; + box-shadow: 0 0 8px var(--accent-glow); +} + +.scroll-progress-bar::after { + content: ''; + position: absolute; + right: -3px; + top: 50%; + width: 10px; + height: 7px; + border-radius: 0 70% 0 70%; + background: var(--accent-green); + box-shadow: 0 0 6px var(--accent-glow); + transform: translateY(-50%) rotate(24deg); +} + +/* Navigation logo pop & spin */ +.nav-logo-img { + animation: logo-entrance 1.2s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; + transform-origin: center; + transition: transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1); + will-change: transform, opacity; +} + +.nav-logo-img:hover { + transform: scale(1.15) rotate(360deg); +} + +@keyframes logo-entrance { + 0% { + transform: scale(0) rotate(-180deg); + opacity: 0; + } + 100% { + transform: scale(1) rotate(0deg); + opacity: 1; + } +} + +/* Glowing reflection sweep on buttons */ +.btn-primary, .btn-firefox, .btn-secondary { + position: relative; + overflow: hidden; +} + +.btn-primary::after, .btn-firefox::after, .btn-secondary::after { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient( + 90deg, + rgba(255, 255, 255, 0) 0%, + rgba(255, 255, 255, 0.25) 50%, + rgba(255, 255, 255, 0) 100% + ); + transform: skewX(-25deg); + transition: none; + pointer-events: none; +} + +.btn-primary:hover::after, .btn-firefox:hover::after, .btn-secondary:hover::after { + left: 100%; + transition: left 0.8s ease-in-out; +} + +/* Step 2 mockup control panel styles */ +.popup-mock-controls { + display: flex; + gap: 8px; + margin-top: 4px; + width: 100%; +} + +.popup-mock-btn { + flex: 1; + text-align: center; + padding: 6px; + border-radius: 6px; + font-size: 0.62rem; + font-weight: 700; + color: white; + cursor: default; + opacity: 0.75; +} + +.popup-mock-btn.play { + background: oklch(0.68 0.13 150); + color: var(--text-on-green); +} + +.popup-mock-btn.pause { + background: oklch(0.55 0.15 25); +} + +/* Phone and portrait-tablet widths: keep the full hero demo out of the page + flow. The demo modal mounts the same node in its own fixed layer. */ +@media (max-width: 900px) { + .hero-grid > .hero-mockup-wrapper { + display: none !important; + } +} + +@media (max-width: 900px) { + .hero-github-cta { + display: none; + } + + .btn-demo-mobile { + display: inline-flex; + } +} + +body.mobile-demo-open { + overflow: hidden; +} + +.mobile-demo-modal[hidden] { + display: none; +} + +.mobile-demo-modal { + position: fixed; + inset: 0; + z-index: 2400; + display: flex; + flex-direction: column; + padding: 0 !important; +} + +.mobile-demo-backdrop { + position: absolute; + inset: 0; + background: oklch(0.10 0.01 135 / 0.25); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); +} + +.mobile-demo-panel { + position: relative; + z-index: 1; + width: 100% !important; + height: 100% !important; + max-height: none !important; + display: flex; + flex-direction: column; + overflow: hidden; + background: transparent !important; + backdrop-filter: none !important; + -webkit-backdrop-filter: none !important; + border: none !important; + border-radius: 0 !important; + box-shadow: none !important; +} + +.mobile-demo-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; + padding: 0.85rem 0.95rem; + border-bottom: none !important; + flex-shrink: 0; +} + +.mobile-demo-header h2 { + margin: 0; + font-size: 0.95rem; + line-height: 1.2; + color: oklch(0.96 0.01 90); +} + +.mobile-demo-close { + display: inline-flex; + align-items: center; + justify-content: center; + width: 38px; + height: 38px; + padding: 0; + border: 1px solid rgba(255, 255, 255, 0.12); + border-radius: 10px; + background: oklch(0.27 0.035 130 / 0.72); + color: oklch(0.88 0.015 90); + cursor: pointer; + flex-shrink: 0; +} + +.mobile-demo-stage { + position: relative; + flex: 1; + min-height: 0; + overflow: hidden !important; + padding: 1rem 0.75rem 1.25rem; + overscroll-behavior: contain; + display: flex !important; + flex-direction: column !important; + justify-content: center !important; + align-items: center !important; +} + +/* Ensure the demo scene canvas is transparent inside the modal to let the glass backdrop blend */ +.mobile-demo-stage .hero-demo-scene { + background: transparent !important; + border: none !important; + box-shadow: none !important; +} + +/* Ensure the close button hover stays consistent in dark glass theme */ +.mobile-demo-close:hover { + background: rgba(51, 65, 85, 0.85) !important; +} + +/* Light Mode Frosted Glass overrides */ +html.theme-light .mobile-demo-backdrop { + background: oklch(0.95 0.01 100 / 0.2) !important; +} + +html.theme-light .mobile-demo-panel { + background: transparent !important; + border: none !important; + box-shadow: none !important; +} + +html.theme-light .mobile-demo-header { + border-bottom: none !important; +} + +html.theme-light .mobile-demo-header h2 { + color: var(--text) !important; +} + +html.theme-light .mobile-demo-close { + background: rgba(255, 255, 255, 0.4) !important; + border-color: oklch(0.20 0.025 140 / 0.1) !important; + color: var(--text) !important; +} + +html.theme-light .mobile-demo-close:hover { + background: rgba(255, 255, 255, 0.75) !important; +} + +.mobile-demo-stage .hero-mockup-wrapper { + display: flex !important; + position: relative; + width: min(100%, 560px); + margin: 0 auto; + will-change: transform; +} + +/* Mobile S / short screens */ +@media (max-height: 600px) { + .mobile-demo-stage .hero-mockup-wrapper { + transform: scale(0.68) !important; + transform-origin: top center !important; + height: 380px !important; + } +} + +/* Mobile M/L / medium screens */ +@media (min-height: 601px) and (max-height: 740px) { + .mobile-demo-stage .hero-mockup-wrapper { + transform: scale(0.82) !important; + transform-origin: top center !important; + height: 460px !important; + } +} + +/* Default / tall screens */ +@media (min-height: 741px) { + .mobile-demo-stage .hero-mockup-wrapper { + transform: scale(1) !important; + transform-origin: top center !important; + height: 550px !important; + } +} + +.mobile-demo-stage .hero-demo-scene, +.mobile-demo-stage .hero-demo-scene.popup-open { + position: relative; + width: 100%; + height: 100% !important; + max-width: none; + margin: 0 auto; +} + +.mobile-demo-stage .demo-tab-stack { + display: block !important; + position: static !important; + height: auto !important; +} + +.mobile-demo-stage .demo-tab-card { + display: block !important; +} + +.mobile-demo-stage .demo-sync-chip { + display: inline-flex !important; +} + +.mobile-demo-stage .demo-cursor { + display: block !important; +} + +.mobile-demo-stage .demo-hint { + display: block !important; + position: absolute !important; + left: 0 !important; + right: 0 !important; + top: auto !important; + bottom: 12px !important; + margin: 0 auto !important; + padding-inline: 1rem; + max-width: 90%; + z-index: 10; +} + +.mobile-demo-stage .hero-demo-scene .extension-mockup { + position: absolute; + bottom: 0 !important; + right: 0; + z-index: 15 !important; + width: min(320px, 66cqw); + transform: none !important; + transform-origin: top right !important; + transition: transform 0.45s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.35s, visibility 0.45s; +} + +.mobile-demo-stage .hero-demo-scene:not(.popup-open) .extension-mockup { + bottom: 0 !important; + transform: translate(2%, -3%) scale(0.5) !important; + opacity: 0; + visibility: hidden; + pointer-events: none; +} + +@media (max-width: 380px) { + .mobile-demo-modal { + padding: 0 !important; + } + + .mobile-demo-stage { + padding-inline: 0.45rem !important; + } +} + +/* Performance optimization for off-screen sections (below the fold) */ +.lazy-section { + content-visibility: auto; +} + +.use-cases-section { + contain-intrinsic-block-size: auto 600px; +} + +.comparison-section { + contain-intrinsic-block-size: auto 800px; +} + +#self-hosting { + contain-intrinsic-block-size: auto 400px; +} + +#faq { + contain-intrinsic-block-size: auto 450px; +} + +@media (max-width: 768px) { + .use-cases-section { + contain-intrinsic-block-size: auto 900px; + } + + .comparison-section { + contain-intrinsic-block-size: auto 1100px; + } + + #self-hosting { + contain-intrinsic-block-size: auto 550px; + } + + #faq { + contain-intrinsic-block-size: auto 700px; + } +} + +/* Final responsive hierarchy overrides. Keep these last: the landing page has + several component-level media blocks above that intentionally own geometry. */ +@media (max-width: 768px) { + .container { + padding-inline: 1.15rem; + } + + section { + padding-block: 4.25rem; + } + + .hero-text h1, + .section-title, + #how-it-works h2, + #self-hosting h2, + #faq h2 { + font-size: clamp(2rem, 9vw, 2.65rem) !important; + text-wrap: balance; + } + + .cta-group { + display: grid; + grid-template-columns: 1fr 1fr; + align-items: stretch; + width: 100%; + } + + .cta-group .btn { + width: 100%; + min-height: 48px; + justify-content: center; + } +} + +@media (max-width: 430px) { + .cta-group { + grid-template-columns: 1fr; + } +} diff --git a/website/styles/alternatives.css b/website/styles/alternatives.css new file mode 100644 index 0000000..dd2e66c --- /dev/null +++ b/website/styles/alternatives.css @@ -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; + } +} + diff --git a/website/styles/demo.css b/website/styles/demo.css new file mode 100644 index 0000000..e33fb87 --- /dev/null +++ b/website/styles/demo.css @@ -0,0 +1,1652 @@ +/* --- Hero Live Demo Scene (two synced tabs + extension popup) --- */ +.hero-mockup-wrapper { + flex-direction: column; + gap: 0.9rem; + container-type: inline-size; +} + +/* Fixed height that already fits the open popup, so toggling it never changes + layout (the vertically centered hero never jumps). The empty lower area is + the invisible room the popup drops into. + + Do not move .hero-mockup-wrapper down to align the headline and demo tops: + the extension popup is bottom-anchored inside this scene, so moving the whole + wrapper destroys the vertical balance and leaves no breathing room below. */ +.hero-demo-scene { + --demo-content-y: 0px; + position: relative; + width: 100%; + height: max(calc(59cqw + 100px), 548px); +} + +.demo-tab-stack { + position: relative; + width: 100%; + height: calc(59cqw + 100px + var(--demo-content-y)); +} + +@media (min-width: 1025px) { + .hero-demo-scene { + /* Balance the demo inside the visible hero area below the fixed navbar. + Shift the scene content, not the hero text or grid. */ + --demo-content-y: 2.75rem; + } +} + +/* While initializing, suppress all transitions so the popup can start closed + without a visible open->closed animation on page load. */ +.hero-demo-scene.demo-no-anim, +.hero-demo-scene.demo-no-anim * { + transition-duration: 0s !important; +} + +/* Sync status chip: green "in sync" at rest, flashes the broadcast event */ +.demo-sync-chip { + position: absolute; + top: var(--demo-content-y); + left: 2px; + z-index: 8; + display: inline-flex; + align-items: center; + gap: 7px; + padding: 5px 12px; + border-radius: 99px; + background: oklch(0.68 0.13 150 / 0.12); + border: 1px solid oklch(0.68 0.13 150 / 0.35); + color: #86efac; + font-family: inherit; + font-size: 11px; + font-weight: 800; + letter-spacing: 0.06em; + text-transform: uppercase; + cursor: default; + transition: background 0.3s, border-color 0.3s, color 0.3s; +} + +.demo-chip-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: oklch(0.68 0.13 150); + box-shadow: 0 0 8px oklch(0.68 0.13 150); + transition: background 0.3s, box-shadow 0.3s; +} + +.demo-sync-chip .demo-chip-event { display: none; } + +.demo-sync-chip.event { + background: oklch(0.68 0.13 150 / 0.14); + border-color: oklch(0.68 0.13 150 / 0.45); + color: oklch(0.85 0.07 150); +} + +.demo-sync-chip.event .demo-chip-dot { + background: oklch(0.75 0.13 150); + box-shadow: 0 0 8px oklch(0.75 0.13 150); +} + +.demo-sync-chip.event .demo-chip-label-sync { display: none; } +.demo-sync-chip.event .demo-chip-event { display: inline; } + +@keyframes demoBlink { + 50% { opacity: 0.35; } +} + +/* Video tab cards: a clean window cascade — the right one is in front */ +.demo-tab-card { + position: absolute; + width: 310px; + width: 70cqw; + background: var(--demo-card); + border: 1px solid rgba(255, 255, 255, 0.09); + border-radius: 14px; + box-shadow: 0 18px 36px rgba(0, 0, 0, 0.45), inset 0 1px rgba(255, 255, 255, 0.05); + overflow: hidden; + cursor: pointer; + user-select: none; + transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1), border-color 0.25s; +} + +.demo-tab-card:hover, +.demo-tab-card:focus-visible { + border-color: oklch(0.68 0.13 150 / 0.55); +} + +/* Card A = the foreground window at the top right ("your" browser, + hosts the extension icon in its toolbar) */ +.demo-tab-a { + top: calc(49px + var(--demo-content-y)); + right: -3%; + transform: rotate(1.2deg); + transform-origin: top right; + z-index: 3; +} + +.demo-tab-a:hover { + z-index: 4; + transform: rotate(1.2deg) scale(1.02); + box-shadow: 0 25px 50px rgba(0, 0, 0, 0.65), 0 0 20px oklch(0.68 0.13 150 / 0.2); +} + +/* Card B = the friend's window, lower left, tucked behind */ +.demo-tab-b { + top: 142px; + top: calc(20cqw + 67px + var(--demo-content-y)); + left: -3%; + transform: rotate(-1.6deg); + transform-origin: top left; + z-index: 2; +} + +.demo-tab-b:hover { + z-index: 4; + transform: rotate(-1.6deg) scale(1.02); + box-shadow: 0 25px 50px rgba(0, 0, 0, 0.65), 0 0 20px oklch(0.68 0.13 150 / 0.2); +} + +/* When the popup slides in from the right edge, the tabs step aside */ +.hero-demo-scene.popup-open .demo-tab-a { + top: calc(22px + var(--demo-content-y)); + transform: rotate(1deg) translate(-44%, 20%) scale(0.88); +} + +.hero-demo-scene.popup-open .demo-tab-a:hover { + z-index: 4; + transform: rotate(1deg) translate(-44%, 20%) scale(0.92); + box-shadow: 0 25px 50px rgba(0, 0, 0, 0.65), 0 0 20px oklch(0.68 0.13 150 / 0.2); +} + +.hero-demo-scene.popup-open .demo-tab-b { + top: calc(20cqw + 40px + var(--demo-content-y)); + transform: rotate(-1.6deg) translate(0, 14%) scale(0.94); +} + +.hero-demo-scene.popup-open .demo-tab-b:hover { + z-index: 4; + transform: rotate(-1.6deg) translate(0, 14%) scale(0.98); + box-shadow: 0 25px 50px rgba(0, 0, 0, 0.65), 0 0 20px oklch(0.68 0.13 150 / 0.2); +} + +.demo-tab-titlebar { + display: flex; + align-items: center; + gap: 8px; + padding: 6px 8px 6px 10px; + background: var(--demo-toolbar); + border-bottom: 1px solid rgba(255, 255, 255, 0.06); + font-size: 10.5px; + color: var(--text-muted); + font-weight: 600; +} + +.demo-tab-dots { + display: flex; + gap: 4px; + flex-shrink: 0; +} + +.demo-tab-dots i { + display: block; + width: 7px; + height: 7px; + border-radius: 50%; + background: oklch(0.32 0.03 125); +} + +.demo-tab-title { + display: flex; + align-items: center; + gap: 5px; + flex: 1; + min-width: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.demo-tab-title svg { flex-shrink: 0; } + +.demo-tab-user { + flex-shrink: 0; + font-size: 9.5px; + font-weight: 700; + padding: 2px 7px; + border-radius: 99px; +} + +.demo-user-a { + color: oklch(0.80 0.10 150); + background: oklch(0.68 0.13 150 / 0.15); +} + +.demo-user-b { + color: #86efac; + background: oklch(0.68 0.13 150 / 0.12); +} + +/* Extension launcher pinned in the foreground window's toolbar */ +.demo-ext-launcher { + position: relative; + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + padding: 0; + border-radius: 7px; + background: oklch(0.30 0.03 130); + border: 1px solid rgba(255, 255, 255, 0.12); + cursor: pointer; + transition: border-color 0.25s, box-shadow 0.25s, transform 0.15s; +} + +.demo-ext-launcher img { + display: block; + width: 16px; + height: 16px; + border-radius: 4px; +} + +.demo-ext-launcher:hover { + border-color: var(--accent); + transform: translateY(-1px); +} + +.demo-ext-launcher:active { + transform: scale(0.94); +} + +.hero-demo-scene.popup-open .demo-ext-launcher { + border-color: oklch(0.68 0.13 150 / 0.7); + box-shadow: 0 0 10px oklch(0.68 0.13 150 / 0.45); +} + +.demo-ext-launcher::after { + content: ''; + position: absolute; + top: -3px; + right: -3px; + width: 8px; + height: 8px; + border-radius: 50%; + background: oklch(0.68 0.13 150); + box-shadow: 0 0 8px oklch(0.68 0.13 150); + opacity: 0; + transition: opacity 0.3s; +} + +.hero-demo-scene:not(.popup-open) .demo-ext-launcher:not(.demo-ext-static)::after { + opacity: 1; + animation: demoBlink 1.4s ease-in-out infinite; +} + +/* Card B shows the same extension icon, purely to signal the friend has it + installed too. It is not interactive and does not blink or glow. */ +.demo-ext-static, +.demo-ext-static:hover { + cursor: default; + border-color: rgba(255, 255, 255, 0.12); + transform: none; + box-shadow: none; +} + +.demo-ext-static::after { display: none; } + +/* Abstract "video" artwork with a slow cinematic pan/zoom while playing */ +.demo-video { + position: relative; + aspect-ratio: 16 / 9; + background: var(--demo-sky-bottom); + overflow: hidden; +} + +.demo-video::after { + content: ''; + position: absolute; + inset: 0; + z-index: 1; + pointer-events: none; + /* Cinematic vignette: soft edge falloff + heavier floor for the controls */ + box-shadow: + inset 0 0 0 1px rgba(255, 255, 255, 0.05), + inset 0 0 34px oklch(0.04 0.015 145 / 0.32), + inset 0 -26px 40px oklch(0.04 0.015 145 / 0.26); +} + +html.theme-light .hero-demo-scene .demo-video::after { + box-shadow: + inset 0 0 0 1px oklch(0.30 0.04 145 / 0.13), + inset 0 18px 32px oklch(0.98 0.03 90 / 0.1), + inset 0 -22px 34px oklch(0.18 0.05 145 / 0.16); +} + +.demo-video-art { + position: absolute; + inset: 0; + background: + radial-gradient(30% 42% at 76% 29%, var(--demo-glow), transparent 64%), + radial-gradient(90% 85% at 18% 5%, var(--demo-glow), transparent 66%), + linear-gradient(160deg, var(--demo-sky-top) 0%, var(--demo-sky-mid) 52%, var(--demo-sky-bottom) 100%); + transform-origin: 60% 40%; + animation: + demoKenBurns 9s ease-in-out infinite alternate, + demoArtShift 7s ease-in-out infinite alternate; + animation-play-state: paused; +} + +/* Soft light drifting through the scene */ +.demo-video-art::before { + content: ''; + position: absolute; + top: -25%; + left: -12%; + width: 70%; + aspect-ratio: 1; + border-radius: 50%; + background: radial-gradient(circle, var(--demo-glow), transparent 65%); + animation: demoGlowDrift 7s ease-in-out infinite alternate; + animation-play-state: paused; +} + +.demo-video-art::after { + content: ''; + position: absolute; + inset: 0; + background: linear-gradient(105deg, transparent 30%, oklch(0.75 0.13 150 / 0.14) 45%, oklch(0.75 0.10 150 / 0.1) 55%, transparent 70%); + transform: translateX(-100%); + animation: demoSheen 2.8s linear infinite; + animation-play-state: paused; +} + +/* Playing runs the animations; pausing freezes them mid-frame, so a synced + pause is visible at a glance */ +.demo-tab-card.playing .demo-video-art, +.demo-tab-card.playing .demo-video-art::before, +.demo-tab-card.playing .demo-video-art::after { + animation-play-state: running; +} + +@keyframes demoKenBurns { + from { transform: scale(1) translate(0, 0); } + to { transform: scale(1.18) translate(-3%, 2%); } +} + +@keyframes demoArtShift { + from { filter: hue-rotate(0deg) brightness(1); } + to { filter: hue-rotate(40deg) brightness(1.15); } +} + +@keyframes demoGlowDrift { + from { transform: translate(0, 0); } + to { transform: translate(65%, 30%); } +} + +@keyframes demoSheen { + to { transform: translateX(100%); } +} + +/* Play / pause overlay */ +.demo-play-overlay { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + background: oklch(0.10 0.01 135 / 0.35); + transition: opacity 0.25s, background 0.25s; +} + +.demo-icon-wrap { + display: flex; + align-items: center; + justify-content: center; + width: 46px; + height: 46px; + border-radius: 50%; + background: oklch(0.20 0.025 140 / 0.75); + border: 1px solid rgba(255, 255, 255, 0.25); + color: white; + box-shadow: 0 4px 18px rgba(0, 0, 0, 0.5); + backdrop-filter: blur(4px); + transition: transform 0.2s; +} + +.demo-tab-card:hover .demo-icon-wrap { transform: scale(1.1); } + +.demo-play-overlay .icon-pause { display: none; } +.demo-tab-card.playing .icon-play { display: none; } +.demo-tab-card.playing .icon-pause { display: block; } + +.demo-tab-card.playing .demo-play-overlay { + opacity: 0; + background: transparent; +} + +.demo-tab-card.playing:hover .demo-play-overlay, +.demo-tab-card.playing:focus-visible .demo-play-overlay { + opacity: 1; +} + +/* Timestamp + seekable progress bar */ +.demo-video-controls { + position: absolute; + left: 0; + right: 0; + bottom: 0; + display: flex; + align-items: center; + gap: 8px; + padding: 14px 10px 8px; + background: linear-gradient(transparent, var(--demo-control-fade)); + font-size: 9.5px; + color: oklch(0.88 0.015 90); + z-index: 1; +} + +.demo-time, +.demo-duration { + flex-shrink: 0; + font-family: ui-monospace, SFMono-Regular, Menlo, monospace; + font-weight: 700; + letter-spacing: 0.03em; + font-variant-numeric: tabular-nums; +} + +.demo-duration { + font-weight: 600; + opacity: 0.7; +} + +/* Decorative player chrome: play state glyph, volume, HD, fullscreen */ +.demo-ctrl-icon { + display: flex; + align-items: center; + flex-shrink: 0; + opacity: 0.85; +} + +.demo-ctrl-play .icon-pause { display: none; } +.demo-tab-card.playing .demo-ctrl-play .icon-play { display: none; } +.demo-tab-card.playing .demo-ctrl-play .icon-pause { display: block; } + +.demo-ctrl-badge { + flex-shrink: 0; + font-size: 7px; + font-weight: 800; + letter-spacing: 0.06em; + line-height: 1; + padding: 2px 3px; + border: 1px solid currentColor; + border-radius: 3px; + opacity: 0.65; +} + +.demo-progress { + position: relative; + flex: 1; + height: 5px; + border-radius: 99px; + background: var(--demo-track); + cursor: pointer; +} + +/* Generous invisible hit area for seeking */ +.demo-progress::before { + content: ''; + position: absolute; + inset: -8px 0; +} + +/* Buffered range behind the playhead, like a real player */ +.demo-progress-buffer { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 56%; + border-radius: 99px; + background: oklch(0.92 0.02 95 / 0.14); + pointer-events: none; +} + +.demo-progress-fill { + position: relative; + height: 100%; + width: 30%; + border-radius: 99px; + background: linear-gradient(90deg, oklch(0.68 0.13 150), oklch(0.62 0.14 45)); + pointer-events: none; +} + +/* Scrub thumb, revealed on hover */ +.demo-progress-fill::after { + content: ''; + position: absolute; + right: -5px; + top: 50%; + width: 11px; + height: 11px; + margin-top: -5.5px; + border-radius: 50%; + background: oklch(0.88 0.015 90); + box-shadow: 0 0 6px oklch(0.68 0.13 150 / 0.8); + opacity: 0; + transform: scale(0.6); + transition: opacity 0.2s, transform 0.2s; +} + +.demo-tab-card:hover .demo-progress-fill::after { + opacity: 1; + transform: scale(1); +} + +/* Sync pulse ring on both cards */ +.demo-tab-card.sync-pulse { + animation: demoCardPulse 0.9s ease-out; +} + +@keyframes demoCardPulse { + 0% { box-shadow: 0 18px 36px rgba(0, 0, 0, 0.45), 0 0 0 0 oklch(0.68 0.13 150 / 0.65); } + 100% { box-shadow: 0 18px 36px rgba(0, 0, 0, 0.45), 0 0 0 22px oklch(0.68 0.13 150 / 0); } +} + +/* The existing popup mockup slides in at the right edge, dropping out of + the toolbar icon of the foreground window */ +.hero-demo-scene .extension-mockup { + position: absolute; + bottom: 0; + right: 0; + z-index: 5; + width: 292px; + width: min(320px, 66cqw); + margin: 0; + transform-origin: top right; + transition: transform 0.45s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.35s, visibility 0.45s; +} + +@media (min-width: 1025px) { + .hero-demo-scene .extension-mockup { + /* Counter the content shift so the opened panel is balanced below nav. */ + bottom: 0.625rem; + } +} + +.hero-demo-scene:not(.popup-open) .extension-mockup { + transform: translate(2%, -3%) scale(0.5); + opacity: 0; + visibility: hidden; + pointer-events: none; +} + +/* Ghost cursor for the automated walkthrough */ +.demo-cursor { + position: absolute; + top: 52%; + left: 46%; + z-index: 20; + width: 22px; + height: 22px; + opacity: 0; + pointer-events: none; + filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.6)); + transition: top 0.65s cubic-bezier(0.45, 0.05, 0.25, 1), left 0.65s cubic-bezier(0.45, 0.05, 0.25, 1), opacity 0.35s; +} + +.demo-cursor.visible { opacity: 1; } + +.demo-cursor svg { + display: block; + width: 100%; + height: 100%; +} + +.demo-cursor::before { + content: ''; + position: absolute; + top: -6px; + left: -8px; + width: 32px; + height: 32px; + border-radius: 50%; + border: 2px solid oklch(0.75 0.13 150); + opacity: 0; + transform: scale(0.35); +} + +.demo-cursor.clicking::before { + animation: demoClickRipple 0.45s ease-out; +} + +@keyframes demoClickRipple { + 0% { opacity: 0.9; transform: scale(0.35); } + 100% { opacity: 0; transform: scale(1.25); } +} + +/* Caption below the scene */ +.demo-hint { + margin: 0.75rem auto 0; + max-width: min(100%, 34rem); + text-align: center; + font-size: 0.82rem; + line-height: 1.45; + color: var(--text-muted); + opacity: 0; + transition: opacity 0.6s; +} + +.demo-hint.show { opacity: 0.75; } + +/* Tablet/Small Desktop: fall back to the classic static popup mockup */ +@media (max-width: 1023px) { + .hero-demo-scene, + .hero-demo-scene.popup-open { + height: auto; + max-width: 320px; + margin: 0 auto; + } + + .demo-tab-card, + .demo-tab-stack, + .demo-sync-chip, + .demo-cursor, + .demo-hint { + display: none !important; + } + + .hero-demo-scene .extension-mockup, + .hero-demo-scene:not(.popup-open) .extension-mockup { + position: static; + width: 100%; + transform: none; + opacity: 1; + visibility: visible; + pointer-events: auto; + } +} + +@media (prefers-reduced-motion: reduce) { + .hero-demo-scene:not(.popup-open) .demo-ext-launcher::after { + animation: none; + } +} + +/* --- Story walkthrough additions: room creation, invite flight, film --- */ + +/* The stick-figure film carries the motion now — keep only the color drift + on the backdrop (the animation shorthand resets play-state, so re-pause) */ +.demo-video-art { + animation: demoArtShift 7s ease-in-out infinite alternate; + animation-play-state: paused; +} + +.demo-film { + position: absolute; + inset: 0; + width: 100%; + height: 100%; +} + +.hero-demo-scene .film-stars { fill: var(--demo-stars) !important; } +.hero-demo-scene .film-clouds { fill: var(--demo-stars) !important; } +.hero-demo-scene .film-mist-scroll { fill: var(--demo-stars) !important; } +.hero-demo-scene .film-shooting-star { stroke: var(--demo-stars) !important; } +.hero-demo-scene .film-moon { + fill: var(--demo-moon) !important; + filter: drop-shadow(0 0 6px var(--demo-moon)); +} +.hero-demo-scene .film-mtn-scroll { fill: var(--demo-mountain) !important; } +.hero-demo-scene .film-mid-scroll > * { fill: var(--demo-midground) !important; } +.hero-demo-scene .film-fore-scroll > * { fill: var(--demo-foreground) !important; } + +/* By day the birds read as silhouettes, not chalk marks */ +html.theme-light .hero-demo-scene .film-birds { stroke: oklch(0.32 0.06 145 / 0.65); } +html.theme-light :is(.hero-demo-scene, .step-illustration-3) .film-hero-bird { stroke: oklch(0.32 0.06 145 / 0.75); } + +/* Stars: staggered twinkle so the sky feels alive */ +.film-star { + transform-box: fill-box; + transform-origin: center; + animation: filmTwinkle 2.8s ease-in-out infinite; +} +.film-star:nth-child(2) { animation-duration: 3.4s; animation-delay: 0.5s; } +.film-star:nth-child(3) { animation-duration: 2.4s; animation-delay: 1.1s; } +.film-star:nth-child(4) { animation-duration: 3.6s; animation-delay: 0.3s; } +.film-star:nth-child(5) { animation-duration: 2.6s; animation-delay: 1.6s; } +.film-star:nth-child(6) { animation-duration: 3.2s; animation-delay: 0.8s; } +.film-star:nth-child(7) { animation-duration: 2.2s; animation-delay: 1.3s; } + +@keyframes filmTwinkle { + 0%, 100% { opacity: 0.25; transform: scale(0.85); } + 50% { opacity: 1; transform: scale(1.15); } +} + +/* Moon: gentle glow pulse + slow arc drift. The drift lives on the wrapper + group so the craters travel with the disc; the glow stays on the circle. + (Step-3 uses a bare .film-moon without wrapper — filmMoon still covers it.) */ +.film-moon-g { + transform-box: fill-box; + transform-origin: center; + animation: filmMoonDrift 6s ease-in-out infinite alternate; + animation-play-state: paused; +} + +.film-moon-g .film-moon { + animation-name: filmMoonGlow; +} + +@keyframes filmMoonDrift { + from { transform: translate(0, 0); } + to { transform: translate(-6px, 3px); } +} + +@keyframes filmMoonGlow { + from { filter: drop-shadow(0 0 4px oklch(0.95 0.01 100 / 0.5)); } + to { filter: drop-shadow(0 0 10px oklch(0.95 0.01 100 / 0.95)); } +} + +.film-moon { + transform-box: fill-box; + transform-origin: center; + animation: filmMoon 6s ease-in-out infinite alternate; + animation-play-state: paused; +} + +@keyframes filmMoon { + from { transform: translate(0, 0); filter: drop-shadow(0 0 4px oklch(0.95 0.01 100 / 0.5)); } + to { transform: translate(-6px, 3px); filter: drop-shadow(0 0 10px oklch(0.95 0.01 100 / 0.95)); } +} + +.film-mtn-scroll { + transform: translateX(var(--scroll-back, 0px)); +} +.film-mid-scroll { + transform: translateX(var(--scroll-mid, 0px)); +} +.film-fore-scroll { + transform: translateX(var(--scroll-fore, 0px)); +} +/* A single gliding bird carries the scene: the outer group rides the + JS-driven drift vars, the inner group flaps its wings via scaleY. */ +.film-hero-bird-drift { + transform: translate(var(--lantern-x, 0px), var(--bounce-y, 0px)); +} + +.film-hero-bird { + transform-box: fill-box; + transform-origin: center; + animation: filmWingFlap 0.85s ease-in-out infinite alternate; + animation-play-state: paused; +} + +@keyframes filmWingFlap { + from { transform: scaleY(1); } + to { transform: scaleY(0.5); } +} + +/* Twinkle & Glow play state bindings */ +.film-star { + transform-box: fill-box; + transform-origin: center; + animation: filmTwinkle 2.8s ease-in-out infinite; + animation-play-state: paused; +} +.demo-tab-card.playing :is(.film-star, .film-moon, .film-moon-g, .film-cloud, .film-shooting-star, .film-birds, .film-hero-bird) { + animation-play-state: running; +} + +/* Drifting clouds: gentle back-and-forth so there is no wrap pop */ +.film-cloud { + transform-box: fill-box; + animation: filmCloudDrift 26s ease-in-out infinite alternate; + animation-play-state: paused; +} + +.film-cloud-2 { + animation-duration: 34s; + animation-delay: -12s; +} + +@keyframes filmCloudDrift { + from { transform: translateX(-6px); } + to { transform: translateX(10px); } +} + +/* A shooting star streaks across the sky every few loops */ +.film-shooting-star { + opacity: 0; + animation: filmShoot 11s linear infinite; + animation-play-state: paused; +} + +@keyframes filmShoot { + 0%, 87% { opacity: 0; transform: translate(0, 0); } + 89% { opacity: 0.9; } + 95%, 100% { opacity: 0; transform: translate(-46px, 20px); } +} + +/* A distant flock crosses the valley, fading out mid-flight */ +.film-birds { + animation: filmBirds 16s linear infinite; + animation-play-state: paused; +} + +@keyframes filmBirds { + 0% { transform: translate(0, 0); opacity: 0; } + 4% { opacity: 0.75; } + 42% { opacity: 0.75; } + 48%, 100% { transform: translate(185px, -12px); opacity: 0; } +} + +/* Valley mist rides the slow background parallax between tree layers */ +.film-mist-scroll { + transform: translateX(var(--scroll-back, 0px)); +} + +/* Play button pulsing indicator at the end of the walkthrough */ +.hero-demo-scene.demo-complete #demo-tab-a:not(.playing) .demo-icon-wrap { + animation: playButtonPulse 1.6s ease-in-out infinite; + border-color: oklch(0.68 0.13 150); + box-shadow: 0 0 15px oklch(0.68 0.13 150 / 0.8); +} +@keyframes playButtonPulse { + 0%, 100% { transform: scale(1); box-shadow: 0 0 10px oklch(0.68 0.13 150 / 0.5); } + 50% { transform: scale(1.12); box-shadow: 0 0 20px oklch(0.68 0.13 150 / 0.9); } +} + +/* Seek-reset: scrubbing restarts animations from frame zero */ +.demo-film.demo-reset * { + animation: none !important; +} + +/* Seek-break: scrubbing the timeline visibly glitches both videos so the + jump reads as a real cut instead of an invisible time update. A scanline + sweep + brightness pop, and the film animations restart mid-stride. */ +.demo-video-art { transition: filter 0.12s ease-out; } + +.demo-tab-card.demo-seeking .demo-video-art { + animation-play-state: paused; + filter: brightness(1.9) saturate(1.4) blur(0.6px); +} + +.demo-tab-card .demo-seek-sweep { + position: absolute; + inset: 0; + z-index: 4; + pointer-events: none; + opacity: 0; + background: linear-gradient(90deg, + transparent 0%, + oklch(0.75 0.13 150 / 0.35) 20%, + rgba(255, 255, 255, 0.55) 50%, + oklch(0.75 0.13 150 / 0.35) 80%, + transparent 100%); + mix-blend-mode: screen; + transform: translateX(-100%); +} + +.demo-tab-card.demo-seeking .demo-seek-sweep { + animation: demoSeekSweep 0.34s ease-out; +} + +@keyframes demoSeekSweep { + 0% { opacity: 0; transform: translateX(-100%); } + 20% { opacity: 1; } + 100% { opacity: 0; transform: translateX(100%); } +} + +/* The sync chip only appears once the room is connected (story progression) */ +.demo-sync-chip { + opacity: 0; + transform: translateY(-4px); + pointer-events: none; + transition: background 0.3s, border-color 0.3s, color 0.3s, opacity 0.4s, transform 0.4s; +} + +.hero-demo-scene.connected .demo-sync-chip { + opacity: 1; + transform: none; + pointer-events: auto; +} + +/* Toast on the friend's window ("Room joined", "Tab selected") */ +.demo-tab-toast { + position: absolute; + top: 36px; + left: 50%; + transform: translate(-50%, -6px); + z-index: 2; + padding: 4px 10px; + border-radius: 99px; + background: oklch(0.20 0.025 140 / 0.92); + border: 1px solid oklch(0.68 0.13 150 / 0.5); + color: oklch(0.85 0.07 150); + font-size: 9.5px; + font-weight: 700; + white-space: nowrap; + opacity: 0; + pointer-events: none; + transition: opacity 0.3s, transform 0.3s; +} + +.demo-tab-toast.show { + opacity: 1; + transform: translate(-50%, 0); +} + +/* Invite link pill flying from the popup to the friend's window */ +.demo-invite-fly { + position: absolute; + z-index: 30; + display: inline-flex; + align-items: center; + gap: 5px; + padding: 4px 10px; + border-radius: 99px; + background: var(--accent); + color: var(--text-on-green); + font-size: 9.5px; + font-weight: 700; + white-space: nowrap; + box-shadow: 0 6px 18px oklch(0.68 0.13 150 / 0.5); + opacity: 0; + pointer-events: none; + transition: left 0.75s cubic-bezier(0.3, 0.7, 0.4, 1), top 0.75s cubic-bezier(0.3, 0.7, 0.4, 1), opacity 0.3s; +} + +/* Attention flash (e.g. on the video select while "choosing the tab") */ +.demo-attn { + animation: demoAttn 0.7s ease-in-out 2; +} + +@keyframes demoAttn { + 50% { box-shadow: 0 0 0 3px oklch(0.68 0.13 150 / 0.75); } +} + + +/* --- Bento Card Host Control Mockup Additions --- */ + +.bento-content-wrap { + display: flex; + flex-direction: column; + gap: 1.2rem; + width: 100%; +} + +.bento-host-control-mockup { + background: oklch(0.20 0.025 140 / 0.6); + border: 1px solid rgba(255, 255, 255, 0.05); + border-radius: 12px; + padding: 0.75rem 1rem; + display: flex; + flex-direction: column; + gap: 0.5rem; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2); + width: 100%; + box-sizing: border-box; +} + +.bento-mockup-header { + font-size: 0.65rem; + text-transform: uppercase; + letter-spacing: 0.5px; + color: var(--text-muted); + font-weight: 700; +} + +.bento-mockup-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + font-size: 0.75rem; + color: white; + font-weight: 600; + margin-top: 2px; +} + +/* Custom switch toggle representation */ +.bento-switch-wrap { + position: relative; + display: inline-block; + width: 28px; + height: 16px; + flex-shrink: 0; +} + +.bento-switch-wrap input { + opacity: 0; + width: 0; + height: 0; +} + +.bento-switch-slider { + position: absolute; + cursor: default; + top: 0; left: 0; right: 0; bottom: 0; + background-color: rgba(255, 255, 255, 0.1); + border: 1px solid rgba(255, 255, 255, 0.15); + border-radius: 16px; + transition: .3s; +} + +.bento-switch-slider:before { + position: absolute; + content: ""; + height: 10px; + width: 10px; + left: 2px; + bottom: 2px; + background-color: oklch(0.68 0.02 100); + border-radius: 50%; + transition: .3s; +} + +.bento-switch-wrap input:checked + .bento-switch-slider { + background-color: oklch(0.68 0.13 150 / 0.25); + border-color: oklch(0.68 0.13 150 / 0.6); + box-shadow: 0 0 8px oklch(0.68 0.13 150 / 0.3); +} + +.bento-switch-wrap input:checked + .bento-switch-slider:before { + transform: translateX(12px); + background-color: var(--accent); + box-shadow: 0 0 6px var(--accent-glow); +} + +/* --- Step 2 & Step 3 Visual Redesign Styles --- */ + +/* Step 2 Browser video player mock */ +.illus-video-player-mock { + width: 85%; + height: 65%; + background: linear-gradient(135deg, oklch(0.20 0.04 145), oklch(0.12 0.02 138)); + border: 1px solid rgba(255, 255, 255, 0.05); + border-radius: 8px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: relative; + margin-top: 15px; +} + +.video-mock-play-button { + width: 32px; + height: 32px; + border-radius: 50%; + background: rgba(255, 255, 255, 0.1); + border: 1px solid rgba(255, 255, 255, 0.2); + display: flex; + align-items: center; + justify-content: center; + color: white; + font-size: 0.8rem; + cursor: default; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); +} + +.video-mock-timeline { + position: absolute; + bottom: 8px; + left: 8px; + right: 8px; + height: 3px; + background: rgba(255, 255, 255, 0.15); + border-radius: 2px; +} + +.video-mock-timeline-fill { + width: 35%; + height: 100%; + background: var(--accent); + border-radius: 2px; +} + +/* Step 2 extension popup dropdown alignment */ +.illus-extension-popup.drop-down { + top: 2.2rem; + right: 0.6rem; + width: 145px; + animation: popup-dropdown-float 4s ease-in-out infinite; +} + +@keyframes popup-dropdown-float { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-3px); } +} + +.status-badge-select { + background: oklch(0.68 0.13 150 / 0.12); + color: oklch(0.85 0.07 150); + font-size: 0.58rem; + font-weight: 700; + padding: 2px 6px; + border-radius: 4px; + border: 1px solid oklch(0.68 0.13 150 / 0.2); + display: inline-flex; + align-items: center; + text-transform: uppercase; + letter-spacing: 0.2px; +} + +.popup-form-group { + margin-top: 6px; + width: 100%; + text-align: left; +} + +.popup-form-label { + display: block; + font-size: 0.6rem; + color: var(--text-muted); + margin-bottom: 4px; + font-weight: 700; +} + +.popup-select-wrapper { + position: relative; + width: 100%; +} + +.popup-select-mock { + width: 100%; + background: var(--card); + border: 1px solid rgba(255, 255, 255, 0.12); + color: white; + padding: 6px; + border-radius: 6px; + font-family: inherit; + font-size: 0.62rem; + outline: none; + appearance: none; + -webkit-appearance: none; + font-weight: 600; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + cursor: default; + display: block; + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.2); +} + +.popup-cursor-pointer { + position: absolute; + bottom: -15px; + right: 15px; + font-size: 1.1rem; + pointer-events: none; + z-index: 10; + animation: pointer-click 1.6s infinite ease-in-out; +} + +@keyframes pointer-click { + 0%, 100% { transform: translate(0, 0) scale(1); } + 50% { transform: translate(-3px, -3px) scale(0.9); } +} + +/* Step 3 forest movie continuous animation bindings */ +.step-illustration-3 .film-mtn-scroll { + animation: scrollBack 12s linear infinite !important; +} +.step-illustration-3 .film-mid-scroll { + animation: scrollMid 6s linear infinite !important; +} +.step-illustration-3 .film-fore-scroll { + animation: scrollFore 3s linear infinite !important; +} +.step-illustration-3 .film-hero-bird { + animation: filmWingFlap 0.85s ease-in-out infinite !important; + animation-direction: alternate !important; +} + +@keyframes scrollBack { + 0% { transform: translateX(0); } + 100% { transform: translateX(-160px); } +} +@keyframes scrollMid { + 0% { transform: translateX(0); } + 100% { transform: translateX(-160px); } +} +@keyframes scrollFore { + 0% { transform: translateX(0); } + 100% { transform: translateX(-160px); } +} + +/* --- High Fidelity Micro-Animations & Identity additions --- */ + +/* Scroll progress bar glowing line at the top */ +.scroll-progress-bar { + position: fixed; + top: 0; + left: 0; + height: 3px; + background: linear-gradient(90deg, var(--accent), oklch(0.62 0.14 45), oklch(0.62 0.14 45)); + z-index: 9999; + width: 0%; + transition: width 0.1s ease-out; + box-shadow: 0 0 8px var(--accent-glow); +} + +.scroll-progress-bar::after { + content: ''; + position: absolute; + right: -3px; + top: 50%; + width: 10px; + height: 7px; + border-radius: 0 70% 0 70%; + background: var(--accent-green); + box-shadow: 0 0 6px var(--accent-glow); + transform: translateY(-50%) rotate(24deg); +} + +/* Navigation logo pop & spin */ +.nav-logo-img { + animation: logo-entrance 1.2s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; + transform-origin: center; + transition: transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1); + will-change: transform, opacity; +} + +.nav-logo-img:hover { + transform: scale(1.15) rotate(360deg); +} + +@keyframes logo-entrance { + 0% { + transform: scale(0) rotate(-180deg); + opacity: 0; + } + 100% { + transform: scale(1) rotate(0deg); + opacity: 1; + } +} + +/* Glowing reflection sweep on buttons */ +.btn-primary, .btn-firefox, .btn-secondary { + position: relative; + overflow: hidden; +} + +.btn-primary::after, .btn-firefox::after, .btn-secondary::after { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient( + 90deg, + rgba(255, 255, 255, 0) 0%, + rgba(255, 255, 255, 0.25) 50%, + rgba(255, 255, 255, 0) 100% + ); + transform: skewX(-25deg); + transition: none; + pointer-events: none; +} + +.btn-primary:hover::after, .btn-firefox:hover::after, .btn-secondary:hover::after { + left: 100%; + transition: left 0.8s ease-in-out; +} + +/* Step 2 mockup control panel styles */ +.popup-mock-controls { + display: flex; + gap: 8px; + margin-top: 4px; + width: 100%; +} + +.popup-mock-btn { + flex: 1; + text-align: center; + padding: 6px; + border-radius: 6px; + font-size: 0.62rem; + font-weight: 700; + color: white; + cursor: default; + opacity: 0.75; +} + +.popup-mock-btn.play { + background: oklch(0.68 0.13 150); + color: var(--text-on-green); +} + +.popup-mock-btn.pause { + background: oklch(0.55 0.15 25); +} + +/* Phone and portrait-tablet widths: keep the full hero demo out of the page + flow. The demo modal mounts the same node in its own fixed layer. */ +@media (max-width: 900px) { + .hero-grid > .hero-mockup-wrapper { + display: none !important; + } +} + +@media (max-width: 900px) { + .hero-github-cta { + display: none; + } + + .btn-demo-mobile { + display: inline-flex; + } +} + +body.mobile-demo-open { + overflow: hidden; +} + +.mobile-demo-modal[hidden] { + display: none; +} + +.mobile-demo-modal { + position: fixed; + inset: 0; + z-index: 2400; + display: flex; + flex-direction: column; + padding: 0 !important; +} + +.mobile-demo-backdrop { + position: absolute; + inset: 0; + background: oklch(0.10 0.01 135 / 0.25); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); +} + +.mobile-demo-panel { + position: relative; + z-index: 1; + width: 100% !important; + height: 100% !important; + max-height: none !important; + display: flex; + flex-direction: column; + overflow: hidden; + background: transparent !important; + backdrop-filter: none !important; + -webkit-backdrop-filter: none !important; + border: none !important; + border-radius: 0 !important; + box-shadow: none !important; +} + +.mobile-demo-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; + padding: 0.85rem 0.95rem; + border-bottom: none !important; + flex-shrink: 0; +} + +.mobile-demo-header h2 { + margin: 0; + font-size: 0.95rem; + line-height: 1.2; + color: oklch(0.96 0.01 90); +} + +.mobile-demo-close { + display: inline-flex; + align-items: center; + justify-content: center; + width: 38px; + height: 38px; + padding: 0; + border: 1px solid rgba(255, 255, 255, 0.12); + border-radius: 10px; + background: oklch(0.27 0.035 130 / 0.72); + color: oklch(0.88 0.015 90); + cursor: pointer; + flex-shrink: 0; +} + +.mobile-demo-stage { + position: relative; + flex: 1; + min-height: 0; + overflow: hidden !important; + padding: 1rem 0.75rem 1.25rem; + overscroll-behavior: contain; + display: flex !important; + flex-direction: column !important; + justify-content: center !important; + align-items: center !important; +} + +/* Ensure the demo scene canvas is transparent inside the modal to let the glass backdrop blend */ +.mobile-demo-stage .hero-demo-scene { + background: transparent !important; + border: none !important; + box-shadow: none !important; +} + +/* Ensure the close button hover stays consistent in dark glass theme */ +.mobile-demo-close:hover { + background: rgba(51, 65, 85, 0.85) !important; +} + +/* Light Mode Frosted Glass overrides */ +html.theme-light .mobile-demo-backdrop { + background: oklch(0.95 0.01 100 / 0.2) !important; +} + +html.theme-light .mobile-demo-panel { + background: transparent !important; + border: none !important; + box-shadow: none !important; +} + +html.theme-light .mobile-demo-header { + border-bottom: none !important; +} + +html.theme-light .mobile-demo-header h2 { + color: var(--text) !important; +} + +html.theme-light .mobile-demo-close { + background: rgba(255, 255, 255, 0.4) !important; + border-color: oklch(0.20 0.025 140 / 0.1) !important; + color: var(--text) !important; +} + +html.theme-light .mobile-demo-close:hover { + background: rgba(255, 255, 255, 0.75) !important; +} + +.mobile-demo-stage .hero-mockup-wrapper { + display: flex !important; + position: relative; + width: min(100%, 560px); + margin: 0 auto; + will-change: transform; +} + +/* Mobile S / short screens */ +@media (max-height: 600px) { + .mobile-demo-stage .hero-mockup-wrapper { + transform: scale(0.68) !important; + transform-origin: top center !important; + height: 380px !important; + } +} + +/* Mobile M/L / medium screens */ +@media (min-height: 601px) and (max-height: 740px) { + .mobile-demo-stage .hero-mockup-wrapper { + transform: scale(0.82) !important; + transform-origin: top center !important; + height: 460px !important; + } +} + +/* Default / tall screens */ +@media (min-height: 741px) { + .mobile-demo-stage .hero-mockup-wrapper { + transform: scale(1) !important; + transform-origin: top center !important; + height: 550px !important; + } +} + +.mobile-demo-stage .hero-demo-scene, +.mobile-demo-stage .hero-demo-scene.popup-open { + position: relative; + width: 100%; + height: 100% !important; + max-width: none; + margin: 0 auto; +} + +.mobile-demo-stage .demo-tab-stack { + display: block !important; + position: static !important; + height: auto !important; +} + +.mobile-demo-stage .demo-tab-card { + display: block !important; +} + +.mobile-demo-stage .demo-sync-chip { + display: inline-flex !important; +} + +.mobile-demo-stage .demo-cursor { + display: block !important; +} + +.mobile-demo-stage .demo-hint { + display: block !important; + position: absolute !important; + left: 0 !important; + right: 0 !important; + top: auto !important; + bottom: 12px !important; + margin: 0 auto !important; + padding-inline: 1rem; + max-width: 90%; + z-index: 10; +} + +.mobile-demo-stage .hero-demo-scene .extension-mockup { + position: absolute; + bottom: 0 !important; + right: 0; + z-index: 15 !important; + width: min(320px, 66cqw); + transform: none !important; + transform-origin: top right !important; + transition: transform 0.45s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.35s, visibility 0.45s; +} + +.mobile-demo-stage .hero-demo-scene:not(.popup-open) .extension-mockup { + bottom: 0 !important; + transform: translate(2%, -3%) scale(0.5) !important; + opacity: 0; + visibility: hidden; + pointer-events: none; +} + +@media (max-width: 380px) { + .mobile-demo-modal { + padding: 0 !important; + } + + .mobile-demo-stage { + padding-inline: 0.45rem !important; + } +} + +/* Performance optimization for off-screen sections (below the fold) */ +.lazy-section { + content-visibility: auto; +} + +.use-cases-section { + contain-intrinsic-block-size: auto 600px; +} + +.comparison-section { + contain-intrinsic-block-size: auto 800px; +} + +#self-hosting { + contain-intrinsic-block-size: auto 400px; +} + +#faq { + contain-intrinsic-block-size: auto 450px; +} + +@media (max-width: 768px) { + .use-cases-section { + contain-intrinsic-block-size: auto 900px; + } + + .comparison-section { + contain-intrinsic-block-size: auto 1100px; + } + + #self-hosting { + contain-intrinsic-block-size: auto 550px; + } + + #faq { + contain-intrinsic-block-size: auto 700px; + } +} + +/* Final responsive hierarchy overrides. Keep these last: the landing page has + several component-level media blocks above that intentionally own geometry. */ +@media (max-width: 768px) { + .container { + padding-inline: 1.15rem; + } + + section { + padding-block: 4.25rem; + } + + .hero-text h1, + .section-title, + #how-it-works h2, + #self-hosting h2, + #faq h2 { + font-size: clamp(2rem, 9vw, 2.65rem) !important; + text-wrap: balance; + } + + .cta-group { + display: grid; + grid-template-columns: 1fr 1fr; + align-items: stretch; + width: 100%; + } + + .cta-group .btn { + width: 100%; + min-height: 48px; + justify-content: center; + } +} + +@media (max-width: 430px) { + .cta-group { + grid-template-columns: 1fr; + } +} diff --git a/website/styles/foundation.css b/website/styles/foundation.css new file mode 100644 index 0000000..a5d7ccb --- /dev/null +++ b/website/styles/foundation.css @@ -0,0 +1,1418 @@ +/* KoalaSync Design System - Privacy-Focused (No External Assets) */ + +@font-face { + font-family: 'Twemoji Country Flags'; + unicode-range: U+1F1E6-1F1FF, U+1F3F4, U+E0062-E0063, U+E0065, U+E0067, U+E006C, U+E006E, U+E0073-E0074, U+E0077, U+E007F; + src: url('assets/TwemojiCountryFlags.woff2') format('woff2'); + font-display: swap; +} + +:root { + /* v3.0.0 "The Greens Update" nature palette */ + --bg-base: oklch(0.20 0.025 140); + --surface: oklch(0.27 0.035 130); + --surface-deep: oklch(0.16 0.025 135); + --surface-alt: oklch(0.23 0.03 130); + --border-soft: rgba(255, 255, 255, 0.06); + --border-strong: oklch(0.32 0.03 125); + --text-primary: oklch(0.96 0.01 90); + --text-secondary: oklch(0.78 0.02 90); + --accent-green: oklch(0.68 0.13 150); + --accent-green-hover: oklch(0.75 0.13 150); + --accent-terracotta: oklch(0.62 0.14 45); + --danger: oklch(0.55 0.15 25); + --text-on-green: oklch(0.14 0.02 90); + + /* Legacy aliases used throughout the stylesheet */ + --bg: var(--bg-base); + --card: var(--surface); + --accent: var(--accent-green); + --accent-glow: oklch(0.68 0.13 150 / 0.3); + --text: var(--text-primary); + --text-muted: var(--text-secondary); + --success: var(--accent-green); + --glass: oklch(0.27 0.035 130 / 0.4); + --glass-border: var(--border-soft); + --section-tint: transparent; + --section-tint-strong: oklch(0.20 0.025 140 / 0.28); + --card-surface: oklch(0.27 0.035 130 / 0.45); + --card-border: rgba(255, 255, 255, 0.08); + color-scheme: dark; +} + +.extension-mockup, +.illus-popup-card, +.illus-player-card { + --bg: oklch(0.20 0.025 140) !important; + --card: oklch(0.27 0.035 130) !important; + --accent: oklch(0.68 0.13 150) !important; + --accent-glow: oklch(0.68 0.13 150 / 0.3) !important; + --text: oklch(0.96 0.01 90) !important; + --text-muted: oklch(0.78 0.02 90) !important; + --success: oklch(0.68 0.13 150) !important; + --error: oklch(0.55 0.15 25) !important; + --glass: oklch(0.27 0.035 130 / 0.4) !important; + --glass-border: rgba(255, 255, 255, 0.05) !important; + --section-tint: rgba(255, 255, 255, 0.02) !important; + --section-tint-strong: oklch(0.20 0.025 140 / 0.4) !important; + --card-surface: oklch(0.27 0.035 130 / 0.45) !important; + --card-border: rgba(255, 255, 255, 0.08) !important; +} + +.invite-banner { + background: var(--accent); + color: var(--text-on-green); + padding: 0.75rem 0; + text-align: center; + font-weight: 600; + font-size: 0.9rem; + position: relative; + z-index: 2000; + box-shadow: 0 4px 12px rgba(0,0,0,0.3); +} + +.btn-banner { + background: white; + color: var(--accent); + padding: 4px 12px; + border-radius: 6px; + text-decoration: none; + font-size: 0.8rem; + font-weight: 700; + margin-left: 12px; + cursor: pointer; + border: none; + transition: transform 0.2s; +} + +.btn-banner:hover { + transform: scale(1.05); +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; + border-radius: 6px; +} + +html { + scroll-behavior: smooth; +} + +body { + font-family: 'Twemoji Country Flags', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; + background-color: var(--bg); + color: var(--text); + line-height: 1.6; + overflow-x: hidden; + position: relative; +} + +/* --- Animated Bamboo Forest Background (v3 Greens Update) --- */ +/* Registered so wind gusts can TRANSITION these instead of jumping */ +@property --sway { + syntax: ''; + initial-value: 1.2deg; + inherits: true; +} + +@property --leaf-drift { + syntax: ''; + initial-value: 26px; + inherits: true; +} + +.bg-nature { + --sway: 1.2deg; + --leaf-drift: 26px; + transition: --sway 1.6s ease-in-out, --leaf-drift 1.6s ease-in-out; +} + +.bg-nature.gusting { + --sway: 3.4deg; + --leaf-drift: 68px; +} + +.bg-nature { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: -1; + overflow: hidden; + pointer-events: none; + background: + radial-gradient(circle at 18% 10%, oklch(0.26 0.05 145 / 0.35), transparent 34rem), + radial-gradient(circle at 78% 68%, oklch(0.24 0.04 140 / 0.3), transparent 38rem), + linear-gradient(180deg, oklch(0.20 0.025 140) 0%, oklch(0.17 0.03 142) 45%, oklch(0.19 0.03 140) 100%); +} + +/* (a) Distant bamboo layer: blurred, scroll-parallaxed by app.js */ +#bamboo-far { + position: absolute; + inset: 0; + will-change: transform; +} + +#bamboo-far .bamboo-stalk { + position: absolute; + /* 200% covers the upward parallax drift (max 0.55vh on the near layer) + with margin while keeping the blurred layer smaller than the old + 240% overscan. Spare coverage sits below, where the drift needs it. */ + top: -40%; + height: 200%; + border-radius: 999px; + filter: blur(4.5px); + transition: filter 1.2s ease; + background: + repeating-linear-gradient(180deg, + transparent 0, transparent 108px, + oklch(0.40 0.06 145 / 0.22) 108px, oklch(0.40 0.06 145 / 0.22) 112px), + linear-gradient(180deg, oklch(0.34 0.05 145 / 0.4), oklch(0.24 0.04 140 / 0.16)); +} + +#bamboo-far .far-1 { left: 2%; width: 26px; } +#bamboo-far .far-2 { left: 7.5%; width: 18px; } +#bamboo-far .far-3 { right: 4%; width: 30px; } +#bamboo-far .far-4 { left: 13%; width: 20px; } +#bamboo-far .far-5 { right: 10.5%; width: 16px; } +/* Two whisper-quiet trunks soften the empty centre without touching the + content: wide apart, thin, and at the lowest opacity in the scene. */ +#bamboo-far .far-6 { left: 39%; width: 12px; opacity: 0.55; } +#bamboo-far .far-7 { right: 42%; width: 14px; opacity: 0.6; } + +/* Two barely-there trunks break up the empty middle without competing with + the content. They share the far layer's single parallax transform. */ +#bamboo-far::before, +#bamboo-far::after { + content: ''; + position: absolute; + top: -40%; + height: 200%; + border-radius: 999px; + filter: blur(6.5px); + opacity: 0.3; + background: linear-gradient(180deg, oklch(0.34 0.05 145 / 0.3), oklch(0.23 0.04 140 / 0.1)); +} + +#bamboo-far::before { left: 24%; width: 13px; } +#bamboo-far::after { right: 25%; width: 17px; } + +/* Depth of field: the far layer blurs further as you descend (stepped by + JS on threshold crossings, never per frame) */ +.bg-nature.depth-mid #bamboo-far .bamboo-stalk { filter: blur(5.5px); } +.bg-nature.depth-deep #bamboo-far .bamboo-stalk { filter: blur(6.5px); } + +/* (a2) Mid bamboo layer: sits between far and near with its own parallax + rate (app.js) and half the near layer's sway, so the three layers drift + apart subtly instead of moving as one sheet. Landing page only — the + static pages keep the lighter two-layer scene. */ +#bamboo-mid { + position: absolute; + inset: 0; + will-change: transform; +} + +#bamboo-mid .bamboo-stalk { + position: absolute; + top: -40%; + height: 200%; + border-radius: 999px; + filter: blur(2px); + transition: filter 1.2s ease; + /* Segment rings + a soft left-of-centre sheen give the stalks culm + structure without any extra elements; the blur keeps both faint. */ + background: + repeating-linear-gradient(180deg, + transparent 0, transparent 84px, + oklch(0.46 0.075 145 / 0.28) 84px, oklch(0.46 0.075 145 / 0.28) 87px), + linear-gradient(90deg, transparent 14%, oklch(0.58 0.07 145 / 0.14) 38%, transparent 68%), + linear-gradient(180deg, oklch(0.36 0.06 145 / 0.45), oklch(0.25 0.045 140 / 0.2)); + transform-origin: bottom center; + animation: bambooSwayHalf 13s ease-in-out infinite; +} + +#bamboo-mid .mid-1 { left: 17%; width: 15px; animation-duration: 12s; } +#bamboo-mid .mid-2 { left: 31%; width: 9px; animation-delay: -5s; opacity: 0.55; } +#bamboo-mid .mid-3 { right: 15.5%; width: 17px; animation-duration: 14.5s; animation-delay: -8s; } +#bamboo-mid .mid-4 { right: 30%; width: 8px; animation-duration: 11.5s; animation-delay: -2.5s; opacity: 0.5; } + +/* A few leaves sprout from the mid stalks (pseudo-elements, so they ride + the stalk's sway for free). The slim centre stalks stay bare. */ +#bamboo-mid .bamboo-stalk::before, +#bamboo-mid .bamboo-stalk::after { + content: ''; + position: absolute; + width: 24px; + height: 11px; + border-radius: 0 100% 0 100%; + background: oklch(0.5 0.1 145 / 0.32); +} + +#bamboo-mid .mid-1::before { top: 31%; left: 13px; transform: rotate(20deg); } +#bamboo-mid .mid-1::after { top: 55%; left: -26px; transform: rotate(-158deg); width: 20px; height: 9px; } +#bamboo-mid .mid-3::before { top: 26%; left: -27px; transform: rotate(-156deg); } +#bamboo-mid .mid-3::after { top: 49%; left: 15px; transform: rotate(24deg); width: 21px; height: 10px; } +#bamboo-mid .mid-2::before, +#bamboo-mid .mid-2::after, +#bamboo-mid .mid-4::before, +#bamboo-mid .mid-4::after { + display: none; +} + +.bg-nature.depth-mid #bamboo-mid .bamboo-stalk { filter: blur(3px); } +.bg-nature.depth-deep #bamboo-mid .bamboo-stalk { filter: blur(4px); } + +@keyframes bambooSwayHalf { + 0%, 100% { transform: rotate(0); } + 50% { transform: rotate(calc(var(--sway, 1.2deg) * 0.5)); } +} + +/* (b) Near bamboo layer: sharp stalks with nodes and leaves, gentle sway */ +#bamboo-near { + position: absolute; + inset: 0; + will-change: transform; +} + +#bamboo-near .bamboo-stalk { + position: absolute; + top: -40%; + height: 200%; + border-radius: 999px; + /* Vertical sheen over the culm gradient: reads as light from the + canopy catching the rounded stalk instead of a flat strip. */ + background: + linear-gradient(90deg, transparent 12%, oklch(0.64 0.08 145 / 0.22) 36%, transparent 64%), + linear-gradient(180deg, oklch(0.42 0.075 145 / 0.6), oklch(0.27 0.05 140 / 0.3)); + transform-origin: bottom center; + animation: bambooSway 10s ease-in-out infinite; +} + +#bamboo-near .near-1 { left: 4.5%; width: 15px; animation-duration: 9.2s; } +#bamboo-near .near-2 { right: 7%; width: 13px; animation-duration: 11s; animation-delay: -3.5s; } +#bamboo-near .near-3 { left: 10%; width: 10px; animation-duration: 12.5s; animation-delay: -6s; opacity: 0.75; } + +#bamboo-near .bamboo-node { + position: absolute; + left: -1px; + right: -1px; + height: 3px; + border-radius: 2px; + background: oklch(0.52 0.085 145 / 0.6); +} + +#bamboo-near .node-1 { top: 28%; } +#bamboo-near .node-2 { top: 48%; } +#bamboo-near .node-3 { top: 68%; } + +#bamboo-near .bamboo-leaf { + position: absolute; + width: 26px; + height: 12px; + border-radius: 0 100% 0 100%; + background: oklch(0.55 0.11 145 / 0.4); +} + +#bamboo-near .near-1 .leaf-1 { top: 27.5%; left: 12px; transform: rotate(18deg); } +#bamboo-near .near-1 .leaf-2 { top: 47.5%; left: -28px; transform: rotate(-160deg); width: 22px; height: 10px; } +#bamboo-near .near-2 .leaf-1 { top: 44%; left: -26px; transform: rotate(-155deg); } +#bamboo-near .near-2 .leaf-2 { top: 61%; left: 11px; transform: rotate(21deg); width: 20px; height: 9px; } +#bamboo-near .near-3 .leaf-1 { top: 33%; left: 8px; transform: rotate(24deg); width: 18px; height: 9px; } + +@keyframes bambooSway { + 0%, 100% { transform: rotate(0); } + 50% { transform: rotate(var(--sway, 1.2deg)); } +} + +/* (d) Falling leaves: outer element falls + rotates, inner leaf sways */ +.fall-leaf { + position: absolute; + top: 0; + animation: leafFall 17s linear infinite; + opacity: 0; +} + +.fall-leaf i { + display: block; + width: 10px; + height: 14px; + border-radius: 0 70% 0 70%; + background: oklch(0.68 0.13 150 / 0.55); + animation: leafSway 3.2s ease-in-out infinite; +} + +.fall-leaf:nth-child(even) i { + background: oklch(0.62 0.14 45 / 0.55); +} + +.fall-leaf-1 { left: 6%; animation-duration: 15s; animation-delay: -2s; } +.fall-leaf-2 { left: 16%; animation-duration: 19s; animation-delay: -9s; } +.fall-leaf-2 i { animation-duration: 2.7s; background: oklch(0.62 0.14 45 / 0.6); } +.fall-leaf-3 { left: 28%; animation-duration: 14s; animation-delay: -5s; } +.fall-leaf-3 i { background: oklch(0.68 0.13 150 / 0.5); } +.fall-leaf-4 { left: 41%; animation-duration: 21s; animation-delay: -13s; } +.fall-leaf-4 i { animation-duration: 4s; } +.fall-leaf-5 { left: 55%; animation-duration: 16s; animation-delay: -7s; } +.fall-leaf-5 i { background: oklch(0.68 0.13 150 / 0.6); } +.fall-leaf-6 { left: 68%; animation-duration: 20s; animation-delay: -16s; } +.fall-leaf-6 i { animation-duration: 3.6s; background: oklch(0.62 0.14 45 / 0.5); } +.fall-leaf-7 { left: 81%; animation-duration: 14.5s; animation-delay: -3s; } +.fall-leaf-7 i { animation-duration: 2.9s; } +.fall-leaf-8 { left: 92%; animation-duration: 18s; animation-delay: -11s; } + +@keyframes leafFall { + 0% { transform: translateY(-40px) rotate(0); opacity: 0; } + 8% { opacity: 0.65; } + 92% { opacity: 0.5; } + 100% { transform: translateY(110vh) translateX(120px) rotate(280deg); opacity: 0; } +} + +@keyframes leafSway { + 0%, 100% { margin-left: 0; } + 50% { margin-left: var(--leaf-drift, 26px); } +} + +/* Scroll journey: JS drives the opacity of these two groups so the page + travels from sunlit canopy (top) into misty dusk (bottom). Static + defaults below keep the scene balanced without JS. */ +.bg-depth-day, +.bg-depth-dusk { + position: absolute; + inset: 0; +} + +.bg-depth-dusk { + opacity: 0.6; +} + +/* Dusk tint: darkens and warms the whole scene as you descend */ +.bg-dusk-tint { + position: absolute; + inset: 0; + opacity: 0; + background: linear-gradient(180deg, + oklch(0.10 0.03 145 / 0.15) 0%, + oklch(0.12 0.045 142 / 0.5) 65%, + oklch(0.16 0.07 55 / 0.35) 100%); +} + +/* God rays: soft light shafts falling through the canopy */ +.bg-godrays { + position: absolute; + top: -10%; + left: 0; + right: 0; + height: 75%; +} + +.godray { + position: absolute; + top: 0; + height: 115%; + background: linear-gradient(180deg, oklch(0.85 0.06 130 / 0.10), transparent 78%); + filter: blur(10px); + transform: rotate(14deg); + transform-origin: top center; + animation: rayShimmer 9s ease-in-out infinite alternate; +} + +.godray-1 { left: 16%; width: 120px; animation-duration: 8s; } +.godray-2 { left: 33%; width: 70px; animation-delay: -3s; background: linear-gradient(180deg, oklch(0.85 0.06 130 / 0.07), transparent 78%); } +.godray-3 { left: 56%; width: 140px; animation-duration: 13s; animation-delay: -6s; background: linear-gradient(180deg, oklch(0.85 0.05 110 / 0.08), transparent 75%); } + +@keyframes rayShimmer { + 0% { opacity: 0.45; } + 100% { opacity: 1; } +} + +/* (e) Canopy: dark vignette at the top plus hanging leaf arcs */ +.bg-canopy { + position: absolute; + top: 0; + left: 0; + right: 0; + height: 150px; + background: + radial-gradient(ellipse 70% 100% at 30% 0%, oklch(0.14 0.03 145 / 0.55), transparent 70%), + radial-gradient(ellipse 60% 90% at 80% 0%, oklch(0.14 0.03 145 / 0.45), transparent 70%); +} + +.canopy-arc { + position: absolute; + border-radius: 0 0 100% 100%; + background: oklch(0.32 0.06 145 / 0.45); +} + +.canopy-arc-1 { top: -3px; left: 12%; width: 52px; height: 16px; transform: rotate(-6deg); background: oklch(0.30 0.06 145 / 0.5); } +.canopy-arc-2 { top: -2px; left: 34%; width: 38px; height: 12px; transform: rotate(4deg); background: oklch(0.34 0.06 145 / 0.35); } +.canopy-arc-3 { top: -4px; left: 61%; width: 46px; height: 14px; transform: rotate(-3deg); } +.canopy-arc-4 { top: -2px; left: 84%; width: 30px; height: 10px; transform: rotate(7deg); background: oklch(0.33 0.06 145 / 0.4); } + +/* (f) Undergrowth: grass blades in the lower corners. Each blade carries a + shorter companion blade (::before) so the corners read as small tufts. */ +.bg-grass { + position: absolute; + bottom: -4px; + transform-origin: bottom; +} + +.bg-grass::before { + content: ''; + position: absolute; + bottom: 0; + width: 72%; + height: 55%; + border-radius: inherit; + background: inherit; + opacity: 0.7; + transform-origin: bottom; +} + +.bg-grass-1::before { left: -5px; transform: rotate(-13deg); } +.bg-grass-2::before { left: 4px; transform: rotate(11deg); } +.bg-grass-3::before { right: -5px; transform: rotate(12deg); } +.bg-grass-4::before { right: 4px; transform: rotate(-9deg); } + +.bg-grass-1 { left: 1.5%; width: 6px; height: 52px; border-radius: 100% 0 0 0; background: oklch(0.38 0.07 145 / 0.4); transform: rotate(-9deg); } +.bg-grass-2 { left: 3.2%; width: 5px; height: 38px; border-radius: 100% 0 0 0; background: oklch(0.35 0.07 145 / 0.3); transform: rotate(-12deg); } +.bg-grass-3 { right: 2%; width: 6px; height: 56px; border-radius: 0 100% 0 0; background: oklch(0.4 0.07 145 / 0.4); transform: rotate(8deg); } +.bg-grass-4 { right: 3.8%; width: 5px; height: 40px; border-radius: 0 100% 0 0; background: oklch(0.36 0.07 145 / 0.32); transform: rotate(11deg); } + +/* (g) Ground mist: soft drifting ellipses at the bottom edge */ +.bg-mist { + position: absolute; + bottom: -40px; + height: 130px; + filter: blur(19px); + will-change: transform; +} + +.bg-mist-1 { + left: -6%; + width: 75%; + background: radial-gradient(ellipse, oklch(0.85 0.03 145 / 0.07), transparent 70%); + animation: mistDrift 22s ease-in-out infinite alternate; +} + +.bg-mist-2 { + right: -6%; + width: 70%; + background: radial-gradient(ellipse, oklch(0.85 0.03 145 / 0.06), transparent 70%); + animation: mistDrift 27s ease-in-out infinite alternate-reverse; +} + +@keyframes mistDrift { + 0% { transform: translateX(-8%); } + 100% { transform: translateX(8%); } +} + +/* (h) Breathing horizon glow */ +.bg-horizon { + position: absolute; + inset: 0; + background: radial-gradient(ellipse 60% 100% at 50% 100%, oklch(0.62 0.14 45 / 0.14), transparent 70%); + animation: horizonBreathe 10s ease-in-out infinite; +} + +@keyframes horizonBreathe { + 0%, 100% { opacity: 0.5; } + 50% { opacity: 0.85; } +} + +/* Theme switch to light: the horizon flares up once like a sunrise */ +.bg-nature.sunrise .bg-horizon { + animation: + horizonBreathe 10s ease-in-out infinite, + sunriseFlash 1.6s ease-out; +} + +@keyframes sunriseFlash { + 0% { opacity: 0.5; } + 30% { opacity: 1; } + 100% { opacity: 0.5; } +} + +/* (i) Fireflies: the wrapper holds the base position and is what the JS + pushes away from the cursor; the inner dot pulses (transform) and + wanders (translate) independently. */ +.firefly-wrap { + position: absolute; + width: 4px; + height: 4px; +} + +.firefly { + display: block; + width: 4px; + height: 4px; + border-radius: 50%; + background: oklch(0.68 0.13 150); + box-shadow: 0 0 8px oklch(0.68 0.13 150); + animation: + fireflyPulse 3.8s ease-in-out infinite, + fireflyWander 21s ease-in-out infinite alternate; +} + +.fw-2 .firefly, +.fw-4 .firefly, +.fw-6 .firefly { + width: 3px; + height: 3px; + background: oklch(0.62 0.14 45); + box-shadow: 0 0 8px oklch(0.62 0.14 45); +} + +.fw-1 { top: 32%; left: 12%; } +.fw-2 { top: 58%; left: 22%; } +.fw-3 { top: 44%; left: 46%; } +.fw-4 { top: 70%; left: 64%; } +.fw-5 { top: 26%; left: 78%; } +.fw-6 { top: 62%; left: 90%; } + +.fw-1 .firefly { animation-delay: -0.4s, -4s; } +.fw-2 .firefly { animation-duration: 4.4s, 26s; animation-delay: -2.1s, -11s; } +.fw-3 .firefly { animation-duration: 3.4s, 18s; animation-delay: -1.2s, -7s; } +.fw-4 .firefly { animation-duration: 4.1s, 29s; animation-delay: -3s, -19s; } +.fw-5 .firefly { animation-duration: 3.6s, 23s; animation-delay: -1.8s, -14s; } +.fw-6 .firefly { animation-duration: 4.2s, 17s; animation-delay: -0.9s, -2s; } + +/* One firefly greets you when a new section scrolls into view */ +.firefly-wrap.firefly-hello { + animation: fireflyHello 1.2s ease-out; +} + +@keyframes fireflyHello { + 0%, 100% { transform: scale(1); filter: brightness(1); } + 35% { transform: scale(2.4); filter: brightness(1.7); } +} + +@keyframes fireflyPulse { + 0%, 100% { opacity: 0.2; transform: scale(1); } + 50% { opacity: 0.9; transform: scale(1.35); } +} + +@keyframes fireflyWander { + 0% { translate: 0 0; } + 25% { translate: 38px -26px; } + 50% { translate: -20px 16px; } + 75% { translate: 26px 30px; } + 100% { translate: -14px -18px; } +} + +/* Day/night: fireflies belong to the dark theme. By daylight the same six + spots are visited by small butterflies instead — the wrapper (and with it + the cursor-shy push and the section greeting) is shared, only the inner + dot is redressed. Only animation-name is swapped so the per-firefly + duration/delay variety above keeps applying to the butterflies. */ +html.theme-light .firefly { + position: relative; + width: 2px; + height: 9px; + border-radius: 1px; + background: oklch(0.35 0.05 140 / 0.8); + box-shadow: none; + opacity: 0.85; + animation-name: butterflyBob, fireflyWander; +} + +html.theme-light .firefly::before, +html.theme-light .firefly::after { + content: ''; + position: absolute; + top: -2px; + width: 7px; + height: 10px; + background: oklch(0.62 0.14 45 / 0.7); + animation: wingFlap 0.5s ease-in-out infinite alternate; +} + +html.theme-light .firefly::before { + left: -6px; + border-radius: 90% 10% 60% 40%; + transform-origin: 100% 55%; +} + +html.theme-light .firefly::after { + right: -6px; + border-radius: 10% 90% 40% 60%; + transform-origin: 0% 55%; + animation-name: wingFlapR; +} + +/* Every other butterfly wears the sage of the canopy and flaps lazier */ +html.theme-light .fw-2 .firefly::before, +html.theme-light .fw-2 .firefly::after, +html.theme-light .fw-4 .firefly::before, +html.theme-light .fw-4 .firefly::after, +html.theme-light .fw-6 .firefly::before, +html.theme-light .fw-6 .firefly::after { + background: oklch(0.52 0.12 150 / 0.55); + animation-duration: 0.62s; +} + +@keyframes butterflyBob { + 0%, 100% { transform: translateY(0) rotate(-4deg); } + 50% { transform: translateY(-8px) rotate(5deg); } +} + +@keyframes wingFlap { + from { transform: scaleX(1) rotate(-6deg); } + to { transform: scaleX(0.3) rotate(8deg); } +} + +@keyframes wingFlapR { + from { transform: scaleX(1) rotate(6deg); } + to { transform: scaleX(0.3) rotate(-8deg); } +} + +/* By day the dusk layer must not dim the butterflies with scroll depth + (the JS crossfade keeps writing inline opacity for the dark theme), and + the warm horizon glow steps back so it reads as haze, not dusk. */ +html.theme-light .bg-depth-dusk { + opacity: 1 !important; +} + +html.theme-light .bg-horizon { + background: radial-gradient(ellipse 60% 100% at 50% 100%, oklch(0.8 0.08 60 / 0.1), transparent 70%); +} + +/* (j) Wandering light sweep */ +.bg-light-sweep { + position: absolute; + top: -30%; + left: -30%; + width: 160%; + height: 160%; + background: linear-gradient(115deg, transparent 42%, oklch(0.68 0.13 150 / 0.05) 50%, transparent 58%); + animation: lightSweep 26s ease-in-out infinite alternate; + will-change: transform; +} + +@keyframes lightSweep { + 0% { transform: translate(-20%, -20%) rotate(8deg); } + 100% { transform: translate(20%, 20%) rotate(8deg); } +} + +/* (j2) Corner vignette: pulls the eye toward the content and grounds the + scene — the forest darkens gently toward the edges like real depth. */ +.bg-nature::after { + content: ''; + position: absolute; + inset: 0; + pointer-events: none; + background: radial-gradient(ellipse 120% 90% at 50% 38%, transparent 55%, oklch(0.10 0.025 142 / 0.4) 100%); +} + +html.theme-light .bg-nature::after { + background: radial-gradient(ellipse 120% 90% at 50% 38%, transparent 62%, oklch(0.78 0.03 120 / 0.3) 100%); +} + +/* (k) Film grain (SVG feTurbulence, no external assets) */ +.bg-grain { + position: absolute; + inset: 0; + opacity: 0.05; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); + background-size: 160px 160px; +} + +@media (max-width: 768px) { + /* Half the leaves, no god rays / light sweep / extra stalks: the scene + stays recognisable while the animation count roughly halves. */ + .fall-leaf-5, + .fall-leaf-6, + .fall-leaf-7, + .fall-leaf-8, + .bg-godrays, + .bg-light-sweep, + #bamboo-mid, + #bamboo-far .far-4, + #bamboo-far .far-5, + #bamboo-far .far-6, + #bamboo-far .far-7, + #bamboo-near .near-3 { + display: none; + } +} + +/* --- UI/UX cohesion pass ------------------------------------------------- + Shared rhythm and interaction rules for the landing page. Component-specific + responsive refinements are repeated at the end of the cascade. */ +html { + scroll-padding-top: 96px; +} + +body { + text-rendering: optimizeLegibility; +} + +.footer-disclaimer { + color: oklch(0.72 0.015 90); +} + +html.theme-light .footer-disclaimer { + color: var(--text-muted); +} + +.logo-area { + text-decoration: none; + border-radius: 12px; +} + +.logo-area:focus-visible, +.nav-links a:focus-visible, +.theme-toggle:focus-visible, +.lang-dropdown:focus-visible, +.hamburger:focus-visible, +.btn:focus-visible, +.faq-item summary:focus-visible { + outline: 3px solid var(--accent); + outline-offset: 4px; +} + +.nav-links a { + position: relative; + padding: 0.55rem 0; +} + +.nav-links a::after { + content: ""; + position: absolute; + right: 0; + bottom: 0.25rem; + left: 0; + height: 2px; + border-radius: 999px; + background: var(--accent); + transform: scaleX(0); + transform-origin: right; + transition: transform 0.25s ease; +} + +.nav-links a:hover::after, +.nav-links a:focus-visible::after { + transform: scaleX(1); + transform-origin: left; +} + +.hero-text { + max-width: 42rem; +} + +.hero-text h1 { + text-wrap: balance; +} + +.hero-subtitle, +.section-subtitle, +.use-cases-subtitle { + text-wrap: pretty; +} + +.cta-group { + align-items: stretch; +} + +.cta-group .btn { + min-height: 48px; + box-sizing: border-box; +} + +.cta-trust { + max-width: 44rem; + line-height: 1.55; +} + +section[id] { + scroll-margin-top: 96px; +} + +.section-title, +#how-it-works h2, +#self-hosting h2, +#faq h2 { + text-wrap: balance; + letter-spacing: -0.035em; +} + +.use-case-card, +.feature-card, +.faq-item, +.comparison-table-wrapper { + box-shadow: 0 18px 46px rgba(0, 0, 0, 0.12); +} + +html.theme-light :is(.use-case-card, .feature-card, .faq-item, .comparison-table-wrapper) { + box-shadow: 0 18px 46px oklch(0.22 0.035 140 / 0.09); +} + +.use-case-card, +.feature-card { + height: 100%; +} + +.use-case-card p, +.feature-card p, +.faq-item p { + line-height: 1.7; +} + +.faq-item summary { + min-height: 52px; + display: flex; + align-items: center; +} + +.comparison-table tbody tr { + transition: background-color 0.2s ease; +} + +@media (max-width: 768px) { + html { + scroll-padding-top: 76px; + } + + .container { + padding-inline: 1.15rem; + } + + section { + padding-block: 4.25rem; + } + + .hero-text { + max-width: none; + } + + .hero-text h1, + .section-title, + #how-it-works h2, + #self-hosting h2, + #faq h2 { + font-size: clamp(2rem, 9vw, 2.65rem) !important; + } + + .cta-group { + display: grid; + grid-template-columns: 1fr 1fr; + width: 100%; + } + + .cta-group .btn { + width: 100%; + justify-content: center; + } + + .cta-group .btn-primary, + .cta-group .btn-firefox { + grid-column: span 1; + } + + .hero-github-cta, + .btn-demo-mobile { + grid-column: span 1; + } + + .use-case-card, + .feature-card, + .faq-item, + .comparison-table-wrapper { + box-shadow: 0 12px 30px rgba(0, 0, 0, 0.1); + } +} + +@media (max-width: 430px) { + .cta-group { + grid-template-columns: 1fr; + } + + .cta-group .btn-primary, + .cta-group .btn-firefox, + .hero-github-cta, + .btn-demo-mobile { + grid-column: 1; + } +} + +@media (prefers-reduced-motion: reduce) { + .nav-links a::after, + .use-case-card, + .feature-card, + .faq-item, + .btn { + transition-duration: 0.01ms !important; + } +} + +/* (l) Click confetti: a small handful of eucalyptus leaves bursts from + wherever the visitor presses (spawned by app.js). Built like the falling + leaves above — the outer span owns the flight path (fast burst that + brakes at its peak, then sinks), the inner leaf rocks around its stem + the whole way down. Deliberately subtle: few, tiny, short-lived. */ +.click-leaf { + position: fixed; + z-index: 3000; + margin: -6px 0 0 -4px; + pointer-events: none; + opacity: 0; + animation: leafBurst var(--leaf-dur, 1.1s) forwards; + will-change: transform, opacity; +} + +.click-leaf i { + display: block; + width: 8px; + height: 12px; + border-radius: 0 70% 0 70%; + background: linear-gradient(135deg, oklch(0.62 0.12 150 / 0.95), oklch(0.45 0.09 145 / 0.85)); + transform: rotate(var(--leaf-rot, 0deg)); + animation: leafRock var(--flutter-dur, 0.6s) ease-in-out infinite alternate; +} + +.click-leaf-sage i { + background: linear-gradient(135deg, oklch(0.72 0.09 140 / 0.9), oklch(0.55 0.08 145 / 0.8)); +} + +.click-leaf-amber i { + background: linear-gradient(135deg, oklch(0.68 0.14 55 / 0.95), oklch(0.55 0.13 40 / 0.85)); +} + +/* Two-phase flight: --leaf-x/--leaf-y is the burst PEAK; from there each + leaf loses momentum and sinks past it while fading. Per-segment timing + functions give the fast pop and the gentle fall. */ +@keyframes leafBurst { + 0% { + opacity: 0; + transform: translate(0, 0) scale(var(--leaf-scale, 1)); + animation-timing-function: cubic-bezier(0.16, 0.84, 0.44, 1); + } + 10% { opacity: 1; } + 45% { + opacity: 1; + transform: translate(var(--leaf-x, 0), var(--leaf-y, -20px)) scale(var(--leaf-scale, 1)); + animation-timing-function: cubic-bezier(0.55, 0.06, 0.68, 0.19); + } + 100% { + opacity: 0; + transform: translate(calc(var(--leaf-x, 0) * 1.4), calc(var(--leaf-y, -20px) + 44px)) scale(calc(var(--leaf-scale, 1) * 0.92)); + } +} + +@keyframes leafRock { + from { transform: rotate(calc(var(--leaf-rot, 0deg) - 24deg)); } + to { transform: rotate(calc(var(--leaf-rot, 0deg) + 24deg)); } +} + +@media (prefers-reduced-motion: reduce) { + .click-leaf { display: none; } + .bg-nature *, + .bg-nature *::before, + .bg-nature *::after { + animation: none !important; + } + .fall-leaf { opacity: 0; } +} + +/* --- Layout --- */ +.container { + max-width: 1100px; + margin: 0 auto; + padding: 0 2rem; +} + +section { + padding: 6rem 0; +} + +#how-it-works { + padding: 4.5rem 0; +} + +/* Content-visibility for offscreen sections to improve INP */ +.use-cases-section, +#self-hosting, +.comparison-section { + content-visibility: auto; + contain-intrinsic-size: auto 1400px; +} + +/* --- Navigation --- */ +nav { + position: fixed; + top: 0; + width: 100%; + z-index: 1000; + background: var(--glass); + backdrop-filter: blur(12px); + border-bottom: 1px solid var(--glass-border); + padding: 1rem 0; + transition: padding 0.3s ease, background 0.3s ease; +} + +nav.nav-scrolled { + padding: 0.75rem 0; + background: oklch(0.20 0.025 140 / 0.9); +} + +html.theme-light nav.nav-scrolled { + background: oklch(0.95 0.01 100 / 0.92); +} + +.nav-content { + display: flex; + align-items: center; +} + +.logo-area { + display: flex; + align-items: center; + gap: 0.75rem; + font-weight: 800; + font-size: 1.5rem; + letter-spacing: -0.5px; +} + +/* Logo can be a plain ; without this it falls back to the UA link blue, + which clashed silently with the old indigo theme and visibly with green. */ +a.logo-area { + color: var(--text); +} + +.logo-area picture { + display: contents; +} + +.logo-area img { + height: 64px; + width: 64px; + object-fit: contain; + border-radius: 8px; + margin: -4px 0; +} + +.nav-ext-status { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 3px 8px; + background: oklch(0.68 0.13 150 / 0.15); + border: 1px solid oklch(0.68 0.13 150 / 0.3); + color: oklch(0.68 0.13 150); + border-radius: 12px; + font-size: 0.55rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.05em; + box-shadow: 0 0 10px oklch(0.68 0.13 150 / 0.1); + animation: fade-in 0.4s ease-out; + margin-left: 0.2rem; +} + +@keyframes fade-in { + from { opacity: 0; transform: translateY(-5px); } + to { opacity: 1; transform: translateY(0); } +} + +.nav-links { + display: flex; + gap: 2rem; + margin-left: auto; +} + +.nav-links a { + color: var(--text-muted); + text-decoration: none; + font-weight: 500; + transition: color 0.3s; +} + +.nav-links a:hover { + color: var(--accent); +} + +.nav-right { + display: flex; + align-items: center; + gap: 0.5rem; + margin-left: 2rem; + flex-shrink: 0; +} + +/* --- Theme Toggle (sun/moon) --- */ +.theme-toggle { + position: relative; + display: inline-flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + padding: 0; + background: transparent; + border: none; + border-radius: 10px; + color: var(--text-muted); + cursor: pointer; + flex-shrink: 0; + transition: color 0.3s, transform 0.3s; + overflow: hidden; +} + +.theme-toggle:hover { + color: var(--accent); + transform: scale(1.15); +} + +.theme-toggle:active { + transform: scale(0.9); +} + +.theme-toggle svg { + position: absolute; + width: 18px; + height: 18px; + transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.4s ease; +} + +/* Default (Dark mode): Sun visible, Moon hidden & rotated */ +.theme-toggle .theme-icon-sun { + opacity: 1; + transform: scale(1) rotate(0deg); +} + +.theme-toggle .theme-icon-moon { + opacity: 0; + transform: scale(0) rotate(-90deg); +} + +/* Light mode: Moon visible, Sun hidden & rotated */ +html.theme-light .theme-toggle .theme-icon-sun { + opacity: 0; + transform: scale(0) rotate(90deg); +} + +html.theme-light .theme-toggle .theme-icon-moon { + opacity: 1; + transform: scale(1) rotate(0deg); +} + +/* --- Light Theme --- */ +html.theme-light { + --accent-green: oklch(0.52 0.12 150); + --accent-green-hover: oklch(0.58 0.12 150); + --text-on-green: #ffffff; + --bg: oklch(0.95 0.01 100); + --card: #ffffff; + --accent: var(--accent-green); + --accent-glow: oklch(0.52 0.12 150 / 0.25); + --text: oklch(0.20 0.025 140); + --text-muted: oklch(0.45 0.02 110); + --glass: rgba(255, 255, 255, 0.7); + --glass-border: oklch(0.20 0.025 140 / 0.08); + --section-tint: transparent; + --section-tint-strong: oklch(0.88 0.015 90 / 0.5); + --card-surface: rgba(255, 255, 255, 0.75); + --card-border: oklch(0.20 0.025 140 / 0.08); + color-scheme: light; +} + +html.theme-light .bg-nature { + background: + radial-gradient(circle at 16% 10%, oklch(0.85 0.06 150 / 0.3), transparent 34rem), + radial-gradient(circle at 78% 68%, oklch(0.85 0.06 60 / 0.2), transparent 38rem), + linear-gradient(180deg, oklch(0.96 0.01 90) 0%, oklch(0.93 0.025 140) 46%, oklch(0.96 0.01 90) 100%); +} + +html.theme-light .bg-canopy, +html.theme-light .canopy-arc, +html.theme-light .bg-grass { + opacity: 0.45; +} + +/* By day the stalks get their own fresh-green recipes — the dark-theme + gradients faded to 45% read as grey smears on the light backdrop and + stopped looking like bamboo at all. */ +html.theme-light #bamboo-far .bamboo-stalk { + opacity: 0.55; + background: + repeating-linear-gradient(180deg, + transparent 0, transparent 108px, + oklch(0.55 0.09 145 / 0.4) 108px, oklch(0.55 0.09 145 / 0.4) 112px), + linear-gradient(180deg, oklch(0.68 0.11 145 / 0.6), oklch(0.62 0.09 140 / 0.35)); +} + +html.theme-light #bamboo-far::before, +html.theme-light #bamboo-far::after { + background: linear-gradient(180deg, oklch(0.7 0.1 145 / 0.5), oklch(0.64 0.08 140 / 0.25)); +} + +html.theme-light #bamboo-mid .bamboo-stalk { + opacity: 0.65; + background: + repeating-linear-gradient(180deg, + transparent 0, transparent 84px, + oklch(0.5 0.1 145 / 0.45) 84px, oklch(0.5 0.1 145 / 0.45) 87px), + linear-gradient(90deg, transparent 14%, oklch(0.85 0.08 130 / 0.35) 38%, transparent 68%), + linear-gradient(180deg, oklch(0.63 0.115 145 / 0.65), oklch(0.57 0.09 140 / 0.4)); +} + +html.theme-light #bamboo-mid .bamboo-stalk::before, +html.theme-light #bamboo-mid .bamboo-stalk::after { + background: oklch(0.55 0.13 145 / 0.5); +} + +html.theme-light #bamboo-near .bamboo-stalk { + opacity: 0.75; + background: + linear-gradient(90deg, transparent 12%, oklch(0.88 0.07 130 / 0.4) 36%, transparent 64%), + linear-gradient(180deg, oklch(0.59 0.12 145 / 0.75), oklch(0.52 0.1 140 / 0.5)); +} + +html.theme-light #bamboo-near .bamboo-node { + background: oklch(0.45 0.1 145 / 0.65); +} + +html.theme-light #bamboo-near .bamboo-leaf { + background: oklch(0.55 0.13 145 / 0.6); +} + +/* The centre trunks stay whisper-quiet by daylight too */ +html.theme-light #bamboo-far .far-6, +html.theme-light #bamboo-far .far-7 { + opacity: 0.26; +} + +html.theme-light .bg-mist { + display: none; +} + +html.theme-light .bg-dusk-tint { + display: none; +} + +html.theme-light .godray { + filter: blur(12px); + opacity: 0.5; +} + +html.theme-light .bg-grain { + opacity: 0.035; +} + +html.theme-light .feature-card, +html.theme-light .faq-item { + background: var(--card-surface); + border-color: var(--card-border); + box-shadow: 0 8px 24px oklch(0.20 0.025 140 / 0.06); +} + +html.theme-light .feature-card::before { + background: linear-gradient(to bottom right, oklch(0.20 0.025 140 / 0.08), transparent); +} + +html.theme-light .use-case-card { + background: var(--card-surface); +} + +html.theme-light .use-case-card h3 { + color: var(--text); +} + +html.theme-light .use-case-card:hover { + box-shadow: 0 12px 30px oklch(0.20 0.025 140 / 0.12); + border-color: oklch(0.20 0.025 140 / 0.15); +} + +html.theme-light .faq-item[open] { + background: #ffffff; +} + +html.theme-light .faq-item p { + border-top-color: oklch(0.20 0.025 140 / 0.08); +} + +html.theme-light .btn-secondary { + background: #ffffff; + border-color: oklch(0.20 0.025 140 / 0.12); +} + +html.theme-light .btn-secondary:hover { + background: oklch(0.88 0.015 90); +} + +html.theme-light .btn-firefox { + background: oklch(0.62 0.14 45); + color: #ffffff; + box-shadow: 0 10px 18px oklch(0.62 0.14 45 / 0.24); +} + +html.theme-light .btn-firefox:hover { + background: oklch(0.55 0.13 45); + box-shadow: 0 18px 26px oklch(0.62 0.14 45 / 0.28); +} + +html.theme-light .btn-firefox img { + filter: brightness(0) saturate(100%) invert(13%) sepia(38%) saturate(1084%) hue-rotate(177deg) brightness(89%) contrast(96%); +} + +html.theme-light .btn-firefox-secondary img { + filter: brightness(0) saturate(100%) invert(13%) sepia(38%) saturate(1084%) hue-rotate(177deg) brightness(89%) contrast(96%); +} + +html.theme-light .comparison-section { + background: transparent; +} + +html.theme-light .comparison-table-wrapper { + background: rgba(255, 255, 255, 0.75); + box-shadow: 0 10px 30px oklch(0.20 0.025 140 / 0.08); +} + +html.theme-light .comparison-table th { + background: oklch(0.20 0.025 140 / 0.06); + color: var(--text); +} + +html.theme-light .comparison-table th, +html.theme-light .comparison-table td { + border-bottom-color: oklch(0.20 0.025 140 / 0.08); +} + +html.theme-light .comparison-table tbody tr:hover { + background: oklch(0.20 0.025 140 / 0.03); +} + +html.theme-light .feat-name strong { + color: var(--text); +} + +html.theme-light .table-footnotes { + background: oklch(0.20 0.025 140 / 0.04); + border-top-color: oklch(0.20 0.025 140 / 0.06); +} + +@media (max-width: 900px) { + html.theme-light .comparison-table tbody tr { + background: rgba(255, 255, 255, 0.85); + box-shadow: 0 6px 18px oklch(0.20 0.025 140 / 0.08); + } + html.theme-light .comparison-table td.feat-name { + background: oklch(0.20 0.025 140 / 0.05); + } + html.theme-light .comparison-table td { + border-bottom-color: oklch(0.20 0.025 140 / 0.08); + } +} + +html.theme-light .compat-logo img, +html.theme-light .compat-logo { + filter: none; +} + diff --git a/website/styles/hero.css b/website/styles/hero.css new file mode 100644 index 0000000..df9651d --- /dev/null +++ b/website/styles/hero.css @@ -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; +} + diff --git a/website/styles/join-spinner.css b/website/styles/join-spinner.css new file mode 100644 index 0000000..bff4456 --- /dev/null +++ b/website/styles/join-spinner.css @@ -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; + } +} + diff --git a/website/styles/join.css b/website/styles/join.css new file mode 100644 index 0000000..e9b2ff8 --- /dev/null +++ b/website/styles/join.css @@ -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); +} + diff --git a/website/styles/landing-controls.css b/website/styles/landing-controls.css new file mode 100644 index 0000000..253104f --- /dev/null +++ b/website/styles/landing-controls.css @@ -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; +} + diff --git a/website/styles/landing-demo-gate.css b/website/styles/landing-demo-gate.css new file mode 100644 index 0000000..2e569d7 --- /dev/null +++ b/website/styles/landing-demo-gate.css @@ -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; + } +} diff --git a/website/styles/landing-faq.css b/website/styles/landing-faq.css new file mode 100644 index 0000000..30994f0 --- /dev/null +++ b/website/styles/landing-faq.css @@ -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 { + } +} + diff --git a/website/styles/landing-primary.css b/website/styles/landing-primary.css new file mode 100644 index 0000000..5f348fe --- /dev/null +++ b/website/styles/landing-primary.css @@ -0,0 +1,1215 @@ +/* --- Features --- */ +/* Bento Grid Layout Configuration */ +.features-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1.5rem; + margin-top: 4rem; +} + +.feature-card.feature-card-compact { + padding: 1.8rem; + border-radius: 16px; +} + +.feature-card.feature-card-compact::before { + border-radius: 16px; +} + +.feature-card.feature-card-compact h3 { + font-size: 1.1rem; + margin-bottom: 0.75rem; +} + +.feature-card.feature-card-compact p { + font-size: 0.9rem; +} + +.feature-card { + background: oklch(0.27 0.035 130 / 0.45); + backdrop-filter: blur(16px) saturate(120%); + -webkit-backdrop-filter: blur(16px) saturate(120%); + padding: 2.5rem; + border-radius: 24px; + border: 1px solid rgba(255, 255, 255, 0.08); + box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.3); + transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.3s cubic-bezier(0.4, 0, 0.2, 1), border-color 0.3s cubic-bezier(0.4, 0, 0.2, 1); + position: relative; + overflow: hidden; +} + +.feature-card::before { + content: ''; + position: absolute; + top: 0; left: 0; right: 0; bottom: 0; + border-radius: 24px; + padding: 1px; + background: linear-gradient(to bottom right, rgba(255, 255, 255, 0.12), transparent); + -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + pointer-events: none; +} + +.feature-card:hover { + transform: translateY(-5px); + box-shadow: 0 15px 30px oklch(0.68 0.13 150 / 0.2); + border-color: oklch(0.68 0.13 150 / 0.3); +} + +.feature-card:hover::before { + background: linear-gradient(to bottom right, oklch(0.68 0.13 150 / 0.3), transparent); +} + +/* Feature Icon with Inline SVG Wrapper */ +.feature-icon-svg { + display: flex; + flex-shrink: 0; + align-items: center; + justify-content: center; + width: 44px; + height: 44px; + background: oklch(0.68 0.13 150 / 0.12); + color: var(--accent); + border-radius: 12px; + margin-right: 14px; + transition: background 0.3s ease, color 0.3s ease, box-shadow 0.3s ease, transform 0.3s ease; +} + +.feature-card:hover .feature-icon-svg { + background: var(--accent); + color: var(--text-on-green); + box-shadow: 0 0 15px var(--accent-glow); + transform: scale(1.05); +} + +.bento-icon { + width: 22px; + height: 22px; + display: block; + flex-shrink: 0; +} + +.feature-card h3 { + margin-bottom: 1rem; + font-size: 1.25rem; + font-weight: 700; + display: flex; + align-items: center; +} + +.feature-card p { + color: var(--text-muted); + font-size: 0.95rem; + line-height: 1.6; +} + +/* Bento Asymmetric Spanning */ +.feature-card.bento-large { + grid-column: span 2; + background: linear-gradient(135deg, oklch(0.27 0.035 130 / 0.45) 0%, oklch(0.68 0.13 150 / 0.08) 100%); + padding: 3rem 3.5rem; + display: grid; + grid-template-columns: 1fr 2fr; + gap: 3rem; + align-items: center; +} + +.feature-card.bento-large h3 { + flex-direction: column; + align-items: center; + text-align: center; + gap: 1rem; + margin-bottom: 0; + font-size: 1.25rem; +} + +.feature-card.bento-large .feature-icon, +.feature-card.bento-large .feature-icon-svg { + width: 52px; + height: 52px; + border-radius: 14px; + margin-right: 0; + flex-shrink: 0; +} + +.feature-card.bento-large .bento-icon { + width: 24px; + height: 24px; +} + +.feature-card.bento-large p { + font-size: 1rem; + line-height: 1.7; + margin: 0; +} + +.feature-card.bento-large::after { + content: ''; + position: absolute; + top: 0; + left: 2.5rem; + right: 2.5rem; + height: 2px; + background: linear-gradient(90deg, transparent, oklch(0.68 0.13 150 / 0.4), transparent); + opacity: 0; + transition: opacity 0.3s ease; +} + +.feature-card.bento-large:hover::after { + opacity: 1; +} + +@media (max-width: 900px) { + .features-grid { + /* Keep the Why KoalaSync grid at either three columns or one column. + The two-column in-between makes compact cards stretch unevenly. */ + grid-template-columns: 1fr; + gap: 1.25rem; + } + .feature-card.bento-large { + grid-column: span 1; + padding: 2.5rem; + gap: 2rem; + } + .feature-card.bento-large h3 { + font-size: 1.15rem; + } +} + +@media (max-width: 600px) { + .features-grid { + grid-template-columns: 1fr; + gap: 1rem; + } + .feature-card.bento-large { + grid-column: span 1; + display: flex; + flex-direction: column; + padding: 1.75rem; + gap: 1rem; + } + .feature-card.bento-large h3 { + flex-direction: row; + align-items: center; + text-align: left; + gap: 0.75rem; + margin-bottom: 0; + font-size: 1.15rem; + } + .feature-card.bento-large .feature-icon, + .feature-card.bento-large .feature-icon-svg { + width: 40px; + height: 40px; + border-radius: 10px; + margin-right: 0; + } + .feature-card.bento-large .bento-icon { + width: 18px; + height: 18px; + } + .feature-card.bento-large p { + font-size: 0.9rem; + line-height: 1.6; + } + .feature-card.bento-large::after { + display: none; + } + .feature-card { + padding: 1.75rem; + } + .feature-card h3 { + font-size: 1.1rem; + } + .feature-icon-svg { + width: 40px; + height: 40px; + border-radius: 10px; + margin-right: 12px; + } + .bento-icon { + width: 18px; + height: 18px; + } +} + +/* --- How it works --- */ +.steps { + display: flex; + flex-direction: column; + gap: 4rem; +} + +.step { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 3rem; + align-items: center; +} + +.step:nth-child(even) .step-text { + order: 2; +} + +.step-num { + font-size: 3.25rem; + font-weight: 900; + background: linear-gradient(135deg, oklch(0.68 0.13 150 / 0.5), oklch(0.62 0.14 45 / 0.15)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + line-height: 1; + margin-bottom: 0.5rem; + font-family: inherit; + letter-spacing: 0; +} + +.step-text h3 { + font-size: 1.65rem; + margin-bottom: 0.75rem; + font-weight: 850; + color: var(--text-main); + letter-spacing: 0; +} + +.step-text p { + color: var(--text-muted); + font-size: 1rem; + line-height: 1.55; +} + +/* Custom CSS Mockups for step illustrations */ +.step-illustration-1, .step-illustration-2, .step-illustration-3 { + background: radial-gradient(circle at 10% 10%, oklch(0.20 0.04 145), oklch(0.10 0.015 138)); + height: 240px; + border-radius: 18px; + border: 1px solid rgba(255, 255, 255, 0.06); + position: relative; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 20px 45px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.05); + transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1), border-color 0.4s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.4s cubic-bezier(0.16, 1, 0.3, 1); +} + +@media (max-width: 900px) { + #how-it-works { + padding: 3.5rem 0; + } + + .steps { + gap: 2.75rem; + } + + .step { + grid-template-columns: minmax(0, 1fr); + gap: 1.1rem; + text-align: left; + } + + #how-it-works .step .step-text { + order: 1; + } + + #how-it-works .step-illustration-1, + #how-it-works .step-illustration-2, + #how-it-works .step-illustration-3 { + order: 2; + height: 220px; + } + + .step-num { + font-size: 2.5rem; + } + + .step-text h3 { + font-size: 1.45rem; + } +} + +.step-illustration-1:hover, .step-illustration-2:hover, .step-illustration-3:hover { + transform: translateY(-6px) scale(1.02); + border-color: oklch(0.68 0.13 150 / 0.25); + box-shadow: 0 25px 50px oklch(0.68 0.13 150 / 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.1); +} + +/* ========================================================================== + Step 1 Illustration: Browser & Installed Success Popup + ========================================================================== */ +.illus-browser-container { + width: 90%; + height: 80%; + background: oklch(0.11 0.015 138); + border: 1px solid rgba(255, 255, 255, 0.06); + border-radius: 12px; + overflow: hidden; + box-shadow: 0 15px 35px rgba(0, 0, 0, 0.6); + display: flex; + flex-direction: column; +} + +.illus-browser-header { + background: oklch(0.20 0.025 140 / 0.8); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + padding: 0.6rem 0.8rem; + display: flex; + align-items: center; + border-bottom: 1px solid rgba(255, 255, 255, 0.06); +} + +.illus-browser-dots { + display: flex; + gap: 6px; + margin-right: 1.5rem; +} + +.illus-browser-dot { + width: 8px; + height: 8px; + border-radius: 50%; + opacity: 0.6; +} +.illus-browser-dot:nth-child(1) { background: oklch(0.55 0.15 25); } +.illus-browser-dot:nth-child(2) { background: oklch(0.68 0.14 45); } +.illus-browser-dot:nth-child(3) { background: oklch(0.68 0.13 150); } + +.illus-browser-address-bar { + background: oklch(0.13 0.02 138); + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 6px; + color: var(--text-muted); + font-size: 0.68rem; + padding: 3px 12px; + flex-grow: 1; + max-width: 60%; + text-align: center; + font-family: monospace; + letter-spacing: 0.2px; +} + +.illus-browser-address-bar .url-protocol { + color: oklch(0.68 0.13 150); + font-weight: 600; +} + +.illus-browser-toolbar { + display: flex; + margin-left: auto; +} + +.illus-extension-btn { + width: 22px; + height: 22px; + border-radius: 6px; + display: flex; + align-items: center; + justify-content: center; + transition: opacity 0.3s, box-shadow 0.3s; +} + +.illus-extension-btn.active { + background: oklch(0.68 0.13 150 / 0.2); + border: 1px solid var(--accent); + box-shadow: 0 0 10px oklch(0.68 0.13 150 / 0.4); + animation: active-pulse 2s infinite alternate; +} + +@keyframes active-pulse { + 0% { transform: scale(1); box-shadow: 0 0 5px oklch(0.68 0.13 150 / 0.4); } + 100% { transform: scale(1.08); box-shadow: 0 0 12px oklch(0.68 0.13 150 / 0.7); } +} + +.illus-browser-content { + flex-grow: 1; + padding: 1rem; + position: relative; + display: flex; + align-items: flex-start; +} + +.illus-store-card { + background: rgba(255, 255, 255, 0.02); + border: 1px solid rgba(255, 255, 255, 0.04); + border-radius: 12px; + padding: 0.8rem; + width: 65%; + display: flex; + flex-direction: column; + gap: 10px; +} + +.illus-store-card-header { + display: flex; + align-items: center; + gap: 8px; +} + +.illus-large-logo { + width: 30px; + height: 30px; + object-fit: contain; + border-radius: 6px; + box-shadow: 0 0 10px oklch(0.68 0.13 150 / 0.2); +} + +.illus-store-info { + display: flex; + flex-direction: column; +} + +.illus-store-title { + font-size: 0.88rem; + font-weight: 800; + color: var(--text); + line-height: 1.1; +} + +.illus-store-desc { + font-size: 0.58rem; + color: var(--text-muted); + line-height: 1.2; + margin-top: 2px; +} + +.illus-store-buttons { + display: flex; + flex-direction: column; + gap: 6px; +} + +.illus-store-btn { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 5px 8px; + border-radius: 8px; + font-size: 0.58rem; + font-weight: 700; + color: white; + border: 1px solid rgba(255, 255, 255, 0.05); + background: rgba(255, 255, 255, 0.03); + box-shadow: 0 2px 5px rgba(0,0,0,0.2); + cursor: default; + transition: opacity 0.2s, transform 0.2s; +} + +.illus-store-btn img { + width: 11px; + height: 11px; + display: block; +} + +.illus-store-btn.chrome { + border-color: oklch(0.68 0.13 150 / 0.25); + background: oklch(0.68 0.13 150 / 0.08); + box-shadow: 0 0 10px oklch(0.68 0.13 150 / 0.05); +} + +.illus-store-btn.firefox { + border-color: oklch(0.62 0.14 45 / 0.25); + background: oklch(0.62 0.14 45 / 0.08); + box-shadow: 0 0 10px oklch(0.62 0.14 45 / 0.05); +} + +.install-breathe { + animation: install-breathe 2.5s ease-in-out infinite; + z-index: 2; + position: relative; +} + +@keyframes install-breathe { + 0%, 100% { + transform: scale(1); + box-shadow: 0 0 15px oklch(0.68 0.13 150 / 0.2); + } + 50% { + transform: scale(1.05); + box-shadow: 0 0 25px oklch(0.68 0.13 150 / 0.5); + } +} + +.install-breathe.firefox, +.btn-firefox.install-breathe { + animation-name: install-breathe-ff; +} + +@keyframes install-breathe-ff { + 0%, 100% { + transform: scale(1); + box-shadow: 0 0 15px oklch(0.62 0.14 45 / 0.2); + } + 50% { + transform: scale(1.05); + box-shadow: 0 0 25px oklch(0.62 0.14 45 / 0.5); + } +} + +/* Glassmorphic Dropping Success Card */ +.illus-extension-popup { + position: absolute; + top: 0.6rem; + right: 0.6rem; + width: 145px; + background: oklch(0.20 0.025 140 / 0.85); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border: 1px solid oklch(0.68 0.13 150 / 0.25); + border-radius: 12px; + padding: 0.6rem 0.8rem; + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.55), 0 0 15px oklch(0.68 0.13 150 / 0.15); + animation: popup-float 4s ease-in-out infinite; + z-index: 5; +} + +@keyframes popup-float { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-4px); } +} + +.illus-extension-popup .popup-title { + display: flex; + align-items: center; + gap: 5px; + font-size: 0.72rem; + font-weight: 700; + color: var(--text); + margin-bottom: 6px; + border-bottom: 1px solid rgba(255, 255, 255, 0.08); + padding-bottom: 4px; +} + +.illus-extension-popup .popup-title img { + width: 12px; + height: 12px; +} + +.illus-extension-popup .popup-status { + margin-bottom: 4px; +} + +.status-badge-success { + background: oklch(0.68 0.13 150 / 0.12); + color: oklch(0.68 0.13 150); + font-size: 0.6rem; + font-weight: 700; + padding: 2px 6px; + border-radius: 4px; + border: 1px solid oklch(0.68 0.13 150 / 0.2); + display: inline-flex; + align-items: center; + gap: 3px; + animation: status-glow 2s infinite alternate; +} + +@keyframes status-glow { + 0% { box-shadow: 0 0 0 oklch(0.68 0.13 150 / 0); } + 100% { box-shadow: 0 0 6px oklch(0.68 0.13 150 / 0.3); } +} + +.illus-extension-popup .popup-quick-start { + font-size: 0.55rem; + color: var(--text-muted); + font-weight: 500; + line-height: 1.2; +} + + +/* ========================================================================== + Step 2 Illustration: Highly Realistic Extension Popup (Create Room View) + ========================================================================== */ +.illus-popup-card { + width: 220px; + background: oklch(0.20 0.025 140 / 0.85); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + border: 1px solid rgba(255, 255, 255, 0.12); + border-radius: 16px; + padding: 0.8rem; + box-shadow: 0 18px 40px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.1); + display: flex; + flex-direction: column; + gap: 0.55rem; + animation: popup-float 4s ease-in-out infinite; + animation-delay: -1s; +} + +.illus-popup-header { + display: flex; + justify-content: space-between; + align-items: center; + border-bottom: 1px solid rgba(255, 255, 255, 0.06); + padding-bottom: 8px; +} + +.illus-popup-brand { + display: flex; + align-items: center; + gap: 6px; + font-size: 0.82rem; /* Scaled up font */ + font-weight: 800; + letter-spacing: -0.2px; +} + +.illus-popup-version { + font-size: 0.58rem; + color: var(--text-muted); +} + +.illus-popup-tabs { + display: flex; + gap: 4px; + background: oklch(0.12 0.015 135 / 0.4); + padding: 3px; + border-radius: 8px; + border: 1px solid rgba(255, 255, 255, 0.03); +} + +.illus-popup-tab { + flex: 1; + text-align: center; + font-size: 0.62rem; /* Scaled up font */ + padding: 4px 0; + border-radius: 6px; + color: var(--text-muted); + font-weight: 600; +} + +.illus-popup-tab.active { + background: var(--card); + color: var(--accent); + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); +} + +.illus-popup-body { + display: flex; + flex-direction: column; + gap: 0.55rem; + position: relative; +} + +.illus-popup-btn { + background: linear-gradient(135deg, var(--accent-green), var(--accent-terracotta)); + border-radius: 8px; + padding: 7px 9px; + color: var(--text-on-green); + font-weight: 700; + font-size: 0.68rem; + text-align: center; + box-shadow: 0 4px 12px oklch(0.68 0.13 150 / 0.3); + position: relative; + overflow: hidden; + cursor: default; + animation: create-btn-pulse 2s infinite alternate; +} + +@keyframes create-btn-pulse { + 0% { transform: scale(1); box-shadow: 0 4px 12px oklch(0.68 0.13 150 / 0.3); } + 100% { transform: scale(1.03); box-shadow: 0 6px 16px oklch(0.68 0.13 150 / 0.5); } +} + +.illus-btn-shine { + position: absolute; + top: 0; + left: -100%; + width: 50%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.25), transparent); + transform: skewX(-20deg); + animation: shimmer-sweep 3s infinite ease-in-out; +} + +@keyframes shimmer-sweep { + 0% { left: -100%; } + 50%, 100% { left: 150%; } +} + +.illus-collapsed-details { + background: oklch(0.12 0.015 135 / 0.4); + border: 1px solid rgba(255, 255, 255, 0.05); + border-radius: 8px; + padding: 6px 11px; + font-size: 0.54rem; + color: var(--text-muted); + font-weight: 700; + text-transform: uppercase; + display: flex; + align-items: center; + gap: 6px; + letter-spacing: 0.2px; +} + +.details-arrow { + font-size: 0.48rem; + color: var(--text-muted); + opacity: 0.7; +} + +/* Floating Success Badge */ +.illus-floating-success { + position: absolute; + /* Fully below the card so the toast never straddles its border */ + bottom: -2.4rem; + left: 50%; + transform: translateX(-50%); + background: oklch(0.12 0.015 135 / 0.92); + border: 1px solid oklch(0.68 0.13 150 / 0.35); + border-radius: 20px; + padding: 3px 10px; + display: flex; + align-items: center; + gap: 4px; + box-shadow: 0 6px 14px rgba(0, 0, 0, 0.45), 0 0 12px oklch(0.68 0.13 150 / 0.18); + animation: floating-success-fade 5s infinite; + white-space: nowrap; + z-index: 10; +} + +@keyframes floating-success-fade { + 0%, 100%, 75% { opacity: 0; transform: translate(-50%, 5px) scale(0.9); } + 15%, 60% { opacity: 1; transform: translate(-50%, 0) scale(1); } +} + +.illus-floating-success .success-icon { + font-size: 0.65rem; +} + +.illus-floating-success span[lang] { + font-size: 0.58rem; + color: oklch(0.68 0.13 150); + font-weight: 700; +} + + +/* ========================================================================== + Step 3 Illustration: Dual Screen Theater Sync Setup + ========================================================================= */ +.illus-sync-theater { + width: 95%; + display: flex; + align-items: center; + justify-content: center; + position: relative; + gap: 0.6rem; +} + +.illus-player-card { + width: 145px; + background: oklch(0.20 0.025 140 / 0.85); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 12px; + padding: 6px; + box-shadow: 0 12px 30px rgba(0,0,0,0.5); + display: flex; + flex-direction: column; + gap: 4px; + position: relative; + z-index: 2; +} + +.illus-player-card.player-a { + animation: popup-float 4s ease-in-out infinite; + animation-delay: -2s; +} + +.illus-player-card.player-b { + animation: popup-float 4s ease-in-out infinite; + animation-delay: -3s; +} + +.player-header { + display: flex; + justify-content: space-between; + align-items: center; + font-size: 0.55rem; +} + +.player-user { + font-weight: 700; + color: var(--text); +} + +.player-badge { + font-size: 0.42rem; + font-weight: 800; + padding: 1px 3px; + border-radius: 3px; +} + +.player-badge.active { + background: oklch(0.68 0.13 150 / 0.15); + color: oklch(0.68 0.13 150); + border: 1px solid oklch(0.68 0.13 150 / 0.25); +} + +.player-video-canvas { + height: 70px; + border-radius: 8px; + background: linear-gradient(135deg, oklch(0.20 0.04 145), oklch(0.22 0.05 45)); + position: relative; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; + border: 1px solid rgba(255, 255, 255, 0.02); +} + +.player-video-overlay { + position: absolute; + top: 0; left: 0; width: 100%; height: 100%; + background: linear-gradient(to bottom, transparent, oklch(0.68 0.13 150 / 0.15), transparent); + background-size: 100% 200%; + animation: playerScrollScanline 3s linear infinite; +} + +@keyframes playerScrollScanline { + 0% { background-position: 0% 0%; } + 100% { background-position: 0% 200%; } +} + +.player-play-pulse { + width: 24px; + height: 24px; + border-radius: 50%; + background: oklch(0.68 0.13 150 / 0.85); + color: var(--text-on-green); + font-size: 0.65rem; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 0 10px oklch(0.68 0.13 150 / 0.5); + z-index: 3; + animation: play-pulse 2s infinite alternate; +} + +@keyframes play-pulse { + 0% { transform: scale(1); box-shadow: 0 0 6px oklch(0.68 0.13 150 / 0.4); } + 100% { transform: scale(1.15); box-shadow: 0 0 15px oklch(0.68 0.13 150 / 0.8); } +} + +.player-controls { + display: flex; + align-items: center; + gap: 4px; + padding: 2px 2px 0 2px; +} + +.control-btn { + font-size: 0.58rem; + color: var(--accent); +} + +.player-timeline { + flex-grow: 1; + height: 3px; + background: rgba(255, 255, 255, 0.1); + border-radius: 2px; + position: relative; +} + +.timeline-fill { + position: absolute; + top: 0; left: 0; + height: 100%; + background: var(--accent); + border-radius: 2px; + width: 60%; + animation: theater-grow 6s infinite linear; +} + +.timeline-knob { + position: absolute; + top: -2px; + left: 60%; + width: 7px; + height: 7px; + border-radius: 50%; + background: white; + box-shadow: 0 0 4px rgba(0,0,0,0.5); + transform: translateX(-50%); + animation: theater-knob-grow 6s infinite linear; +} + +@keyframes theater-grow { + 0% { width: 0%; } + 100% { width: 100%; } +} + +@keyframes theater-knob-grow { + 0% { left: 0%; } + 100% { left: 100%; } +} + +.control-time { + font-size: 0.5rem; + color: var(--text-muted); + font-family: monospace; +} + +/* Glowing Connection Sync Bridge */ +.illus-sync-bridge { + flex-grow: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 6px; + position: relative; + z-index: 1; +} + +.illus-sync-bridge .sync-line { + width: 100%; + height: 2px; + background: rgba(255,255,255,0.06); + position: relative; + overflow: hidden; +} + +.illus-sync-bridge .sync-beam { + position: absolute; + top: 0; + left: -100%; + width: 50px; + height: 100%; + background: linear-gradient(90deg, transparent, var(--accent), var(--success), transparent); + animation: sync-flow 2.5s infinite linear; +} + +@keyframes sync-flow { + 0% { left: -100%; } + 100% { left: 100%; } +} + +.sync-status-badge { + background: oklch(0.68 0.13 150 / 0.12); + border: 1px solid oklch(0.68 0.13 150 / 0.25); + color: oklch(0.68 0.13 150); + font-size: 0.5rem; + font-weight: 800; + letter-spacing: 0.5px; + padding: 2px 6px; + border-radius: 6px; + white-space: nowrap; + animation: active-pulse-green 2s infinite alternate; +} + +@keyframes active-pulse-green { + 0% { box-shadow: 0 0 3px oklch(0.68 0.13 150 / 0.1); } + 100% { box-shadow: 0 0 8px oklch(0.68 0.13 150 / 0.4); } +} + +/* --- Self Hosters Terminal --- */ +.terminal-container { + max-width: 850px; + margin: 3rem auto 0 auto; + background: oklch(0.20 0.025 140); + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 16px; + overflow: hidden; + box-shadow: 0 20px 45px rgba(0,0,0,0.5); + text-align: left; +} + +.terminal-header { + background: oklch(0.27 0.035 130); + padding: 0.85rem 1.25rem; + display: flex; + justify-content: space-between; + align-items: center; + border-bottom: 1px solid rgba(255,255,255,0.06); +} + +.terminal-dots { + display: flex; + gap: 6px; +} + +.terminal-dot { + width: 10px; + height: 10px; + border-radius: 50%; +} + +.terminal-dot.red { background: oklch(0.55 0.15 25); } +.terminal-dot.yellow { background: oklch(0.68 0.14 45); } +.terminal-dot.green { background: oklch(0.68 0.13 150); } + +.terminal-tabs { + display: flex; + gap: 0.5rem; +} + +.terminal-tab-btn { + background: none; + border: none; + color: var(--text-muted); + font-weight: 600; + font-size: 0.8rem; + padding: 0.35rem 0.75rem; + border-radius: 6px; + cursor: pointer; + transition: color 0.2s, background-color 0.2s; +} + +.terminal-tab-btn:hover { + color: var(--text); + background: rgba(255, 255, 255, 0.03); +} + +.terminal-tab-btn.active { + color: var(--accent); + background: oklch(0.68 0.13 150 / 0.1); + border: 1px solid oklch(0.68 0.13 150 / 0.2); +} + +.terminal-body { + position: relative; + padding: 1.5rem; +} + +.terminal-pane { + display: none; + margin: 0; +} + +.terminal-pane.active { + display: block; +} + +.terminal-pane pre { + margin: 0; + overflow-x: auto; +} + +.terminal-pane code { + font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; + font-size: 0.85rem; + line-height: 1.6; + color: oklch(0.88 0.015 90); +} + +/* Syntax Highlighting */ +.t-comment { color: oklch(0.55 0.02 110); font-style: italic; } +.t-key { color: oklch(0.72 0.12 45); } +.t-val { color: oklch(0.75 0.10 150); } +.t-str { color: oklch(0.80 0.12 155); } +.t-num { color: oklch(0.75 0.12 50); } + +.terminal-copy-btn { + position: absolute; + top: 1rem; + right: 1.5rem; + background: rgba(255, 255, 255, 0.04); + border: 1px solid rgba(255, 255, 255, 0.08); + color: var(--text-muted); + padding: 0.35rem 0.75rem; + font-size: 0.75rem; + font-weight: 600; + border-radius: 6px; + cursor: pointer; + transition: background-color 0.2s, color 0.2s, opacity 0.2s; + display: flex; + align-items: center; + gap: 0.3rem; +} + +.terminal-copy-btn:hover { + background: rgba(255, 255, 255, 0.08); + color: var(--text); + border-color: rgba(255, 255, 255, 0.15); +} + +.terminal-copy-btn:active { + transform: scale(0.97); +} + +/* --- Footer --- */ +footer { + padding: 4rem 0; + border-top: 0; + text-align: center; + color: var(--text-muted); +} + +footer a { + color: var(--text-muted); + text-decoration: none; + transition: color 0.2s ease; +} + +footer a:hover { + color: var(--text); +} + + +/* --- Animations --- */ +[data-reveal] { + opacity: 0; + transform: translateY(30px); + transition: opacity 0.8s cubic-bezier(0.4, 0, 0.2, 1), transform 0.8s cubic-bezier(0.4, 0, 0.2, 1); +} + +[data-reveal].revealed { + opacity: 1; + transform: translateY(0); +} + +html.theme-light .hero-demo-scene .demo-user-a, +html.theme-light .hero-demo-scene .demo-user-b { + color: oklch(0.36 0.105 150); + background: oklch(0.60 0.12 150 / 0.13); +} + +html.theme-light .hero-demo-scene .extension-mockup { + --bg: oklch(0.935 0.018 110) !important; + --card: oklch(0.995 0.006 100) !important; + --accent: oklch(0.56 0.13 150) !important; + --accent-glow: oklch(0.56 0.13 150 / 0.22) !important; + --text: oklch(0.21 0.03 140) !important; + --text-muted: oklch(0.43 0.03 135) !important; + --success: oklch(0.56 0.13 150) !important; + --error: oklch(0.57 0.17 25) !important; + --glass-border: oklch(0.28 0.035 140 / 0.10) !important; + background: oklch(0.935 0.018 110) !important; + border-color: oklch(0.28 0.035 140 / 0.12); + box-shadow: + 0 20px 42px oklch(0.20 0.025 140 / 0.16), + 0 0 26px oklch(0.82 0.10 85 / 0.12); +} + +html.theme-light .hero-demo-scene .mock-tabs, +html.theme-light .hero-demo-scene .mock-card, +html.theme-light .hero-demo-scene .mock-input { + background: oklch(0.995 0.006 100); + border-color: oklch(0.28 0.035 140 / 0.14); + color: oklch(0.21 0.03 140); +} + +html.theme-light .hero-demo-scene .mock-peer-item { + background: oklch(0.28 0.035 140 / 0.035); + border-color: oklch(0.28 0.035 140 / 0.09); +} + +html.theme-light .hero-demo-scene .mock-joined-room, +html.theme-light .hero-demo-scene .mock-status-pill { + background: oklch(0.56 0.13 150 / 0.10); + border-color: oklch(0.56 0.13 150 / 0.24); +} + +html.theme-light .hero-demo-scene .mock-close-btn:hover { + color: oklch(0.21 0.03 140); + background: oklch(0.28 0.035 140 / 0.08); +} + +/* A direct hash destination is content, not an entrance animation. Keep it + readable from the first paint even before app.js has initialized. */ +section:target [data-reveal], +section:target[data-reveal] { + opacity: 1; + transform: translateY(0); +} + + diff --git a/website/styles/landing-sections.css b/website/styles/landing-sections.css new file mode 100644 index 0000000..47fb11c --- /dev/null +++ b/website/styles/landing-sections.css @@ -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; + } +} + diff --git a/website/styles/legal.css b/website/styles/legal.css new file mode 100644 index 0000000..9e36e8d --- /dev/null +++ b/website/styles/legal.css @@ -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; + } +} + diff --git a/website/template.html b/website/template.html index bc24506..9a3460f 100644 --- a/website/template.html +++ b/website/template.html @@ -10,9 +10,9 @@ - + + - @@ -209,7 +209,7 @@ - +