854 lines
21 KiB
Vue
854 lines
21 KiB
Vue
<script setup>
|
||
import { ref, onMounted, onUnmounted, computed } from 'vue'
|
||
import { useRouter, withBase, useData } from 'vitepress'
|
||
import GitHubStars from './GitHubStars.vue'
|
||
import VibeStories from './VibeStories.vue'
|
||
import { provide } from 'vue'
|
||
import { i18n } from './home/HomeI18n'
|
||
import { locales } from './home/HomeData'
|
||
import HomeStage1 from './home/HomeStage1.vue'
|
||
import HomeStage2 from './home/HomeStage2.vue'
|
||
import HomeStage3 from './home/HomeStage3.vue'
|
||
import HomeAppendix from './home/HomeAppendix.vue'
|
||
import HomeAppleFooter from './home/HomeAppleFooter.vue'
|
||
|
||
const router = useRouter()
|
||
const { site, page, lang } = useData()
|
||
const activeTab = ref('home')
|
||
const showLangMenu = ref(false)
|
||
const topPromoProgress = ref(1)
|
||
const topPromoDismissed = ref(false)
|
||
const topPromoIntroProgress = ref(0)
|
||
const topPromoColorProgress = ref(0)
|
||
let topPromoIntroRaf = 0
|
||
let topPromoColorRaf = 0
|
||
let topPromoColorTimer = 0
|
||
const WELCOME_SEEN_KEY = 'easy-vibe-welcome-seen'
|
||
|
||
const vibeStoriesSection = ref(null)
|
||
|
||
const t = computed(() => {
|
||
const code = lang.value ? lang.value.toLowerCase() : 'zh-cn'
|
||
const result = i18n[code] || i18n['en']
|
||
result._locale = code
|
||
return result
|
||
})
|
||
|
||
provide('t', t)
|
||
|
||
const isCjkLocale = computed(() => {
|
||
const code = lang.value ? lang.value.toLowerCase() : ''
|
||
if (['zh-cn', 'zh-tw', 'ja-jp', 'ko-kr'].includes(code)) {
|
||
return true
|
||
}
|
||
const path = router.route.path.toLowerCase()
|
||
return /^\/(zh-cn|zh-tw|ja-jp|ko-kr)\//.test(path)
|
||
})
|
||
|
||
const topPromo = computed(() => {
|
||
const code = lang.value ? lang.value.toLowerCase() : 'en'
|
||
const isChinese = code === 'zh-cn' || code === 'zh-tw'
|
||
return {
|
||
text: isChinese
|
||
? '用 Easy-Vibe 构建你的第一个 AI 应用,最快当天可上线原型。'
|
||
: 'Build your first AI app with Easy-Vibe and ship a working prototype fast.',
|
||
cta: isChinese ? '开始学习 ›' : 'Start learning ›',
|
||
link: `/${code}/stage-1/learning-map/`
|
||
}
|
||
})
|
||
|
||
const toggleLangMenu = () => {
|
||
showLangMenu.value = !showLangMenu.value
|
||
}
|
||
|
||
const updateHash = (id) => {
|
||
const targetHash = id === 'home' ? '#home' : `#${id}`
|
||
const currentUrl = `${window.location.pathname}${window.location.search}${window.location.hash}`
|
||
const nextUrl = `${window.location.pathname}${window.location.search}${targetHash}`
|
||
if (currentUrl !== nextUrl) {
|
||
window.history.replaceState(null, '', nextUrl)
|
||
}
|
||
}
|
||
|
||
const syncTopPromoWithHash = () => {
|
||
const rawHash = window.location.hash.replace(/^#/, '')
|
||
const targetId = rawHash || 'home'
|
||
if (targetId === 'home') {
|
||
topPromoDismissed.value = false
|
||
topPromoProgress.value = 1
|
||
return
|
||
}
|
||
topPromoDismissed.value = true
|
||
topPromoProgress.value = 0
|
||
}
|
||
|
||
const changeLang = (targetLocale) => {
|
||
const currentPath = router.route.path
|
||
const currentLocale = locales.find((l) =>
|
||
currentPath.startsWith(`/${l.code}/`)
|
||
)
|
||
|
||
let newPath
|
||
if (currentLocale) {
|
||
newPath = currentPath.replace(
|
||
`/${currentLocale.code}/`,
|
||
`/${targetLocale}/`
|
||
)
|
||
} else {
|
||
newPath = `/${targetLocale}/`
|
||
}
|
||
|
||
const hash = window.location.hash || ''
|
||
router.go(withBase(`${newPath}${hash}`))
|
||
showLangMenu.value = false
|
||
}
|
||
|
||
const scrollTo = (id) => {
|
||
if (id === 'home') {
|
||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||
activeTab.value = 'home'
|
||
updateHash('home')
|
||
syncTopPromoWithHash()
|
||
updateTopPromoVisibility()
|
||
return
|
||
}
|
||
const el = document.getElementById(id)
|
||
if (el) {
|
||
const navHeight = 48
|
||
const elementPosition = el.getBoundingClientRect().top + window.pageYOffset
|
||
const extraOffset = id === 'vibe-stories' ? 20 : 40
|
||
const offset = elementPosition - navHeight - extraOffset
|
||
window.scrollTo({ top: offset, behavior: 'smooth' })
|
||
activeTab.value = id
|
||
updateHash(id)
|
||
syncTopPromoWithHash()
|
||
}
|
||
}
|
||
|
||
const scrollToHashTarget = (behavior = 'auto') => {
|
||
const rawHash = window.location.hash.replace(/^#/, '')
|
||
const targetId = rawHash || 'home'
|
||
if (targetId === 'home') {
|
||
window.scrollTo({ top: 0, behavior })
|
||
activeTab.value = 'home'
|
||
syncTopPromoWithHash()
|
||
updateTopPromoVisibility()
|
||
return
|
||
}
|
||
const el = document.getElementById(targetId)
|
||
if (el) {
|
||
const navHeight = 48
|
||
const elementPosition = el.getBoundingClientRect().top + window.pageYOffset
|
||
const extraOffset = targetId === 'vibe-stories' ? 20 : 40
|
||
const offset = elementPosition - navHeight - extraOffset
|
||
window.scrollTo({ top: offset, behavior })
|
||
activeTab.value = targetId
|
||
syncTopPromoWithHash()
|
||
}
|
||
}
|
||
|
||
const closeLangMenu = (e) => {
|
||
if (!e.target.closest('.lang-switch-wrapper')) {
|
||
showLangMenu.value = false
|
||
}
|
||
}
|
||
|
||
const updateTopPromoVisibility = () => {
|
||
if (topPromoDismissed.value) {
|
||
topPromoProgress.value = 0
|
||
return
|
||
}
|
||
if (!vibeStoriesSection.value) {
|
||
topPromoProgress.value = 1
|
||
return
|
||
}
|
||
const navHeight = 44
|
||
const sectionTop =
|
||
vibeStoriesSection.value.getBoundingClientRect().top + window.pageYOffset
|
||
const endY = sectionTop - navHeight
|
||
const startY = endY - 96
|
||
const scrollY = window.pageYOffset
|
||
if (scrollY <= startY) {
|
||
topPromoProgress.value = 1
|
||
return
|
||
}
|
||
if (scrollY >= endY) {
|
||
topPromoProgress.value = 0
|
||
topPromoDismissed.value = true
|
||
return
|
||
}
|
||
topPromoProgress.value = (endY - scrollY) / (endY - startY)
|
||
}
|
||
|
||
const topPromoStyle = computed(() => {
|
||
const scrollProgress = topPromoProgress.value
|
||
const introProgress = topPromoIntroProgress.value
|
||
const colorProgress = topPromoColorProgress.value
|
||
const progress = scrollProgress * introProgress
|
||
const scrollOffset = -100 * (1 - scrollProgress)
|
||
const startTextColor = { r: 255, g: 255, b: 255 }
|
||
const endTextColor = { r: 29, g: 29, b: 31 }
|
||
const startBgColor = { r: 0, g: 113, b: 227 }
|
||
const endBgColor = { r: 245, g: 245, b: 247 }
|
||
const startLinkColor = { r: 255, g: 255, b: 255 }
|
||
const endLinkColor = { r: 0, g: 102, b: 204 }
|
||
const textColor = `rgb(${Math.round(startTextColor.r + (endTextColor.r - startTextColor.r) * colorProgress)}, ${Math.round(startTextColor.g + (endTextColor.g - startTextColor.g) * colorProgress)}, ${Math.round(startTextColor.b + (endTextColor.b - startTextColor.b) * colorProgress)})`
|
||
const bgColor = `rgb(${Math.round(startBgColor.r + (endBgColor.r - startBgColor.r) * colorProgress)}, ${Math.round(startBgColor.g + (endBgColor.g - startBgColor.g) * colorProgress)}, ${Math.round(startBgColor.b + (endBgColor.b - startBgColor.b) * colorProgress)})`
|
||
const linkColor = `rgb(${Math.round(startLinkColor.r + (endLinkColor.r - startLinkColor.r) * colorProgress)}, ${Math.round(startLinkColor.g + (endLinkColor.g - startLinkColor.g) * colorProgress)}, ${Math.round(startLinkColor.b + (endLinkColor.b - startLinkColor.b) * colorProgress)})`
|
||
return {
|
||
opacity: progress,
|
||
transform: `translateY(${scrollOffset}%)`,
|
||
maxHeight: `${30 * progress}px`,
|
||
backgroundColor: bgColor,
|
||
color: textColor,
|
||
'--top-promo-link-color': linkColor,
|
||
pointerEvents: progress < 0.02 ? 'none' : 'auto'
|
||
}
|
||
})
|
||
|
||
const resolveFooterHref = (link) => {
|
||
if (link.startsWith('http://') || link.startsWith('https://')) {
|
||
return link
|
||
}
|
||
return withBase(link)
|
||
}
|
||
|
||
onMounted(() => {
|
||
const introDuration = 1800
|
||
const colorDelay = 500
|
||
const colorDuration = 1800
|
||
const introStart = performance.now()
|
||
const stepTopPromoIntro = (now) => {
|
||
const raw = Math.min(1, (now - introStart) / introDuration)
|
||
const eased = 1 - Math.pow(1 - raw, 3)
|
||
topPromoIntroProgress.value = eased
|
||
if (raw < 1) {
|
||
topPromoIntroRaf = window.requestAnimationFrame(stepTopPromoIntro)
|
||
return
|
||
}
|
||
topPromoColorTimer = window.setTimeout(() => {
|
||
const colorStart = performance.now()
|
||
const stepTopPromoColor = (time) => {
|
||
const colorRaw = Math.min(1, (time - colorStart) / colorDuration)
|
||
const colorEased = 1 - Math.pow(1 - colorRaw, 3)
|
||
topPromoColorProgress.value = colorEased
|
||
if (colorRaw < 1) {
|
||
topPromoColorRaf = window.requestAnimationFrame(stepTopPromoColor)
|
||
}
|
||
}
|
||
topPromoColorRaf = window.requestAnimationFrame(stepTopPromoColor)
|
||
}, colorDelay)
|
||
}
|
||
topPromoIntroRaf = window.requestAnimationFrame(stepTopPromoIntro)
|
||
|
||
const currentPath = window.location.pathname
|
||
const basePath = site.value.base || '/'
|
||
const normalizedBase = basePath.endsWith('/') ? basePath : `${basePath}/`
|
||
const normalizedPath = currentPath.endsWith('/')
|
||
? currentPath
|
||
: `${currentPath}/`
|
||
const localeHomeSuffixes = [
|
||
'/zh-cn/',
|
||
'/en/',
|
||
'/zh-tw/',
|
||
'/ja-jp/',
|
||
'/ko-kr/',
|
||
'/es-es/',
|
||
'/fr-fr/',
|
||
'/de-de/',
|
||
'/ar-sa/',
|
||
'/vi-vn/'
|
||
]
|
||
const isLocaleHome = localeHomeSuffixes.some(
|
||
(suffix) =>
|
||
currentPath.endsWith(suffix) ||
|
||
currentPath.endsWith(`${suffix}index.html`)
|
||
)
|
||
const isRootHome =
|
||
normalizedPath === normalizedBase ||
|
||
currentPath === `${normalizedBase}index.html`
|
||
if (isRootHome && !isLocaleHome) {
|
||
const hasSeenWelcome = window.localStorage.getItem(WELCOME_SEEN_KEY) === '1'
|
||
if (!hasSeenWelcome) {
|
||
router.go(withBase(`/welcome/?next=${encodeURIComponent(currentPath)}`))
|
||
return
|
||
}
|
||
}
|
||
|
||
document.addEventListener('click', closeLangMenu)
|
||
syncTopPromoWithHash()
|
||
window.setTimeout(() => {
|
||
scrollToHashTarget('auto')
|
||
}, 0)
|
||
updateTopPromoVisibility()
|
||
window.addEventListener('scroll', updateTopPromoVisibility, { passive: true })
|
||
window.addEventListener('resize', updateTopPromoVisibility)
|
||
window.addEventListener('hashchange', scrollToHashTarget)
|
||
})
|
||
|
||
onUnmounted(() => {
|
||
if (topPromoIntroRaf) {
|
||
window.cancelAnimationFrame(topPromoIntroRaf)
|
||
topPromoIntroRaf = 0
|
||
}
|
||
if (topPromoColorRaf) {
|
||
window.cancelAnimationFrame(topPromoColorRaf)
|
||
topPromoColorRaf = 0
|
||
}
|
||
if (topPromoColorTimer) {
|
||
window.clearTimeout(topPromoColorTimer)
|
||
topPromoColorTimer = 0
|
||
}
|
||
document.removeEventListener('click', closeLangMenu)
|
||
window.removeEventListener('scroll', updateTopPromoVisibility)
|
||
window.removeEventListener('resize', updateTopPromoVisibility)
|
||
window.removeEventListener('hashchange', scrollToHashTarget)
|
||
})
|
||
</script>
|
||
|
||
<template>
|
||
<div class="apple-container">
|
||
<nav class="sticky-nav glass">
|
||
<div class="nav-content">
|
||
<div class="nav-cluster">
|
||
<div
|
||
class="nav-title"
|
||
:aria-label="t.nav.title"
|
||
>
|
||
<img
|
||
class="nav-title-logo no-viewer"
|
||
:src="withBase('/assets/easy-vibe-logo-hd.svg')"
|
||
:alt="t.nav.title"
|
||
width="64"
|
||
height="30"
|
||
draggable="false"
|
||
>
|
||
</div>
|
||
<div class="nav-links">
|
||
<button
|
||
:class="{ active: activeTab === 'home' }"
|
||
class="nav-link-item"
|
||
@click="scrollTo('home')"
|
||
>
|
||
{{ t.nav.home }}
|
||
</button>
|
||
<button
|
||
:class="{ active: activeTab === 'vibe-stories' }"
|
||
class="nav-link-item"
|
||
@click="scrollTo('vibe-stories')"
|
||
>
|
||
{{ t.nav.stories || 'Vibe 故事' }}
|
||
</button>
|
||
<button
|
||
:class="{ active: activeTab === 'pm' }"
|
||
class="nav-link-item"
|
||
@click="scrollTo('pm')"
|
||
>
|
||
{{ t.nav.pm }}
|
||
</button>
|
||
<button
|
||
:class="{ active: activeTab === 'junior' }"
|
||
class="nav-link-item"
|
||
@click="scrollTo('junior')"
|
||
>
|
||
{{ t.nav.junior }}
|
||
</button>
|
||
<button
|
||
:class="{ active: activeTab === 'senior' }"
|
||
class="nav-link-item"
|
||
@click="scrollTo('senior')"
|
||
>
|
||
{{ t.nav.senior }}
|
||
</button>
|
||
<button
|
||
:class="{ active: activeTab === 'appendix' }"
|
||
class="nav-link-item"
|
||
@click="scrollTo('appendix')"
|
||
>
|
||
{{ t.nav.appendix }}
|
||
</button>
|
||
</div>
|
||
<div class="nav-action">
|
||
<div class="nav-icons">
|
||
<div class="lang-switch-wrapper">
|
||
<button
|
||
type="button"
|
||
class="button"
|
||
aria-haspopup="true"
|
||
:aria-expanded="showLangMenu"
|
||
aria-label="Change language"
|
||
@click.stop="toggleLangMenu"
|
||
>
|
||
<span class="text">
|
||
<span class="vpi-languages option-icon" />
|
||
<span class="vpi-chevron-down text-icon" />
|
||
</span>
|
||
</button>
|
||
<div
|
||
v-if="showLangMenu"
|
||
class="lang-dropdown glass"
|
||
>
|
||
<button
|
||
v-for="locale in locales"
|
||
:key="locale.code"
|
||
class="lang-item"
|
||
@click="changeLang(locale.code)"
|
||
>
|
||
{{ locale.text }}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<GitHubStars class="nav-github-stars" />
|
||
</div>
|
||
<a
|
||
class="buy-btn"
|
||
:href="withBase(t.stage1.cards[0].link)"
|
||
>{{ t.footer.btn }}</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div
|
||
class="nav-promo"
|
||
:style="topPromoStyle"
|
||
>
|
||
<span>{{ topPromo.text }}</span>
|
||
<a :href="resolveFooterHref(topPromo.link)">{{ topPromo.cta }}</a>
|
||
</div>
|
||
</nav>
|
||
|
||
<div
|
||
id="home"
|
||
style="height: 0"
|
||
/>
|
||
|
||
<section
|
||
id="vibe-stories"
|
||
ref="vibeStoriesSection"
|
||
class="section-container"
|
||
>
|
||
<VibeStories />
|
||
</section>
|
||
|
||
<div class="section-band section-band-learning">
|
||
<HomeStage1 />
|
||
<HomeStage2 />
|
||
</div>
|
||
|
||
<HomeStage3 />
|
||
<HomeAppendix />
|
||
<HomeAppleFooter :is-cjk-locale="isCjkLocale" />
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.apple-container {
|
||
font-family:
|
||
-apple-system, BlinkMacSystemFont, 'SF Pro Text', 'PingFang SC',
|
||
'Helvetica Neue', sans-serif;
|
||
color: var(--vp-c-text-1);
|
||
background: transparent;
|
||
}
|
||
|
||
#vibe-stories,
|
||
#vibe-stories:focus,
|
||
#vibe-stories:focus-visible,
|
||
#vibe-stories:target {
|
||
outline: none !important;
|
||
box-shadow: none !important;
|
||
}
|
||
|
||
a {
|
||
text-decoration: none;
|
||
color: inherit;
|
||
}
|
||
|
||
:is(.buy-btn) {
|
||
border-bottom: none !important;
|
||
outline: none !important;
|
||
-webkit-tap-highlight-color: transparent;
|
||
}
|
||
|
||
:is(.buy-btn):is(:hover, :focus, :focus-visible, :active) {
|
||
border-bottom-color: transparent !important;
|
||
text-decoration: none !important;
|
||
outline: none !important;
|
||
}
|
||
|
||
.highlight {
|
||
color: var(--vp-c-text-2);
|
||
}
|
||
|
||
.sticky-nav {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 100;
|
||
border-bottom: 1px solid #d2d2d7;
|
||
transition: all 0.3s ease;
|
||
background: rgba(245, 245, 247, 0.82);
|
||
backdrop-filter: blur(20px);
|
||
-webkit-backdrop-filter: blur(20px);
|
||
}
|
||
|
||
:root.dark .sticky-nav {
|
||
background: rgba(18, 18, 20, 0.8);
|
||
border-bottom: 1px solid rgba(255, 255, 255, 0.12);
|
||
}
|
||
|
||
.nav-content {
|
||
max-width: 1280px;
|
||
margin: 0 auto;
|
||
padding: 0 28px;
|
||
height: 44px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
position: relative;
|
||
z-index: 2;
|
||
}
|
||
|
||
.nav-cluster {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 20px;
|
||
max-width: 100%;
|
||
}
|
||
|
||
.nav-title {
|
||
flex-shrink: 0;
|
||
background: none;
|
||
border: none;
|
||
padding: 0;
|
||
margin: 0;
|
||
cursor: default;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.nav-title-logo {
|
||
display: block;
|
||
max-width: 64px !important;
|
||
max-height: 30px !important;
|
||
height: 30px !important;
|
||
width: 64px !important;
|
||
min-width: 64px;
|
||
min-height: 30px;
|
||
object-fit: contain;
|
||
flex: 0 0 auto;
|
||
filter: grayscale(1) brightness(0.28) contrast(1.05);
|
||
}
|
||
|
||
.nav-links {
|
||
display: flex;
|
||
gap: 20px;
|
||
align-items: center;
|
||
margin: 0;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.nav-links button,
|
||
.nav-link-item {
|
||
background: none;
|
||
border: none;
|
||
font-size: 12px;
|
||
color: var(--vp-c-text-1) !important;
|
||
cursor: pointer;
|
||
transition: opacity 0.2s;
|
||
padding: 0;
|
||
margin: 0;
|
||
line-height: 1;
|
||
font-weight: 400;
|
||
opacity: 0.76;
|
||
text-decoration: none;
|
||
}
|
||
|
||
.nav-links button:hover,
|
||
.nav-links button.active,
|
||
.nav-link-item:hover {
|
||
color: var(--vp-c-text-1) !important;
|
||
opacity: 1;
|
||
}
|
||
|
||
.nav-action {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.nav-icons {
|
||
display: flex;
|
||
gap: 10px;
|
||
align-items: center;
|
||
}
|
||
|
||
:deep(.nav-github-stars) {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
:deep(.nav-github-stars .github-stars-link) {
|
||
color: var(--vp-c-text-1) !important;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
text-decoration: none;
|
||
}
|
||
|
||
:deep(.nav-github-stars .github-stars-link:hover) {
|
||
opacity: 0.7;
|
||
}
|
||
|
||
:deep(.nav-github-stars .github-stars-wrapper) {
|
||
padding-left: 0 !important;
|
||
}
|
||
|
||
.nav-promo {
|
||
height: 30px;
|
||
max-height: 30px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 6px;
|
||
font-size: 13px;
|
||
color: #1d1d1f;
|
||
padding: 0 16px;
|
||
overflow: hidden;
|
||
transform-origin: top center;
|
||
position: relative;
|
||
z-index: 1;
|
||
will-change: transform, opacity, max-height, background-color, color;
|
||
transition:
|
||
transform 0.16s ease-out,
|
||
opacity 0.16s ease-out,
|
||
max-height 0.16s ease-out,
|
||
background-color 0.22s ease-out,
|
||
color 0.22s ease-out;
|
||
}
|
||
|
||
.nav-promo a {
|
||
color: var(--top-promo-link-color, #0066cc);
|
||
text-decoration: none;
|
||
transition: color 0.25s ease-out;
|
||
}
|
||
|
||
.button {
|
||
background: none;
|
||
border: none;
|
||
padding: 0;
|
||
color: var(--vp-c-text-1) !important;
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
opacity: 1;
|
||
transition: opacity 0.2s;
|
||
}
|
||
|
||
.button:hover {
|
||
opacity: 0.7;
|
||
}
|
||
|
||
.button .text {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 2px;
|
||
}
|
||
|
||
.button .option-icon {
|
||
width: 20px;
|
||
height: 20px;
|
||
color: var(--vp-c-text-1) !important;
|
||
}
|
||
|
||
.button .text-icon {
|
||
width: 14px;
|
||
height: 14px;
|
||
color: var(--vp-c-text-1) !important;
|
||
}
|
||
|
||
.lang-switch-wrapper {
|
||
position: relative;
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.lang-dropdown {
|
||
position: absolute;
|
||
top: 100%;
|
||
right: -10px;
|
||
margin-top: 12px;
|
||
background: var(--vp-c-bg);
|
||
border: 1px solid var(--vp-c-divider);
|
||
border-radius: 16px;
|
||
padding: 6px;
|
||
min-width: 140px;
|
||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.14);
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 2px;
|
||
max-height: 300px;
|
||
overflow-y: auto;
|
||
z-index: 20;
|
||
}
|
||
|
||
.lang-item {
|
||
text-align: left;
|
||
padding: 8px 12px;
|
||
border-radius: 6px;
|
||
font-size: 13px;
|
||
color: var(--vp-c-text-1);
|
||
transition: background 0.2s;
|
||
background: transparent;
|
||
border: none;
|
||
cursor: pointer;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.lang-item:hover {
|
||
background: var(--vp-c-bg-soft);
|
||
}
|
||
|
||
.buy-btn {
|
||
background: #0071e3;
|
||
color: #fff !important;
|
||
padding: 7px 16px;
|
||
border-radius: 980px;
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
line-height: 1;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.buy-btn:hover {
|
||
background: #0077ed;
|
||
transform: scale(1.02);
|
||
}
|
||
|
||
.buy-btn.large {
|
||
padding: 12px 24px;
|
||
font-size: 15px;
|
||
margin-top: 20px;
|
||
display: inline-block;
|
||
}
|
||
|
||
.section-container {
|
||
max-width: 1280px;
|
||
margin: 0 auto 96px;
|
||
padding: 0 40px;
|
||
}
|
||
|
||
.section-band-learning {
|
||
width: 100vw;
|
||
max-width: none;
|
||
margin: 0 calc(50% - 50vw) 96px;
|
||
background: #f5f5f7;
|
||
border-radius: 0;
|
||
padding-top: 64px;
|
||
padding-bottom: 64px;
|
||
padding-left: max(40px, calc((100vw - 1280px) / 2 + 40px));
|
||
padding-right: max(40px, calc((100vw - 1280px) / 2 + 40px));
|
||
}
|
||
|
||
.section-band-learning .section-container {
|
||
max-width: 1280px;
|
||
margin: 0 auto;
|
||
padding: 0;
|
||
}
|
||
|
||
.section-band-learning .section-junior {
|
||
margin-top: 72px;
|
||
}
|
||
|
||
.dark .section-band-learning {
|
||
background: rgba(255, 255, 255, 0.03);
|
||
}
|
||
|
||
.section-header {
|
||
margin-bottom: 44px;
|
||
}
|
||
|
||
.section-category {
|
||
font-size: 24px;
|
||
font-weight: 700;
|
||
margin-bottom: 14px;
|
||
border: none;
|
||
padding: 0;
|
||
color: #1d1d1f;
|
||
letter-spacing: -0.024em;
|
||
font-family:
|
||
-apple-system, BlinkMacSystemFont, 'SF Pro Display', 'PingFang SC',
|
||
sans-serif;
|
||
}
|
||
|
||
.section-headline {
|
||
font-size: 64px;
|
||
line-height: 1.08;
|
||
font-weight: 700;
|
||
letter-spacing: -0.034em;
|
||
margin-bottom: 12px;
|
||
color: #1d1d1f;
|
||
font-family:
|
||
-apple-system, BlinkMacSystemFont, 'SF Pro Display', 'PingFang SC',
|
||
sans-serif;
|
||
}
|
||
|
||
.section-sub {
|
||
font-size: 21px;
|
||
line-height: 1.4;
|
||
font-weight: 400;
|
||
letter-spacing: -0.01em;
|
||
color: #6e6e73;
|
||
max-width: 760px;
|
||
font-family:
|
||
-apple-system, BlinkMacSystemFont, 'SF Pro Text', 'PingFang SC',
|
||
sans-serif;
|
||
}
|
||
|
||
.dark .section-category,
|
||
.dark .section-headline {
|
||
color: var(--vp-c-text-1);
|
||
}
|
||
|
||
.dark .section-sub {
|
||
color: var(--vp-c-text-2);
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.section-headline {
|
||
font-size: 42px;
|
||
}
|
||
|
||
.nav-links {
|
||
display: none;
|
||
}
|
||
|
||
.nav-promo {
|
||
font-size: 12px;
|
||
height: 28px;
|
||
justify-content: flex-start;
|
||
overflow-x: auto;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.section-band-learning {
|
||
margin-bottom: 96px;
|
||
padding-top: 42px;
|
||
padding-bottom: 42px;
|
||
padding-left: 24px;
|
||
padding-right: 24px;
|
||
}
|
||
|
||
.section-band-learning .section-junior {
|
||
margin-top: 56px;
|
||
}
|
||
}
|
||
</style>
|
||
|
||
<style>
|
||
.VPHome {
|
||
padding-top: 84px !important;
|
||
}
|
||
</style>
|