Files
2026-07-14 00:01:48 +03:00

2019 lines
74 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@extends('frontend.layouts.app')
@section('title', 'Keşfet — Sana Uygun Animeyi Bul')
@section('meta_description', 'Kaydır, beğen, listene ekle. Binlerce anime arasından sana uygun olanı saniyeler içinde bul.')
@push('styles')
<style>
/* ── Page shell — hide footer/mobile-nav on this page ─── */
footer,
.mob-nav,
#mobile-nav,
#mob-bottom-nav,
[class*="bottom-nav"] { display: none !important; }
/* ════════════════════════════════════════════════════════
DISCOVER PAGE
════════════════════════════════════════════════════════ */
.dsc-page {
min-height: 100vh;
display: grid;
grid-template-rows: auto auto auto 1fr auto;
align-items: start;
background: var(--bg);
position: relative;
overflow: hidden;
padding-top: var(--nav-h);
padding-bottom: 110px;
}
/* Ambient glow orbs */
.dsc-orb {
position: fixed;
border-radius: 50%;
pointer-events: none;
z-index: 0;
filter: blur(80px);
}
.dsc-orb-1 {
width: 500px; height: 500px;
top: -160px; left: -160px;
background: radial-gradient(circle, rgba(0,245,255,.055), transparent 65%);
animation: orbDrift1 12s ease-in-out infinite alternate;
}
.dsc-orb-2 {
width: 420px; height: 420px;
bottom: 60px; right: -120px;
background: radial-gradient(circle, rgba(255,45,125,.05), transparent 65%);
animation: orbDrift2 14s ease-in-out infinite alternate;
}
@keyframes orbDrift1 { to { transform: translate(40px, 30px); } }
@keyframes orbDrift2 { to { transform: translate(-30px, -20px); } }
/* ── Header ─────────────────────────────────────────── */
.dsc-header {
position: relative;
z-index: 10;
display: flex;
align-items: center;
gap: 12px;
padding: 20px 20px 0;
max-width: 480px;
width: 100%;
margin: 0 auto;
}
.dsc-back {
width: 40px; height: 40px;
border-radius: 50%;
background: rgba(255,255,255,.04);
border: 1px solid rgba(255,255,255,.08);
display: flex; align-items: center; justify-content: center;
color: var(--text3);
text-decoration: none;
flex-shrink: 0;
transition: all .2s;
}
.dsc-back:hover { background: rgba(0,245,255,.08); border-color: rgba(0,245,255,.3); color: var(--accent2); }
.dsc-heading { flex: 1 }
.dsc-heading-main {
display: block;
font-family: var(--font-display);
font-size: .82rem;
font-weight: 700;
letter-spacing: .12em;
text-transform: uppercase;
background: linear-gradient(90deg, var(--accent2) 0%, var(--accent3) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.dsc-heading-sub { font-size: .62rem; color: var(--text3); display: block; margin-top: 1px; }
.dsc-filter-toggle {
width: 40px; height: 40px;
border-radius: 50%;
background: rgba(255,255,255,.04);
border: 1px solid rgba(255,255,255,.08);
color: var(--text3);
display: flex; align-items: center; justify-content: center;
cursor: pointer;
transition: all .2s;
position: relative;
font-size: .9rem;
}
.dsc-filter-toggle:hover,
.dsc-filter-toggle.active { border-color: rgba(0,245,255,.3); color: var(--accent2); background: rgba(0,245,255,.06); }
.dsc-filter-dot {
position: absolute; top: 8px; right: 8px;
width: 7px; height: 7px; border-radius: 50%;
background: var(--accent); display: none;
}
.dsc-filter-dot.on { display: block; }
/* ── Filter panel ───────────────────────────────────── */
.dsc-filters {
position: relative; z-index: 9;
max-width: 480px; width: 100%;
margin: 0 auto;
overflow: hidden;
max-height: 0;
transition: max-height .35s cubic-bezier(.4,0,.2,1);
padding: 0 20px;
}
.dsc-filters.open { max-height: 240px; padding: 14px 20px 2px; }
.dsc-fgroup { margin-bottom: 12px; }
.dsc-fgroup label {
display: block; font-size: .58rem; text-transform: uppercase;
letter-spacing: .14em; color: var(--text3); margin-bottom: 7px; font-weight: 700;
}
.dsc-fpills { display: flex; gap: 6px; flex-wrap: wrap; }
.dfp {
padding: 5px 13px; border-radius: 999px; font-size: .67rem;
background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.07);
color: var(--text3); cursor: pointer; transition: all .15s;
white-space: nowrap; font-family: var(--font);
}
.dfp:hover { border-color: rgba(0,245,255,.3); color: var(--text2); }
.dfp.active { background: rgba(0,245,255,.1); border-color: var(--accent2); color: var(--accent2); }
/* ── Progress bar ───────────────────────────────────── */
.dsc-progress {
position: relative; z-index: 9;
max-width: 480px; width: 100%;
margin: 12px auto 0;
padding: 0 20px;
display: flex; align-items: center; gap: 10px;
}
.dsc-progress-bar {
flex: 1; height: 2px; border-radius: 999px;
background: rgba(255,255,255,.06); overflow: hidden;
}
.dsc-progress-fill {
height: 100%;
background: linear-gradient(90deg, var(--accent2), var(--accent3));
border-radius: 999px;
transition: width .45s cubic-bezier(.4,0,.2,1);
width: 0%;
}
.dsc-count-txt { font-size: .6rem; color: var(--text3); white-space: nowrap; min-width: 48px; text-align: right; }
/* ── Card stack area ────────────────────────────────── */
.dsc-stage {
position: relative; z-index: 5;
display: flex; align-items: center; justify-content: center;
padding: 16px 20px 0;
width: 100%;
}
.dsc-stack {
position: relative;
width: min(100%, 400px);
height: min(74vw, 520px);
}
/* ── Cards ──────────────────────────────────────────── */
.dsc-card {
position: absolute; inset: 0;
border-radius: 24px;
overflow: hidden;
box-shadow: 0 28px 70px rgba(0,0,0,.7), 0 0 0 1px rgba(255,255,255,.05);
cursor: grab;
user-select: none;
transform-origin: center 115%;
will-change: transform, opacity;
-webkit-tap-highlight-color: transparent;
}
.dsc-card:active { cursor: grabbing; }
/* Entrance animation */
@keyframes cardEnter {
from { opacity: 0; transform: translateY(32px) scale(.94); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
.dsc-card.entering { animation: cardEnter .42s cubic-bezier(.175,.885,.32,1.275) forwards; }
/* Stack positions */
.dsc-card.pos-0 { z-index: 10; transition: transform .32s cubic-bezier(.175,.885,.32,1.275); }
.dsc-card.pos-1 {
z-index: 9; pointer-events: none;
transform: scale(.95) translateY(18px);
transition: transform .32s cubic-bezier(.175,.885,.32,1.275);
}
.dsc-card.pos-2 {
z-index: 8; pointer-events: none;
transform: scale(.90) translateY(36px);
opacity: .55;
transition: transform .32s cubic-bezier(.175,.885,.32,1.275), opacity .32s;
}
/* Fly-out animations */
.dsc-card.going-right {
transition: transform .4s cubic-bezier(.55,.055,.675,.19), opacity .4s !important;
transform: translateX(160%) rotate(28deg) !important;
opacity: 0 !important; pointer-events: none;
}
.dsc-card.going-left {
transition: transform .4s cubic-bezier(.55,.055,.675,.19), opacity .4s !important;
transform: translateX(-160%) rotate(-28deg) !important;
opacity: 0 !important; pointer-events: none;
}
/* Card image */
.dsc-card-img { position: absolute; inset: 0; }
.dsc-card-img img {
width: 100%; height: 100%; object-fit: cover;
display: block; pointer-events: none; user-select: none; -webkit-user-drag: none;
}
.dsc-card-gradient {
position: absolute; inset: 0;
background: linear-gradient(
180deg,
rgba(6,6,13,.12) 0%,
rgba(6,6,13,0) 30%,
rgba(6,6,13,.42) 60%,
rgba(6,6,13,.9) 80%,
rgba(6,6,13,.98) 100%
);
}
/* Color tint overlay during drag */
.dsc-card-tint {
position: absolute; inset: 0; pointer-events: none; opacity: 0;
transition: opacity .08s;
z-index: 4;
}
.dsc-card.hint-like .dsc-card-tint { background: rgba(0,245,255,.08); opacity: 1; }
.dsc-card.hint-skip .dsc-card-tint { background: rgba(255,45,125,.08); opacity: 1; }
/* Swipe stamps */
.dsc-stamp {
position: absolute; top: 24px; z-index: 6;
padding: 6px 16px; border-radius: 10px;
font-size: .74rem; font-weight: 800;
letter-spacing: .1em; text-transform: uppercase;
opacity: 0; transition: opacity .1s; pointer-events: none;
backdrop-filter: blur(8px);
}
.dsc-stamp-like { right: 20px; border: 2.5px solid #00f5ff; color: #00f5ff; background: rgba(0,245,255,.12); transform: rotate(7deg); }
.dsc-stamp-skip { left: 20px; border: 2.5px solid #ff2d7d; color: #ff2d7d; background: rgba(255,45,125,.1); transform: rotate(-7deg); }
.dsc-card.hint-like .dsc-stamp-like { opacity: 1; }
.dsc-card.hint-skip .dsc-stamp-skip { opacity: 1; }
/* No-image fallback */
.dsc-card-noimg {
position: absolute; inset: 0;
background: linear-gradient(145deg, var(--surface2), #0e0e1c);
display: flex; align-items: center; justify-content: center;
font-size: 4rem; color: rgba(255,255,255,.08);
}
/* Card info layer */
.dsc-card-info {
position: absolute; bottom: 0; left: 0; right: 0;
padding: 20px 18px 18px;
z-index: 5;
}
.dsc-card-genres { display: flex; gap: 5px; flex-wrap: wrap; margin-bottom: 9px; }
.dsc-genre-pill {
font-size: .58rem; padding: 3px 10px; border-radius: 999px;
background: rgba(0,245,255,.1); border: 1px solid rgba(0,245,255,.2);
color: var(--accent2); font-weight: 600; letter-spacing: .04em;
}
.dsc-card-title {
font-size: clamp(1.2rem, 5.5vw, 1.55rem);
font-weight: 800; color: #fff; line-height: 1.15;
margin: 0 0 7px;
text-shadow: 0 2px 14px rgba(0,0,0,.5);
}
.dsc-card-meta {
display: flex; align-items: center; gap: 8px;
font-size: .68rem; color: rgba(240,244,255,.5);
flex-wrap: wrap; margin-bottom: 8px;
}
.dsc-card-star {
display: flex; align-items: center; gap: 3px;
color: #fbbf24; font-weight: 700;
}
.dsc-card-star svg { width: 10px; height: 10px; fill: #fbbf24; }
.dsc-card-hook {
font-size: .75rem; color: rgba(240,244,255,.65); font-style: italic;
line-height: 1.55; margin: 0;
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* Tap-hint pill (shows briefly on first load) */
.dsc-hint-pill {
position: absolute; bottom: 20px; left: 50%; z-index: 6;
transform: translateX(-50%);
background: rgba(255,255,255,.08); backdrop-filter: blur(10px);
border: 1px solid rgba(255,255,255,.1); border-radius: 999px;
font-size: .62rem; color: rgba(255,255,255,.5);
padding: 5px 14px; white-space: nowrap; pointer-events: none;
animation: hintFadeOut 3s ease forwards 2.5s;
}
@keyframes hintFadeOut { to { opacity: 0; } }
/* ── Action bar ─────────────────────────────────────── */
.dsc-actions {
position: fixed; bottom: 0; left: 0; right: 0;
display: flex; align-items: center; justify-content: center;
gap: 16px;
padding: 16px 0 calc(22px + env(safe-area-inset-bottom, 0px));
background: linear-gradient(to top, rgba(6,6,13,1) 55%, transparent);
z-index: 50; pointer-events: none;
}
.dsc-actions > * { pointer-events: auto; }
.dsc-btn {
display: flex; align-items: center; justify-content: center;
border: none; border-radius: 50%; cursor: pointer;
transition: transform .15s cubic-bezier(.175,.885,.32,1.275), box-shadow .2s;
flex-shrink: 0; -webkit-tap-highlight-color: transparent; outline: none;
}
.dsc-btn:active { transform: scale(.85) !important; }
.dsc-btn svg, .dsc-btn i { pointer-events: none; flex-shrink: 0; }
.dsc-btn:disabled { opacity: .3; cursor: default; pointer-events: none; }
.dsc-btn-skip {
width: 56px; height: 56px;
background: rgba(255,45,125,.08);
border: 1.5px solid rgba(255,45,125,.3);
color: #ff2d7d;
}
.dsc-btn-skip:hover:not(:disabled) {
border-color: #ff2d7d;
box-shadow: 0 0 28px rgba(255,45,125,.25), 0 4px 12px rgba(0,0,0,.3);
transform: scale(1.09);
}
.dsc-btn-info {
width: 44px; height: 44px;
background: rgba(255,255,255,.05);
border: 1.5px solid rgba(255,255,255,.1);
color: var(--text3); font-size: 1rem;
}
.dsc-btn-info:hover:not(:disabled) {
border-color: rgba(184,77,255,.4); color: var(--accent3);
transform: scale(1.09);
}
.dsc-btn-like {
width: 70px; height: 70px;
background: linear-gradient(145deg, rgba(0,245,255,.14), rgba(0,245,255,.05));
border: 1.5px solid rgba(0,245,255,.4);
color: var(--accent2);
box-shadow: 0 0 30px rgba(0,245,255,.12), 0 4px 16px rgba(0,0,0,.3);
}
.dsc-btn-like:hover:not(:disabled) {
border-color: var(--accent2);
box-shadow: 0 0 50px rgba(0,245,255,.3), 0 4px 16px rgba(0,0,0,.3);
transform: scale(1.1);
}
@keyframes likePulse {
0% { transform: scale(1); }
35% { transform: scale(1.24); }
100% { transform: scale(1); }
}
.dsc-btn-like.pulse { animation: likePulse .3s ease; }
/* ── Keyboard hint ──────────────────────────────────── */
.dsc-kbd {
position: fixed; bottom: 118px; left: 50%; transform: translateX(-50%);
display: flex; gap: 20px; font-size: .6rem; color: var(--text3);
pointer-events: none; z-index: 50; white-space: nowrap;
}
.dsc-kbd kbd {
background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.1);
border-radius: 5px; padding: 2px 7px; font-family: var(--font);
font-size: .58rem; margin-right: 4px; color: var(--text2);
}
@media (max-width: 768px) { .dsc-kbd { display: none; } }
/* ── Loading / Empty states ─────────────────────────── */
.dsc-state {
position: absolute; inset: 0;
display: flex; flex-direction: column;
align-items: center; justify-content: center;
text-align: center; padding: 24px; z-index: 20;
border-radius: 24px;
overflow: hidden;
}
/* ── Splash loader ──────────────────────────────────── */
.dsc-splash {
position: absolute; inset: 0;
background: linear-gradient(160deg, #0a0a18 0%, #0d0d22 50%, #080814 100%);
border-radius: 24px;
display: flex; flex-direction: column;
align-items: center; justify-content: center;
gap: 0; z-index: 21; overflow: hidden;
padding: 28px 24px;
}
/* Floating icons container */
.splash-floats {
position: absolute; inset: 0; pointer-events: none; overflow: hidden;
}
.sf-icon {
position: absolute; bottom: -40px;
font-size: 1.4rem; opacity: 0;
animation: sfFloat linear infinite;
filter: drop-shadow(0 0 8px currentColor);
}
@keyframes sfFloat {
0% { opacity: 0; transform: translateY(0) rotate(0deg) scale(.7); }
10% { opacity: .9; }
85% { opacity: .6; }
100% { opacity: 0; transform: translateY(-120%) rotate(25deg) scale(1.1); }
}
/* Logo mark */
.splash-logo {
font-family: var(--font-display);
font-size: .7rem; font-weight: 700;
letter-spacing: .22em; text-transform: uppercase;
background: linear-gradient(90deg, var(--accent2), var(--accent3));
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 20px; opacity: .7;
}
/* Pulsing ring */
.splash-ring-wrap {
position: relative; width: 76px; height: 76px;
display: flex; align-items: center; justify-content: center;
margin-bottom: 28px; flex-shrink: 0;
}
.splash-ring {
position: absolute; inset: 0; border-radius: 50%;
border: 2px solid transparent;
border-top-color: var(--accent2);
border-right-color: rgba(0,245,255,.3);
animation: splashSpin 1.1s linear infinite;
}
.splash-ring-2 {
position: absolute; inset: 8px; border-radius: 50%;
border: 1.5px solid transparent;
border-bottom-color: var(--accent3);
border-left-color: rgba(184,77,255,.3);
animation: splashSpin 1.7s linear infinite reverse;
}
.splash-icon-center {
font-size: 1.6rem;
animation: splashIconPulse 1.8s ease-in-out infinite;
filter: drop-shadow(0 0 12px rgba(0,245,255,.5));
}
@keyframes splashSpin { to { transform: rotate(360deg); } }
@keyframes splashIconPulse {
0%,100% { transform: scale(1); opacity: .8; }
50% { transform: scale(1.15); opacity: 1; }
}
/* Cycling text */
.splash-text-wrap {
height: 28px; overflow: hidden; position: relative;
width: 100%; display: flex; align-items: center; justify-content: center;
}
.splash-msg {
position: absolute; font-size: .82rem; font-weight: 600;
color: var(--text2); letter-spacing: .02em;
opacity: 0; transform: translateY(16px);
transition: opacity .4s ease, transform .4s ease;
white-space: nowrap;
}
.splash-msg.active {
opacity: 1; transform: translateY(0);
}
.splash-msg.exit {
opacity: 0; transform: translateY(-16px);
}
/* Sub text */
.splash-sub {
margin-top: 10px; font-size: .65rem; color: var(--text3);
animation: splashSubPulse 2s ease-in-out infinite;
}
@keyframes splashSubPulse {
0%,100% { opacity: .4; }
50% { opacity: .8; }
}
/* Dot bar */
.splash-dots {
display: flex; gap: 5px; margin-top: 18px;
}
.splash-dot {
width: 5px; height: 5px; border-radius: 50%;
background: rgba(0,245,255,.25);
animation: splashDotPop .8s ease-in-out infinite;
}
.splash-dot:nth-child(2) { animation-delay: .15s; }
.splash-dot:nth-child(3) { animation-delay: .3s; }
@keyframes splashDotPop {
0%,100% { transform: scale(1); background: rgba(0,245,255,.25); }
50% { transform: scale(1.5); background: rgba(0,245,255,.8); }
}
@keyframes spin { to { transform: rotate(360deg); } }
/* ── Results screen (full-page overlay over stack) ───── */
.dsc-results-overlay {
position: fixed; inset: 0; z-index: 80;
background: var(--bg);
overflow-y: auto; overflow-x: hidden;
-webkit-overflow-scrolling: touch;
padding-top: var(--nav-h);
padding-bottom: 120px;
animation: resultsFadeIn .5s cubic-bezier(.22,1,.36,1);
}
@keyframes resultsFadeIn {
from { opacity: 0; transform: translateY(24px); }
to { opacity: 1; transform: none; }
}
.res-wrap { max-width: 480px; margin: 0 auto; padding: 0 18px; }
/* Trophy header */
.res-header {
text-align: center; padding: 28px 0 22px;
position: relative;
}
.res-trophy {
font-size: 3.2rem; display: block; margin-bottom: 12px;
animation: trophyBounce .7s cubic-bezier(.175,.885,.32,1.275) .2s both;
filter: drop-shadow(0 0 20px rgba(0,245,255,.4));
}
@keyframes trophyBounce {
from { opacity: 0; transform: scale(0) rotate(-15deg); }
to { opacity: 1; transform: scale(1) rotate(0deg); }
}
.res-title {
font-family: var(--font-display); font-size: 1.3rem; font-weight: 900;
background: linear-gradient(90deg, var(--accent2), var(--accent3));
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
background-clip: text; letter-spacing: .05em; margin-bottom: 6px;
}
.res-subtitle { font-size: .76rem; color: var(--text3); }
/* Stats row */
.res-stats {
display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 22px;
}
.res-stat {
background: rgba(255,255,255,.03); border: 1px solid rgba(255,255,255,.06);
border-radius: 14px; padding: 14px 16px; text-align: center;
}
.res-stat-val { font-size: 1.6rem; font-weight: 800; display: block; line-height: 1; }
.res-stat-val.like { color: var(--accent2); text-shadow: 0 0 18px rgba(0,245,255,.3); }
.res-stat-val.skip { color: #ff2d7d; text-shadow: 0 0 18px rgba(255,45,125,.3); }
.res-stat-label { font-size: .6rem; color: var(--text3); text-transform: uppercase; letter-spacing: .1em; margin-top: 5px; display: block; }
/* Section titles */
.res-sec { margin-bottom: 20px; }
.res-sec-title {
font-size: .62rem; font-weight: 800; text-transform: uppercase;
letter-spacing: .16em; color: var(--text3); margin-bottom: 12px;
display: flex; align-items: center; gap: 8px;
}
.res-sec-title::before {
content: ''; display: block; width: 16px; height: 2px; border-radius: 1px; flex-shrink: 0;
}
.res-sec-title.cyan::before { background: var(--accent2); }
.res-sec-title.purple::before{ background: var(--accent3); }
.res-sec-title.pink::before { background: var(--accent); }
/* Liked anime strip */
.res-liked-strip {
display: flex; gap: 9px; overflow-x: auto; padding-bottom: 6px;
}
.res-liked-strip::-webkit-scrollbar { display: none; }
.res-liked-card {
flex-shrink: 0; width: 72px; text-decoration: none;
}
.res-liked-poster {
width: 72px; height: 100px; border-radius: 10px; overflow: hidden;
position: relative; border: 1.5px solid rgba(0,245,255,.2);
box-shadow: 0 4px 16px rgba(0,0,0,.4);
}
.res-liked-poster img { width: 100%; height: 100%; object-fit: cover; display: block; }
.res-liked-poster-ph {
width: 100%; height: 100%; background: var(--surface2);
display: flex; align-items: center; justify-content: center;
font-size: 1.5rem; color: var(--text3);
}
.res-liked-heart {
position: absolute; bottom: 4px; right: 4px;
width: 18px; height: 18px; border-radius: 50%;
background: rgba(0,245,255,.2); backdrop-filter: blur(4px);
display: flex; align-items: center; justify-content: center;
font-size: .5rem; color: var(--accent2);
}
.res-liked-name {
font-size: .55rem; color: var(--text3); text-align: center;
margin-top: 5px; line-height: 1.3;
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* AI rec cards */
.res-recs { display: flex; flex-direction: column; gap: 10px; }
.res-rec-card {
display: flex; gap: 12px; align-items: flex-start;
background: rgba(255,255,255,.03); border: 1px solid rgba(255,255,255,.06);
border-radius: 14px; padding: 12px; text-decoration: none;
transition: background .2s, border-color .2s;
}
.res-rec-card:hover {
background: rgba(184,77,255,.06); border-color: rgba(184,77,255,.2);
}
.res-rec-cover {
width: 50px; height: 70px; border-radius: 8px; overflow: hidden; flex-shrink: 0;
border: 1px solid rgba(255,255,255,.07);
}
.res-rec-cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
.res-rec-cover-ph {
width: 100%; height: 100%; background: var(--surface2);
display: flex; align-items: center; justify-content: center;
font-size: 1.2rem; color: var(--text3);
}
.res-rec-body { flex: 1; min-width: 0; }
.res-rec-title { font-size: .82rem; font-weight: 700; color: var(--text); margin-bottom: 4px; line-height: 1.3; }
.res-rec-meta {
display: flex; gap: 7px; font-size: .62rem; color: var(--text3); margin-bottom: 6px; flex-wrap: wrap;
}
.res-rec-meta .star { color: #fbbf24; }
.res-rec-reason {
font-size: .72rem; color: rgba(184,77,255,.8); font-style: italic;
line-height: 1.5;
}
.res-rec-reason::before { content: '"'; }
.res-rec-reason::after { content: '"'; }
.res-rec-genres { display: flex; gap: 4px; flex-wrap: wrap; }
.res-rec-genre {
font-size: .56rem; padding: 2px 7px; border-radius: 999px;
background: rgba(184,77,255,.08); border: 1px solid rgba(184,77,255,.18);
color: var(--accent3);
}
/* Where to find box */
.res-find-box {
background: linear-gradient(125deg, rgba(0,245,255,.05), rgba(184,77,255,.04));
border: 1px solid rgba(0,245,255,.14); border-radius: 16px;
padding: 18px; margin-bottom: 20px;
}
.res-find-title {
font-size: .7rem; font-weight: 800; color: var(--accent2);
text-transform: uppercase; letter-spacing: .1em; margin-bottom: 14px;
display: flex; align-items: center; gap: 7px;
}
.res-find-steps { display: flex; flex-direction: column; gap: 10px; }
.res-find-step {
display: flex; align-items: flex-start; gap: 10px;
}
.res-find-num {
width: 22px; height: 22px; border-radius: 50%; flex-shrink: 0;
background: rgba(0,245,255,.1); border: 1px solid rgba(0,245,255,.2);
color: var(--accent2); font-size: .6rem; font-weight: 800;
display: flex; align-items: center; justify-content: center; margin-top: 1px;
}
.res-find-text { font-size: .76rem; color: var(--text2); line-height: 1.5; }
.res-find-text strong { color: var(--text); font-weight: 700; }
.res-find-text a { color: var(--accent2); text-decoration: none; font-weight: 600; }
.res-find-text a:hover { text-decoration: underline; }
/* Action buttons */
.res-actions { display: flex; flex-direction: column; gap: 8px; margin-bottom: 10px; }
.res-btn-continue {
width: 100%; padding: 15px; border-radius: 14px;
background: linear-gradient(135deg, rgba(0,245,255,.18), rgba(0,245,255,.08));
border: 1.5px solid rgba(0,245,255,.4); color: var(--accent2);
font-size: .9rem; font-weight: 800; cursor: pointer;
display: flex; align-items: center; justify-content: center; gap: 9px;
font-family: var(--font); transition: all .2s;
box-shadow: 0 0 28px rgba(0,245,255,.1);
}
.res-btn-continue:hover { box-shadow: 0 0 48px rgba(0,245,255,.25); border-color: var(--accent2); }
.res-btn-profile {
width: 100%; padding: 13px; border-radius: 14px;
background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.08);
color: var(--text2); font-size: .82rem; font-weight: 600; cursor: pointer;
display: flex; align-items: center; justify-content: center; gap: 8px;
font-family: var(--font); text-decoration: none; transition: all .2s;
}
.res-btn-profile:hover { border-color: rgba(184,77,255,.3); color: var(--accent3); }
/* Guest notice */
.res-guest-notice {
background: rgba(251,191,36,.07); border: 1px solid rgba(251,191,36,.2);
border-radius: 12px; padding: 12px 14px; margin-bottom: 16px;
font-size: .74rem; color: rgba(251,191,36,.8); line-height: 1.5; text-align: center;
}
.res-guest-notice a { color: #fbbf24; font-weight: 700; text-decoration: none; }
.dsc-restart {
display: inline-flex; align-items: center; gap: 7px;
padding: 11px 24px; border-radius: 999px;
border: 1px solid var(--accent2); background: rgba(0,245,255,.08);
color: var(--accent2); font-size: .8rem; font-weight: 600; cursor: pointer;
transition: all .2s;
}
.dsc-restart:hover { background: rgba(0,245,255,.15); box-shadow: 0 0 24px rgba(0,245,255,.2); }
/* ════════════════════════════════════════════════════════
BOTTOM SHEET
════════════════════════════════════════════════════════ */
.bs-overlay {
position: fixed; inset: 0; z-index: 200;
background: rgba(0,0,0,0);
backdrop-filter: blur(0px);
pointer-events: none;
transition: background .35s, backdrop-filter .35s;
}
.bs-overlay.open {
background: rgba(0,0,0,.65);
backdrop-filter: blur(14px);
pointer-events: auto;
}
.bs-sheet {
position: fixed; left: 0; right: 0; bottom: 0; z-index: 201;
max-width: 520px; margin: 0 auto;
background: #0d0d1a;
border: 1px solid rgba(255,255,255,.07);
border-bottom: none;
border-radius: 26px 26px 0 0;
transform: translateY(100%);
transition: transform .45s cubic-bezier(.32,.72,0,1);
overflow: hidden;
display: flex; flex-direction: column;
max-height: 92vh;
box-shadow: 0 -20px 60px rgba(0,0,0,.6);
}
.bs-sheet.open { transform: translateY(0); }
/* Handle */
.bs-handle-row {
display: flex; align-items: center; justify-content: center;
padding: 12px 0 6px; flex-shrink: 0;
}
.bs-handle {
width: 40px; height: 4px; border-radius: 2px;
background: rgba(255,255,255,.14); cursor: grab;
}
.bs-handle:active { cursor: grabbing; }
/* Scrollable body */
.bs-body {
flex: 1; overflow-y: auto; overflow-x: hidden;
-webkit-overflow-scrolling: touch;
overscroll-behavior: contain;
}
.bs-body::-webkit-scrollbar { display: none; }
/* Banner image */
.bs-banner {
position: relative; width: 100%; aspect-ratio: 16/9; flex-shrink: 0;
}
.bs-banner img { width: 100%; height: 100%; object-fit: cover; display: block; }
.bs-banner-grad {
position: absolute; inset: 0;
background: linear-gradient(180deg, transparent 50%, #0d0d1a 100%);
}
.bs-banner-ph {
width: 100%; aspect-ratio: 16/9;
background: linear-gradient(135deg, #14142a, #1a1a30);
display: flex; align-items: center; justify-content: center;
font-size: 3.5rem; color: rgba(255,255,255,.06);
}
/* Content */
.bs-content { padding: 4px 22px 20px; }
.bs-genres { display: flex; gap: 5px; flex-wrap: wrap; margin-bottom: 12px; }
.bs-genre-pill {
font-size: .6rem; padding: 3px 10px; border-radius: 999px;
background: rgba(0,245,255,.09); border: 1px solid rgba(0,245,255,.2);
color: var(--accent2); font-weight: 600;
}
.bs-title {
font-size: 1.35rem; font-weight: 800; color: #fff;
line-height: 1.15; margin: 0 0 10px;
}
.bs-meta {
display: flex; align-items: center; gap: 12px;
font-size: .72rem; color: rgba(240,244,255,.45);
flex-wrap: wrap; margin-bottom: 14px;
}
.bs-meta-star {
display: flex; align-items: center; gap: 4px;
color: #fbbf24; font-weight: 700;
}
.bs-meta-star svg { width: 11px; height: 11px; fill: #fbbf24; }
.bs-badges { display: flex; gap: 7px; flex-wrap: wrap; margin-bottom: 16px; }
.bs-badge {
font-size: .62rem; font-weight: 700; padding: 4px 12px; border-radius: 999px;
letter-spacing: .04em;
}
.bs-badge-type { background: rgba(184,77,255,.12); border: 1px solid rgba(184,77,255,.25); color: var(--accent3); }
.bs-badge-status { background: rgba(0,245,255,.09); border: 1px solid rgba(0,245,255,.2); color: var(--accent2); }
/* Stats grid */
.bs-stats {
display: grid; grid-template-columns: repeat(3,1fr);
gap: 1px; margin-bottom: 18px;
background: rgba(255,255,255,.06); border-radius: 14px; overflow: hidden;
border: 1px solid rgba(255,255,255,.06);
}
.bs-stat {
background: rgba(255,255,255,.025); padding: 12px 10px; text-align: center;
}
.bs-stat-val { font-size: .9rem; font-weight: 800; color: var(--text); display: block; }
.bs-stat-label { font-size: .58rem; color: var(--text3); text-transform: uppercase; letter-spacing: .1em; display: block; margin-top: 2px; }
/* Divider */
.bs-divider { height: 1px; background: rgba(255,255,255,.06); margin: 0 0 16px; }
/* Description */
.bs-desc-title { font-size: .6rem; text-transform: uppercase; letter-spacing: .12em; color: var(--text3); font-weight: 700; margin-bottom: 8px; }
.bs-desc { font-size: .82rem; color: var(--text2); line-height: 1.7; margin-bottom: 20px; }
/* Detail link */
.bs-detail-link {
display: flex; align-items: center; justify-content: space-between;
padding: 13px 16px; border-radius: 13px;
background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.07);
color: var(--text2); text-decoration: none; font-size: .82rem; font-weight: 600;
transition: all .2s; margin-bottom: 14px;
}
.bs-detail-link:hover { border-color: rgba(184,77,255,.3); color: var(--accent3); }
/* Sheet action buttons */
.bs-actions { padding: 10px 22px calc(20px + env(safe-area-inset-bottom, 0px)); display: flex; gap: 10px; flex-shrink: 0; }
.bs-act-skip {
flex: 1; padding: 14px; border-radius: 14px;
background: rgba(255,45,125,.08); border: 1.5px solid rgba(255,45,125,.3);
color: #ff2d7d; font-size: .85rem; font-weight: 700;
cursor: pointer; transition: all .2s; display: flex; align-items: center; justify-content: center; gap: 7px;
}
.bs-act-skip:hover { background: rgba(255,45,125,.14); border-color: #ff2d7d; }
.bs-act-like {
flex: 2; padding: 14px; border-radius: 14px;
background: linear-gradient(135deg, rgba(0,245,255,.18), rgba(0,245,255,.08));
border: 1.5px solid rgba(0,245,255,.4); color: var(--accent2);
font-size: .85rem; font-weight: 700;
cursor: pointer; transition: all .2s; display: flex; align-items: center; justify-content: center; gap: 7px;
box-shadow: 0 0 24px rgba(0,245,255,.1);
}
.bs-act-like:hover { box-shadow: 0 0 40px rgba(0,245,255,.25); border-color: var(--accent2); }
/* ════════════════════════════════════════════════════════
MATCH / LIKE CELEBRATION OVERLAY
════════════════════════════════════════════════════════ */
.match-overlay {
position: fixed; inset: 0; z-index: 300;
display: flex; flex-direction: column; align-items: center; justify-content: center;
pointer-events: none; overflow: hidden;
}
.match-heart {
font-size: 5rem; line-height: 1;
filter: drop-shadow(0 0 30px rgba(0,245,255,.7));
animation: matchHeartPop .9s cubic-bezier(.175,.885,.32,1.275) forwards;
}
.match-text {
font-family: var(--font-display); font-size: 1.1rem; font-weight: 800;
color: var(--accent2); letter-spacing: .08em; text-transform: uppercase;
margin-top: 14px;
animation: matchTextFade .9s ease forwards;
text-shadow: 0 0 20px rgba(0,245,255,.5);
}
@keyframes matchHeartPop {
0% { opacity: 0; transform: scale(0) rotate(-20deg); }
55% { opacity: 1; transform: scale(1.35) rotate(8deg); }
75% { transform: scale(.95) rotate(-2deg); }
85% { opacity: 1; transform: scale(1.05) rotate(0deg); }
100% { opacity: 0; transform: scale(.9); }
}
@keyframes matchTextFade {
0%,20% { opacity: 0; transform: translateY(12px); }
45%,70%{ opacity: 1; transform: translateY(0); }
100% { opacity: 0; transform: translateY(-8px); }
}
/* Sparkle particles */
.sp-particle {
position: absolute;
width: 6px; height: 6px; border-radius: 50%;
animation: spFly 1s ease-out forwards;
pointer-events: none;
}
@keyframes spFly {
0% { opacity: 1; transform: translate(0,0) scale(1); }
100% { opacity: 0; transform: var(--fly); scale: 0; }
}
/* ════════════════════════════════════════════════════════
AÇIKLAMA PANELİ
════════════════════════════════════════════════════════ */
.dsc-desc-panel {
position: relative; z-index: 5;
max-width: 400px; width: calc(100% - 40px);
margin: 10px auto 0;
}
.dsc-desc-toggle {
width: 100%;
background: rgba(255,255,255,.04);
border: 1px solid rgba(255,255,255,.08);
border-radius: 14px;
padding: 11px 16px;
display: flex; align-items: center; gap: 10px;
cursor: pointer;
color: var(--text3);
font-family: var(--font); font-size: .76rem;
transition: background .2s, border-color .2s, color .2s;
text-align: left;
}
.dsc-desc-toggle:hover {
background: rgba(0,245,255,.06);
border-color: rgba(0,245,255,.2);
color: var(--text2);
}
.dsc-desc-toggle.open {
background: rgba(0,245,255,.07);
border-color: rgba(0,245,255,.22);
color: var(--accent2);
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
.dsc-desc-toggle-icon {
width: 26px; height: 26px; flex-shrink: 0;
border-radius: 50%;
background: rgba(0,245,255,.1);
display: flex; align-items: center; justify-content: center;
font-size: .75rem; color: var(--accent2);
transition: transform .3s cubic-bezier(.4,0,.2,1);
}
.dsc-desc-toggle.open .dsc-desc-toggle-icon { transform: rotate(180deg); }
.dsc-desc-toggle-label { flex: 1; font-weight: 600; }
.dsc-desc-toggle-hint {
font-size: .62rem; color: var(--text3); font-style: italic;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
max-width: 140px;
}
.dsc-desc-toggle.open .dsc-desc-toggle-hint { display: none; }
.dsc-desc-body {
max-height: 0; overflow: hidden;
background: rgba(0,245,255,.04);
border: 1px solid rgba(0,245,255,.22);
border-top: none;
border-bottom-left-radius: 14px;
border-bottom-right-radius: 14px;
transition: max-height .38s cubic-bezier(.4,0,.2,1);
}
.dsc-desc-body.open { max-height: 280px; }
.dsc-desc-inner {
padding: 14px 16px 16px;
}
.dsc-desc-anime-title {
font-size: .7rem; font-weight: 800; color: var(--accent2);
text-transform: uppercase; letter-spacing: .08em;
margin-bottom: 8px; display: flex; align-items: center; gap: 6px;
}
.dsc-desc-anime-title::before {
content: ''; display: block;
width: 14px; height: 2px; border-radius: 1px;
background: var(--accent2); flex-shrink: 0;
}
.dsc-desc-text {
font-size: .8rem; color: var(--text2); line-height: 1.7;
margin: 0 0 12px;
}
.dsc-desc-empty {
font-size: .76rem; color: var(--text3); font-style: italic;
}
.dsc-desc-link {
display: inline-flex; align-items: center; gap: 6px;
font-size: .7rem; font-weight: 600; color: var(--accent2);
text-decoration: none; padding: 6px 13px; border-radius: 999px;
background: rgba(0,245,255,.08); border: 1px solid rgba(0,245,255,.2);
transition: all .2s;
}
.dsc-desc-link:hover { background: rgba(0,245,255,.15); border-color: var(--accent2); }
</style>
@endpush
@section('content')
<div class="dsc-page" id="dscPage">
{{-- Ambient orbs --}}
<div class="dsc-orb dsc-orb-1" aria-hidden="true"></div>
<div class="dsc-orb dsc-orb-2" aria-hidden="true"></div>
{{-- Header --}}
<div class="dsc-header">
<a href="{{ route('home') }}" class="dsc-back">
<i class="bi bi-arrow-left" style="font-size:.9rem"></i>
</a>
<div class="dsc-heading">
<span class="dsc-heading-main">Keşfet</span>
<span class="dsc-heading-sub">Sana uygun animeyi bul</span>
</div>
<button class="dsc-filter-toggle" id="dscFilterToggle" title="Filtrele">
<i class="bi bi-sliders2"></i>
<span class="dsc-filter-dot" id="dscFilterDot"></span>
</button>
</div>
{{-- Filter panel --}}
<div class="dsc-filters" id="dscFilters">
<div class="dsc-fgroup">
<label>Tür</label>
<div class="dsc-fpills" id="dscGenrePills">
<button class="dfp active" data-genre="">Tümü</button>
@foreach($genres as $g)
<button class="dfp" data-genre="{{ $g->slug }}">{{ $g->name }}</button>
@endforeach
</div>
</div>
<div class="dsc-fgroup">
<label>Format</label>
<div class="dsc-fpills" id="dscTypePills">
<button class="dfp active" data-type="">Tümü</button>
<button class="dfp" data-type="series">Dizi</button>
<button class="dfp" data-type="movie">Film</button>
<button class="dfp" data-type="ova">OVA</button>
</div>
</div>
</div>
{{-- Progress --}}
<div class="dsc-progress">
<div class="dsc-progress-bar">
<div class="dsc-progress-fill" id="dscProgFill"></div>
</div>
<span class="dsc-count-txt" id="dscCountTxt"></span>
</div>
{{-- Card stack --}}
<div class="dsc-stage">
<div class="dsc-stack" id="dscStack">
<div class="dsc-state" id="dscLoading">
<div class="dsc-splash" id="dscSplash">
{{-- Floating icons --}}
<div class="splash-floats" id="splashFloats"></div>
{{-- Logo --}}
<div class="splash-logo">Animexe Keşfet</div>
{{-- Spinning rings + center icon --}}
<div class="splash-ring-wrap">
<div class="splash-ring"></div>
<div class="splash-ring-2"></div>
<span class="splash-icon-center"></span>
</div>
{{-- Cycling messages --}}
<div class="splash-text-wrap" id="splashTextWrap">
<span class="splash-msg active" id="splashMsg">Animeler hazırlanıyor...</span>
</div>
<div class="splash-sub">Zevklerine göre seçiyoruz</div>
{{-- Dots --}}
<div class="splash-dots">
<div class="splash-dot"></div>
<div class="splash-dot"></div>
<div class="splash-dot"></div>
</div>
</div>
</div>
{{-- Results screen (replaces empty state) --}}
<div class="dsc-state" id="dscEmpty" style="display:none">
<div class="dsc-loader" id="dscResultsLoader"></div>
</div>
</div>
</div>
{{-- Açıklama paneli --}}
<div class="dsc-desc-panel" id="dscDescPanel" style="display:none">
<button class="dsc-desc-toggle" id="dscDescToggle">
<span class="dsc-desc-toggle-icon"><i class="bi bi-chevron-down"></i></span>
<span class="dsc-desc-toggle-label">Açıklama</span>
<span class="dsc-desc-toggle-hint" id="dscDescHint"></span>
</button>
<div class="dsc-desc-body" id="dscDescBody">
<div class="dsc-desc-inner">
<div class="dsc-desc-anime-title" id="dscDescTitle"></div>
<p class="dsc-desc-text" id="dscDescText"></p>
<a class="dsc-desc-link" id="dscDescLink" href="#" target="_blank">
<i class="bi bi-box-arrow-up-right" style="font-size:.7rem"></i> Anime sayfası
</a>
</div>
</div>
</div>
{{-- Action buttons --}}
<div class="dsc-actions" id="dscActions">
<button class="dsc-btn dsc-btn-skip" id="dscSkipBtn" disabled title="Geç ←">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.8" stroke-linecap="round">
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
</svg>
</button>
<button class="dsc-btn dsc-btn-info" id="dscInfoBtn" disabled title="Detaylar ↑">
<i class="bi bi-chevron-up" style="font-size:.9rem"></i>
</button>
<button class="dsc-btn dsc-btn-like" id="dscLikeBtn" disabled title="Listeme Ekle →">
<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/>
</svg>
</button>
</div>
{{-- Keyboard hint --}}
<div class="dsc-kbd">
<span><kbd></kbd> Geç</span>
<span><kbd></kbd> Detaylar</span>
<span><kbd></kbd> Listeme Ekle</span>
</div>
</div>
{{-- ─────────────────────────────────────────────────
BOTTOM SHEET
───────────────────────────────────────────────── --}}
<div class="bs-overlay" id="bsOverlay"></div>
<div class="bs-sheet" id="bsSheet" role="dialog" aria-modal="true" aria-label="Anime Detayları">
<div class="bs-handle-row" id="bsHandleRow">
<div class="bs-handle" id="bsHandle"></div>
</div>
<div class="bs-body" id="bsBody">
{{-- Banner --}}
<div id="bsBanner"></div>
<div class="bs-content">
<div class="bs-genres" id="bsGenres"></div>
<h2 class="bs-title" id="bsTitle"></h2>
<div class="bs-meta" id="bsMeta"></div>
<div class="bs-badges" id="bsBadges"></div>
<div class="bs-stats" id="bsStats"></div>
<div class="bs-divider"></div>
<div id="bsDescBlock">
<div class="bs-desc-title">Hakkında</div>
<p class="bs-desc" id="bsDesc"></p>
</div>
<a class="bs-detail-link" id="bsDetailLink" href="#" target="_blank">
<span>Anime sayfasına git</span>
<i class="bi bi-arrow-up-right" style="font-size:.78rem"></i>
</a>
</div>
</div>
<div class="bs-actions">
<button class="bs-act-skip" id="bsSkipBtn">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round">
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
</svg>
Geç
</button>
<button class="bs-act-like" id="bsLikeBtn">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/>
</svg>
Listeme Ekle
</button>
</div>
</div>
{{-- Match celebration overlay --}}
<div class="match-overlay" id="matchOverlay" style="display:none"></div>
@endsection
@push('scripts')
<script>
(function () {
'use strict';
/* ── Config ───────────────────────────────────────────── */
const SWIPE_THRESH = 88;
const ROT_FACTOR = 0.1;
const FETCH_AHEAD = 3;
const CSRF = document.querySelector('meta[name="csrf-token"]')?.content ?? '';
const TYPE_LABELS = { series:'Dizi', movie:'Film', ova:'OVA', ona:'ONA', special:'Özel' };
const STATUS_LABELS = { ongoing:'Devam Ediyor', completed:'Tamamlandı', finished:'Bitti', announced:'Duyuruldu' };
/* ── State ────────────────────────────────────────────── */
let buffer = [];
let shownCount = 0;
let fetching = false;
let fetchErrState = false;
let filters = { genre:'', type:'' };
let dragging = false, startX = 0, startY = 0, curDX = 0;
let topCard = null;
let currentData = null;
let firstLoad = true;
/* ── DOM ──────────────────────────────────────────────── */
const stack = document.getElementById('dscStack');
const loadingEl = document.getElementById('dscLoading');
const emptyEl = document.getElementById('dscEmpty');
const skipBtn = document.getElementById('dscSkipBtn');
const likeBtn = document.getElementById('dscLikeBtn');
const infoBtn = document.getElementById('dscInfoBtn');
const progFill = document.getElementById('dscProgFill');
const countTxt = document.getElementById('dscCountTxt');
const filterToggle= document.getElementById('dscFilterToggle');
const filterPanel = document.getElementById('dscFilters');
const filterDot = document.getElementById('dscFilterDot');
const matchEl = document.getElementById('matchOverlay');
/* ── Sheet DOM ────────────────────────────────────────── */
const bsOverlay = document.getElementById('bsOverlay');
const bsSheet = document.getElementById('bsSheet');
const bsBody = document.getElementById('bsBody');
const bsHandleRow= document.getElementById('bsHandleRow');
const bsSkipBtn = document.getElementById('bsSkipBtn');
const bsLikeBtn = document.getElementById('bsLikeBtn');
/* ── Desc panel DOM ───────────────────────────────────── */
const descPanel = document.getElementById('dscDescPanel');
const descToggle = document.getElementById('dscDescToggle');
const descBody = document.getElementById('dscDescBody');
const descTitle = document.getElementById('dscDescTitle');
const descText = document.getElementById('dscDescText');
const descHint = document.getElementById('dscDescHint');
const descLink = document.getElementById('dscDescLink');
let descOpen = false;
/* ══════════════════════════════════════════════════════
CARD LOGIC
══════════════════════════════════════════════════════ */
async function fetchCards(limit = 8) {
if (fetching) return;
fetching = true;
try {
const ctrl = new AbortController();
const timer = setTimeout(() => ctrl.abort(), 10000); // 10sn timeout
const p = new URLSearchParams({ limit, ...filters });
const r = await fetch(`/kesfet/cards?${p}`, {
headers: { Accept:'application/json' },
signal: ctrl.signal,
});
clearTimeout(timer);
const d = await r.json();
if (d.error) { showFetchError(); return; }
if (d.cards?.length) buffer.push(...d.cards);
} catch(e) {
if (e.name === 'AbortError') {
showFetchError();
} else {
console.warn('[Keşfet]', e);
showFetchError();
}
} finally { fetching = false; }
}
function showFetchError() {
fetchErrState = true;
setLoading(false);
const splash = document.getElementById('dscSplash');
if (splash) {
splash.innerHTML = `
<div style="text-align:center;padding:20px">
<div style="font-size:2.2rem;margin-bottom:14px;color:#ff2d7d">⚠</div>
<div style="font-size:.9rem;font-weight:700;color:var(--text);margin-bottom:8px">Yüklenemedi</div>
<div style="font-size:.72rem;color:var(--text3);margin-bottom:20px;line-height:1.6">Sunucu yanıt vermedi.<br>İnternet bağlantını kontrol et.</div>
<button onclick="window.location.reload()" style="
padding:10px 22px;border-radius:999px;border:1px solid rgba(0,245,255,.4);
background:rgba(0,245,255,.08);color:#00f5ff;font-size:.8rem;
font-weight:600;cursor:pointer;font-family:inherit">
Tekrar Dene
</button>
</div>`;
loadingEl.style.display = 'flex';
}
}
function makeCard(data, animate = false) {
const el = document.createElement('div');
el.className = 'dsc-card' + (animate ? ' entering' : '');
el.dataset.id = data.id; el.dataset.slug = data.slug;
if (animate) el.addEventListener('animationend', () => el.classList.remove('entering'), { once:true });
const imgSrc = data.banner_url || data.cover_url || '';
const genres = (data.genres || []).slice(0,3)
.map(g => `<span class="dsc-genre-pill">${esc(g)}</span>`).join('');
const starHtml = data.rating
? `<span class="dsc-card-star">
<svg viewBox="0 0 24 24"><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>
${data.rating}
</span>` : '';
const metaParts = [
data.year ? `<span>${data.year}</span>` : '',
data.episode_count ? `<span>${data.episode_count} bölüm</span>` : '',
starHtml,
].filter(Boolean);
el.innerHTML = `
<div class="dsc-card-img">
${imgSrc
? `<img src="${imgSrc}" alt="" draggable="false" loading="eager">`
: `<div class="dsc-card-noimg"><i class="bi bi-film"></i></div>`}
<div class="dsc-card-gradient"></div>
</div>
<div class="dsc-card-tint"></div>
<span class="dsc-stamp dsc-stamp-like">Listeme Ekle</span>
<span class="dsc-stamp dsc-stamp-skip">Geç</span>
<div class="dsc-card-info">
<div class="dsc-card-genres">${genres}</div>
<h2 class="dsc-card-title">${esc(data.title)}</h2>
<div class="dsc-card-meta">${metaParts.join('')}</div>
${data.hook ? `<p class="dsc-card-hook">${esc(data.hook)}</p>` : ''}
</div>
`;
return el;
}
function renderStack() {
stack.querySelectorAll('.dsc-card').forEach(c => c.remove());
if (!buffer.length) {
if (!fetchErrState) { setLoading(false); showEmpty(); }
return;
}
fetchErrState = false;
const visible = buffer.slice(0, 3);
[...visible].reverse().forEach((data, i) => {
const el = makeCard(data, i === visible.length - 1 && firstLoad);
stack.appendChild(el);
});
if (firstLoad) {
firstLoad = false;
// Tap hint on the first card
const hint = document.createElement('div');
hint.className = 'dsc-hint-pill';
hint.textContent = 'Kaydırarak keşfet';
stack.querySelector('.pos-0, .dsc-card:last-child')?.appendChild(hint);
}
assignPositions();
bindTopCard();
updateUI();
setLoading(false);
emptyEl.style.display = 'none';
}
function assignPositions() {
const cards = Array.from(stack.querySelectorAll('.dsc-card'));
cards.forEach(c => c.classList.remove('pos-0','pos-1','pos-2'));
if (cards[cards.length-1]) cards[cards.length-1].classList.add('pos-0');
if (cards[cards.length-2]) cards[cards.length-2].classList.add('pos-1');
if (cards[cards.length-3]) cards[cards.length-3].classList.add('pos-2');
}
function bindTopCard() {
topCard = stack.querySelector('.pos-0');
currentData = topCard ? buffer[0] : null;
if (!topCard) return;
topCard.addEventListener('mousedown', onDragStart, { passive:false });
topCard.addEventListener('touchstart', onDragStart, { passive:true });
}
/* ── Drag ─────────────────────────────────────────────── */
function onDragStart(e) {
if (bsSheet.classList.contains('open')) return;
dragging = true;
startX = e.type === 'touchstart' ? e.touches[0].clientX : e.clientX;
startY = e.type === 'touchstart' ? e.touches[0].clientY : e.clientY;
curDX = 0;
if (topCard) topCard.style.transition = 'none';
}
function onDragMove(e) {
if (!dragging || !topCard) return;
const cx = e.type === 'touchmove' ? e.touches[0].clientX : e.clientX;
curDX = cx - startX;
const dy = (e.type === 'touchmove' ? e.touches[0].clientY : e.clientY) - startY;
const rot = curDX * ROT_FACTOR;
const lift= Math.abs(curDX) * 0.025;
topCard.style.transform = `translateX(${curDX}px) translateY(${-lift}px) rotate(${rot}deg)`;
// Stamp hints
if (curDX > 30) { topCard.classList.add('hint-like'); topCard.classList.remove('hint-skip'); }
else if (curDX < -30) { topCard.classList.add('hint-skip'); topCard.classList.remove('hint-like'); }
else { topCard.classList.remove('hint-like','hint-skip'); }
// Promote second card
const second = stack.querySelector('.pos-1');
if (second) {
const p = Math.min(Math.abs(curDX) / SWIPE_THRESH, 1);
second.style.transition = 'none';
second.style.transform = `scale(${0.95 + p*.05}) translateY(${18 - p*18}px)`;
}
}
function onDragEnd() {
if (!dragging) return;
dragging = false;
if (!topCard) return;
if (curDX > SWIPE_THRESH) commitSwipe('like');
else if (curDX < -SWIPE_THRESH) commitSwipe('skip');
else snapBack();
}
function snapBack() {
if (!topCard) return;
topCard.classList.remove('hint-like','hint-skip');
topCard.style.transition = 'transform .38s cubic-bezier(.175,.885,.32,1.275)';
topCard.style.transform = '';
const second = stack.querySelector('.pos-1');
if (second) {
second.style.transition = 'transform .38s cubic-bezier(.175,.885,.32,1.275)';
second.style.transform = 'scale(.95) translateY(18px)';
}
}
/* ── Commit swipe ─────────────────────────────────────── */
function commitSwipe(dir) {
if (!topCard) return;
const animeId = parseInt(topCard.dataset.id);
topCard.classList.remove('hint-like','hint-skip');
topCard.classList.add(dir === 'like' ? 'going-right' : 'going-left');
if (dir === 'like') {
likeBtn.classList.add('pulse');
setTimeout(() => likeBtn.classList.remove('pulse'), 300);
triggerMatchAnim();
}
shownCount++;
buffer.shift();
closeDesc();
setTimeout(() => {
topCard?.remove();
topCard = null;
currentData = null;
if (!buffer.length) {
showEmpty();
return;
}
// Add 3rd card if buffer has it
if (buffer[2] && !stack.querySelector(`[data-id="${buffer[2].id}"]`)) {
const el = makeCard(buffer[2], true);
stack.prepend(el);
}
assignPositions();
bindTopCard();
updateUI();
}, 400);
postSwipe(animeId, dir);
}
async function postSwipe(id, dir) {
try {
await fetch('/kesfet/swipe', {
method:'POST',
headers:{ 'Content-Type':'application/json', 'X-CSRF-TOKEN':CSRF, Accept:'application/json' },
body: JSON.stringify({ anime_id:id, direction:dir }),
});
} catch {}
}
/* ── UI helpers ───────────────────────────────────────── */
function updateUI() {
const has = !!buffer.length;
skipBtn.disabled = !has; likeBtn.disabled = !has; infoBtn.disabled = !has;
countTxt.textContent = has ? `${shownCount + 1}. kart` : '';
const total = shownCount + buffer.length;
progFill.style.width = total > 0 ? `${Math.min((shownCount / total) * 100, 95)}%` : '0%';
updateDescPanel();
}
function updateDescPanel() {
if (!currentData) {
descPanel.style.display = 'none';
closeDesc();
return;
}
descPanel.style.display = '';
const text = currentData.description || currentData.hook || '';
descTitle.textContent = currentData.title;
descText.textContent = text;
descHint.textContent = text ? text.slice(0, 55) + '…' : 'Açıklama yok';
descLink.href = `/anime/${currentData.slug}`;
if (!text) {
descText.innerHTML = '<span class="dsc-desc-empty">Bu anime için açıklama bulunamadı.</span>';
}
}
function closeDesc() {
descOpen = false;
descToggle.classList.remove('open');
descBody.classList.remove('open');
}
descToggle.addEventListener('click', () => {
descOpen = !descOpen;
descToggle.classList.toggle('open', descOpen);
descBody.classList.toggle('open', descOpen);
});
function setLoading(v) {
loadingEl.style.display = v ? 'flex' : 'none';
if (!v && window._dscStopSplash) window._dscStopSplash();
}
function showEmpty() {
emptyEl.style.display = 'flex';
skipBtn.disabled = likeBtn.disabled = infoBtn.disabled = true;
countTxt.textContent = '';
progFill.style.width = '100%';
descPanel.style.display = 'none';
closeDesc();
loadResults();
}
async function loadResults() {
try {
const ctrl = new AbortController();
const timer = setTimeout(() => ctrl.abort(), 15000);
const r = await fetch('/kesfet/results', {
headers: { Accept:'application/json' },
signal: ctrl.signal,
});
clearTimeout(timer);
const d = await r.json();
renderResults(d);
} catch {
renderResultsFallback();
}
}
function renderResults(d) {
// Remove existing results overlay if any
document.getElementById('dscResultsOverlay')?.remove();
const isAuth = d.is_auth;
const liked = d.liked || [];
const recs = d.recommendations || [];
const likeCount = d.like_count ?? liked.length;
const skipCount = d.skip_count ?? 0;
// Build liked posters HTML
const likedHtml = liked.length
? liked.map(a => `
<a class="res-liked-card" href="/anime/${a.slug}" target="_blank" title="${esc(a.title)}">
<div class="res-liked-poster">
${a.cover_url
? `<img src="${a.cover_url}" alt="${esc(a.title)}" loading="lazy">`
: `<div class="res-liked-poster-ph"><i class="bi bi-film"></i></div>`}
<div class="res-liked-heart"><i class="bi bi-heart-fill"></i></div>
</div>
<div class="res-liked-name">${esc(a.title)}</div>
</a>`).join('')
: `<p style="font-size:.76rem;color:var(--text3);font-style:italic;padding:6px 0">Hiç anime beğenmedin.</p>`;
// Build recs HTML
const recsHtml = recs.length
? recs.map(a => `
<a class="res-rec-card" href="/anime/${a.slug}" target="_blank">
<div class="res-rec-cover">
${a.cover_url
? `<img src="${a.cover_url}" alt="${esc(a.title)}" loading="lazy">`
: `<div class="res-rec-cover-ph"><i class="bi bi-film"></i></div>`}
</div>
<div class="res-rec-body">
<div class="res-rec-title">${esc(a.title)}</div>
<div class="res-rec-meta">
${a.rating ? `<span class="star"><i class="bi bi-star-fill" style="font-size:.55rem"></i> ${a.rating}</span>` : ''}
${a.year ? `<span>${a.year}</span>` : ''}
</div>
${(a.genres||[]).length ? `<div class="res-rec-genres">${a.genres.map(g=>`<span class="res-rec-genre">${esc(g)}</span>`).join('')}</div>` : ''}
${a.reason ? `<div class="res-rec-reason" style="margin-top:6px">${esc(a.reason)}</div>` : ''}
</div>
</a>`).join('')
: `<p style="font-size:.76rem;color:var(--text3);font-style:italic;padding:6px 0">Öneri üretilemedi.</p>`;
// Where to find section
const findSteps = isAuth ? `
<div class="res-find-step">
<div class="res-find-num">1</div>
<div class="res-find-text">Beğendiğin animeler otomatik olarak <strong>İzleme Listene</strong> eklendi (İzlemek İstiyorum olarak).</div>
</div>
<div class="res-find-step">
<div class="res-find-num">2</div>
<div class="res-find-text"><a href="/profile">Profil sayfana</a> git → <strong>Listem</strong> sekmesine tıkla → Tüm beğendiklerini göreceksin.</div>
</div>
<div class="res-find-step">
<div class="res-find-num">3</div>
<div class="res-find-text">Keşfet geçmişine bakmak için <strong>Profil → Keşfet</strong> sekmesini kullan — beğenilenler ve geçilenler ayrı görünür.</div>
</div>
<div class="res-find-step">
<div class="res-find-num">4</div>
<div class="res-find-text">Bir animenin adına tıklayarak <strong>anime sayfasına</strong> gidebilir ve izlemeye başlayabilirsin.</div>
</div>` : `
<div class="res-find-step">
<div class="res-find-num">1</div>
<div class="res-find-text">Şu an misafir olarak kullanıyorsun. Beğenilerin <strong>kaydedilmedi</strong>.</div>
</div>
<div class="res-find-step">
<div class="res-find-num">2</div>
<div class="res-find-text"><a href="/register">Ücretsiz kayıt ol</a> ya da <a href="/login">giriş yap</a> — beğenilerin İzleme Listene otomatik eklensin.</div>
</div>`;
const guestNotice = !isAuth ? `
<div class="res-guest-notice">
⚠️ Misafir olarak kullanıyorsun — beğenilerin kaydedilmedi.
<a href="/register">Kayıt ol</a> ya da <a href="/login">giriş yap</a> ve tekrar keşfet.
</div>` : '';
const overlay = document.createElement('div');
overlay.id = 'dscResultsOverlay';
overlay.className = 'dsc-results-overlay';
overlay.innerHTML = `
<div class="res-wrap">
<div class="res-header">
<span class="res-trophy">🎉</span>
<div class="res-title">Keşif Tamamlandı!</div>
<div class="res-subtitle">Bu tur bitti — işte özeting</div>
</div>
<div class="res-stats">
<div class="res-stat">
<span class="res-stat-val like">${likeCount}</span>
<span class="res-stat-label">♥ Beğendin</span>
</div>
<div class="res-stat">
<span class="res-stat-val skip">${skipCount}</span>
<span class="res-stat-label">✕ Geçtin</span>
</div>
</div>
${guestNotice}
${liked.length ? `
<div class="res-sec">
<div class="res-sec-title cyan">Beğendiklerin</div>
<div class="res-liked-strip">${likedHtml}</div>
</div>` : ''}
<div class="res-sec">
<div class="res-sec-title purple">Yapay Zeka Önerileri</div>
<div class="res-recs">${recsHtml}</div>
</div>
<div class="res-sec">
<div class="res-find-box">
<div class="res-find-title"><i class="bi bi-compass-fill"></i> Beğendiklerime Nereden Ulaşırım?</div>
<div class="res-find-steps">${findSteps}</div>
</div>
</div>
<div class="res-actions">
<button class="res-btn-continue" id="resContinueBtn">
<i class="bi bi-arrow-clockwise"></i> Daha Fazla Anime Keşfet
</button>
${isAuth
? `<a class="res-btn-profile" href="/profile"><i class="bi bi-person-circle"></i> Profilime Git Listem</a>`
: `<a class="res-btn-profile" href="/register"><i class="bi bi-person-plus"></i> Kayıt Ol Listeni Kaydet</a>`
}
</div>
</div>`;
document.body.appendChild(overlay);
emptyEl.style.display = 'none';
document.getElementById('resContinueBtn')?.addEventListener('click', async () => {
overlay.style.animation = 'resultsFadeIn .3s cubic-bezier(.22,1,.36,1) reverse forwards';
await new Promise(r => setTimeout(r, 280));
overlay.remove();
restart();
});
}
function renderResultsFallback() {
emptyEl.innerHTML = `
<div style="text-align:center;padding:20px">
<div style="font-size:2.2rem;margin-bottom:14px">✓</div>
<div style="font-size:1rem;font-weight:700;color:var(--text);margin-bottom:8px">Hepsini gördün!</div>
<div style="font-size:.76rem;color:var(--text3);margin-bottom:20px">Tüm animeleri kaydırdın.</div>
<button class="dsc-restart" id="dscRestartBtn"><i class="bi bi-arrow-clockwise"></i> Yeniden başla</button>
</div>`;
document.getElementById('dscRestartBtn')?.addEventListener('click', restart);
}
/* ── Match animation ──────────────────────────────────── */
function triggerMatchAnim() {
matchEl.style.display = 'flex';
matchEl.innerHTML = `
<div class="match-heart">♥</div>
<div class="match-text">Listene Eklendi!</div>
`;
// Spawn particles
const colors = ['#00f5ff','#b84dff','#00f5ff','#fff'];
for (let i = 0; i < 14; i++) {
const p = document.createElement('div');
p.className = 'sp-particle';
const ang = (i / 14) * 360;
const dist = 80 + Math.random() * 80;
const rad = (ang * Math.PI) / 180;
const tx = Math.cos(rad) * dist;
const ty = Math.sin(rad) * dist;
p.style.cssText = `
left:50%; top:45%;
margin:-3px;
background:${colors[i % colors.length]};
--fly: translate(${tx}px,${ty}px);
animation-delay:${i * 0.03}s;
`;
matchEl.appendChild(p);
}
setTimeout(() => { matchEl.style.display = 'none'; matchEl.innerHTML = ''; }, 1100);
}
/* ── Boot & restart ───────────────────────────────────── */
async function init() {
setLoading(true);
await fetchCards(10);
renderStack();
}
async function restart() {
document.getElementById('dscResultsOverlay')?.remove();
buffer = []; shownCount = 0; firstLoad = false; fetchErrState = false; fetching = false;
stack.querySelectorAll('.dsc-card').forEach(c => c.remove());
emptyEl.style.display = 'none';
setLoading(true);
try { await fetch('/kesfet/reset', { method:'POST', headers:{ 'X-CSRF-TOKEN':CSRF } }); } catch {}
await fetchCards(10);
renderStack();
}
/* ══════════════════════════════════════════════════════
BOTTOM SHEET
══════════════════════════════════════════════════════ */
function openSheet() {
if (!currentData) return;
populateSheet(currentData);
bsBody.scrollTop = 0;
bsOverlay.classList.add('open');
bsSheet.classList.add('open');
document.body.style.overflow = 'hidden';
}
function closeSheet() {
bsSheet.classList.remove('open');
bsOverlay.classList.remove('open');
document.body.style.overflow = '';
}
function populateSheet(d) {
// Banner
const bannerEl = document.getElementById('bsBanner');
if (d.banner_url || d.cover_url) {
bannerEl.innerHTML = `
<div class="bs-banner">
<img src="${d.banner_url || d.cover_url}" alt="${esc(d.title)}" loading="lazy">
<div class="bs-banner-grad"></div>
</div>`;
} else {
bannerEl.innerHTML = `<div class="bs-banner-ph"><i class="bi bi-film"></i></div>`;
}
// Genres
document.getElementById('bsGenres').innerHTML =
(d.genres || []).map(g => `<span class="bs-genre-pill">${esc(g)}</span>`).join('');
// Title
document.getElementById('bsTitle').textContent = d.title;
// Meta
const metaArr = [];
if (d.rating) metaArr.push(`<span class="bs-meta-star">
<svg viewBox="0 0 24 24"><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>
${d.rating}</span>`);
if (d.year) metaArr.push(`<span>${d.year}</span>`);
if (d.episode_count) metaArr.push(`<span>${d.episode_count} bölüm</span>`);
document.getElementById('bsMeta').innerHTML = metaArr.join('');
// Badges
const badges = [];
if (d.type) badges.push(`<span class="bs-badge bs-badge-type">${TYPE_LABELS[d.type] || d.type}</span>`);
if (d.status) badges.push(`<span class="bs-badge bs-badge-status">${STATUS_LABELS[d.status] || d.status}</span>`);
document.getElementById('bsBadges').innerHTML = badges.join('');
// Stats
const statsHtml = [
{ val: d.year || '—', label: 'Yıl' },
{ val: d.episode_count || '—', label: 'Bölüm' },
{ val: d.rating || '—', label: 'Puan' },
].map(s => `
<div class="bs-stat">
<span class="bs-stat-val">${s.val}</span>
<span class="bs-stat-label">${s.label}</span>
</div>`).join('');
document.getElementById('bsStats').innerHTML = statsHtml;
// Description
const descText = d.description || d.hook || '';
const descBlock = document.getElementById('bsDescBlock');
if (descText) {
descBlock.style.display = '';
document.getElementById('bsDesc').textContent = descText;
} else {
descBlock.style.display = 'none';
}
// Detail link
document.getElementById('bsDetailLink').href = `/anime/${d.slug}`;
}
// Sheet skip/like from within the sheet
bsSkipBtn.addEventListener('click', () => { closeSheet(); setTimeout(() => commitSwipe('skip'), 200); });
bsLikeBtn.addEventListener('click', () => { closeSheet(); setTimeout(() => commitSwipe('like'), 200); });
// Close on overlay click
bsOverlay.addEventListener('click', closeSheet);
// Drag-to-dismiss the sheet
let sheetDragStart = 0, sheetDragging = false, sheetTranslate = 0;
bsHandleRow.addEventListener('touchstart', e => {
sheetDragStart = e.touches[0].clientY;
sheetDragging = true;
sheetTranslate = 0;
bsSheet.style.transition = 'none';
}, { passive:true });
document.addEventListener('touchmove', e => {
if (!sheetDragging) return;
const dy = e.touches[0].clientY - sheetDragStart;
if (dy < 0) return;
sheetTranslate = dy;
bsSheet.style.transform = `translateY(${dy}px)`;
}, { passive:true });
document.addEventListener('touchend', () => {
if (!sheetDragging) return;
sheetDragging = false;
bsSheet.style.transition = '';
bsSheet.style.transform = '';
if (sheetTranslate > 120) closeSheet();
}, { passive:true });
/* ══════════════════════════════════════════════════════
GLOBAL EVENTS
══════════════════════════════════════════════════════ */
document.addEventListener('mousemove', onDragMove);
document.addEventListener('touchmove', onDragMove, { passive:true });
document.addEventListener('mouseup', onDragEnd);
document.addEventListener('touchend', onDragEnd);
skipBtn.addEventListener('click', () => { if (!skipBtn.disabled) commitSwipe('skip'); });
likeBtn.addEventListener('click', () => { if (!likeBtn.disabled) commitSwipe('like'); });
infoBtn.addEventListener('click', openSheet);
document.addEventListener('keydown', e => {
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
if (bsSheet.classList.contains('open')) {
if (e.key === 'Escape') closeSheet();
return;
}
if (e.key === 'ArrowRight' || e.key === 'l') { if (!likeBtn.disabled) commitSwipe('like'); }
else if (e.key === 'ArrowLeft' || e.key === 'h') { if (!skipBtn.disabled) commitSwipe('skip'); }
else if (e.key === 'ArrowUp' || e.key === 'i' || e.key === 'I') openSheet();
});
document.getElementById('dscRestartBtn')?.addEventListener('click', restart);
filterToggle.addEventListener('click', () => {
filterToggle.classList.toggle('active');
filterPanel.classList.toggle('open');
});
document.querySelectorAll('#dscGenrePills .dfp').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('#dscGenrePills .dfp').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
filters.genre = btn.dataset.genre;
updateFilterDot();
resetAndLoad();
});
});
document.querySelectorAll('#dscTypePills .dfp').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('#dscTypePills .dfp').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
filters.type = btn.dataset.type;
updateFilterDot();
resetAndLoad();
});
});
function updateFilterDot() {
filterDot.classList.toggle('on', !!(filters.genre || filters.type));
}
async function resetAndLoad() {
buffer = []; shownCount = 0; fetchErrState = false;
stack.querySelectorAll('.dsc-card').forEach(c => c.remove());
emptyEl.style.display = 'none';
setLoading(true);
await fetchCards(10);
renderStack();
}
/* ── Helpers ─────────────────────────────────────────── */
function esc(s) {
return String(s ?? '').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}
/* ══════════════════════════════════════════════════════
SPLASH SCREEN
══════════════════════════════════════════════════════ */
(function initSplash() {
const msgs = [
'Animeler hazırlanıyor...',
'En iyi seçimler seçiliyor...',
'Zevklerine göre sıralanıyor...',
'Yapay zeka çalışıyor...',
'Sürprizler yükleniyor...',
'Az kaldı, neredeyse hazır...',
];
const floatIcons = [
{ ch: '♥', color: '#00f5ff' },
{ ch: '✕', color: '#ff2d7d' },
{ ch: '♥', color: '#b84dff' },
{ ch: '★', color: '#fbbf24' },
{ ch: '♥', color: '#00f5ff' },
{ ch: '✕', color: '#ff2d7d' },
{ ch: '◆', color: '#00f5ff' },
{ ch: '♥', color: '#b84dff' },
{ ch: '✕', color: '#ff2d7d' },
{ ch: '♥', color: '#00f5ff' },
];
const floatContainer = document.getElementById('splashFloats');
const msgEl = document.getElementById('splashMsg');
if (!floatContainer || !msgEl) return;
// Spawn floating icons
floatIcons.forEach((ic, i) => {
const el = document.createElement('span');
el.className = 'sf-icon';
el.textContent = ic.ch;
el.style.cssText = `
left: ${8 + (i / floatIcons.length) * 85}%;
color: ${ic.color};
animation-duration: ${2.8 + Math.random() * 2.4}s;
animation-delay: ${i * 0.28 + Math.random() * 0.4}s;
font-size: ${1.1 + Math.random() * .8}rem;
`;
floatContainer.appendChild(el);
});
// Cycle messages
let msgIdx = 0;
const cycleMsg = setInterval(() => {
msgIdx = (msgIdx + 1) % msgs.length;
msgEl.classList.remove('active');
msgEl.classList.add('exit');
setTimeout(() => {
msgEl.textContent = msgs[msgIdx];
msgEl.classList.remove('exit');
msgEl.classList.add('active');
}, 420);
}, 1800);
// Stop cycling when loading ends
const orig = window._dscStopSplash;
window._dscStopSplash = function () {
clearInterval(cycleMsg);
if (orig) orig();
};
})();
init();
})();
</script>
@endpush