39050-vm/public/stream.html
2026-03-08 07:59:52 +00:00

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 + ' &middot; ' + 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>