diff --git a/src/components/MediaCard.jsx b/src/components/MediaCard.jsx index 237c4f0..f08238e 100644 --- a/src/components/MediaCard.jsx +++ b/src/components/MediaCard.jsx @@ -1,8 +1,8 @@ // Author: fruit.com.cn -import { Play } from 'lucide-react'; +import { MoreVertical, Play } from 'lucide-react'; import { Link } from 'react-router-dom'; export function MediaCard({ video, compact = false }) { const image = video.image?.startsWith('/') || video.image?.startsWith('http') ? video.image : `/assets/${video.image}`; - return
{video.title}{video.duration}
{video.channel?.[0] || 'Y'}
{video.title}{video.channel}
{video.views} · 2 hari lalu
; + return
{video.title}{video.duration}{compact ? {video.title} :
{video.channel?.[0] || 'Y'}
{video.title}{video.channel}
{video.views} · 2 hari lalu
}
; } diff --git a/src/i18n/index.js b/src/i18n/index.js index 0d2ea38..d35f4d2 100644 --- a/src/i18n/index.js +++ b/src/i18n/index.js @@ -1,7 +1,7 @@ // Author: fruit.com.cn export const locales = { - id: { code: 'id', htmlLang: 'id', title: 'YMB · Cerita pendek, emosi besar', description: 'Temukan drama pendek terbaik dan episode baru setiap hari.', search: 'Cari drama, aktor, atau genre', home: 'Beranda', shorts: 'Shorts', subscriptions: 'Langganan', library: 'Perpustakaan', history: 'Riwayat', liked: 'Drama disukai', hero: 'Cerita pendek,', accent: 'emosi besar.', watch: 'Mulai menonton', picks: 'Drama pilihan minggu ini', shortTitle: 'Cerita dalam satu tarikan napas', viewAll: 'Lihat semua →', explore: 'Jelajahi →', videoCount: 'video', channels: 'Ikuti cerita favoritmu', genres: ['Semua', 'Romansa', 'Balas dendam', 'Keluarga', 'Rahasia', 'Kisah kedua'], follow: 'Ikuti', following: 'Mengikuti', empty: 'Belum ada drama yang cocok.', loading: 'Memuat dari server…', apiFallback: 'Server belum tersedia, menampilkan katalog demo sementara.', playbackUnavailable: 'Pilih episode untuk mulai menonton.', swipeHint: 'Geser ke atas untuk menemukan cerita berikutnya' }, - en: { code: 'en', htmlLang: 'en', title: 'YMB · Short stories, big emotions', description: 'Discover the best short dramas and new episodes every day.', search: 'Search dramas, actors, or genres', home: 'Home', shorts: 'Shorts', subscriptions: 'Subscriptions', library: 'Library', history: 'History', liked: 'Liked dramas', hero: 'Short stories,', accent: 'big emotions.', watch: 'Watch now', picks: 'Drama picks this week', shortTitle: 'Stories in one breath', viewAll: 'View all →', explore: 'Explore →', videoCount: 'videos', channels: 'Follow your favorite stories', genres: ['All', 'Romance', 'Revenge', 'Family', 'Secrets', 'Second chance'], follow: 'Follow', following: 'Following', empty: 'No dramas match your search.', loading: 'Loading from server…', apiFallback: 'Server unavailable, showing the demo catalog temporarily.', playbackUnavailable: 'Select an episode to start watching.', swipeHint: 'Swipe up to discover the next story' }, - zh: { code: 'zh', htmlLang: 'zh-CN', title: 'YMB · 短故事,大情绪', description: '发现精彩短剧,每天更新新剧集。', search: '搜索短剧、演员或题材', home: '首页', shorts: 'Shorts', subscriptions: '订阅', library: '媒体库', history: '观看记录', liked: '喜欢的短剧', hero: '短故事,', accent: '大情绪。', watch: '立即观看', picks: '本周精选短剧', shortTitle: '一口气看完的故事', viewAll: '查看全部 →', explore: '探索 →', videoCount: '个视频', channels: '关注你喜欢的故事', genres: ['全部', '爱情', '复仇', '家庭', '秘密', '重生'], follow: '关注', following: '已关注', empty: '没有找到匹配的短剧。', loading: '正在从服务端加载…', apiFallback: '服务端暂不可用,暂时展示演示目录。', playbackUnavailable: '请选择剧集开始播放。', swipeHint: '向上滑动发现下一个故事' } + id: { code: 'id', htmlLang: 'id', title: 'YMB · Cerita pendek, emosi besar', description: 'Temukan drama pendek terbaik dan episode baru setiap hari.', search: 'Cari drama, aktor, atau genre', voiceSearch: 'Cari dengan suara', create: 'Buat', notifications: 'Notifikasi', account: 'Akun', home: 'Beranda', shorts: 'Shorts', subscriptions: 'Langganan', library: 'Perpustakaan', history: 'Riwayat', you: 'Anda', playlists: 'Playlist', watchLater: 'Tonton nanti', likedVideos: 'Video yang disukai', myVideos: 'Video saya', explore: 'Jelajahi', music: 'Musik', movies: 'Film', live: 'Live', more: 'Lainnya', liked: 'Drama disukai', hero: 'Cerita pendek,', accent: 'emosi besar.', watch: 'Mulai menonton', picks: 'Drama pilihan minggu ini', shortTitle: 'Shorts', viewAll: 'Lihat semua →', videoCount: 'video', channels: 'Ikuti cerita favoritmu', genres: ['Semua', 'Romansa', 'Balas dendam', 'Keluarga', 'Rahasia', 'Kisah kedua'], homeCategories: ['Semua', 'Podcast', 'Musik', 'Game', 'Live', 'Travel', 'Baru diupload', 'Temukan video baru'], follow: 'Ikuti', following: 'Mengikuti', empty: 'Belum ada drama yang cocok.', loading: 'Memuat dari server…', apiFallback: 'Server belum tersedia, menampilkan katalog demo sementara.', playbackUnavailable: 'Pilih episode untuk mulai menonton.', swipeHint: 'Geser ke atas untuk menemukan cerita berikutnya' }, + en: { code: 'en', htmlLang: 'en', title: 'YMB · Short stories, big emotions', description: 'Discover the best short dramas and new episodes every day.', search: 'Search dramas, actors, or genres', voiceSearch: 'Search with your voice', create: 'Create', notifications: 'Notifications', account: 'Account', home: 'Home', shorts: 'Shorts', subscriptions: 'Subscriptions', library: 'Library', history: 'History', you: 'You', playlists: 'Playlists', watchLater: 'Watch later', likedVideos: 'Liked videos', myVideos: 'Your videos', explore: 'Explore', music: 'Music', movies: 'Movies', live: 'Live', more: 'Show more', liked: 'Liked dramas', hero: 'Short stories,', accent: 'big emotions.', watch: 'Watch now', picks: 'Drama picks this week', shortTitle: 'Shorts', viewAll: 'View all →', videoCount: 'videos', channels: 'Follow your favorite stories', genres: ['All', 'Romance', 'Revenge', 'Family', 'Secrets', 'Second chance'], homeCategories: ['All', 'Podcasts', 'Music', 'Gaming', 'Live', 'Travel', 'Recently uploaded', 'New to you'], follow: 'Follow', following: 'Following', empty: 'No dramas match your search.', loading: 'Loading from server…', apiFallback: 'Server unavailable, showing the demo catalog temporarily.', playbackUnavailable: 'Select an episode to start watching.', swipeHint: 'Swipe up to discover the next story' }, + zh: { code: 'zh', htmlLang: 'zh-CN', title: 'YMB · 短故事,大情绪', description: '发现精彩短剧,每天更新新剧集。', search: '搜索短剧、演员或题材', voiceSearch: '通过语音搜索内容', create: '创建', notifications: '通知', account: '账号菜单', home: '首页', shorts: 'Shorts', subscriptions: '订阅', library: '媒体库', history: '观看记录', you: '我', playlists: '播放列表', watchLater: '稍后观看', likedVideos: '赞过的视频', myVideos: '我的视频', explore: '探索', music: '音乐', movies: '电影', live: '直播', more: '展开', liked: '喜欢的短剧', hero: '短故事,', accent: '大情绪。', watch: '立即观看', picks: '本周精选短剧', shortTitle: 'Shorts', viewAll: '查看全部 →', videoCount: '个视频', channels: '关注你喜欢的故事', genres: ['全部', '爱情', '复仇', '家庭', '秘密', '重生'], homeCategories: ['全部', '播客', '音乐', '游戏', '直播', '旅游', '最近上传', '发现新视频'], follow: '关注', following: '已关注', empty: '没有找到匹配的短剧。', loading: '正在从服务端加载…', apiFallback: '服务端暂不可用,暂时展示演示目录。', playbackUnavailable: '请选择剧集开始播放。', swipeHint: '向上滑动发现下一个故事' } }; export function getLocale(code) { return locales[code] || locales.id; } diff --git a/src/layouts/AppShell.jsx b/src/layouts/AppShell.jsx index 22d42a7..74f782d 100644 --- a/src/layouts/AppShell.jsx +++ b/src/layouts/AppShell.jsx @@ -1,5 +1,5 @@ // Author: fruit.com.cn -import { Bell, Bookmark, Compass, History, Home, Menu, Search, UserRound, X } from 'lucide-react'; +import { Bell, Bookmark, ChevronDown, ChevronRight, Compass, Film, History, Home, ListVideo, Menu, Mic, Music2, Plus, Radio, Search, ThumbsUp, UserRound, Video, X } from 'lucide-react'; import { useState } from 'react'; import { NavLink, Outlet, useNavigate } from 'react-router-dom'; import { useAppStore } from '../app/store'; @@ -9,7 +9,9 @@ export function AppShell() { const { locale, query, setQuery, sidebarOpen, setSidebarOpen } = useAppStore(); const navigate = useNavigate(); const [searchOpen, setSearchOpen] = useState(false); - const navItems = [[Home, locale.home, '/'], [Compass, locale.shorts, '/shorts'], [UserRound, locale.subscriptions, '/subscriptions'], [Bookmark, locale.library, '/library'], [History, locale.history, '/history']]; + const primaryItems = [[Home, locale.home, '/'], [Compass, locale.shorts, '/shorts'], [UserRound, locale.subscriptions, '/subscriptions']]; + const libraryItems = [[History, locale.history, '/history'], [ListVideo, locale.playlists, '/library'], [Bookmark, locale.watchLater, '/library'], [ThumbsUp, locale.likedVideos, '/library'], [Video, locale.myVideos, '/library']]; + const exploreItems = [[Music2, locale.music, '/search?genre=music'], [Film, locale.movies, '/search?genre=movies'], [Radio, locale.live, '/shorts']]; const submitSearch = (event) => { event.preventDefault(); navigate(`/search?q=${encodeURIComponent(query)}`); @@ -28,17 +30,19 @@ export function AppShell() { setQuery(event.target.value)} placeholder={locale.search} aria-label={locale.search} /> +
-
{sidebarOpen && } diff --git a/src/main.jsx b/src/main.jsx index 78dc0ef..f843dba 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -3,5 +3,6 @@ import { createRoot } from 'react-dom/client'; import { App } from './app/App'; import './styles.css'; import './enterprise.css'; +import './youtube-overrides.css'; createRoot(document.getElementById('root')).render(); diff --git a/src/pages/HomePage.jsx b/src/pages/HomePage.jsx index ed50268..e57c51f 100644 --- a/src/pages/HomePage.jsx +++ b/src/pages/HomePage.jsx @@ -9,7 +9,8 @@ import { appApi } from '../services/appApi'; import { normalizeDrama, uniqueDramas } from '../services/normalizers'; import { useApi } from '../hooks/useApi'; -const SHORTS_LIMIT = 6; +const SHORTS_LIMIT = 5; +const FIRST_SHELF_LIMIT = 5; const ALL_GENRES_INDEX = 0; function normalizeHomeFeed(payload, localeCode) { @@ -26,7 +27,8 @@ function filterByGenre(items, genre, localeGenres) { export function HomePage() { const { locale } = useAppStore(); - const [genre, setGenre] = useState(locale.genres[ALL_GENRES_INDEX]); + const categoryItems = locale.homeCategories || locale.genres; + const [genre, setGenre] = useState(categoryItems[ALL_GENRES_INDEX]); const api = useApi( () => appApi.homeFeed().then((payload) => normalizeHomeFeed(payload, locale.code)), [locale.code], @@ -34,34 +36,28 @@ export function HomePage() { const remoteItems = api.data || []; const fallbackItems = searchCatalog('', genre, locale.genres.indexOf(genre)); const items = useMemo( - () => filterByGenre(remoteItems.length ? remoteItems : fallbackItems, genre, locale.genres), - [fallbackItems, genre, locale.genres, remoteItems], + () => filterByGenre(remoteItems.length ? remoteItems : fallbackItems, genre, categoryItems), + [categoryItems, fallbackItems, genre, remoteItems], ); const shortsItems = (remoteItems.length ? remoteItems : videos).slice(0, SHORTS_LIMIT); + const firstShelf = items.slice(0, FIRST_SHELF_LIMIT); + const secondShelf = items.slice(FIRST_SHELF_LIMIT); useEffect(() => { - setGenre(locale.genres[ALL_GENRES_INDEX]); - }, [locale.code, locale.genres]); + setGenre(categoryItems[ALL_GENRES_INDEX]); + }, [categoryItems, locale.code]); return ( <> -
-
- YMB HOME -

{locale.picks}

-

{locale.description}

-
- {items.length} {locale.videoCount} -
- -
- -
- {locale.genres.map((item) => ( +
+
+ {categoryItems.map((item) => (
- +
+

▶{locale.shortTitle}

+ +
{shortsItems.map((video) => )}
+ + {secondShelf.length > 0 &&
+
+ {secondShelf.map((video) => )} +
+
} ); } diff --git a/src/youtube-overrides.css b/src/youtube-overrides.css new file mode 100644 index 0000000..98c47ae --- /dev/null +++ b/src/youtube-overrides.css @@ -0,0 +1,108 @@ +/* Author: fruit.com.cn */ +:root { + --bg: #fff; + --surface: #fff; + --line: #e5e5e5; + --text: #0f0f0f; + --muted: #606060; + --accent: #ff0033; + --sidebar: 240px; +} + +body { background: var(--bg); color: var(--text); font-family: Arial, Helvetica, sans-serif; } +.topbar { height: 56px; gap: 16px; padding: 0 20px; background: #fff; border-bottom: 1px solid var(--line); box-shadow: none; } +.topbar-menu { color: var(--text); } +.topbar-menu:hover, .voice-search:hover, .notification-button:hover { background: #f2f2f2; } +.brand { min-width: 138px; height: 40px; } +.brand img { width: 104px; height: 38px; object-fit: contain; } +.search-form { height: 40px; max-width: 640px; flex: 1; margin: 0 0 0 clamp(18px, 7vw, 110px); border-radius: 22px; } +.search-form input { height: 40px; border-color: #c6c6c6; background: #fff; color: var(--text); } +.search-form button { height: 40px; background: #f8f8f8; color: var(--text); border-color: #c6c6c6; } +.voice-search { flex: none; width: 40px; height: 40px; border-radius: 50%; color: var(--text); } +.top-actions { gap: 14px; margin-left: auto; color: var(--text); } +.create-button { display: flex; align-items: center; gap: 6px; border: 0; border-radius: 22px; padding: 9px 14px; background: #f2f2f2; color: var(--text); font-weight: 600; cursor: pointer; } +.create-button:hover { background: #e5e5e5; } +.notification-button { width: 40px; height: 40px; border-radius: 50%; } +.language-trigger { border-color: #d6d6d6; background: #fff; color: var(--text); } +.profile-chip { background: #065fd4; color: #fff; } + +.sidebar { top: 56px; width: var(--sidebar); padding: 12px; background: #fff; border: 0; box-shadow: none; color: var(--text); } +.sidebar.closed { width: 72px; padding: 12px 8px; } +.sidebar nav a, .genre-link { min-height: 40px; padding: 9px 12px; border-radius: 10px; color: var(--text); gap: 22px; } +.sidebar nav a:hover, .genre-link:hover, .sidebar nav a.active { background: #f2f2f2; color: var(--text); } +.sidebar-group { display: grid; gap: 2px; } +.sidebar-group-title { display: flex; align-items: center; gap: 7px; padding: 12px 12px 6px; font-size: 16px; font-weight: 700; } +.sidebar-group-title svg { color: var(--muted); } +.sidebar small { display: none; } +.sidebar.closed .sidebar-group-title, .sidebar.closed .sidebar-label { display: none; } +.sidebar.closed nav a, .sidebar.closed .genre-link { justify-content: center; gap: 0; } +.sidebar.closed .nav-rule { display: block; margin: 12px 6px; } +.nav-rule { height: 1px; background: var(--line); margin: 12px 0; } + +.content { max-width: none; padding: 12px 24px 80px 264px; margin: 0; } +.content-wide { padding-left: 96px; } +.youtube-home-header { display: none; } +.youtube-home-feed, .youtube-shorts-shelf { margin-bottom: 40px; } +.youtube-category-bar { position: sticky; top: 56px; z-index: 2; display: flex; gap: 12px; overflow-x: auto; padding: 12px 0; background: #fff; scrollbar-width: none; } +.youtube-category-bar::-webkit-scrollbar { display: none; } +.youtube-category-chip { flex: none; border: 0; border-radius: 8px; padding: 9px 14px; background: #f2f2f2; color: var(--text); white-space: nowrap; cursor: pointer; font-size: 14px; } +.youtube-category-chip:hover { background: #e5e5e5; } +.youtube-category-chip.active { background: var(--text); color: #fff; } +.video-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 36px 16px; } +.media-card { min-width: 0; } +.media-thumb { border-radius: 10px; background: #f2f2f2; } +.media-thumb img { display: block; } +.media-meta { align-items: flex-start; gap: 12px; padding-top: 12px; } +.channel-avatar { width: 36px; height: 36px; background: #e5e5e5; color: var(--text); } +.media-copy { min-width: 0; flex: 1; } +.media-title { display: -webkit-box; overflow: hidden; color: var(--text); font-size: 16px; line-height: 1.35; -webkit-box-orient: vertical; -webkit-line-clamp: 2; } +.media-meta small { color: var(--muted); font-size: 13px; line-height: 1.5; } +.media-more { flex: none; border: 0; background: transparent; color: var(--text); padding: 4px; cursor: pointer; } + +.youtube-shorts-shelf { padding: 20px 0 26px; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); } +.youtube-shelf-heading { display: flex; align-items: center; justify-content: space-between; margin-bottom: 18px; } +.youtube-shelf-heading h2 { display: flex; align-items: center; gap: 9px; margin: 0; color: var(--text); font-size: 24px; } +.youtube-shelf-heading button { border: 0; background: transparent; color: #065fd4; font-weight: 600; cursor: pointer; } +.shorts-mark { display: grid; place-items: center; width: 28px; height: 28px; border-radius: 8px; background: var(--accent); color: #fff; font-size: 13px; } +.shorts-row { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 16px; } +.shorts-row .media-thumb { aspect-ratio: 9 / 16; border-radius: 10px; } +.shorts-row .media-meta { display: none; } +.short-media-title { display: -webkit-box; overflow: hidden; margin-top: 9px; color: var(--text); font-size: 14px; line-height: 1.4; -webkit-box-orient: vertical; -webkit-line-clamp: 2; } +.language-menu { background: #fff; border-color: var(--line); box-shadow: 0 8px 24px #0002; } +.language-menu button { color: var(--text); } +.language-menu button:hover, .language-menu button.selected { background: #f2f2f2; color: var(--text); } + +@media (min-width: 1500px) { .video-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } } +@media (max-width: 1100px) and (min-width: 761px) { .video-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } .shorts-row { grid-template-columns: repeat(4, minmax(0, 1fr)); } } +@media (max-width: 760px) { + .topbar { height: 56px; padding: 0 12px; gap: 9px; } + .brand { min-width: 74px; } + .brand img { width: 74px; height: 34px; } + .voice-search, .create-button, .notification-button { display: none; } + .language-switcher { display: block; } + .language-trigger { width: 38px; height: 38px; justify-content: center; padding: 0; } + .language-trigger span, .language-trigger b, .language-trigger svg:last-child { display: none; } + .top-actions { gap: 8px; } + .search-form { position: absolute; top: 58px; left: 12px; right: 12px; max-width: none; margin: 0; } + .content, .content-wide { padding: 0 12px 88px; } + .youtube-category-bar { top: 56px; margin: 0 -12px; padding: 10px 12px; } + .video-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 28px 10px; } + .media-meta { gap: 8px; padding-top: 9px; } + .channel-avatar { width: 28px; height: 28px; font-size: 12px; } + .media-title { font-size: 13px; } + .media-meta small { font-size: 11px; } + .media-more { display: none; } + .youtube-shorts-shelf { margin: 28px -12px 34px; padding: 16px 12px 22px; } + .youtube-shelf-heading h2 { font-size: 20px; } + .shorts-row { display: flex; gap: 10px; overflow-x: auto; margin: 0; } + .shorts-row .media-card { flex: 0 0 150px; } + .shorts-row .media-thumb { aspect-ratio: 9 / 16; } + .youtube-home-feed-secondary { margin-top: 0; } + .bottom-nav { height: 64px; background: #fff; border-top: 1px solid var(--line); } + .bottom-nav a { color: var(--muted); } + .bottom-nav a.active { color: var(--text); } + .sidebar { display: none; top: 56px; bottom: 64px; width: 246px; padding: 16px 12px; box-shadow: 16px 0 30px #0008; } + .sidebar.open { display: block; } + .sidebar.closed { display: none; } + .scrim { background: #0006; } +} diff --git a/styles.css b/styles.css index 3e86092..2fc01eb 100644 --- a/styles.css +++ b/styles.css @@ -3,7 +3,3 @@ :root{--bg:#0f0f0f;--surface:#181818;--line:#303030;--text:#f1f1f1;--muted:#aaa;--accent:#ff4b3e;--sidebar:230px}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--text);font:14px Arial,sans-serif}a{color:inherit;text-decoration:none}button,input{font:inherit}.topbar{height:64px;position:sticky;top:0;z-index:3;display:flex;align-items:center;gap:24px;padding:0 28px;background:#0f0f0ff2;border-bottom:1px solid #222}.brand{display:flex;align-items:center;gap:8px;font-size:26px;font-weight:700;min-width:150px}.brand-symbol{display:grid;place-items:center;width:28px;height:28px;border-radius:8px;background:var(--accent);font-size:12px}.search-form{display:flex;max-width:620px;flex:1;margin:auto}.search-form input{width:100%;height:40px;padding:0 16px;border:1px solid #3b3b3b;border-radius:22px 0 0 22px;background:#121212;color:#fff}.search-form button{width:54px;border:1px solid #3b3b3b;border-left:0;border-radius:0 22px 22px 0;background:#282828;color:#fff;font-size:24px}.top-actions{display:flex;align-items:center;gap:10px}.icon-button{border:0;background:transparent;color:#fff;font-size:22px;cursor:pointer}.profile-chip{border:0;border-radius:50%;width:32px;height:32px;background:#d45a68;color:#fff}.sidebar{position:fixed;top:64px;bottom:0;width:var(--sidebar);padding:20px 12px;background:var(--bg);border-right:1px solid #222}.main-nav,.genre-nav{display:grid;gap:4px}.nav-item,.genre-nav a{display:flex;gap:18px;align-items:center;padding:11px 14px;border-radius:10px;color:#ccc}.nav-item:hover,.nav-item.active,.genre-nav a:hover{background:#272727;color:#fff}.nav-item span{width:18px;text-align:center;font-size:20px}.nav-item b{font-weight:400}.nav-rule{height:1px;background:var(--line);margin:18px 10px}.nav-label{padding:0 14px;color:#888;font-size:11px;text-transform:uppercase;letter-spacing:.1em}.sidebar-footer{position:absolute;bottom:24px;padding:0 14px;color:#666;font-size:11px;line-height:1.8}.content{max-width:1500px;padding:28px 34px 80px calc(var(--sidebar) + 34px);margin:auto}.hero-card{position:relative;min-height:350px;overflow:hidden;border-radius:18px;background:#27191a;margin-bottom:44px}.hero-copy{position:relative;z-index:1;width:52%;padding:54px}.eyebrow{color:#ff786f;font-size:11px;font-weight:700;letter-spacing:.16em}.hero-copy h1{margin:12px 0;font-size:56px;line-height:.98}.hero-copy h1 em{color:#ff786f;font-style:normal}.hero-copy p{max-width:390px;color:#ccc;line-height:1.7}.hero-art{position:absolute;inset:0 0 0 40%}.hero-art img{width:100%;height:100%;object-fit:cover}.hero-gradient{position:absolute;inset:0;background:linear-gradient(90deg,#27191a,rgba(39,25,26,.75) 26%,transparent 80%)}.hero-actions{display:flex;gap:10px}.primary-button,.ghost-button,.chip{border:0;border-radius:22px;padding:11px 17px;cursor:pointer;font-weight:600}.primary-button{background:#fff;color:#111}.ghost-button,.chip{background:#2b2b2b;color:#fff}.section-block{margin-bottom:46px}.section-heading{display:flex;justify-content:space-between;align-items:end;margin-bottom:17px}.section-heading h2{margin:7px 0 0;font-size:24px}.see-all{color:#ff8a82;font-size:13px}.chip-row{display:flex;gap:8px;overflow:auto;margin-bottom:18px}.chip.active,.chip:hover{background:#fff;color:#111}.video-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:24px 16px}.thumb{position:relative;aspect-ratio:16/9;overflow:hidden;border-radius:10px;background:#222}.thumb img,.short-card img{width:100%;height:100%;object-fit:cover}.duration{position:absolute;right:7px;bottom:7px;padding:3px 6px;background:#000c;border-radius:4px;font-size:11px}.video-meta{display:flex;gap:9px;padding-top:9px}.channel-avatar{display:grid;place-items:center;flex:none;width:32px;height:32px;border-radius:50%;background:#734c67;font-weight:700}.video-title{margin:0;line-height:1.4;font-weight:600}.video-sub{margin:4px 0 0;color:#999;font-size:11px;line-height:1.45}.shorts-row{display:grid;grid-template-columns:repeat(6,1fr);gap:14px}.short-card{position:relative;aspect-ratio:9/14;overflow:hidden;border-radius:11px}.short-card:after{content:'';position:absolute;inset:45% 0 0;background:linear-gradient(transparent,#000c)}.short-title{position:absolute;z-index:1;bottom:10px;left:9px;right:9px;font-size:12px;font-weight:600}.channel-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:12px}.channel-card{display:flex;align-items:center;gap:10px;padding:14px;border:1px solid var(--line);border-radius:11px}.channel-card p{margin:0 0 4px;font-weight:600}.channel-card span{color:#888;font-size:11px}.follow-button{margin-left:auto;border:0;border-radius:18px;padding:8px 11px;background:#fff;color:#111}.bottom-nav,.mobile-only{display:none}.toast{position:fixed;left:50%;bottom:24px;transform:translate(-50%,20px);opacity:0;background:#fff;color:#111;padding:11px 16px;border-radius:7px;transition:.2s}.toast.show{opacity:1;transform:translate(-50%,0)} @media(max-width:1100px){.video-grid{grid-template-columns:repeat(3,1fr)}.shorts-row{grid-template-columns:repeat(5,1fr)}.channel-grid{grid-template-columns:repeat(2,1fr)}} @media(max-width:760px){.topbar{height:58px;padding:0 14px;gap:12px}.mobile-only{display:block}.desktop-only,.sidebar{display:none}.brand{min-width:auto;font-size:23px}.search-form{position:absolute;top:65px;left:14px;right:14px;display:none}.search-form.open{display:flex}.content{padding:20px 14px 90px}.hero-card{min-height:420px;margin:0 -14px 36px;border-radius:0;display:flex;align-items:flex-end}.hero-copy{width:100%;padding:24px}.hero-copy h1{font-size:42px}.hero-art{inset:0}.hero-gradient{background:linear-gradient(0deg,#27191a,rgba(39,25,26,.45) 55%,transparent)}.section-heading h2{font-size:21px}.video-grid{grid-template-columns:repeat(2,1fr);gap:22px 12px}.shorts-row{display:flex;overflow:auto;margin-right:-14px}.short-card{flex:0 0 130px}.channel-grid{grid-template-columns:1fr}.bottom-nav{position:fixed;display:flex;justify-content:space-around;align-items:center;left:0;right:0;bottom:0;height:66px;z-index:3;background:#151515;border-top:1px solid #292929}.bottom-nav a{display:grid;place-items:center;gap:4px;color:#aaa;font-size:10px}.bottom-nav span{font-size:20px}.bottom-nav .active{color:#fff}.bottom-nav .active span{color:var(--accent)}} - -/* Author: fruit.com.cn */ -.topbar-menu{display:grid;place-items:center;flex:none;width:40px;height:40px;border-radius:50%;cursor:pointer}.topbar-menu:hover{background:#272727}.mobile-search{display:none}.sidebar{transition:width .2s,padding .2s}.sidebar.closed{width:84px;padding-left:10px;padding-right:10px}.sidebar.closed nav a,.sidebar.closed .genre-link{justify-content:center;padding-left:10px;padding-right:10px}.sidebar.closed .sidebar-label,.sidebar.closed small,.sidebar.closed .nav-rule{display:none}.content{transition:padding-left .2s}.content-wide{padding-left:112px}.youtube-home-header{display:flex;align-items:end;justify-content:space-between;gap:20px;padding:4px 0 22px;border-bottom:1px solid var(--line);margin-bottom:22px}.youtube-home-header h1{margin:7px 0 5px;font:700 30px Outfit}.youtube-home-header p{margin:0;color:var(--muted)}.youtube-home-header .feed-count{color:#888;font-size:12px;white-space:nowrap}.youtube-home-feed,.youtube-shorts-shelf{margin-bottom:46px}.youtube-home-feed .section-heading button,.youtube-shorts-shelf .section-heading button{border:0;background:transparent;color:#aaa;cursor:pointer}.youtube-home-feed .section-heading button:hover,.youtube-shorts-shelf .section-heading button:hover{color:#fff}.youtube-home-feed .video-grid{margin-top:2px} -@media(max-width:760px){.topbar-menu{width:34px;height:34px}.mobile-search{display:grid}.top-actions>svg{display:none}.top-actions{margin-left:auto}.sidebar{display:none;top:58px;bottom:66px;width:246px;padding:16px 12px;box-shadow:16px 0 30px #0008}.sidebar.open{display:block}.sidebar.closed{display:none}.content,.content-wide{padding:18px 14px 90px}.search-form{z-index:8}.search-form.open{display:flex}.youtube-home-header{align-items:flex-start;padding:0 0 18px}.youtube-home-header h1{font-size:24px}.youtube-home-header .feed-count{padding-top:30px}.youtube-home-feed,.youtube-shorts-shelf{margin-bottom:34px}}