1764 lines
85 KiB
HTML
1764 lines
85 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
|
<meta name="theme-color" content="#141414">
|
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
<title>STREAM</title>
|
|
<style>
|
|
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
|
|
:root{
|
|
--bg:#141414;
|
|
--surface:#1c1c1c;
|
|
--surface-hover:#242424;
|
|
--surface-alt:#1f1f1f;
|
|
--card:#1a1a1a;
|
|
--card-hover:#222;
|
|
--accent:#ff6600;
|
|
--accent-dim:#cc5200;
|
|
--accent-glow:rgba(255,102,0,.18);
|
|
--text:#f0f0f0;
|
|
--text-secondary:#888;
|
|
--green:#3bb33b;
|
|
--green-bg:rgba(59,179,59,.15);
|
|
--red:#e74c3c;
|
|
--radius:14px;
|
|
--radius-sm:10px;
|
|
--header-h:56px;
|
|
--bottom-h:60px;
|
|
--transition:cubic-bezier(.22,1,.36,1);
|
|
--overlay:rgba(0,0,0,.55);
|
|
--border:rgba(255,255,255,.07);
|
|
--border-hover:rgba(255,255,255,.14);
|
|
--shimmer-a:#1a1a1a;
|
|
--shimmer-b:#252525;
|
|
--input-focus-bg:var(--card-hover);
|
|
}
|
|
:root.light{
|
|
--bg:#f5f5f5;
|
|
--surface:#ffffff;
|
|
--surface-hover:#f0f0f0;
|
|
--surface-alt:#fafafa;
|
|
--card:#ffffff;
|
|
--card-hover:#f8f8f8;
|
|
--text:#111111;
|
|
--text-secondary:#666666;
|
|
--overlay:rgba(0,0,0,.35);
|
|
--border:rgba(0,0,0,.1);
|
|
--border-hover:rgba(0,0,0,.18);
|
|
--shimmer-a:#e8e8e8;
|
|
--shimmer-b:#f2f2f2;
|
|
--input-focus-bg:#ffffff;
|
|
}
|
|
html{-webkit-tap-highlight-color:transparent;scroll-behavior:smooth}
|
|
body{
|
|
font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;
|
|
background:var(--bg);color:var(--text);
|
|
min-height:100dvh;overflow-x:hidden;
|
|
-webkit-font-smoothing:antialiased;
|
|
transition:background .35s ease,color .35s ease;
|
|
}
|
|
|
|
/* ═══════════════════════════════════════════════
|
|
SPLASH SCREEN
|
|
═══════════════════════════════════════════════ */
|
|
.splash{
|
|
position:fixed;inset:0;z-index:9999;
|
|
display:flex;flex-direction:column;
|
|
align-items:center;justify-content:center;
|
|
background:var(--bg);
|
|
transition:opacity .6s ease,visibility .6s;
|
|
}
|
|
.splash.fade-out{opacity:0;visibility:hidden}
|
|
.splash-logo{
|
|
font-size:42px;font-weight:900;letter-spacing:8px;
|
|
color:var(--accent);
|
|
animation:splashPulse 1.4s ease-in-out infinite alternate;
|
|
}
|
|
.splash-bar{
|
|
width:120px;height:3px;border-radius:2px;
|
|
background:rgba(255,255,255,.06);
|
|
margin-top:28px;overflow:hidden;
|
|
}
|
|
.splash-bar-inner{
|
|
width:0%;height:100%;border-radius:2px;
|
|
background:linear-gradient(90deg,var(--accent),#ff9944);
|
|
animation:splashLoad 2.6s ease forwards;
|
|
}
|
|
.splash-tagline{
|
|
margin-top:14px;font-size:12px;font-weight:500;
|
|
color:var(--text-secondary);letter-spacing:1.5px;
|
|
text-transform:uppercase;opacity:0;
|
|
animation:splashFadeIn .6s .6s ease forwards;
|
|
}
|
|
@keyframes splashPulse{
|
|
0%{text-shadow:0 0 20px rgba(255,102,0,.15)}
|
|
100%{text-shadow:0 0 40px rgba(255,102,0,.4)}
|
|
}
|
|
@keyframes splashLoad{0%{width:0}60%{width:75%}100%{width:100%}}
|
|
@keyframes splashFadeIn{to{opacity:1}}
|
|
|
|
/* ═══════════════════════════════════════════════
|
|
LOGIN SCREEN
|
|
═══════════════════════════════════════════════ */
|
|
.login-screen{
|
|
position:fixed;inset:0;z-index:9000;
|
|
display:none;flex-direction:column;
|
|
align-items:center;justify-content:center;
|
|
background:var(--bg);padding:24px;
|
|
opacity:0;transition:opacity .4s ease;
|
|
}
|
|
.login-screen.visible{display:flex;opacity:1}
|
|
.login-screen.fade-out{opacity:0}
|
|
.login-card{width:100%;max-width:360px;display:flex;flex-direction:column;align-items:center}
|
|
.login-logo{font-size:36px;font-weight:900;letter-spacing:7px;color:var(--accent);margin-bottom:6px}
|
|
.login-subtitle{font-size:13px;color:var(--text-secondary);margin-bottom:36px;letter-spacing:.5px}
|
|
.login-form{width:100%;display:flex;flex-direction:column;gap:14px}
|
|
.login-field{position:relative}
|
|
.login-field svg{
|
|
position:absolute;left:14px;top:50%;transform:translateY(-50%);
|
|
width:18px;height:18px;color:var(--text-secondary);
|
|
pointer-events:none;transition:color .2s;
|
|
}
|
|
.login-input{
|
|
width:100%;padding:14px 14px 14px 46px;
|
|
border-radius:var(--radius);
|
|
border:1.5px solid var(--border);
|
|
background:var(--surface);color:var(--text);font-size:15px;
|
|
outline:none;transition:border-color .25s,box-shadow .25s,background .25s;
|
|
}
|
|
.login-input::placeholder{color:var(--text-secondary)}
|
|
.login-input:focus{
|
|
border-color:var(--accent);background:var(--input-focus-bg);
|
|
box-shadow:0 0 0 3px var(--accent-glow);
|
|
}
|
|
.login-input:focus ~ svg{color:var(--accent)}
|
|
.login-pw-toggle{
|
|
position:absolute;right:12px;top:50%;transform:translateY(-50%);
|
|
background:none;border:none;cursor:pointer;color:var(--text-secondary);
|
|
padding:4px;transition:color .2s;
|
|
}
|
|
.login-pw-toggle:hover{color:var(--text)}
|
|
.login-pw-toggle svg{width:18px;height:18px}
|
|
.login-error{font-size:12px;color:var(--red);padding:0 4px;min-height:18px;opacity:0;transition:opacity .2s}
|
|
.login-error.show{opacity:1}
|
|
.login-btn{
|
|
width:100%;padding:15px;border:none;border-radius:var(--radius);
|
|
background:linear-gradient(135deg,var(--accent),#e85d00);
|
|
color:#fff;font-size:16px;font-weight:700;letter-spacing:.5px;
|
|
cursor:pointer;transition:transform .15s,box-shadow .3s,opacity .2s;
|
|
margin-top:4px;position:relative;overflow:hidden;
|
|
}
|
|
.login-btn:hover{box-shadow:0 6px 24px rgba(255,102,0,.3)}
|
|
.login-btn:active{transform:scale(.97)}
|
|
.login-btn.loading{pointer-events:none;opacity:.8}
|
|
.login-btn .btn-text{transition:opacity .2s}
|
|
.login-btn.loading .btn-text{opacity:0}
|
|
.login-spinner{
|
|
position:absolute;top:50%;left:50%;
|
|
transform:translate(-50%,-50%);
|
|
width:20px;height:20px;
|
|
border:2.5px solid rgba(255,255,255,.25);
|
|
border-top-color:#fff;border-radius:50%;
|
|
animation:spin .6s linear infinite;opacity:0;
|
|
}
|
|
.login-btn.loading .login-spinner{opacity:1}
|
|
@keyframes spin{to{transform:translate(-50%,-50%) rotate(360deg)}}
|
|
.login-divider{display:flex;align-items:center;gap:14px;width:100%;margin:22px 0}
|
|
.login-divider span{font-size:12px;color:var(--text-secondary);white-space:nowrap}
|
|
.login-divider::before,.login-divider::after{content:'';flex:1;height:1px;background:var(--border)}
|
|
.login-social{display:flex;gap:12px;width:100%}
|
|
.social-btn{
|
|
flex:1;display:flex;align-items:center;justify-content:center;gap:8px;
|
|
padding:12px;border-radius:var(--radius);
|
|
background:var(--surface);border:1.5px solid var(--border);
|
|
color:var(--text);font-size:13px;font-weight:600;
|
|
cursor:pointer;transition:background .2s,border-color .2s;
|
|
}
|
|
.social-btn:hover{background:var(--surface-hover);border-color:var(--border-hover)}
|
|
.social-btn:active{transform:scale(.97)}
|
|
.social-btn svg{width:18px;height:18px}
|
|
.login-footer{margin-top:28px;font-size:13.5px;color:var(--text-secondary)}
|
|
.login-footer a{color:var(--accent);font-weight:600;text-decoration:none;transition:opacity .2s}
|
|
.login-footer a:hover{opacity:.8}
|
|
|
|
/* ═══════════════════════════════════════════════
|
|
MAIN APP WRAPPER
|
|
═══════════════════════════════════════════════ */
|
|
.app{display:none;opacity:0;transition:opacity .4s ease}
|
|
.app.visible{display:block;opacity:1}
|
|
|
|
/* -- HEADER -- */
|
|
.header{
|
|
position:sticky;top:0;z-index:100;
|
|
display:flex;align-items:center;justify-content:space-between;
|
|
height:var(--header-h);padding:0 16px;
|
|
background:color-mix(in srgb, var(--bg) 94%, transparent);
|
|
backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);
|
|
border-bottom:1px solid var(--border);
|
|
transition:background .35s,border-color .35s;
|
|
}
|
|
.logo{font-size:21px;font-weight:800;letter-spacing:3.5px;color:var(--accent);user-select:none}
|
|
.header-actions{display:flex;align-items:center;gap:10px}
|
|
.icon-btn{
|
|
width:36px;height:36px;border-radius:50%;
|
|
display:flex;align-items:center;justify-content:center;
|
|
background:var(--surface);border:none;cursor:pointer;
|
|
transition:background .2s,transform .15s;color:var(--text);
|
|
}
|
|
.icon-btn:active{transform:scale(.9)}
|
|
.icon-btn:hover{background:var(--surface-hover)}
|
|
.icon-btn svg{width:18px;height:18px}
|
|
.profile-avatar{
|
|
width:34px;height:34px;border-radius:50%;
|
|
background:linear-gradient(135deg,var(--accent),#ff9944);
|
|
display:flex;align-items:center;justify-content:center;
|
|
font-weight:700;font-size:14px;color:#fff;
|
|
cursor:pointer;user-select:none;border:none;
|
|
transition:box-shadow .25s,transform .15s;
|
|
overflow:hidden;
|
|
}
|
|
.profile-avatar:hover{box-shadow:0 0 0 2.5px var(--accent)}
|
|
.profile-avatar:active{transform:scale(.9)}
|
|
.profile-avatar img{width:100%;height:100%;object-fit:cover;display:block}
|
|
|
|
/* -- SEARCH BAR -- */
|
|
.search-wrap{
|
|
padding:12px 16px 4px;
|
|
position:sticky;top:var(--header-h);z-index:90;
|
|
background:color-mix(in srgb, var(--bg) 90%, transparent);
|
|
backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);
|
|
transition:background .35s;
|
|
}
|
|
.search-box{position:relative;display:flex;align-items:center}
|
|
.search-box svg.search-icon{
|
|
position:absolute;left:14px;width:17px;height:17px;
|
|
color:var(--text-secondary);pointer-events:none;transition:color .2s;
|
|
}
|
|
.search-input{
|
|
width:100%;padding:11px 42px 11px 42px;
|
|
border-radius:var(--radius);
|
|
border:1.5px solid var(--border);
|
|
background:var(--surface);color:var(--text);font-size:15px;
|
|
outline:none;transition:border-color .25s,background .25s,box-shadow .25s;
|
|
}
|
|
.search-input::placeholder{color:var(--text-secondary)}
|
|
.search-input:focus{
|
|
border-color:var(--accent);background:var(--input-focus-bg);
|
|
box-shadow:0 0 0 3px var(--accent-glow);
|
|
}
|
|
.search-input:focus ~ svg.search-icon{color:var(--accent)}
|
|
.search-clear{
|
|
position:absolute;right:10px;width:28px;height:28px;
|
|
border-radius:50%;display:none;align-items:center;justify-content:center;
|
|
background:rgba(128,128,128,.15);border:none;cursor:pointer;
|
|
color:var(--text-secondary);transition:background .2s;
|
|
}
|
|
.search-clear.visible{display:flex}
|
|
.search-clear:hover{background:rgba(128,128,128,.25)}
|
|
.search-clear svg{width:14px;height:14px}
|
|
|
|
/* -- SECTION TITLES -- */
|
|
.section-bar{display:flex;align-items:center;justify-content:space-between;padding:18px 16px 10px}
|
|
.section-title{font-size:18px;font-weight:700;line-height:1.3}
|
|
.section-link{font-size:13px;font-weight:600;color:var(--accent);text-decoration:none;cursor:pointer;transition:opacity .2s}
|
|
.section-link:hover{opacity:.8}
|
|
|
|
/* -- CHIPS -- */
|
|
.chips{display:flex;gap:8px;padding:4px 16px 8px;overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch}
|
|
.chips::-webkit-scrollbar{display:none}
|
|
.chip{
|
|
flex-shrink:0;padding:7px 15px;border-radius:20px;
|
|
background:var(--surface);border:1.5px solid var(--border);
|
|
font-size:13px;font-weight:600;color:var(--text-secondary);
|
|
cursor:pointer;transition:all .25s;user-select:none;white-space:nowrap;
|
|
}
|
|
.chip:active{transform:scale(.95)}
|
|
.chip.active{background:var(--accent);border-color:var(--accent);color:#fff}
|
|
.chip:not(.active):hover{border-color:var(--border-hover);color:var(--text)}
|
|
|
|
/* -- MOVIE GRID -- */
|
|
.grid{display:grid;grid-template-columns:repeat(2,1fr);gap:12px;padding:10px 16px 100px}
|
|
.grid.compact{padding:10px 0 20px}
|
|
|
|
/* -- MOVIE CARD -- */
|
|
.card{
|
|
position:relative;border-radius:var(--radius);
|
|
background:var(--card);overflow:hidden;cursor:pointer;
|
|
transition:transform .3s var(--transition),box-shadow .3s,background .35s;
|
|
-webkit-user-select:none;user-select:none;
|
|
border:1px solid var(--border);
|
|
}
|
|
.card:hover,.card:active{transform:translateY(-3px) scale(1.012);box-shadow:0 10px 28px rgba(0,0,0,.25)}
|
|
.card-poster-wrap{position:relative;aspect-ratio:2/3;overflow:hidden;background:var(--shimmer-a)}
|
|
.card-poster{
|
|
width:100%;height:100%;object-fit:cover;display:block;
|
|
transition:transform .4s var(--transition),filter .3s;
|
|
}
|
|
.card:hover .card-poster{transform:scale(1.05);filter:brightness(1.06)}
|
|
.card-poster.loading{opacity:0}
|
|
.card-poster.loaded{opacity:1;transition:opacity .4s ease,transform .4s var(--transition),filter .3s}
|
|
.shimmer{
|
|
position:absolute;inset:0;
|
|
background:linear-gradient(110deg,var(--shimmer-a) 30%,var(--shimmer-b) 50%,var(--shimmer-a) 70%);
|
|
background-size:200% 100%;animation:shimmer 1.4s ease-in-out infinite;
|
|
}
|
|
@keyframes shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}
|
|
.card-poster.loaded ~ .shimmer{opacity:0;transition:opacity .3s}
|
|
.badge{
|
|
position:absolute;top:8px;left:8px;
|
|
display:flex;align-items:center;gap:3px;
|
|
padding:3px 7px;border-radius:6px;
|
|
background:var(--green-bg);
|
|
backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);
|
|
border:1px solid rgba(59,179,59,.2);z-index:2;
|
|
}
|
|
.badge-star{width:11px;height:11px;color:var(--green)}
|
|
.badge-num{font-size:11.5px;font-weight:700;color:var(--green);line-height:1}
|
|
.bookmark{
|
|
position:absolute;top:8px;right:8px;
|
|
width:30px;height:30px;border-radius:50%;
|
|
display:flex;align-items:center;justify-content:center;
|
|
background:rgba(0,0,0,.5);
|
|
backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);
|
|
border:none;cursor:pointer;z-index:2;
|
|
transition:color .2s,background .2s,transform .2s var(--transition);
|
|
}
|
|
.bookmark:active{transform:scale(.85)}
|
|
.bookmark svg{width:14px;height:14px}
|
|
.bookmark .bm-outline{color:rgba(255,255,255,.6)}
|
|
.bookmark .bm-filled{color:var(--accent);display:none}
|
|
.bookmark.saved .bm-outline{display:none}
|
|
.bookmark.saved .bm-filled{display:block}
|
|
.bookmark.saved{background:rgba(255,102,0,.18)}
|
|
.bookmark:hover{background:rgba(0,0,0,.7)}
|
|
.bookmark.saved:hover{background:rgba(255,102,0,.25)}
|
|
@keyframes bmPop{0%{transform:scale(1)}50%{transform:scale(1.35)}100%{transform:scale(1)}}
|
|
.bookmark.pop{animation:bmPop .35s var(--transition)}
|
|
.card-info{padding:10px 10px 14px}
|
|
.card-title{
|
|
font-size:13.5px;font-weight:700;line-height:1.3;
|
|
display:-webkit-box;-webkit-line-clamp:2;
|
|
-webkit-box-orient:vertical;overflow:hidden;margin-bottom:3px;
|
|
}
|
|
.card-meta{
|
|
font-size:11.5px;color:var(--text-secondary);line-height:1.3;
|
|
display:-webkit-box;-webkit-line-clamp:1;
|
|
-webkit-box-orient:vertical;overflow:hidden;
|
|
}
|
|
|
|
/* -- EMPTY STATE -- */
|
|
.empty-state{
|
|
grid-column:1/-1;display:flex;flex-direction:column;
|
|
align-items:center;justify-content:center;
|
|
padding:60px 20px;text-align:center;
|
|
}
|
|
.empty-state svg{width:52px;height:52px;color:var(--text-secondary);margin-bottom:14px;opacity:.4}
|
|
.empty-state h3{font-size:16px;font-weight:700;margin-bottom:5px;color:var(--text)}
|
|
.empty-state p{font-size:13px;color:var(--text-secondary);max-width:240px;line-height:1.5}
|
|
|
|
/* -- SPA PAGES -- */
|
|
.page{display:none;animation:pageIn .32s ease}
|
|
.page.active{display:block}
|
|
@keyframes pageIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}
|
|
|
|
/* -- SEARCH PAGE -- */
|
|
.sp-wrap{padding:20px 16px 100px}
|
|
.sp-title{font-size:20px;font-weight:700;margin-bottom:16px}
|
|
.sp-input-wrap{position:relative;margin-bottom:20px}
|
|
.sp-input-wrap svg.sp-ico{
|
|
position:absolute;left:14px;top:50%;transform:translateY(-50%);
|
|
width:17px;height:17px;color:var(--text-secondary);pointer-events:none;
|
|
}
|
|
.sp-input{
|
|
width:100%;padding:13px 44px 13px 44px;
|
|
border-radius:var(--radius);border:1.5px solid var(--border);
|
|
background:var(--surface);color:var(--text);font-size:15px;outline:none;
|
|
transition:border-color .25s,box-shadow .25s,background .35s;
|
|
}
|
|
.sp-input:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-glow)}
|
|
.sp-input::placeholder{color:var(--text-secondary)}
|
|
.sp-clear{
|
|
position:absolute;right:10px;top:50%;transform:translateY(-50%);
|
|
width:28px;height:28px;border-radius:50%;
|
|
display:none;align-items:center;justify-content:center;
|
|
background:rgba(128,128,128,.15);border:none;cursor:pointer;
|
|
color:var(--text-secondary);transition:background .2s;
|
|
}
|
|
.sp-clear.visible{display:flex}
|
|
.sp-clear svg{width:14px;height:14px}
|
|
.recent-section{margin-bottom:24px}
|
|
.recent-section h3{font-size:14px;font-weight:700;color:var(--text-secondary);margin-bottom:10px;text-transform:uppercase;letter-spacing:.5px}
|
|
.recent-item{
|
|
display:flex;align-items:center;gap:12px;
|
|
padding:10px 0;border-bottom:1px solid var(--border);
|
|
cursor:pointer;transition:opacity .2s;
|
|
}
|
|
.recent-item:hover{opacity:.7}
|
|
.recent-item svg{width:16px;height:16px;color:var(--text-secondary);flex-shrink:0}
|
|
.recent-item span{font-size:14px}
|
|
.trending-tags{display:flex;flex-wrap:wrap;gap:8px}
|
|
.trending-tag{
|
|
padding:8px 14px;border-radius:20px;background:var(--surface);
|
|
border:1px solid var(--border);font-size:13px;font-weight:500;
|
|
color:var(--text-secondary);cursor:pointer;transition:all .2s;
|
|
}
|
|
.trending-tag:hover{border-color:var(--accent);color:var(--accent)}
|
|
|
|
/* -- SAVED PAGE -- */
|
|
.saved-wrap{padding:20px 16px 100px}
|
|
.saved-title{font-size:20px;font-weight:700;margin-bottom:3px}
|
|
.saved-sub{font-size:13px;color:var(--text-secondary);margin-bottom:18px}
|
|
.saved-count{
|
|
display:inline-flex;align-items:center;gap:6px;
|
|
padding:5px 12px;border-radius:20px;
|
|
background:var(--accent-glow);
|
|
font-size:12px;font-weight:700;color:var(--accent);
|
|
margin-bottom:16px;
|
|
}
|
|
.saved-count svg{width:14px;height:14px}
|
|
|
|
/* -- PROFILE PAGE -- */
|
|
.prof-wrap{padding:20px 16px 100px}
|
|
.prof-head{display:flex;flex-direction:column;align-items:center;padding:24px 0 20px}
|
|
.prof-avatar{
|
|
width:76px;height:76px;border-radius:50%;
|
|
background:linear-gradient(135deg,var(--accent),#ff9944);
|
|
display:flex;align-items:center;justify-content:center;
|
|
font-size:30px;font-weight:800;color:#fff;margin-bottom:12px;
|
|
overflow:hidden;
|
|
}
|
|
.prof-avatar img{width:100%;height:100%;object-fit:cover;display:block}
|
|
.prof-name{font-size:19px;font-weight:700;margin-bottom:2px}
|
|
.prof-email{font-size:12.5px;color:var(--text-secondary)}
|
|
.prof-stats{
|
|
display:flex;margin:18px 0;border-radius:var(--radius);
|
|
overflow:hidden;border:1px solid var(--border);
|
|
transition:border-color .35s;
|
|
}
|
|
.prof-stat{flex:1;display:flex;flex-direction:column;align-items:center;padding:14px 8px;background:var(--surface);transition:background .35s}
|
|
.prof-stat+.prof-stat{border-left:1px solid var(--border)}
|
|
.prof-stat-n{font-size:19px;font-weight:800;color:var(--accent)}
|
|
.prof-stat-l{font-size:10.5px;color:var(--text-secondary);margin-top:2px;font-weight:600;text-transform:uppercase;letter-spacing:.3px}
|
|
.prof-menu{display:flex;flex-direction:column;gap:2px}
|
|
.prof-menu-item{
|
|
display:flex;align-items:center;gap:13px;
|
|
padding:13px 16px;border-radius:var(--radius-sm);
|
|
background:var(--surface);cursor:pointer;transition:background .2s;
|
|
border:none;width:100%;color:var(--text);font-size:14.5px;font-weight:500;text-align:left;
|
|
}
|
|
.prof-menu-item:hover{background:var(--surface-hover)}
|
|
.prof-menu-item:active{opacity:.7}
|
|
.prof-menu-item svg{width:20px;height:20px;color:var(--text-secondary);flex-shrink:0}
|
|
.prof-menu-item .arrow{margin-left:auto;width:16px;height:16px;color:rgba(128,128,128,.4)}
|
|
.prof-menu-item.danger{color:var(--red)}
|
|
.prof-menu-item.danger svg{color:var(--red)}
|
|
|
|
/* -- MODAL (bottom sheet) -- */
|
|
.modal-bg{
|
|
position:fixed;inset:0;z-index:200;
|
|
background:var(--overlay);
|
|
backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);
|
|
display:flex;align-items:flex-end;justify-content:center;
|
|
opacity:0;pointer-events:none;transition:opacity .3s;
|
|
}
|
|
.modal-bg.open{opacity:1;pointer-events:auto}
|
|
.modal-sheet{
|
|
width:100%;max-width:480px;max-height:85dvh;
|
|
background:var(--bg);border-radius:20px 20px 0 0;
|
|
overflow:hidden;transform:translateY(100%);
|
|
transition:transform .4s var(--transition),background .35s;
|
|
}
|
|
.modal-bg.open .modal-sheet{transform:translateY(0)}
|
|
.modal-handle{display:flex;justify-content:center;padding:12px 0 4px}
|
|
.modal-handle span{width:36px;height:4px;border-radius:2px;background:rgba(128,128,128,.25)}
|
|
.modal-head{display:flex;align-items:center;justify-content:space-between;padding:6px 20px 16px}
|
|
.modal-title{font-size:19px;font-weight:700}
|
|
.modal-x{
|
|
width:32px;height:32px;border-radius:50%;
|
|
display:flex;align-items:center;justify-content:center;
|
|
background:var(--surface);border:none;cursor:pointer;
|
|
color:var(--text-secondary);transition:background .2s,color .2s;
|
|
}
|
|
.modal-x:hover{background:var(--surface-hover);color:var(--text)}
|
|
.modal-x svg{width:18px;height:18px}
|
|
.modal-body{padding:0 20px 34px;overflow-y:auto;-webkit-overflow-scrolling:touch}
|
|
.s-group{margin-bottom:22px}
|
|
.s-group-title{
|
|
font-size:11px;font-weight:700;color:var(--text-secondary);
|
|
text-transform:uppercase;letter-spacing:.8px;margin-bottom:8px;
|
|
}
|
|
.s-item{
|
|
display:flex;align-items:center;gap:13px;
|
|
padding:13px 14px;border-radius:var(--radius-sm);
|
|
background:var(--surface);cursor:pointer;transition:background .2s;margin-bottom:2px;
|
|
}
|
|
.s-item:hover{background:var(--surface-hover)}
|
|
.s-item:active{opacity:.75}
|
|
.s-item svg.s-ico{width:20px;height:20px;color:var(--accent);flex-shrink:0}
|
|
.s-item-text{flex:1}
|
|
.s-item-label{font-size:14.5px;font-weight:500;line-height:1.3}
|
|
.s-item-desc{font-size:11.5px;color:var(--text-secondary);line-height:1.3;margin-top:1px}
|
|
.s-item .s-arrow{width:16px;height:16px;color:rgba(128,128,128,.4);flex-shrink:0}
|
|
.s-item.danger .s-item-label{color:var(--red)}
|
|
.s-item.danger svg.s-ico{color:var(--red)}
|
|
.toggle{
|
|
position:relative;width:44px;height:26px;
|
|
background:rgba(128,128,128,.25);border-radius:13px;
|
|
cursor:pointer;transition:background .3s;flex-shrink:0;
|
|
}
|
|
.toggle.on{background:var(--accent)}
|
|
.toggle::after{
|
|
content:'';position:absolute;top:3px;left:3px;
|
|
width:20px;height:20px;border-radius:50%;background:#fff;
|
|
box-shadow:0 1px 3px rgba(0,0,0,.2);
|
|
transition:transform .3s var(--transition);
|
|
}
|
|
.toggle.on::after{transform:translateX(18px)}
|
|
.lang-opts{display:flex;flex-direction:column;gap:2px}
|
|
.lang-opt{
|
|
display:flex;align-items:center;gap:12px;
|
|
padding:12px 14px;border-radius:var(--radius-sm);
|
|
background:var(--surface);cursor:pointer;transition:all .2s;
|
|
border:1.5px solid transparent;
|
|
}
|
|
.lang-opt:hover{background:var(--surface-hover)}
|
|
.lang-opt.sel{border-color:var(--accent);background:rgba(255,102,0,.06)}
|
|
.lang-opt span{font-size:14.5px;font-weight:500}
|
|
.lang-chk{margin-left:auto;width:18px;height:18px;color:var(--accent);opacity:0;transition:opacity .2s}
|
|
.lang-opt.sel .lang-chk{opacity:1}
|
|
|
|
/* -- EDIT PROFILE MODAL -- */
|
|
.edit-profile-bg{
|
|
position:fixed;inset:0;z-index:210;
|
|
background:var(--overlay);
|
|
backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);
|
|
display:flex;align-items:flex-end;justify-content:center;
|
|
opacity:0;pointer-events:none;transition:opacity .3s;
|
|
}
|
|
.edit-profile-bg.open{opacity:1;pointer-events:auto}
|
|
.edit-profile-sheet{
|
|
width:100%;max-width:480px;max-height:90dvh;
|
|
background:var(--bg);border-radius:20px 20px 0 0;
|
|
overflow-y:auto;-webkit-overflow-scrolling:touch;
|
|
transform:translateY(100%);
|
|
transition:transform .4s var(--transition),background .35s;
|
|
}
|
|
.edit-profile-bg.open .edit-profile-sheet{transform:translateY(0)}
|
|
.ep-head{display:flex;align-items:center;justify-content:space-between;padding:16px 20px}
|
|
.ep-title{font-size:19px;font-weight:700}
|
|
.ep-close{
|
|
width:32px;height:32px;border-radius:50%;
|
|
display:flex;align-items:center;justify-content:center;
|
|
background:var(--surface);border:none;cursor:pointer;
|
|
color:var(--text-secondary);transition:background .2s,color .2s;
|
|
}
|
|
.ep-close:hover{background:var(--surface-hover);color:var(--text)}
|
|
.ep-close svg{width:18px;height:18px}
|
|
.ep-body{padding:0 20px 34px}
|
|
.ep-avatar-section{display:flex;flex-direction:column;align-items:center;margin-bottom:28px}
|
|
.ep-avatar{
|
|
width:90px;height:90px;border-radius:50%;
|
|
background:linear-gradient(135deg,var(--accent),#ff9944);
|
|
display:flex;align-items:center;justify-content:center;
|
|
font-size:36px;font-weight:800;color:#fff;
|
|
margin-bottom:12px;overflow:hidden;position:relative;
|
|
cursor:pointer;
|
|
}
|
|
.ep-avatar img{width:100%;height:100%;object-fit:cover;display:block}
|
|
.ep-avatar-overlay{
|
|
position:absolute;inset:0;background:rgba(0,0,0,.45);
|
|
display:flex;align-items:center;justify-content:center;
|
|
opacity:0;transition:opacity .2s;
|
|
}
|
|
.ep-avatar:hover .ep-avatar-overlay{opacity:1}
|
|
.ep-avatar-overlay svg{width:24px;height:24px;color:#fff}
|
|
.ep-upload-btn{
|
|
padding:8px 18px;border-radius:20px;
|
|
background:var(--surface);border:1.5px solid var(--border);
|
|
color:var(--accent);font-size:13px;font-weight:600;
|
|
cursor:pointer;transition:background .2s,border-color .2s;
|
|
}
|
|
.ep-upload-btn:hover{background:var(--surface-hover);border-color:var(--accent)}
|
|
.ep-field{margin-bottom:18px}
|
|
.ep-label{font-size:12px;font-weight:700;color:var(--text-secondary);text-transform:uppercase;letter-spacing:.6px;margin-bottom:6px;display:block}
|
|
.ep-input{
|
|
width:100%;padding:12px 14px;
|
|
border-radius:var(--radius-sm);border:1.5px solid var(--border);
|
|
background:var(--surface);color:var(--text);font-size:15px;
|
|
outline:none;transition:border-color .25s,box-shadow .25s,background .35s;
|
|
}
|
|
.ep-input:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-glow);background:var(--input-focus-bg)}
|
|
.ep-input::placeholder{color:var(--text-secondary)}
|
|
.ep-textarea{
|
|
width:100%;padding:12px 14px;min-height:100px;
|
|
border-radius:var(--radius-sm);border:1.5px solid var(--border);
|
|
background:var(--surface);color:var(--text);font-size:15px;
|
|
outline:none;resize:vertical;font-family:inherit;
|
|
transition:border-color .25s,box-shadow .25s,background .35s;
|
|
}
|
|
.ep-textarea:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-glow);background:var(--input-focus-bg)}
|
|
.ep-textarea::placeholder{color:var(--text-secondary)}
|
|
.ep-char-count{font-size:11px;color:var(--text-secondary);text-align:right;margin-top:4px}
|
|
.ep-save{
|
|
width:100%;padding:14px;border:none;border-radius:var(--radius);
|
|
background:linear-gradient(135deg,var(--accent),#e85d00);
|
|
color:#fff;font-size:15px;font-weight:700;
|
|
cursor:pointer;transition:transform .15s,box-shadow .3s;
|
|
margin-top:8px;
|
|
}
|
|
.ep-save:hover{box-shadow:0 6px 20px rgba(255,102,0,.3)}
|
|
.ep-save:active{transform:scale(.97)}
|
|
|
|
/* -- TOAST -- */
|
|
.toast{
|
|
position:fixed;bottom:80px;left:50%;transform:translateX(-50%) translateY(20px);
|
|
z-index:300;padding:10px 20px;border-radius:10px;
|
|
background:var(--surface-alt);border:1px solid var(--border);
|
|
font-size:13px;font-weight:600;color:var(--text);
|
|
opacity:0;pointer-events:none;
|
|
transition:opacity .3s,transform .3s var(--transition);
|
|
white-space:nowrap;
|
|
box-shadow:0 8px 24px rgba(0,0,0,.25);
|
|
}
|
|
.toast.show{opacity:1;transform:translateX(-50%) translateY(0)}
|
|
|
|
/* -- LOGOUT CONFIRM -- */
|
|
.confirm-bg{
|
|
position:fixed;inset:0;z-index:250;
|
|
background:rgba(0,0,0,.6);
|
|
backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);
|
|
display:flex;align-items:center;justify-content:center;
|
|
opacity:0;pointer-events:none;transition:opacity .25s;
|
|
padding:20px;
|
|
}
|
|
.confirm-bg.open{opacity:1;pointer-events:auto}
|
|
.confirm-box{
|
|
width:100%;max-width:320px;background:var(--surface);
|
|
border-radius:var(--radius);padding:24px;text-align:center;
|
|
transform:scale(.92);transition:transform .3s var(--transition),background .35s;
|
|
}
|
|
.confirm-bg.open .confirm-box{transform:scale(1)}
|
|
.confirm-box h3{font-size:17px;font-weight:700;margin-bottom:6px;color:var(--text)}
|
|
.confirm-box p{font-size:13.5px;color:var(--text-secondary);margin-bottom:20px;line-height:1.5}
|
|
.confirm-btns{display:flex;gap:10px}
|
|
.confirm-btn{
|
|
flex:1;padding:12px;border-radius:var(--radius-sm);
|
|
border:none;cursor:pointer;font-size:14px;font-weight:600;
|
|
transition:opacity .2s,transform .15s;
|
|
}
|
|
.confirm-btn:active{transform:scale(.96)}
|
|
.confirm-btn.cancel{background:rgba(128,128,128,.15);color:var(--text)}
|
|
.confirm-btn.cancel:hover{background:rgba(128,128,128,.25)}
|
|
.confirm-btn.logout{background:var(--red);color:#fff}
|
|
.confirm-btn.logout:hover{opacity:.85}
|
|
|
|
/* -- BOTTOM NAV -- */
|
|
.bottom-nav{
|
|
position:fixed;bottom:0;left:0;right:0;z-index:100;
|
|
display:flex;align-items:center;justify-content:space-around;
|
|
height:var(--bottom-h);
|
|
background:color-mix(in srgb, var(--bg) 95%, transparent);
|
|
backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);
|
|
border-top:1px solid var(--border);
|
|
padding-bottom:env(safe-area-inset-bottom,0);
|
|
transition:background .35s,border-color .35s;
|
|
}
|
|
.nav-btn{
|
|
display:flex;flex-direction:column;align-items:center;gap:2px;
|
|
background:none;border:none;cursor:pointer;
|
|
color:var(--text-secondary);font-size:10px;font-weight:600;
|
|
transition:color .2s;padding:6px 14px;
|
|
-webkit-tap-highlight-color:transparent;position:relative;
|
|
}
|
|
.nav-btn:active{opacity:.7}
|
|
.nav-btn.active{color:var(--accent)}
|
|
.nav-btn svg{width:22px;height:22px;transition:transform .2s var(--transition)}
|
|
.nav-btn.active svg{transform:translateY(-1px)}
|
|
.nav-indicator{
|
|
position:absolute;top:0;left:50%;transform:translateX(-50%);
|
|
width:20px;height:2.5px;border-radius:2px;
|
|
background:var(--accent);opacity:0;transition:opacity .25s,width .25s;
|
|
}
|
|
.nav-btn.active .nav-indicator{opacity:1;width:24px}
|
|
|
|
::-webkit-scrollbar{width:0;height:0}
|
|
|
|
@media(min-width:420px){.grid{gap:14px;padding:12px 20px 100px}.card-title{font-size:14px}}
|
|
@media(min-width:600px){
|
|
.header{padding:0 24px}.search-wrap{padding:14px 24px 6px}
|
|
.section-bar{padding:20px 24px 12px}.chips{padding:4px 24px 10px}
|
|
.grid{gap:16px;padding:14px 24px 100px}
|
|
.modal-sheet,.edit-profile-sheet{border-radius:20px;margin:auto;max-height:75dvh}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<!-- SPLASH SCREEN -->
|
|
<div class="splash" id="splashScreen">
|
|
<div class="splash-logo">STREAM</div>
|
|
<div class="splash-bar"><div class="splash-bar-inner"></div></div>
|
|
<div class="splash-tagline">Movies & Series</div>
|
|
</div>
|
|
|
|
<!-- LOGIN SCREEN -->
|
|
<div class="login-screen" id="loginScreen">
|
|
<div class="login-card">
|
|
<div class="login-logo">STREAM</div>
|
|
<div class="login-subtitle" data-i18n="loginSubtitle">Sign in to continue</div>
|
|
<form class="login-form" id="loginForm" autocomplete="off">
|
|
<div class="login-field">
|
|
<input class="login-input" id="loginUser" type="text" placeholder="Username" data-i18n-ph="phUsername" autocomplete="off" required>
|
|
<svg fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
|
|
</div>
|
|
<div class="login-field">
|
|
<input class="login-input" id="loginPass" type="password" placeholder="Password" data-i18n-ph="phPassword" autocomplete="off" required>
|
|
<svg fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
|
|
<button class="login-pw-toggle" type="button" id="pwToggle" aria-label="Show password">
|
|
<svg id="pwIconShow" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
|
|
<svg id="pwIconHide" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24" style="display:none"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"/><line x1="1" y1="1" x2="23" y2="23"/></svg>
|
|
</button>
|
|
</div>
|
|
<div class="login-error" id="loginError" data-i18n="loginError">Invalid username or password</div>
|
|
<button class="login-btn" type="submit" id="loginBtn">
|
|
<span class="btn-text" data-i18n="loginBtnText">Log In</span>
|
|
<div class="login-spinner"></div>
|
|
</button>
|
|
</form>
|
|
<div class="login-divider"><span data-i18n="orContinue">or continue with</span></div>
|
|
<div class="login-social">
|
|
<button class="social-btn" type="button">
|
|
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z"/><path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/><path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/><path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/></svg>
|
|
Google
|
|
</button>
|
|
<button class="social-btn" type="button">
|
|
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M16.365 1.43c0 1.14-.493 2.27-1.177 3.08-.744.9-1.99 1.57-2.987 1.57-.18 0-.36-.02-.53-.06.02-.17.04-.36.04-.56 0-1.12.487-2.16 1.157-2.98C13.612 1.68 14.98 1.05 16.16 1c.07.14.1.29.1.43h.105zm3.24 17.97c-1.14 1.8-2.33 3.6-4.2 3.64-1.84.04-2.43-1.09-4.54-1.09s-2.76 1.05-4.5 1.13c-1.8.08-3.17-1.95-4.32-3.75C.34 16.44-.53 12.04.95 8.99 1.9 6.65 4.08 5.15 6.06 5.12c1.77-.04 3.44 1.19 4.52 1.19s3.08-1.47 5.2-1.26c.89.04 3.37.36 4.97 2.7-.13.08-2.97 1.73-2.94 5.17.04 4.13 3.62 5.5 3.66 5.52-.03.08-.57 1.97-1.82 3.96z"/></svg>
|
|
Apple
|
|
</button>
|
|
</div>
|
|
<div class="login-footer">
|
|
<span data-i18n="noAccount">Don't have an account?</span> <a href="#" id="signUpLink" data-i18n="signUp">Sign Up</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- MAIN APP -->
|
|
<div class="app" id="mainApp">
|
|
<header class="header">
|
|
<div class="logo">STREAM</div>
|
|
<div class="header-actions">
|
|
<button class="icon-btn" id="bellBtn" aria-label="Notifications">
|
|
<svg fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg>
|
|
</button>
|
|
<button class="profile-avatar" id="avatarBtn" aria-label="Open Settings"><span id="avatarContent">A</span></button>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- PAGE: HOME -->
|
|
<section class="page active" id="page-home">
|
|
<div class="search-wrap">
|
|
<div class="search-box">
|
|
<svg class="search-icon" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
|
|
<input class="search-input" id="homeSearch" type="text" placeholder="Search movies and series..." data-i18n-ph="phSearch" autocomplete="off">
|
|
<button class="search-clear" id="homeClear" aria-label="Clear">
|
|
<svg fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" viewBox="0 0 24 24"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="section-bar">
|
|
<h2 class="section-title" id="homeTitle" data-i18n="popularNow">Popular Now</h2>
|
|
<a class="section-link" data-i18n="seeAll">See All</a>
|
|
</div>
|
|
<div class="chips" id="chipBar">
|
|
<button class="chip active" data-genre="all" data-i18n="genreAll">All</button>
|
|
<button class="chip" data-genre="action" data-i18n="genreAction">Action</button>
|
|
<button class="chip" data-genre="drama" data-i18n="genreDrama">Drama</button>
|
|
<button class="chip" data-genre="sci-fi" data-i18n="genreSciFi">Sci-Fi</button>
|
|
<button class="chip" data-genre="thriller" data-i18n="genreThriller">Thriller</button>
|
|
<button class="chip" data-genre="comedy" data-i18n="genreComedy">Comedy</button>
|
|
<button class="chip" data-genre="horror" data-i18n="genreHorror">Horror</button>
|
|
<button class="chip" data-genre="adventure" data-i18n="genreAdventure">Adventure</button>
|
|
</div>
|
|
<div class="grid" id="homeGrid"></div>
|
|
</section>
|
|
|
|
<!-- PAGE: SEARCH -->
|
|
<section class="page" id="page-search">
|
|
<div class="sp-wrap">
|
|
<h2 class="sp-title" data-i18n="searchTitle">Search</h2>
|
|
<div class="sp-input-wrap">
|
|
<svg class="sp-ico" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
|
|
<input class="sp-input" id="spInput" type="text" placeholder="Movies, series, actors..." data-i18n-ph="phSearchFull" autocomplete="off">
|
|
<button class="sp-clear" id="spClear" aria-label="Clear">
|
|
<svg fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" viewBox="0 0 24 24"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
|
</button>
|
|
</div>
|
|
<div id="spDefault">
|
|
<div class="recent-section">
|
|
<h3 data-i18n="recent">Recent</h3>
|
|
<div class="recent-item" data-q="Interstellar">
|
|
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><polyline points="12 8 12 12 14 14"/><circle cx="12" cy="12" r="10"/></svg>
|
|
<span>Interstellar</span>
|
|
</div>
|
|
<div class="recent-item" data-q="The Batman">
|
|
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><polyline points="12 8 12 12 14 14"/><circle cx="12" cy="12" r="10"/></svg>
|
|
<span>The Batman</span>
|
|
</div>
|
|
<div class="recent-item" data-q="Oppenheimer">
|
|
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><polyline points="12 8 12 12 14 14"/><circle cx="12" cy="12" r="10"/></svg>
|
|
<span>Oppenheimer</span>
|
|
</div>
|
|
</div>
|
|
<h3 style="font-size:14px;font-weight:700;color:var(--text-secondary);margin-bottom:10px;text-transform:uppercase;letter-spacing:.5px" data-i18n="trending">Trending</h3>
|
|
<div class="trending-tags">
|
|
<span class="trending-tag" data-q="Dune">Dune</span>
|
|
<span class="trending-tag" data-q="Marvel">Marvel</span>
|
|
<span class="trending-tag" data-q="Sci-Fi">Sci-Fi</span>
|
|
<span class="trending-tag" data-q="Thriller">Thriller</span>
|
|
<span class="trending-tag" data-q="Action">Action</span>
|
|
<span class="trending-tag" data-q="2024">2024</span>
|
|
</div>
|
|
</div>
|
|
<div class="grid compact" id="spGrid"></div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- PAGE: SAVED -->
|
|
<section class="page" id="page-saved">
|
|
<div class="saved-wrap">
|
|
<h2 class="saved-title" data-i18n="savedTitle">Saved</h2>
|
|
<p class="saved-sub" data-i18n="savedSub">Your bookmarked movies and series</p>
|
|
<div class="saved-count" id="savedCount" style="display:none">
|
|
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg>
|
|
<span id="savedNum">0</span> <span data-i18n="savedWord">saved</span>
|
|
</div>
|
|
<div class="grid compact" id="savedGrid"></div>
|
|
<div class="empty-state" id="savedEmpty">
|
|
<svg fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M17.593 3.322c1.1.128 1.907 1.077 1.907 2.185V21L12 17.25 4.5 21V5.507c0-1.108.806-2.057 1.907-2.185a48.507 48.507 0 0 1 11.186 0z"/></svg>
|
|
<h3 data-i18n="noSaved">No saved movies yet</h3>
|
|
<p data-i18n="noSavedDesc">Tap the bookmark icon on any movie card to save it here for later.</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- PAGE: PROFILE -->
|
|
<section class="page" id="page-profile">
|
|
<div class="prof-wrap">
|
|
<div class="prof-head">
|
|
<div class="prof-avatar" id="profAvatar"><span id="profAvatarLetter">A</span></div>
|
|
<div class="prof-name" id="profDisplayName">Guest</div>
|
|
<div class="prof-email" id="profDisplayEmail">guest@stream.app</div>
|
|
</div>
|
|
<div class="prof-stats">
|
|
<div class="prof-stat"><span class="prof-stat-n">24</span><span class="prof-stat-l" data-i18n="watched">Watched</span></div>
|
|
<div class="prof-stat"><span class="prof-stat-n" id="profSavedN">0</span><span class="prof-stat-l" data-i18n="savedLabel">Saved</span></div>
|
|
<div class="prof-stat"><span class="prof-stat-n">3</span><span class="prof-stat-l" data-i18n="lists">Lists</span></div>
|
|
</div>
|
|
<div class="prof-menu">
|
|
<button class="prof-menu-item" id="profEditBtn">
|
|
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
|
|
<span data-i18n="editProfile">Edit Profile</span>
|
|
<svg class="arrow" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg>
|
|
</button>
|
|
<button class="prof-menu-item" id="profSettingsBtn">
|
|
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
|
<span data-i18n="settings">Settings</span>
|
|
<svg class="arrow" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg>
|
|
</button>
|
|
<button class="prof-menu-item">
|
|
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
|
|
<span data-i18n="watchHistory">Watch History</span>
|
|
<svg class="arrow" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg>
|
|
</button>
|
|
<button class="prof-menu-item">
|
|
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
|
|
<span data-i18n="helpSupport">Help & Support</span>
|
|
<svg class="arrow" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg>
|
|
</button>
|
|
<button class="prof-menu-item danger" id="profLogoutBtn">
|
|
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
|
<span data-i18n="logOut">Log Out</span>
|
|
<svg class="arrow" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- BOTTOM NAV -->
|
|
<nav class="bottom-nav" id="bottomNav">
|
|
<button class="nav-btn active" data-page="home" aria-label="Home">
|
|
<span class="nav-indicator"></span>
|
|
<svg fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
|
|
<span data-i18n="navHome">Home</span>
|
|
</button>
|
|
<button class="nav-btn" data-page="search" aria-label="Search">
|
|
<span class="nav-indicator"></span>
|
|
<svg fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
|
|
<span data-i18n="navSearch">Search</span>
|
|
</button>
|
|
<button class="nav-btn" data-page="saved" aria-label="Saved">
|
|
<span class="nav-indicator"></span>
|
|
<svg fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg>
|
|
<span data-i18n="navSaved">Saved</span>
|
|
</button>
|
|
<button class="nav-btn" data-page="profile" aria-label="Profile">
|
|
<span class="nav-indicator"></span>
|
|
<svg fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
|
|
<span data-i18n="navProfile">Profile</span>
|
|
</button>
|
|
</nav>
|
|
</div>
|
|
|
|
<!-- SETTINGS MODAL -->
|
|
<div class="modal-bg" id="settingsModal">
|
|
<div class="modal-sheet">
|
|
<div class="modal-handle"><span></span></div>
|
|
<div class="modal-head">
|
|
<h2 class="modal-title" data-i18n="settings">Settings</h2>
|
|
<button class="modal-x" id="modalX" aria-label="Close">
|
|
<svg fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" viewBox="0 0 24 24"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
|
</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="s-group">
|
|
<div class="s-group-title" data-i18n="account">Account</div>
|
|
<div class="s-item" id="settingsEditProfile">
|
|
<svg class="s-ico" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
|
|
<div class="s-item-text"><div class="s-item-label" data-i18n="editProfile">Edit Profile</div><div class="s-item-desc" data-i18n="editProfileDesc">Name, photo, and bio</div></div>
|
|
<svg class="s-arrow" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg>
|
|
</div>
|
|
<div class="s-item">
|
|
<svg class="s-ico" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
|
|
<div class="s-item-text"><div class="s-item-label" data-i18n="privacy">Privacy & Security</div><div class="s-item-desc" data-i18n="privacyDesc">Password, two-factor auth</div></div>
|
|
<svg class="s-arrow" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg>
|
|
</div>
|
|
</div>
|
|
<div class="s-group">
|
|
<div class="s-group-title" data-i18n="appTheme">App Theme</div>
|
|
<div class="s-item" id="darkModeRow">
|
|
<svg class="s-ico" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
|
|
<div class="s-item-text"><div class="s-item-label" data-i18n="darkMode">Dark Mode</div><div class="s-item-desc" id="darkModeDesc" data-i18n="darkModeOn">Enabled</div></div>
|
|
<div class="toggle on" id="darkModeToggle"></div>
|
|
</div>
|
|
<div class="s-item">
|
|
<svg class="s-ico" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg>
|
|
<div class="s-item-text"><div class="s-item-label" data-i18n="pushNotifications">Push Notifications</div><div class="s-item-desc" data-i18n="pushDesc">New releases and updates</div></div>
|
|
<div class="toggle on" data-toggle="push"></div>
|
|
</div>
|
|
</div>
|
|
<div class="s-group">
|
|
<div class="s-group-title" data-i18n="language">Language</div>
|
|
<div class="lang-opts" id="langOpts">
|
|
<div class="lang-opt sel" data-lang="en">
|
|
<span>English</span>
|
|
<svg class="lang-chk" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>
|
|
</div>
|
|
<div class="lang-opt" data-lang="ru">
|
|
<span>Russian</span>
|
|
<svg class="lang-chk" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="s-group">
|
|
<div class="s-item danger" id="settingsLogout">
|
|
<svg class="s-ico" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
|
<div class="s-item-text"><div class="s-item-label" data-i18n="logOut">Log Out</div><div class="s-item-desc" data-i18n="logOutDesc">Sign out of your account</div></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- EDIT PROFILE MODAL -->
|
|
<div class="edit-profile-bg" id="editProfileModal">
|
|
<div class="edit-profile-sheet">
|
|
<div class="modal-handle"><span></span></div>
|
|
<div class="ep-head">
|
|
<h2 class="ep-title" data-i18n="editProfile">Edit Profile</h2>
|
|
<button class="ep-close" id="epClose" aria-label="Close">
|
|
<svg fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" viewBox="0 0 24 24"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
|
</button>
|
|
</div>
|
|
<div class="ep-body">
|
|
<div class="ep-avatar-section">
|
|
<div class="ep-avatar" id="epAvatar">
|
|
<span id="epAvatarLetter">A</span>
|
|
<div class="ep-avatar-overlay">
|
|
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg>
|
|
</div>
|
|
</div>
|
|
<button class="ep-upload-btn" id="epUploadBtn" data-i18n="uploadPhoto">Upload Photo</button>
|
|
<input type="file" id="epFileInput" accept="image/*" style="display:none">
|
|
</div>
|
|
<div class="ep-field">
|
|
<label class="ep-label" data-i18n="fullName">Full Name</label>
|
|
<input class="ep-input" id="epName" type="text" placeholder="Enter your name" data-i18n-ph="phEnterName">
|
|
</div>
|
|
<div class="ep-field">
|
|
<label class="ep-label" data-i18n="username">Username</label>
|
|
<input class="ep-input" id="epUsername" type="text" placeholder="Enter username" data-i18n-ph="phEnterUsername">
|
|
</div>
|
|
<div class="ep-field">
|
|
<label class="ep-label" data-i18n="bio">Bio</label>
|
|
<textarea class="ep-textarea" id="epBio" placeholder="Tell us about yourself..." data-i18n-ph="phBio" maxlength="200"></textarea>
|
|
<div class="ep-char-count"><span id="epBioCount">0</span>/200</div>
|
|
</div>
|
|
<button class="ep-save" id="epSave" data-i18n="saveChanges">Save Changes</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- LOGOUT CONFIRM -->
|
|
<div class="confirm-bg" id="confirmLogout">
|
|
<div class="confirm-box">
|
|
<h3 data-i18n="logOutQuestion">Log Out?</h3>
|
|
<p data-i18n="logOutConfirmText">Are you sure you want to sign out of your account?</p>
|
|
<div class="confirm-btns">
|
|
<button class="confirm-btn cancel" id="logoutCancel" data-i18n="cancel">Cancel</button>
|
|
<button class="confirm-btn logout" id="logoutConfirm" data-i18n="logOut">Log Out</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- TOAST -->
|
|
<div class="toast" id="toast"></div>
|
|
|
|
<script>
|
|
// ============================================================
|
|
// i18n TRANSLATIONS
|
|
// ============================================================
|
|
var translations = {
|
|
en: {
|
|
loginSubtitle: "Sign in to continue",
|
|
phUsername: "Username",
|
|
phPassword: "Password",
|
|
loginError: "Please fill in all fields",
|
|
loginBtnText: "Log In",
|
|
orContinue: "or continue with",
|
|
noAccount: "Don't have an account?",
|
|
signUp: "Sign Up",
|
|
phSearch: "Search movies and series...",
|
|
phSearchFull: "Movies, series, actors...",
|
|
popularNow: "Popular Now",
|
|
seeAll: "See All",
|
|
genreAll: "All",
|
|
genreAction: "Action",
|
|
genreDrama: "Drama",
|
|
genreSciFi: "Sci-Fi",
|
|
genreThriller: "Thriller",
|
|
genreComedy: "Comedy",
|
|
genreHorror: "Horror",
|
|
genreAdventure: "Adventure",
|
|
searchTitle: "Search",
|
|
recent: "Recent",
|
|
trending: "Trending",
|
|
savedTitle: "Saved",
|
|
savedSub: "Your bookmarked movies and series",
|
|
savedWord: "saved",
|
|
noSaved: "No saved movies yet",
|
|
noSavedDesc: "Tap the bookmark icon on any movie card to save it here for later.",
|
|
watched: "Watched",
|
|
savedLabel: "Saved",
|
|
lists: "Lists",
|
|
editProfile: "Edit Profile",
|
|
settings: "Settings",
|
|
watchHistory: "Watch History",
|
|
helpSupport: "Help & Support",
|
|
logOut: "Log Out",
|
|
navHome: "Home",
|
|
navSearch: "Search",
|
|
navSaved: "Saved",
|
|
navProfile: "Profile",
|
|
account: "Account",
|
|
editProfileDesc: "Name, photo, and bio",
|
|
privacy: "Privacy & Security",
|
|
privacyDesc: "Password, two-factor auth",
|
|
appTheme: "App Theme",
|
|
darkMode: "Dark Mode",
|
|
darkModeOn: "Enabled",
|
|
darkModeOff: "Disabled",
|
|
pushNotifications: "Push Notifications",
|
|
pushDesc: "New releases and updates",
|
|
language: "Language",
|
|
logOutDesc: "Sign out of your account",
|
|
logOutQuestion: "Log Out?",
|
|
logOutConfirmText: "Are you sure you want to sign out of your account?",
|
|
cancel: "Cancel",
|
|
fullName: "Full Name",
|
|
username: "Username",
|
|
bio: "Bio",
|
|
uploadPhoto: "Upload Photo",
|
|
saveChanges: "Save Changes",
|
|
phEnterName: "Enter your name",
|
|
phEnterUsername: "Enter username",
|
|
phBio: "Tell us about yourself...",
|
|
noResults: "No movies found",
|
|
noResultsDesc: "Try adjusting your search or filter.",
|
|
noSearchResults: "No results",
|
|
resultsFor: "Results for",
|
|
savedToast: "Saved",
|
|
removedToast: "Removed",
|
|
profileSaved: "Profile updated",
|
|
signUpSoon: "Sign Up coming soon!",
|
|
socialSoon: "Social login coming soon!",
|
|
langChanged: "Language",
|
|
enabled: "Enabled",
|
|
disabled: "Disabled",
|
|
},
|
|
ru: {
|
|
loginSubtitle: "\u0412\u043e\u0439\u0434\u0438\u0442\u0435, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c",
|
|
phUsername: "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f",
|
|
phPassword: "\u041f\u0430\u0440\u043e\u043b\u044c",
|
|
loginError: "\u0417\u0430\u043f\u043e\u043b\u043d\u0438\u0442\u0435 \u0432\u0441\u0435 \u043f\u043e\u043b\u044f",
|
|
loginBtnText: "\u0412\u043e\u0439\u0442\u0438",
|
|
orContinue: "\u0438\u043b\u0438 \u0432\u043e\u0439\u0442\u0438 \u0447\u0435\u0440\u0435\u0437",
|
|
noAccount: "\u041d\u0435\u0442 \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0430?",
|
|
signUp: "\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f",
|
|
phSearch: "\u041f\u043e\u0438\u0441\u043a \u0444\u0438\u043b\u044c\u043c\u043e\u0432 \u0438 \u0441\u0435\u0440\u0438\u0430\u043b\u043e\u0432...",
|
|
phSearchFull: "\u0424\u0438\u043b\u044c\u043c\u044b, \u0441\u0435\u0440\u0438\u0430\u043b\u044b, \u0430\u043a\u0442\u0451\u0440\u044b...",
|
|
popularNow: "\u041f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u043e\u0435",
|
|
seeAll: "\u0412\u0441\u0435",
|
|
genreAll: "\u0412\u0441\u0435",
|
|
genreAction: "\u0411\u043e\u0435\u0432\u0438\u043a",
|
|
genreDrama: "\u0414\u0440\u0430\u043c\u0430",
|
|
genreSciFi: "\u0424\u0430\u043d\u0442\u0430\u0441\u0442\u0438\u043a\u0430",
|
|
genreThriller: "\u0422\u0440\u0438\u043b\u043b\u0435\u0440",
|
|
genreComedy: "\u041a\u043e\u043c\u0435\u0434\u0438\u044f",
|
|
genreHorror: "\u0423\u0436\u0430\u0441\u044b",
|
|
genreAdventure: "\u041f\u0440\u0438\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f",
|
|
searchTitle: "\u041f\u043e\u0438\u0441\u043a",
|
|
recent: "\u041d\u0435\u0434\u0430\u0432\u043d\u0435\u0435",
|
|
trending: "\u0422\u0440\u0435\u043d\u0434\u044b",
|
|
savedTitle: "\u0421\u043e\u0445\u0440\u0430\u043d\u0451\u043d\u043d\u043e\u0435",
|
|
savedSub: "\u0412\u0430\u0448\u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0451\u043d\u043d\u044b\u0435 \u0444\u0438\u043b\u044c\u043c\u044b",
|
|
savedWord: "\u0441\u043e\u0445\u0440.",
|
|
noSaved: "\u041d\u0435\u0442 \u0441\u043e\u0445\u0440\u0430\u043d\u0451\u043d\u043d\u044b\u0445 \u0444\u0438\u043b\u044c\u043c\u043e\u0432",
|
|
noSavedDesc: "\u041d\u0430\u0436\u043c\u0438\u0442\u0435 \u043d\u0430 \u0437\u0430\u043a\u043b\u0430\u0434\u043a\u0443 \u043d\u0430 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0435 \u0444\u0438\u043b\u044c\u043c\u0430, \u0447\u0442\u043e\u0431\u044b \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c.",
|
|
watched: "\u041f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043e",
|
|
savedLabel: "\u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u043e",
|
|
lists: "\u0421\u043f\u0438\u0441\u043a\u0438",
|
|
editProfile: "\u0420\u0435\u0434. \u043f\u0440\u043e\u0444\u0438\u043b\u044c",
|
|
settings: "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438",
|
|
watchHistory: "\u0418\u0441\u0442\u043e\u0440\u0438\u044f",
|
|
helpSupport: "\u041f\u043e\u043c\u043e\u0449\u044c",
|
|
logOut: "\u0412\u044b\u0439\u0442\u0438",
|
|
navHome: "\u0413\u043b\u0430\u0432\u043d\u0430\u044f",
|
|
navSearch: "\u041f\u043e\u0438\u0441\u043a",
|
|
navSaved: "\u0421\u043e\u0445\u0440.",
|
|
navProfile: "\u041f\u0440\u043e\u0444\u0438\u043b\u044c",
|
|
account: "\u0410\u043a\u043a\u0430\u0443\u043d\u0442",
|
|
editProfileDesc: "\u0418\u043c\u044f, \u0444\u043e\u0442\u043e \u0438 \u0431\u0438\u043e",
|
|
privacy: "\u041a\u043e\u043d\u0444\u0438\u0434\u0435\u043d\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c",
|
|
privacyDesc: "\u041f\u0430\u0440\u043e\u043b\u044c, \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430\u044f",
|
|
appTheme: "\u0422\u0435\u043c\u0430",
|
|
darkMode: "\u0422\u0451\u043c\u043d\u0430\u044f \u0442\u0435\u043c\u0430",
|
|
darkModeOn: "\u0412\u043a\u043b\u044e\u0447\u0435\u043d\u0430",
|
|
darkModeOff: "\u0412\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u0430",
|
|
pushNotifications: "\u0423\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f",
|
|
pushDesc: "\u041d\u043e\u0432\u0438\u043d\u043a\u0438 \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f",
|
|
language: "\u042f\u0437\u044b\u043a",
|
|
logOutDesc: "\u0412\u044b\u0439\u0442\u0438 \u0438\u0437 \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0430",
|
|
logOutQuestion: "\u0412\u044b\u0439\u0442\u0438?",
|
|
logOutConfirmText: "\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0432\u044b\u0439\u0442\u0438?",
|
|
cancel: "\u041e\u0442\u043c\u0435\u043d\u0430",
|
|
fullName: "\u041f\u043e\u043b\u043d\u043e\u0435 \u0438\u043c\u044f",
|
|
username: "\u041b\u043e\u0433\u0438\u043d",
|
|
bio: "\u041e \u0441\u0435\u0431\u0435",
|
|
uploadPhoto: "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u043e\u0442\u043e",
|
|
saveChanges: "\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c",
|
|
phEnterName: "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043c\u044f",
|
|
phEnterUsername: "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043b\u043e\u0433\u0438\u043d",
|
|
phBio: "\u0420\u0430\u0441\u0441\u043a\u0430\u0436\u0438\u0442\u0435 \u043e \u0441\u0435\u0431\u0435...",
|
|
noResults: "\u0424\u0438\u043b\u044c\u043c\u044b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b",
|
|
noResultsDesc: "\u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441.",
|
|
noSearchResults: "\u041d\u0435\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432",
|
|
resultsFor: "\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0434\u043b\u044f",
|
|
savedToast: "\u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u043e",
|
|
removedToast: "\u0423\u0434\u0430\u043b\u0435\u043d\u043e",
|
|
profileSaved: "\u041f\u0440\u043e\u0444\u0438\u043b\u044c \u043e\u0431\u043d\u043e\u0432\u043b\u0451\u043d",
|
|
signUpSoon: "\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u0441\u043a\u043e\u0440\u043e!",
|
|
socialSoon: "\u0421\u043e\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 \u0432\u0445\u043e\u0434 \u0441\u043a\u043e\u0440\u043e!",
|
|
langChanged: "\u042f\u0437\u044b\u043a",
|
|
enabled: "\u0412\u043a\u043b\u044e\u0447\u0435\u043d\u043e",
|
|
disabled: "\u0412\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u043e",
|
|
}
|
|
};
|
|
|
|
var currentLang = "en";
|
|
|
|
function t(key) {
|
|
return (translations[currentLang] && translations[currentLang][key]) || translations.en[key] || key;
|
|
}
|
|
|
|
function applyI18n() {
|
|
document.querySelectorAll("[data-i18n]").forEach(function(el) {
|
|
var key = el.getAttribute("data-i18n");
|
|
el.textContent = t(key);
|
|
});
|
|
document.querySelectorAll("[data-i18n-ph]").forEach(function(el) {
|
|
var key = el.getAttribute("data-i18n-ph");
|
|
el.placeholder = t(key);
|
|
});
|
|
}
|
|
|
|
// ============================================================
|
|
// MOVIE DATA
|
|
// ============================================================
|
|
var movies = [
|
|
{ id:1, title:"Dune: Part Two", year:2024, genre:"sci-fi", rating:8.6, poster:"https://image.tmdb.org/t/p/w500/8b8R8l88Qje9dn9OE8PY05Nez7.jpg" },
|
|
{ id:2, title:"Oppenheimer", year:2023, genre:"drama", rating:8.9, poster:"https://image.tmdb.org/t/p/w500/8Gxv8gSFCU0XGDykEGv7zR1n2ua.jpg" },
|
|
{ id:3, title:"The Batman", year:2022, genre:"action", rating:8.1, poster:"https://image.tmdb.org/t/p/w500/74xTEgt7R36Fpooo50r9T25onhq.jpg" },
|
|
{ id:4, title:"Interstellar", year:2014, genre:"sci-fi", rating:9.0, poster:"https://image.tmdb.org/t/p/w500/gEU2QniE6E77NI6lCU6MxlNBvIx.jpg" },
|
|
{ id:5, title:"Parasite", year:2019, genre:"thriller", rating:8.5, poster:"https://image.tmdb.org/t/p/w500/7IiTTgloJzvGI1TAYymCfbfl3vT.jpg" },
|
|
{ id:6, title:"The Grand Budapest Hotel",year:2014,genre:"comedy", rating:8.1, poster:"https://image.tmdb.org/t/p/w500/eWdyYQreja6JGCzqHWXpWHDrrPo.jpg" },
|
|
{ id:7, title:"Mad Max: Fury Road", year:2015, genre:"action", rating:8.4, poster:"https://image.tmdb.org/t/p/w500/8tZYtuWezp8JbcsvHYO0O46tFbo.jpg" },
|
|
{ id:8, title:"Blade Runner 2049", year:2017, genre:"sci-fi", rating:8.3, poster:"https://image.tmdb.org/t/p/w500/gajva2L0rPYkEWjzgFlBXCAVBE5.jpg" },
|
|
{ id:9, title:"Get Out", year:2017, genre:"horror", rating:8.2, poster:"https://image.tmdb.org/t/p/w500/tFXcEccSQMf3lfhfXKSU9iRBpa3.jpg" },
|
|
{ id:10, title:"John Wick: Chapter 4", year:2023, genre:"action", rating:7.9, poster:"https://image.tmdb.org/t/p/w500/vZloFAK7NmvMGKE7BeLlfcIDEUm.jpg" },
|
|
{ id:11, title:"Everything Everywhere All at Once",year:2022,genre:"adventure",rating:8.7,poster:"https://image.tmdb.org/t/p/w500/w3LxiVYdWWRvEVdn5RYq6jIqkb1.jpg" },
|
|
{ id:12, title:"The Shawshank Redemption",year:1994,genre:"drama", rating:9.3, poster:"https://image.tmdb.org/t/p/w500/9cjIGRQL922ppjWz0uTkRg2jjkm.jpg" },
|
|
{ id:13, title:"Inception", year:2010, genre:"sci-fi", rating:8.8, poster:"https://image.tmdb.org/t/p/w500/ljsZTbVsrQSqZgWeep2B1QiDKuh.jpg" },
|
|
{ id:14, title:"Fight Club", year:1999, genre:"thriller", rating:8.8, poster:"https://image.tmdb.org/t/p/w500/pB8BM7pdSp6B6Ih7QZ4DrQ3PmJK.jpg" },
|
|
{ id:15, title:"The Dark Knight", year:2008, genre:"action", rating:9.0, poster:"https://image.tmdb.org/t/p/w500/qJ2tW6WMUDux911BTUgME9Yf0Ws.jpg" },
|
|
{ id:16, title:"Superbad", year:2007, genre:"comedy", rating:7.6, poster:"https://image.tmdb.org/t/p/w500/ek8e8txUyUwd2BNqj6lFEerJfbq.jpg" },
|
|
{ id:17, title:"A Quiet Place", year:2018, genre:"horror", rating:7.9, poster:"https://image.tmdb.org/t/p/w500/nAU74GmpUk7t5iklEp3bufwDq4n.jpg" },
|
|
{ id:18, title:"Guardians of the Galaxy",year:2014, genre:"adventure", rating:8.0, poster:"https://image.tmdb.org/t/p/w500/r7vmZjiyZw9rpJMQJp0Oz7VnM.jpg" },
|
|
{ id:19, title:"Whiplash", year:2014, genre:"drama", rating:8.5, poster:"https://image.tmdb.org/t/p/w500/7fn624j544nKhRe1XnINGZKSM0P.jpg" },
|
|
{ id:20, title:"Arrival", year:2016, genre:"sci-fi", rating:8.3, poster:"https://image.tmdb.org/t/p/w500/x2FJsf1ElAgr63Y3PNPtJrcmpoe.jpg" },
|
|
];
|
|
|
|
var FALLBACK = "data:image/svg+xml," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="300" height="450" fill="#1a1a1a"><rect width="300" height="450"/><text x="150" y="230" text-anchor="middle" font-family="sans-serif" font-size="16" fill="#444">No Poster</text></svg>');
|
|
|
|
var genreMapEN = {"sci-fi":"Sci-Fi","action":"Action","drama":"Drama","thriller":"Thriller","comedy":"Comedy","horror":"Horror","adventure":"Adventure"};
|
|
var genreMapRU = {"sci-fi":"\u0424\u0430\u043d\u0442\u0430\u0441\u0442\u0438\u043a\u0430","action":"\u0411\u043e\u0435\u0432\u0438\u043a","drama":"\u0414\u0440\u0430\u043c\u0430","thriller":"\u0422\u0440\u0438\u043b\u043b\u0435\u0440","comedy":"\u041a\u043e\u043c\u0435\u0434\u0438\u044f","horror":"\u0423\u0436\u0430\u0441\u044b","adventure":"\u041f\u0440\u0438\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f"};
|
|
|
|
function genreLabel(key) {
|
|
if (currentLang === "ru") return genreMapRU[key] || key;
|
|
return genreMapEN[key] || key;
|
|
}
|
|
|
|
// ============================================================
|
|
// STATE
|
|
// ============================================================
|
|
var activeGenre = "all";
|
|
var homeQuery = "";
|
|
var currentPage = "home";
|
|
var savedIds = new Set();
|
|
var loggedInUser = null;
|
|
var isDarkMode = true;
|
|
var profileData = { name: "", username: "", bio: "", photoUrl: null };
|
|
|
|
// ============================================================
|
|
// DOM HELPERS
|
|
// ============================================================
|
|
var $ = function(s) { return document.querySelector(s); };
|
|
var $$ = function(s) { return document.querySelectorAll(s); };
|
|
|
|
var splashScreen = $("#splashScreen");
|
|
var loginScreen = $("#loginScreen");
|
|
var mainApp = $("#mainApp");
|
|
var loginForm = $("#loginForm");
|
|
var loginUser = $("#loginUser");
|
|
var loginPass = $("#loginPass");
|
|
var loginBtn = $("#loginBtn");
|
|
var loginError = $("#loginError");
|
|
var pwToggle = $("#pwToggle");
|
|
var signUpLink = $("#signUpLink");
|
|
var homeGrid = $("#homeGrid");
|
|
var homeSearch = $("#homeSearch");
|
|
var homeClear = $("#homeClear");
|
|
var homeTitle = $("#homeTitle");
|
|
var chipBar = $("#chipBar");
|
|
var bottomNav = $("#bottomNav");
|
|
var spInput = $("#spInput");
|
|
var spClear = $("#spClear");
|
|
var spGrid = $("#spGrid");
|
|
var spDefault = $("#spDefault");
|
|
var savedGrid = $("#savedGrid");
|
|
var savedEmpty = $("#savedEmpty");
|
|
var savedCount = $("#savedCount");
|
|
var savedNum = $("#savedNum");
|
|
var profSavedN = $("#profSavedN");
|
|
var settingsModal = $("#settingsModal");
|
|
var editProfileModal = $("#editProfileModal");
|
|
var confirmLogout = $("#confirmLogout");
|
|
var toastEl = $("#toast");
|
|
|
|
// ============================================================
|
|
// SPLASH -> LOGIN
|
|
// ============================================================
|
|
setTimeout(function() {
|
|
splashScreen.classList.add("fade-out");
|
|
setTimeout(function() {
|
|
splashScreen.style.display = "none";
|
|
loginScreen.classList.add("visible");
|
|
}, 600);
|
|
}, 3000);
|
|
|
|
// ============================================================
|
|
// PASSWORD TOGGLE
|
|
// ============================================================
|
|
pwToggle.addEventListener("click", function() {
|
|
var isPassword = loginPass.type === "password";
|
|
loginPass.type = isPassword ? "text" : "password";
|
|
$("#pwIconShow").style.display = isPassword ? "none" : "block";
|
|
$("#pwIconHide").style.display = isPassword ? "block" : "none";
|
|
});
|
|
|
|
// ============================================================
|
|
// LOGIN
|
|
// ============================================================
|
|
loginForm.addEventListener("submit", function(e) {
|
|
e.preventDefault();
|
|
var user = loginUser.value.trim();
|
|
var pass = loginPass.value.trim();
|
|
if (!user || !pass) {
|
|
loginError.textContent = t("loginError");
|
|
loginError.classList.add("show");
|
|
return;
|
|
}
|
|
loginError.classList.remove("show");
|
|
loginBtn.classList.add("loading");
|
|
setTimeout(function() {
|
|
loginBtn.classList.remove("loading");
|
|
loggedInUser = user;
|
|
profileData.name = user;
|
|
profileData.username = user;
|
|
var initial = user.charAt(0).toUpperCase();
|
|
updateAvatarsWithInitial(initial);
|
|
$("#profDisplayName").textContent = user;
|
|
$("#profDisplayEmail").textContent = user.toLowerCase().replace(/\s+/g, ".") + "@stream.app";
|
|
loginScreen.classList.add("fade-out");
|
|
setTimeout(function() {
|
|
loginScreen.style.display = "none";
|
|
mainApp.classList.add("visible");
|
|
renderHome();
|
|
updateSavedCounts();
|
|
}, 400);
|
|
}, 1200);
|
|
});
|
|
|
|
function updateAvatarsWithInitial(initial) {
|
|
var content = $("#avatarContent");
|
|
var profLetter = $("#profAvatarLetter");
|
|
var epLetter = $("#epAvatarLetter");
|
|
if (profileData.photoUrl) {
|
|
content.innerHTML = '<img src="' + profileData.photoUrl + '" alt="Avatar">';
|
|
profLetter.innerHTML = '<img src="' + profileData.photoUrl + '" alt="Avatar">';
|
|
epLetter.innerHTML = '<img src="' + profileData.photoUrl + '" alt="Avatar">';
|
|
} else {
|
|
content.textContent = initial;
|
|
profLetter.textContent = initial;
|
|
epLetter.textContent = initial;
|
|
}
|
|
}
|
|
|
|
signUpLink.addEventListener("click", function(e) {
|
|
e.preventDefault();
|
|
showToast(t("signUpSoon"));
|
|
});
|
|
$$(".social-btn").forEach(function(btn) {
|
|
btn.addEventListener("click", function() { showToast(t("socialSoon")); });
|
|
});
|
|
|
|
// ============================================================
|
|
// HELPERS
|
|
// ============================================================
|
|
function buildCard(m) {
|
|
var saved = savedIds.has(m.id);
|
|
return '<article class="card" data-id="' + m.id + '">' +
|
|
'<div class="card-poster-wrap">' +
|
|
'<div class="shimmer"></div>' +
|
|
'<img class="card-poster loading" src="' + m.poster + '" alt="' + m.title + ' poster" loading="lazy" crossorigin="anonymous"' +
|
|
' onerror="this.src=\'' + FALLBACK + '\';this.classList.add(\'loaded\')"' +
|
|
' onload="this.classList.remove(\'loading\');this.classList.add(\'loaded\')">' +
|
|
'<div class="badge">' +
|
|
'<svg class="badge-star" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>' +
|
|
'<span class="badge-num">' + m.rating.toFixed(1) + '</span>' +
|
|
'</div>' +
|
|
'<button class="bookmark' + (saved ? ' saved' : '') + '" onclick="event.stopPropagation();toggleSave(' + m.id + ',this)" aria-label="Save ' + m.title + '">' +
|
|
'<svg class="bm-outline" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg>' +
|
|
'<svg class="bm-filled" fill="currentColor" viewBox="0 0 24 24"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg>' +
|
|
'</button>' +
|
|
'</div>' +
|
|
'<div class="card-info">' +
|
|
'<div class="card-title">' + m.title + '</div>' +
|
|
'<div class="card-meta">' + m.year + ' · ' + genreLabel(m.genre) + '</div>' +
|
|
'</div>' +
|
|
'</article>';
|
|
}
|
|
|
|
function emptyBlock(title, desc) {
|
|
return '<div class="empty-state">' +
|
|
'<svg fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="m15.75 10.5 4.72-4.72a.75.75 0 0 1 1.28.53v11.38a.75.75 0 0 1-1.28.53l-4.72-4.72M4.5 18.75h9a2.25 2.25 0 0 0 2.25-2.25v-9a2.25 2.25 0 0 0-2.25-2.25h-9A2.25 2.25 0 0 0 2.25 7.5v9a2.25 2.25 0 0 0 2.25 2.25Z"/></svg>' +
|
|
'<h3>' + title + '</h3><p>' + desc + '</p></div>';
|
|
}
|
|
|
|
function getGenreTitle() {
|
|
if (activeGenre === "all") return t("popularNow");
|
|
return genreLabel(activeGenre);
|
|
}
|
|
|
|
var toastTimer = null;
|
|
function showToast(msg) {
|
|
toastEl.textContent = msg;
|
|
toastEl.classList.add("show");
|
|
clearTimeout(toastTimer);
|
|
toastTimer = setTimeout(function() { toastEl.classList.remove("show"); }, 1800);
|
|
}
|
|
|
|
function syncBookmarks(id, isSaved) {
|
|
$$('.card[data-id="' + id + '"] .bookmark').forEach(function(btn) {
|
|
if (isSaved) btn.classList.add("saved"); else btn.classList.remove("saved");
|
|
});
|
|
}
|
|
|
|
function updateSavedCounts() {
|
|
var n = savedIds.size;
|
|
profSavedN.textContent = n;
|
|
savedNum.textContent = n;
|
|
savedCount.style.display = n > 0 ? "inline-flex" : "none";
|
|
}
|
|
|
|
// ============================================================
|
|
// BOOKMARK TOGGLE
|
|
// ============================================================
|
|
function toggleSave(id, btn) {
|
|
if (savedIds.has(id)) savedIds.delete(id); else savedIds.add(id);
|
|
var isSaved = savedIds.has(id);
|
|
if (btn) { btn.classList.remove("pop"); void btn.offsetWidth; btn.classList.add("pop"); }
|
|
syncBookmarks(id, isSaved);
|
|
updateSavedCounts();
|
|
var movie = movies.find(function(m) { return m.id === id; });
|
|
showToast((isSaved ? t("savedToast") : t("removedToast")) + ": " + movie.title);
|
|
if (currentPage === "saved") renderSaved();
|
|
}
|
|
|
|
// ============================================================
|
|
// RENDER: HOME
|
|
// ============================================================
|
|
function renderHome() {
|
|
var list = movies.filter(function(m) {
|
|
var gOk = activeGenre === "all" || m.genre === activeGenre;
|
|
var sOk = !homeQuery || m.title.toLowerCase().indexOf(homeQuery.toLowerCase()) !== -1;
|
|
return gOk && sOk;
|
|
});
|
|
if (list.length === 0) {
|
|
homeGrid.innerHTML = emptyBlock(t("noResults"), t("noResultsDesc"));
|
|
return;
|
|
}
|
|
homeGrid.innerHTML = list.map(buildCard).join("");
|
|
}
|
|
|
|
// ============================================================
|
|
// RENDER: SEARCH
|
|
// ============================================================
|
|
function renderSearchResults(q) {
|
|
if (!q) { spGrid.innerHTML = ""; spDefault.style.display = "block"; return; }
|
|
spDefault.style.display = "none";
|
|
var lo = q.toLowerCase();
|
|
var list = movies.filter(function(m) {
|
|
return m.title.toLowerCase().indexOf(lo) !== -1 ||
|
|
m.genre.toLowerCase().indexOf(lo) !== -1 ||
|
|
String(m.year).indexOf(lo) !== -1;
|
|
});
|
|
if (list.length === 0) {
|
|
spGrid.innerHTML = emptyBlock(t("noSearchResults"), '"' + q + '"');
|
|
return;
|
|
}
|
|
spGrid.innerHTML = list.map(buildCard).join("");
|
|
}
|
|
|
|
// ============================================================
|
|
// RENDER: SAVED
|
|
// ============================================================
|
|
function renderSaved() {
|
|
var list = movies.filter(function(m) { return savedIds.has(m.id); });
|
|
if (list.length === 0) {
|
|
savedGrid.innerHTML = "";
|
|
savedEmpty.style.display = "flex";
|
|
} else {
|
|
savedEmpty.style.display = "none";
|
|
savedGrid.innerHTML = list.map(buildCard).join("");
|
|
}
|
|
updateSavedCounts();
|
|
}
|
|
|
|
// ============================================================
|
|
// HOME SEARCH
|
|
// ============================================================
|
|
homeSearch.addEventListener("input", function(e) {
|
|
homeQuery = e.target.value.trim();
|
|
homeClear.classList.toggle("visible", homeQuery.length > 0);
|
|
homeTitle.textContent = homeQuery ? t("resultsFor") + ' "' + homeQuery + '"' : getGenreTitle();
|
|
homeTitle.removeAttribute("data-i18n");
|
|
renderHome();
|
|
});
|
|
homeClear.addEventListener("click", function() {
|
|
homeSearch.value = ""; homeQuery = "";
|
|
homeClear.classList.remove("visible");
|
|
homeTitle.textContent = getGenreTitle();
|
|
homeTitle.setAttribute("data-i18n", "popularNow");
|
|
homeSearch.focus(); renderHome();
|
|
});
|
|
|
|
// ============================================================
|
|
// CHIPS
|
|
// ============================================================
|
|
chipBar.addEventListener("click", function(e) {
|
|
var chip = e.target.closest(".chip");
|
|
if (!chip) return;
|
|
$$(".chip").forEach(function(c) { c.classList.remove("active"); });
|
|
chip.classList.add("active");
|
|
activeGenre = chip.dataset.genre;
|
|
if (!homeQuery) { homeTitle.textContent = getGenreTitle(); }
|
|
renderHome();
|
|
});
|
|
|
|
// ============================================================
|
|
// SEARCH PAGE
|
|
// ============================================================
|
|
spInput.addEventListener("input", function(e) {
|
|
var v = e.target.value.trim();
|
|
spClear.classList.toggle("visible", v.length > 0);
|
|
renderSearchResults(v);
|
|
});
|
|
spClear.addEventListener("click", function() {
|
|
spInput.value = ""; spClear.classList.remove("visible");
|
|
renderSearchResults(""); spInput.focus();
|
|
});
|
|
$$(".recent-item, .trending-tag").forEach(function(el) {
|
|
el.addEventListener("click", function() {
|
|
var q = el.dataset.q;
|
|
spInput.value = q; spClear.classList.add("visible");
|
|
renderSearchResults(q);
|
|
});
|
|
});
|
|
|
|
// ============================================================
|
|
// SPA NAVIGATION
|
|
// ============================================================
|
|
function navigateTo(page) {
|
|
currentPage = page;
|
|
$$(".page").forEach(function(s) { s.classList.remove("active"); });
|
|
var target = $("#page-" + page);
|
|
if (target) target.classList.add("active");
|
|
$$(".nav-btn").forEach(function(btn) {
|
|
if (btn.dataset.page === page) btn.classList.add("active"); else btn.classList.remove("active");
|
|
});
|
|
if (page === "search") setTimeout(function() { spInput.focus(); }, 120);
|
|
if (page === "saved") renderSaved();
|
|
if (page === "profile") updateSavedCounts();
|
|
window.scrollTo({ top: 0, behavior: "smooth" });
|
|
}
|
|
bottomNav.addEventListener("click", function(e) {
|
|
var btn = e.target.closest(".nav-btn");
|
|
if (!btn) return;
|
|
navigateTo(btn.dataset.page);
|
|
});
|
|
|
|
// ============================================================
|
|
// SETTINGS MODAL
|
|
// ============================================================
|
|
function openSettings() {
|
|
settingsModal.classList.add("open");
|
|
document.body.style.overflow = "hidden";
|
|
}
|
|
function closeSettings() {
|
|
settingsModal.classList.remove("open");
|
|
document.body.style.overflow = "";
|
|
}
|
|
$("#avatarBtn").addEventListener("click", openSettings);
|
|
$("#bellBtn").addEventListener("click", function() { showToast(t("pushNotifications")); });
|
|
$("#profSettingsBtn").addEventListener("click", openSettings);
|
|
$("#modalX").addEventListener("click", closeSettings);
|
|
settingsModal.addEventListener("click", function(e) { if (e.target === settingsModal) closeSettings(); });
|
|
|
|
// ============================================================
|
|
// DARK MODE TOGGLE
|
|
// ============================================================
|
|
var darkModeToggle = $("#darkModeToggle");
|
|
var darkModeDesc = $("#darkModeDesc");
|
|
|
|
function setTheme(dark) {
|
|
isDarkMode = dark;
|
|
if (dark) {
|
|
document.documentElement.classList.remove("light");
|
|
darkModeToggle.classList.add("on");
|
|
darkModeDesc.textContent = t("darkModeOn");
|
|
darkModeDesc.setAttribute("data-i18n", "darkModeOn");
|
|
document.querySelector('meta[name="theme-color"]').content = "#141414";
|
|
} else {
|
|
document.documentElement.classList.add("light");
|
|
darkModeToggle.classList.remove("on");
|
|
darkModeDesc.textContent = t("darkModeOff");
|
|
darkModeDesc.setAttribute("data-i18n", "darkModeOff");
|
|
document.querySelector('meta[name="theme-color"]').content = "#f5f5f5";
|
|
}
|
|
}
|
|
|
|
darkModeToggle.addEventListener("click", function() {
|
|
setTheme(!isDarkMode);
|
|
showToast(t("darkMode") + ": " + (isDarkMode ? t("enabled") : t("disabled")));
|
|
});
|
|
|
|
// push toggle (non-functional but interactive)
|
|
$$(".toggle[data-toggle]").forEach(function(tg) {
|
|
tg.addEventListener("click", function() {
|
|
tg.classList.toggle("on");
|
|
showToast((tg.classList.contains("on") ? t("enabled") : t("disabled")));
|
|
});
|
|
});
|
|
|
|
// ============================================================
|
|
// LANGUAGE SWITCHER
|
|
// ============================================================
|
|
$("#langOpts").addEventListener("click", function(e) {
|
|
var opt = e.target.closest(".lang-opt");
|
|
if (!opt) return;
|
|
var lang = opt.dataset.lang;
|
|
if (lang === currentLang) return;
|
|
currentLang = lang;
|
|
$$(".lang-opt").forEach(function(o) { o.classList.remove("sel"); });
|
|
opt.classList.add("sel");
|
|
applyI18n();
|
|
// Re-render dynamic content with new language
|
|
if (!homeQuery) homeTitle.textContent = getGenreTitle();
|
|
renderHome();
|
|
if (currentPage === "saved") renderSaved();
|
|
if (spInput.value.trim()) renderSearchResults(spInput.value.trim());
|
|
showToast(t("langChanged") + ": " + opt.querySelector("span").textContent);
|
|
});
|
|
|
|
// ============================================================
|
|
// EDIT PROFILE MODAL
|
|
// ============================================================
|
|
function openEditProfile() {
|
|
closeSettings();
|
|
setTimeout(function() {
|
|
var epName = $("#epName");
|
|
var epUsername = $("#epUsername");
|
|
var epBio = $("#epBio");
|
|
epName.value = profileData.name || "";
|
|
epUsername.value = profileData.username || "";
|
|
epBio.value = profileData.bio || "";
|
|
$("#epBioCount").textContent = (profileData.bio || "").length;
|
|
editProfileModal.classList.add("open");
|
|
document.body.style.overflow = "hidden";
|
|
}, 250);
|
|
}
|
|
|
|
function closeEditProfile() {
|
|
editProfileModal.classList.remove("open");
|
|
document.body.style.overflow = "";
|
|
}
|
|
|
|
// Open triggers
|
|
$("#settingsEditProfile").addEventListener("click", openEditProfile);
|
|
$("#profEditBtn").addEventListener("click", openEditProfile);
|
|
$("#epClose").addEventListener("click", closeEditProfile);
|
|
editProfileModal.addEventListener("click", function(e) { if (e.target === editProfileModal) closeEditProfile(); });
|
|
|
|
// Bio character count
|
|
$("#epBio").addEventListener("input", function() {
|
|
$("#epBioCount").textContent = this.value.length;
|
|
});
|
|
|
|
// Photo upload
|
|
var epFileInput = $("#epFileInput");
|
|
$("#epUploadBtn").addEventListener("click", function() { epFileInput.click(); });
|
|
$("#epAvatar").addEventListener("click", function() { epFileInput.click(); });
|
|
|
|
epFileInput.addEventListener("change", function() {
|
|
var file = this.files[0];
|
|
if (!file) return;
|
|
var reader = new FileReader();
|
|
reader.onload = function(e) {
|
|
profileData.photoUrl = e.target.result;
|
|
var initial = (profileData.name || "A").charAt(0).toUpperCase();
|
|
updateAvatarsWithInitial(initial);
|
|
};
|
|
reader.readAsDataURL(file);
|
|
});
|
|
|
|
// Save profile
|
|
$("#epSave").addEventListener("click", function() {
|
|
var name = $("#epName").value.trim();
|
|
var username = $("#epUsername").value.trim();
|
|
var bio = $("#epBio").value.trim();
|
|
if (name) profileData.name = name;
|
|
if (username) profileData.username = username;
|
|
profileData.bio = bio;
|
|
// Update display
|
|
$("#profDisplayName").textContent = profileData.name || loggedInUser || "Guest";
|
|
var emailBase = (profileData.username || profileData.name || "guest").toLowerCase().replace(/\s+/g, ".");
|
|
$("#profDisplayEmail").textContent = emailBase + "@stream.app";
|
|
var initial = (profileData.name || "A").charAt(0).toUpperCase();
|
|
updateAvatarsWithInitial(initial);
|
|
closeEditProfile();
|
|
showToast(t("profileSaved"));
|
|
});
|
|
|
|
// ============================================================
|
|
// LOGOUT
|
|
// ============================================================
|
|
function openLogoutConfirm() {
|
|
closeSettings();
|
|
setTimeout(function() {
|
|
confirmLogout.classList.add("open");
|
|
document.body.style.overflow = "hidden";
|
|
}, 200);
|
|
}
|
|
function closeLogoutConfirm() {
|
|
confirmLogout.classList.remove("open");
|
|
document.body.style.overflow = "";
|
|
}
|
|
function performLogout() {
|
|
closeLogoutConfirm();
|
|
loggedInUser = null;
|
|
savedIds.clear();
|
|
updateSavedCounts();
|
|
homeQuery = "";
|
|
homeSearch.value = "";
|
|
activeGenre = "all";
|
|
profileData = { name: "", username: "", bio: "", photoUrl: null };
|
|
$$(".chip").forEach(function(c) { c.classList.remove("active"); });
|
|
$$(".chip")[0].classList.add("active");
|
|
homeTitle.textContent = t("popularNow");
|
|
navigateTo("home");
|
|
mainApp.style.transition = "opacity .35s ease";
|
|
mainApp.style.opacity = "0";
|
|
setTimeout(function() {
|
|
mainApp.classList.remove("visible");
|
|
mainApp.style.opacity = "";
|
|
loginScreen.style.display = "";
|
|
loginScreen.classList.remove("fade-out");
|
|
loginScreen.classList.add("visible");
|
|
loginUser.value = "";
|
|
loginPass.value = "";
|
|
loginError.classList.remove("show");
|
|
}, 350);
|
|
}
|
|
|
|
$("#settingsLogout").addEventListener("click", openLogoutConfirm);
|
|
$("#profLogoutBtn").addEventListener("click", openLogoutConfirm);
|
|
$("#logoutCancel").addEventListener("click", closeLogoutConfirm);
|
|
confirmLogout.addEventListener("click", function(e) { if (e.target === confirmLogout) closeLogoutConfirm(); });
|
|
$("#logoutConfirm").addEventListener("click", performLogout);
|
|
|
|
// ============================================================
|
|
// GLOBAL KEYBOARD
|
|
// ============================================================
|
|
document.addEventListener("keydown", function(e) {
|
|
if (e.key === "Escape") {
|
|
if (confirmLogout.classList.contains("open")) { closeLogoutConfirm(); return; }
|
|
if (editProfileModal.classList.contains("open")) { closeEditProfile(); return; }
|
|
if (settingsModal.classList.contains("open")) { closeSettings(); return; }
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|