Upload 13 files
Browse files- .gitattributes +35 -35
- chatbox.js +176 -0
- control-panel.html +1348 -0
- endpoint_API_sercret +2 -0
- index.html +1110 -19
- login.html +462 -0
- manga-api.js +113 -0
- manga-config.js +1 -0
- scan_anime_result.json +1314 -0
- scan_missing_episodes.json +124 -0
- scan_multi_server.json +54 -0
- signup-loginsystem.js +169 -0
- style.css +1889 -18
.gitattributes
CHANGED
|
@@ -1,35 +1,35 @@
|
|
| 1 |
-
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
-
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
-
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
-
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
-
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
-
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
-
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
-
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
-
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
-
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
-
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
-
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
-
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
-
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
-
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
-
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
-
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
-
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
-
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
-
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
-
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
-
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
-
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
-
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
-
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
-
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
-
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
-
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
-
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
-
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
-
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
-
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
-
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
-
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
-
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
chatbox.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
(function () {
|
| 2 |
+
'use strict';
|
| 3 |
+
|
| 4 |
+
var API_DEFAULT = 'https://chatbox.npgia-himlam.workers.dev/';
|
| 5 |
+
var ID_PREFIX = 'avchat';
|
| 6 |
+
var MODELS = { nivip: 'nivip', zerta: 'zerta' };
|
| 7 |
+
var MODEL_LABELS = { nivip: 'Nivip', zerta: 'Zerta' };
|
| 8 |
+
|
| 9 |
+
function baseUrl() {
|
| 10 |
+
try {
|
| 11 |
+
var v = localStorage.getItem('anivietdb-chat-api');
|
| 12 |
+
if (v) return v;
|
| 13 |
+
} catch (e) {}
|
| 14 |
+
return API_DEFAULT;
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
function injectCss() {
|
| 18 |
+
if (document.getElementById(ID_PREFIX + '-style')) return;
|
| 19 |
+
var st = document.createElement('style');
|
| 20 |
+
st.id = ID_PREFIX + '-style';
|
| 21 |
+
st.textContent = [
|
| 22 |
+
'.' + ID_PREFIX + '-fab {',
|
| 23 |
+
' position: fixed; right: 16px; bottom: 16px; width: 56px; height: 56px;',
|
| 24 |
+
' border-radius: 50%; border: none; cursor: pointer; font-size: 24px; z-index: 9998;',
|
| 25 |
+
' background: linear-gradient(135deg, #e84393, #a29bfe); color: #fff;',
|
| 26 |
+
' box-shadow: 0 8px 24px rgba(232,67,147,.5); display: flex; align-items: center; justify-content: center;',
|
| 27 |
+
' transition: transform .2s;',
|
| 28 |
+
'}',
|
| 29 |
+
'.' + ID_PREFIX + '-fab:hover { transform: scale(1.08); }',
|
| 30 |
+
'.' + ID_PREFIX + '-box {',
|
| 31 |
+
' position: fixed; right: 16px; bottom: 16px; width: 340px; max-width: calc(100vw - 32px);',
|
| 32 |
+
' background: #14141f; color: #fff; border-radius: 12px; overflow: hidden;',
|
| 33 |
+
' box-shadow: 0 8px 30px rgba(0,0,0,.5); z-index: 9999;',
|
| 34 |
+
' font-family: Roboto, Arial, sans-serif; display: none;',
|
| 35 |
+
'}',
|
| 36 |
+
'.' + ID_PREFIX + '-head { background: #1d1d2e; padding: 12px 16px; border-bottom: 1px solid #333; display: flex; justify-content: space-between; align-items: center; }',
|
| 37 |
+
'.' + ID_PREFIX + '-head h3 { margin: 0; font-size: 15px; color: #fff; }',
|
| 38 |
+
'.' + ID_PREFIX + '-model { background: #14141f; color: #fff; border: 1px solid #444; border-radius: 6px; font-size: 12.5px; padding: 4px 6px; outline: none; cursor: pointer; }',
|
| 39 |
+
'.' + ID_PREFIX + '-close { background: none; border: none; color: #aaa; font-size: 15px; cursor: pointer; padding: 0 4px; }',
|
| 40 |
+
'.' + ID_PREFIX + '-close:hover { color: #fff; }',
|
| 41 |
+
'.' + ID_PREFIX + '-msgs { padding: 14px; max-height: 340px; overflow-y: auto; font-size: 14px; line-height: 1.5; background: #fff; color: #222; }',
|
| 42 |
+
'.' + ID_PREFIX + '-msgs .' + ID_PREFIX + '-user { text-align: right; margin: 6px 0; }',
|
| 43 |
+
'.' + ID_PREFIX + '-msgs .' + ID_PREFIX + '-user::before { content: "Bạn: "; color: #007aff; font-weight: 700; }',
|
| 44 |
+
'.' + ID_PREFIX + '-msgs .' + ID_PREFIX + '-ai { margin: 6px 0; white-space: pre-wrap; word-break: break-word; }',
|
| 45 |
+
'.' + ID_PREFIX + '-msgs .' + ID_PREFIX + '-ai::before { content: "AI: "; color: #e84393; font-weight: 700; }',
|
| 46 |
+
'.' + ID_PREFIX + '-msgs .' + ID_PREFIX + '-err { margin: 6px 0; color: #c0392b; white-space: pre-wrap; word-break: break-word; }',
|
| 47 |
+
'.' + ID_PREFIX + '-input { display: flex; border-top: 1px solid #333; background: #1d1d2e; }',
|
| 48 |
+
'.' + ID_PREFIX + '-input input { flex: 1; padding: 10px 12px; border: none; outline: none; background: #1d1d2e; color: #fff; font-size: 14px; }',
|
| 49 |
+
'.' + ID_PREFIX + '-input button { background: #e84393; color: #fff; border: none; padding: 0 16px; cursor: pointer; font-size: 14px; }',
|
| 50 |
+
'.' + ID_PREFIX + '-input button:disabled { opacity: .5; cursor: wait; }',
|
| 51 |
+
'.' + ID_PREFIX + '-input button:hover:not(:disabled) { background: #d63384; }'
|
| 52 |
+
].join('\n');
|
| 53 |
+
document.head.appendChild(st);
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
function el(tag, className, html) {
|
| 57 |
+
var n = document.createElement(tag);
|
| 58 |
+
if (className) n.className = className;
|
| 59 |
+
if (html !== undefined) n.innerHTML = html;
|
| 60 |
+
return n;
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
function escapeHtml(s) {
|
| 64 |
+
return String(s).replace(/[&<>"']/g, function (c) {
|
| 65 |
+
return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c];
|
| 66 |
+
});
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
function build() {
|
| 70 |
+
injectCss();
|
| 71 |
+
|
| 72 |
+
var fab = el('button', ID_PREFIX + '-fab', '💬');
|
| 73 |
+
fab.title = 'Chat với trợ lý gợi ý anime/manga';
|
| 74 |
+
|
| 75 |
+
var box = el('div', ID_PREFIX + '-box');
|
| 76 |
+
box.innerHTML =
|
| 77 |
+
'<div class="' + ID_PREFIX + '-head">' +
|
| 78 |
+
'<h3>Chatbox ANIVIETDB</h3>' +
|
| 79 |
+
'<select class="' + ID_PREFIX + '-model" id="' + ID_PREFIX + '-model" aria-label="Chọn mô hình">' +
|
| 80 |
+
Object.keys(MODELS).map(function (k) {
|
| 81 |
+
return '<option value="' + k + '">' + MODEL_LABELS[k] + '</option>';
|
| 82 |
+
}).join('') +
|
| 83 |
+
'</select>' +
|
| 84 |
+
'<button class="' + ID_PREFIX + '-close" id="' + ID_PREFIX + '-close" aria-label="Đóng">✕</button>' +
|
| 85 |
+
'</div>';
|
| 86 |
+
var msgs = el('div', ID_PREFIX + '-msgs');
|
| 87 |
+
msgs.innerHTML = '<div class="' + ID_PREFIX + '-ai">Chào bạn! Mình là trợ lý gợi ý anime, manga và light novel của ANIVIETDB. Bạn muốn xem gì hôm nay?</div>';
|
| 88 |
+
box.appendChild(msgs);
|
| 89 |
+
var inputRow = el('div', ID_PREFIX + '-input');
|
| 90 |
+
var inp = el('input');
|
| 91 |
+
inp.type = 'text';
|
| 92 |
+
inp.placeholder = 'Nhập tin nhắn...';
|
| 93 |
+
inp.autocomplete = 'off';
|
| 94 |
+
var sendBtn = el('button', null, 'Gửi');
|
| 95 |
+
inputRow.appendChild(inp);
|
| 96 |
+
inputRow.appendChild(sendBtn);
|
| 97 |
+
box.appendChild(inputRow);
|
| 98 |
+
document.body.appendChild(box);
|
| 99 |
+
document.body.appendChild(fab);
|
| 100 |
+
|
| 101 |
+
var closeBtn = box.querySelector('#' + ID_PREFIX + '-close');
|
| 102 |
+
var modelSel = box.querySelector('#' + ID_PREFIX + '-model');
|
| 103 |
+
|
| 104 |
+
function currentModel() {
|
| 105 |
+
var saved = null;
|
| 106 |
+
try { saved = localStorage.getItem('anivietdb-chat-model'); } catch (e) {}
|
| 107 |
+
return MODELS[saved] ? saved : 'nivip';
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
modelSel.value = currentModel();
|
| 111 |
+
modelSel.addEventListener('change', function () {
|
| 112 |
+
try { localStorage.setItem('anivietdb-chat-model', modelSel.value); } catch (e) {}
|
| 113 |
+
});
|
| 114 |
+
|
| 115 |
+
function toggle(show) {
|
| 116 |
+
box.style.display = show ? 'block' : 'none';
|
| 117 |
+
fab.style.display = show ? 'none' : 'block';
|
| 118 |
+
if (show) inp.focus();
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
function send() {
|
| 122 |
+
var msg = inp.value.trim();
|
| 123 |
+
if (!msg) return;
|
| 124 |
+
inp.value = '';
|
| 125 |
+
msgs.innerHTML += '<div class="' + ID_PREFIX + '-user">' + escapeHtml(msg) + '</div>';
|
| 126 |
+
msgs.scrollTop = msgs.scrollHeight;
|
| 127 |
+
var p = el('div', ID_PREFIX + '-ai', '...');
|
| 128 |
+
msgs.appendChild(p);
|
| 129 |
+
msgs.scrollTop = msgs.scrollHeight;
|
| 130 |
+
sendBtn.disabled = true;
|
| 131 |
+
inp.disabled = true;
|
| 132 |
+
fetch(baseUrl().replace(/\/+$/, '') + '/chat', {
|
| 133 |
+
method: 'POST',
|
| 134 |
+
headers: { 'Content-Type': 'application/json' },
|
| 135 |
+
body: JSON.stringify({ message: msg, model: modelSel.value })
|
| 136 |
+
}).then(function (r) {
|
| 137 |
+
return r.json().catch(function () { return {}; }).then(function (d) {
|
| 138 |
+
if (!r.ok) {
|
| 139 |
+
var e = new Error((d && d.error) || ('HTTP ' + r.status));
|
| 140 |
+
e.status = r.status;
|
| 141 |
+
throw e;
|
| 142 |
+
}
|
| 143 |
+
return d;
|
| 144 |
+
});
|
| 145 |
+
}).then(function (d) {
|
| 146 |
+
p.className = ID_PREFIX + '-ai';
|
| 147 |
+
p.textContent = d.reply || '...';
|
| 148 |
+
msgs.scrollTop = msgs.scrollHeight;
|
| 149 |
+
}).catch(function (e) {
|
| 150 |
+
p.className = ID_PREFIX + '-err';
|
| 151 |
+
p.textContent = 'Lỗi: ' + (e.message || 'không rõ');
|
| 152 |
+
msgs.scrollTop = msgs.scrollHeight;
|
| 153 |
+
}).finally(function () {
|
| 154 |
+
sendBtn.disabled = false;
|
| 155 |
+
inp.disabled = false;
|
| 156 |
+
inp.focus();
|
| 157 |
+
});
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
fab.addEventListener('click', function () { toggle(true); });
|
| 161 |
+
closeBtn.addEventListener('click', function () { toggle(false); });
|
| 162 |
+
sendBtn.addEventListener('click', send);
|
| 163 |
+
inp.addEventListener('keydown', function (e) { if (e.key === 'Enter') send(); });
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
function init() {
|
| 167 |
+
if (document.getElementById(ID_PREFIX + '-fab')) return;
|
| 168 |
+
build();
|
| 169 |
+
}
|
| 170 |
+
|
| 171 |
+
if (document.readyState === 'loading') {
|
| 172 |
+
document.addEventListener('DOMContentLoaded', init);
|
| 173 |
+
} else {
|
| 174 |
+
init();
|
| 175 |
+
}
|
| 176 |
+
})();
|
control-panel.html
ADDED
|
@@ -0,0 +1,1348 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="vi">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
| 7 |
+
<meta http-equiv="Pragma" content="no-cache">
|
| 8 |
+
<meta http-equiv="Expires" content="0">
|
| 9 |
+
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><rect width='100' height='100' rx='22' fill='%23151515'/><text x='50' y='68' font-size='52' font-family='Arial' font-weight='bold' fill='%23ff5e00' text-anchor='middle'>A</text></svg>">
|
| 10 |
+
<title>Control Panel - ANIVIETDB</title>
|
| 11 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
|
| 12 |
+
<link rel="stylesheet" href="style.css">
|
| 13 |
+
<style>
|
| 14 |
+
:root {
|
| 15 |
+
--cp-bg: #101216;
|
| 16 |
+
--cp-panel: #16181f;
|
| 17 |
+
--cp-panel-2: #1d2029;
|
| 18 |
+
--cp-border: #262a35;
|
| 19 |
+
--cp-ink: #e8eaf2;
|
| 20 |
+
--cp-muted: #8b90a2;
|
| 21 |
+
--cp-accent: #ff5e00;
|
| 22 |
+
--cp-accent-2: #2abdbb;
|
| 23 |
+
--cp-purple: #a3419f;
|
| 24 |
+
}
|
| 25 |
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
| 26 |
+
html, body { height: 100%; }
|
| 27 |
+
body {
|
| 28 |
+
font-family: 'Inter', 'Roboto', sans-serif;
|
| 29 |
+
background: var(--cp-bg);
|
| 30 |
+
min-height: 100vh; color: var(--cp-ink);
|
| 31 |
+
display: flex;
|
| 32 |
+
flex-direction: row;
|
| 33 |
+
padding: 0;
|
| 34 |
+
}
|
| 35 |
+
::-webkit-scrollbar { width: 10px; height: 10px; }
|
| 36 |
+
::-webkit-scrollbar-thumb { background: #2e3342; border-radius: 8px; }
|
| 37 |
+
::-webkit-scrollbar-track { background: transparent; }
|
| 38 |
+
|
| 39 |
+
/* ---------- Sidebar ---------- */
|
| 40 |
+
.cp-side {
|
| 41 |
+
width: 248px; min-width: 248px;
|
| 42 |
+
background: var(--cp-panel);
|
| 43 |
+
border-right: 1px solid var(--cp-border);
|
| 44 |
+
display: flex; flex-direction: column;
|
| 45 |
+
position: sticky; top: 0; height: 100vh;
|
| 46 |
+
z-index: 40;
|
| 47 |
+
}
|
| 48 |
+
.cp-logo {
|
| 49 |
+
display: flex; align-items: center; gap: 10px;
|
| 50 |
+
padding: 18px 20px; border-bottom: 1px solid var(--cp-border);
|
| 51 |
+
}
|
| 52 |
+
.cp-logo-mark {
|
| 53 |
+
width: 38px; height: 38px; border-radius: 10px;
|
| 54 |
+
background: linear-gradient(135deg, #ff5e00, #a3419f);
|
| 55 |
+
display: grid; place-items: center;
|
| 56 |
+
font-weight: 800; font-size: 18px; color: #fff;
|
| 57 |
+
box-shadow: 0 4px 14px rgba(255,94,0,.35);
|
| 58 |
+
}
|
| 59 |
+
.cp-logo-name { font-weight: 700; font-size: 14px; line-height: 1.15; }
|
| 60 |
+
.cp-logo-sub { font-size: 11px; color: var(--cp-muted); }
|
| 61 |
+
.cp-nav { padding: 12px 12px; flex: 1; overflow-y: auto; }
|
| 62 |
+
.cp-nav-label {
|
| 63 |
+
font-size: 10px; text-transform: uppercase; letter-spacing: .12em;
|
| 64 |
+
color: #5d6275; padding: 12px 12px 6px;
|
| 65 |
+
}
|
| 66 |
+
.cp-nav-item {
|
| 67 |
+
display: flex; align-items: center; gap: 12px;
|
| 68 |
+
padding: 10px 12px; margin: 2px 0;
|
| 69 |
+
border-radius: 10px; color: var(--cp-muted);
|
| 70 |
+
font-size: 13.5px; font-weight: 500;
|
| 71 |
+
cursor: pointer; text-decoration: none; transition: all .15s;
|
| 72 |
+
border: 1px solid transparent;
|
| 73 |
+
}
|
| 74 |
+
.cp-nav-item .ico { width: 18px; text-align: center; font-size: 15px; }
|
| 75 |
+
.cp-nav-item:hover { background: var(--cp-panel-2); color: var(--cp-ink); }
|
| 76 |
+
.cp-nav-item.active {
|
| 77 |
+
background: linear-gradient(135deg, rgba(255,94,0,.16), rgba(163,65,159,.14));
|
| 78 |
+
border-color: rgba(255,94,0,.35);
|
| 79 |
+
color: #fff;
|
| 80 |
+
}
|
| 81 |
+
.cp-nav-foot {
|
| 82 |
+
padding: 14px 16px; border-top: 1px solid var(--cp-border);
|
| 83 |
+
display: flex; align-items: center; gap: 10px;
|
| 84 |
+
}
|
| 85 |
+
.cp-avatar {
|
| 86 |
+
width: 36px; height: 36px; border-radius: 50%;
|
| 87 |
+
background: linear-gradient(135deg, #2abdbb, #2b1d54);
|
| 88 |
+
display: grid; place-items: center; font-weight: 700; color: #fff;
|
| 89 |
+
flex-shrink: 0;
|
| 90 |
+
}
|
| 91 |
+
.cp-nav-foot-info { min-width: 0; flex: 1; }
|
| 92 |
+
.cp-nav-foot-name { font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
| 93 |
+
.cp-nav-foot-sub { font-size: 11px; color: var(--cp-muted); }
|
| 94 |
+
.cp-auth-btn {
|
| 95 |
+
background: none; border: 1px solid var(--cp-border); color: var(--cp-muted);
|
| 96 |
+
border-radius: 8px; padding: 5px 10px; font-size: 11px; font-weight: 600;
|
| 97 |
+
cursor: pointer; text-decoration: none; font-family: inherit; transition: all .15s;
|
| 98 |
+
}
|
| 99 |
+
.cp-auth-btn:hover { color: var(--cp-accent); border-color: rgba(255,94,0,.5); }
|
| 100 |
+
|
| 101 |
+
/* ---------- Main ---------- */
|
| 102 |
+
.cp-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
|
| 103 |
+
.cp-topbar {
|
| 104 |
+
display: flex; align-items: center; justify-content: space-between; gap: 16px;
|
| 105 |
+
padding: 14px 24px;
|
| 106 |
+
background: rgba(16,18,22,.86);
|
| 107 |
+
backdrop-filter: blur(12px);
|
| 108 |
+
-webkit-backdrop-filter: blur(12px);
|
| 109 |
+
border-bottom: 1px solid var(--cp-border);
|
| 110 |
+
position: sticky; top: 0; z-index: 30;
|
| 111 |
+
}
|
| 112 |
+
.cp-topbar-title { font-size: 16px; font-weight: 700; }
|
| 113 |
+
.cp-topbar-title small { display: block; font-size: 11.5px; color: var(--cp-muted); font-weight: 500; }
|
| 114 |
+
.cp-topbar-links { display: flex; align-items: center; gap: 8px; }
|
| 115 |
+
.cp-btn {
|
| 116 |
+
padding: 8px 14px; border-radius: 10px; border: 1px solid var(--cp-border);
|
| 117 |
+
background: var(--cp-panel-2); color: var(--cp-ink); font-size: 12.5px; font-weight: 600;
|
| 118 |
+
cursor: pointer; text-decoration: none; display: inline-block; font-family: inherit;
|
| 119 |
+
transition: all .15s;
|
| 120 |
+
}
|
| 121 |
+
.cp-btn:hover { border-color: var(--cp-accent); color: var(--cp-accent); }
|
| 122 |
+
.cp-btn-sm { padding: 5px 10px; font-size: 11.5px; border-radius: 8px; }
|
| 123 |
+
.cp-btn-danger { border-color: rgba(255,138,138,.4); color: #ff8a8a; }
|
| 124 |
+
.cp-btn-danger:hover { background: rgba(255,138,138,.08); border-color: #ff8a8a; }
|
| 125 |
+
.cp-burger { display: none; background: none; border: 1px solid var(--cp-border); color: var(--cp-ink); border-radius: 10px; padding: 8px 12px; font-size: 16px; }
|
| 126 |
+
|
| 127 |
+
.cp-content { padding: 24px; flex: 1; }
|
| 128 |
+
/* ---------- Cards / stats / tables ---------- */
|
| 129 |
+
.card {
|
| 130 |
+
background: var(--cp-panel);
|
| 131 |
+
border: 1px solid var(--cp-border);
|
| 132 |
+
border-radius: 16px; padding: 22px; margin-bottom: 24px;
|
| 133 |
+
}
|
| 134 |
+
.card h2 { font-size: 16px; margin-bottom: 4px; }
|
| 135 |
+
.card .sub { font-size: 12px; color: var(--cp-muted); margin-bottom: 16px; }
|
| 136 |
+
table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
| 137 |
+
th, td { text-align: left; padding: 10px 12px; border-bottom: 1px solid rgba(120,100,230,0.15); }
|
| 138 |
+
th { color: #9a9ab0; font-weight: 500; font-size: 12px; text-transform: uppercase; letter-spacing: .4px; }
|
| 139 |
+
tr:hover td { background: rgba(120,100,230,0.06); }
|
| 140 |
+
.badge { display: inline-block; padding: 2px 8px; border-radius: 20px; font-size: 11px; font-weight: 700; }
|
| 141 |
+
.badge-admin { background: rgba(126,231,135,0.15); color: #7ee787; }
|
| 142 |
+
.badge-user { background: rgba(120,100,230,0.15); color: #b9adff; }
|
| 143 |
+
.btn-del {
|
| 144 |
+
padding: 5px 12px; border-radius: 8px; border: 1px solid rgba(255,138,138,0.5);
|
| 145 |
+
background: transparent; color: #ff8a8a; font-size: 12px; font-weight: 600; cursor: pointer;
|
| 146 |
+
}
|
| 147 |
+
.btn-del:hover { background: rgba(255,138,138,0.15); }
|
| 148 |
+
.btn-del[disabled] { opacity: .4; cursor: not-allowed; }
|
| 149 |
+
.stat-grid {
|
| 150 |
+
display: grid;
|
| 151 |
+
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
| 152 |
+
gap: 14px;
|
| 153 |
+
margin-bottom: 24px;
|
| 154 |
+
}
|
| 155 |
+
.stat-card {
|
| 156 |
+
background: var(--cp-panel);
|
| 157 |
+
border: 1px solid var(--cp-border);
|
| 158 |
+
border-radius: 14px;
|
| 159 |
+
padding: 16px 18px;
|
| 160 |
+
display: flex; flex-direction: column; gap: 6px;
|
| 161 |
+
transition: border-color .2s;
|
| 162 |
+
}
|
| 163 |
+
.stat-card:hover { border-color: rgba(255,94,0,.4); }
|
| 164 |
+
.stat-label { font-size: 12px; color: var(--cp-muted); display: flex; align-items: center; gap: 8px; }
|
| 165 |
+
.stat-label .dot { width: 8px; height: 8px; border-radius: 50%; }
|
| 166 |
+
.stat-value { font-size: 22px; font-weight: 800; letter-spacing: -.01em; }
|
| 167 |
+
.stat-value small { font-size: 12px; color: var(--cp-muted); font-weight: 500; }
|
| 168 |
+
.stat-delta { font-size: 11.5px; font-weight: 600; }
|
| 169 |
+
.stat-delta.up { color: #2abdbb; }
|
| 170 |
+
.stat-delta.down { color: #ff5e00; }
|
| 171 |
+
.content-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
| 172 |
+
@media (max-width: 720px) { .content-grid { grid-template-columns: 1fr; } }
|
| 173 |
+
.content-box {
|
| 174 |
+
border: 1px solid rgba(120,100,230,0.2); border-radius: 12px; padding: 14px;
|
| 175 |
+
background: rgba(255,255,255,0.02);
|
| 176 |
+
}
|
| 177 |
+
.content-box h3 { font-size: 14px; margin-bottom: 10px; color: #b9adff; }
|
| 178 |
+
.content-box ul { list-style: none; max-height: 320px; overflow-y: auto; }
|
| 179 |
+
.content-box li {
|
| 180 |
+
padding: 7px 10px; border-radius: 8px; font-size: 12.5px;
|
| 181 |
+
display: flex; justify-content: space-between; gap: 10px;
|
| 182 |
+
}
|
| 183 |
+
.content-box li:nth-child(odd) { background: rgba(120,100,230,0.07); }
|
| 184 |
+
.content-box li .t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
| 185 |
+
.content-box li .s { color: #9a9ab0; flex-shrink: 0; font-size: 11px; }
|
| 186 |
+
.nsfw-tag { color: #ff8a8a; font-weight: 700; }
|
| 187 |
+
.sfw-tag { color: #7ee787; font-weight: 700; }
|
| 188 |
+
.cat-tabs { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 14px; }
|
| 189 |
+
.cat-tab {
|
| 190 |
+
padding: 7px 16px; border-radius: 20px; border: 1px solid rgba(120,100,230,0.3);
|
| 191 |
+
background: transparent; color: #9a9ab0; font-size: 13px; font-weight: 600; cursor: pointer;
|
| 192 |
+
transition: all .2s; font-family: inherit;
|
| 193 |
+
}
|
| 194 |
+
.cat-tab:hover { color: #fff; border-color: #8b7bff; }
|
| 195 |
+
.cat-tab.active { background: linear-gradient(135deg, #7c5cff, #5a3bd6); color: #fff; border-color: transparent; }
|
| 196 |
+
.btn-go {
|
| 197 |
+
display: inline-block; padding: 4px 12px; border-radius: 8px; font-size: 12px; font-weight: 600;
|
| 198 |
+
border: 1px solid rgba(126,231,135,0.45); color: #7ee787; text-decoration: none; text-align: center;
|
| 199 |
+
}
|
| 200 |
+
.btn-go:hover { background: rgba(126,231,135,0.15); }
|
| 201 |
+
.table-wrap { max-height: 420px; overflow-y: auto; border: 1px solid rgba(120,100,230,0.15); border-radius: 12px; }
|
| 202 |
+
.msg { color: #9a9ab0; font-size: 13px; padding: 8px 0; }
|
| 203 |
+
.ok { color: #7ee787; }
|
| 204 |
+
.err { color: #ff8a8a; }
|
| 205 |
+
|
| 206 |
+
.chip-row { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 18px; }
|
| 207 |
+
.chip {
|
| 208 |
+
background: var(--cp-panel);
|
| 209 |
+
border: 1px solid var(--cp-border);
|
| 210 |
+
color: var(--cp-muted);
|
| 211 |
+
border-radius: 999px;
|
| 212 |
+
padding: 7px 16px;
|
| 213 |
+
font-size: 12.5px; font-weight: 600;
|
| 214 |
+
cursor: pointer; transition: all .15s;
|
| 215 |
+
font-family: inherit;
|
| 216 |
+
}
|
| 217 |
+
.chip:hover { color: var(--cp-ink); border-color: #3a4052; }
|
| 218 |
+
.chip.active { background: linear-gradient(135deg, #ff5e00, #e04a00); color: #fff; border-color: transparent; box-shadow: 0 4px 14px rgba(255,94,0,.3); }
|
| 219 |
+
|
| 220 |
+
.product-grid {
|
| 221 |
+
display: grid;
|
| 222 |
+
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
|
| 223 |
+
gap: 14px;
|
| 224 |
+
}
|
| 225 |
+
.product-card {
|
| 226 |
+
background: var(--cp-panel);
|
| 227 |
+
border: 1px solid var(--cp-border);
|
| 228 |
+
border-radius: 14px;
|
| 229 |
+
overflow: hidden;
|
| 230 |
+
display: flex; flex-direction: column;
|
| 231 |
+
transition: transform .22s ease, border-color .22s ease, box-shadow .22s ease;
|
| 232 |
+
}
|
| 233 |
+
.product-card:hover { transform: translateY(-6px); border-color: rgba(255,94,0,.45); box-shadow: 0 14px 30px rgba(0,0,0,.45); }
|
| 234 |
+
.product-thumb { position: relative; aspect-ratio: 3/3.6; overflow: hidden; background: #0d0f14; }
|
| 235 |
+
.product-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
| 236 |
+
.product-thumb::after { content: ""; position: absolute; inset: 0; background: linear-gradient(to top, rgba(13,15,20,.55), transparent 40%); pointer-events: none; }
|
| 237 |
+
.product-tag {
|
| 238 |
+
position: absolute; top: 10px; left: 10px; z-index: 2;
|
| 239 |
+
background: rgba(13,15,20,.8); backdrop-filter: blur(4px);
|
| 240 |
+
color: #ffb48a; font-size: 10px; font-weight: 700; letter-spacing: .03em;
|
| 241 |
+
border-radius: 999px; padding: 4px 10px;
|
| 242 |
+
border: 1px solid rgba(255,94,0,.4);
|
| 243 |
+
}
|
| 244 |
+
.product-body { padding: 10px 12px 12px; display: flex; flex-direction: column; gap: 5px; flex: 1; }
|
| 245 |
+
.product-anime { font-size: 10px; color: var(--cp-muted); text-transform: uppercase; letter-spacing: .06em; font-weight: 600; }
|
| 246 |
+
.product-name { font-size: 12.5px; font-weight: 600; line-height: 1.35; color: var(--cp-ink); }
|
| 247 |
+
.product-stock { font-size: 11px; font-weight: 700; }
|
| 248 |
+
.product-stock.low { color: #ff8a8a; }
|
| 249 |
+
.product-stock.ok { color: #2abdbb; }
|
| 250 |
+
.product-price { display: flex; align-items: baseline; gap: 8px; }
|
| 251 |
+
.price-now { font-size: 15px; font-weight: 800; color: #e84393; }
|
| 252 |
+
.price-old { font-size: 12px; color: var(--cp-muted); text-decoration: line-through; }
|
| 253 |
+
|
| 254 |
+
.empty-shop { text-align: center; padding: 50px 20px; color: var(--cp-muted); }
|
| 255 |
+
.empty-shop .big { font-size: 34px; margin-bottom: 10px; }
|
| 256 |
+
/* ---------- Confirm modal ---------- */
|
| 257 |
+
.confirm-overlay {
|
| 258 |
+
position: fixed; inset: 0; z-index: 1000; display: none;
|
| 259 |
+
align-items: center; justify-content: center; padding: 20px;
|
| 260 |
+
background: rgba(8, 9, 18, 0.72); backdrop-filter: blur(4px);
|
| 261 |
+
}
|
| 262 |
+
.confirm-overlay.active { display: flex; }
|
| 263 |
+
.confirm-box {
|
| 264 |
+
width: 100%; max-width: 380px; background: #16182b;
|
| 265 |
+
border: 1px solid rgba(255,138,138,0.35); border-radius: 16px;
|
| 266 |
+
padding: 24px; box-shadow: 0 18px 50px rgba(0,0,0,.55);
|
| 267 |
+
animation: confirmPop .22s ease;
|
| 268 |
+
}
|
| 269 |
+
@keyframes confirmPop {
|
| 270 |
+
from { transform: scale(.92); opacity: 0; }
|
| 271 |
+
to { transform: scale(1); opacity: 1; }
|
| 272 |
+
}
|
| 273 |
+
.confirm-icon {
|
| 274 |
+
width: 52px; height: 52px; margin: 0 auto 14px; border-radius: 50%;
|
| 275 |
+
display: flex; align-items: center; justify-content: center;
|
| 276 |
+
font-size: 24px; color: #ff8a8a; background: rgba(255,138,138,0.12);
|
| 277 |
+
border: 1px solid rgba(255,138,138,0.3);
|
| 278 |
+
}
|
| 279 |
+
.confirm-title { font-size: 17px; font-weight: 700; text-align: center; color: #fff; margin-bottom: 8px; }
|
| 280 |
+
.confirm-text { font-size: 13px; text-align: center; color: #9a9ab0; margin-bottom: 22px; line-height: 1.6; }
|
| 281 |
+
.confirm-text b { color: #ff8a8a; }
|
| 282 |
+
.confirm-actions { display: flex; gap: 10px; }
|
| 283 |
+
.confirm-btn {
|
| 284 |
+
flex: 1; padding: 11px 0; border-radius: 10px; font-size: 14px; font-weight: 700; cursor: pointer;
|
| 285 |
+
border: 1px solid transparent; transition: all .2s; font-family: inherit;
|
| 286 |
+
}
|
| 287 |
+
.confirm-cancel { background: transparent; border-color: rgba(120,100,230,0.4); color: #b9adff; }
|
| 288 |
+
.confirm-cancel:hover { background: rgba(120,100,230,0.1); }
|
| 289 |
+
.confirm-ok { background: linear-gradient(135deg, #ff6b81, #f0334a); color: #fff; }
|
| 290 |
+
.confirm-ok:hover { filter: brightness(1.1); }
|
| 291 |
+
.cp-reason {
|
| 292 |
+
width: 100%; margin: 0 0 18px; padding: 11px 13px;
|
| 293 |
+
background: #0f1122; border: 1px solid rgba(255,138,138,0.35);
|
| 294 |
+
border-radius: 10px; color: #e8eaf2; font-size: 13px; font-family: inherit;
|
| 295 |
+
resize: vertical; outline: none;
|
| 296 |
+
}
|
| 297 |
+
.cp-reason:focus { border-color: #ff8a8a; }
|
| 298 |
+
.cp-reason-err { display: none; font-size: 12px; color: #ff8a8a; margin: -10px 0 14px; }
|
| 299 |
+
.cp-reason-err.show { display: block; }
|
| 300 |
+
#orderModal .confirm-ok.ok-ship { background: linear-gradient(135deg, #2abdbb, #1d9a99); }
|
| 301 |
+
#orderModal .confirm-ok.ok-ship:hover { filter: brightness(1.1); }
|
| 302 |
+
|
| 303 |
+
/* ---------- Mobile ---------- */
|
| 304 |
+
@media (max-width: 992px) {
|
| 305 |
+
.cp-side { position: fixed; left: 0; top: 0; transform: translateX(-100%); transition: transform .25s; }
|
| 306 |
+
.cp-side.open { transform: translateX(0); box-shadow: 0 0 40px rgba(0,0,0,.6); }
|
| 307 |
+
.cp-burger { display: block; }
|
| 308 |
+
.cp-content { padding: 16px; }
|
| 309 |
+
.stat-grid { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; }
|
| 310 |
+
}
|
| 311 |
+
</style>
|
| 312 |
+
</head>
|
| 313 |
+
<body>
|
| 314 |
+
<!-- Sidebar -->
|
| 315 |
+
<aside class="cp-side" id="cpSide">
|
| 316 |
+
<div class="cp-logo">
|
| 317 |
+
<div class="cp-logo-mark">A</div>
|
| 318 |
+
<div>
|
| 319 |
+
<div class="cp-logo-name">ANIVIETDB Control</div>
|
| 320 |
+
<div class="cp-logo-sub">Panel · Quản trị</div>
|
| 321 |
+
</div>
|
| 322 |
+
</div>
|
| 323 |
+
<nav class="cp-nav">
|
| 324 |
+
<div class="cp-nav-label">Tổng quan</div>
|
| 325 |
+
<a class="cp-nav-item active" data-panel="overview"><span class="ico">📈</span> Thống kê</a>
|
| 326 |
+
<div class="cp-nav-label">Quản trị</div>
|
| 327 |
+
<a class="cp-nav-item" data-panel="users"><span class="ico">👥</span> Người dùng</a>
|
| 328 |
+
<a class="cp-nav-item" data-panel="content"><span class="ico">📦</span> Nội dung</a>
|
| 329 |
+
<div class="cp-nav-label">Cửa hàng</div>
|
| 330 |
+
<a class="cp-nav-item" data-panel="products"><span class="ico">🎓</span> Sản phẩm</a>
|
| 331 |
+
<a class="cp-nav-item" data-panel="orders"><span class="ico">🛒</span> Đơn hàng</a>
|
| 332 |
+
<a class="cp-nav-item" data-panel="customers"><span class="ico">👥</span> Khách hàng</a>
|
| 333 |
+
<a class="cp-nav-item" data-panel="reports"><span class="ico">📈</span> Báo cáo</a>
|
| 334 |
+
<div class="cp-nav-label">Hệ thống</div>
|
| 335 |
+
<a class="cp-nav-item" data-panel="updates"><span class="ico">🔄</span> Cập nhật</a>
|
| 336 |
+
<div class="cp-nav-label">Liên kết</div>
|
| 337 |
+
<a class="cp-nav-item" href="index.html"><span class="ico">🏠</span> Trang chủ</a>
|
| 338 |
+
<a class="cp-nav-item" href="sfw/index.html"><span class="ico">🎬</span> Xem Anime</a>
|
| 339 |
+
<a class="cp-nav-item" href="shop/"><span class="ico">🛒</span> Cửa hàng</a>
|
| 340 |
+
</nav>
|
| 341 |
+
<div class="cp-nav-foot">
|
| 342 |
+
<div class="cp-avatar" id="userAvatar">A</div>
|
| 343 |
+
<div class="cp-nav-foot-info">
|
| 344 |
+
<div class="cp-nav-foot-name" id="who">—</div>
|
| 345 |
+
<div class="cp-nav-foot-sub" id="userRole">Chưa xác thực</div>
|
| 346 |
+
</div>
|
| 347 |
+
<button class="cp-auth-btn" id="logoutBtn">Thoát</button>
|
| 348 |
+
</div>
|
| 349 |
+
</aside>
|
| 350 |
+
|
| 351 |
+
<!-- Main -->
|
| 352 |
+
<main class="cp-main">
|
| 353 |
+
<div class="cp-topbar">
|
| 354 |
+
<div style="display:flex;align-items:center;gap:12px;">
|
| 355 |
+
<button class="cp-burger" id="cpBurger">☰</button>
|
| 356 |
+
<div class="cp-topbar-title">
|
| 357 |
+
Thống kê
|
| 358 |
+
<small>ANIVIETDB Control Panel</small>
|
| 359 |
+
</div>
|
| 360 |
+
</div>
|
| 361 |
+
<div class="cp-topbar-links">
|
| 362 |
+
<a class="cp-btn" href="shop/">🛒 Cửa hàng</a>
|
| 363 |
+
<a class="cp-btn" href="index.html">🏠 Trang chủ</a>
|
| 364 |
+
<a class="cp-btn" href="sfw/index.html">🎬 Xem Anime</a>
|
| 365 |
+
</div>
|
| 366 |
+
</div>
|
| 367 |
+
|
| 368 |
+
<div class="cp-content">
|
| 369 |
+
<div id="accessDenied" style="display:none;">
|
| 370 |
+
<div class="card">
|
| 371 |
+
<div class="msg" id="deniedMsg">Bạn không có quyền truy cập.</div>
|
| 372 |
+
</div>
|
| 373 |
+
</div>
|
| 374 |
+
|
| 375 |
+
<div id="panel">
|
| 376 |
+
<!-- Panel: Thống kê -->
|
| 377 |
+
<div id="panel-overview">
|
| 378 |
+
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:12px;">
|
| 379 |
+
<span style="color:var(--cp-muted);font-size:13px;" id="overviewInfo"></span>
|
| 380 |
+
<button class="cp-btn" id="refreshOverview">🔄 Làm mới</button>
|
| 381 |
+
</div>
|
| 382 |
+
<div class="stat-grid" id="overviewStats"></div>
|
| 383 |
+
<div class="card">
|
| 384 |
+
<h2>Nội dung trang web</h2>
|
| 385 |
+
<p class="sub">Số lượng theo từng phân loại (anime / manga / light novel / gallery)</p>
|
| 386 |
+
<div class="stat-grid" id="contentStats"></div>
|
| 387 |
+
</div>
|
| 388 |
+
</div>
|
| 389 |
+
|
| 390 |
+
<!-- Panel: Người dùng -->
|
| 391 |
+
<div id="panel-users" style="display:none;">
|
| 392 |
+
<div class="card">
|
| 393 |
+
<h2>Người dùng</h2>
|
| 394 |
+
<p class="sub">Tất cả tài khoản và lần đăng nhập gần nhất</p>
|
| 395 |
+
<div class="msg" id="usersMsg"></div>
|
| 396 |
+
<table>
|
| 397 |
+
<thead>
|
| 398 |
+
<tr>
|
| 399 |
+
<th>ID</th>
|
| 400 |
+
<th>Tên đăng nhập</th>
|
| 401 |
+
<th>Email</th>
|
| 402 |
+
<th>Vai trò</th>
|
| 403 |
+
<th>Số lần tham gia</th>
|
| 404 |
+
<th>Đăng nhập gần nhất</th>
|
| 405 |
+
<th>Ngày tạo</th>
|
| 406 |
+
<th></th>
|
| 407 |
+
</tr>
|
| 408 |
+
</thead>
|
| 409 |
+
<tbody id="usersBody"></tbody>
|
| 410 |
+
</table>
|
| 411 |
+
</div>
|
| 412 |
+
</div>
|
| 413 |
+
|
| 414 |
+
<!-- Panel: Nội dung -->
|
| 415 |
+
<div id="panel-content" style="display:none;">
|
| 416 |
+
<div class="card">
|
| 417 |
+
<h2>Quản lý trang web</h2>
|
| 418 |
+
<p class="sub">Nội dung đã upload (anime / manga)</p>
|
| 419 |
+
<div class="cat-tabs" id="catTabs"></div>
|
| 420 |
+
<div class="table-wrap">
|
| 421 |
+
<table>
|
| 422 |
+
<thead>
|
| 423 |
+
<tr>
|
| 424 |
+
<th style="width:50px;">#</th>
|
| 425 |
+
<th>Tên</th>
|
| 426 |
+
<th style="width:90px;">Phân loại</th>
|
| 427 |
+
<th style="width:140px;">Trạng thái</th>
|
| 428 |
+
<th style="width:70px;"></th>
|
| 429 |
+
</tr>
|
| 430 |
+
</thead>
|
| 431 |
+
<tbody id="contentBody"></tbody>
|
| 432 |
+
</table>
|
| 433 |
+
</div>
|
| 434 |
+
<div class="msg" id="contentMsg"></div>
|
| 435 |
+
</div>
|
| 436 |
+
</div>
|
| 437 |
+
<!-- Panel: Sản phẩm -->
|
| 438 |
+
<div id="panel-products" style="display:none;">
|
| 439 |
+
<div class="card">
|
| 440 |
+
<h2>Quản lý sản phẩm</h2>
|
| 441 |
+
<p class="sub">Figure, đồ lưu niệm, manga & Blu-ray đang bán trên ANIVIETDB Shop</p>
|
| 442 |
+
<div style="display:flex;align-items:center;justify-content:space-between;margin:16px 0 10px;">
|
| 443 |
+
<span style="color:var(--cp-muted);font-size:13px;" id="cpProductsInfo">Đang tải...</span>
|
| 444 |
+
<button class="cp-btn" id="refreshProducts">🔄 Làm mới</button>
|
| 445 |
+
</div>
|
| 446 |
+
<div class="chip-row" id="cpCatChips">
|
| 447 |
+
<button class="chip active" data-cat="all">Tất cả</button>
|
| 448 |
+
</div>
|
| 449 |
+
<div class="product-grid" id="cpProductGrid"></div>
|
| 450 |
+
<div class="empty-shop" id="cpEmptyProducts" style="display:none;">
|
| 451 |
+
<div class="big">😅</div>
|
| 452 |
+
Không có sản phẩm phù hợp.
|
| 453 |
+
</div>
|
| 454 |
+
</div>
|
| 455 |
+
</div>
|
| 456 |
+
|
| 457 |
+
<!-- Panel: Đơn hàng -->
|
| 458 |
+
<div id="panel-orders" style="display:none;">
|
| 459 |
+
<div class="card">
|
| 460 |
+
<h2>Đơn hàng</h2>
|
| 461 |
+
<p class="sub">Đơn hàng từ khách hàng (từ DB shop-system)</p>
|
| 462 |
+
<div style="display:flex;align-items:center;justify-content:space-between;margin:16px 0 10px;">
|
| 463 |
+
<span style="color:var(--cp-muted);font-size:13px;" id="ordersInfo">Đang tải...</span>
|
| 464 |
+
<button class="cp-btn" id="refreshOrders">🔄 Làm mới</button>
|
| 465 |
+
</div>
|
| 466 |
+
<div class="table-wrap">
|
| 467 |
+
<table>
|
| 468 |
+
<thead>
|
| 469 |
+
<tr>
|
| 470 |
+
<th>Mã đơn</th>
|
| 471 |
+
<th>Khách hàng</th>
|
| 472 |
+
<th>Sản phẩm</th>
|
| 473 |
+
<th style="width:90px;">Số món</th>
|
| 474 |
+
<th style="width:110px;">Tổng tiền</th>
|
| 475 |
+
<th style="width:90px;">Trạng thái</th>
|
| 476 |
+
<th style="width:150px;">Thời gian</th>
|
| 477 |
+
<th style="width:110px;">Thao tác</th>
|
| 478 |
+
</tr>
|
| 479 |
+
</thead>
|
| 480 |
+
<tbody id="ordersBody"></tbody>
|
| 481 |
+
</table>
|
| 482 |
+
</div>
|
| 483 |
+
<div class="msg" id="ordersMsg"></div>
|
| 484 |
+
</div>
|
| 485 |
+
</div>
|
| 486 |
+
|
| 487 |
+
<!-- Panel: Khách hàng -->
|
| 488 |
+
<div id="panel-customers" style="display:none;">
|
| 489 |
+
<div class="card">
|
| 490 |
+
<h2>Khách hàng</h2>
|
| 491 |
+
<p class="sub">Mỗi tài khoản là 1 khách hàng — số đơn / món / chi tiêu lấy từ DB shop-system</p>
|
| 492 |
+
<div class="stat-grid" id="customerStats"></div>
|
| 493 |
+
<div style="display:flex;align-items:center;justify-content:space-between;margin:16px 0 10px;">
|
| 494 |
+
<span style="color:var(--cp-muted);font-size:13px;">Danh sách từ máy chủ <code>shop-system</code></span>
|
| 495 |
+
<button class="cp-btn" id="refreshCustomers">🔄 Làm mới</button>
|
| 496 |
+
</div>
|
| 497 |
+
<table>
|
| 498 |
+
<thead>
|
| 499 |
+
<tr>
|
| 500 |
+
<th>ID</th>
|
| 501 |
+
<th>Khách hàng</th>
|
| 502 |
+
<th>Đơn hàng</th>
|
| 503 |
+
<th>Món đã mua</th>
|
| 504 |
+
<th>Tổng chi tiêu</th>
|
| 505 |
+
<th>Điểm</th>
|
| 506 |
+
<th>Ngày tạo</th>
|
| 507 |
+
</tr>
|
| 508 |
+
</thead>
|
| 509 |
+
<tbody id="customersBody"></tbody>
|
| 510 |
+
</table>
|
| 511 |
+
</div>
|
| 512 |
+
</div>
|
| 513 |
+
|
| 514 |
+
<!-- Panel: Báo cáo -->
|
| 515 |
+
<div id="panel-reports" style="display:none;">
|
| 516 |
+
<div class="card">
|
| 517 |
+
<div style="display:flex;align-items:center;justify-content:space-between;">
|
| 518 |
+
<div>
|
| 519 |
+
<h2>Báo cáo doanh thu</h2>
|
| 520 |
+
<p class="sub">Tổng hợp doanh thu cửa hàng</p>
|
| 521 |
+
</div>
|
| 522 |
+
<button class="cp-btn" id="refreshReports">🔄 Làm mới</button>
|
| 523 |
+
</div>
|
| 524 |
+
<div class="stat-grid" id="reportStats"></div>
|
| 525 |
+
<div class="content-box">
|
| 526 |
+
<h3>Doanh thu 7 ngày gần nhất</h3>
|
| 527 |
+
<canvas id="revenueChart" width="800" height="260" style="width:100%;height:auto;display:block;background:#0d0f14;border-radius:10px;"></canvas>
|
| 528 |
+
<div class="msg" style="margin-top:10px;">Biểu đồ cập nhật khi mở panel Báo cáo.</div>
|
| 529 |
+
</div>
|
| 530 |
+
</div>
|
| 531 |
+
</div>
|
| 532 |
+
|
| 533 |
+
<!-- Panel: Cập nhật -->
|
| 534 |
+
<div id="panel-updates" style="display:none;">
|
| 535 |
+
<div class="card">
|
| 536 |
+
<h2>Cập nhật hệ thống</h2>
|
| 537 |
+
<p class="sub">Lịch sử cập nhật phiên bản ANIVIETDB</p>
|
| 538 |
+
<div class="content-box" style="line-height:1.7;">
|
| 539 |
+
<div class="update-item">
|
| 540 |
+
<h3 style="margin:0 0 4px;">v2.4 — Luồng thanh toán & hủy đơn</h3>
|
| 541 |
+
<p class="update-date">17/08/2026</p>
|
| 542 |
+
<ul>
|
| 543 |
+
<li>Đơn hàng có thêm trạng thái <code>Chờ thanh toán</code> — khách phải bấm “Tôi đã chuyển khoản” để đơn vào <code>Chờ xác nhận</code>; nếu không thanh toán trong 5 phút, đơn tự động hủy.</li>
|
| 544 |
+
<li>Trang thanh toán mới: QR + thông tin đơn (mã đơn, người mua, sản phẩm, tổng tiền), nút “Tôi đã chuyển khoản” chờ 5 giây rồi quay về.</li>
|
| 545 |
+
<li>Khách có thể hủy đơn (<code>Chờ thanh toán</code> / <code>Chờ xác nhận</code>) kèm lí do bằng popup; admin hủy bất kỳ đơn nào kèm lí do.</li>
|
| 546 |
+
<li>Đơn <code>Đã hủy</code> hiển lí do hủy; stock được hoàn lại.</li>
|
| 547 |
+
<li>Pop-up thay cho alert trình duyệt khi xác nhận / hủy đơn; đơn hàng trong shop chỉ hiển đơn của chính mình.</li>
|
| 548 |
+
<li>Sửa lỗi hiển thị “sản phẩm” trong giỏ hàng.</li>
|
| 549 |
+
</ul>
|
| 550 |
+
</div>
|
| 551 |
+
<hr style="border:none;border-top:1px solid var(--cp-border);margin:16px 0;">
|
| 552 |
+
<div class="update-item">
|
| 553 |
+
<h3 style="margin:0 0 4px;">v2.3 — Thanh toán & xác nhận đơn hàng</h3>
|
| 554 |
+
<p class="update-date">17/08/2026</p>
|
| 555 |
+
<ul>
|
| 556 |
+
<li>Sau khi đặt hàng, khách chuyển qua trang thanh toán (QR ủng hộ + mã đơn + đếm ngược thời gian thanh toán).</li>
|
| 557 |
+
<li>Admin bấm “Xác nhận” trên đơn ở trạng thái <code>Chờ xác nhận</code> → đơn chuyển sang <code>Đang giao hàng</code>, tự động thành <code>Đã giao</code> sau 5 phút.</li>
|
| 558 |
+
<li>Trang shop tự cập nhật trạng thái đơn hàng theo thời gian thực.</li>
|
| 559 |
+
</ul>
|
| 560 |
+
</div>
|
| 561 |
+
<hr style="border:none;border-top:1px solid var(--cp-border);margin:16px 0;">
|
| 562 |
+
<div class="update-item">
|
| 563 |
+
<h3 style="margin:0 0 4px;">v2.2 — Shop dừ liệu thực</h3>
|
| 564 |
+
<p class="update-date">17/08/2026</p>
|
| 565 |
+
<ul>
|
| 566 |
+
<li>Sản phẩm và đơn hàng lưu trư trên DB (worker <code>shop-system</code> + D1): danh mục sản phẩm có tồn kho (stock), mua hàng trừ stock.</li>
|
| 567 |
+
<li>Khách hàng = tài khoản: mỗi user là 1 khách hàng, thống kê đơn hàng / món / tổng chi tiêu tính từ DB.</li>
|
| 568 |
+
<li>Tổng quan & Báo cáo: doanh thu, đơn hàng, món đã bán lấy số liệu thực từ DB (không còn số dự kiến).</li>
|
| 569 |
+
<li>Control Panel: panel Đơn hàng mới + quản lý tồn kho sản phẩm (tăng/giảm stock).</li>
|
| 570 |
+
</ul>
|
| 571 |
+
</div>
|
| 572 |
+
<hr style="border:none;border-top:1px solid var(--cp-border);margin:16px 0;">
|
| 573 |
+
<div class="update-item">
|
| 574 |
+
<h3 style="margin:0 0 4px;">v2.1 — Shop & Control Panel</h3>
|
| 575 |
+
<p class="update-date">16/08/2026</p>
|
| 576 |
+
<ul>
|
| 577 |
+
<li>Thêm cửa hàng figure, đồ lưu niệm, manga & Blu-ray (shop/index.html) — giỏ hàng, đặt hàng.</li>
|
| 578 |
+
<li>Control Panel: sidebar điều hướng, thống kê, quản lý sản phẩm, khách hàng, báo cáo, cài đặt.</li>
|
| 579 |
+
<li>Trang chủ thêm stage 3 giới thiệu shop với 4 sản phẩm tiêu biểu.</li>
|
| 580 |
+
<li>Đồng bộ khách hàng vào DB D1 riêng <b>shop-system</b> qua worker <code>shop-system</code>.</li>
|
| 581 |
+
</ul>
|
| 582 |
+
</div>
|
| 583 |
+
<hr style="border:none;border-top:1px solid var(--cp-border);margin:16px 0;">
|
| 584 |
+
<div class="update-item">
|
| 585 |
+
<h3 style="margin:0 0 4px;">v2.0 — Giao diện mới</h3>
|
| 586 |
+
<p class="update-date">15/08/2026</p>
|
| 587 |
+
<ul>
|
| 588 |
+
<li>Trang chủ Crunchyroll-style với hero 3D + poster grid + phim nổi bật.</li>
|
| 589 |
+
<li>Nền poster-grid stage 1 áp dụng toàn bộ trang (style.css).</li>
|
| 590 |
+
<li>Đổi tên nút "Xem feature" thành "Xem phim nổi bật".</li>
|
| 591 |
+
</ul>
|
| 592 |
+
</div>
|
| 593 |
+
<hr style="border:none;border-top:1px solid var(--cp-border);margin:16px 0;">
|
| 594 |
+
<div class="update-item">
|
| 595 |
+
<h3 style="margin:0 0 4px;">v1.x — Nền tảng</h3>
|
| 596 |
+
<p class="update-date">Trước 15/08/2026</p>
|
| 597 |
+
<ul>
|
| 598 |
+
<li>Đăng ký / đăng nhập qua worker auth (sginup-loginsystem).</li>
|
| 599 |
+
<li>Sưu tầm anime, manga, manhwa, manhua, light novel & gallery (SFW + NSFW).</li>
|
| 600 |
+
</ul>
|
| 601 |
+
</div>
|
| 602 |
+
</div>
|
| 603 |
+
</div>
|
| 604 |
+
</div>
|
| 605 |
+
</div>
|
| 606 |
+
</div>
|
| 607 |
+
</main>
|
| 608 |
+
|
| 609 |
+
<div class="confirm-overlay" id="confirmOverlay">
|
| 610 |
+
<div class="confirm-box">
|
| 611 |
+
<div class="confirm-icon">⚠</div>
|
| 612 |
+
<div class="confirm-title">Xóa người dùng?</div>
|
| 613 |
+
<div class="confirm-text">Bạn có chắc muốn xóa tài khoản <b id="confirmTarget"></b>? Hành động này không thể hoàn tác.</div>
|
| 614 |
+
<div class="confirm-actions">
|
| 615 |
+
<button class="confirm-btn confirm-cancel" id="confirmCancel">Hủy</button>
|
| 616 |
+
<button class="confirm-btn confirm-ok" id="confirmOk">Xóa</button>
|
| 617 |
+
</div>
|
| 618 |
+
</div>
|
| 619 |
+
</div>
|
| 620 |
+
|
| 621 |
+
<!-- Popup xác nhận / hủy đơn -->
|
| 622 |
+
<div class="confirm-overlay" id="orderModal">
|
| 623 |
+
<div class="confirm-box">
|
| 624 |
+
<div class="confirm-icon">🛒</div>
|
| 625 |
+
<div class="confirm-title" id="orderModalTitle">Xác nhận đơn</div>
|
| 626 |
+
<div class="confirm-text" id="orderModalText"></div>
|
| 627 |
+
<textarea id="orderModalReason" class="cp-reason" placeholder="Nhập lí do hủy (bắt buộc)..." rows="3" style="display:none;"></textarea>
|
| 628 |
+
<div class="cp-reason-err" id="orderModalReasonErr">⚠️ Vui lòng nhập lí do hủy đơn</div>
|
| 629 |
+
<div class="confirm-actions">
|
| 630 |
+
<button class="confirm-btn confirm-cancel" id="orderModalCancel">Đóng</button>
|
| 631 |
+
<button class="confirm-btn confirm-ok" id="orderModalOk">Xác nhận</button>
|
| 632 |
+
</div>
|
| 633 |
+
</div>
|
| 634 |
+
</div>
|
| 635 |
+
<script src="signup-loginsystem.js?v=9"></script>
|
| 636 |
+
<script>
|
| 637 |
+
var whoEl = document.getElementById('who');
|
| 638 |
+
var roleEl = document.getElementById('userRole');
|
| 639 |
+
var avatarEl = document.getElementById('userAvatar');
|
| 640 |
+
var panel = document.getElementById('panel');
|
| 641 |
+
var denied = document.getElementById('accessDenied');
|
| 642 |
+
|
| 643 |
+
var confirmOverlay = document.getElementById('confirmOverlay');
|
| 644 |
+
var confirmCancel = document.getElementById('confirmCancel');
|
| 645 |
+
var confirmOk = document.getElementById('confirmOk');
|
| 646 |
+
|
| 647 |
+
var PANELS = ['overview', 'users', 'content', 'products', 'orders', 'customers', 'reports', 'updates'];
|
| 648 |
+
var currentCat = 'anime';
|
| 649 |
+
|
| 650 |
+
var IMG = "https://phim.nguonc.com/public/images/";
|
| 651 |
+
var PRODUCTS = [];
|
| 652 |
+
var SHOP_API = localStorage.getItem("anivietdb-shop-api") || "https://shop-system.npgia-himlam.workers.dev/";
|
| 653 |
+
var shopCustomers = [];
|
| 654 |
+
var shopOrders = [];
|
| 655 |
+
var cpStats = null;
|
| 656 |
+
|
| 657 |
+
function api(url, init) {
|
| 658 |
+
var base = SHOP_API.replace(/\/$/, "");
|
| 659 |
+
var token = AuthSystem.getToken();
|
| 660 |
+
init = init || {};
|
| 661 |
+
init.headers = init.headers || {};
|
| 662 |
+
if (token) init.headers["Authorization"] = "Bearer " + token;
|
| 663 |
+
return fetch(base + url, init).then(function (r) {
|
| 664 |
+
return r.json().then(function (data) {
|
| 665 |
+
if (!r.ok) { var e = new Error(data.error || ("HTTP " + r.status)); e.status = r.status; e.data = data; throw e; }
|
| 666 |
+
return data;
|
| 667 |
+
});
|
| 668 |
+
});
|
| 669 |
+
}
|
| 670 |
+
|
| 671 |
+
function fmt(n) { return n.toLocaleString("vi-VN") + "đ"; }
|
| 672 |
+
function $(s) { return document.querySelector(s); }
|
| 673 |
+
function $$(s) { return document.querySelectorAll(s); }
|
| 674 |
+
|
| 675 |
+
function closeConfirm() {
|
| 676 |
+
confirmOverlay.classList.remove('active');
|
| 677 |
+
confirmOk.onclick = null;
|
| 678 |
+
}
|
| 679 |
+
confirmCancel.addEventListener('click', closeConfirm);
|
| 680 |
+
confirmOverlay.addEventListener('click', function (e) { if (e.target === confirmOverlay) closeConfirm(); });
|
| 681 |
+
document.addEventListener('keydown', function (e) { if (e.key === 'Escape') closeConfirm(); });
|
| 682 |
+
|
| 683 |
+
function switchPanel(name) {
|
| 684 |
+
$$(".cp-nav-item[data-panel]").forEach(function (a) { a.classList.toggle('active', a.dataset.panel === name); });
|
| 685 |
+
PANELS.forEach(function (n) {
|
| 686 |
+
var el = $('#panel-' + n);
|
| 687 |
+
if (el) el.style.display = n === name ? 'block' : 'none';
|
| 688 |
+
});
|
| 689 |
+
var t = document.querySelector('.cp-topbar-title');
|
| 690 |
+
if (t) {
|
| 691 |
+
var map = { overview: 'Thống kê', users: 'Người dùng', content: 'Nội dung', products: 'Sản phẩm', orders: 'Đơn hàng', customers: 'Khách hàng', reports: 'Báo cáo', updates: 'Cập nhật' };
|
| 692 |
+
t.childNodes[0].textContent = map[name] || 'Thống kê';
|
| 693 |
+
}
|
| 694 |
+
if (name === 'overview') renderOverview();
|
| 695 |
+
if (name === 'products') loadShopProducts();
|
| 696 |
+
if (name === 'orders') loadShopOrders();
|
| 697 |
+
if (name === 'customers') loadShopCustomers();
|
| 698 |
+
if (name === 'reports') renderReports();
|
| 699 |
+
if (name === 'updates') renderUpdates();
|
| 700 |
+
}
|
| 701 |
+
|
| 702 |
+
function fmtTime(iso) {
|
| 703 |
+
if (!iso) return '—';
|
| 704 |
+
try {
|
| 705 |
+
var d = new Date(iso);
|
| 706 |
+
return d.toLocaleString('vi-VN', { hour12: false });
|
| 707 |
+
} catch (e) { return iso; }
|
| 708 |
+
}
|
| 709 |
+
|
| 710 |
+
function esc(s) {
|
| 711 |
+
return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) {
|
| 712 |
+
return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c];
|
| 713 |
+
});
|
| 714 |
+
}
|
| 715 |
+
function renderUsers(users) {
|
| 716 |
+
var body = document.getElementById('usersBody');
|
| 717 |
+
body.innerHTML = users.map(function (u) {
|
| 718 |
+
var isSelf = u.id === (AuthSystem.getUser() || {}).id;
|
| 719 |
+
var badge = u.role === 'admin' ? '<span class="badge badge-admin">ADMIN</span>' : '<span class="badge badge-user">USER</span>';
|
| 720 |
+
var del = u.role === 'admin'
|
| 721 |
+
? '<button class="btn-del" disabled title="Không thể xóa Admin">—</button>'
|
| 722 |
+
: '<button class="btn-del" data-id="' + u.id + '">Xóa</button>';
|
| 723 |
+
return '<tr>' +
|
| 724 |
+
'<td>' + u.id + '</td>' +
|
| 725 |
+
'<td>' + esc(u.username) + (isSelf ? ' <span style="color:#7ee787;font-size:11px">(bạn)</span>' : '') + '</td>' +
|
| 726 |
+
'<td>' + esc(u.email) + '</td>' +
|
| 727 |
+
'<td>' + badge + '</td>' +
|
| 728 |
+
'<td>' + (u.login_count || 0) + '</td>' +
|
| 729 |
+
'<td>' + fmtTime(u.last_login_at) + '</td>' +
|
| 730 |
+
'<td>' + fmtTime(u.created_at) + '</td>' +
|
| 731 |
+
'<td>' + del + '</td>' +
|
| 732 |
+
'</tr>';
|
| 733 |
+
}).join('');
|
| 734 |
+
body.querySelectorAll('.btn-del[data-id]').forEach(function (btn) {
|
| 735 |
+
btn.addEventListener('click', function () {
|
| 736 |
+
var id = btn.getAttribute('data-id');
|
| 737 |
+
var row = btn.closest('tr');
|
| 738 |
+
var name = row ? row.cells[1].textContent.trim() : '';
|
| 739 |
+
confirmDelete(id, name);
|
| 740 |
+
});
|
| 741 |
+
});
|
| 742 |
+
}
|
| 743 |
+
|
| 744 |
+
function confirmDelete(id, name) {
|
| 745 |
+
var u = AuthSystem.getUser();
|
| 746 |
+
if (u && String(u.id) === String(id)) { return; }
|
| 747 |
+
document.getElementById('confirmTarget').textContent = name || 'ID ' + id;
|
| 748 |
+
confirmOverlay.classList.add('active');
|
| 749 |
+
confirmOk.onclick = function () {
|
| 750 |
+
confirmOverlay.classList.remove('active');
|
| 751 |
+
deleteUser(id);
|
| 752 |
+
};
|
| 753 |
+
}
|
| 754 |
+
|
| 755 |
+
function deleteUser(id) {
|
| 756 |
+
AuthSystem.adminDeleteUser(id).then(function () {
|
| 757 |
+
loadUsers();
|
| 758 |
+
}).catch(function (e) {
|
| 759 |
+
if (!handleAuthFail(e)) document.getElementById('usersMsg').textContent = 'Lỗi: ' + e.message;
|
| 760 |
+
});
|
| 761 |
+
}
|
| 762 |
+
|
| 763 |
+
function handleAuthFail(e) {
|
| 764 |
+
console.log('[control-panel] FAIL status=', e && e.status, 'msg=', e && e.message, 'data=', e && e.data, 'base=', AuthSystem.baseUrl);
|
| 765 |
+
if (e && e.status === 401) {
|
| 766 |
+
AuthSystem.clearSession();
|
| 767 |
+
return false;
|
| 768 |
+
}
|
| 769 |
+
return false;
|
| 770 |
+
}
|
| 771 |
+
|
| 772 |
+
function loadUsers() {
|
| 773 |
+
var msg = document.getElementById('usersMsg');
|
| 774 |
+
msg.textContent = 'Đang tải...';
|
| 775 |
+
AuthSystem.adminUsers().then(function (data) {
|
| 776 |
+
msg.textContent = 'Tổng cộng ' + data.users.length + ' tài khoản.';
|
| 777 |
+
renderUsers(data.users);
|
| 778 |
+
}).catch(function (e) {
|
| 779 |
+
msg.textContent = 'Lỗi tải danh sách: ' + e.message +
|
| 780 |
+
' (base=' + AuthSystem.baseUrl + ', token=' + (AuthSystem.getToken() ? 'có' : 'KHÔNG') + ')';
|
| 781 |
+
handleAuthFail(e);
|
| 782 |
+
});
|
| 783 |
+
}
|
| 784 |
+
|
| 785 |
+
function loadContent() {
|
| 786 |
+
var CATS = [
|
| 787 |
+
{ key: 'anime', label: 'Anime' },
|
| 788 |
+
{ key: 'manga', label: 'Manga' },
|
| 789 |
+
{ key: 'manhwa', label: 'Manhwa' },
|
| 790 |
+
{ key: 'manhua', label: 'Manhua' },
|
| 791 |
+
{ key: 'light-novel', label: 'Light Novel' },
|
| 792 |
+
{ key: 'gallery', label: 'Galleries' }
|
| 793 |
+
];
|
| 794 |
+
var byCat = {};
|
| 795 |
+
CATS.forEach(function (c) { byCat[c.key] = []; });
|
| 796 |
+
var animeErr = null;
|
| 797 |
+
var mangaErr = null;
|
| 798 |
+
var metaErr = null;
|
| 799 |
+
var metaById = {};
|
| 800 |
+
|
| 801 |
+
function push(cat, name, nsfw, status, slug, animePages, id) {
|
| 802 |
+
byCat[cat].push({
|
| 803 |
+
id: id || '',
|
| 804 |
+
name: name || '(không tên)',
|
| 805 |
+
nsfw: !!nsfw,
|
| 806 |
+
status: status || '',
|
| 807 |
+
slug: slug || '',
|
| 808 |
+
animePages: animePages || []
|
| 809 |
+
});
|
| 810 |
+
}
|
| 811 |
+
|
| 812 |
+
var p1 = fetch('data/manga_map.json').then(function (r) {
|
| 813 |
+
if (!r.ok) throw new Error('HTTP ' + r.status);
|
| 814 |
+
return r.json();
|
| 815 |
+
})
|
| 816 |
+
.then(function (arr) {
|
| 817 |
+
(Array.isArray(arr) ? arr : []).forEach(function (a) {
|
| 818 |
+
push('anime', a.titleVN || a.titleEN || a.slug, a.nsfw, a.status, a.slug, a.animePages);
|
| 819 |
+
});
|
| 820 |
+
})
|
| 821 |
+
.catch(function (e) { animeErr = e; });
|
| 822 |
+
var p2 = fetch('data/manga-sources.json').then(function (r) {
|
| 823 |
+
if (!r.ok) throw new Error('HTTP ' + r.status);
|
| 824 |
+
return r.json();
|
| 825 |
+
})
|
| 826 |
+
.then(function (arr) {
|
| 827 |
+
(Array.isArray(arr) ? arr : []).forEach(function (m) {
|
| 828 |
+
var cat = m.type === 'gallery' ? 'gallery' : 'manga';
|
| 829 |
+
push(cat, m.titleVN || m.titleEN || m.slug, m.nsfw, m.status, m.slug, [], m.id);
|
| 830 |
+
});
|
| 831 |
+
})
|
| 832 |
+
.catch(function (e) { mangaErr = e; });
|
| 833 |
+
var p3 = fetch('data/manga-metadata.json').then(function (r) {
|
| 834 |
+
if (!r.ok) throw new Error('HTTP ' + r.status);
|
| 835 |
+
return r.json();
|
| 836 |
+
})
|
| 837 |
+
.then(function (arr) {
|
| 838 |
+
(Array.isArray(arr) ? arr : []).forEach(function (e) {
|
| 839 |
+
if (e && e.metadata && e.metadata.status) metaById[e.id] = e.metadata.status;
|
| 840 |
+
});
|
| 841 |
+
})
|
| 842 |
+
.catch(function (e) { metaErr = e; });
|
| 843 |
+
|
| 844 |
+
Promise.all([p1, p2, p3]).then(function () {
|
| 845 |
+
Object.keys(byCat).forEach(function (cat) {
|
| 846 |
+
byCat[cat].forEach(function (it) {
|
| 847 |
+
if (!it.status && metaById[it.id]) it.status = metaById[it.id];
|
| 848 |
+
});
|
| 849 |
+
});
|
| 850 |
+
var msg = document.getElementById('contentMsg');
|
| 851 |
+
if (animeErr || mangaErr || metaErr) {
|
| 852 |
+
msg.textContent = 'Lỗi tải nội dung: ' +
|
| 853 |
+
(animeErr ? ('manga_map.json → ' + animeErr.message) : '') +
|
| 854 |
+
(animeErr && (mangaErr || metaErr) ? ' | ' : '') +
|
| 855 |
+
(mangaErr ? ('manga-sources.json → ' + mangaErr.message) : '') +
|
| 856 |
+
(mangaErr && metaErr ? ' | ' : '') +
|
| 857 |
+
(metaErr ? ('manga-metadata.json → ' + metaErr.message) : '') +
|
| 858 |
+
'. Nếu mở file trực tiếp (file://), hãy dùng Live Server.';
|
| 859 |
+
msg.style.color = '#f85149';
|
| 860 |
+
} else {
|
| 861 |
+
msg.textContent = '';
|
| 862 |
+
msg.style.color = '';
|
| 863 |
+
}
|
| 864 |
+
|
| 865 |
+
var statsEl = document.getElementById('contentStats');
|
| 866 |
+
if (statsEl) {
|
| 867 |
+
statsEl.innerHTML = CATS.map(function (c) {
|
| 868 |
+
return '<div class="stat-card"><span class="stat-label"><span class="dot" style="background:#a3419f;"></span> ' + c.label + '</span><span class="stat-value">' + byCat[c.key].length + '</span></div>';
|
| 869 |
+
}).join('');
|
| 870 |
+
}
|
| 871 |
+
|
| 872 |
+
var tabsWrap = document.getElementById('catTabs');
|
| 873 |
+
tabsWrap.innerHTML = CATS.map(function (c, i) {
|
| 874 |
+
return '<button class="cat-tab' + (i === 0 ? ' active' : '') + '" data-cat="' + c.key + '">' + c.label + '</button>';
|
| 875 |
+
}).join('');
|
| 876 |
+
|
| 877 |
+
var body = document.getElementById('contentBody');
|
| 878 |
+
|
| 879 |
+
function itemLink(cat, item) {
|
| 880 |
+
if (cat === 'anime') {
|
| 881 |
+
var p = item.animePages && item.animePages[0];
|
| 882 |
+
if (p) return (p.indexOf('nsfw/') === 0 ? '' : 'sfw/') + p;
|
| 883 |
+
return (item.nsfw ? 'nsfw/' : 'sfw/') + 'anime/' + item.slug + '/';
|
| 884 |
+
}
|
| 885 |
+
if (cat === 'gallery') return 'sfw/galleries/' + item.slug + '/index.html';
|
| 886 |
+
return 'sfw/manga/' + item.slug + '/index.html';
|
| 887 |
+
}
|
| 888 |
+
|
| 889 |
+
function renderTable() {
|
| 890 |
+
var STATUS_VN = { ongoing: 'Đang ra', completed: 'Hoàn thành', hiatus: 'Tạm ngưng', cancelled: 'Bị hủy' };
|
| 891 |
+
var items = byCat[currentCat];
|
| 892 |
+
body.innerHTML = items.length ? items.map(function (it, i) {
|
| 893 |
+
var tag = it.nsfw
|
| 894 |
+
? '<span class="nsfw-tag">NSFW</span>'
|
| 895 |
+
: '<span class="sfw-tag">SFW</span>';
|
| 896 |
+
var status = it.status ? (STATUS_VN[it.status] || it.status) : '—';
|
| 897 |
+
return '<tr>' +
|
| 898 |
+
'<td>' + (i + 1) + '</td>' +
|
| 899 |
+
'<td>' + esc(it.name) + '</td>' +
|
| 900 |
+
'<td>' + tag + '</td>' +
|
| 901 |
+
'<td>' + esc(status) + '</td>' +
|
| 902 |
+
'<td style="width:70px;"><a class="btn-go" href="' + esc(itemLink(currentCat, it)) + '" target="_blank">Xem</a></td>' +
|
| 903 |
+
'</tr>';
|
| 904 |
+
}).join('') : '<tr><td colspan="5" class="msg">Chưa có dữ liệu</td></tr>';
|
| 905 |
+
}
|
| 906 |
+
|
| 907 |
+
tabsWrap.addEventListener('click', function (e) {
|
| 908 |
+
var btn = e.target.closest('.cat-tab');
|
| 909 |
+
if (!btn) return;
|
| 910 |
+
tabsWrap.querySelectorAll('.cat-tab').forEach(function (t) { t.classList.remove('active'); });
|
| 911 |
+
btn.classList.add('active');
|
| 912 |
+
currentCat = btn.getAttribute('data-cat');
|
| 913 |
+
renderTable();
|
| 914 |
+
});
|
| 915 |
+
|
| 916 |
+
renderTable();
|
| 917 |
+
});
|
| 918 |
+
}
|
| 919 |
+
// ---------- Shop: Sản phẩm ----------
|
| 920 |
+
var cpCurrentCat = 'all';
|
| 921 |
+
var CATS = [];
|
| 922 |
+
|
| 923 |
+
function cpCardHTML(p) {
|
| 924 |
+
var stockCls = p.stock <= 3 ? 'low' : 'ok';
|
| 925 |
+
var stockTxt = p.stock <= 3 ? 'Sắp hết (' + p.stock + ')' : 'Còn ' + p.stock;
|
| 926 |
+
var tag = '<span class="product-tag">' + p.cat + '</span>';
|
| 927 |
+
var sale = p.old ? ' <span class="price-old">' + fmt(p.old) + '</span>' : '';
|
| 928 |
+
return '<div class="product-card" data-id="' + p.id + '">' +
|
| 929 |
+
'<div class="product-thumb">' + tag +
|
| 930 |
+
'<img src="' + p.img + '" alt="' + p.name + '" loading="lazy">' +
|
| 931 |
+
'</div>' +
|
| 932 |
+
'<div class="product-body">' +
|
| 933 |
+
'<div class="product-anime">' + p.anime + '</div>' +
|
| 934 |
+
'<div class="product-name">' + p.name + '</div>' +
|
| 935 |
+
'<div class="product-price"><span class="price-now">' + fmt(p.price) + '</span>' + sale + '</div>' +
|
| 936 |
+
'<div class="product-stock ' + stockCls + '" id="cpStock-' + p.id + '">' + stockTxt + '</div>' +
|
| 937 |
+
'<div class="product-stock" style="display:flex;gap:8px;align-items:center;margin-top:2px;">' +
|
| 938 |
+
'<button class="cp-btn" data-stock="' + p.id + '" data-delta="-1" style="padding:3px 10px;font-size:12px;">-1</button>' +
|
| 939 |
+
'<button class="cp-btn" data-stock="' + p.id + '" data-delta="1" style="padding:3px 10px;font-size:12px;">+1</button>' +
|
| 940 |
+
'<button class="cp-btn" data-stock="' + p.id + '" data-delta="5" style="padding:3px 10px;font-size:12px;">+5</button>' +
|
| 941 |
+
'</div>' +
|
| 942 |
+
'</div></div>';
|
| 943 |
+
}
|
| 944 |
+
|
| 945 |
+
function renderCpProducts(filterCat, query) {
|
| 946 |
+
filterCat = filterCat || 'all';
|
| 947 |
+
query = (query || '').toLowerCase();
|
| 948 |
+
var list = PRODUCTS.filter(function (p) {
|
| 949 |
+
return (filterCat === 'all' || p.cat === filterCat) &&
|
| 950 |
+
(!query || p.name.toLowerCase().indexOf(query) !== -1 || p.anime.toLowerCase().indexOf(query) !== -1);
|
| 951 |
+
});
|
| 952 |
+
$('#cpProductGrid').innerHTML = list.map(cpCardHTML).join('');
|
| 953 |
+
$('#cpEmptyProducts').style.display = list.length ? 'none' : 'block';
|
| 954 |
+
}
|
| 955 |
+
|
| 956 |
+
function loadShopProducts() {
|
| 957 |
+
var info = $('#cpProductsInfo');
|
| 958 |
+
if (info) info.textContent = 'Đang tải...';
|
| 959 |
+
api('/api/shop/products').then(function (d) {
|
| 960 |
+
PRODUCTS = Array.isArray(d.products) ? d.products : [];
|
| 961 |
+
var seen = [];
|
| 962 |
+
PRODUCTS.forEach(function (p) { if (seen.indexOf(p.cat) === -1) seen.push(p.cat); });
|
| 963 |
+
CATS = seen;
|
| 964 |
+
if (info) info.textContent = PRODUCTS.length + ' sản phẩm';
|
| 965 |
+
var wrap = $('#cpCatChips');
|
| 966 |
+
wrap.innerHTML = '<button class="chip active" data-cat="all">Tất cả <span style="opacity:.7">(' + PRODUCTS.length + ')</span></button>';
|
| 967 |
+
CATS.forEach(function (c) {
|
| 968 |
+
var n = PRODUCTS.filter(function (p) { return p.cat === c; }).length;
|
| 969 |
+
var b = document.createElement('button');
|
| 970 |
+
b.className = 'chip';
|
| 971 |
+
b.dataset.cat = c;
|
| 972 |
+
b.innerHTML = c + " <span style='opacity:.7'>(" + n + ")</span>";
|
| 973 |
+
wrap.appendChild(b);
|
| 974 |
+
});
|
| 975 |
+
renderCpProducts('all', '');
|
| 976 |
+
}).catch(function (e) {
|
| 977 |
+
$('#cpEmptyProducts').style.display = 'block';
|
| 978 |
+
$('#cpEmptyProducts').innerHTML = '<div class="big">😅</div>Lỗi tải sản phẩm: ' + esc(e.message);
|
| 979 |
+
});
|
| 980 |
+
}
|
| 981 |
+
|
| 982 |
+
function setStock(id, delta) {
|
| 983 |
+
var btn = $('#cpProductGrid').querySelector('[data-stock="' + id + '"][data-delta="' + delta + '"]');
|
| 984 |
+
if (btn) { btn.disabled = true; }
|
| 985 |
+
api('/api/shop/products/' + id, {
|
| 986 |
+
method: 'PATCH',
|
| 987 |
+
headers: { 'Content-Type': 'application/json' },
|
| 988 |
+
body: JSON.stringify({ delta: delta })
|
| 989 |
+
}).then(function (d) {
|
| 990 |
+
var p = PRODUCTS.find(function (x) { return x.id === d.product.id; });
|
| 991 |
+
if (p) { p.stock = d.product.stock; }
|
| 992 |
+
var el = $('#cpStock-' + id);
|
| 993 |
+
if (el) {
|
| 994 |
+
var cls = d.product.stock <= 3 ? 'low' : 'ok';
|
| 995 |
+
var txt = d.product.stock <= 3 ? 'Sắp hết (' + d.product.stock + ')' : 'Còn ' + d.product.stock;
|
| 996 |
+
el.className = 'product-stock ' + cls;
|
| 997 |
+
el.textContent = txt;
|
| 998 |
+
}
|
| 999 |
+
}).catch(function (e) {
|
| 1000 |
+
alert('Lỗi cập nhật stock: ' + e.message);
|
| 1001 |
+
}).finally(function () {
|
| 1002 |
+
if (btn) { btn.disabled = false; }
|
| 1003 |
+
});
|
| 1004 |
+
}
|
| 1005 |
+
|
| 1006 |
+
$('#cpProductGrid').addEventListener('click', function (e) {
|
| 1007 |
+
var b = e.target.closest('[data-stock]');
|
| 1008 |
+
if (!b) return;
|
| 1009 |
+
setStock(Number(b.dataset.stock), Number(b.dataset.delta));
|
| 1010 |
+
});
|
| 1011 |
+
|
| 1012 |
+
// ---------- Shop: Khách hàng ----------
|
| 1013 |
+
function renderCustomers() {
|
| 1014 |
+
var bought = shopCustomers.length ? shopCustomers.filter(function (c) { return (c.orders_count || 0) > 0; }).length : 0;
|
| 1015 |
+
var totalSpent = shopCustomers.reduce(function (s, c) { return s + (c.total_spent || 0); }, 0);
|
| 1016 |
+
$('#customerStats').innerHTML =
|
| 1017 |
+
'<div class="stat-card"><span class="stat-label"><span class="dot" style="background:#2abdbb;"></span> Tổng khách hàng (user)</span><span class="stat-value">' + (shopCustomers.length || 0) + '</span></div>' +
|
| 1018 |
+
'<div class="stat-card"><span class="stat-label"><span class="dot" style="background:#ff5e00;"></span> Đã mua hàng</span><span class="stat-value">' + bought + '</span></div>' +
|
| 1019 |
+
'<div class="stat-card"><span class="stat-label"><span class="dot" style="background:#a3419f;"></span> Tổng chi tiêu</span><span class="stat-value">' + fmt(totalSpent) + '</span></div>';
|
| 1020 |
+
|
| 1021 |
+
if (!shopCustomers.length) {
|
| 1022 |
+
$('#customersBody').innerHTML = '<tr><td colspan="7" style="text-align:center;color:var(--cp-muted);padding:24px;">Chưa có dữ liệu khách hàng từ máy chủ shop-system. Bấm nút đồng bộ phía dưới.</td></tr>';
|
| 1023 |
+
return;
|
| 1024 |
+
}
|
| 1025 |
+
$('#customersBody').innerHTML = shopCustomers.map(function (c, i) {
|
| 1026 |
+
var name = esc(c.name || 'Khách');
|
| 1027 |
+
var badge = c.role === 'admin' ? '<span class="badge badge-admin">ADMIN</span>' : '<span class="badge badge-user">USER</span>';
|
| 1028 |
+
var spent = (c.total_spent || 0) > 0 ? fmt(c.total_spent) : '<span style="color:var(--cp-muted);">N/A</span>';
|
| 1029 |
+
var items = c.items_count || 0;
|
| 1030 |
+
return '<tr>' +
|
| 1031 |
+
'<td>' + c.id + '</td>' +
|
| 1032 |
+
'<td>' + name + badge + (c.email ? '<div style="font-size:11px;color:var(--cp-muted);">' + esc(c.email) + '</div>' : '') + '</td>' +
|
| 1033 |
+
'<td>' + (c.orders_count || 0) + '</td>' +
|
| 1034 |
+
'<td>' + items + '</td>' +
|
| 1035 |
+
'<td>' + spent + '</td>' +
|
| 1036 |
+
'<td>⭐ ' + Number(c.points || 0).toLocaleString('vi-VN') + '</td>' +
|
| 1037 |
+
'<td>' + fmtTime(c.created_at) + '</td>' +
|
| 1038 |
+
'</tr>';
|
| 1039 |
+
}).join('');
|
| 1040 |
+
}
|
| 1041 |
+
|
| 1042 |
+
function loadShopCustomers() {
|
| 1043 |
+
if (!AuthSystem.getToken()) return;
|
| 1044 |
+
api('/api/shop/customers').then(function (d) {
|
| 1045 |
+
shopCustomers = Array.isArray(d.customers) ? d.customers : [];
|
| 1046 |
+
renderCustomers();
|
| 1047 |
+
}).catch(function () { renderCustomers(); });
|
| 1048 |
+
}
|
| 1049 |
+
|
| 1050 |
+
$('#refreshCustomers').addEventListener('click', function () {
|
| 1051 |
+
var btn = this;
|
| 1052 |
+
btn.innerHTML = '⌛ Đang đồng bộ…';
|
| 1053 |
+
loadShopCustomers();
|
| 1054 |
+
setTimeout(function () { btn.innerHTML = '🔄 Làm mới'; }, 1200);
|
| 1055 |
+
});
|
| 1056 |
+
|
| 1057 |
+
// ---------- Shop: Đơn hàng ----------
|
| 1058 |
+
function renderShopOrders() {
|
| 1059 |
+
$('#ordersInfo').textContent = shopOrders.length ? 'Tổng cộng ' + shopOrders.length + ' đơn hàng.' : 'Chưa có đơn hàng nào.';
|
| 1060 |
+
if (!shopOrders.length) {
|
| 1061 |
+
$('#ordersBody').innerHTML = '<tr><td colspan="8" style="text-align:center;color:var(--cp-muted);padding:24px;">Chưa có đơn hàng.</td></tr>';
|
| 1062 |
+
return;
|
| 1063 |
+
}
|
| 1064 |
+
$('#ordersBody').innerHTML = shopOrders.map(function (o) {
|
| 1065 |
+
var items = (o.items || []).map(function (it) { return it.name + ' x' + it.q; }).join(', ');
|
| 1066 |
+
var action = '';
|
| 1067 |
+
if (o.status === 'Chờ xác nhận') {
|
| 1068 |
+
action = '<div style="display:flex;gap:6px;flex-wrap:wrap;">' +
|
| 1069 |
+
'<button class="cp-btn cp-btn-sm" data-ship="' + o.id + '" title="Xác nhận và chuyển sang giao hàng">🚚 Xác nhận</button>' +
|
| 1070 |
+
'<button class="cp-btn cp-btn-sm cp-btn-danger" data-cancel="' + o.id + '" title="Hủy đơn (kèm lí do)">❌ Hủy</button>' +
|
| 1071 |
+
'</div>';
|
| 1072 |
+
} else if (o.status === 'Chờ thanh toán') {
|
| 1073 |
+
action = '<button class="cp-btn cp-btn-sm cp-btn-danger" data-cancel="' + o.id + '" title="Hủy đơn (kèm lí do)">❌ Hủy</button>';
|
| 1074 |
+
} else if (o.status === 'Đang giao hàng') {
|
| 1075 |
+
action = '<div style="display:flex;gap:6px;flex-wrap:wrap;">' +
|
| 1076 |
+
'<span style="font-size:11px;color:var(--cp-muted);align-self:center;">🚚 Đang giao…</span>' +
|
| 1077 |
+
'<button class="cp-btn cp-btn-sm cp-btn-danger" data-cancel="' + o.id + '" title="Hủy đơn (kèm lí do)">❌ Hủy</button>' +
|
| 1078 |
+
'</div>';
|
| 1079 |
+
} else if (o.status === 'Đã giao') {
|
| 1080 |
+
action = '<span style="font-size:11px;color:#2abdbb;">✅ Đã giao</span>';
|
| 1081 |
+
} else if (o.status === 'Đã hủy') {
|
| 1082 |
+
action = '<span style="font-size:11px;color:#ff8a8a;">❌ Đã hủy</span>';
|
| 1083 |
+
}
|
| 1084 |
+
var reasonRow = (o.status === 'Đã hủy' && o.cancel_reason)
|
| 1085 |
+
? '<div style="font-size:11px;color:#ff8a8a;margin-top:3px;">Lí do: ' + esc(o.cancel_reason) + '</div>'
|
| 1086 |
+
: '';
|
| 1087 |
+
return '<tr>' +
|
| 1088 |
+
'<td>' + o.code + '</td>' +
|
| 1089 |
+
'<td>' + esc(o.customer_name) + (o.customer_email ? '<div style="font-size:11px;color:var(--cp-muted);">' + esc(o.customer_email) + '</div>' : '') + '</td>' +
|
| 1090 |
+
'<td>' + esc(items) + '</td>' +
|
| 1091 |
+
'<td>' + o.item_count + '</td>' +
|
| 1092 |
+
'<td>' + fmt(o.total) + '</td>' +
|
| 1093 |
+
'<td>' + esc(o.status) + reasonRow + '</td>' +
|
| 1094 |
+
'<td>' + fmtTime(o.created_at) + '</td>' +
|
| 1095 |
+
'<td>' + action + '</td>' +
|
| 1096 |
+
'</tr>';
|
| 1097 |
+
}).join('');
|
| 1098 |
+
}
|
| 1099 |
+
|
| 1100 |
+
function loadShopOrders() {
|
| 1101 |
+
if (!AuthSystem.getToken()) return;
|
| 1102 |
+
$('#ordersMsg').textContent = 'Đang tải...';
|
| 1103 |
+
api('/api/shop/orders/all').then(function (d) {
|
| 1104 |
+
shopOrders = Array.isArray(d.orders) ? d.orders : [];
|
| 1105 |
+
renderShopOrders();
|
| 1106 |
+
$('#ordersMsg').textContent = '';
|
| 1107 |
+
}).catch(function (e) {
|
| 1108 |
+
$('#ordersMsg').textContent = 'Lỗi tải đơn hàng: ' + e.message;
|
| 1109 |
+
});
|
| 1110 |
+
}
|
| 1111 |
+
|
| 1112 |
+
$('#refreshOrders').addEventListener('click', loadShopOrders);
|
| 1113 |
+
$('#refreshProducts').addEventListener('click', loadShopProducts);
|
| 1114 |
+
$('#refreshOverview').addEventListener('click', renderOverview);
|
| 1115 |
+
$('#refreshReports').addEventListener('click', function () {
|
| 1116 |
+
api('/api/shop/stats').then(function (d) {
|
| 1117 |
+
cpStats = d.stats;
|
| 1118 |
+
renderReports();
|
| 1119 |
+
}).catch(function (e) {
|
| 1120 |
+
$('#reportStats').innerHTML = '<div class="msg err">Lỗi tải lại: ' + esc(e.message) + '</div>';
|
| 1121 |
+
});
|
| 1122 |
+
});
|
| 1123 |
+
|
| 1124 |
+
// ---------- Popup xác nhận / hủy đơn ----------
|
| 1125 |
+
function openOrderModal(type, id) {
|
| 1126 |
+
var order = shopOrders.filter(function (o) { return o.id === id; })[0];
|
| 1127 |
+
var label = order ? order.code : ('#' + id);
|
| 1128 |
+
var modal = document.getElementById('orderModal');
|
| 1129 |
+
document.getElementById('orderModalTitle').textContent =
|
| 1130 |
+
type === 'ship' ? 'Xác nhận đơn ' + label : 'Hủy đơn ' + label;
|
| 1131 |
+
document.getElementById('orderModalText').textContent =
|
| 1132 |
+
type === 'ship'
|
| 1133 |
+
? 'Xác nhận đơn ' + label + ' đã thanh toán và chuyển sang giao hàng?'
|
| 1134 |
+
: 'Đơn ' + label + ' sẽ bị hủy và trả lại số lượng sản phẩm vào kho. Nhập lí do hủy:';
|
| 1135 |
+
document.getElementById('orderModalOk').innerHTML =
|
| 1136 |
+
type === 'ship' ? '🚚 Xác nhận' : '❌ Xác nhận hủy';
|
| 1137 |
+
document.getElementById('orderModalOk').className =
|
| 1138 |
+
'confirm-btn confirm-ok' + (type === 'ship' ? ' ok-ship' : '');
|
| 1139 |
+
document.getElementById('orderModalReason').style.display =
|
| 1140 |
+
type === 'ship' ? 'none' : 'block';
|
| 1141 |
+
document.getElementById('orderModalReason').value = '';
|
| 1142 |
+
document.getElementById('orderModalReasonErr').classList.remove('show');
|
| 1143 |
+
modal.classList.add('active');
|
| 1144 |
+
modal.dataset.type = type;
|
| 1145 |
+
modal.dataset.id = id;
|
| 1146 |
+
setTimeout(function () {
|
| 1147 |
+
if (type === 'cancel') document.getElementById('orderModalReason').focus();
|
| 1148 |
+
}, 120);
|
| 1149 |
+
}
|
| 1150 |
+
function closeOrderModal() {
|
| 1151 |
+
document.getElementById('orderModal').classList.remove('active');
|
| 1152 |
+
}
|
| 1153 |
+
|
| 1154 |
+
document.getElementById('orderModalCancel').addEventListener('click', closeOrderModal);
|
| 1155 |
+
document.getElementById('orderModalReason').addEventListener('input', function () {
|
| 1156 |
+
if (this.value.trim()) document.getElementById('orderModalReasonErr').classList.remove('show');
|
| 1157 |
+
});
|
| 1158 |
+
document.getElementById('orderModal').addEventListener('click', function (e) {
|
| 1159 |
+
if (e.target === this) closeOrderModal();
|
| 1160 |
+
});
|
| 1161 |
+
document.addEventListener('keydown', function (e) { if (e.key === 'Escape') closeOrderModal(); });
|
| 1162 |
+
document.getElementById('orderModalOk').addEventListener('click', function () {
|
| 1163 |
+
var modal = document.getElementById('orderModal');
|
| 1164 |
+
var type = modal.dataset.type;
|
| 1165 |
+
var id = Number(modal.dataset.id);
|
| 1166 |
+
var order = shopOrders.filter(function (o) { return o.id === id; })[0];
|
| 1167 |
+
var label = order ? order.code : ('#' + id);
|
| 1168 |
+
var btn = this;
|
| 1169 |
+
btn.disabled = true;
|
| 1170 |
+
var url = type === 'ship'
|
| 1171 |
+
? '/api/shop/orders/' + id + '/ship'
|
| 1172 |
+
: '/api/shop/orders/' + id + '/admin-cancel';
|
| 1173 |
+
var init = { method: 'PATCH' };
|
| 1174 |
+
if (type === 'cancel') {
|
| 1175 |
+
var reason = document.getElementById('orderModalReason').value.trim();
|
| 1176 |
+
if (!reason) {
|
| 1177 |
+
document.getElementById('orderModalReasonErr').classList.add('show');
|
| 1178 |
+
document.getElementById('orderModalReason').focus();
|
| 1179 |
+
btn.disabled = false;
|
| 1180 |
+
return;
|
| 1181 |
+
}
|
| 1182 |
+
init.headers = { 'Content-Type': 'application/json' };
|
| 1183 |
+
init.body = JSON.stringify({ reason: reason });
|
| 1184 |
+
}
|
| 1185 |
+
api(url, init).then(function (d) {
|
| 1186 |
+
closeOrderModal();
|
| 1187 |
+
$('#ordersMsg').textContent = type === 'ship'
|
| 1188 |
+
? 'Đã xác nhận ' + label + ' → Đang giao hàng.'
|
| 1189 |
+
: 'Đã hủy ' + label + '.';
|
| 1190 |
+
setTimeout(function () { $('#ordersMsg').textContent = ''; }, 3000);
|
| 1191 |
+
loadShopOrders();
|
| 1192 |
+
}).catch(function (e2) {
|
| 1193 |
+
$('#ordersMsg').textContent = 'Lỗi: ' + e2.message;
|
| 1194 |
+
}).finally(function () { btn.disabled = false; });
|
| 1195 |
+
});
|
| 1196 |
+
|
| 1197 |
+
$('#ordersBody').addEventListener('click', function (e) {
|
| 1198 |
+
var ship = e.target.closest('[data-ship]');
|
| 1199 |
+
var cancel = e.target.closest('[data-cancel]');
|
| 1200 |
+
if (ship) openOrderModal('ship', Number(ship.dataset.ship));
|
| 1201 |
+
if (cancel) openOrderModal('cancel', Number(cancel.dataset.cancel));
|
| 1202 |
+
});
|
| 1203 |
+
|
| 1204 |
+
// ---------- Shop: Cập nhật ----------
|
| 1205 |
+
function renderUpdates() {}
|
| 1206 |
+
|
| 1207 |
+
// ---------- Shop: Báo cáo ----------
|
| 1208 |
+
function renderReports() {
|
| 1209 |
+
if (!cpStats) { $('#reportStats').innerHTML = '<div class="msg">Đang tải... (bấm "Thống kê" để tải lại)</div>'; return; }
|
| 1210 |
+
var avg = cpStats.orders > 0 ? Math.round(cpStats.revenue / cpStats.orders) : 0;
|
| 1211 |
+
$('#reportStats').innerHTML =
|
| 1212 |
+
'<div class="stat-card"><span class="stat-label"><span class="dot" style="background:#ff5e00;"></span> Doanh thu (tổng)</span><span class="stat-value">' + fmt(cpStats.revenue) + '</span></div>' +
|
| 1213 |
+
'<div class="stat-card"><span class="stat-label"><span class="dot" style="background:#2abdbb;"></span> Đơn hàng</span><span class="stat-value">' + cpStats.orders + '</span></div>' +
|
| 1214 |
+
'<div class="stat-card"><span class="stat-label"><span class="dot" style="background:#f5c518;"></span> Món đã bán</span><span class="stat-value">' + cpStats.items_sold + '</span></div>' +
|
| 1215 |
+
'<div class="stat-card"><span class="stat-label"><span class="dot" style="background:#ff8a8a;"></span> Giá trị đơn TB</span><span class="stat-value">' + avg + '<small>đ</small></span></div>';
|
| 1216 |
+
api('/api/shop/reports').then(function (d) {
|
| 1217 |
+
drawRevenueChart(d.series || []);
|
| 1218 |
+
}).catch(function () {
|
| 1219 |
+
var cv = $('#revenueChart');
|
| 1220 |
+
if (cv) { cv.style.display = 'none'; }
|
| 1221 |
+
});
|
| 1222 |
+
}
|
| 1223 |
+
|
| 1224 |
+
function drawRevenueChart(series) {
|
| 1225 |
+
var cv = $('#revenueChart');
|
| 1226 |
+
if (!cv || !series.length) return;
|
| 1227 |
+
cv.style.display = 'block';
|
| 1228 |
+
var ctx = cv.getContext('2d');
|
| 1229 |
+
var W = cv.width, H = cv.height;
|
| 1230 |
+
ctx.clearRect(0, 0, W, H);
|
| 1231 |
+
ctx.fillStyle = '#0d0f14';
|
| 1232 |
+
ctx.fillRect(0, 0, W, H);
|
| 1233 |
+
|
| 1234 |
+
var max = 1;
|
| 1235 |
+
series.forEach(function (s) { if (s.revenue > max) max = s.revenue; });
|
| 1236 |
+
var padL = 64, padR = 12, padT = 18, padB = 34;
|
| 1237 |
+
var bw = (W - padL - padR) / series.length;
|
| 1238 |
+
var barW = Math.min(44, bw * 0.6);
|
| 1239 |
+
ctx.font = '11px Segoe UI, sans-serif';
|
| 1240 |
+
series.forEach(function (s, i) {
|
| 1241 |
+
var x = padL + i * bw + (bw - barW) / 2;
|
| 1242 |
+
var h = max > 0 ? (s.revenue / max) * (H - padT - padB) : 2;
|
| 1243 |
+
var y = H - padB - h;
|
| 1244 |
+
ctx.fillStyle = s.revenue > 0 ? 'rgba(255,94,0,.85)' : 'rgba(255,255,255,.08)';
|
| 1245 |
+
ctx.beginPath();
|
| 1246 |
+
if (ctx.roundRect) ctx.roundRect(x, y, barW, h, 5); else ctx.rect(x, y, barW, h);
|
| 1247 |
+
ctx.fill();
|
| 1248 |
+
if (s.revenue > 0) {
|
| 1249 |
+
ctx.fillStyle = '#fff';
|
| 1250 |
+
ctx.textAlign = 'center';
|
| 1251 |
+
ctx.fillText((s.revenue / 1000).toFixed(0) + 'K', x + barW / 2, y - 5);
|
| 1252 |
+
}
|
| 1253 |
+
ctx.fillStyle = '#8b90a5';
|
| 1254 |
+
ctx.fillText(s.day.slice(5), x + barW / 2, H - 12);
|
| 1255 |
+
});
|
| 1256 |
+
ctx.strokeStyle = 'rgba(255,255,255,.08)';
|
| 1257 |
+
ctx.beginPath();
|
| 1258 |
+
ctx.moveTo(padL - 6, padT);
|
| 1259 |
+
ctx.lineTo(padL - 6, H - padB);
|
| 1260 |
+
ctx.stroke();
|
| 1261 |
+
}
|
| 1262 |
+
|
| 1263 |
+
// ---------- Overview ----------
|
| 1264 |
+
function renderOverview() {
|
| 1265 |
+
$('#overviewStats').innerHTML = '<div class="msg">Đang tải thống kê...</div>';
|
| 1266 |
+
if (!AuthSystem.getToken()) { $('#overviewStats').innerHTML = '<div class="msg">Cần đăng nhập để xem thống kê.</div>'; return; }
|
| 1267 |
+
api('/api/shop/stats').then(function (d) {
|
| 1268 |
+
cpStats = d.stats;
|
| 1269 |
+
var s = d.stats;
|
| 1270 |
+
var lowCls = s.low_stock > 0 ? 'down' : 'up';
|
| 1271 |
+
var lowTxt = s.low_stock > 0 ? '▼ ' + s.low_stock + ' sắp hết' : '▲ đủ hàng';
|
| 1272 |
+
$('#overviewInfo').textContent = 'Cập nhật: ' + new Date().toLocaleTimeString('vi-VN');
|
| 1273 |
+
$('#overviewStats').innerHTML =
|
| 1274 |
+
'<div class="stat-card"><span class="stat-label"><span class="dot" style="background:#ff5e00;"></span> Doanh thu (tổng)</span><span class="stat-value">' + fmt(s.revenue) + '</span></div>' +
|
| 1275 |
+
'<div class="stat-card"><span class="stat-label"><span class="dot" style="background:#2abdbb;"></span> Đơn hàng</span><span class="stat-value">' + s.orders + '</span></div>' +
|
| 1276 |
+
'<div class="stat-card"><span class="stat-label"><span class="dot" style="background:#a3419f;"></span> Sản phẩm trong kho</span><span class="stat-value">' + s.products + '</span><span class="stat-delta ' + lowCls + '">' + lowTxt + '</span></div>' +
|
| 1277 |
+
'<div class="stat-card"><span class="stat-label"><span class="dot" style="background:#f5c518;"></span> Khách hàng (user)</span><span class="stat-value">' + s.customers + '</span></div>' +
|
| 1278 |
+
'<div class="stat-card"><span class="stat-label"><span class="dot" style="background:#f9d423;"></span> Tổng điểm đã tích</span><span class="stat-value">' + Number(s.total_points || 0).toLocaleString('vi-VN') + '</span></div>';
|
| 1279 |
+
}).catch(function (e) {
|
| 1280 |
+
$('#overviewStats').innerHTML = '<div class="msg err">Lỗi tải thống kê: ' + esc(e.message) + '</div>';
|
| 1281 |
+
});
|
| 1282 |
+
}
|
| 1283 |
+
|
| 1284 |
+
// ---------- Boot ----------
|
| 1285 |
+
function boot() {
|
| 1286 |
+
var hasToken = !!AuthSystem.getToken();
|
| 1287 |
+
console.log('[control-panel] boot, hasToken=', hasToken);
|
| 1288 |
+
AuthSystem.me().then(function (user) {
|
| 1289 |
+
console.log('[control-panel] me() ->', user ? user.username + '/' + user.role : null);
|
| 1290 |
+
if (!user) {
|
| 1291 |
+
panel.style.display = 'none';
|
| 1292 |
+
denied.style.display = 'block';
|
| 1293 |
+
var msg = hasToken
|
| 1294 |
+
? 'Phiên đã hết hạn hoặc không thể kết nối máy chủ. <a href="login.html">Đăng nhập lại</a> hoặc <a href="#" id="retryLink">thử lại</a>.'
|
| 1295 |
+
: 'Bạn chưa đăng nhập. <a href="login.html">Đăng nhập bằng tài khoản Admin</a>.';
|
| 1296 |
+
document.getElementById('deniedMsg').innerHTML = msg;
|
| 1297 |
+
var retry = document.getElementById('retryLink');
|
| 1298 |
+
if (retry) retry.addEventListener('click', function (e) { e.preventDefault(); boot(); });
|
| 1299 |
+
return;
|
| 1300 |
+
}
|
| 1301 |
+
whoEl.textContent = user.username || user.email || '';
|
| 1302 |
+
roleEl.textContent = user.role === 'admin' ? 'Admin' : 'Thành viên';
|
| 1303 |
+
avatarEl.textContent = (user.username || 'A').charAt(0).toUpperCase();
|
| 1304 |
+
if (user.role !== 'admin') {
|
| 1305 |
+
denied.style.display = 'block';
|
| 1306 |
+
return;
|
| 1307 |
+
}
|
| 1308 |
+
panel.style.display = 'block';
|
| 1309 |
+
renderOverview();
|
| 1310 |
+
loadUsers();
|
| 1311 |
+
loadContent();
|
| 1312 |
+
loadShopCustomers();
|
| 1313 |
+
loadShopOrders();
|
| 1314 |
+
});
|
| 1315 |
+
}
|
| 1316 |
+
|
| 1317 |
+
boot();
|
| 1318 |
+
window.addEventListener('pageshow', function (e) {
|
| 1319 |
+
if (e.persisted) boot();
|
| 1320 |
+
});
|
| 1321 |
+
|
| 1322 |
+
document.getElementById('logoutBtn').addEventListener('click', function () {
|
| 1323 |
+
AuthSystem.logout().then(function () { window.location.href = 'login.html'; });
|
| 1324 |
+
});
|
| 1325 |
+
|
| 1326 |
+
// Panel switching
|
| 1327 |
+
$$('.cp-nav-item[data-panel]').forEach(function (a) {
|
| 1328 |
+
a.addEventListener('click', function () {
|
| 1329 |
+
switchPanel(a.dataset.panel);
|
| 1330 |
+
$('#cpSide').classList.remove('open');
|
| 1331 |
+
});
|
| 1332 |
+
});
|
| 1333 |
+
$('#cpBurger').addEventListener('click', function () { $('#cpSide').classList.toggle('open'); });
|
| 1334 |
+
|
| 1335 |
+
// Products filter (chips are built in loadShopProducts; wire up clicks here)
|
| 1336 |
+
(function () {
|
| 1337 |
+
$('#cpCatChips').addEventListener('click', function (e) {
|
| 1338 |
+
var chip = e.target.closest('.chip');
|
| 1339 |
+
if (!chip) return;
|
| 1340 |
+
$('#cpCatChips .chip').forEach(function (c) { c.classList.remove('active'); });
|
| 1341 |
+
chip.classList.add('active');
|
| 1342 |
+
cpCurrentCat = chip.dataset.cat;
|
| 1343 |
+
renderCpProducts(cpCurrentCat, '');
|
| 1344 |
+
});
|
| 1345 |
+
})();
|
| 1346 |
+
</script>
|
| 1347 |
+
</body>
|
| 1348 |
+
</html>
|
endpoint_API_sercret
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Client ID:"personal-client-ff37a307-28e2-4a7c-9472-d21ae7d95918-fd5e22d2"
|
| 2 |
+
Client Secret:"v41E0aYeKyl8CQiSKqK8014c5URiYM58"
|
index.html
CHANGED
|
@@ -1,19 +1,1110 @@
|
|
| 1 |
-
<!
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="vi">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8" />
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
+
<meta
|
| 7 |
+
name="description"
|
| 8 |
+
content="ANIVIETDB — Vương Tầm Thế Giới. Một trang web, vô vàn thế giới nội dung anime, manga, light novel."
|
| 9 |
+
/>
|
| 10 |
+
<meta name="theme-color" content="#151515" />
|
| 11 |
+
<link
|
| 12 |
+
rel="icon"
|
| 13 |
+
href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><rect width='100' height='100' rx='22' fill='%23151515'/><text x='50' y='68' font-size='52' font-family='Arial' font-weight='bold' fill='%23ff5e00' text-anchor='middle'>A</text></svg>"
|
| 14 |
+
/>
|
| 15 |
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
| 16 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
| 17 |
+
<link
|
| 18 |
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=Space+Grotesk:wght@400;500;600;700&display=swap"
|
| 19 |
+
rel="stylesheet"
|
| 20 |
+
/>
|
| 21 |
+
<link
|
| 22 |
+
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
|
| 23 |
+
rel="stylesheet"
|
| 24 |
+
/>
|
| 25 |
+
<title>ANIVIETDB · Vươn Tầm Thế Giới</title>
|
| 26 |
+
<style>
|
| 27 |
+
:root {
|
| 28 |
+
--cr-bg: #151515;
|
| 29 |
+
--cr-ink: #ffffff;
|
| 30 |
+
--cr-muted: #8c8c8c;
|
| 31 |
+
--cr-subtle: #bbbbbb;
|
| 32 |
+
--cr-border: #2a2a2a;
|
| 33 |
+
--cr-orange: #ff5e00;
|
| 34 |
+
--cr-orange-hover: #ff7a2a;
|
| 35 |
+
--cr-accent: #2abdbb;
|
| 36 |
+
}
|
| 37 |
+
html,
|
| 38 |
+
body {
|
| 39 |
+
height: 100%;
|
| 40 |
+
overflow-x: hidden;
|
| 41 |
+
}
|
| 42 |
+
body {
|
| 43 |
+
background: var(--cr-bg);
|
| 44 |
+
color: var(--cr-ink);
|
| 45 |
+
font-family: "Inter", system-ui, -apple-system, sans-serif;
|
| 46 |
+
-webkit-font-smoothing: antialiased;
|
| 47 |
+
}
|
| 48 |
+
#bg3d {
|
| 49 |
+
position: fixed;
|
| 50 |
+
inset: 0;
|
| 51 |
+
z-index: 0;
|
| 52 |
+
pointer-events: none;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
/* ----- Crunchyroll-style hero (stage 1) ----- */
|
| 56 |
+
.cr-hero {
|
| 57 |
+
position: relative;
|
| 58 |
+
z-index: 1;
|
| 59 |
+
min-height: 100vh;
|
| 60 |
+
display: flex;
|
| 61 |
+
flex-direction: column;
|
| 62 |
+
justify-content: center;
|
| 63 |
+
padding: 6.5rem 0 8rem;
|
| 64 |
+
background: var(--cr-bg);
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
.cr-wall {
|
| 68 |
+
position: absolute;
|
| 69 |
+
inset: 0;
|
| 70 |
+
z-index: 0;
|
| 71 |
+
overflow: hidden;
|
| 72 |
+
will-change: transform;
|
| 73 |
+
}
|
| 74 |
+
.cr-grid {
|
| 75 |
+
position: absolute;
|
| 76 |
+
inset: -8px;
|
| 77 |
+
display: grid;
|
| 78 |
+
grid-template-columns: repeat(8, 1fr);
|
| 79 |
+
grid-template-rows: repeat(3, 1fr);
|
| 80 |
+
gap: 2px;
|
| 81 |
+
}
|
| 82 |
+
.cr-cell {
|
| 83 |
+
overflow: hidden;
|
| 84 |
+
}
|
| 85 |
+
.cr-cell img {
|
| 86 |
+
display: block;
|
| 87 |
+
width: 100%;
|
| 88 |
+
height: 100%;
|
| 89 |
+
object-fit: cover;
|
| 90 |
+
filter: brightness(0.42) saturate(0.65);
|
| 91 |
+
opacity: 0.9;
|
| 92 |
+
}
|
| 93 |
+
.cr-wall::after {
|
| 94 |
+
content: "";
|
| 95 |
+
position: absolute;
|
| 96 |
+
inset: 0;
|
| 97 |
+
background:
|
| 98 |
+
linear-gradient(180deg, rgba(21, 21, 21, 0.2), rgba(21, 21, 21, 0.72) 68%, #151515 100%),
|
| 99 |
+
radial-gradient(120% 90% at 50% 0%, rgba(21, 21, 21, 0) 42%, rgba(21, 21, 21, 0.55) 100%);
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
.cr-hero-inner {
|
| 103 |
+
position: relative;
|
| 104 |
+
z-index: 2;
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
.cr-badge {
|
| 108 |
+
display: inline-flex;
|
| 109 |
+
align-items: center;
|
| 110 |
+
gap: 0.5rem;
|
| 111 |
+
border-radius: 999px;
|
| 112 |
+
border: 1px solid rgba(255, 255, 255, 0.16);
|
| 113 |
+
background: rgba(255, 255, 255, 0.05);
|
| 114 |
+
backdrop-filter: blur(8px);
|
| 115 |
+
padding: 0.4rem 0.95rem;
|
| 116 |
+
font-size: 0.75rem;
|
| 117 |
+
font-weight: 500;
|
| 118 |
+
letter-spacing: 0.03em;
|
| 119 |
+
color: var(--cr-subtle);
|
| 120 |
+
}
|
| 121 |
+
.cr-badge .dot {
|
| 122 |
+
width: 6px;
|
| 123 |
+
height: 6px;
|
| 124 |
+
border-radius: 50%;
|
| 125 |
+
background: var(--cr-orange);
|
| 126 |
+
animation: cr-pulse 1.6s ease-in-out infinite;
|
| 127 |
+
}
|
| 128 |
+
@keyframes cr-pulse {
|
| 129 |
+
0%,
|
| 130 |
+
100% {
|
| 131 |
+
opacity: 1;
|
| 132 |
+
transform: scale(1);
|
| 133 |
+
}
|
| 134 |
+
50% {
|
| 135 |
+
opacity: 0.45;
|
| 136 |
+
transform: scale(0.75);
|
| 137 |
+
}
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
.cr-title {
|
| 141 |
+
font-family: "Space Grotesk", "Inter", sans-serif;
|
| 142 |
+
font-weight: 700;
|
| 143 |
+
letter-spacing: -0.025em;
|
| 144 |
+
font-size: clamp(2.7rem, 9vw, 6rem);
|
| 145 |
+
line-height: 1.02;
|
| 146 |
+
color: var(--cr-ink);
|
| 147 |
+
}
|
| 148 |
+
.cr-title .hero-letter {
|
| 149 |
+
display: inline-block;
|
| 150 |
+
will-change: transform;
|
| 151 |
+
}
|
| 152 |
+
.cr-title .grad {
|
| 153 |
+
background: linear-gradient(92deg, var(--cr-orange) 0%, #ff9a5a 45%, #f0abfc 100%);
|
| 154 |
+
-webkit-background-clip: text;
|
| 155 |
+
background-clip: text;
|
| 156 |
+
color: transparent;
|
| 157 |
+
}
|
| 158 |
+
|
| 159 |
+
.cr-sub {
|
| 160 |
+
margin-top: 1.25rem;
|
| 161 |
+
max-width: 42rem;
|
| 162 |
+
font-size: 1.05rem;
|
| 163 |
+
line-height: 1.7;
|
| 164 |
+
color: var(--cr-muted);
|
| 165 |
+
}
|
| 166 |
+
|
| 167 |
+
.cr-cta-row {
|
| 168 |
+
margin-top: 2.25rem;
|
| 169 |
+
display: flex;
|
| 170 |
+
flex-wrap: wrap;
|
| 171 |
+
gap: 0.9rem;
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
.cr-marquee {
|
| 175 |
+
position: absolute;
|
| 176 |
+
inset-inline: 0;
|
| 177 |
+
bottom: 0;
|
| 178 |
+
z-index: 3;
|
| 179 |
+
border-top: 1px solid rgba(255, 255, 255, 0.08);
|
| 180 |
+
background: rgba(21, 21, 21, 0.55);
|
| 181 |
+
backdrop-filter: blur(12px);
|
| 182 |
+
padding: 0.95rem 0;
|
| 183 |
+
overflow: hidden;
|
| 184 |
+
}
|
| 185 |
+
.cr-marquee-track {
|
| 186 |
+
display: flex;
|
| 187 |
+
gap: 2.5rem;
|
| 188 |
+
white-space: nowrap;
|
| 189 |
+
animation: cr-marquee 30s linear infinite;
|
| 190 |
+
will-change: transform;
|
| 191 |
+
}
|
| 192 |
+
.cr-marquee-item {
|
| 193 |
+
display: inline-flex;
|
| 194 |
+
align-items: center;
|
| 195 |
+
gap: 2.5rem;
|
| 196 |
+
font-size: 0.85rem;
|
| 197 |
+
font-weight: 500;
|
| 198 |
+
letter-spacing: 0.04em;
|
| 199 |
+
color: var(--cr-muted);
|
| 200 |
+
}
|
| 201 |
+
.cr-marquee-item .sep {
|
| 202 |
+
width: 6px;
|
| 203 |
+
height: 6px;
|
| 204 |
+
border-radius: 50%;
|
| 205 |
+
background: var(--cr-orange);
|
| 206 |
+
opacity: 0.75;
|
| 207 |
+
}
|
| 208 |
+
@keyframes cr-marquee {
|
| 209 |
+
to {
|
| 210 |
+
transform: translateX(-50%);
|
| 211 |
+
}
|
| 212 |
+
}
|
| 213 |
+
|
| 214 |
+
/* ----- shadcn-style Button (primary / outline) ----- */
|
| 215 |
+
.btn-cr {
|
| 216 |
+
display: inline-flex;
|
| 217 |
+
align-items: center;
|
| 218 |
+
justify-content: center;
|
| 219 |
+
gap: 0.5rem;
|
| 220 |
+
border-radius: 999px;
|
| 221 |
+
font-weight: 600;
|
| 222 |
+
font-size: 0.9375rem;
|
| 223 |
+
line-height: 1.5;
|
| 224 |
+
padding: 0.625rem 1.5rem;
|
| 225 |
+
border: 1px solid transparent;
|
| 226 |
+
cursor: pointer;
|
| 227 |
+
transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease,
|
| 228 |
+
box-shadow 0.2s ease, transform 0.2s ease;
|
| 229 |
+
text-decoration: none;
|
| 230 |
+
white-space: nowrap;
|
| 231 |
+
user-select: none;
|
| 232 |
+
}
|
| 233 |
+
.btn-cr:focus-visible {
|
| 234 |
+
outline: none;
|
| 235 |
+
box-shadow: 0 0 0 2px var(--cr-bg), 0 0 0 4px var(--cr-orange);
|
| 236 |
+
}
|
| 237 |
+
.btn-cr:disabled {
|
| 238 |
+
opacity: 0.5;
|
| 239 |
+
pointer-events: none;
|
| 240 |
+
}
|
| 241 |
+
.btn-cr:hover {
|
| 242 |
+
transform: translateY(-1px);
|
| 243 |
+
}
|
| 244 |
+
.btn-cr-default {
|
| 245 |
+
background: var(--cr-orange);
|
| 246 |
+
color: #000;
|
| 247 |
+
box-shadow: 0 10px 28px rgba(255, 94, 0, 0.32);
|
| 248 |
+
}
|
| 249 |
+
.btn-cr-default:hover {
|
| 250 |
+
background: var(--cr-orange-hover);
|
| 251 |
+
color: #000;
|
| 252 |
+
box-shadow: 0 12px 32px rgba(255, 94, 0, 0.42);
|
| 253 |
+
}
|
| 254 |
+
.btn-cr-outline {
|
| 255 |
+
border-color: rgba(255, 255, 255, 0.32);
|
| 256 |
+
color: var(--cr-ink);
|
| 257 |
+
background: transparent;
|
| 258 |
+
}
|
| 259 |
+
.btn-cr-outline:hover {
|
| 260 |
+
border-color: rgba(255, 255, 255, 0.9);
|
| 261 |
+
background: rgba(255, 255, 255, 0.07);
|
| 262 |
+
color: var(--cr-ink);
|
| 263 |
+
}
|
| 264 |
+
.btn-cr-lg {
|
| 265 |
+
height: 3rem;
|
| 266 |
+
padding-inline: 2.1rem;
|
| 267 |
+
font-size: 1rem;
|
| 268 |
+
border-radius: 999px;
|
| 269 |
+
}
|
| 270 |
+
|
| 271 |
+
/* ----- Feature stage ----- */
|
| 272 |
+
.cr-features {
|
| 273 |
+
position: relative;
|
| 274 |
+
z-index: 1;
|
| 275 |
+
background: var(--cr-bg);
|
| 276 |
+
padding: 5.5rem 0 6rem;
|
| 277 |
+
}
|
| 278 |
+
.cr-features-head {
|
| 279 |
+
text-align: left;
|
| 280 |
+
margin-bottom: 2.5rem;
|
| 281 |
+
}
|
| 282 |
+
.cr-features-head h2 {
|
| 283 |
+
font-family: "Space Grotesk", sans-serif;
|
| 284 |
+
font-weight: 700;
|
| 285 |
+
font-size: clamp(1.8rem, 4vw, 2.8rem);
|
| 286 |
+
letter-spacing: -0.02em;
|
| 287 |
+
color: var(--cr-ink);
|
| 288 |
+
}
|
| 289 |
+
.cr-features-head h2 .grad {
|
| 290 |
+
background: linear-gradient(92deg, var(--cr-orange) 0%, #ff9a5a 60%, #f0abfc 100%);
|
| 291 |
+
-webkit-background-clip: text;
|
| 292 |
+
background-clip: text;
|
| 293 |
+
color: transparent;
|
| 294 |
+
}
|
| 295 |
+
.cr-features-head p {
|
| 296 |
+
margin-top: 0.6rem;
|
| 297 |
+
color: var(--cr-muted);
|
| 298 |
+
font-size: 0.95rem;
|
| 299 |
+
}
|
| 300 |
+
.cr-feature-row {
|
| 301 |
+
display: grid;
|
| 302 |
+
grid-template-columns: repeat(5, minmax(0, 1fr));
|
| 303 |
+
gap: 1.25rem;
|
| 304 |
+
}
|
| 305 |
+
.cr-feature-box {
|
| 306 |
+
position: relative;
|
| 307 |
+
border-radius: 16px;
|
| 308 |
+
overflow: hidden;
|
| 309 |
+
aspect-ratio: 2 / 3;
|
| 310 |
+
background: #1e1e1e;
|
| 311 |
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
| 312 |
+
cursor: pointer;
|
| 313 |
+
text-decoration: none;
|
| 314 |
+
display: flex;
|
| 315 |
+
flex-direction: column;
|
| 316 |
+
justify-content: flex-end;
|
| 317 |
+
will-change: transform;
|
| 318 |
+
transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease;
|
| 319 |
+
}
|
| 320 |
+
.cr-feature-box:hover {
|
| 321 |
+
transform: translateY(-6px);
|
| 322 |
+
box-shadow: 0 18px 40px rgba(255, 94, 0, 0.2);
|
| 323 |
+
border-color: rgba(255, 94, 0, 0.45);
|
| 324 |
+
}
|
| 325 |
+
.cr-feature-box img {
|
| 326 |
+
position: absolute;
|
| 327 |
+
inset: 0;
|
| 328 |
+
width: 100%;
|
| 329 |
+
height: 100%;
|
| 330 |
+
object-fit: cover;
|
| 331 |
+
transition: transform 0.35s ease;
|
| 332 |
+
}
|
| 333 |
+
.cr-feature-box:hover img {
|
| 334 |
+
transform: scale(1.06);
|
| 335 |
+
}
|
| 336 |
+
.cr-feature-box .shade {
|
| 337 |
+
position: absolute;
|
| 338 |
+
inset: 0;
|
| 339 |
+
background: linear-gradient(to top, rgba(21, 21, 21, 0.95) 8%, rgba(21, 21, 21, 0.25) 55%, transparent);
|
| 340 |
+
}
|
| 341 |
+
.cr-feature-box .info {
|
| 342 |
+
position: relative;
|
| 343 |
+
padding: 1rem 0.9rem;
|
| 344 |
+
color: #fff;
|
| 345 |
+
}
|
| 346 |
+
.cr-feature-box .tag {
|
| 347 |
+
display: inline-block;
|
| 348 |
+
border-radius: 999px;
|
| 349 |
+
background: var(--cr-orange);
|
| 350 |
+
color: #000;
|
| 351 |
+
font-size: 0.6rem;
|
| 352 |
+
font-weight: 700;
|
| 353 |
+
letter-spacing: 0.04em;
|
| 354 |
+
padding: 0.18rem 0.55rem;
|
| 355 |
+
margin-bottom: 0.4rem;
|
| 356 |
+
}
|
| 357 |
+
.cr-feature-box .t {
|
| 358 |
+
font-family: "Space Grotesk", sans-serif;
|
| 359 |
+
font-weight: 600;
|
| 360 |
+
font-size: 1rem;
|
| 361 |
+
line-height: 1.25;
|
| 362 |
+
margin: 0;
|
| 363 |
+
}
|
| 364 |
+
.cr-feature-box .s {
|
| 365 |
+
color: rgba(255, 255, 255, 0.7);
|
| 366 |
+
font-size: 0.72rem;
|
| 367 |
+
margin-top: 0.2rem;
|
| 368 |
+
}
|
| 369 |
+
.cr-feature-box.fade-in {
|
| 370 |
+
opacity: 0;
|
| 371 |
+
transform: translateY(28px);
|
| 372 |
+
}
|
| 373 |
+
.cr-feature-box.fade-in.visible {
|
| 374 |
+
opacity: 1;
|
| 375 |
+
transform: translateY(0);
|
| 376 |
+
transition: opacity 0.6s ease, transform 0.6s ease, box-shadow 0.25s ease,
|
| 377 |
+
border-color 0.25s ease;
|
| 378 |
+
}
|
| 379 |
+
|
| 380 |
+
/* ----- Shop stage (stage 3) ----- */
|
| 381 |
+
.cr-shop {
|
| 382 |
+
position: relative;
|
| 383 |
+
z-index: 1;
|
| 384 |
+
background: linear-gradient(180deg, var(--cr-bg) 0%, #171719 100%);
|
| 385 |
+
padding: 5.5rem 0 6rem;
|
| 386 |
+
}
|
| 387 |
+
.cr-shop-head {
|
| 388 |
+
text-align: center;
|
| 389 |
+
margin-bottom: 2.5rem;
|
| 390 |
+
}
|
| 391 |
+
.cr-shop-head h2 {
|
| 392 |
+
font-family: "Space Grotesk", sans-serif;
|
| 393 |
+
font-weight: 700;
|
| 394 |
+
font-size: clamp(1.8rem, 4vw, 2.8rem);
|
| 395 |
+
letter-spacing: -0.02em;
|
| 396 |
+
color: var(--cr-ink);
|
| 397 |
+
}
|
| 398 |
+
.cr-shop-head h2 .grad {
|
| 399 |
+
background: linear-gradient(92deg, var(--cr-orange) 0%, #ff9a5a 60%, #f0abfc 100%);
|
| 400 |
+
-webkit-background-clip: text;
|
| 401 |
+
background-clip: text;
|
| 402 |
+
color: transparent;
|
| 403 |
+
}
|
| 404 |
+
.cr-shop-head p {
|
| 405 |
+
margin-top: 0.6rem;
|
| 406 |
+
color: var(--cr-muted);
|
| 407 |
+
font-size: 0.95rem;
|
| 408 |
+
}
|
| 409 |
+
.cr-shop-row {
|
| 410 |
+
display: grid;
|
| 411 |
+
grid-template-columns: repeat(4, minmax(0, 1fr));
|
| 412 |
+
gap: 1.25rem;
|
| 413 |
+
}
|
| 414 |
+
.cr-shop-card {
|
| 415 |
+
border-radius: 16px;
|
| 416 |
+
overflow: hidden;
|
| 417 |
+
background: #1d1d1f;
|
| 418 |
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
| 419 |
+
text-decoration: none;
|
| 420 |
+
display: flex;
|
| 421 |
+
flex-direction: column;
|
| 422 |
+
transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease;
|
| 423 |
+
cursor: pointer;
|
| 424 |
+
}
|
| 425 |
+
.cr-shop-card:hover {
|
| 426 |
+
transform: translateY(-6px);
|
| 427 |
+
box-shadow: 0 18px 40px rgba(255, 94, 0, 0.18);
|
| 428 |
+
border-color: rgba(255, 94, 0, 0.45);
|
| 429 |
+
}
|
| 430 |
+
.cr-shop-card .pic {
|
| 431 |
+
position: relative;
|
| 432 |
+
aspect-ratio: 1 / 1;
|
| 433 |
+
background: #141416;
|
| 434 |
+
overflow: hidden;
|
| 435 |
+
}
|
| 436 |
+
.cr-shop-card .pic img {
|
| 437 |
+
width: 100%;
|
| 438 |
+
height: 100%;
|
| 439 |
+
object-fit: cover;
|
| 440 |
+
transition: transform 0.35s ease;
|
| 441 |
+
}
|
| 442 |
+
.cr-shop-card:hover .pic img {
|
| 443 |
+
transform: scale(1.07);
|
| 444 |
+
}
|
| 445 |
+
.cr-shop-card .pic .cat {
|
| 446 |
+
position: absolute;
|
| 447 |
+
top: 0.6rem;
|
| 448 |
+
left: 0.6rem;
|
| 449 |
+
border-radius: 999px;
|
| 450 |
+
background: rgba(21, 21, 21, 0.75);
|
| 451 |
+
color: var(--cr-orange);
|
| 452 |
+
font-size: 0.62rem;
|
| 453 |
+
font-weight: 700;
|
| 454 |
+
letter-spacing: 0.04em;
|
| 455 |
+
padding: 0.2rem 0.6rem;
|
| 456 |
+
backdrop-filter: blur(4px);
|
| 457 |
+
}
|
| 458 |
+
.cr-shop-card .pic .price {
|
| 459 |
+
position: absolute;
|
| 460 |
+
bottom: 0.6rem;
|
| 461 |
+
right: 0.6rem;
|
| 462 |
+
border-radius: 10px;
|
| 463 |
+
background: var(--cr-orange);
|
| 464 |
+
color: #000;
|
| 465 |
+
font-weight: 800;
|
| 466 |
+
font-size: 0.8rem;
|
| 467 |
+
padding: 0.3rem 0.65rem;
|
| 468 |
+
}
|
| 469 |
+
.cr-shop-card .meta {
|
| 470 |
+
padding: 0.9rem 1rem 1.1rem;
|
| 471 |
+
}
|
| 472 |
+
.cr-shop-card .meta .t {
|
| 473 |
+
font-family: "Space Grotesk", sans-serif;
|
| 474 |
+
font-weight: 600;
|
| 475 |
+
font-size: 0.95rem;
|
| 476 |
+
color: #fff;
|
| 477 |
+
margin: 0 0 0.15rem;
|
| 478 |
+
}
|
| 479 |
+
.cr-shop-card .meta .s {
|
| 480 |
+
color: var(--cr-muted);
|
| 481 |
+
font-size: 0.75rem;
|
| 482 |
+
}
|
| 483 |
+
.cr-shop-card.fade-in {
|
| 484 |
+
opacity: 0;
|
| 485 |
+
transform: translateY(28px);
|
| 486 |
+
}
|
| 487 |
+
.cr-shop-card.fade-in.visible {
|
| 488 |
+
opacity: 1;
|
| 489 |
+
transform: translateY(0);
|
| 490 |
+
transition: opacity 0.6s ease, transform 0.6s ease, box-shadow 0.25s ease,
|
| 491 |
+
border-color 0.25s ease;
|
| 492 |
+
}
|
| 493 |
+
.cr-shop-cta {
|
| 494 |
+
margin-top: 2.75rem;
|
| 495 |
+
text-align: center;
|
| 496 |
+
}
|
| 497 |
+
|
| 498 |
+
/* ----- Footer ----- */
|
| 499 |
+
.cr-footer {
|
| 500 |
+
position: relative;
|
| 501 |
+
z-index: 1;
|
| 502 |
+
border-top: 1px solid rgba(255, 255, 255, 0.08);
|
| 503 |
+
background: var(--cr-bg);
|
| 504 |
+
padding: 3.25rem 1.5rem 3rem;
|
| 505 |
+
text-align: center;
|
| 506 |
+
}
|
| 507 |
+
.cr-footer p {
|
| 508 |
+
max-width: 46rem;
|
| 509 |
+
margin-inline: auto;
|
| 510 |
+
color: var(--cr-muted);
|
| 511 |
+
font-size: 0.85rem;
|
| 512 |
+
line-height: 1.7;
|
| 513 |
+
}
|
| 514 |
+
.cr-footer p + p {
|
| 515 |
+
margin-top: 0.35rem;
|
| 516 |
+
color: var(--cr-subtle);
|
| 517 |
+
font-size: 0.8rem;
|
| 518 |
+
}
|
| 519 |
+
.cr-footer .brand {
|
| 520 |
+
font-family: "Space Grotesk", sans-serif;
|
| 521 |
+
font-weight: 700;
|
| 522 |
+
font-size: 1rem;
|
| 523 |
+
letter-spacing: 0.06em;
|
| 524 |
+
color: var(--cr-ink);
|
| 525 |
+
margin-bottom: 0.9rem;
|
| 526 |
+
}
|
| 527 |
+
.cr-footer .brand span {
|
| 528 |
+
color: var(--cr-orange);
|
| 529 |
+
}
|
| 530 |
+
|
| 531 |
+
@media (max-width: 767.98px) {
|
| 532 |
+
.cr-hero {
|
| 533 |
+
padding-top: 5rem;
|
| 534 |
+
}
|
| 535 |
+
.cr-grid {
|
| 536 |
+
grid-template-columns: repeat(4, 1fr);
|
| 537 |
+
grid-template-rows: repeat(6, 1fr);
|
| 538 |
+
}
|
| 539 |
+
.cr-feature-row {
|
| 540 |
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
| 541 |
+
gap: 0.9rem;
|
| 542 |
+
}
|
| 543 |
+
.cr-shop-row {
|
| 544 |
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
| 545 |
+
gap: 0.9rem;
|
| 546 |
+
}
|
| 547 |
+
}
|
| 548 |
+
</style>
|
| 549 |
+
</head>
|
| 550 |
+
<body>
|
| 551 |
+
<div id="root"></div>
|
| 552 |
+
|
| 553 |
+
<script src="https://unpkg.com/react@18.3.1/umd/react.production.min.js"></script>
|
| 554 |
+
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.production.min.js"></script>
|
| 555 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
| 556 |
+
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
|
| 557 |
+
|
| 558 |
+
<script>
|
| 559 |
+
(function () {
|
| 560 |
+
var React = window.React;
|
| 561 |
+
var ReactDOM = window.ReactDOM;
|
| 562 |
+
var e = React.createElement;
|
| 563 |
+
var useRef = React.useRef;
|
| 564 |
+
var useEffect = React.useEffect;
|
| 565 |
+
|
| 566 |
+
var POSTERS = [
|
| 567 |
+
"https://phim.nguonc.com/public/images/Film/76.jpg",
|
| 568 |
+
"https://phim.nguonc.com/public/images/Film/666.jpg",
|
| 569 |
+
"https://phim.nguonc.com/public/images/Film/77.jpg",
|
| 570 |
+
"https://phim.nguonc.com/public/images/Film/667.jpg",
|
| 571 |
+
"https://phim.nguonc.com/public/images/Film/259.jpg",
|
| 572 |
+
"https://phim.nguonc.com/public/images/Film/2738.jpg",
|
| 573 |
+
"https://phim.nguonc.com/public/images/Film/3485.jpg",
|
| 574 |
+
"https://phim.nguonc.com/public/images/Film/141438.jpg",
|
| 575 |
+
"https://phim.nguonc.com/public/images/Film/date-a-bullet-thumb.jpg",
|
| 576 |
+
"https://phim.nguonc.com/public/images/Film/3103.jpg",
|
| 577 |
+
"https://phim.nguonc.com/public/images/Post/5/aishiteru-game-wo-owarasetai.jpg",
|
| 578 |
+
"https://phim.nguonc.com/public/images/Film/541.jpg",
|
| 579 |
+
"https://phim.nguonc.com/public/images/Film/536.jpg",
|
| 580 |
+
"https://phim.nguonc.com/public/images/Film/56167.jpg",
|
| 581 |
+
"https://phim.nguonc.com/public/images/Film/56643.jpg",
|
| 582 |
+
"https://phim.nguonc.com/public/images/Film/538.jpg",
|
| 583 |
+
"https://phim.nguonc.com/public/images/Film/540.jpg",
|
| 584 |
+
"https://phim.nguonc.com/public/images/Film/60351.jpg",
|
| 585 |
+
"https://phim.nguonc.com/public/images/Film/2607.jpg",
|
| 586 |
+
"https://phim.nguonc.com/public/images/Film/2661.jpg",
|
| 587 |
+
"https://phim.nguonc.com/public/images/Film/5MgMCnslrxQfOfaq6T5ndkLlcCk.jpg",
|
| 588 |
+
"https://phim.nguonc.com/public/images/Film/134808.jpg",
|
| 589 |
+
"https://phim.nguonc.com/public/images/Film/138379.jpg",
|
| 590 |
+
"https://phim.nguonc.com/public/images/Post/9/seishun-buta-yarou-wa-santa-claus-no-yume-wo-minai-1.jpg"
|
| 591 |
+
];
|
| 592 |
+
|
| 593 |
+
var MARQUEE = [
|
| 594 |
+
"Anime",
|
| 595 |
+
"Manga",
|
| 596 |
+
"Manhwa",
|
| 597 |
+
"Manhua",
|
| 598 |
+
"Light Novel",
|
| 599 |
+
"Sưu tầm",
|
| 600 |
+
"Vô vàn thế giới"
|
| 601 |
+
];
|
| 602 |
+
|
| 603 |
+
var FEATURES = [
|
| 604 |
+
{ name: "Death Note", sub: "37 tập", tag: "Anime", page: "sfw/anime/universal/death-note/", img: "https://phim.nguonc.com/public/images/Film/244.jpg" },
|
| 605 |
+
{ name: "Tensei Slime", sub: "24 tập", tag: "Anime", page: "sfw/anime/universal/tensei-slime/phan-1/", img: "https://phim.nguonc.com/public/images/Film/2658.jpg" },
|
| 606 |
+
{ name: "JoJo Phần 1", sub: "26 tập", tag: "Anime", page: "sfw/anime/universal/jojo/phan-1/", img: "https://phim.nguonc.com/public/images/Film/40409.jpg" },
|
| 607 |
+
{ name: "Fate/Zero", sub: "13 tập", tag: "Anime", page: "sfw/anime/multiversal/fate/fate-zero/phan-1/", img: "https://phim.nguonc.com/public/images/Film/117644.jpg" },
|
| 608 |
+
{ name: "Bocchi the Rock!", sub: "12 tập", tag: "Anime", page: "sfw/anime/universal/bocchi-the-rock/phan-1/", img: "https://phim.nguonc.com/public/images/Film/127956.jpg" }
|
| 609 |
+
];
|
| 610 |
+
|
| 611 |
+
var SHOP_PRODUCTS = [
|
| 612 |
+
{ name: "Figure Gojo Satoru", anime: "Jujutsu Kaisen", cat: "Figure", price: "1.290.000đ", img: "https://phim.nguonc.com/public/images/Film/76.jpg" },
|
| 613 |
+
{ name: "Figure Luffy Gear 5", anime: "One Piece", cat: "Figure", price: "1.450.000đ", img: "https://phim.nguonc.com/public/images/Film/666.jpg" },
|
| 614 |
+
{ name: "Plushie Anya Forger", anime: "Spy x Family", cat: "Plushie", price: "350.000đ", img: "https://phim.nguonc.com/public/images/Film/3485.jpg" },
|
| 615 |
+
{ name: "Mug Steins;Gate", anime: "Steins;Gate", cat: "Mug", price: "185.000đ", img: "https://phim.nguonc.com/public/images/Film/536.jpg" }
|
| 616 |
+
];
|
| 617 |
+
|
| 618 |
+
function cx() {
|
| 619 |
+
return Array.prototype.filter.call(arguments, Boolean).join(" ");
|
| 620 |
+
}
|
| 621 |
+
|
| 622 |
+
function Button(props) {
|
| 623 |
+
var className = cx(
|
| 624 |
+
"btn-cr",
|
| 625 |
+
"btn-cr-" + (props.variant || "default"),
|
| 626 |
+
(props.size || "default") === "lg" ? "btn-cr-lg" : "",
|
| 627 |
+
props.className
|
| 628 |
+
);
|
| 629 |
+
var p = {};
|
| 630 |
+
for (var k in props) {
|
| 631 |
+
if (k === "variant" || k === "size" || k === "className" || k === "children") continue;
|
| 632 |
+
p[k] = props[k];
|
| 633 |
+
}
|
| 634 |
+
return e("button", Object.assign({}, p, { className: className }), props.children);
|
| 635 |
+
}
|
| 636 |
+
|
| 637 |
+
function ThreeBackground() {
|
| 638 |
+
var ref = useRef(null);
|
| 639 |
+
useEffect(function () {
|
| 640 |
+
var canvas = ref.current;
|
| 641 |
+
var renderer = new THREE.WebGLRenderer({
|
| 642 |
+
canvas: canvas,
|
| 643 |
+
alpha: true,
|
| 644 |
+
antialias: true
|
| 645 |
+
});
|
| 646 |
+
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
| 647 |
+
var w = window.innerWidth;
|
| 648 |
+
var h = window.innerHeight;
|
| 649 |
+
var scene = new THREE.Scene();
|
| 650 |
+
var camera = new THREE.PerspectiveCamera(70, w / h, 0.1, 40);
|
| 651 |
+
camera.position.z = 1;
|
| 652 |
+
|
| 653 |
+
function onResize() {
|
| 654 |
+
w = window.innerWidth;
|
| 655 |
+
h = window.innerHeight;
|
| 656 |
+
renderer.setSize(w, h);
|
| 657 |
+
camera.aspect = w / h;
|
| 658 |
+
camera.updateProjectionMatrix();
|
| 659 |
+
}
|
| 660 |
+
onResize();
|
| 661 |
+
window.addEventListener("resize", onResize);
|
| 662 |
+
|
| 663 |
+
var COUNT = 1400;
|
| 664 |
+
var positions = new Float32Array(COUNT * 3);
|
| 665 |
+
for (var i = 0; i < COUNT * 3; i++) positions[i] = (Math.random() - 0.5) * 4;
|
| 666 |
+
var geo = new THREE.BufferGeometry();
|
| 667 |
+
geo.setAttribute("position", new THREE.BufferAttribute(positions, 3));
|
| 668 |
+
var mat = new THREE.PointsMaterial({
|
| 669 |
+
color: 0xffffff,
|
| 670 |
+
size: 0.004,
|
| 671 |
+
transparent: true,
|
| 672 |
+
opacity: 0.65,
|
| 673 |
+
sizeAttenuation: true
|
| 674 |
+
});
|
| 675 |
+
var stars = new THREE.Points(geo, mat);
|
| 676 |
+
scene.add(stars);
|
| 677 |
+
|
| 678 |
+
var glow = new THREE.Mesh(
|
| 679 |
+
new THREE.SphereGeometry(0.16, 32, 32),
|
| 680 |
+
new THREE.MeshBasicMaterial({
|
| 681 |
+
color: 0xff5e00,
|
| 682 |
+
transparent: true,
|
| 683 |
+
opacity: 0.16
|
| 684 |
+
})
|
| 685 |
+
);
|
| 686 |
+
glow.position.set(0.9, 0.55, -0.6);
|
| 687 |
+
scene.add(glow);
|
| 688 |
+
|
| 689 |
+
var raf;
|
| 690 |
+
var mx = 0;
|
| 691 |
+
var my = 0;
|
| 692 |
+
function onMouse(e2) {
|
| 693 |
+
mx = (e2.clientX / w - 0.5) * 2;
|
| 694 |
+
my = (e2.clientY / h - 0.5) * 2;
|
| 695 |
+
}
|
| 696 |
+
window.addEventListener("mousemove", onMouse);
|
| 697 |
+
|
| 698 |
+
function tick() {
|
| 699 |
+
stars.rotation.y += 0.00022;
|
| 700 |
+
stars.rotation.x += 0.00008;
|
| 701 |
+
camera.position.x += (mx * 0.05 - camera.position.x) * 0.04;
|
| 702 |
+
camera.position.y += (-my * 0.05 - camera.position.y) * 0.04;
|
| 703 |
+
camera.lookAt(0, 0, 0);
|
| 704 |
+
renderer.render(scene, camera);
|
| 705 |
+
raf = requestAnimationFrame(tick);
|
| 706 |
+
}
|
| 707 |
+
tick();
|
| 708 |
+
|
| 709 |
+
return function () {
|
| 710 |
+
cancelAnimationFrame(raf);
|
| 711 |
+
window.removeEventListener("resize", onResize);
|
| 712 |
+
window.removeEventListener("mousemove", onMouse);
|
| 713 |
+
renderer.dispose();
|
| 714 |
+
};
|
| 715 |
+
}, []);
|
| 716 |
+
return e("canvas", { id: "bg3d", ref: ref });
|
| 717 |
+
}
|
| 718 |
+
|
| 719 |
+
function Hero() {
|
| 720 |
+
var wallRef = useRef(null);
|
| 721 |
+
useEffect(function () {
|
| 722 |
+
var letters = gsap.utils.toArray(".hero-letter");
|
| 723 |
+
var reveals = gsap.utils.toArray(".cr-reveal");
|
| 724 |
+
gsap.set(letters, { yPercent: 115, opacity: 0 });
|
| 725 |
+
gsap.set(reveals, { y: 26, opacity: 0 });
|
| 726 |
+
var tl = gsap.timeline({ defaults: { ease: "power3.out" } });
|
| 727 |
+
tl.to(letters, { yPercent: 0, opacity: 1, stagger: 0.05, duration: 0.85 }, 0.2)
|
| 728 |
+
.to(reveals, { y: 0, opacity: 1, stagger: 0.1, duration: 0.6 }, 0.6)
|
| 729 |
+
.fromTo(
|
| 730 |
+
wallRef.current,
|
| 731 |
+
{ scale: 1.22 },
|
| 732 |
+
{ scale: 1.02, duration: 2.4, ease: "power2.out" },
|
| 733 |
+
0
|
| 734 |
+
);
|
| 735 |
+
return function () {
|
| 736 |
+
tl.kill();
|
| 737 |
+
};
|
| 738 |
+
}, []);
|
| 739 |
+
|
| 740 |
+
var brand = "ANIVIETDB".split("");
|
| 741 |
+
var heroLetters = brand.map(function (c, i) {
|
| 742 |
+
return e("span", { className: "hero-letter", key: i }, c);
|
| 743 |
+
});
|
| 744 |
+
|
| 745 |
+
return e(
|
| 746 |
+
"section",
|
| 747 |
+
{ className: "cr-hero" },
|
| 748 |
+
e(
|
| 749 |
+
"div",
|
| 750 |
+
{ className: "cr-wall", ref: wallRef },
|
| 751 |
+
e(
|
| 752 |
+
"div",
|
| 753 |
+
{ className: "cr-grid" },
|
| 754 |
+
POSTERS.map(function (u, i) {
|
| 755 |
+
return e(
|
| 756 |
+
"div",
|
| 757 |
+
{ className: "cr-cell", key: i },
|
| 758 |
+
e("img", { src: u, alt: "", loading: "lazy" })
|
| 759 |
+
);
|
| 760 |
+
})
|
| 761 |
+
)
|
| 762 |
+
),
|
| 763 |
+
e(
|
| 764 |
+
"div",
|
| 765 |
+
{ className: "cr-hero-inner container" },
|
| 766 |
+
e(
|
| 767 |
+
"div",
|
| 768 |
+
{ className: "row" },
|
| 769 |
+
e(
|
| 770 |
+
"div",
|
| 771 |
+
{ className: "col-12 col-lg-9 col-xl-8" },
|
| 772 |
+
e(
|
| 773 |
+
"span",
|
| 774 |
+
{ className: "cr-badge cr-reveal" },
|
| 775 |
+
e("span", { className: "dot" }),
|
| 776 |
+
" Giới thiệu · Vươn Tầm Thế Giới"
|
| 777 |
+
),
|
| 778 |
+
e(
|
| 779 |
+
"h1",
|
| 780 |
+
{ className: "cr-title" },
|
| 781 |
+
heroLetters,
|
| 782 |
+
e(
|
| 783 |
+
"span",
|
| 784 |
+
{ className: "d-block" },
|
| 785 |
+
e("span", { className: "grad" }, "Vươn Tầm Thế Giới")
|
| 786 |
+
)
|
| 787 |
+
),
|
| 788 |
+
e(
|
| 789 |
+
"p",
|
| 790 |
+
{ className: "cr-sub cr-reveal" },
|
| 791 |
+
"Nơi hội tụ anime, manga, webtoon và light novel — một trang web, vô vàn thế giới."
|
| 792 |
+
),
|
| 793 |
+
e(
|
| 794 |
+
"div",
|
| 795 |
+
{ className: "cr-cta-row cr-reveal" },
|
| 796 |
+
e(
|
| 797 |
+
Button,
|
| 798 |
+
{ size: "lg", onClick: function () { openSfwWithLoginCheck(); } },
|
| 799 |
+
"Bắt đầu"
|
| 800 |
+
),
|
| 801 |
+
e(
|
| 802 |
+
Button,
|
| 803 |
+
{
|
| 804 |
+
size: "lg",
|
| 805 |
+
variant: "outline",
|
| 806 |
+
onClick: function () {
|
| 807 |
+
var el = document.getElementById("featureStage");
|
| 808 |
+
if (el) el.scrollIntoView({ behavior: "smooth" });
|
| 809 |
+
}
|
| 810 |
+
},
|
| 811 |
+
"Xem phim nổi bật"
|
| 812 |
+
)
|
| 813 |
+
)
|
| 814 |
+
)
|
| 815 |
+
)
|
| 816 |
+
),
|
| 817 |
+
e(
|
| 818 |
+
"div",
|
| 819 |
+
{ className: "cr-marquee" },
|
| 820 |
+
e(
|
| 821 |
+
"div",
|
| 822 |
+
{ className: "cr-marquee-track" },
|
| 823 |
+
MARQUEE.concat(MARQUEE).map(function (w, i) {
|
| 824 |
+
return e(
|
| 825 |
+
"span",
|
| 826 |
+
{ className: "cr-marquee-item", key: i },
|
| 827 |
+
w,
|
| 828 |
+
e("span", { className: "sep" })
|
| 829 |
+
);
|
| 830 |
+
})
|
| 831 |
+
)
|
| 832 |
+
)
|
| 833 |
+
);
|
| 834 |
+
}
|
| 835 |
+
|
| 836 |
+
function FeatureStage() {
|
| 837 |
+
var secRef = useRef(null);
|
| 838 |
+
useEffect(function () {
|
| 839 |
+
var boxes = secRef.current ? secRef.current.querySelectorAll(".cr-feature-box.fade-in") : [];
|
| 840 |
+
if (!("IntersectionObserver" in window)) {
|
| 841 |
+
Array.prototype.forEach.call(boxes, function (b) { b.classList.add("visible"); });
|
| 842 |
+
return;
|
| 843 |
+
}
|
| 844 |
+
var io = new IntersectionObserver(
|
| 845 |
+
function (entries) {
|
| 846 |
+
entries.forEach(function (en) {
|
| 847 |
+
if (en.isIntersecting) {
|
| 848 |
+
en.target.classList.add("visible");
|
| 849 |
+
io.unobserve(en.target);
|
| 850 |
+
}
|
| 851 |
+
});
|
| 852 |
+
},
|
| 853 |
+
{ threshold: 0.15 }
|
| 854 |
+
);
|
| 855 |
+
Array.prototype.forEach.call(boxes, function (b) { io.observe(b); });
|
| 856 |
+
return function () { io.disconnect(); };
|
| 857 |
+
}, []);
|
| 858 |
+
|
| 859 |
+
return e(
|
| 860 |
+
"section",
|
| 861 |
+
{ id: "featureStage", className: "cr-features", ref: secRef },
|
| 862 |
+
e(
|
| 863 |
+
"div",
|
| 864 |
+
{ className: "container" },
|
| 865 |
+
e(
|
| 866 |
+
"div",
|
| 867 |
+
{ className: "cr-features-head" },
|
| 868 |
+
e("h2", null, "Phim nổi bật ", e("span", { className: "grad" }, "Feature")),
|
| 869 |
+
e(
|
| 870 |
+
"p",
|
| 871 |
+
null,
|
| 872 |
+
"Những tựa anime đáng xem nhất, cập nhật liên tục từ ANIVIETDB."
|
| 873 |
+
)
|
| 874 |
+
),
|
| 875 |
+
e(
|
| 876 |
+
"div",
|
| 877 |
+
{ className: "cr-feature-row" },
|
| 878 |
+
FEATURES.map(function (f, i) {
|
| 879 |
+
return e(
|
| 880 |
+
"a",
|
| 881 |
+
{
|
| 882 |
+
key: i,
|
| 883 |
+
href: f.page,
|
| 884 |
+
className: "cr-feature-box fade-in",
|
| 885 |
+
style: { transitionDelay: i * 0.08 + "s" }
|
| 886 |
+
},
|
| 887 |
+
e("img", { src: f.img, alt: f.name, loading: "lazy" }),
|
| 888 |
+
e("div", { className: "shade" }),
|
| 889 |
+
e(
|
| 890 |
+
"div",
|
| 891 |
+
{ className: "info" },
|
| 892 |
+
e("span", { className: "tag" }, f.tag),
|
| 893 |
+
e("p", { className: "t" }, f.name),
|
| 894 |
+
e("p", { className: "s" }, f.sub)
|
| 895 |
+
)
|
| 896 |
+
);
|
| 897 |
+
})
|
| 898 |
+
)
|
| 899 |
+
)
|
| 900 |
+
);
|
| 901 |
+
}
|
| 902 |
+
|
| 903 |
+
function openWithLoginCheck(url) {
|
| 904 |
+
function go() { window.location.href = url; }
|
| 905 |
+
function showPopup() {
|
| 906 |
+
var m = document.getElementById("loginReqPopup");
|
| 907 |
+
m.classList.add("active");
|
| 908 |
+
}
|
| 909 |
+
if (window.AuthSystem) {
|
| 910 |
+
window.AuthSystem.me().then(function (user) {
|
| 911 |
+
if (user) { go(); } else { showPopup(); }
|
| 912 |
+
}).catch(function () { showPopup(); });
|
| 913 |
+
return;
|
| 914 |
+
}
|
| 915 |
+
var s = document.createElement("script");
|
| 916 |
+
s.src = "signup-loginsystem.js";
|
| 917 |
+
s.onload = function () {
|
| 918 |
+
if (window.AuthSystem) {
|
| 919 |
+
window.AuthSystem.me().then(function (user) {
|
| 920 |
+
if (user) { go(); } else { showPopup(); }
|
| 921 |
+
}).catch(function () { showPopup(); });
|
| 922 |
+
} else {
|
| 923 |
+
showPopup();
|
| 924 |
+
}
|
| 925 |
+
};
|
| 926 |
+
s.onerror = function () { showPopup(); };
|
| 927 |
+
document.head.appendChild(s);
|
| 928 |
+
}
|
| 929 |
+
function openShopWithLoginCheck() { openWithLoginCheck("shop/index.html"); }
|
| 930 |
+
function openSfwWithLoginCheck() {
|
| 931 |
+
function go() { window.location.href = "sfw/index.html"; }
|
| 932 |
+
function goLogin() { window.location.href = "login.html"; }
|
| 933 |
+
function check() {
|
| 934 |
+
window.AuthSystem.me().then(function (user) {
|
| 935 |
+
if (user) { go(); } else { goLogin(); }
|
| 936 |
+
}).catch(function () { goLogin(); });
|
| 937 |
+
}
|
| 938 |
+
if (window.AuthSystem) { check(); return; }
|
| 939 |
+
var s = document.createElement("script");
|
| 940 |
+
s.src = "signup-loginsystem.js";
|
| 941 |
+
s.onload = function () { if (window.AuthSystem) { check(); } else { goLogin(); } };
|
| 942 |
+
s.onerror = function () { goLogin(); };
|
| 943 |
+
document.head.appendChild(s);
|
| 944 |
+
}
|
| 945 |
+
|
| 946 |
+
function ShopStage() {
|
| 947 |
+
var secRef = useRef(null);
|
| 948 |
+
useEffect(function () {
|
| 949 |
+
var cards = secRef.current ? secRef.current.querySelectorAll(".cr-shop-card.fade-in") : [];
|
| 950 |
+
if (!("IntersectionObserver" in window)) {
|
| 951 |
+
Array.prototype.forEach.call(cards, function (b) { b.classList.add("visible"); });
|
| 952 |
+
return;
|
| 953 |
+
}
|
| 954 |
+
var io = new IntersectionObserver(
|
| 955 |
+
function (entries) {
|
| 956 |
+
entries.forEach(function (en) {
|
| 957 |
+
if (en.isIntersecting) {
|
| 958 |
+
en.target.classList.add("visible");
|
| 959 |
+
io.unobserve(en.target);
|
| 960 |
+
}
|
| 961 |
+
});
|
| 962 |
+
},
|
| 963 |
+
{ threshold: 0.15 }
|
| 964 |
+
);
|
| 965 |
+
Array.prototype.forEach.call(cards, function (b) { io.observe(b); });
|
| 966 |
+
return function () { io.disconnect(); };
|
| 967 |
+
}, []);
|
| 968 |
+
|
| 969 |
+
return e(
|
| 970 |
+
"section",
|
| 971 |
+
{ id: "shopStage", className: "cr-shop", ref: secRef },
|
| 972 |
+
e(
|
| 973 |
+
"div",
|
| 974 |
+
{ className: "container" },
|
| 975 |
+
e(
|
| 976 |
+
"div",
|
| 977 |
+
{ className: "cr-shop-head" },
|
| 978 |
+
e("h2", null, "Figure Shop ", e("span", { className: "grad" }, "ANIVIETDB")),
|
| 979 |
+
e(
|
| 980 |
+
"p",
|
| 981 |
+
null,
|
| 982 |
+
"Figure, plushie, manga, Blu-ray và đồ lưu niệm sưu tầm — khám phá ngay, đặt mua tại cửa hàng."
|
| 983 |
+
)
|
| 984 |
+
),
|
| 985 |
+
e(
|
| 986 |
+
"div",
|
| 987 |
+
{ className: "cr-shop-row" },
|
| 988 |
+
SHOP_PRODUCTS.map(function (f, i) {
|
| 989 |
+
return e(
|
| 990 |
+
"a",
|
| 991 |
+
{
|
| 992 |
+
key: i,
|
| 993 |
+
href: "shop/index.html",
|
| 994 |
+
className: "cr-shop-card fade-in",
|
| 995 |
+
style: { transitionDelay: i * 0.08 + "s" }
|
| 996 |
+
},
|
| 997 |
+
e(
|
| 998 |
+
"div",
|
| 999 |
+
{ className: "pic" },
|
| 1000 |
+
e("span", { className: "cat" }, f.cat),
|
| 1001 |
+
e("img", { src: f.img, alt: f.name, loading: "lazy" }),
|
| 1002 |
+
e("span", { className: "price" }, f.price)
|
| 1003 |
+
),
|
| 1004 |
+
e(
|
| 1005 |
+
"div",
|
| 1006 |
+
{ className: "meta" },
|
| 1007 |
+
e("p", { className: "t" }, f.name),
|
| 1008 |
+
e("p", { className: "s" }, f.anime)
|
| 1009 |
+
)
|
| 1010 |
+
);
|
| 1011 |
+
})
|
| 1012 |
+
),
|
| 1013 |
+
e(
|
| 1014 |
+
"div",
|
| 1015 |
+
{ className: "cr-shop-cta" },
|
| 1016 |
+
e(
|
| 1017 |
+
Button,
|
| 1018 |
+
{
|
| 1019 |
+
size: "lg",
|
| 1020 |
+
onClick: function () { openShopWithLoginCheck(); }
|
| 1021 |
+
},
|
| 1022 |
+
"Khám phá shop"
|
| 1023 |
+
)
|
| 1024 |
+
)
|
| 1025 |
+
)
|
| 1026 |
+
);
|
| 1027 |
+
}
|
| 1028 |
+
|
| 1029 |
+
function Footer() {
|
| 1030 |
+
return e(
|
| 1031 |
+
"footer",
|
| 1032 |
+
{ className: "cr-footer" },
|
| 1033 |
+
e(
|
| 1034 |
+
"p",
|
| 1035 |
+
{ className: "brand" },
|
| 1036 |
+
"ANIVIETDB ",
|
| 1037 |
+
e("span", null, "·"),
|
| 1038 |
+
" Vươn Tầm Thế Giới"
|
| 1039 |
+
),
|
| 1040 |
+
e(
|
| 1041 |
+
"p",
|
| 1042 |
+
null,
|
| 1043 |
+
"© 2026 ANIVIETDB. Tất cả nội dung Anime, Manga, Manhwa, Manhua và Light Novel được sưu tầm và thuộc bản quyền của chủ sở hữu tương ứng."
|
| 1044 |
+
),
|
| 1045 |
+
e(
|
| 1046 |
+
"p",
|
| 1047 |
+
null,
|
| 1048 |
+
"Trang web sưu tầm cá nhân - Phi thương mại. Mọi quyền được bảo lưu."
|
| 1049 |
+
)
|
| 1050 |
+
);
|
| 1051 |
+
}
|
| 1052 |
+
|
| 1053 |
+
function App() {
|
| 1054 |
+
return e(
|
| 1055 |
+
"main",
|
| 1056 |
+
{ className: "position-relative" },
|
| 1057 |
+
e(ThreeBackground),
|
| 1058 |
+
e(Hero),
|
| 1059 |
+
e(FeatureStage),
|
| 1060 |
+
e(ShopStage),
|
| 1061 |
+
e(Footer)
|
| 1062 |
+
);
|
| 1063 |
+
}
|
| 1064 |
+
|
| 1065 |
+
ReactDOM.createRoot(document.getElementById("root")).render(e(App));
|
| 1066 |
+
})();
|
| 1067 |
+
</script>
|
| 1068 |
+
|
| 1069 |
+
<div class="login-req-overlay" id="loginReqPopup">
|
| 1070 |
+
<div class="login-req-popup">
|
| 1071 |
+
<h3>🔒 Bạn chưa đăng nhập</h3>
|
| 1072 |
+
<p>Hãy đăng nhập để khám phá cửa hàng ANIVIETDB và đặt mua sản phẩm.</p>
|
| 1073 |
+
<div class="login-req-actions">
|
| 1074 |
+
<button class="login-req-btn" id="loginReqGo">Bắt đầu đăng nhập</button>
|
| 1075 |
+
<button class="login-req-btn login-req-ghost" id="loginReqClose">Để sau</button>
|
| 1076 |
+
</div>
|
| 1077 |
+
</div>
|
| 1078 |
+
</div>
|
| 1079 |
+
|
| 1080 |
+
<style>
|
| 1081 |
+
.login-req-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.7); display: none; align-items: center; justify-content: center; z-index: 10000; backdrop-filter: blur(4px); }
|
| 1082 |
+
.login-req-overlay.active { display: flex; }
|
| 1083 |
+
.login-req-popup { background: #1d1d1d; border: 1px solid var(--cr-border); border-radius: 16px; padding: 30px 36px; text-align: center; color: var(--cr-ink); font-family: "Inter", system-ui, sans-serif; max-width: 380px; box-shadow: 0 20px 60px rgba(0,0,0,.6); }
|
| 1084 |
+
.login-req-popup h3 { margin: 0 0 10px; font-size: 20px; font-weight: 700; }
|
| 1085 |
+
.login-req-popup p { margin: 0 0 22px; color: var(--cr-muted); font-size: 14px; line-height: 1.6; }
|
| 1086 |
+
.login-req-actions { display: flex; flex-direction: column; gap: 10px; }
|
| 1087 |
+
.login-req-btn { background: var(--cr-orange); color: #fff; border: none; padding: 12px 20px; border-radius: 10px; font-weight: 600; font-size: 15px; cursor: pointer; transition: background .15s; }
|
| 1088 |
+
.login-req-btn:hover { background: var(--cr-orange-hover); }
|
| 1089 |
+
.login-req-btn.login-req-ghost { background: none; color: var(--cr-muted); border: 1px solid var(--cr-border); }
|
| 1090 |
+
.login-req-btn.login-req-ghost:hover { color: #fff; border-color: #555; }
|
| 1091 |
+
</style>
|
| 1092 |
+
|
| 1093 |
+
<script>
|
| 1094 |
+
(function () {
|
| 1095 |
+
var overlay = document.getElementById("loginReqPopup");
|
| 1096 |
+
var goBtn = document.getElementById("loginReqGo");
|
| 1097 |
+
var closeBtn = document.getElementById("loginReqClose");
|
| 1098 |
+
if (goBtn) goBtn.addEventListener("click", function () {
|
| 1099 |
+
window.location.href = "login.html";
|
| 1100 |
+
});
|
| 1101 |
+
if (closeBtn) closeBtn.addEventListener("click", function () {
|
| 1102 |
+
overlay.classList.remove("active");
|
| 1103 |
+
});
|
| 1104 |
+
if (overlay) overlay.addEventListener("click", function (e) {
|
| 1105 |
+
if (e.target === overlay) overlay.classList.remove("active");
|
| 1106 |
+
});
|
| 1107 |
+
})();
|
| 1108 |
+
</script>
|
| 1109 |
+
</body>
|
| 1110 |
+
</html>
|
login.html
ADDED
|
@@ -0,0 +1,462 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="vi">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
| 7 |
+
<meta http-equiv="Pragma" content="no-cache">
|
| 8 |
+
<meta http-equiv="Expires" content="0">
|
| 9 |
+
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><rect width='100' height='100' rx='22' fill='%23151515'/><text x='50' y='68' font-size='52' font-family='Arial' font-weight='bold' fill='%23ff5e00' text-anchor='middle'>A</text></svg>">
|
| 10 |
+
<title>Đăng nhập - ANIVIETDB</title>
|
| 11 |
+
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">
|
| 12 |
+
<style>
|
| 13 |
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
| 14 |
+
body {
|
| 15 |
+
font-family: 'Roboto', sans-serif;
|
| 16 |
+
background: linear-gradient(135deg, #0b0d16 0%, #1a1033 50%, #2d1b4e 100%);
|
| 17 |
+
min-height: 100vh;
|
| 18 |
+
display: flex; align-items: center; justify-content: center;
|
| 19 |
+
padding: 20px; color: #e8e8f0;
|
| 20 |
+
}
|
| 21 |
+
body::before {
|
| 22 |
+
content: "";
|
| 23 |
+
position: fixed;
|
| 24 |
+
inset: 0;
|
| 25 |
+
z-index: -2;
|
| 26 |
+
pointer-events: none;
|
| 27 |
+
background-color: #151515;
|
| 28 |
+
background-image:
|
| 29 |
+
url("https://phim.nguonc.com/public/images/Film/76.jpg"),
|
| 30 |
+
url("https://phim.nguonc.com/public/images/Film/666.jpg"),
|
| 31 |
+
url("https://phim.nguonc.com/public/images/Film/77.jpg"),
|
| 32 |
+
url("https://phim.nguonc.com/public/images/Film/667.jpg"),
|
| 33 |
+
url("https://phim.nguonc.com/public/images/Film/259.jpg"),
|
| 34 |
+
url("https://phim.nguonc.com/public/images/Film/2738.jpg"),
|
| 35 |
+
url("https://phim.nguonc.com/public/images/Film/3485.jpg"),
|
| 36 |
+
url("https://phim.nguonc.com/public/images/Film/141438.jpg"),
|
| 37 |
+
url("https://phim.nguonc.com/public/images/Film/date-a-bullet-thumb.jpg"),
|
| 38 |
+
url("https://phim.nguonc.com/public/images/Film/3103.jpg"),
|
| 39 |
+
url("https://phim.nguonc.com/public/images/Post/5/aishiteru-game-wo-owarasetai.jpg"),
|
| 40 |
+
url("https://phim.nguonc.com/public/images/Film/541.jpg"),
|
| 41 |
+
url("https://phim.nguonc.com/public/images/Film/536.jpg"),
|
| 42 |
+
url("https://phim.nguonc.com/public/images/Film/56167.jpg"),
|
| 43 |
+
url("https://phim.nguonc.com/public/images/Film/56643.jpg"),
|
| 44 |
+
url("https://phim.nguonc.com/public/images/Film/538.jpg"),
|
| 45 |
+
url("https://phim.nguonc.com/public/images/Film/540.jpg"),
|
| 46 |
+
url("https://phim.nguonc.com/public/images/Film/60351.jpg"),
|
| 47 |
+
url("https://phim.nguonc.com/public/images/Film/2607.jpg"),
|
| 48 |
+
url("https://phim.nguonc.com/public/images/Film/2661.jpg"),
|
| 49 |
+
url("https://phim.nguonc.com/public/images/Film/5MgMCnslrxQfOfaq6T5ndkLlcCk.jpg"),
|
| 50 |
+
url("https://phim.nguonc.com/public/images/Film/134808.jpg"),
|
| 51 |
+
url("https://phim.nguonc.com/public/images/Film/138379.jpg"),
|
| 52 |
+
url("https://phim.nguonc.com/public/images/Post/9/seishun-buta-yarou-wa-santa-claus-no-yume-wo-minai-1.jpg");
|
| 53 |
+
background-size: 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%;
|
| 54 |
+
background-position: 0% 0%, 14.2857% 0%, 28.5714% 0%, 42.8571% 0%, 57.1429% 0%, 71.4286% 0%, 85.7143% 0%, 100% 0%, 0% 50%, 14.2857% 50%, 28.5714% 50%, 42.8571% 50%, 57.1429% 50%, 71.4286% 50%, 85.7143% 50%, 100% 50%, 0% 100%, 14.2857% 100%, 28.5714% 100%, 42.8571% 100%, 57.1429% 100%, 71.4286% 100%, 85.7143% 100%, 100% 100%;
|
| 55 |
+
background-repeat: no-repeat;
|
| 56 |
+
filter: brightness(0.42) saturate(0.65);
|
| 57 |
+
}
|
| 58 |
+
body::after {
|
| 59 |
+
content: "";
|
| 60 |
+
position: fixed;
|
| 61 |
+
inset: 0;
|
| 62 |
+
z-index: -1;
|
| 63 |
+
pointer-events: none;
|
| 64 |
+
background: linear-gradient(180deg, rgba(21,21,21,0.55), rgba(21,21,21,0.78) 68%, #151515 100%),
|
| 65 |
+
radial-gradient(120% 90% at 50% 0%, rgba(21,21,21,0) 42%, rgba(21,21,21,0.5) 100%);
|
| 66 |
+
}
|
| 67 |
+
.auth-card {
|
| 68 |
+
width: 100%; max-width: 400px;
|
| 69 |
+
background: rgba(20, 22, 38, 0.92);
|
| 70 |
+
border: 1px solid rgba(120, 100, 230, 0.35);
|
| 71 |
+
border-radius: 16px;
|
| 72 |
+
padding: 32px 28px;
|
| 73 |
+
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.55);
|
| 74 |
+
}
|
| 75 |
+
.auth-logo {
|
| 76 |
+
text-align: center; font-size: 22px; font-weight: 700; letter-spacing: 0.5px;
|
| 77 |
+
margin-bottom: 6px;
|
| 78 |
+
}
|
| 79 |
+
.auth-logo span { color: #8b7bff; }
|
| 80 |
+
.auth-sub { text-align: center; color: #9a9ab0; font-size: 13px; margin-bottom: 22px; }
|
| 81 |
+
.auth-tabs { display: flex; margin-bottom: 22px; border-bottom: 1px solid rgba(120,100,230,0.25); }
|
| 82 |
+
.auth-tab {
|
| 83 |
+
flex: 1; text-align: center; padding: 10px 0; cursor: pointer;
|
| 84 |
+
color: #9a9ab0; font-weight: 500; border-bottom: 2px solid transparent; transition: all .2s;
|
| 85 |
+
}
|
| 86 |
+
.auth-tab.active { color: #fff; border-bottom-color: #8b7bff; }
|
| 87 |
+
.field { margin-bottom: 16px; }
|
| 88 |
+
.field label { display: block; font-size: 12px; color: #a9a9c4; margin-bottom: 6px; }
|
| 89 |
+
.field input {
|
| 90 |
+
width: 100%; padding: 12px 14px; border-radius: 10px;
|
| 91 |
+
border: 1px solid rgba(120,100,230,0.3); background: #10121f;
|
| 92 |
+
color: #fff; font-size: 14px; outline: none; transition: border .2s;
|
| 93 |
+
}
|
| 94 |
+
.field input:focus { border-color: #8b7bff; }
|
| 95 |
+
.auth-btn {
|
| 96 |
+
width: 100%; padding: 13px; border: none; border-radius: 10px;
|
| 97 |
+
background: linear-gradient(135deg, #7c5cff, #5a3bd6);
|
| 98 |
+
color: #fff; font-size: 15px; font-weight: 700; cursor: pointer;
|
| 99 |
+
transition: transform .15s, filter .15s;
|
| 100 |
+
}
|
| 101 |
+
.auth-btn:hover { filter: brightness(1.1); transform: translateY(-1px); }
|
| 102 |
+
.auth-btn:disabled { opacity: 0.6; cursor: not-allowed; }
|
| 103 |
+
.auth-error {
|
| 104 |
+
min-height: 20px; color: #ff8a8a; font-size: 13px; margin-top: 12px; text-align: center;
|
| 105 |
+
}
|
| 106 |
+
.auth-ok {
|
| 107 |
+
color: #7ee787; font-size: 13px; text-align: center; margin-top: 12px; min-height: 20px;
|
| 108 |
+
}
|
| 109 |
+
.logout-row { display: flex; align-items: center; justify-content: space-between; margin-top: 18px; } .logged-in { text-align: center; padding: 10px 0; }
|
| 110 |
+
.logged-in .name { font-size: 18px; font-weight: 700; margin-bottom: 4px; }
|
| 111 |
+
.logged-in .email { font-size: 13px; color: #9a9ab0; margin-bottom: 18px; }
|
| 112 |
+
.logged-title {
|
| 113 |
+
text-align: center; font-size: 13px; color: #7ee787; font-weight: 500; margin-bottom: 18px;
|
| 114 |
+
}
|
| 115 |
+
.btn-ghost {
|
| 116 |
+
width: 100%; padding: 12px; border-radius: 10px;
|
| 117 |
+
border: 1px solid rgba(255,138,138,0.4); background: transparent;
|
| 118 |
+
color: #ff8a8a; font-size: 14px; font-weight: 600; cursor: pointer;
|
| 119 |
+
}
|
| 120 |
+
.btn-ghost:hover { background: rgba(255,138,138,0.08); }
|
| 121 |
+
.btn-admin {
|
| 122 |
+
display: block; width: 100%; padding: 12px; border-radius: 10px;
|
| 123 |
+
border: 1px solid rgba(126,231,135,0.45); background: rgba(126,231,135,0.08);
|
| 124 |
+
color: #7ee787; font-size: 14px; font-weight: 700; cursor: pointer;
|
| 125 |
+
text-align: center; text-decoration: none;
|
| 126 |
+
}
|
| 127 |
+
.btn-admin:hover { background: rgba(126,231,135,0.18); }
|
| 128 |
+
.btn-home {
|
| 129 |
+
display: block; width: 100%; padding: 12px; border-radius: 10px;
|
| 130 |
+
border: 1px solid rgba(139,123,255,0.45); background: rgba(139,123,255,0.1);
|
| 131 |
+
color: #b7abff; font-size: 14px; font-weight: 700; cursor: pointer;
|
| 132 |
+
text-align: center; text-decoration: none; margin-bottom: 10px;
|
| 133 |
+
}
|
| 134 |
+
.btn-home:hover { background: rgba(139,123,255,0.2); }
|
| 135 |
+
.btn-shop {
|
| 136 |
+
display: block; width: 100%; padding: 12px; border-radius: 10px;
|
| 137 |
+
border: 1px solid rgba(255,94,0,0.5); background: rgba(255,94,0,0.1);
|
| 138 |
+
color: #ff9a5a; font-size: 14px; font-weight: 700; cursor: pointer;
|
| 139 |
+
text-align: center; text-decoration: none; margin-bottom: 10px;
|
| 140 |
+
}
|
| 141 |
+
.btn-shop:hover { background: rgba(255,94,0,0.2); }
|
| 142 |
+
.auth-back {
|
| 143 |
+
display: block; text-align: center; margin-top: 18px;
|
| 144 |
+
color: #9a9ab0; font-size: 13px; text-decoration: none;
|
| 145 |
+
}
|
| 146 |
+
.auth-back:hover { color: #fff; text-decoration: underline; }
|
| 147 |
+
.auth-copyright {
|
| 148 |
+
text-align: center; margin-top: 20px; padding-top: 14px;
|
| 149 |
+
border-top: 1px solid rgba(120,100,230,0.15);
|
| 150 |
+
color: #6d6d85; font-size: 12px;
|
| 151 |
+
}
|
| 152 |
+
.terms-row {
|
| 153 |
+
display: flex; align-items: flex-start; gap: 8px;
|
| 154 |
+
font-size: 12px; color: #a9a9c4; line-height: 1.5; margin-bottom: 16px;
|
| 155 |
+
}
|
| 156 |
+
.terms-row input { margin-top: 2px; accent-color: #8b7bff; }
|
| 157 |
+
.turnstile-wrap { display: flex; justify-content: center; margin-bottom: 16px; }
|
| 158 |
+
.terms-row a { color: #8b7bff; text-decoration: none; }
|
| 159 |
+
.terms-row a:hover { text-decoration: underline; }
|
| 160 |
+
.terms-modal {
|
| 161 |
+
position: fixed; inset: 0; z-index: 1000;
|
| 162 |
+
display: none; align-items: center; justify-content: center;
|
| 163 |
+
background: rgba(0,0,0,0.6); padding: 20px;
|
| 164 |
+
}
|
| 165 |
+
.terms-modal.open { display: flex; }
|
| 166 |
+
.terms-modal-card {
|
| 167 |
+
width: 100%; max-width: 520px; max-height: 80vh;
|
| 168 |
+
background: #141622; border: 1px solid rgba(120,100,230,0.35);
|
| 169 |
+
border-radius: 14px; display: flex; flex-direction: column; overflow: hidden;
|
| 170 |
+
}
|
| 171 |
+
.terms-modal-head {
|
| 172 |
+
display: flex; align-items: center; justify-content: space-between;
|
| 173 |
+
padding: 14px 18px; border-bottom: 1px solid rgba(120,100,230,0.25);
|
| 174 |
+
}
|
| 175 |
+
.terms-modal-head h3 { font-size: 16px; color: #fff; }
|
| 176 |
+
.terms-modal-close {
|
| 177 |
+
background: none; border: none; color: #9a9ab0; font-size: 20px;
|
| 178 |
+
cursor: pointer; line-height: 1;
|
| 179 |
+
}
|
| 180 |
+
.terms-modal-close:hover { color: #fff; }
|
| 181 |
+
.terms-modal-body { padding: 16px 18px; overflow-y: auto; font-size: 13px; line-height: 1.7; color: #c6c6da; }
|
| 182 |
+
.terms-modal-body h4 { color: #fff; font-size: 14px; margin: 14px 0 6px; }
|
| 183 |
+
.terms-modal-body h4:first-child { margin-top: 0; }
|
| 184 |
+
.terms-modal-foot {
|
| 185 |
+
padding: 12px 18px; border-top: 1px solid rgba(120,100,230,0.25); text-align: right;
|
| 186 |
+
}
|
| 187 |
+
.terms-modal-foot button {
|
| 188 |
+
padding: 9px 18px; border: none; border-radius: 8px;
|
| 189 |
+
background: linear-gradient(135deg, #7c5cff, #5a3bd6); color: #fff;
|
| 190 |
+
font-size: 13px; font-weight: 700; cursor: pointer;
|
| 191 |
+
}
|
| 192 |
+
</style>
|
| 193 |
+
</head>
|
| 194 |
+
<body>
|
| 195 |
+
<div class="auth-card">
|
| 196 |
+
<div class="auth-logo">ANIVIET<span>DB</span></div>
|
| 197 |
+
<p class="auth-sub">Đăng nhập hoặc tạo tài khoản</p>
|
| 198 |
+
|
| 199 |
+
<div class="auth-tabs" id="authTabs">
|
| 200 |
+
<div class="auth-tab active" data-mode="login">Đăng nhập</div>
|
| 201 |
+
<div class="auth-tab" data-mode="register">Đăng ký</div>
|
| 202 |
+
</div>
|
| 203 |
+
|
| 204 |
+
<div id="authForms">
|
| 205 |
+
<div class="field">
|
| 206 |
+
<label id="loginLabel">Email hoặc tên đăng nhập</label>
|
| 207 |
+
<input type="text" id="email" placeholder="Email hoặc tên đăng nhập" autocomplete="username">
|
| 208 |
+
</div>
|
| 209 |
+
<div class="field register-only" style="display:none;">
|
| 210 |
+
<label>Tên đăng nhập</label>
|
| 211 |
+
<input type="text" id="name" placeholder="Tên của bạn" autocomplete="name">
|
| 212 |
+
</div>
|
| 213 |
+
<div class="field">
|
| 214 |
+
<label>Mật khẩu</label>
|
| 215 |
+
<input type="password" id="password" placeholder="••••••••" autocomplete="current-password">
|
| 216 |
+
</div>
|
| 217 |
+
<div class="field register-only" style="display:none;">
|
| 218 |
+
<label>Nhập lại mật khẩu</label>
|
| 219 |
+
<input type="password" id="password2" placeholder="••••••••" autocomplete="new-password">
|
| 220 |
+
</div>
|
| 221 |
+
<label class="terms-row register-only" style="display:none;">
|
| 222 |
+
<input type="checkbox" id="termsCheck">
|
| 223 |
+
<span>Tôi đã đọc và đồng ý với <a href="javascript:void(0)" id="termsLink">Điều khoản sử dụng</a></span>
|
| 224 |
+
</label>
|
| 225 |
+
<div class="turnstile-wrap">
|
| 226 |
+
<div id="turnstile-login" class="cf-turnstile login-only"
|
| 227 |
+
data-sitekey="0x4AAAAAAEQz9I-TXTQoVuii"
|
| 228 |
+
data-action="login"
|
| 229 |
+
data-callback="onTurnstileSuccess"
|
| 230 |
+
data-expired-callback="onTurnstileExpired"
|
| 231 |
+
data-error-callback="onTurnstileError"></div>
|
| 232 |
+
<div id="turnstile-register" class="cf-turnstile register-only" style="display:none;"
|
| 233 |
+
data-sitekey="0x4AAAAAAEQz9I-TXTQoVuii"
|
| 234 |
+
data-action="register"
|
| 235 |
+
data-callback="onTurnstileSuccess"
|
| 236 |
+
data-expired-callback="onTurnstileExpired"
|
| 237 |
+
data-error-callback="onTurnstileError"></div>
|
| 238 |
+
</div>
|
| 239 |
+
<button class="auth-btn" id="submitBtn">Đăng nhập</button>
|
| 240 |
+
</div>
|
| 241 |
+
|
| 242 |
+
<div id="loggedIn" style="display:none;">
|
| 243 |
+
<div class="logged-in">
|
| 244 |
+
<div class="name" id="userName"></div>
|
| 245 |
+
<div class="email" id="userEmail"></div>
|
| 246 |
+
<div class="logged-title">Bạn đã đăng nhập</div>
|
| 247 |
+
</div>
|
| 248 |
+
<div style="display:none; margin-bottom:10px;" id="adminPanelWrap">
|
| 249 |
+
<a href="control-panel.html" class="btn-admin">Control Panel</a>
|
| 250 |
+
</div>
|
| 251 |
+
<a href="sfw/index.html" class="btn-home">Trang chủ</a>
|
| 252 |
+
<a href="shop/index.html" class="btn-shop">🛒 Cửa hàng</a>
|
| 253 |
+
<button class="btn-ghost" id="logoutBtn">Đăng xuất</button>
|
| 254 |
+
</div>
|
| 255 |
+
|
| 256 |
+
<div class="auth-error" id="error"></div>
|
| 257 |
+
<div class="auth-ok" id="ok"></div>
|
| 258 |
+
<a href="index.html" class="auth-back" id="authBack">← Về trang chủ</a>
|
| 259 |
+
<div class="auth-copyright">© 2026 ANIVIETDB. All rights reserved.</div>
|
| 260 |
+
</div>
|
| 261 |
+
|
| 262 |
+
<div class="terms-modal" id="termsModal">
|
| 263 |
+
<div class="terms-modal-card">
|
| 264 |
+
<div class="terms-modal-head">
|
| 265 |
+
<h3>Điều khoản sử dụng</h3>
|
| 266 |
+
<button class="terms-modal-close" id="termsClose" aria-label="Đóng">×</button>
|
| 267 |
+
</div>
|
| 268 |
+
<div class="terms-modal-body">
|
| 269 |
+
<h4>1. Chấp nhận điều khoản</h4>
|
| 270 |
+
<p>Bằng việc truy cập và sử dụng ANIVIETDB, bạn xác nhận đã đọc, hiểu và đồng ý tuân thủ các điều khoản dưới đây. Nếu không đồng ý, vui lòng ngừng sử dụng dịch vụ.</p>
|
| 271 |
+
<h4>2. Mục đích sử dụng</h4>
|
| 272 |
+
<p>ANIVIETDB là nền tảng tổng hợp thông tin anime, manga, manhwa, manhua và light novel phục vụ nhu cầu tìm kiếm, theo dõi và giải trí cá nhân của người dùng. Không được dùng thông tin tại đây cho mục đích thương mại hoặc trái pháp luật.</p>
|
| 273 |
+
<h4>3. Tài khoản người dùng</h4>
|
| 274 |
+
<p>Bạn chịu trách nhiệm bảo mật mật khẩu và mọi hoạt động diễn ra trên tài khoản của mình. Không được tạo tài khoản giả mạo, mạo danh người khác, hoặc lợi dụng tài khoản để gây ảnh hưởng tiêu cực tới cộng đồng.</p>
|
| 275 |
+
<h4>4. Nội dung và bản quyền</h4>
|
| 276 |
+
<p>Toàn bộ thông tin trên ANIVIETDB chỉ mang tính tham khảo. Quyền sở hữu trí tuệ của các tác phẩm thuộc về tác giả và đơn vị phát hành. Nếu bạn là chủ sở hữu và muốn yêu cầu gỡ nội dung, vui lòng liên hệ ban quản trị.</p>
|
| 277 |
+
<h4>5. Hành vi bị cấm</h4>
|
| 278 |
+
<p>Cấm spam, phát tán thông tin sai lệch, phần mềm độc hại, hoặc thực hiện hành vi xâm phạm hệ thống và tài khoản người dùng khác trên nền tảng.</p>
|
| 279 |
+
<h4>6. Từ chối trách nhiệm</h4>
|
| 280 |
+
<p>ANIVIETDB nỗ lực cập nhật thông tin chính xác nhưng không đảm bảo tính đầy đủ, kịp thời của dữ liệu. Website không chịu trách nhiệm về bất kỳ thiệt hại phát sinh từ việc sử dụng dịch vụ.</p>
|
| 281 |
+
<h4>7. Thay đổi điều khoản</h4>
|
| 282 |
+
<p>ANIVIETDB có quyền cập nhật điều khoản bất cứ lúc nào. Việc tiếp tục sử dụng website sau khi có thay đổi đồng nghĩa với việc bạn chấp nhận điều khoản mới.</p>
|
| 283 |
+
</div>
|
| 284 |
+
<div class="terms-modal-foot">
|
| 285 |
+
<button id="termsAgree">Tôi đồng ý</button>
|
| 286 |
+
</div>
|
| 287 |
+
</div>
|
| 288 |
+
</div>
|
| 289 |
+
|
| 290 |
+
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
|
| 291 |
+
<script src="signup-loginsystem.js?v=8"></script>
|
| 292 |
+
<script>
|
| 293 |
+
var mode = 'login';
|
| 294 |
+
var emailEl = document.getElementById('email');
|
| 295 |
+
var nameEl = document.getElementById('name');
|
| 296 |
+
var passEl = document.getElementById('password');
|
| 297 |
+
var pass2El = document.getElementById('password2');
|
| 298 |
+
var submitBtn = document.getElementById('submitBtn');
|
| 299 |
+
var errorEl = document.getElementById('error');
|
| 300 |
+
var okEl = document.getElementById('ok');
|
| 301 |
+
var authForms = document.getElementById('authForms');
|
| 302 |
+
var loggedIn = document.getElementById('loggedIn');
|
| 303 |
+
|
| 304 |
+
var cooldownTimer = null;
|
| 305 |
+
function updateCooldown() {
|
| 306 |
+
var wait = window.AuthSystem.cooldownRemaining();
|
| 307 |
+
if (wait <= 0) {
|
| 308 |
+
if (cooldownTimer) { clearInterval(cooldownTimer); cooldownTimer = null; }
|
| 309 |
+
errorEl.textContent = '';
|
| 310 |
+
submitBtn.disabled = false;
|
| 311 |
+
return;
|
| 312 |
+
}
|
| 313 |
+
errorEl.textContent = 'Vui lòng đợi ' + Math.ceil(wait / 1000) + ' giây trước khi đăng nhập hoặc tạo tài khoản.';
|
| 314 |
+
submitBtn.disabled = true;
|
| 315 |
+
}
|
| 316 |
+
function startCooldown() {
|
| 317 |
+
if (cooldownTimer) { clearInterval(cooldownTimer); }
|
| 318 |
+
cooldownTimer = setInterval(updateCooldown, 200);
|
| 319 |
+
updateCooldown();
|
| 320 |
+
}
|
| 321 |
+
|
| 322 |
+
function showSession(user) {
|
| 323 |
+
authForms.style.display = 'none';
|
| 324 |
+
document.getElementById('authTabs').style.display = 'none';
|
| 325 |
+
document.querySelector('.auth-sub').style.display = 'none';
|
| 326 |
+
document.getElementById('authBack').style.display = 'none';
|
| 327 |
+
loggedIn.style.display = 'block';
|
| 328 |
+
document.getElementById('userName').textContent = user.username || user.name || '';
|
| 329 |
+
document.getElementById('userEmail').textContent = user.email || '';
|
| 330 |
+
errorEl.textContent = '';
|
| 331 |
+
okEl.textContent = 'Đã đăng nhập.';
|
| 332 |
+
var isAdmin = user.role === 'admin';
|
| 333 |
+
document.getElementById('adminPanelWrap').style.display = isAdmin ? 'block' : 'none';
|
| 334 |
+
}
|
| 335 |
+
|
| 336 |
+
function resetWidget() {
|
| 337 |
+
var el = activeWidget();
|
| 338 |
+
if (el && window.turnstile && typeof window.turnstile.reset === 'function') {
|
| 339 |
+
try { window.turnstile.reset(el); } catch (e) { /* ignore */ }
|
| 340 |
+
}
|
| 341 |
+
submitBtn.disabled = true;
|
| 342 |
+
}
|
| 343 |
+
|
| 344 |
+
function showError(msg) {
|
| 345 |
+
okEl.textContent = '';
|
| 346 |
+
errorEl.textContent = msg;
|
| 347 |
+
if (window.turnstile && !turnstileSolved()) submitBtn.disabled = true;
|
| 348 |
+
else submitBtn.disabled = false;
|
| 349 |
+
}
|
| 350 |
+
|
| 351 |
+
function setMode(m) {
|
| 352 |
+
mode = m;
|
| 353 |
+
document.querySelectorAll('.auth-tab').forEach(function (t) {
|
| 354 |
+
t.classList.toggle('active', t.getAttribute('data-mode') === m);
|
| 355 |
+
});
|
| 356 |
+
document.querySelectorAll('.register-only').forEach(function (el) {
|
| 357 |
+
el.style.display = (m === 'register') ? '' : 'none';
|
| 358 |
+
});
|
| 359 |
+
document.querySelectorAll('.login-only').forEach(function (el) {
|
| 360 |
+
el.style.display = (m === 'login') ? '' : 'none';
|
| 361 |
+
});
|
| 362 |
+
submitBtn.textContent = (m === 'register') ? 'Tạo tài khoản' : 'Đăng nhập';
|
| 363 |
+
errorEl.textContent = '';
|
| 364 |
+
okEl.textContent = '';
|
| 365 |
+
if (window.turnstile && !turnstileSolved()) submitBtn.disabled = true;
|
| 366 |
+
}
|
| 367 |
+
|
| 368 |
+
function activeWidget() {
|
| 369 |
+
return mode === 'register'
|
| 370 |
+
? document.getElementById('turnstile-register')
|
| 371 |
+
: document.getElementById('turnstile-login');
|
| 372 |
+
}
|
| 373 |
+
|
| 374 |
+
function turnstileSolved() {
|
| 375 |
+
var el = activeWidget();
|
| 376 |
+
return !!(window.turnstile && el && window.turnstile.getResponse(el));
|
| 377 |
+
}
|
| 378 |
+
|
| 379 |
+
function onTurnstileSuccess() { submitBtn.disabled = false; }
|
| 380 |
+
function onTurnstileExpired() { submitBtn.disabled = true; }
|
| 381 |
+
function onTurnstileError() { submitBtn.disabled = true; }
|
| 382 |
+
|
| 383 |
+
var termsModal = document.getElementById('termsModal');
|
| 384 |
+
var termsCheck = document.getElementById('termsCheck');
|
| 385 |
+
document.getElementById('termsLink').addEventListener('click', function () {
|
| 386 |
+
termsModal.classList.add('open');
|
| 387 |
+
});
|
| 388 |
+
document.getElementById('termsClose').addEventListener('click', function () {
|
| 389 |
+
termsModal.classList.remove('open');
|
| 390 |
+
});
|
| 391 |
+
document.getElementById('termsAgree').addEventListener('click', function () {
|
| 392 |
+
termsCheck.checked = true;
|
| 393 |
+
termsModal.classList.remove('open');
|
| 394 |
+
});
|
| 395 |
+
termsModal.addEventListener('click', function (e) {
|
| 396 |
+
if (e.target === termsModal) termsModal.classList.remove('open');
|
| 397 |
+
});
|
| 398 |
+
|
| 399 |
+
document.querySelectorAll('.auth-tab').forEach(function (t) {
|
| 400 |
+
t.addEventListener('click', function () { setMode(t.getAttribute('data-mode')); });
|
| 401 |
+
});
|
| 402 |
+
|
| 403 |
+
function doSubmit() {
|
| 404 |
+
if (!window.AuthSystem || !window.AuthSystem.isConfigured()) {
|
| 405 |
+
showError('Backend chưa được cấu hình. Vui lòng dán URL API.');
|
| 406 |
+
return;
|
| 407 |
+
}
|
| 408 |
+
var wait = window.AuthSystem.cooldownRemaining();
|
| 409 |
+
if (wait > 0) {
|
| 410 |
+
showError('Vui lòng đợi ' + Math.ceil(wait / 1000) + ' giây trước khi đăng nhập hoặc tạo tài khoản.');
|
| 411 |
+
return;
|
| 412 |
+
}
|
| 413 |
+
var loginId = emailEl.value.trim();
|
| 414 |
+
var password = passEl.value;
|
| 415 |
+
if (!loginId || !password) { showError('Vui lòng nhập email/tên đăng nhập và mật khẩu.'); return; }
|
| 416 |
+
if (window.turnstile && !turnstileSolved()) {
|
| 417 |
+
showError('Vui lòng hoàn tất xác minh "Tôi không phải người máy".');
|
| 418 |
+
return;
|
| 419 |
+
}
|
| 420 |
+
|
| 421 |
+
if (mode === 'register') {
|
| 422 |
+
var name = nameEl.value.trim();
|
| 423 |
+
if (!name) { showError('Vui lòng nhập tên đăng nhập.'); return; }
|
| 424 |
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(loginId)) { showError('Email không hợp lệ.'); return; }
|
| 425 |
+
if (password.length < 6) { showError('Mật khẩu phải có ít nhất 6 ký tự.'); return; }
|
| 426 |
+
if (password !== pass2El.value) { showError('Mật khẩu nhập lại không khớp.'); return; }
|
| 427 |
+
if (!termsCheck.checked) { showError('Vui lòng đồng ý với Điều khoản sử dụng.'); return; }
|
| 428 |
+
submitBtn.disabled = true;
|
| 429 |
+
AuthSystem.register(loginId, name, password).then(function (data) {
|
| 430 |
+
showSession(data.user);
|
| 431 |
+
setTimeout(function () { window.location.href = 'sfw/'; }, 1200);
|
| 432 |
+
}).catch(function (e) { resetWidget(); showError(e.message); });
|
| 433 |
+
} else {
|
| 434 |
+
submitBtn.disabled = true;
|
| 435 |
+
AuthSystem.login(loginId, password).then(function (data) {
|
| 436 |
+
showSession(data.user);
|
| 437 |
+
setTimeout(function () {
|
| 438 |
+
window.location.href = (data.user && data.user.role === 'admin') ? 'control-panel.html' : 'sfw/';
|
| 439 |
+
}, 1200);
|
| 440 |
+
}).catch(function (e) { resetWidget(); showError(e.message); });
|
| 441 |
+
}
|
| 442 |
+
}
|
| 443 |
+
|
| 444 |
+
submitBtn.addEventListener('click', doSubmit);
|
| 445 |
+
document.addEventListener('keydown', function (e) { if (e.key === 'Enter') doSubmit(); });
|
| 446 |
+
document.getElementById('logoutBtn').addEventListener('click', function () {
|
| 447 |
+
AuthSystem.logout().then(function () {
|
| 448 |
+
loggedIn.style.display = 'none';
|
| 449 |
+
authForms.style.display = '';
|
| 450 |
+
document.getElementById('authTabs').style.display = '';
|
| 451 |
+
document.querySelector('.auth-sub').style.display = '';
|
| 452 |
+
document.getElementById('authBack').style.display = '';
|
| 453 |
+
setMode('login');
|
| 454 |
+
startCooldown();
|
| 455 |
+
});
|
| 456 |
+
});
|
| 457 |
+
|
| 458 |
+
if (window.AuthSystem.cooldownRemaining() > 0) startCooldown();
|
| 459 |
+
window.AuthSystem.me().then(function (user) { if (user) showSession(user); });
|
| 460 |
+
</script>
|
| 461 |
+
</body>
|
| 462 |
+
</html>
|
manga-api.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
window.MANGA_API = (function () {
|
| 2 |
+
var RATE_MSG = 'MangaDex đang giới hạn yêu cầu. Vui lòng thử lại sau';
|
| 3 |
+
|
| 4 |
+
var RATING_SAFE = '&contentRating[]=safe&contentRating[]=suggestive';
|
| 5 |
+
var RATING_ALL = RATING_SAFE + '&contentRating[]=erotica&contentRating[]=pornographic';
|
| 6 |
+
var FEED_LIMIT = 100;
|
| 7 |
+
|
| 8 |
+
function api(url) {
|
| 9 |
+
function request() {
|
| 10 |
+
return fetch(window.MD_WORKER + url).then(function (r) {
|
| 11 |
+
if (r.status === 404) throw { notFound: true };
|
| 12 |
+
if (r.status === 429) throw { rateLimited: true };
|
| 13 |
+
if (r.status >= 500) throw { serverError: true };
|
| 14 |
+
if (!r.ok) return r.text().then(function (t) { throw new Error('HTTP ' + r.status + (t ? ': ' + t : '')); });
|
| 15 |
+
return r.json();
|
| 16 |
+
});
|
| 17 |
+
}
|
| 18 |
+
return request().catch(function (e) {
|
| 19 |
+
if (!(e && e.rateLimited)) throw e;
|
| 20 |
+
return new Promise(function (resolve) { setTimeout(resolve, 2000); }).then(request);
|
| 21 |
+
});
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
function contentRatingParam(nsfw) { return nsfw ? RATING_ALL : RATING_SAFE; }
|
| 25 |
+
|
| 26 |
+
function feedUrl(mangaId, lang, nsfw, offset) {
|
| 27 |
+
return '/manga/' + mangaId + '/feed?limit=' + FEED_LIMIT + '&offset=' + offset + '&order[chapter]=asc&includes[]=scanlation_group'
|
| 28 |
+
+ (lang && lang !== 'all' ? '&translatedLanguage[]=' + lang : '')
|
| 29 |
+
+ contentRatingParam(nsfw);
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
function fetchLang(mangaId, lang, nsfw) {
|
| 33 |
+
var all = [];
|
| 34 |
+
var offset = 0;
|
| 35 |
+
function page() {
|
| 36 |
+
return api(feedUrl(mangaId, lang, nsfw, offset)).then(function (j) {
|
| 37 |
+
all = all.concat(j.data || []);
|
| 38 |
+
offset += FEED_LIMIT;
|
| 39 |
+
if (j.data && j.data.length === FEED_LIMIT) return page();
|
| 40 |
+
return all;
|
| 41 |
+
});
|
| 42 |
+
}
|
| 43 |
+
return page();
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
function chooseChapters(mangaId, nsfw, want) {
|
| 47 |
+
var w = want || 'vi';
|
| 48 |
+
return fetchLang(mangaId, w, nsfw).then(function (chs) {
|
| 49 |
+
if (chs.length) return { lang: w, chapters: chs };
|
| 50 |
+
if (w === 'vi') {
|
| 51 |
+
return fetchLang(mangaId, 'en', nsfw).then(function (en) {
|
| 52 |
+
if (en.length) return { lang: 'en', chapters: en };
|
| 53 |
+
return fetchLang(mangaId, 'all', nsfw).then(function (all) { return { lang: 'all', chapters: all }; });
|
| 54 |
+
});
|
| 55 |
+
}
|
| 56 |
+
if (w === 'en') {
|
| 57 |
+
return fetchLang(mangaId, 'all', nsfw).then(function (all) { return { lang: 'all', chapters: all }; });
|
| 58 |
+
}
|
| 59 |
+
return { lang: w, chapters: chs };
|
| 60 |
+
});
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
function chapterPages(chapterId) {
|
| 64 |
+
return api('/at-home/server/' + chapterId).then(function (j) {
|
| 65 |
+
return {
|
| 66 |
+
base: j.baseUrl,
|
| 67 |
+
hash: j.chapter && j.chapter.hash,
|
| 68 |
+
files: (j.chapter && j.chapter.data) || [],
|
| 69 |
+
saver: (j.chapter && j.chapter.dataSaver) || []
|
| 70 |
+
};
|
| 71 |
+
});
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
function pageSrc(base, hash, file, saver) {
|
| 75 |
+
var direct = base + (saver ? '/data-saver/' : '/data/') + hash + '/' + file;
|
| 76 |
+
if (window.MD_WORKER) {
|
| 77 |
+
var workerBase = String(window.MD_WORKER).replace(/\/+$/, '');
|
| 78 |
+
return workerBase + '/img?u=' + encodeURIComponent(direct);
|
| 79 |
+
}
|
| 80 |
+
return direct;
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
function langName(l) { return l === 'vi' ? 'Vietsub' : l === 'en' ? 'English' : 'All'; }
|
| 84 |
+
|
| 85 |
+
function errorMessage(e) {
|
| 86 |
+
if (e && e.notFound) return 'Content not found.';
|
| 87 |
+
if (e && e.rateLimited) return RATE_MSG;
|
| 88 |
+
if (e && e.serverError) return 'MangaDex đang gặp lỗi tạm thời. Vui lòng thử lại sau.';
|
| 89 |
+
return null;
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
function qs(n) { var m = new RegExp('[?&]' + n + '=([^&]*)').exec(location.search); return m ? decodeURIComponent(m[1]) : null; }
|
| 93 |
+
|
| 94 |
+
function chapterLabel(ch) {
|
| 95 |
+
var a = ch.attributes || {};
|
| 96 |
+
var s = a.chapter ? 'Chương ' + a.chapter : 'Chương khác';
|
| 97 |
+
if (a.title) s += ': ' + a.title;
|
| 98 |
+
return s;
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
return {
|
| 102 |
+
RATE_MSG: RATE_MSG,
|
| 103 |
+
api: api,
|
| 104 |
+
fetchLang: fetchLang,
|
| 105 |
+
chooseChapters: chooseChapters,
|
| 106 |
+
chapterPages: chapterPages,
|
| 107 |
+
pageSrc: pageSrc,
|
| 108 |
+
langName: langName,
|
| 109 |
+
errorMessage: errorMessage,
|
| 110 |
+
qs: qs,
|
| 111 |
+
chapterLabel: chapterLabel
|
| 112 |
+
};
|
| 113 |
+
})();
|
manga-config.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
window.MD_WORKER = 'https://anivietdb.npgia-himlam.workers.dev/';
|
scan_anime_result.json
ADDED
|
@@ -0,0 +1,1314 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"name": "Date A Live (Phần 1)",
|
| 4 |
+
"page": "anime/universal/date-a-live/phan-1/",
|
| 5 |
+
"local": 0,
|
| 6 |
+
"src": "Hoàn tất (12/12)",
|
| 7 |
+
"srcName": "Date A Live",
|
| 8 |
+
"diff": null
|
| 9 |
+
},
|
| 10 |
+
{
|
| 11 |
+
"name": "Date A Live: Date to Date (OVA)",
|
| 12 |
+
"page": "anime/universal/date-a-live/ova-date-to-date/",
|
| 13 |
+
"local": 0,
|
| 14 |
+
"src": "FULL",
|
| 15 |
+
"srcName": "Date A Live: Date to Date",
|
| 16 |
+
"diff": null
|
| 17 |
+
},
|
| 18 |
+
{
|
| 19 |
+
"name": "Date A Live II (Phần 2)",
|
| 20 |
+
"page": "anime/universal/date-a-live/phan-2/",
|
| 21 |
+
"local": 0,
|
| 22 |
+
"src": "Hoàn tất (10/10)",
|
| 23 |
+
"srcName": "Date A Live II",
|
| 24 |
+
"diff": null
|
| 25 |
+
},
|
| 26 |
+
{
|
| 27 |
+
"name": "Date A Live II: Kurumi Star Festival (OVA)",
|
| 28 |
+
"page": "anime/universal/date-a-live/ova-kurumi/",
|
| 29 |
+
"local": 0,
|
| 30 |
+
"src": "FULL",
|
| 31 |
+
"srcName": "Date A Live II: Kurumi Star Festival",
|
| 32 |
+
"diff": null
|
| 33 |
+
},
|
| 34 |
+
{
|
| 35 |
+
"name": "Date A Live Movie: Mayuri Judgment",
|
| 36 |
+
"page": "anime/universal/date-a-live/movie-mayuri/",
|
| 37 |
+
"local": 0,
|
| 38 |
+
"src": "FULL",
|
| 39 |
+
"srcName": "Date A Live Movie: Mayuri Judgment",
|
| 40 |
+
"diff": null
|
| 41 |
+
},
|
| 42 |
+
{
|
| 43 |
+
"name": "Date A Live III (Phần 3)",
|
| 44 |
+
"page": "anime/universal/date-a-live/phan-3/",
|
| 45 |
+
"local": 0,
|
| 46 |
+
"src": "Hoàn tất (12/12)",
|
| 47 |
+
"srcName": "Date A Live III",
|
| 48 |
+
"diff": null
|
| 49 |
+
},
|
| 50 |
+
{
|
| 51 |
+
"name": "Date A Live IV (Phần 4)",
|
| 52 |
+
"page": "anime/universal/date-a-live/phan-4/",
|
| 53 |
+
"local": 0,
|
| 54 |
+
"src": "Hoàn tất (12/12)",
|
| 55 |
+
"srcName": "Date A Live IV",
|
| 56 |
+
"diff": null
|
| 57 |
+
},
|
| 58 |
+
{
|
| 59 |
+
"name": "Date A Live V (Phần 5)",
|
| 60 |
+
"page": "anime/universal/date-a-live/phan-5/",
|
| 61 |
+
"local": 0,
|
| 62 |
+
"src": "Hoàn tất (12/12)",
|
| 63 |
+
"srcName": "Date A Live V",
|
| 64 |
+
"diff": null
|
| 65 |
+
},
|
| 66 |
+
{
|
| 67 |
+
"name": "Date A Bullet",
|
| 68 |
+
"page": "anime/universal/date-a-live/spin-off-date-a-bullet/",
|
| 69 |
+
"local": 0,
|
| 70 |
+
"src": "Hoàn tất (2/2)",
|
| 71 |
+
"srcName": "Date A Bullet",
|
| 72 |
+
"diff": null
|
| 73 |
+
},
|
| 74 |
+
{
|
| 75 |
+
"name": "Date A Bullet: Dead or Bullet",
|
| 76 |
+
"page": "anime/universal/date-a-live/spin-off-dead-or-bullet/",
|
| 77 |
+
"local": 0,
|
| 78 |
+
"src": "FULL",
|
| 79 |
+
"srcName": "Date A Bullet: Dead or Bullet",
|
| 80 |
+
"diff": null
|
| 81 |
+
},
|
| 82 |
+
{
|
| 83 |
+
"name": "Aishiteru Game wo Owarasetai",
|
| 84 |
+
"page": "anime/universal/aishiteru-game/",
|
| 85 |
+
"local": 0,
|
| 86 |
+
"src": "Hoàn tất (12/12)",
|
| 87 |
+
"srcName": "Aishiteru Game wo Owarasetai",
|
| 88 |
+
"diff": null
|
| 89 |
+
},
|
| 90 |
+
{
|
| 91 |
+
"name": "Chuunibyou (Phần 1)",
|
| 92 |
+
"page": "anime/universal/chuunibyou-demo-koi-ga-shitai/phan-1/",
|
| 93 |
+
"local": 0,
|
| 94 |
+
"src": "Hoàn tất (12/12)",
|
| 95 |
+
"srcName": "Chuunibyou demo Koi ga Shitai!",
|
| 96 |
+
"diff": null
|
| 97 |
+
},
|
| 98 |
+
{
|
| 99 |
+
"name": "Chuunibyou: Slapstick Noel",
|
| 100 |
+
"page": "anime/universal/chuunibyou-demo-koi-ga-shitai/slapstick-noel/",
|
| 101 |
+
"local": 0,
|
| 102 |
+
"src": "FULL",
|
| 103 |
+
"srcName": "Chuunibyou demo Koi ga Shitai!: Kirameki no... Slapstick Noel",
|
| 104 |
+
"diff": null
|
| 105 |
+
},
|
| 106 |
+
{
|
| 107 |
+
"name": "Chuunibyou Movie: Rikka Kai",
|
| 108 |
+
"page": "anime/universal/chuunibyou-demo-koi-ga-shitai/movie-rikka-kai/",
|
| 109 |
+
"local": 0,
|
| 110 |
+
"src": "FULL",
|
| 111 |
+
"srcName": "Takanashi Rikka Kai: Chuunibyou demo Koi ga Shitai! Movie",
|
| 112 |
+
"diff": null
|
| 113 |
+
},
|
| 114 |
+
{
|
| 115 |
+
"name": "Chuunibyou Ren (Phần 2)",
|
| 116 |
+
"page": "anime/universal/chuunibyou-demo-koi-ga-shitai/phan-2/",
|
| 117 |
+
"local": 0,
|
| 118 |
+
"src": "Hoàn tất (7/7)",
|
| 119 |
+
"srcName": "Chuunibyou demo Koi ga Shitai! Ren Specials",
|
| 120 |
+
"diff": null
|
| 121 |
+
},
|
| 122 |
+
{
|
| 123 |
+
"name": "Chuunibyou Ren Lite",
|
| 124 |
+
"page": "anime/universal/chuunibyou-demo-koi-ga-shitai/ren-lite/",
|
| 125 |
+
"local": 0,
|
| 126 |
+
"src": "Hoàn tất (6/6)",
|
| 127 |
+
"srcName": "Chuunibyou demo Koi ga Shitai! Ren Lite",
|
| 128 |
+
"diff": null
|
| 129 |
+
},
|
| 130 |
+
{
|
| 131 |
+
"name": "Chuunibyou: The Rikka Wars",
|
| 132 |
+
"page": "anime/universal/chuunibyou-demo-koi-ga-shitai/rikka-wars/",
|
| 133 |
+
"local": 0,
|
| 134 |
+
"src": "FULL",
|
| 135 |
+
"srcName": "Chuunibyou demo Koi ga Shitai! Ren: The Rikka Wars",
|
| 136 |
+
"diff": null
|
| 137 |
+
},
|
| 138 |
+
{
|
| 139 |
+
"name": "Chuunibyou Ren Specials",
|
| 140 |
+
"page": "anime/universal/chuunibyou-demo-koi-ga-shitai/ren-specials/",
|
| 141 |
+
"local": 0,
|
| 142 |
+
"src": "Hoàn tất (7/7)",
|
| 143 |
+
"srcName": "Chuunibyou demo Koi ga Shitai! Ren Specials",
|
| 144 |
+
"diff": null
|
| 145 |
+
},
|
| 146 |
+
{
|
| 147 |
+
"name": "Chuunibyou Movie: Take On Me",
|
| 148 |
+
"page": "anime/universal/chuunibyou-demo-koi-ga-shitai/movie-take-on-me/",
|
| 149 |
+
"local": 0,
|
| 150 |
+
"src": "Trailer",
|
| 151 |
+
"srcName": "Hội Chứng Chuunibyou: Chấp Nhận Tôi",
|
| 152 |
+
"diff": null
|
| 153 |
+
},
|
| 154 |
+
{
|
| 155 |
+
"name": "Seishun Buta Yarou (Phần 1)",
|
| 156 |
+
"page": "anime/universal/seishun-buta-yarou/phan-1/",
|
| 157 |
+
"local": 13,
|
| 158 |
+
"src": "Hoàn tất (13/13)",
|
| 159 |
+
"srcName": "Seishun Buta Yarou wa Bunny Girl Senpai no Yume wo Minai",
|
| 160 |
+
"diff": null
|
| 161 |
+
},
|
| 162 |
+
{
|
| 163 |
+
"name": "Hội Chứng Tuổi Thanh Xuân: Thiếu Nữ Mộng Mơ",
|
| 164 |
+
"page": "anime/universal/seishun-buta-yarou/movie-1/",
|
| 165 |
+
"local": 0,
|
| 166 |
+
"src": "FULL",
|
| 167 |
+
"srcName": "Hội chứng tuổi thanh xuân: Thiếu nữ mộng mơ",
|
| 168 |
+
"diff": null
|
| 169 |
+
},
|
| 170 |
+
{
|
| 171 |
+
"name": "Hội Chứng Tuổi Thanh Xuân: Em Gái Xuất Hành",
|
| 172 |
+
"page": "anime/universal/seishun-buta-yarou/movie-2/",
|
| 173 |
+
"local": 0,
|
| 174 |
+
"src": "FULL",
|
| 175 |
+
"srcName": "Hội Chứng Tuổi Thanh Xuân: Em Gái Xuất Hành",
|
| 176 |
+
"diff": null
|
| 177 |
+
},
|
| 178 |
+
{
|
| 179 |
+
"name": "Hội Chứng Tuổi Thanh Xuân: Cô Bé Đeo Cặp Sách",
|
| 180 |
+
"page": "anime/universal/seishun-buta-yarou/movie-3/",
|
| 181 |
+
"local": 0,
|
| 182 |
+
"src": null,
|
| 183 |
+
"srcName": null,
|
| 184 |
+
"diff": null
|
| 185 |
+
},
|
| 186 |
+
{
|
| 187 |
+
"name": "Seishun Buta Yarou (Phần 2)",
|
| 188 |
+
"page": "anime/universal/seishun-buta-yarou/phan-2/",
|
| 189 |
+
"local": 13,
|
| 190 |
+
"src": "Hoàn tất (13/13)",
|
| 191 |
+
"srcName": "Seishun Buta Yarou wa Santa Claus no Yume wo Minai",
|
| 192 |
+
"diff": null
|
| 193 |
+
},
|
| 194 |
+
{
|
| 195 |
+
"name": "Slime (Phần 1)",
|
| 196 |
+
"page": "anime/universal/tensei-slime/phan-1/",
|
| 197 |
+
"local": 0,
|
| 198 |
+
"src": "Hoàn tất (5/5)",
|
| 199 |
+
"srcName": "Tensei shitara Slime Datta Ken OVA",
|
| 200 |
+
"diff": null
|
| 201 |
+
},
|
| 202 |
+
{
|
| 203 |
+
"name": "Slime (Phần 2)",
|
| 204 |
+
"page": "anime/universal/tensei-slime/phan-2/",
|
| 205 |
+
"local": 0,
|
| 206 |
+
"src": "Hoàn tất (24/24)",
|
| 207 |
+
"srcName": "Lúc Đó, Tôi Đã Chuyển Sinh Thành Slime (Phần 2)",
|
| 208 |
+
"diff": null
|
| 209 |
+
},
|
| 210 |
+
{
|
| 211 |
+
"name": "Slime (Phần 3)",
|
| 212 |
+
"page": "anime/universal/tensei-slime/phan-3/",
|
| 213 |
+
"local": 0,
|
| 214 |
+
"src": "Hoàn tất (26/26)",
|
| 215 |
+
"srcName": "Lúc Đó, Tôi Đã Chuyển Sinh Thành Slime (Phần 3)",
|
| 216 |
+
"diff": null
|
| 217 |
+
},
|
| 218 |
+
{
|
| 219 |
+
"name": "Slime (Phần 4)",
|
| 220 |
+
"page": "anime/universal/tensei-slime/phan-4/",
|
| 221 |
+
"local": 0,
|
| 222 |
+
"src": null,
|
| 223 |
+
"srcName": null,
|
| 224 |
+
"diff": null
|
| 225 |
+
},
|
| 226 |
+
{
|
| 227 |
+
"name": "Slime: Nhật Ký Của Rimuru",
|
| 228 |
+
"page": "anime/universal/tensei-slime/nhat-ky-rimuru/",
|
| 229 |
+
"local": 0,
|
| 230 |
+
"src": null,
|
| 231 |
+
"srcName": null,
|
| 232 |
+
"diff": null
|
| 233 |
+
},
|
| 234 |
+
{
|
| 235 |
+
"name": "Slime OVA",
|
| 236 |
+
"page": "anime/universal/tensei-slime/ova/",
|
| 237 |
+
"local": 0,
|
| 238 |
+
"src": "Hoàn tất (5/5)",
|
| 239 |
+
"srcName": "Tensei shitara Slime Datta Ken OVA",
|
| 240 |
+
"diff": null
|
| 241 |
+
},
|
| 242 |
+
{
|
| 243 |
+
"name": "Slime Movie: Mối Liên Kết Đỏ Thẫm",
|
| 244 |
+
"page": "anime/universal/tensei-slime/moi-lien-ket-do-tham/",
|
| 245 |
+
"local": 0,
|
| 246 |
+
"src": null,
|
| 247 |
+
"srcName": null,
|
| 248 |
+
"diff": null
|
| 249 |
+
},
|
| 250 |
+
{
|
| 251 |
+
"name": "Slime Movie: Nước Mắt Đại Dương",
|
| 252 |
+
"page": "anime/universal/tensei-slime/nuoc-mat-dai-duong/",
|
| 253 |
+
"local": 0,
|
| 254 |
+
"src": "FULL",
|
| 255 |
+
"srcName": "Lúc Đó Tôi Chuyển Sinh Thành Slime: Nước Mắt Đại Dương",
|
| 256 |
+
"diff": null
|
| 257 |
+
},
|
| 258 |
+
{
|
| 259 |
+
"name": "Slime: Giấc Mộng Coleus",
|
| 260 |
+
"page": "anime/universal/tensei-slime/giac-mong-coleus/",
|
| 261 |
+
"local": 0,
|
| 262 |
+
"src": "Hoàn tất (3/3)",
|
| 263 |
+
"srcName": "Lúc đó tôi đã chuyển sinh thành Slime: Giấc mộng Coleus",
|
| 264 |
+
"diff": null
|
| 265 |
+
},
|
| 266 |
+
{
|
| 267 |
+
"name": "Arya Bàn Bên Thỉnh Thoảng Lại Trêu Ghẹo Tôi Bằng Tiếng Nga",
|
| 268 |
+
"page": "anime/universal/arya-ban-ben/",
|
| 269 |
+
"local": 0,
|
| 270 |
+
"src": "Hoàn tất (12/12)",
|
| 271 |
+
"srcName": "Arya Bàn Bên Thỉnh Thoảng Lại Trêu Ghẹo Tôi Bằng Tiếng Nga",
|
| 272 |
+
"diff": null
|
| 273 |
+
},
|
| 274 |
+
{
|
| 275 |
+
"name": "Thiên Sứ Nhà Bên (Phần 1)",
|
| 276 |
+
"page": "anime/universal/thien-su-nha-ben/phan-1/",
|
| 277 |
+
"local": 0,
|
| 278 |
+
"src": "Hoàn tất (12/12)",
|
| 279 |
+
"srcName": "Thiên Sứ Nhà Bên (Phần 1)",
|
| 280 |
+
"diff": null
|
| 281 |
+
},
|
| 282 |
+
{
|
| 283 |
+
"name": "Thiên Sứ Nhà Bên (Phần 2)",
|
| 284 |
+
"page": "anime/universal/thien-su-nha-ben/phan-2/",
|
| 285 |
+
"local": 0,
|
| 286 |
+
"src": null,
|
| 287 |
+
"srcName": null,
|
| 288 |
+
"diff": null
|
| 289 |
+
},
|
| 290 |
+
{
|
| 291 |
+
"name": "Gundam 00 (Phần 1)",
|
| 292 |
+
"page": "anime/multiversal/gundam/gundam-00/phan-1/",
|
| 293 |
+
"local": 0,
|
| 294 |
+
"src": "Hoàn tất (25/25)",
|
| 295 |
+
"srcName": "Mobile Suit Gundam 00",
|
| 296 |
+
"diff": null
|
| 297 |
+
},
|
| 298 |
+
{
|
| 299 |
+
"name": "Gundam 00 (Phần 2)",
|
| 300 |
+
"page": "anime/multiversal/gundam/gundam-00/phan-2/",
|
| 301 |
+
"local": 0,
|
| 302 |
+
"src": "Hoàn tất (25/25)",
|
| 303 |
+
"srcName": "Mobile Suit Gundam 00 Second Season",
|
| 304 |
+
"diff": null
|
| 305 |
+
},
|
| 306 |
+
{
|
| 307 |
+
"name": "Gundam 00 Movie: A Wakening of the Trailblazer",
|
| 308 |
+
"page": "anime/multiversal/gundam/gundam-00/movie/",
|
| 309 |
+
"local": 0,
|
| 310 |
+
"src": "FULL",
|
| 311 |
+
"srcName": "Mobile Suit Gundam 00 The Movie: A Wakening of the Trailblazer",
|
| 312 |
+
"diff": null
|
| 313 |
+
},
|
| 314 |
+
{
|
| 315 |
+
"name": "Gundam Build Fighters",
|
| 316 |
+
"page": "anime/multiversal/gundam/gundam-build-fighters/phan-1/",
|
| 317 |
+
"local": 25,
|
| 318 |
+
"src": "Hoàn tất (25/25)",
|
| 319 |
+
"srcName": "Gundam Build Fighters",
|
| 320 |
+
"diff": null
|
| 321 |
+
},
|
| 322 |
+
{
|
| 323 |
+
"name": "Gundam Build Fighters Try",
|
| 324 |
+
"page": "anime/multiversal/gundam/gundam-build-fighters/try/",
|
| 325 |
+
"local": 25,
|
| 326 |
+
"src": "Hoàn tất (25/25)",
|
| 327 |
+
"srcName": "Gundam Build Fighters Try",
|
| 328 |
+
"diff": null
|
| 329 |
+
},
|
| 330 |
+
{
|
| 331 |
+
"name": "Gundam Build Fighters Try: Island Wars",
|
| 332 |
+
"page": "anime/multiversal/gundam/gundam-build-fighters/island-wars/",
|
| 333 |
+
"local": 0,
|
| 334 |
+
"src": "FULL",
|
| 335 |
+
"srcName": "Gundam Build Fighters Try: Island Wars",
|
| 336 |
+
"diff": null
|
| 337 |
+
},
|
| 338 |
+
{
|
| 339 |
+
"name": "Gundam Build Fighters Specials",
|
| 340 |
+
"page": "anime/multiversal/gundam/gundam-build-fighters/specials/",
|
| 341 |
+
"local": 3,
|
| 342 |
+
"src": "Hoàn tất (3/3)",
|
| 343 |
+
"srcName": "Gundam Build Fighters Specials",
|
| 344 |
+
"diff": null
|
| 345 |
+
},
|
| 346 |
+
{
|
| 347 |
+
"name": "Gundam Build Fighters Battlogue",
|
| 348 |
+
"page": "anime/multiversal/gundam/gundam-build-fighters/battlogue/",
|
| 349 |
+
"local": 5,
|
| 350 |
+
"src": "Hoàn tất (5/5)",
|
| 351 |
+
"srcName": "Gundam Build Fighters: Battlogue",
|
| 352 |
+
"diff": null
|
| 353 |
+
},
|
| 354 |
+
{
|
| 355 |
+
"name": "Gundam Build Divers",
|
| 356 |
+
"page": "anime/multiversal/gundam/gundam-build-divers/phan-1/",
|
| 357 |
+
"local": 25,
|
| 358 |
+
"src": "Hoàn tất (25/25)",
|
| 359 |
+
"srcName": "Gundam Build Divers",
|
| 360 |
+
"diff": null
|
| 361 |
+
},
|
| 362 |
+
{
|
| 363 |
+
"name": "Gundam Build Divers Re:Rise",
|
| 364 |
+
"page": "anime/multiversal/gundam/gundam-build-divers/rerise/",
|
| 365 |
+
"local": 13,
|
| 366 |
+
"src": null,
|
| 367 |
+
"srcName": null,
|
| 368 |
+
"diff": null
|
| 369 |
+
},
|
| 370 |
+
{
|
| 371 |
+
"name": "Gundam Build Divers Re:Rise Season 2",
|
| 372 |
+
"page": "anime/multiversal/gundam/gundam-build-divers/rerise-s2/",
|
| 373 |
+
"local": 14,
|
| 374 |
+
"src": null,
|
| 375 |
+
"srcName": null,
|
| 376 |
+
"diff": null
|
| 377 |
+
},
|
| 378 |
+
{
|
| 379 |
+
"name": "Gundam Build Divers Re:Rise OVA",
|
| 380 |
+
"page": "anime/multiversal/gundam/gundam-build-divers/ova/",
|
| 381 |
+
"local": 0,
|
| 382 |
+
"src": null,
|
| 383 |
+
"srcName": null,
|
| 384 |
+
"diff": null
|
| 385 |
+
},
|
| 386 |
+
{
|
| 387 |
+
"name": "Fate/Zero (Phần 1)",
|
| 388 |
+
"page": "anime/multiversal/fate/fate-zero/phan-1/",
|
| 389 |
+
"local": 0,
|
| 390 |
+
"src": null,
|
| 391 |
+
"srcName": null,
|
| 392 |
+
"diff": null
|
| 393 |
+
},
|
| 394 |
+
{
|
| 395 |
+
"name": "Fate/Zero (Phần 2)",
|
| 396 |
+
"page": "anime/multiversal/fate/fate-zero/phan-2/",
|
| 397 |
+
"local": 0,
|
| 398 |
+
"src": null,
|
| 399 |
+
"srcName": null,
|
| 400 |
+
"diff": null
|
| 401 |
+
},
|
| 402 |
+
{
|
| 403 |
+
"name": "Fate/stay night (2006)",
|
| 404 |
+
"page": "anime/multiversal/fate/fate-stay-night/fsn-2006/",
|
| 405 |
+
"local": 0,
|
| 406 |
+
"src": "Hoàn tất (24/24)",
|
| 407 |
+
"srcName": "Fate/stay night",
|
| 408 |
+
"diff": null
|
| 409 |
+
},
|
| 410 |
+
{
|
| 411 |
+
"name": "Fate/stay night: Unlimited Blade Works",
|
| 412 |
+
"page": "anime/multiversal/fate/fate-stay-night/ubw/",
|
| 413 |
+
"local": 0,
|
| 414 |
+
"src": "Hoàn tất (12/12)",
|
| 415 |
+
"srcName": "Fate/stay night: Unlimited Blade Works",
|
| 416 |
+
"diff": null
|
| 417 |
+
},
|
| 418 |
+
{
|
| 419 |
+
"name": "Fate/stay night: UBW (Phần 2)",
|
| 420 |
+
"page": "anime/multiversal/fate/fate-stay-night/ubw-s2/",
|
| 421 |
+
"local": 0,
|
| 422 |
+
"src": "Hoàn tất (13/13)",
|
| 423 |
+
"srcName": "Fate/stay night: Unlimited Blade Works 2nd Season",
|
| 424 |
+
"diff": null
|
| 425 |
+
},
|
| 426 |
+
{
|
| 427 |
+
"name": "Fate/stay night: UBW Sunny Day",
|
| 428 |
+
"page": "anime/multiversal/fate/fate-stay-night/sunny-day/",
|
| 429 |
+
"local": 0,
|
| 430 |
+
"src": null,
|
| 431 |
+
"srcName": null,
|
| 432 |
+
"diff": null
|
| 433 |
+
},
|
| 434 |
+
{
|
| 435 |
+
"name": "Fate/stay night Movie: Unlimited Blade Works",
|
| 436 |
+
"page": "anime/multiversal/fate/fate-stay-night/movie-ubw/",
|
| 437 |
+
"local": 0,
|
| 438 |
+
"src": null,
|
| 439 |
+
"srcName": null,
|
| 440 |
+
"diff": null
|
| 441 |
+
},
|
| 442 |
+
{
|
| 443 |
+
"name": "Heaven\\",
|
| 444 |
+
"page": "anime/multiversal/fate/heavens-feel/movie-1/",
|
| 445 |
+
"local": 0,
|
| 446 |
+
"src": null,
|
| 447 |
+
"srcName": null,
|
| 448 |
+
"diff": null
|
| 449 |
+
},
|
| 450 |
+
{
|
| 451 |
+
"name": "Heaven\\",
|
| 452 |
+
"page": "anime/multiversal/fate/heavens-feel/movie-2/",
|
| 453 |
+
"local": 0,
|
| 454 |
+
"src": null,
|
| 455 |
+
"srcName": null,
|
| 456 |
+
"diff": null
|
| 457 |
+
},
|
| 458 |
+
{
|
| 459 |
+
"name": "Heaven\\",
|
| 460 |
+
"page": "anime/multiversal/fate/heavens-feel/movie-3/",
|
| 461 |
+
"local": 0,
|
| 462 |
+
"src": null,
|
| 463 |
+
"srcName": null,
|
| 464 |
+
"diff": null
|
| 465 |
+
},
|
| 466 |
+
{
|
| 467 |
+
"name": "Fate/kaleid Prisma☆Illya (Phần 1)",
|
| 468 |
+
"page": "anime/multiversal/fate/fate-kaleid/phan-1/",
|
| 469 |
+
"local": 0,
|
| 470 |
+
"src": null,
|
| 471 |
+
"srcName": null,
|
| 472 |
+
"diff": null
|
| 473 |
+
},
|
| 474 |
+
{
|
| 475 |
+
"name": "Fate/kaleid Prisma☆Illya 2wei!",
|
| 476 |
+
"page": "anime/multiversal/fate/fate-kaleid/phan-2/",
|
| 477 |
+
"local": 0,
|
| 478 |
+
"src": "Hoàn tất (10/10)",
|
| 479 |
+
"srcName": "Fate/kaleid liner Prisma☆Illya 2wei!",
|
| 480 |
+
"diff": null
|
| 481 |
+
},
|
| 482 |
+
{
|
| 483 |
+
"name": "Fate/kaleid Prisma☆Illya 2wei Herz!",
|
| 484 |
+
"page": "anime/multiversal/fate/fate-kaleid/phan-3/",
|
| 485 |
+
"local": 0,
|
| 486 |
+
"src": "Hoàn tất (10/10)",
|
| 487 |
+
"srcName": "Fate/kaleid liner Prisma☆Illya 2wei Herz!",
|
| 488 |
+
"diff": null
|
| 489 |
+
},
|
| 490 |
+
{
|
| 491 |
+
"name": "Fate/kaleid Prisma☆Illya 3rei!!",
|
| 492 |
+
"page": "anime/multiversal/fate/fate-kaleid/phan-4/",
|
| 493 |
+
"local": 0,
|
| 494 |
+
"src": null,
|
| 495 |
+
"srcName": null,
|
| 496 |
+
"diff": null
|
| 497 |
+
},
|
| 498 |
+
{
|
| 499 |
+
"name": "Fate/kaleid Prisma☆Illya Movie: Sekka no Chikai",
|
| 500 |
+
"page": "anime/multiversal/fate/fate-kaleid/movie-sekka/",
|
| 501 |
+
"local": 0,
|
| 502 |
+
"src": null,
|
| 503 |
+
"srcName": null,
|
| 504 |
+
"diff": null
|
| 505 |
+
},
|
| 506 |
+
{
|
| 507 |
+
"name": "Fate/kaleid Prisma☆Illya Movie: Licht",
|
| 508 |
+
"page": "anime/multiversal/fate/fate-kaleid/movie-licht/",
|
| 509 |
+
"local": 0,
|
| 510 |
+
"src": null,
|
| 511 |
+
"srcName": null,
|
| 512 |
+
"diff": null
|
| 513 |
+
},
|
| 514 |
+
{
|
| 515 |
+
"name": "Cuộc Chiến Chén Thánh",
|
| 516 |
+
"page": "anime/multiversal/fate/fate-kaleid/cuoc-chien-chen-thanh/",
|
| 517 |
+
"local": 0,
|
| 518 |
+
"src": null,
|
| 519 |
+
"srcName": null,
|
| 520 |
+
"diff": null
|
| 521 |
+
},
|
| 522 |
+
{
|
| 523 |
+
"name": "Fate/Apocrypha",
|
| 524 |
+
"page": "anime/multiversal/fate/fate-apocrypha/",
|
| 525 |
+
"local": 0,
|
| 526 |
+
"src": null,
|
| 527 |
+
"srcName": null,
|
| 528 |
+
"diff": null
|
| 529 |
+
},
|
| 530 |
+
{
|
| 531 |
+
"name": "Fate/Extra: Last Encore",
|
| 532 |
+
"page": "anime/multiversal/fate/fate-extra/last-encore/",
|
| 533 |
+
"local": 0,
|
| 534 |
+
"src": null,
|
| 535 |
+
"srcName": null,
|
| 536 |
+
"diff": null
|
| 537 |
+
},
|
| 538 |
+
{
|
| 539 |
+
"name": "Fate/Extra: Last Encore - Illustrias Tendousetsu",
|
| 540 |
+
"page": "anime/multiversal/fate/fate-extra/illustrias/",
|
| 541 |
+
"local": 0,
|
| 542 |
+
"src": null,
|
| 543 |
+
"srcName": null,
|
| 544 |
+
"diff": null
|
| 545 |
+
},
|
| 546 |
+
{
|
| 547 |
+
"name": "Fate/Grand Order: First Order",
|
| 548 |
+
"page": "anime/multiversal/fate/fate-grand-order/first-order/",
|
| 549 |
+
"local": 0,
|
| 550 |
+
"src": null,
|
| 551 |
+
"srcName": null,
|
| 552 |
+
"diff": null
|
| 553 |
+
},
|
| 554 |
+
{
|
| 555 |
+
"name": "Fate/Grand Order: Moonlight/Lostroom",
|
| 556 |
+
"page": "anime/multiversal/fate/fate-grand-order/moonlight-lostroom/",
|
| 557 |
+
"local": 0,
|
| 558 |
+
"src": "FULL",
|
| 559 |
+
"srcName": "Fate/Grand Order: Moonlight/Lostroom",
|
| 560 |
+
"diff": null
|
| 561 |
+
},
|
| 562 |
+
{
|
| 563 |
+
"name": "Fate/Grand Order: Babylonia",
|
| 564 |
+
"page": "anime/multiversal/fate/fate-grand-order/babylonia/",
|
| 565 |
+
"local": 0,
|
| 566 |
+
"src": null,
|
| 567 |
+
"srcName": null,
|
| 568 |
+
"diff": null
|
| 569 |
+
},
|
| 570 |
+
{
|
| 571 |
+
"name": "Fate/Grand Order: Camelot 1 - Wandering; Agateram",
|
| 572 |
+
"page": "anime/multiversal/fate/fate-grand-order/camelot-1/",
|
| 573 |
+
"local": 0,
|
| 574 |
+
"src": null,
|
| 575 |
+
"srcName": null,
|
| 576 |
+
"diff": null
|
| 577 |
+
},
|
| 578 |
+
{
|
| 579 |
+
"name": "Fate/Grand Order: Camelot 2 - Paladin; Agateram",
|
| 580 |
+
"page": "anime/multiversal/fate/fate-grand-order/camelot-2/",
|
| 581 |
+
"local": 0,
|
| 582 |
+
"src": null,
|
| 583 |
+
"srcName": null,
|
| 584 |
+
"diff": null
|
| 585 |
+
},
|
| 586 |
+
{
|
| 587 |
+
"name": "Fate/Grand Order: Solomon",
|
| 588 |
+
"page": "anime/multiversal/fate/fate-grand-order/solomon/",
|
| 589 |
+
"local": 0,
|
| 590 |
+
"src": "FULL",
|
| 591 |
+
"srcName": "Fate/Grand Order: Shuukyoku Tokuiten - Kani Jikan Shinden Solomon",
|
| 592 |
+
"diff": null
|
| 593 |
+
},
|
| 594 |
+
{
|
| 595 |
+
"name": "Fate/Grand Order: Himuro no Tenchi",
|
| 596 |
+
"page": "anime/multiversal/fate/fate-grand-order/himuro/",
|
| 597 |
+
"local": 0,
|
| 598 |
+
"src": null,
|
| 599 |
+
"srcName": null,
|
| 600 |
+
"diff": null
|
| 601 |
+
},
|
| 602 |
+
{
|
| 603 |
+
"name": "Fate/strange Fake: Whispers of Dawn",
|
| 604 |
+
"page": "anime/multiversal/fate/fate-strange-fake/whispers-of-dawn/",
|
| 605 |
+
"local": 0,
|
| 606 |
+
"src": null,
|
| 607 |
+
"srcName": null,
|
| 608 |
+
"diff": null
|
| 609 |
+
},
|
| 610 |
+
{
|
| 611 |
+
"name": "Fate/strange Fake",
|
| 612 |
+
"page": "anime/multiversal/fate/fate-strange-fake/tv/",
|
| 613 |
+
"local": 0,
|
| 614 |
+
"src": "Hoàn tất (13/13)",
|
| 615 |
+
"srcName": "Fate/strange Fake",
|
| 616 |
+
"diff": null
|
| 617 |
+
},
|
| 618 |
+
{
|
| 619 |
+
"name": "Gundam Build Metaverse",
|
| 620 |
+
"page": "anime/multiversal/gundam/gundam-build-metaverse/",
|
| 621 |
+
"local": 0,
|
| 622 |
+
"src": "Tập 1",
|
| 623 |
+
"srcName": "Gundam Build Metaverse",
|
| 624 |
+
"diff": null
|
| 625 |
+
},
|
| 626 |
+
{
|
| 627 |
+
"name": "JoJo Phần 1",
|
| 628 |
+
"page": "anime/universal/jojo/phan-1/",
|
| 629 |
+
"local": 26,
|
| 630 |
+
"src": null,
|
| 631 |
+
"srcName": null,
|
| 632 |
+
"diff": null
|
| 633 |
+
},
|
| 634 |
+
{
|
| 635 |
+
"name": "JoJo Phần 2",
|
| 636 |
+
"page": "anime/universal/jojo/phan-2/",
|
| 637 |
+
"local": 48,
|
| 638 |
+
"src": "Hoàn tất (48/48)",
|
| 639 |
+
"srcName": "Cuộc phiêu lưu kì lạ của JoJo (Phần 2)",
|
| 640 |
+
"diff": null
|
| 641 |
+
},
|
| 642 |
+
{
|
| 643 |
+
"name": "JoJo Phần 3",
|
| 644 |
+
"page": "anime/universal/jojo/phan-3/",
|
| 645 |
+
"local": 39,
|
| 646 |
+
"src": "FULL",
|
| 647 |
+
"srcName": "Cuộc Phiêu Lưu Kì Lạ Của Jojo: Kim Cương Bất Bại",
|
| 648 |
+
"diff": null
|
| 649 |
+
},
|
| 650 |
+
{
|
| 651 |
+
"name": "JoJo Phần 4",
|
| 652 |
+
"page": "anime/universal/jojo/phan-4/",
|
| 653 |
+
"local": 39,
|
| 654 |
+
"src": "Hoàn tất (39/39)",
|
| 655 |
+
"srcName": "Cuộc phiêu lưu kì lạ của JoJo (Phần 4)",
|
| 656 |
+
"diff": null
|
| 657 |
+
},
|
| 658 |
+
{
|
| 659 |
+
"name": "JoJo Phần 5",
|
| 660 |
+
"page": "anime/universal/jojo/phan-5/",
|
| 661 |
+
"local": 38,
|
| 662 |
+
"src": "Hoàn tất (38/38)",
|
| 663 |
+
"srcName": "Cuộc phiêu lưu kì lạ của JoJo (Phần 5)",
|
| 664 |
+
"diff": null
|
| 665 |
+
},
|
| 666 |
+
{
|
| 667 |
+
"name": "JoJo Steel Ball Run",
|
| 668 |
+
"page": "anime/universal/jojo/steel-ball-run/",
|
| 669 |
+
"local": 0,
|
| 670 |
+
"src": "FULL",
|
| 671 |
+
"srcName": "STEEL BALL RUN: Cuộc phiêu lưu kì lạ của JoJo",
|
| 672 |
+
"diff": null
|
| 673 |
+
},
|
| 674 |
+
{
|
| 675 |
+
"name": "Bocchi the Rock!",
|
| 676 |
+
"page": "anime/universal/bocchi-the-rock/phan-1/",
|
| 677 |
+
"local": 12,
|
| 678 |
+
"src": "Hoàn tất (12/12)",
|
| 679 |
+
"srcName": "Tay rock Bocchi!",
|
| 680 |
+
"diff": null
|
| 681 |
+
},
|
| 682 |
+
{
|
| 683 |
+
"name": "Bocchi the Rock! Movie",
|
| 684 |
+
"page": "anime/universal/bocchi-the-rock/movie/",
|
| 685 |
+
"local": 1,
|
| 686 |
+
"src": "Hoàn tất (2/2)",
|
| 687 |
+
"srcName": "Bocchi the Rock! Movie",
|
| 688 |
+
"diff": null
|
| 689 |
+
},
|
| 690 |
+
{
|
| 691 |
+
"name": "Death Note",
|
| 692 |
+
"page": "anime/universal/death-note/",
|
| 693 |
+
"local": 37,
|
| 694 |
+
"src": "Hoàn tất (37/37)",
|
| 695 |
+
"srcName": "Quyển Sổ Sinh Mệnh",
|
| 696 |
+
"diff": null
|
| 697 |
+
},
|
| 698 |
+
{
|
| 699 |
+
"name": "Kết Hôn Với Người Mình Ghét Nhất Lớp",
|
| 700 |
+
"page": "anime/universal/class-no-daikirai/",
|
| 701 |
+
"local": 12,
|
| 702 |
+
"src": "Hoàn tất (12/12)",
|
| 703 |
+
"srcName": "Class no Daikirai na Joshi to Kekkon suru Koto ni Natta.",
|
| 704 |
+
"diff": null
|
| 705 |
+
},
|
| 706 |
+
{
|
| 707 |
+
"name": "Công Chúa Ma Cà Rồng Tầm Thường",
|
| 708 |
+
"page": "anime/universal/cong-chua-ma-ca-rong/",
|
| 709 |
+
"local": 12,
|
| 710 |
+
"src": "Hoàn tất (12/12)",
|
| 711 |
+
"srcName": "Công Chúa Ma Cà Rồng Tầm Thường",
|
| 712 |
+
"diff": null
|
| 713 |
+
},
|
| 714 |
+
{
|
| 715 |
+
"name": "Vùng Đất Linh Hồn",
|
| 716 |
+
"page": "anime/universal/vung-dat-linh-hon/",
|
| 717 |
+
"local": 0,
|
| 718 |
+
"src": null,
|
| 719 |
+
"srcName": null,
|
| 720 |
+
"diff": null
|
| 721 |
+
},
|
| 722 |
+
{
|
| 723 |
+
"name": "Lời Thì Thầm Của Trái Tim",
|
| 724 |
+
"page": "anime/universal/loi-thi-tham-cua-trai-tim/",
|
| 725 |
+
"local": 0,
|
| 726 |
+
"src": "FULL",
|
| 727 |
+
"srcName": "Lời Thì Thầm Của Trái Tim",
|
| 728 |
+
"diff": null
|
| 729 |
+
},
|
| 730 |
+
{
|
| 731 |
+
"name": "Lâu Đài Di Động Của Howl",
|
| 732 |
+
"page": "anime/universal/lau-dai-di-dong-cua-howl/",
|
| 733 |
+
"local": 0,
|
| 734 |
+
"src": "FULL",
|
| 735 |
+
"srcName": "Lâu Đài Di Động Của Howl",
|
| 736 |
+
"diff": null
|
| 737 |
+
},
|
| 738 |
+
{
|
| 739 |
+
"name": "Shikimori-san",
|
| 740 |
+
"page": "anime/universal/shikimori-san/",
|
| 741 |
+
"local": 12,
|
| 742 |
+
"src": "Hoàn tất (14/14)",
|
| 743 |
+
"srcName": "Kawaii dake ja Nai Shikimori-san",
|
| 744 |
+
"diff": null
|
| 745 |
+
},
|
| 746 |
+
{
|
| 747 |
+
"name": "Dr. Stone (Phần 1)",
|
| 748 |
+
"page": "anime/universal/dr-stone/phan-1/",
|
| 749 |
+
"local": 24,
|
| 750 |
+
"src": "Hoàn tất (24/24)",
|
| 751 |
+
"srcName": "Tiến sĩ Đá (Phần 1)",
|
| 752 |
+
"diff": null
|
| 753 |
+
},
|
| 754 |
+
{
|
| 755 |
+
"name": "Dr. Stone (Phần 2)",
|
| 756 |
+
"page": "anime/universal/dr-stone/phan-2/",
|
| 757 |
+
"local": 11,
|
| 758 |
+
"src": null,
|
| 759 |
+
"srcName": null,
|
| 760 |
+
"diff": null
|
| 761 |
+
},
|
| 762 |
+
{
|
| 763 |
+
"name": "Dr. Stone (Phần 3)",
|
| 764 |
+
"page": "anime/universal/dr-stone/phan-3/",
|
| 765 |
+
"local": 22,
|
| 766 |
+
"src": null,
|
| 767 |
+
"srcName": null,
|
| 768 |
+
"diff": null
|
| 769 |
+
},
|
| 770 |
+
{
|
| 771 |
+
"name": "Dr. Stone (Phần 4)",
|
| 772 |
+
"page": "anime/universal/dr-stone/phan-4/",
|
| 773 |
+
"local": 37,
|
| 774 |
+
"src": null,
|
| 775 |
+
"srcName": null,
|
| 776 |
+
"diff": null
|
| 777 |
+
},
|
| 778 |
+
{
|
| 779 |
+
"name": "Kimi ga Shinu made Koi wo Shitai",
|
| 780 |
+
"page": "anime/universal/kimi-ga-shinu-made-koi-wo-shitai/",
|
| 781 |
+
"local": 6,
|
| 782 |
+
"src": "Tập 6",
|
| 783 |
+
"srcName": "Kimi ga Shinu made Koi wo Shitai",
|
| 784 |
+
"diff": null
|
| 785 |
+
},
|
| 786 |
+
{
|
| 787 |
+
"name": "Oregairu OVA",
|
| 788 |
+
"page": "anime/universal/oregairu/ova/",
|
| 789 |
+
"local": 0,
|
| 790 |
+
"src": "FULL",
|
| 791 |
+
"srcName": "Yahari Ore no Seishun Love Comedy wa Machigatteiru. OVA",
|
| 792 |
+
"diff": null
|
| 793 |
+
},
|
| 794 |
+
{
|
| 795 |
+
"name": "Oregairu (Phần 1)",
|
| 796 |
+
"page": "anime/universal/oregairu/phan-1/",
|
| 797 |
+
"local": 13,
|
| 798 |
+
"src": "Hoàn tất (13/13)",
|
| 799 |
+
"srcName": "Yahari Ore no Seishun Love Comedy wa Machigatteiru. Zoku",
|
| 800 |
+
"diff": null
|
| 801 |
+
},
|
| 802 |
+
{
|
| 803 |
+
"name": "Oregairu (Phần 2) - Zoku",
|
| 804 |
+
"page": "anime/universal/oregairu/phan-2/",
|
| 805 |
+
"local": 13,
|
| 806 |
+
"src": "Hoàn tất (13/13)",
|
| 807 |
+
"srcName": "Yahari Ore no Seishun Love Comedy wa Machigatteiru. Zoku",
|
| 808 |
+
"diff": null
|
| 809 |
+
},
|
| 810 |
+
{
|
| 811 |
+
"name": "Oregairu Zoku OVA",
|
| 812 |
+
"page": "anime/universal/oregairu/zoku-ova/",
|
| 813 |
+
"local": 0,
|
| 814 |
+
"src": "FULL",
|
| 815 |
+
"srcName": "Yahari Ore no Seishun Love Comedy wa Machigatteiru. Zoku OVA",
|
| 816 |
+
"diff": null
|
| 817 |
+
},
|
| 818 |
+
{
|
| 819 |
+
"name": "Oregairu (Phần 3) - Kan",
|
| 820 |
+
"page": "anime/universal/oregairu/phan-3/",
|
| 821 |
+
"local": 12,
|
| 822 |
+
"src": "Hoàn tất (12/12)",
|
| 823 |
+
"srcName": "Yahari Ore no Seishun Love Comedy wa Machigatteiru. Kan",
|
| 824 |
+
"diff": null
|
| 825 |
+
},
|
| 826 |
+
{
|
| 827 |
+
"name": "Oregairu Kan OVA",
|
| 828 |
+
"page": "anime/universal/oregairu/kan-ova/",
|
| 829 |
+
"local": 0,
|
| 830 |
+
"src": "FULL",
|
| 831 |
+
"srcName": "Yahari Ore no Seishun Love Comedy wa Machigatteiru. Kan OVA",
|
| 832 |
+
"diff": null
|
| 833 |
+
},
|
| 834 |
+
{
|
| 835 |
+
"name": "Classroom of the Elite (Phần 1)",
|
| 836 |
+
"page": "anime/universal/classroom-of-the-elite/phan-1/",
|
| 837 |
+
"local": 12,
|
| 838 |
+
"src": "Hoàn tất (13/13)",
|
| 839 |
+
"srcName": "Chào Mừng Đến Lớp Học Đề Cao Thực Lực (Phần 2)",
|
| 840 |
+
"diff": null
|
| 841 |
+
},
|
| 842 |
+
{
|
| 843 |
+
"name": "Classroom of the Elite (Phần 2)",
|
| 844 |
+
"page": "anime/universal/classroom-of-the-elite/phan-2/",
|
| 845 |
+
"local": 13,
|
| 846 |
+
"src": "Hoàn tất (13/13)",
|
| 847 |
+
"srcName": "Chào Mừng Đến Lớp Học Đề Cao Thực Lực (Phần 2)",
|
| 848 |
+
"diff": null
|
| 849 |
+
},
|
| 850 |
+
{
|
| 851 |
+
"name": "Classroom of the Elite (Phần 3)",
|
| 852 |
+
"page": "anime/universal/classroom-of-the-elite/phan-3/",
|
| 853 |
+
"local": 13,
|
| 854 |
+
"src": null,
|
| 855 |
+
"srcName": null,
|
| 856 |
+
"diff": null
|
| 857 |
+
},
|
| 858 |
+
{
|
| 859 |
+
"name": "Classroom of the Elite (Phần 4)",
|
| 860 |
+
"page": "anime/universal/classroom-of-the-elite/phan-4/",
|
| 861 |
+
"local": 16,
|
| 862 |
+
"src": null,
|
| 863 |
+
"srcName": null,
|
| 864 |
+
"diff": null
|
| 865 |
+
},
|
| 866 |
+
{
|
| 867 |
+
"name": "Bukiyou na Senpai",
|
| 868 |
+
"page": "anime/universal/bukiyou-na-senpai/",
|
| 869 |
+
"local": 12,
|
| 870 |
+
"src": "Hoàn tất (12/12)",
|
| 871 |
+
"srcName": "Bukiyou na Senpai.",
|
| 872 |
+
"diff": null
|
| 873 |
+
},
|
| 874 |
+
{
|
| 875 |
+
"name": "Tomodachi no Imouto",
|
| 876 |
+
"page": "anime/universal/tomodachi-no-imouto/",
|
| 877 |
+
"local": 12,
|
| 878 |
+
"src": "Hoàn tất (12/12)",
|
| 879 |
+
"srcName": "Tomodachi no Imouto ga Ore ni dake Uzai",
|
| 880 |
+
"diff": null
|
| 881 |
+
},
|
| 882 |
+
{
|
| 883 |
+
"name": "Class de 2-banme ni Kawaii",
|
| 884 |
+
"page": "anime/universal/class-de-2-banme/",
|
| 885 |
+
"local": 12,
|
| 886 |
+
"src": "Hoàn tất (12/12)",
|
| 887 |
+
"srcName": "Class de 2-banme ni Kawaii Onnanoko to Tomodachi ni Natta",
|
| 888 |
+
"diff": null
|
| 889 |
+
},
|
| 890 |
+
{
|
| 891 |
+
"name": "Kẻ Ngây Thơ, Người Từng Trải",
|
| 892 |
+
"page": "anime/universal/ke-ngay-tho/",
|
| 893 |
+
"local": 12,
|
| 894 |
+
"src": "Hoàn tất (12/12)",
|
| 895 |
+
"srcName": "Kẻ Ngây Thơ, Người Từng Trải Và Câu Chuyện Tình Của Chúng Ta",
|
| 896 |
+
"diff": null
|
| 897 |
+
},
|
| 898 |
+
{
|
| 899 |
+
"name": "Cô Bạn Tôi Thầm Thích Lại Quên Mang Kính Rồi",
|
| 900 |
+
"page": "anime/universal/co-ban-toi-tham-thich/",
|
| 901 |
+
"local": 13,
|
| 902 |
+
"src": "Hoàn tất (13/13)",
|
| 903 |
+
"srcName": "Cô Bạn Tôi Thầm Thích Lại Quên Mang Kính Rồi",
|
| 904 |
+
"diff": null
|
| 905 |
+
},
|
| 906 |
+
{
|
| 907 |
+
"name": "Shigatsu wa Kimi no Uso",
|
| 908 |
+
"page": "anime/universal/shigatsu-wa-kimi-no-uso/",
|
| 909 |
+
"local": 22,
|
| 910 |
+
"src": "Hoàn tất (22/22)",
|
| 911 |
+
"srcName": "Shigatsu wa Kimi no Uso",
|
| 912 |
+
"diff": null
|
| 913 |
+
},
|
| 914 |
+
{
|
| 915 |
+
"name": "Kanojo mo Kanojo (Phần 1)",
|
| 916 |
+
"page": "anime/universal/kanojo-mo-kanojo/phan-1/",
|
| 917 |
+
"local": 12,
|
| 918 |
+
"src": null,
|
| 919 |
+
"srcName": null,
|
| 920 |
+
"diff": null
|
| 921 |
+
},
|
| 922 |
+
{
|
| 923 |
+
"name": "Kanojo mo Kanojo (Phần 2)",
|
| 924 |
+
"page": "anime/universal/kanojo-mo-kanojo/phan-2/",
|
| 925 |
+
"local": 12,
|
| 926 |
+
"src": null,
|
| 927 |
+
"srcName": null,
|
| 928 |
+
"diff": null
|
| 929 |
+
},
|
| 930 |
+
{
|
| 931 |
+
"name": "BokuBen OVA",
|
| 932 |
+
"page": "anime/universal/bokutachi-wa-benkyou-ga-dekinai/ova/",
|
| 933 |
+
"local": 2,
|
| 934 |
+
"src": "Hoàn tất (2/2)",
|
| 935 |
+
"srcName": "Bokutachi wa Benkyou ga Dekinai OVA",
|
| 936 |
+
"diff": null
|
| 937 |
+
},
|
| 938 |
+
{
|
| 939 |
+
"name": "BokuBen (Phần 1)",
|
| 940 |
+
"page": "anime/universal/bokutachi-wa-benkyou-ga-dekinai/phan-1/",
|
| 941 |
+
"local": 13,
|
| 942 |
+
"src": null,
|
| 943 |
+
"srcName": null,
|
| 944 |
+
"diff": null
|
| 945 |
+
},
|
| 946 |
+
{
|
| 947 |
+
"name": "BokuBen (Phần 2)",
|
| 948 |
+
"page": "anime/universal/bokutachi-wa-benkyou-ga-dekinai/phan-2/",
|
| 949 |
+
"local": 13,
|
| 950 |
+
"src": null,
|
| 951 |
+
"srcName": null,
|
| 952 |
+
"diff": null
|
| 953 |
+
},
|
| 954 |
+
{
|
| 955 |
+
"name": "Umayon (Spin-off)",
|
| 956 |
+
"page": "anime/universal/uma-musume/umayon/",
|
| 957 |
+
"local": 12,
|
| 958 |
+
"src": null,
|
| 959 |
+
"srcName": null,
|
| 960 |
+
"diff": null
|
| 961 |
+
},
|
| 962 |
+
{
|
| 963 |
+
"name": "Uma Musume (Phần 1)",
|
| 964 |
+
"page": "anime/universal/uma-musume/phan-1/",
|
| 965 |
+
"local": 13,
|
| 966 |
+
"src": null,
|
| 967 |
+
"srcName": null,
|
| 968 |
+
"diff": null
|
| 969 |
+
},
|
| 970 |
+
{
|
| 971 |
+
"name": "Uma Musume: BNW no Chikai",
|
| 972 |
+
"page": "anime/universal/uma-musume/bnw-no-chikai/",
|
| 973 |
+
"local": 3,
|
| 974 |
+
"src": "Hoàn tất (3/3)",
|
| 975 |
+
"srcName": "Uma Musume: Pretty Derby - BNW no Chikai",
|
| 976 |
+
"diff": null
|
| 977 |
+
},
|
| 978 |
+
{
|
| 979 |
+
"name": "Uma Musume (Phần 2)",
|
| 980 |
+
"page": "anime/universal/uma-musume/phan-2/",
|
| 981 |
+
"local": 13,
|
| 982 |
+
"src": null,
|
| 983 |
+
"srcName": null,
|
| 984 |
+
"diff": null
|
| 985 |
+
},
|
| 986 |
+
{
|
| 987 |
+
"name": "Uma Musume (Phần 3)",
|
| 988 |
+
"page": "anime/universal/uma-musume/phan-3/",
|
| 989 |
+
"local": 13,
|
| 990 |
+
"src": null,
|
| 991 |
+
"srcName": null,
|
| 992 |
+
"diff": null
|
| 993 |
+
},
|
| 994 |
+
{
|
| 995 |
+
"name": "Uma Musume Movie: Shin Jidai",
|
| 996 |
+
"page": "anime/universal/uma-musume/movie/",
|
| 997 |
+
"local": 0,
|
| 998 |
+
"src": "FULL",
|
| 999 |
+
"srcName": "Uma Musume: Pretty Derby - Shin Jidai no Tobira",
|
| 1000 |
+
"diff": null
|
| 1001 |
+
},
|
| 1002 |
+
{
|
| 1003 |
+
"name": "Uma Musume: Road to the Top",
|
| 1004 |
+
"page": "anime/universal/uma-musume/road-to-the-top/",
|
| 1005 |
+
"local": 4,
|
| 1006 |
+
"src": "Hoàn tất (4/4)",
|
| 1007 |
+
"srcName": "Uma Musume: Pretty Derby - Road to the Top",
|
| 1008 |
+
"diff": null
|
| 1009 |
+
},
|
| 1010 |
+
{
|
| 1011 |
+
"name": "Uma Musume: Cinderella Gray",
|
| 1012 |
+
"page": "anime/universal/uma-musume/cinderella-gray/",
|
| 1013 |
+
"local": 23,
|
| 1014 |
+
"src": "Hoàn tất (23/23)",
|
| 1015 |
+
"srcName": "Uma Musume: Cinderella Gray",
|
| 1016 |
+
"diff": null
|
| 1017 |
+
},
|
| 1018 |
+
{
|
| 1019 |
+
"name": "Record of Ragnarok (P1)",
|
| 1020 |
+
"page": "anime/universal/record-of-ragnarok/phan-1/",
|
| 1021 |
+
"local": 12,
|
| 1022 |
+
"src": null,
|
| 1023 |
+
"srcName": null,
|
| 1024 |
+
"diff": null
|
| 1025 |
+
},
|
| 1026 |
+
{
|
| 1027 |
+
"name": "Record of Ragnarok (P2)",
|
| 1028 |
+
"page": "anime/universal/record-of-ragnarok/phan-2/",
|
| 1029 |
+
"local": 15,
|
| 1030 |
+
"src": null,
|
| 1031 |
+
"srcName": null,
|
| 1032 |
+
"diff": null
|
| 1033 |
+
},
|
| 1034 |
+
{
|
| 1035 |
+
"name": "Record of Ragnarok (P3)",
|
| 1036 |
+
"page": "anime/universal/record-of-ragnarok/phan-3/",
|
| 1037 |
+
"local": 15,
|
| 1038 |
+
"src": null,
|
| 1039 |
+
"srcName": null,
|
| 1040 |
+
"diff": null
|
| 1041 |
+
},
|
| 1042 |
+
{
|
| 1043 |
+
"name": "Eminence in Shadow (P1)",
|
| 1044 |
+
"page": "anime/universal/emine-in-shadow/phan-1/",
|
| 1045 |
+
"local": 20,
|
| 1046 |
+
"src": null,
|
| 1047 |
+
"srcName": null,
|
| 1048 |
+
"diff": null
|
| 1049 |
+
},
|
| 1050 |
+
{
|
| 1051 |
+
"name": "Eminence in Shadow (P2)",
|
| 1052 |
+
"page": "anime/universal/emine-in-shadow/phan-2/",
|
| 1053 |
+
"local": 12,
|
| 1054 |
+
"src": null,
|
| 1055 |
+
"srcName": null,
|
| 1056 |
+
"diff": null
|
| 1057 |
+
},
|
| 1058 |
+
{
|
| 1059 |
+
"name": "Bảo Mẫu Bí Mật Của Tiểu Thư",
|
| 1060 |
+
"page": "anime/universal/bao-mau-bi-mat-cua-tieu-thu/",
|
| 1061 |
+
"local": 0,
|
| 1062 |
+
"src": "Tập 7",
|
| 1063 |
+
"srcName": "Bảo Mẫu Bí Mật Của Tiểu Thư",
|
| 1064 |
+
"diff": null
|
| 1065 |
+
},
|
| 1066 |
+
{
|
| 1067 |
+
"name": "Thiếu Nữ Quái Vật Caramelise",
|
| 1068 |
+
"page": "anime/universal/thieu-nu-quai-vat-caramelise/",
|
| 1069 |
+
"local": 0,
|
| 1070 |
+
"src": "Tập 7",
|
| 1071 |
+
"srcName": "Thiếu Nữ Quái Vật Caramelise",
|
| 1072 |
+
"diff": null
|
| 1073 |
+
},
|
| 1074 |
+
{
|
| 1075 |
+
"name": "Nhật Ký Thường Ngày Của Tiên Vương (Phần 1)",
|
| 1076 |
+
"page": "anime/universal/nhat-ky-tien-vuong/phan-1/",
|
| 1077 |
+
"local": 0,
|
| 1078 |
+
"src": "Hoàn tất (12/12)",
|
| 1079 |
+
"srcName": "Nhật Ký Thường Ngày Của Tiên Vương (Phần 3)",
|
| 1080 |
+
"diff": null
|
| 1081 |
+
},
|
| 1082 |
+
{
|
| 1083 |
+
"name": "Nhật Ký Thường Ngày Của Tiên Vương (Phần 2)",
|
| 1084 |
+
"page": "anime/universal/nhat-ky-tien-vuong/phan-2/",
|
| 1085 |
+
"local": 0,
|
| 1086 |
+
"src": null,
|
| 1087 |
+
"srcName": null,
|
| 1088 |
+
"diff": null
|
| 1089 |
+
},
|
| 1090 |
+
{
|
| 1091 |
+
"name": "Nhật Ký Thường Ngày Của Tiên Vương (Phần 3)",
|
| 1092 |
+
"page": "anime/universal/nhat-ky-tien-vuong/phan-3/",
|
| 1093 |
+
"local": 0,
|
| 1094 |
+
"src": null,
|
| 1095 |
+
"srcName": null,
|
| 1096 |
+
"diff": null
|
| 1097 |
+
},
|
| 1098 |
+
{
|
| 1099 |
+
"name": "Nhật Ký Thường Ngày Của Tiên Vương (Phần 4)",
|
| 1100 |
+
"page": "anime/universal/nhat-ky-tien-vuong/phan-4/",
|
| 1101 |
+
"local": 0,
|
| 1102 |
+
"src": null,
|
| 1103 |
+
"srcName": null,
|
| 1104 |
+
"diff": null
|
| 1105 |
+
},
|
| 1106 |
+
{
|
| 1107 |
+
"name": "Nhật Ký Thường Ngày Của Tiên Vương (Phần 5)",
|
| 1108 |
+
"page": "anime/universal/nhat-ky-tien-vuong/phan-5/",
|
| 1109 |
+
"local": 0,
|
| 1110 |
+
"src": null,
|
| 1111 |
+
"srcName": null,
|
| 1112 |
+
"diff": null
|
| 1113 |
+
},
|
| 1114 |
+
{
|
| 1115 |
+
"name": "Ore wo Suki nano wa Omae dake ka yo",
|
| 1116 |
+
"page": "anime/universal/ore-wo-suki/phan-1/",
|
| 1117 |
+
"local": 0,
|
| 1118 |
+
"src": "Hoàn tất (12/12)",
|
| 1119 |
+
"srcName": "Ore wo Suki nano wa Omae dake ka yo",
|
| 1120 |
+
"diff": null
|
| 1121 |
+
},
|
| 1122 |
+
{
|
| 1123 |
+
"name": "Ore wo Suki Movie: Oretachi no Game Set",
|
| 1124 |
+
"page": "anime/universal/ore-wo-suki/movie/",
|
| 1125 |
+
"local": 0,
|
| 1126 |
+
"src": null,
|
| 1127 |
+
"srcName": null,
|
| 1128 |
+
"diff": null
|
| 1129 |
+
},
|
| 1130 |
+
{
|
| 1131 |
+
"name": "Boku no Kokoro no Yabai Yatsu (Phần 1)",
|
| 1132 |
+
"page": "anime/universal/boku-yabai/phan-1/",
|
| 1133 |
+
"local": 0,
|
| 1134 |
+
"src": "Hoàn tất (12/12)",
|
| 1135 |
+
"srcName": "Boku no Kokoro no Yabai Yatsu",
|
| 1136 |
+
"diff": null
|
| 1137 |
+
},
|
| 1138 |
+
{
|
| 1139 |
+
"name": "Boku no Kokoro no Yabai Yatsu (Phần 2)",
|
| 1140 |
+
"page": "anime/universal/boku-yabai/phan-2/",
|
| 1141 |
+
"local": 0,
|
| 1142 |
+
"src": null,
|
| 1143 |
+
"srcName": null,
|
| 1144 |
+
"diff": null
|
| 1145 |
+
},
|
| 1146 |
+
{
|
| 1147 |
+
"name": "Thất Nghiệp Chuyển Sinh (Phần 1)",
|
| 1148 |
+
"page": "anime/universal/that-nghiep-chuyen-sinh/phan-1/",
|
| 1149 |
+
"local": 0,
|
| 1150 |
+
"src": "Hoàn tất (23/23)",
|
| 1151 |
+
"srcName": "Thất Nghiệp Chuyển Sinh (Phần 1)",
|
| 1152 |
+
"diff": null
|
| 1153 |
+
},
|
| 1154 |
+
{
|
| 1155 |
+
"name": "Thất Nghiệp Chuyển Sinh (Phần 2)",
|
| 1156 |
+
"page": "anime/universal/that-nghiep-chuyen-sinh/phan-2/",
|
| 1157 |
+
"local": 0,
|
| 1158 |
+
"src": "Hoàn tất (25/25)",
|
| 1159 |
+
"srcName": "Thất Nghiệp Chuyển Sinh (Phần 2)",
|
| 1160 |
+
"diff": null
|
| 1161 |
+
},
|
| 1162 |
+
{
|
| 1163 |
+
"name": "Thất Nghiệp Chuyển Sinh (Phần 3)",
|
| 1164 |
+
"page": "anime/universal/that-nghiep-chuyen-sinh/phan-3/",
|
| 1165 |
+
"local": 0,
|
| 1166 |
+
"src": "Tập 8",
|
| 1167 |
+
"srcName": "Thất Nghiệp Chuyển Sinh (Phần 3)",
|
| 1168 |
+
"diff": null
|
| 1169 |
+
},
|
| 1170 |
+
{
|
| 1171 |
+
"name": "Dáng Hình Thanh Âm",
|
| 1172 |
+
"page": "anime/universal/dang-hinh-thanh-am/",
|
| 1173 |
+
"local": 0,
|
| 1174 |
+
"src": "FULL",
|
| 1175 |
+
"srcName": "Dáng Hình Thanh Âm",
|
| 1176 |
+
"diff": null
|
| 1177 |
+
},
|
| 1178 |
+
{
|
| 1179 |
+
"name": "Sasayaku You ni Koi wo Utau",
|
| 1180 |
+
"page": "anime/universal/sasayaku/",
|
| 1181 |
+
"local": 0,
|
| 1182 |
+
"src": "Hoàn tất (12/12)",
|
| 1183 |
+
"srcName": "Sasayaku You ni Koi wo Utau",
|
| 1184 |
+
"diff": null
|
| 1185 |
+
},
|
| 1186 |
+
{
|
| 1187 |
+
"name": "Amagami-san Chi no Enmusubi",
|
| 1188 |
+
"page": "anime/universal/amagami-san/",
|
| 1189 |
+
"local": 0,
|
| 1190 |
+
"src": "Hoàn tất (24/24)",
|
| 1191 |
+
"srcName": "Amagami-san Chi no Enmusubi",
|
| 1192 |
+
"diff": null
|
| 1193 |
+
},
|
| 1194 |
+
{
|
| 1195 |
+
"name": "Senpai Của Tôi Thật Phiền Phức",
|
| 1196 |
+
"page": "anime/universal/senpai-ga-uzai/",
|
| 1197 |
+
"local": 0,
|
| 1198 |
+
"src": "Hoàn tất (12/12)",
|
| 1199 |
+
"srcName": "Senpai ga Uzai Kouhai no Hanashi",
|
| 1200 |
+
"diff": null
|
| 1201 |
+
},
|
| 1202 |
+
{
|
| 1203 |
+
"name": "Mirai Nikki",
|
| 1204 |
+
"page": "anime/universal/mirai-nikki/",
|
| 1205 |
+
"local": 0,
|
| 1206 |
+
"src": "Hoàn tất (26/26)",
|
| 1207 |
+
"srcName": "Mirai Nikki",
|
| 1208 |
+
"diff": null
|
| 1209 |
+
},
|
| 1210 |
+
{
|
| 1211 |
+
"name": "Mirai Nikki: Redial",
|
| 1212 |
+
"page": "anime/universal/mirai-nikki-redial/",
|
| 1213 |
+
"local": 0,
|
| 1214 |
+
"src": "FULL",
|
| 1215 |
+
"srcName": "Mirai Nikki: Redial",
|
| 1216 |
+
"diff": null
|
| 1217 |
+
},
|
| 1218 |
+
{
|
| 1219 |
+
"name": "Toumei na Yoru ni Kakeru Kimi to, Me ni Mienai Koi wo Shita.",
|
| 1220 |
+
"page": "anime/universal/toumei-na-yoru-ni-kakeru-kimi-to-me-ni-mienai-koi-wo-shita/",
|
| 1221 |
+
"local": 0,
|
| 1222 |
+
"src": "Tập 7",
|
| 1223 |
+
"srcName": "Toumei na Yoru ni Kakeru Kimi to, Me ni Mienai Koi wo Shita.",
|
| 1224 |
+
"diff": null
|
| 1225 |
+
},
|
| 1226 |
+
{
|
| 1227 |
+
"name": "Oh Boy, Was I Wrong About Her",
|
| 1228 |
+
"page": "anime/universal/oh-boy-was-i-wrong-about-her/",
|
| 1229 |
+
"local": 0,
|
| 1230 |
+
"src": "Tập 7",
|
| 1231 |
+
"srcName": "Oh Boy, Was I Wrong About Her",
|
| 1232 |
+
"diff": null
|
| 1233 |
+
},
|
| 1234 |
+
{
|
| 1235 |
+
"name": "Kiếm Cơm Bằng Trò Chơi Sinh Tử",
|
| 1236 |
+
"page": "anime/universal/kiem-com-bang-tro-choi-sinh-tu/",
|
| 1237 |
+
"local": 0,
|
| 1238 |
+
"src": "Hoàn tất (11/11)",
|
| 1239 |
+
"srcName": "Kiếm Cơm Bằng Trò Chơi Sinh Tử",
|
| 1240 |
+
"diff": null
|
| 1241 |
+
},
|
| 1242 |
+
{
|
| 1243 |
+
"name": "Hoa thơm kiêu hãnh",
|
| 1244 |
+
"page": "anime/universal/hoa-thom-kieu-hanh/",
|
| 1245 |
+
"local": 0,
|
| 1246 |
+
"src": "Hoàn tất (13/13)",
|
| 1247 |
+
"srcName": "Hoa thơm kiêu hãnh",
|
| 1248 |
+
"diff": null
|
| 1249 |
+
},
|
| 1250 |
+
{
|
| 1251 |
+
"name": "Tôi Sẽ Đỡ Hết Mọi Thứ: Người Mạnh Nhất Thế Giới Với Hiểu Lầm Ngược Đời Muốn Làm Mạo Hiểm Giả",
|
| 1252 |
+
"page": "anime/universal/toi-se-do-het-moi-thu-nguoi-manh-nhat-the-gioi-voi-hieu-lam-nguoc-doi-muon-lam-mao-hiem-gia/",
|
| 1253 |
+
"local": 0,
|
| 1254 |
+
"src": "Hoàn tất (12/12)",
|
| 1255 |
+
"srcName": "Tôi Sẽ Đỡ Hết Mọi Thứ: Người Mạnh Nhất Thế Giới Với Hiểu Lầm Ngược Đời Muốn Làm Mạo Hiểm Giả",
|
| 1256 |
+
"diff": null
|
| 1257 |
+
},
|
| 1258 |
+
{
|
| 1259 |
+
"name": "Tình Bạn Trai Gái Liệu Có Bền Lâu?",
|
| 1260 |
+
"page": "anime/universal/tinh-ban-trai-gai-lieu-co-ben-lau/",
|
| 1261 |
+
"local": 0,
|
| 1262 |
+
"src": "Hoàn tất (12/12)",
|
| 1263 |
+
"srcName": "Tình Bạn Trai Gái Liệu Có Bền Lâu?",
|
| 1264 |
+
"diff": null
|
| 1265 |
+
},
|
| 1266 |
+
{
|
| 1267 |
+
"name": "Chuyển Sinh Thành Đệ Thất Hoàng Tử, Tôi Quyết Định Trau Dồi Ma Thuật (Phần 1)",
|
| 1268 |
+
"page": "anime/universal/chuyen-sinh-thanh-de-that-hoang-tu-toi-quyet-dinh-trau-doi-ma-thuat-phan-1/",
|
| 1269 |
+
"local": 0,
|
| 1270 |
+
"src": "Hoàn tất (12/12)",
|
| 1271 |
+
"srcName": "Chuyển Sinh Thành Đệ Thất Hoàng Tử, Tôi Quyết Định Trau Dồi Ma Thuật (Phần 1)",
|
| 1272 |
+
"diff": null
|
| 1273 |
+
},
|
| 1274 |
+
{
|
| 1275 |
+
"name": "Chuyển Sinh Thành Đệ Thất Hoàng Tử, Tôi Quyết Định Trau Dồi Ma Thuật (Phần 2)",
|
| 1276 |
+
"page": "anime/universal/chuyen-sinh-thanh-de-that-hoang-tu-toi-quyet-dinh-trau-doi-ma-thuat-phan-2/",
|
| 1277 |
+
"local": 0,
|
| 1278 |
+
"src": "Hoàn tất (12/12)",
|
| 1279 |
+
"srcName": "Chuyển Sinh Thành Đệ Thất Hoàng Tử, Tôi Quyết Định Trau Dồi Ma Thuật (Phần 2)",
|
| 1280 |
+
"diff": null
|
| 1281 |
+
},
|
| 1282 |
+
{
|
| 1283 |
+
"name": "Nguyệt Đạo Dị Giới (Phần 1)",
|
| 1284 |
+
"page": "anime/universal/nguyet-dao-di-gioi-phan-1/",
|
| 1285 |
+
"local": 0,
|
| 1286 |
+
"src": "Hoàn tất (12/12)",
|
| 1287 |
+
"srcName": "Nguyệt Đạo Dị Giới (Phần 1)",
|
| 1288 |
+
"diff": null
|
| 1289 |
+
},
|
| 1290 |
+
{
|
| 1291 |
+
"name": "Nguyệt Đạo Dị Giới (Phần 2)",
|
| 1292 |
+
"page": "anime/universal/nguyet-dao-di-gioi-phan-2/",
|
| 1293 |
+
"local": 0,
|
| 1294 |
+
"src": "Hoàn tất (25/25)",
|
| 1295 |
+
"srcName": "Nguyệt Đạo Dị Giới (Phần 2)",
|
| 1296 |
+
"diff": null
|
| 1297 |
+
},
|
| 1298 |
+
{
|
| 1299 |
+
"name": "Mashle",
|
| 1300 |
+
"page": "anime/universal/mashle/",
|
| 1301 |
+
"local": 0,
|
| 1302 |
+
"src": "Hoàn tất (12/12)",
|
| 1303 |
+
"srcName": "Mashle",
|
| 1304 |
+
"diff": null
|
| 1305 |
+
},
|
| 1306 |
+
{
|
| 1307 |
+
"name": "Mashle 2nd Season",
|
| 1308 |
+
"page": "anime/universal/mashle-2nd-season/",
|
| 1309 |
+
"local": 0,
|
| 1310 |
+
"src": "Hoàn tất (12/12)",
|
| 1311 |
+
"srcName": "Mashle 2nd Season",
|
| 1312 |
+
"diff": null
|
| 1313 |
+
}
|
| 1314 |
+
]
|
scan_missing_episodes.json
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"name": "Lời Thì Thầm Của Trái Tim",
|
| 4 |
+
"page": "anime/universal/loi-thi-tham-cua-trai-tim/",
|
| 5 |
+
"local": 1,
|
| 6 |
+
"src": 2,
|
| 7 |
+
"missing": [
|
| 8 |
+
{
|
| 9 |
+
"name": "Full",
|
| 10 |
+
"slug": "tap-full",
|
| 11 |
+
"embed": "https://embed14.streamc.xyz/embed.php?hash=d1f64f6d09c52e3173de404da54103bf"
|
| 12 |
+
}
|
| 13 |
+
]
|
| 14 |
+
},
|
| 15 |
+
{
|
| 16 |
+
"name": "Lâu Đài Di Động Của Howl",
|
| 17 |
+
"page": "anime/universal/lau-dai-di-dong-cua-howl/",
|
| 18 |
+
"local": 1,
|
| 19 |
+
"src": 2,
|
| 20 |
+
"missing": [
|
| 21 |
+
{
|
| 22 |
+
"name": "Full",
|
| 23 |
+
"slug": "tap-full",
|
| 24 |
+
"embed": "https://embed14.streamc.xyz/embed.php?hash=4c6515b6c3fbb1ddb17170807501dbc7"
|
| 25 |
+
}
|
| 26 |
+
]
|
| 27 |
+
},
|
| 28 |
+
{
|
| 29 |
+
"name": "Classroom of the Elite (Phần 1)",
|
| 30 |
+
"page": "anime/universal/classroom-of-the-elite/phan-1/",
|
| 31 |
+
"local": 12,
|
| 32 |
+
"src": 13,
|
| 33 |
+
"missing": [
|
| 34 |
+
{
|
| 35 |
+
"name": "13",
|
| 36 |
+
"slug": "tap-13",
|
| 37 |
+
"embed": "https://embed13.streamc.xyz/embed.php?hash=581db20d7f51959c1bdaa933fb442cfa"
|
| 38 |
+
}
|
| 39 |
+
]
|
| 40 |
+
},
|
| 41 |
+
{
|
| 42 |
+
"name": "Bảo Mẫu Bí Mật Của Tiểu Thư",
|
| 43 |
+
"page": "anime/universal/bao-mau-bi-mat-cua-tieu-thu/",
|
| 44 |
+
"local": 6,
|
| 45 |
+
"src": 7,
|
| 46 |
+
"missing": [
|
| 47 |
+
{
|
| 48 |
+
"name": "7",
|
| 49 |
+
"slug": "tap-7",
|
| 50 |
+
"embed": "https://embed14.streamc.xyz/embed.php?hash=60226eae761a770d80b248a4d13dd127"
|
| 51 |
+
}
|
| 52 |
+
]
|
| 53 |
+
},
|
| 54 |
+
{
|
| 55 |
+
"name": "Thiếu Nữ Quái Vật Caramelise",
|
| 56 |
+
"page": "anime/universal/thieu-nu-quai-vat-caramelise/",
|
| 57 |
+
"local": 6,
|
| 58 |
+
"src": 7,
|
| 59 |
+
"missing": [
|
| 60 |
+
{
|
| 61 |
+
"name": "7",
|
| 62 |
+
"slug": "tap-7",
|
| 63 |
+
"embed": "https://embed18.streamc.xyz/embed.php?hash=092bb7e9d6b02aa4f8e8b4392e616339"
|
| 64 |
+
}
|
| 65 |
+
]
|
| 66 |
+
},
|
| 67 |
+
{
|
| 68 |
+
"name": "Thất Nghiệp Chuyển Sinh (Phần 3)",
|
| 69 |
+
"page": "anime/universal/that-nghiep-chuyen-sinh/phan-3/",
|
| 70 |
+
"local": 7,
|
| 71 |
+
"src": 8,
|
| 72 |
+
"missing": [
|
| 73 |
+
{
|
| 74 |
+
"name": "8",
|
| 75 |
+
"slug": "tap-8",
|
| 76 |
+
"embed": "https://embed1.streamc.xyz/embed.php?hash=2498fe2437b0fd5bd0500bc22ff7b6b1"
|
| 77 |
+
}
|
| 78 |
+
]
|
| 79 |
+
},
|
| 80 |
+
{
|
| 81 |
+
"name": "Toumei na Yoru ni Kakeru Kimi to, Me ni Mienai Koi wo Shita.",
|
| 82 |
+
"page": "anime/universal/toumei-na-yoru-ni-kakeru-kimi-to-me-ni-mienai-koi-wo-shita/",
|
| 83 |
+
"local": 6,
|
| 84 |
+
"src": 7,
|
| 85 |
+
"missing": [
|
| 86 |
+
{
|
| 87 |
+
"name": "7",
|
| 88 |
+
"slug": "tap-7",
|
| 89 |
+
"embed": "https://embed2.streamc.xyz/embed.php?hash=7bc1401d400ceefc0c01f0940fb95eb1"
|
| 90 |
+
}
|
| 91 |
+
]
|
| 92 |
+
},
|
| 93 |
+
{
|
| 94 |
+
"name": "Oh Boy, Was I Wrong About Her",
|
| 95 |
+
"page": "anime/universal/oh-boy-was-i-wrong-about-her/",
|
| 96 |
+
"local": 6,
|
| 97 |
+
"src": 7,
|
| 98 |
+
"missing": [
|
| 99 |
+
{
|
| 100 |
+
"name": "7",
|
| 101 |
+
"slug": "tap-7",
|
| 102 |
+
"embed": "https://embed12.streamc.xyz/embed.php?hash=11cbc5514e2bdf1c4b5f7ecfc322033a"
|
| 103 |
+
}
|
| 104 |
+
]
|
| 105 |
+
},
|
| 106 |
+
{
|
| 107 |
+
"name": "Chuyển Sinh Thành Đệ Thất Hoàng Tử, Tôi Quyết Định Trau Dồi Ma Thuật (Phần 2)",
|
| 108 |
+
"page": "anime/universal/chuyen-sinh-thanh-de-that-hoang-tu-toi-quyet-dinh-trau-doi-ma-thuat-phan-2/",
|
| 109 |
+
"local": 12,
|
| 110 |
+
"src": 14,
|
| 111 |
+
"missing": [
|
| 112 |
+
{
|
| 113 |
+
"name": "11",
|
| 114 |
+
"slug": "tap-11",
|
| 115 |
+
"embed": "https://embed13.streamc.xyz/embed.php?hash=ad8d259c7d0d55959461a92e51d5d224"
|
| 116 |
+
},
|
| 117 |
+
{
|
| 118 |
+
"name": "12",
|
| 119 |
+
"slug": "tap-12",
|
| 120 |
+
"embed": "https://embed11.streamc.xyz/embed.php?hash=879224235c38bd2fe533db3be3243b0f"
|
| 121 |
+
}
|
| 122 |
+
]
|
| 123 |
+
}
|
| 124 |
+
]
|
scan_multi_server.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"name": "Lời Thì Thầm Của Trái Tim",
|
| 4 |
+
"page": "anime/universal/loi-thi-tham-cua-trai-tim/",
|
| 5 |
+
"servers": [
|
| 6 |
+
{
|
| 7 |
+
"name": "Vietsub #1",
|
| 8 |
+
"count": 1,
|
| 9 |
+
"items": [
|
| 10 |
+
{
|
| 11 |
+
"name": "FULL",
|
| 12 |
+
"embed": "https://embed12.streamc.xyz/embed.php?hash=159da7b3838872aec76fe20e24148967"
|
| 13 |
+
}
|
| 14 |
+
]
|
| 15 |
+
},
|
| 16 |
+
{
|
| 17 |
+
"name": "Lồng Tiếng #1",
|
| 18 |
+
"count": 1,
|
| 19 |
+
"items": [
|
| 20 |
+
{
|
| 21 |
+
"name": "Full",
|
| 22 |
+
"embed": "https://embed14.streamc.xyz/embed.php?hash=d1f64f6d09c52e3173de404da54103bf"
|
| 23 |
+
}
|
| 24 |
+
]
|
| 25 |
+
}
|
| 26 |
+
]
|
| 27 |
+
},
|
| 28 |
+
{
|
| 29 |
+
"name": "Lâu Đài Di Động Của Howl",
|
| 30 |
+
"page": "anime/universal/lau-dai-di-dong-cua-howl/",
|
| 31 |
+
"servers": [
|
| 32 |
+
{
|
| 33 |
+
"name": "Vietsub #1",
|
| 34 |
+
"count": 1,
|
| 35 |
+
"items": [
|
| 36 |
+
{
|
| 37 |
+
"name": "Full",
|
| 38 |
+
"embed": "https://embed13.streamc.xyz/embed.php?hash=b6bd17a0e4c3fc30931c6f9bc4d2e64e"
|
| 39 |
+
}
|
| 40 |
+
]
|
| 41 |
+
},
|
| 42 |
+
{
|
| 43 |
+
"name": "Lồng Tiếng #1",
|
| 44 |
+
"count": 1,
|
| 45 |
+
"items": [
|
| 46 |
+
{
|
| 47 |
+
"name": "Full",
|
| 48 |
+
"embed": "https://embed14.streamc.xyz/embed.php?hash=4c6515b6c3fbb1ddb17170807501dbc7"
|
| 49 |
+
}
|
| 50 |
+
]
|
| 51 |
+
}
|
| 52 |
+
]
|
| 53 |
+
}
|
| 54 |
+
]
|
signup-loginsystem.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
(function (window) {
|
| 2 |
+
'use strict';
|
| 3 |
+
|
| 4 |
+
var API_BASE = localStorage.getItem('anivietdb-auth-api') || 'https://sginup-loginsystem.npgia-himlam.workers.dev/';
|
| 5 |
+
|
| 6 |
+
var MEM = {};
|
| 7 |
+
var STORE = 'localStorage';
|
| 8 |
+
try {
|
| 9 |
+
if (localStorage.getItem('anivietdb-token')) MEM.token = localStorage.getItem('anivietdb-token');
|
| 10 |
+
if (localStorage.getItem('anivietdb-user')) MEM.user = JSON.parse(localStorage.getItem('anivietdb-user'));
|
| 11 |
+
} catch (e) { /* localStorage might be blocked */ }
|
| 12 |
+
if (!MEM.token) {
|
| 13 |
+
try {
|
| 14 |
+
if (sessionStorage.getItem('anivietdb-token')) MEM.token = sessionStorage.getItem('anivietdb-token');
|
| 15 |
+
if (sessionStorage.getItem('anivietdb-user')) MEM.user = JSON.parse(sessionStorage.getItem('anivietdb-user'));
|
| 16 |
+
} catch (e) { /* ignore */ }
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
function save() {
|
| 20 |
+
try {
|
| 21 |
+
if (MEM.token) localStorage.setItem('anivietdb-token', MEM.token);
|
| 22 |
+
else localStorage.removeItem('anivietdb-token');
|
| 23 |
+
if (MEM.user) localStorage.setItem('anivietdb-user', JSON.stringify(MEM.user));
|
| 24 |
+
else localStorage.removeItem('anivietdb-user');
|
| 25 |
+
} catch (e) {
|
| 26 |
+
try {
|
| 27 |
+
if (MEM.token) sessionStorage.setItem('anivietdb-token', MEM.token);
|
| 28 |
+
else sessionStorage.removeItem('anivietdb-token');
|
| 29 |
+
if (MEM.user) sessionStorage.setItem('anivietdb-user', JSON.stringify(MEM.user));
|
| 30 |
+
else sessionStorage.removeItem('anivietdb-user');
|
| 31 |
+
} catch (e2) { /* ignore */ }
|
| 32 |
+
}
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
function setSession(data) {
|
| 36 |
+
MEM.token = data && data.token ? data.token : null;
|
| 37 |
+
MEM.user = data && data.user ? data.user : null;
|
| 38 |
+
save();
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
function clearSession() {
|
| 42 |
+
MEM.token = null;
|
| 43 |
+
MEM.user = null;
|
| 44 |
+
save();
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
var COOLDOWN_MS = 10 * 1000;
|
| 48 |
+
var COOLDOWN_KEY = 'anivietdb-cooldown';
|
| 49 |
+
|
| 50 |
+
function setCooldown() {
|
| 51 |
+
try { sessionStorage.setItem(COOLDOWN_KEY, String(Date.now() + COOLDOWN_MS)); } catch (e) { /* ignore */ }
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
function cooldownRemaining() {
|
| 55 |
+
try {
|
| 56 |
+
var until = Number(sessionStorage.getItem(COOLDOWN_KEY));
|
| 57 |
+
var r = until - Date.now();
|
| 58 |
+
return r > 0 ? r : 0;
|
| 59 |
+
} catch (e) { return 0; }
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
function request(path, method, body) {
|
| 63 |
+
var headers = { 'Content-Type': 'application/json' };
|
| 64 |
+
if (MEM.token) headers['Authorization'] = 'Bearer ' + MEM.token;
|
| 65 |
+
|
| 66 |
+
var sep = path.indexOf('?') === -1 ? '?' : '&';
|
| 67 |
+
var url = API_BASE + path + sep + '_t=' + Date.now();
|
| 68 |
+
var init = { method: method || 'GET', headers: headers, cache: 'no-store' };
|
| 69 |
+
if (body) init.body = JSON.stringify(body);
|
| 70 |
+
|
| 71 |
+
return fetch(url, init).then(function (resp) {
|
| 72 |
+
return resp.json().catch(function () { return {}; }).then(function (data) {
|
| 73 |
+
if (!resp.ok) {
|
| 74 |
+
var err = new Error((data && data.error) || ('HTTP ' + resp.status));
|
| 75 |
+
err.status = resp.status;
|
| 76 |
+
err.data = data;
|
| 77 |
+
throw err;
|
| 78 |
+
}
|
| 79 |
+
return data;
|
| 80 |
+
});
|
| 81 |
+
});
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
function turnstileToken() {
|
| 85 |
+
try {
|
| 86 |
+
if (!window.turnstile || typeof window.turnstile.getResponse !== 'function') return '';
|
| 87 |
+
var widgets = document.querySelectorAll('.cf-turnstile');
|
| 88 |
+
for (var i = 0; i < widgets.length; i++) {
|
| 89 |
+
var el = widgets[i];
|
| 90 |
+
if (el.offsetParent === null) continue;
|
| 91 |
+
return window.turnstile.getResponse(el) || '';
|
| 92 |
+
}
|
| 93 |
+
return '';
|
| 94 |
+
} catch (e) { return ''; }
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
window.AuthSystem = {
|
| 98 |
+
baseUrl: API_BASE,
|
| 99 |
+
|
| 100 |
+
setBaseUrl: function (url) {
|
| 101 |
+
localStorage.setItem('anivietdb-auth-api', url);
|
| 102 |
+
API_BASE = url;
|
| 103 |
+
},
|
| 104 |
+
|
| 105 |
+
isConfigured: function () {
|
| 106 |
+
return !!API_BASE && API_BASE.indexOf('__PASTE') === -1;
|
| 107 |
+
},
|
| 108 |
+
|
| 109 |
+
isLoggedIn: function () {
|
| 110 |
+
return !!MEM.token;
|
| 111 |
+
},
|
| 112 |
+
|
| 113 |
+
getToken: function () {
|
| 114 |
+
return MEM.token;
|
| 115 |
+
},
|
| 116 |
+
|
| 117 |
+
getUser: function () {
|
| 118 |
+
return MEM.user;
|
| 119 |
+
},
|
| 120 |
+
|
| 121 |
+
register: function (email, username, password) {
|
| 122 |
+
return request('/api/auth/register', 'POST', { email: email, username: username, password: password, 'cf-turnstile-response': turnstileToken() })
|
| 123 |
+
.then(function (data) { setSession(data); return data; });
|
| 124 |
+
},
|
| 125 |
+
|
| 126 |
+
login: function (emailOrUsername, password) {
|
| 127 |
+
return request('/api/auth/login', 'POST', { email: emailOrUsername, password: password, 'cf-turnstile-response': turnstileToken() })
|
| 128 |
+
.then(function (data) { setSession(data); return data; });
|
| 129 |
+
},
|
| 130 |
+
|
| 131 |
+
logout: function () {
|
| 132 |
+
return request('/api/auth/logout', 'POST')
|
| 133 |
+
.then(function (data) { clearSession(); setCooldown(); return data; })
|
| 134 |
+
.catch(function () { clearSession(); setCooldown(); });
|
| 135 |
+
},
|
| 136 |
+
|
| 137 |
+
cooldownRemaining: cooldownRemaining,
|
| 138 |
+
|
| 139 |
+
isAdmin: function () {
|
| 140 |
+
return !!(MEM.user && MEM.user.role === 'admin');
|
| 141 |
+
},
|
| 142 |
+
|
| 143 |
+
adminUsers: function () {
|
| 144 |
+
return request('/api/auth/users', 'GET');
|
| 145 |
+
},
|
| 146 |
+
|
| 147 |
+
adminDeleteUser: function (id) {
|
| 148 |
+
return request('/api/auth/users/' + id, 'DELETE');
|
| 149 |
+
},
|
| 150 |
+
|
| 151 |
+
me: function () {
|
| 152 |
+
if (!MEM.token) return Promise.resolve(null);
|
| 153 |
+
return request('/api/auth/me', 'GET')
|
| 154 |
+
.then(function (data) {
|
| 155 |
+
if (data && data.user) {
|
| 156 |
+
MEM.user = data.user;
|
| 157 |
+
save();
|
| 158 |
+
}
|
| 159 |
+
return data.user || null;
|
| 160 |
+
})
|
| 161 |
+
.catch(function (err) {
|
| 162 |
+
if (err && err.status === 401) clearSession();
|
| 163 |
+
return null;
|
| 164 |
+
});
|
| 165 |
+
},
|
| 166 |
+
|
| 167 |
+
clearSession: clearSession
|
| 168 |
+
};
|
| 169 |
+
})(window);
|
style.css
CHANGED
|
@@ -1,28 +1,1899 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
body {
|
| 2 |
-
|
| 3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
}
|
| 5 |
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
}
|
| 10 |
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
}
|
| 17 |
|
| 18 |
-
.
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
}
|
| 25 |
|
| 26 |
-
.
|
| 27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
}
|
|
|
|
| 1 |
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
| 2 |
+
|
| 3 |
+
:root {
|
| 4 |
+
--header-gradient: linear-gradient(135deg, #ff9a9e, #fad0c4);
|
| 5 |
+
--accent-color: #ff9a9e;
|
| 6 |
+
--ep-color: #ff4b5c;
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
body {
|
| 10 |
+
font-family: 'Roboto', sans-serif;
|
| 11 |
+
background: #f5f7fa;
|
| 12 |
+
color: #333;
|
| 13 |
+
padding: 20px;
|
| 14 |
+
min-height: 100vh;
|
| 15 |
+
display: flex;
|
| 16 |
+
flex-direction: column;
|
| 17 |
}
|
| 18 |
|
| 19 |
+
.container {
|
| 20 |
+
max-width: 1000px;
|
| 21 |
+
margin: 40px auto;
|
| 22 |
+
background: rgba(255,255,255,0.88);
|
| 23 |
+
backdrop-filter: blur(10px);
|
| 24 |
+
-webkit-backdrop-filter: blur(10px);
|
| 25 |
+
border-radius: 12px;
|
| 26 |
+
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
|
| 27 |
+
overflow: hidden;
|
| 28 |
+
flex: 1;
|
| 29 |
}
|
| 30 |
|
| 31 |
+
.header {
|
| 32 |
+
display: flex;
|
| 33 |
+
background: var(--header-gradient);
|
| 34 |
+
padding: 20px;
|
| 35 |
+
align-items: center;
|
| 36 |
}
|
| 37 |
|
| 38 |
+
.poster-wrapper {
|
| 39 |
+
flex-shrink: 0;
|
| 40 |
+
width: 200px;
|
| 41 |
+
border-radius: 8px;
|
| 42 |
+
overflow: hidden;
|
| 43 |
+
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
|
| 44 |
}
|
| 45 |
|
| 46 |
+
.poster {
|
| 47 |
+
display: block;
|
| 48 |
+
width: 100%;
|
| 49 |
+
height: auto;
|
| 50 |
+
aspect-ratio: 3 / 4;
|
| 51 |
+
object-fit: cover;
|
| 52 |
+
background: #222;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
.info {
|
| 56 |
+
margin-left: 20px;
|
| 57 |
+
color: #222;
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
.info h1 { margin: 0; font-size: 28px; font-weight: 700; }
|
| 61 |
+
.alt-title { margin: 8px 0; font-size: 15px; opacity: 0.75; font-style: italic; }
|
| 62 |
+
.info-table td { padding: 3px 8px 3px 0; vertical-align: top; font-size: 14px; }
|
| 63 |
+
.info-table .label { font-weight: 600; opacity: 0.75; white-space: nowrap; }
|
| 64 |
+
|
| 65 |
+
.description {
|
| 66 |
+
padding: 20px;
|
| 67 |
+
font-size: 16px;
|
| 68 |
+
line-height: 1.6;
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
.details {
|
| 72 |
+
display: flex;
|
| 73 |
+
flex-wrap: wrap;
|
| 74 |
+
padding: 0 20px 20px;
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
.detail-item {
|
| 78 |
+
flex: 1 1 200px;
|
| 79 |
+
margin: 10px;
|
| 80 |
+
background: #f0f0f0;
|
| 81 |
+
padding: 12px;
|
| 82 |
+
border-radius: 8px;
|
| 83 |
+
font-size: 14px;
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
.episodes {
|
| 87 |
+
padding: 20px;
|
| 88 |
+
background: #fafafa;
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
.episodes h2 {
|
| 92 |
+
margin-bottom: 15px;
|
| 93 |
+
font-size: 22px;
|
| 94 |
+
border-left: 4px solid var(--accent-color);
|
| 95 |
+
padding-left: 10px;
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
.lang-note {
|
| 99 |
+
display: inline-block;
|
| 100 |
+
margin: 0 0 12px;
|
| 101 |
+
padding: 6px 12px;
|
| 102 |
+
border-radius: 20px;
|
| 103 |
+
background: #fff3cd;
|
| 104 |
+
color: #856404;
|
| 105 |
+
border: 1px solid #ffd75e;
|
| 106 |
+
font-size: 13px;
|
| 107 |
+
font-weight: 600;
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
.episode-list {
|
| 111 |
+
display: grid;
|
| 112 |
+
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
| 113 |
+
gap: 15px;
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
.episode-card {
|
| 117 |
+
background: #fff;
|
| 118 |
+
border: 1px solid #ddd;
|
| 119 |
+
border-radius: 8px;
|
| 120 |
+
text-align: center;
|
| 121 |
+
padding: 10px;
|
| 122 |
+
cursor: pointer;
|
| 123 |
+
transition: transform 0.2s;
|
| 124 |
+
user-select: none;
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
.episode-card:hover {
|
| 128 |
+
transform: translateY(-5px);
|
| 129 |
+
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
.ep-num {
|
| 133 |
+
font-weight: 700;
|
| 134 |
+
font-size: 14px;
|
| 135 |
+
color: var(--ep-color);
|
| 136 |
+
}
|
| 137 |
+
|
| 138 |
+
.modal-overlay {
|
| 139 |
+
display: none;
|
| 140 |
+
position: fixed;
|
| 141 |
+
inset: 0;
|
| 142 |
+
background: rgba(0,0,0,0.88);
|
| 143 |
+
z-index: 1000;
|
| 144 |
+
justify-content: center;
|
| 145 |
+
align-items: center;
|
| 146 |
+
padding: 20px;
|
| 147 |
+
}
|
| 148 |
+
|
| 149 |
+
.modal-overlay.active { display: flex; }
|
| 150 |
+
|
| 151 |
+
.modal-content {
|
| 152 |
+
background: #111;
|
| 153 |
+
border-radius: 12px;
|
| 154 |
+
overflow: hidden;
|
| 155 |
+
width: 100%;
|
| 156 |
+
max-width: 960px;
|
| 157 |
+
box-shadow: 0 8px 40px rgba(0,0,0,0.6);
|
| 158 |
+
animation: modalIn 0.2s ease-out;
|
| 159 |
+
}
|
| 160 |
+
|
| 161 |
+
@keyframes modalIn {
|
| 162 |
+
from { opacity: 0; transform: scale(0.95); }
|
| 163 |
+
to { opacity: 1; transform: scale(1); }
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
.modal-header {
|
| 167 |
+
display: flex;
|
| 168 |
+
justify-content: space-between;
|
| 169 |
+
align-items: center;
|
| 170 |
+
padding: 10px 16px;
|
| 171 |
+
background: #1a1a1a;
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
.modal-title { font-weight: 700; font-size: 15px; color: #fff; }
|
| 175 |
+
|
| 176 |
+
.close-btn {
|
| 177 |
+
background: none;
|
| 178 |
+
border: none;
|
| 179 |
+
color: #999;
|
| 180 |
+
font-size: 28px;
|
| 181 |
+
cursor: pointer;
|
| 182 |
+
line-height: 1;
|
| 183 |
+
padding: 0 4px;
|
| 184 |
+
transition: color 0.2s;
|
| 185 |
+
}
|
| 186 |
+
|
| 187 |
+
.close-btn:hover { color: #fff; }
|
| 188 |
+
|
| 189 |
+
.modal-body {
|
| 190 |
+
position: relative;
|
| 191 |
+
width: 100%;
|
| 192 |
+
padding-top: 56.25%;
|
| 193 |
+
}
|
| 194 |
+
|
| 195 |
+
.modal-body iframe {
|
| 196 |
+
position: absolute;
|
| 197 |
+
inset: 0;
|
| 198 |
+
width: 100%;
|
| 199 |
+
height: 100%;
|
| 200 |
+
border: none;
|
| 201 |
+
}
|
| 202 |
+
|
| 203 |
+
.vi-popup-overlay {
|
| 204 |
+
display: none;
|
| 205 |
+
position: fixed;
|
| 206 |
+
inset: 0;
|
| 207 |
+
background: rgba(0,0,0,0.88);
|
| 208 |
+
z-index: 1000;
|
| 209 |
+
justify-content: center;
|
| 210 |
+
align-items: center;
|
| 211 |
+
padding: 20px;
|
| 212 |
+
}
|
| 213 |
+
|
| 214 |
+
.vi-popup-overlay.active { display: flex; }
|
| 215 |
+
|
| 216 |
+
.vi-popup {
|
| 217 |
+
background: #15151a;
|
| 218 |
+
border: 1px solid #2a2a35;
|
| 219 |
+
border-radius: 14px;
|
| 220 |
+
max-width: 420px;
|
| 221 |
+
width: 100%;
|
| 222 |
+
padding: 26px 22px 22px;
|
| 223 |
+
text-align: center;
|
| 224 |
+
box-shadow: 0 10px 40px rgba(0,0,0,0.6);
|
| 225 |
+
animation: modalIn 0.2s ease-out;
|
| 226 |
+
}
|
| 227 |
+
|
| 228 |
+
.vi-popup .vi-popup-title { color: #fff; font-size: 17px; font-weight: 700; margin: 0 0 10px; }
|
| 229 |
+
.vi-popup .vi-popup-msg { color: #aaa; font-size: 14px; line-height: 1.65; margin: 0 0 20px; }
|
| 230 |
+
.vi-popup .vi-popup-actions { display: flex; flex-direction: column; gap: 10px; }
|
| 231 |
+
.vi-popup .vi-popup-actions button {
|
| 232 |
+
padding: 12px 16px;
|
| 233 |
+
border: none;
|
| 234 |
+
border-radius: 8px;
|
| 235 |
+
font-size: 14px;
|
| 236 |
+
font-weight: 700;
|
| 237 |
+
cursor: pointer;
|
| 238 |
+
font-family: inherit;
|
| 239 |
+
transition: background 0.2s, color 0.2s;
|
| 240 |
+
}
|
| 241 |
+
.vi-popup .vi-continue { background: #667eea; color: #fff; }
|
| 242 |
+
.vi-popup .vi-continue:hover { background: #7a90f5; }
|
| 243 |
+
.vi-popup .vi-cancel { background: #23232b; color: #ccc; }
|
| 244 |
+
.vi-popup .vi-cancel:hover { color: #fff; }
|
| 245 |
+
.vi-popup a.vi-cancel { display: block; text-decoration: none; text-align: center; }
|
| 246 |
+
|
| 247 |
+
/* Home page */
|
| 248 |
+
.home-header {
|
| 249 |
+
background: linear-gradient(135deg, #667eea, #764ba2);
|
| 250 |
+
padding: 40px 20px;
|
| 251 |
+
text-align: center;
|
| 252 |
+
}
|
| 253 |
+
.site-title {
|
| 254 |
+
font-size: 36px;
|
| 255 |
+
font-weight: 700;
|
| 256 |
+
margin-bottom: 16px;
|
| 257 |
+
letter-spacing: 3px;
|
| 258 |
+
text-shadow: 1px 1px 2px rgba(0,0,0,0.1);
|
| 259 |
+
color: #fff;
|
| 260 |
+
}
|
| 261 |
+
.site-desc {
|
| 262 |
+
color: rgba(255,255,255,0.85);
|
| 263 |
+
font-size: 14px;
|
| 264 |
+
margin-bottom: 20px;
|
| 265 |
+
}
|
| 266 |
+
.search-box {
|
| 267 |
+
display: flex;
|
| 268 |
+
max-width: 400px;
|
| 269 |
+
margin: 0 auto;
|
| 270 |
+
border-radius: 8px;
|
| 271 |
+
overflow: hidden;
|
| 272 |
+
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
| 273 |
+
}
|
| 274 |
+
.search-box input {
|
| 275 |
+
flex: 1;
|
| 276 |
+
padding: 12px 16px;
|
| 277 |
+
border: none;
|
| 278 |
+
outline: none;
|
| 279 |
+
font-size: 15px;
|
| 280 |
+
font-family: inherit;
|
| 281 |
+
}
|
| 282 |
+
.search-box button {
|
| 283 |
+
padding: 12px 18px;
|
| 284 |
+
border: none;
|
| 285 |
+
background: #222;
|
| 286 |
+
color: #fff;
|
| 287 |
+
cursor: pointer;
|
| 288 |
+
font-size: 16px;
|
| 289 |
+
}
|
| 290 |
+
.search-box button:hover { background: #000; }
|
| 291 |
+
|
| 292 |
+
.home-nav {
|
| 293 |
+
display: flex;
|
| 294 |
+
gap: 8px;
|
| 295 |
+
padding: 16px 20px;
|
| 296 |
+
border-bottom: 1px solid #eee;
|
| 297 |
+
flex-wrap: wrap;
|
| 298 |
+
}
|
| 299 |
+
.home-nav a {
|
| 300 |
+
text-decoration: none;
|
| 301 |
+
color: #555;
|
| 302 |
+
padding: 6px 14px;
|
| 303 |
+
border-radius: 4px;
|
| 304 |
+
font-size: 14px;
|
| 305 |
+
transition: background 0.2s;
|
| 306 |
+
cursor: pointer;
|
| 307 |
+
}
|
| 308 |
+
.home-nav a:hover,
|
| 309 |
+
.home-nav a.active { background: #667eea; color: #fff; }
|
| 310 |
+
|
| 311 |
+
.random-section {
|
| 312 |
+
text-align: center;
|
| 313 |
+
padding: 12px 20px;
|
| 314 |
+
border-bottom: 1px solid #eee;
|
| 315 |
+
}
|
| 316 |
+
.random-section button {
|
| 317 |
+
padding: 10px 24px;
|
| 318 |
+
font-size: 15px;
|
| 319 |
+
font-weight: 600;
|
| 320 |
+
border: none;
|
| 321 |
+
border-radius: 8px;
|
| 322 |
+
background: linear-gradient(135deg, #667eea, #764ba2);
|
| 323 |
+
color: #fff;
|
| 324 |
+
cursor: pointer;
|
| 325 |
+
transition: transform 0.2s, box-shadow 0.2s;
|
| 326 |
+
font-family: inherit;
|
| 327 |
+
}
|
| 328 |
+
.random-section button:hover {
|
| 329 |
+
transform: translateY(-2px);
|
| 330 |
+
box-shadow: 0 6px 20px rgba(102,126,234,0.4);
|
| 331 |
+
}
|
| 332 |
+
|
| 333 |
+
.stats-bar {
|
| 334 |
+
display: flex;
|
| 335 |
+
gap: 12px;
|
| 336 |
+
padding: 10px 20px;
|
| 337 |
+
background: #f9f9f9;
|
| 338 |
+
border-bottom: 1px solid #eee;
|
| 339 |
+
font-size: 13px;
|
| 340 |
+
color: #666;
|
| 341 |
+
flex-wrap: wrap;
|
| 342 |
+
justify-content: center;
|
| 343 |
+
}
|
| 344 |
+
.stats-bar .stat-item span { font-weight: 700; color: #333; }
|
| 345 |
+
|
| 346 |
+
.category-section { margin: 0; }
|
| 347 |
+
.category-title {
|
| 348 |
+
font-size: 16px;
|
| 349 |
+
font-weight: 700;
|
| 350 |
+
padding: 12px 0 8px 10px;
|
| 351 |
+
margin: 16px 20px 0;
|
| 352 |
+
border-left: 4px solid var(--accent-color);
|
| 353 |
+
}
|
| 354 |
+
|
| 355 |
+
.movie-grid {
|
| 356 |
+
display: grid;
|
| 357 |
+
grid-template-columns: repeat(4, 1fr);
|
| 358 |
+
gap: 20px;
|
| 359 |
+
padding: 20px;
|
| 360 |
+
}
|
| 361 |
+
.movie-card {
|
| 362 |
+
background: #fff;
|
| 363 |
+
border-radius: 8px;
|
| 364 |
+
overflow: hidden;
|
| 365 |
+
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
| 366 |
+
transition: transform 0.2s;
|
| 367 |
+
text-decoration: none;
|
| 368 |
+
color: inherit;
|
| 369 |
+
display: block;
|
| 370 |
+
}
|
| 371 |
+
.movie-card:hover { transform: translateY(-4px); box-shadow: 0 6px 16px rgba(0,0,0,0.15); }
|
| 372 |
+
.movie-thumb {
|
| 373 |
+
position: relative;
|
| 374 |
+
width: 100%;
|
| 375 |
+
aspect-ratio: 3 / 4;
|
| 376 |
+
background: #222;
|
| 377 |
+
}
|
| 378 |
+
.movie-thumb img {
|
| 379 |
+
display: block;
|
| 380 |
+
width: 100%;
|
| 381 |
+
height: 100%;
|
| 382 |
+
object-fit: cover;
|
| 383 |
+
}
|
| 384 |
+
.movie-type {
|
| 385 |
+
position: absolute;
|
| 386 |
+
top: 6px;
|
| 387 |
+
left: 6px;
|
| 388 |
+
background: rgba(0,0,0,0.8);
|
| 389 |
+
color: #fff;
|
| 390 |
+
font-size: 10px;
|
| 391 |
+
padding: 2px 6px;
|
| 392 |
+
border-radius: 3px;
|
| 393 |
+
text-transform: uppercase;
|
| 394 |
+
}
|
| 395 |
+
.movie-ep {
|
| 396 |
+
position: absolute;
|
| 397 |
+
bottom: 6px;
|
| 398 |
+
left: 6px;
|
| 399 |
+
background: rgba(0,0,0,0.8);
|
| 400 |
+
color: #fff;
|
| 401 |
+
font-size: 11px;
|
| 402 |
+
padding: 2px 6px;
|
| 403 |
+
border-radius: 3px;
|
| 404 |
+
}
|
| 405 |
+
.movie-info { padding: 10px 12px; }
|
| 406 |
+
.movie-title {
|
| 407 |
+
font-size: 14px;
|
| 408 |
+
font-weight: 700;
|
| 409 |
+
margin-bottom: 4px;
|
| 410 |
+
display: -webkit-box;
|
| 411 |
+
-webkit-line-clamp: 2;
|
| 412 |
+
-webkit-box-orient: vertical;
|
| 413 |
+
overflow: hidden;
|
| 414 |
+
}
|
| 415 |
+
.movie-original { font-size: 12px; color: #888; }
|
| 416 |
+
|
| 417 |
+
/* Footer */
|
| 418 |
+
.footer {
|
| 419 |
+
text-align: center;
|
| 420 |
+
padding: 20px;
|
| 421 |
+
font-size: 12px;
|
| 422 |
+
color: #888;
|
| 423 |
+
border-top: 1px solid #eee;
|
| 424 |
+
margin-top: 20px;
|
| 425 |
+
line-height: 1.8;
|
| 426 |
+
}
|
| 427 |
+
|
| 428 |
+
/* Back link */
|
| 429 |
+
.back-link {
|
| 430 |
+
display: inline-flex;
|
| 431 |
+
align-items: center;
|
| 432 |
+
gap: 6px;
|
| 433 |
+
margin: 12px 0 12px 20px;
|
| 434 |
+
padding: 8px 16px;
|
| 435 |
+
text-decoration: none;
|
| 436 |
+
color: #667eea;
|
| 437 |
+
font-size: 13px;
|
| 438 |
+
font-weight: 600;
|
| 439 |
+
border-radius: 8px;
|
| 440 |
+
background: #f0f0ff;
|
| 441 |
+
border: 1px solid #e0e0ff;
|
| 442 |
+
transition: all 0.25s;
|
| 443 |
+
position: relative;
|
| 444 |
+
overflow: hidden;
|
| 445 |
+
z-index: 1;
|
| 446 |
+
}
|
| 447 |
+
.back-link::before {
|
| 448 |
+
content: '';
|
| 449 |
+
position: absolute;
|
| 450 |
+
inset: 0;
|
| 451 |
+
background: linear-gradient(135deg, #667eea18, #764ba218);
|
| 452 |
+
z-index: -1;
|
| 453 |
+
opacity: 0;
|
| 454 |
+
transition: opacity 0.25s;
|
| 455 |
+
border-radius: 8px;
|
| 456 |
+
}
|
| 457 |
+
.back-link:hover {
|
| 458 |
+
transform: translateX(-3px);
|
| 459 |
+
border-color: #667eea;
|
| 460 |
+
color: #764ba2;
|
| 461 |
+
}
|
| 462 |
+
.back-link:hover::before {
|
| 463 |
+
opacity: 1;
|
| 464 |
+
}
|
| 465 |
+
|
| 466 |
+
.next-link {
|
| 467 |
+
margin-left: 4px;
|
| 468 |
+
}
|
| 469 |
+
.next-link:hover {
|
| 470 |
+
transform: translateX(3px);
|
| 471 |
+
}
|
| 472 |
+
|
| 473 |
+
/* UI Toggle */
|
| 474 |
+
.ui-toggle {
|
| 475 |
+
display: flex;
|
| 476 |
+
align-items: center;
|
| 477 |
+
justify-content: center;
|
| 478 |
+
gap: 6px;
|
| 479 |
+
margin-top: 14px;
|
| 480 |
+
}
|
| 481 |
+
.ui-btn {
|
| 482 |
+
padding: 6px 14px;
|
| 483 |
+
border: 1px solid rgba(255,255,255,0.3);
|
| 484 |
+
background: transparent;
|
| 485 |
+
color: rgba(255,255,255,0.7);
|
| 486 |
+
border-radius: 6px;
|
| 487 |
+
cursor: pointer;
|
| 488 |
+
font-size: 13px;
|
| 489 |
+
font-family: inherit;
|
| 490 |
+
transition: all 0.2s;
|
| 491 |
+
line-height: 1.4;
|
| 492 |
+
}
|
| 493 |
+
.ui-btn.active {
|
| 494 |
+
background: rgba(255,255,255,0.2);
|
| 495 |
+
color: #fff;
|
| 496 |
+
border-color: rgba(255,255,255,0.6);
|
| 497 |
+
box-shadow: 0 0 12px rgba(255,255,255,0.1);
|
| 498 |
+
}
|
| 499 |
+
.ui-btn:hover {
|
| 500 |
+
background: rgba(255,255,255,0.1);
|
| 501 |
+
}
|
| 502 |
+
|
| 503 |
+
/* Mobile UI */
|
| 504 |
+
body.ui-mobile {
|
| 505 |
+
padding: 0;
|
| 506 |
+
background: #fff;
|
| 507 |
+
}
|
| 508 |
+
body.ui-mobile .container {
|
| 509 |
+
margin: 0;
|
| 510 |
+
border-radius: 0;
|
| 511 |
+
box-shadow: none;
|
| 512 |
+
}
|
| 513 |
+
body.ui-mobile .home-header {
|
| 514 |
+
padding: 24px 12px 16px;
|
| 515 |
+
}
|
| 516 |
+
body.ui-mobile .site-title {
|
| 517 |
+
font-size: 24px;
|
| 518 |
+
margin-bottom: 8px;
|
| 519 |
+
}
|
| 520 |
+
body.ui-mobile .site-desc {
|
| 521 |
+
font-size: 12px;
|
| 522 |
+
margin-bottom: 12px;
|
| 523 |
+
}
|
| 524 |
+
body.ui-mobile .search-box {
|
| 525 |
+
max-width: 100%;
|
| 526 |
+
}
|
| 527 |
+
body.ui-mobile .search-box input {
|
| 528 |
+
padding: 10px 12px;
|
| 529 |
+
font-size: 14px;
|
| 530 |
+
}
|
| 531 |
+
body.ui-mobile .search-box button {
|
| 532 |
+
padding: 10px 14px;
|
| 533 |
+
}
|
| 534 |
+
body.ui-mobile .ui-toggle {
|
| 535 |
+
margin-top: 10px;
|
| 536 |
+
}
|
| 537 |
+
body.ui-mobile .ui-btn {
|
| 538 |
+
padding: 5px 12px;
|
| 539 |
+
font-size: 12px;
|
| 540 |
+
}
|
| 541 |
+
body.ui-mobile .settings-link {
|
| 542 |
+
font-size: 17px;
|
| 543 |
+
}
|
| 544 |
+
body.ui-mobile .stats-bar {
|
| 545 |
+
font-size: 11px;
|
| 546 |
+
gap: 6px;
|
| 547 |
+
padding: 8px;
|
| 548 |
+
}
|
| 549 |
+
body.ui-mobile .random-section {
|
| 550 |
+
padding: 8px 12px;
|
| 551 |
+
}
|
| 552 |
+
body.ui-mobile .random-section button {
|
| 553 |
+
padding: 8px 18px;
|
| 554 |
+
font-size: 13px;
|
| 555 |
+
width: 100%;
|
| 556 |
+
max-width: 300px;
|
| 557 |
+
}
|
| 558 |
+
body.ui-mobile .home-nav {
|
| 559 |
+
overflow-x: auto;
|
| 560 |
+
flex-wrap: nowrap;
|
| 561 |
+
padding: 10px 8px;
|
| 562 |
+
gap: 4px;
|
| 563 |
+
-webkit-overflow-scrolling: touch;
|
| 564 |
+
}
|
| 565 |
+
body.ui-mobile .home-nav a {
|
| 566 |
+
white-space: nowrap;
|
| 567 |
+
padding: 8px 14px;
|
| 568 |
+
font-size: 13px;
|
| 569 |
+
flex-shrink: 0;
|
| 570 |
+
}
|
| 571 |
+
body.ui-mobile .movie-grid {
|
| 572 |
+
grid-template-columns: repeat(2, 1fr);
|
| 573 |
+
gap: 10px;
|
| 574 |
+
padding: 10px;
|
| 575 |
+
}
|
| 576 |
+
body.ui-mobile .movie-card {
|
| 577 |
+
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
| 578 |
+
border-radius: 10px;
|
| 579 |
+
}
|
| 580 |
+
body.ui-mobile .movie-thumb {
|
| 581 |
+
border-radius: 10px 10px 0 0;
|
| 582 |
+
}
|
| 583 |
+
body.ui-mobile .movie-card:hover {
|
| 584 |
+
transform: none;
|
| 585 |
+
}
|
| 586 |
+
body.ui-mobile .movie-title {
|
| 587 |
+
font-size: 13px;
|
| 588 |
+
}
|
| 589 |
+
body.ui-mobile .movie-original {
|
| 590 |
+
font-size: 11px;
|
| 591 |
+
}
|
| 592 |
+
body.ui-mobile .movie-ep {
|
| 593 |
+
font-size: 10px;
|
| 594 |
+
bottom: 4px;
|
| 595 |
+
left: 4px;
|
| 596 |
+
}
|
| 597 |
+
body.ui-mobile .movie-type {
|
| 598 |
+
font-size: 9px;
|
| 599 |
+
top: 4px;
|
| 600 |
+
left: 4px;
|
| 601 |
+
}
|
| 602 |
+
body.ui-mobile .pagination {
|
| 603 |
+
gap: 4px;
|
| 604 |
+
padding: 12px 8px;
|
| 605 |
+
}
|
| 606 |
+
body.ui-mobile .pg-btn {
|
| 607 |
+
min-width: 32px;
|
| 608 |
+
height: 32px;
|
| 609 |
+
font-size: 13px;
|
| 610 |
+
}
|
| 611 |
+
body.ui-mobile .category-title {
|
| 612 |
+
margin: 8px 8px 0;
|
| 613 |
+
font-size: 14px;
|
| 614 |
+
padding: 8px 0 6px 8px;
|
| 615 |
+
}
|
| 616 |
+
body.ui-mobile .footer {
|
| 617 |
+
padding: 14px 12px;
|
| 618 |
+
font-size: 11px;
|
| 619 |
+
}
|
| 620 |
+
|
| 621 |
+
/* Mobile detail page */
|
| 622 |
+
body.ui-mobile .header {
|
| 623 |
+
flex-direction: column;
|
| 624 |
+
align-items: center;
|
| 625 |
+
text-align: center;
|
| 626 |
+
padding: 16px;
|
| 627 |
+
}
|
| 628 |
+
body.ui-mobile .poster-wrapper {
|
| 629 |
+
width: 160px;
|
| 630 |
+
border-radius: 10px;
|
| 631 |
+
}
|
| 632 |
+
body.ui-mobile .info {
|
| 633 |
+
margin-left: 0;
|
| 634 |
+
margin-top: 12px;
|
| 635 |
+
}
|
| 636 |
+
body.ui-mobile .info h1 {
|
| 637 |
+
font-size: 20px;
|
| 638 |
+
}
|
| 639 |
+
body.ui-mobile .info-table td {
|
| 640 |
+
font-size: 13px;
|
| 641 |
+
}
|
| 642 |
+
body.ui-mobile .description {
|
| 643 |
+
padding: 16px;
|
| 644 |
+
font-size: 14px;
|
| 645 |
+
}
|
| 646 |
+
body.ui-mobile .details {
|
| 647 |
+
padding: 0 16px 16px;
|
| 648 |
+
}
|
| 649 |
+
body.ui-mobile .detail-item {
|
| 650 |
+
flex: 1 1 100%;
|
| 651 |
+
margin: 4px 0;
|
| 652 |
+
padding: 10px;
|
| 653 |
+
font-size: 13px;
|
| 654 |
+
}
|
| 655 |
+
body.ui-mobile .episodes {
|
| 656 |
+
padding: 16px;
|
| 657 |
+
}
|
| 658 |
+
body.ui-mobile .episodes h2 {
|
| 659 |
+
font-size: 18px;
|
| 660 |
+
}
|
| 661 |
+
body.ui-mobile .episode-list {
|
| 662 |
+
grid-template-columns: repeat(3, 1fr);
|
| 663 |
+
gap: 8px;
|
| 664 |
+
}
|
| 665 |
+
body.ui-mobile .episode-card {
|
| 666 |
+
padding: 8px 4px;
|
| 667 |
+
}
|
| 668 |
+
body.ui-mobile .episode-card:hover {
|
| 669 |
+
transform: none;
|
| 670 |
+
}
|
| 671 |
+
body.ui-mobile .ep-num {
|
| 672 |
+
font-size: 12px;
|
| 673 |
+
}
|
| 674 |
+
body.ui-mobile .back-link {
|
| 675 |
+
margin: 8px 0 8px 12px;
|
| 676 |
+
padding: 6px 12px;
|
| 677 |
+
font-size: 12px;
|
| 678 |
+
}
|
| 679 |
+
body.ui-mobile .modal-overlay {
|
| 680 |
+
padding: 4px;
|
| 681 |
+
}
|
| 682 |
+
body.ui-mobile .modal-content {
|
| 683 |
+
border-radius: 8px;
|
| 684 |
+
}
|
| 685 |
+
body.ui-mobile .modal-header {
|
| 686 |
+
padding: 8px 12px;
|
| 687 |
+
}
|
| 688 |
+
body.ui-mobile .modal-title {
|
| 689 |
+
font-size: 13px;
|
| 690 |
+
}
|
| 691 |
+
|
| 692 |
+
/* Series hub */
|
| 693 |
+
.series-header {
|
| 694 |
+
display: flex;
|
| 695 |
+
gap: 20px;
|
| 696 |
+
padding: 20px;
|
| 697 |
+
background: linear-gradient(135deg, #667eea22, #764ba222);
|
| 698 |
+
align-items: center;
|
| 699 |
+
}
|
| 700 |
+
.series-poster {
|
| 701 |
+
width: 160px;
|
| 702 |
+
flex-shrink: 0;
|
| 703 |
+
border-radius: 8px;
|
| 704 |
+
overflow: hidden;
|
| 705 |
+
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
| 706 |
+
}
|
| 707 |
+
.series-poster img {
|
| 708 |
+
width: 100%;
|
| 709 |
+
display: block;
|
| 710 |
+
aspect-ratio: 3/4;
|
| 711 |
+
object-fit: cover;
|
| 712 |
+
}
|
| 713 |
+
.series-info h1 {
|
| 714 |
+
font-size: 26px;
|
| 715 |
+
margin-bottom: 6px;
|
| 716 |
+
}
|
| 717 |
+
.series-desc {
|
| 718 |
+
margin-top: 10px;
|
| 719 |
+
font-size: 14px;
|
| 720 |
+
color: #555;
|
| 721 |
+
line-height: 1.6;
|
| 722 |
+
}
|
| 723 |
+
.series-grid {
|
| 724 |
+
display: grid;
|
| 725 |
+
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
| 726 |
+
gap: 16px;
|
| 727 |
+
padding: 20px;
|
| 728 |
+
}
|
| 729 |
+
.series-card {
|
| 730 |
+
text-decoration: none;
|
| 731 |
+
color: inherit;
|
| 732 |
+
border-radius: 10px;
|
| 733 |
+
overflow: hidden;
|
| 734 |
+
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
| 735 |
+
transition: transform 0.25s, box-shadow 0.25s;
|
| 736 |
+
border-bottom: 3px solid var(--card-color);
|
| 737 |
+
background: #fff;
|
| 738 |
+
}
|
| 739 |
+
.series-card:hover {
|
| 740 |
+
transform: translateY(-5px);
|
| 741 |
+
box-shadow: 0 8px 20px rgba(0,0,0,0.12);
|
| 742 |
+
}
|
| 743 |
+
.series-card-thumb {
|
| 744 |
+
width: 100%;
|
| 745 |
+
aspect-ratio: 3/4;
|
| 746 |
+
overflow: hidden;
|
| 747 |
+
background: #222;
|
| 748 |
+
}
|
| 749 |
+
.series-card-thumb img {
|
| 750 |
+
width: 100%;
|
| 751 |
+
height: 100%;
|
| 752 |
+
object-fit: cover;
|
| 753 |
+
display: block;
|
| 754 |
+
}
|
| 755 |
+
.series-card-body {
|
| 756 |
+
padding: 10px 12px;
|
| 757 |
+
}
|
| 758 |
+
.series-card-body h3 {
|
| 759 |
+
font-size: 14px;
|
| 760 |
+
font-weight: 700;
|
| 761 |
+
margin-bottom: 4px;
|
| 762 |
+
}
|
| 763 |
+
.series-card-body p {
|
| 764 |
+
font-size: 12px;
|
| 765 |
+
color: #888;
|
| 766 |
+
}
|
| 767 |
+
|
| 768 |
+
/* Pagination */
|
| 769 |
+
.pagination {
|
| 770 |
+
display: flex;
|
| 771 |
+
justify-content: center;
|
| 772 |
+
align-items: center;
|
| 773 |
+
gap: 6px;
|
| 774 |
+
padding: 20px;
|
| 775 |
+
flex-wrap: wrap;
|
| 776 |
+
}
|
| 777 |
+
.pg-btn {
|
| 778 |
+
min-width: 36px;
|
| 779 |
+
height: 36px;
|
| 780 |
+
border: 1px solid #ddd;
|
| 781 |
+
border-radius: 6px;
|
| 782 |
+
background: #fff;
|
| 783 |
+
color: #555;
|
| 784 |
+
font-size: 14px;
|
| 785 |
+
font-weight: 600;
|
| 786 |
+
cursor: pointer;
|
| 787 |
+
transition: all 0.2s;
|
| 788 |
+
font-family: inherit;
|
| 789 |
+
display: inline-flex;
|
| 790 |
+
align-items: center;
|
| 791 |
+
justify-content: center;
|
| 792 |
+
}
|
| 793 |
+
.pg-btn:hover:not(.disabled):not(.active) {
|
| 794 |
+
border-color: #667eea;
|
| 795 |
+
color: #667eea;
|
| 796 |
+
}
|
| 797 |
+
.pg-btn.active {
|
| 798 |
+
background: #667eea;
|
| 799 |
+
border-color: #667eea;
|
| 800 |
+
color: #fff;
|
| 801 |
+
}
|
| 802 |
+
.pg-btn.disabled {
|
| 803 |
+
opacity: 0.35;
|
| 804 |
+
cursor: default;
|
| 805 |
+
}
|
| 806 |
+
|
| 807 |
+
@media (max-width: 700px) {
|
| 808 |
+
body { padding: 10px; }
|
| 809 |
+
.container { margin: 16px auto; }
|
| 810 |
+
.header { flex-direction: column; align-items: center; text-align: center; padding: 20px; }
|
| 811 |
+
.poster-wrapper { width: 160px; border-radius: 10px; }
|
| 812 |
+
.info { margin-left: 0; }
|
| 813 |
+
.episode-list { grid-template-columns: repeat(auto-fill, minmax(90px, 1fr)); }
|
| 814 |
+
.modal-overlay { padding: 8px; }
|
| 815 |
+
.movie-grid { grid-template-columns: repeat(3, 1fr); gap: 14px; padding: 14px; }
|
| 816 |
+
.movie-card { border-radius: 10px; }
|
| 817 |
+
.movie-thumb { border-radius: 10px 10px 0 0; }
|
| 818 |
+
.series-header { flex-direction: column; text-align: center; }
|
| 819 |
+
.series-poster { width: 140px; }
|
| 820 |
+
.series-info h1 { font-size: 20px; }
|
| 821 |
+
.series-grid { grid-template-columns: repeat(2, 1fr); gap: 10px; padding: 12px; }
|
| 822 |
+
}
|
| 823 |
+
|
| 824 |
+
@media (max-width: 480px) {
|
| 825 |
+
.movie-grid { grid-template-columns: repeat(2, 1fr); gap: 10px; padding: 10px; }
|
| 826 |
+
.movie-card { border-radius: 10px; }
|
| 827 |
+
.movie-thumb { border-radius: 10px 10px 0 0; }
|
| 828 |
+
}
|
| 829 |
+
|
| 830 |
+
/* Settings link */
|
| 831 |
+
.settings-link {
|
| 832 |
+
color: rgba(255,255,255,0.6);
|
| 833 |
+
text-decoration: none;
|
| 834 |
+
font-size: 20px;
|
| 835 |
+
line-height: 1;
|
| 836 |
+
transition: color 0.2s, transform 0.2s;
|
| 837 |
+
display: inline-flex;
|
| 838 |
+
align-items: center;
|
| 839 |
+
}
|
| 840 |
+
.settings-link:hover {
|
| 841 |
+
color: #fff;
|
| 842 |
+
transform: rotate(60deg);
|
| 843 |
+
}
|
| 844 |
+
|
| 845 |
+
/* Settings page */
|
| 846 |
+
.settings-page {
|
| 847 |
+
display: flex;
|
| 848 |
+
justify-content: center;
|
| 849 |
+
align-items: center;
|
| 850 |
+
min-height: 300px;
|
| 851 |
+
padding: 40px 20px;
|
| 852 |
+
}
|
| 853 |
+
.settings-box {
|
| 854 |
+
background: #f9f9f9;
|
| 855 |
+
border-radius: 12px;
|
| 856 |
+
padding: 40px;
|
| 857 |
+
text-align: center;
|
| 858 |
+
max-width: 400px;
|
| 859 |
+
width: 100%;
|
| 860 |
+
box-shadow: 0 2px 12px rgba(0,0,0,0.06);
|
| 861 |
+
}
|
| 862 |
+
.settings-title {
|
| 863 |
+
font-size: 22px;
|
| 864 |
+
margin-bottom: 24px;
|
| 865 |
+
color: #333;
|
| 866 |
+
}
|
| 867 |
+
.settings-field {
|
| 868 |
+
display: flex;
|
| 869 |
+
gap: 8px;
|
| 870 |
+
}
|
| 871 |
+
.settings-field input {
|
| 872 |
+
flex: 1;
|
| 873 |
+
padding: 12px 16px;
|
| 874 |
+
border: 2px solid #ddd;
|
| 875 |
+
border-radius: 8px;
|
| 876 |
+
font-size: 15px;
|
| 877 |
+
font-family: inherit;
|
| 878 |
+
outline: none;
|
| 879 |
+
transition: border-color 0.2s;
|
| 880 |
+
}
|
| 881 |
+
.settings-field input:focus {
|
| 882 |
+
border-color: #667eea;
|
| 883 |
+
}
|
| 884 |
+
.settings-field button {
|
| 885 |
+
padding: 12px 20px;
|
| 886 |
+
border: none;
|
| 887 |
+
border-radius: 8px;
|
| 888 |
+
background: linear-gradient(135deg, #667eea, #764ba2);
|
| 889 |
+
color: #fff;
|
| 890 |
+
font-size: 15px;
|
| 891 |
+
font-weight: 600;
|
| 892 |
+
cursor: pointer;
|
| 893 |
+
font-family: inherit;
|
| 894 |
+
transition: transform 0.2s;
|
| 895 |
+
}
|
| 896 |
+
.settings-field button:hover {
|
| 897 |
+
transform: translateY(-2px);
|
| 898 |
+
}
|
| 899 |
+
.settings-error {
|
| 900 |
+
color: #e74c3c;
|
| 901 |
+
font-size: 13px;
|
| 902 |
+
margin-top: 12px;
|
| 903 |
+
min-height: 20px;
|
| 904 |
+
}
|
| 905 |
+
body.ui-mobile .settings-box {
|
| 906 |
+
padding: 24px 16px;
|
| 907 |
+
}
|
| 908 |
+
body.ui-mobile .settings-field {
|
| 909 |
+
flex-direction: column;
|
| 910 |
+
}
|
| 911 |
+
body.ui-mobile .settings-title {
|
| 912 |
+
font-size: 18px;
|
| 913 |
+
}
|
| 914 |
+
|
| 915 |
+
/* Age confirmation modal */
|
| 916 |
+
.age-modal-content {
|
| 917 |
+
max-width: 420px !important;
|
| 918 |
+
}
|
| 919 |
+
.age-modal-body {
|
| 920 |
+
padding: 30px 24px;
|
| 921 |
+
text-align: center;
|
| 922 |
+
background: #1a1a1a;
|
| 923 |
+
}
|
| 924 |
+
.age-warning-icon {
|
| 925 |
+
font-size: 48px;
|
| 926 |
+
color: #ff4444;
|
| 927 |
+
margin-bottom: 12px;
|
| 928 |
+
}
|
| 929 |
+
.age-warning-text {
|
| 930 |
+
font-size: 20px;
|
| 931 |
+
font-weight: 700;
|
| 932 |
+
color: #fff;
|
| 933 |
+
margin-bottom: 8px;
|
| 934 |
+
}
|
| 935 |
+
.age-warning-desc {
|
| 936 |
+
font-size: 13px;
|
| 937 |
+
color: #999;
|
| 938 |
+
margin-bottom: 24px;
|
| 939 |
+
line-height: 1.5;
|
| 940 |
+
}
|
| 941 |
+
.age-buttons {
|
| 942 |
+
display: flex;
|
| 943 |
+
gap: 12px;
|
| 944 |
+
justify-content: center;
|
| 945 |
+
}
|
| 946 |
+
.age-btn {
|
| 947 |
+
padding: 12px 24px;
|
| 948 |
+
border: none;
|
| 949 |
+
border-radius: 8px;
|
| 950 |
+
font-size: 15px;
|
| 951 |
+
font-weight: 600;
|
| 952 |
+
cursor: pointer;
|
| 953 |
+
font-family: inherit;
|
| 954 |
+
transition: transform 0.2s, box-shadow 0.2s;
|
| 955 |
+
}
|
| 956 |
+
.age-btn:hover {
|
| 957 |
+
transform: translateY(-2px);
|
| 958 |
+
}
|
| 959 |
+
.age-btn-yes {
|
| 960 |
+
background: linear-gradient(135deg, #28a745, #20c997);
|
| 961 |
+
color: #fff;
|
| 962 |
+
}
|
| 963 |
+
.age-btn-yes:hover {
|
| 964 |
+
box-shadow: 0 4px 16px rgba(40,167,69,0.4);
|
| 965 |
+
}
|
| 966 |
+
.age-btn-no {
|
| 967 |
+
background: linear-gradient(135deg, #dc3545, #c82333);
|
| 968 |
+
color: #fff;
|
| 969 |
+
}
|
| 970 |
+
.age-btn-no:hover {
|
| 971 |
+
box-shadow: 0 4px 16px rgba(220,53,69,0.4);
|
| 972 |
+
}
|
| 973 |
+
body.ui-mobile .age-modal-body {
|
| 974 |
+
padding: 20px 16px;
|
| 975 |
+
}
|
| 976 |
+
body.ui-mobile .age-warning-text {
|
| 977 |
+
font-size: 17px;
|
| 978 |
+
}
|
| 979 |
+
body.ui-mobile .age-btn {
|
| 980 |
+
font-size: 13px;
|
| 981 |
+
padding: 10px 16px;
|
| 982 |
+
}
|
| 983 |
+
|
| 984 |
+
/* Adult card on homepage */
|
| 985 |
+
.adult-card {
|
| 986 |
+
border-bottom: 3px solid #cc0000 !important;
|
| 987 |
+
}
|
| 988 |
+
.adult-badge-tag {
|
| 989 |
+
background: rgba(200,0,0,0.9) !important;
|
| 990 |
+
}
|
| 991 |
+
|
| 992 |
+
/* Adult mode page (legacy) */
|
| 993 |
+
.adult-mode {
|
| 994 |
+
background: #0d0d0d;
|
| 995 |
+
}
|
| 996 |
+
.adult-container {
|
| 997 |
+
background: rgba(26,26,26,0.82);
|
| 998 |
+
backdrop-filter: blur(12px);
|
| 999 |
+
-webkit-backdrop-filter: blur(12px);
|
| 1000 |
+
box-shadow: 0 4px 20px rgba(255,0,0,0.08);
|
| 1001 |
+
}
|
| 1002 |
+
.adult-header {
|
| 1003 |
+
background: linear-gradient(135deg, #1a0000, #4a0000) !important;
|
| 1004 |
+
}
|
| 1005 |
+
.adult-title {
|
| 1006 |
+
color: #ff4444 !important;
|
| 1007 |
+
text-shadow: 0 0 20px rgba(255,0,0,0.3) !important;
|
| 1008 |
+
}
|
| 1009 |
+
.adult-badge {
|
| 1010 |
+
display: inline-block;
|
| 1011 |
+
background: #ff0000;
|
| 1012 |
+
color: #fff;
|
| 1013 |
+
font-size: 12px;
|
| 1014 |
+
padding: 2px 8px;
|
| 1015 |
+
border-radius: 4px;
|
| 1016 |
+
vertical-align: super;
|
| 1017 |
+
margin-left: 6px;
|
| 1018 |
+
font-weight: 700;
|
| 1019 |
+
letter-spacing: 1px;
|
| 1020 |
+
}
|
| 1021 |
+
.adult-desc {
|
| 1022 |
+
color: rgba(255,100,100,0.7) !important;
|
| 1023 |
+
}
|
| 1024 |
+
.adult-stats {
|
| 1025 |
+
background: #222 !important;
|
| 1026 |
+
border-bottom-color: #333 !important;
|
| 1027 |
+
color: #888 !important;
|
| 1028 |
+
}
|
| 1029 |
+
.adult-stats .stat-item span {
|
| 1030 |
+
color: #ff6666 !important;
|
| 1031 |
+
}
|
| 1032 |
+
.adult-random {
|
| 1033 |
+
border-bottom-color: #333 !important;
|
| 1034 |
+
}
|
| 1035 |
+
.adult-random button {
|
| 1036 |
+
background: linear-gradient(135deg, #cc0000, #660000) !important;
|
| 1037 |
+
}
|
| 1038 |
+
.adult-random button:hover {
|
| 1039 |
+
box-shadow: 0 6px 20px rgba(255,0,0,0.3) !important;
|
| 1040 |
+
}
|
| 1041 |
+
.adult-back-link {
|
| 1042 |
+
background: #222 !important;
|
| 1043 |
+
border-color: #440000 !important;
|
| 1044 |
+
color: #ff6666 !important;
|
| 1045 |
+
}
|
| 1046 |
+
.adult-back-link:hover {
|
| 1047 |
+
border-color: #ff0000 !important;
|
| 1048 |
+
color: #ff4444 !important;
|
| 1049 |
+
}
|
| 1050 |
+
.adult-card {
|
| 1051 |
+
border-bottom: 3px solid #cc0000 !important;
|
| 1052 |
+
}
|
| 1053 |
+
.adult-type-badge {
|
| 1054 |
+
background: rgba(200,0,0,0.9) !important;
|
| 1055 |
+
}
|
| 1056 |
+
.adult-footer {
|
| 1057 |
+
border-top-color: #333 !important;
|
| 1058 |
+
color: #666 !important;
|
| 1059 |
+
}
|
| 1060 |
+
body.ui-mobile.adult-mode {
|
| 1061 |
+
background: #0d0d0d;
|
| 1062 |
+
}
|
| 1063 |
+
|
| 1064 |
+
/* Light Novel list view */
|
| 1065 |
+
.ln-list {
|
| 1066 |
+
padding: 16px 20px;
|
| 1067 |
+
display: flex;
|
| 1068 |
+
flex-direction: column;
|
| 1069 |
+
gap: 12px;
|
| 1070 |
+
}
|
| 1071 |
+
.ln-list-item {
|
| 1072 |
+
display: flex;
|
| 1073 |
+
gap: 16px;
|
| 1074 |
+
background: #fff;
|
| 1075 |
+
border-radius: 10px;
|
| 1076 |
+
padding: 14px;
|
| 1077 |
+
box-shadow: 0 1px 6px rgba(0,0,0,0.07);
|
| 1078 |
+
cursor: pointer;
|
| 1079 |
+
transition: transform 0.2s, box-shadow 0.2s;
|
| 1080 |
+
border: 1px solid #eee;
|
| 1081 |
+
}
|
| 1082 |
+
.ln-list-item:hover {
|
| 1083 |
+
transform: translateY(-2px);
|
| 1084 |
+
box-shadow: 0 4px 14px rgba(0,0,0,0.1);
|
| 1085 |
+
}
|
| 1086 |
+
.ln-cover {
|
| 1087 |
+
width: 100px;
|
| 1088 |
+
flex-shrink: 0;
|
| 1089 |
+
border-radius: 6px;
|
| 1090 |
+
overflow: hidden;
|
| 1091 |
+
}
|
| 1092 |
+
.ln-cover img {
|
| 1093 |
+
width: 100%;
|
| 1094 |
+
display: block;
|
| 1095 |
+
aspect-ratio: 3/4;
|
| 1096 |
+
object-fit: cover;
|
| 1097 |
+
background: #222;
|
| 1098 |
+
}
|
| 1099 |
+
.ln-info {
|
| 1100 |
+
flex: 1;
|
| 1101 |
+
min-width: 0;
|
| 1102 |
+
}
|
| 1103 |
+
.ln-title {
|
| 1104 |
+
font-size: 16px;
|
| 1105 |
+
font-weight: 700;
|
| 1106 |
+
margin-bottom: 4px;
|
| 1107 |
+
color: #222;
|
| 1108 |
+
}
|
| 1109 |
+
.ln-author {
|
| 1110 |
+
font-size: 12px;
|
| 1111 |
+
color: #888;
|
| 1112 |
+
margin-bottom: 6px;
|
| 1113 |
+
}
|
| 1114 |
+
.ln-genres {
|
| 1115 |
+
display: flex;
|
| 1116 |
+
flex-wrap: wrap;
|
| 1117 |
+
gap: 4px;
|
| 1118 |
+
margin-bottom: 6px;
|
| 1119 |
+
}
|
| 1120 |
+
.ln-genre-tag {
|
| 1121 |
+
display: inline-block;
|
| 1122 |
+
padding: 2px 8px;
|
| 1123 |
+
border-radius: 4px;
|
| 1124 |
+
background: #667eea;
|
| 1125 |
+
color: #fff;
|
| 1126 |
+
font-size: 10px;
|
| 1127 |
+
font-weight: 600;
|
| 1128 |
+
}
|
| 1129 |
+
.ln-desc {
|
| 1130 |
+
font-size: 13px;
|
| 1131 |
+
color: #666;
|
| 1132 |
+
line-height: 1.5;
|
| 1133 |
+
display: -webkit-box;
|
| 1134 |
+
-webkit-line-clamp: 2;
|
| 1135 |
+
-webkit-box-orient: vertical;
|
| 1136 |
+
overflow: hidden;
|
| 1137 |
+
}
|
| 1138 |
+
.ln-status {
|
| 1139 |
+
display: inline-block;
|
| 1140 |
+
margin-top: 6px;
|
| 1141 |
+
font-size: 11px;
|
| 1142 |
+
color: #28a745;
|
| 1143 |
+
font-weight: 600;
|
| 1144 |
+
}
|
| 1145 |
+
body.ui-mobile .ln-list {
|
| 1146 |
+
padding: 10px 12px;
|
| 1147 |
+
gap: 10px;
|
| 1148 |
+
}
|
| 1149 |
+
body.ui-mobile .ln-list-item {
|
| 1150 |
+
padding: 10px;
|
| 1151 |
+
}
|
| 1152 |
+
body.ui-mobile .ln-cover {
|
| 1153 |
+
width: 80px;
|
| 1154 |
+
}
|
| 1155 |
+
body.ui-mobile .ln-title {
|
| 1156 |
+
font-size: 14px;
|
| 1157 |
+
}
|
| 1158 |
+
body.ui-mobile .ln-desc {
|
| 1159 |
+
font-size: 12px;
|
| 1160 |
+
-webkit-line-clamp: 1;
|
| 1161 |
+
}
|
| 1162 |
+
|
| 1163 |
+
/* LN Detail modal */
|
| 1164 |
+
.ln-detail-content {
|
| 1165 |
+
max-width: 700px !important;
|
| 1166 |
+
}
|
| 1167 |
+
.ln-detail-body {
|
| 1168 |
+
padding: 20px;
|
| 1169 |
+
background: #1a1a1a;
|
| 1170 |
+
max-height: 80vh;
|
| 1171 |
+
overflow-y: auto;
|
| 1172 |
+
}
|
| 1173 |
+
.ln-detail-header {
|
| 1174 |
+
display: flex;
|
| 1175 |
+
gap: 20px;
|
| 1176 |
+
margin-bottom: 24px;
|
| 1177 |
+
}
|
| 1178 |
+
.ln-detail-cover {
|
| 1179 |
+
width: 160px;
|
| 1180 |
+
flex-shrink: 0;
|
| 1181 |
+
border-radius: 8px;
|
| 1182 |
+
aspect-ratio: 3/4;
|
| 1183 |
+
object-fit: cover;
|
| 1184 |
+
background: #222;
|
| 1185 |
+
}
|
| 1186 |
+
.ln-detail-info {
|
| 1187 |
+
flex: 1;
|
| 1188 |
+
min-width: 0;
|
| 1189 |
+
}
|
| 1190 |
+
.ln-detail-info h2 {
|
| 1191 |
+
font-size: 22px;
|
| 1192 |
+
color: #fff;
|
| 1193 |
+
margin-bottom: 6px;
|
| 1194 |
+
}
|
| 1195 |
+
.ln-detail-author {
|
| 1196 |
+
font-size: 13px;
|
| 1197 |
+
color: #aaa;
|
| 1198 |
+
margin-bottom: 8px;
|
| 1199 |
+
}
|
| 1200 |
+
.ln-detail-genres {
|
| 1201 |
+
margin-bottom: 10px;
|
| 1202 |
+
}
|
| 1203 |
+
.ln-detail-desc {
|
| 1204 |
+
font-size: 14px;
|
| 1205 |
+
color: #ccc;
|
| 1206 |
+
line-height: 1.6;
|
| 1207 |
+
margin-bottom: 10px;
|
| 1208 |
+
}
|
| 1209 |
+
.ln-detail-status {
|
| 1210 |
+
font-size: 12px;
|
| 1211 |
+
color: #28a745;
|
| 1212 |
+
font-weight: 600;
|
| 1213 |
+
}
|
| 1214 |
+
.ln-detail-chapters h3 {
|
| 1215 |
+
font-size: 18px;
|
| 1216 |
+
color: #fff;
|
| 1217 |
+
margin-bottom: 12px;
|
| 1218 |
+
border-left: 4px solid #667eea;
|
| 1219 |
+
padding-left: 10px;
|
| 1220 |
+
}
|
| 1221 |
+
.ln-chapter-item {
|
| 1222 |
+
display: flex;
|
| 1223 |
+
gap: 12px;
|
| 1224 |
+
padding: 10px 14px;
|
| 1225 |
+
background: #222;
|
| 1226 |
+
border-radius: 6px;
|
| 1227 |
+
margin-bottom: 6px;
|
| 1228 |
+
cursor: pointer;
|
| 1229 |
+
transition: background 0.2s;
|
| 1230 |
+
align-items: center;
|
| 1231 |
+
}
|
| 1232 |
+
.ln-chapter-item:hover {
|
| 1233 |
+
background: #333;
|
| 1234 |
+
}
|
| 1235 |
+
.ln-ch-num {
|
| 1236 |
+
font-size: 13px;
|
| 1237 |
+
font-weight: 700;
|
| 1238 |
+
color: #667eea;
|
| 1239 |
+
white-space: nowrap;
|
| 1240 |
+
}
|
| 1241 |
+
.ln-ch-title {
|
| 1242 |
+
font-size: 13px;
|
| 1243 |
+
color: #ccc;
|
| 1244 |
+
}
|
| 1245 |
+
body.ui-mobile .ln-detail-body {
|
| 1246 |
+
padding: 14px;
|
| 1247 |
+
}
|
| 1248 |
+
body.ui-mobile .ln-detail-header {
|
| 1249 |
+
flex-direction: column;
|
| 1250 |
+
align-items: center;
|
| 1251 |
+
text-align: center;
|
| 1252 |
+
}
|
| 1253 |
+
body.ui-mobile .ln-detail-cover {
|
| 1254 |
+
width: 130px;
|
| 1255 |
+
}
|
| 1256 |
+
body.ui-mobile .ln-detail-info h2 {
|
| 1257 |
+
font-size: 18px;
|
| 1258 |
+
}
|
| 1259 |
+
|
| 1260 |
+
/* LN Reader modal */
|
| 1261 |
+
.ln-reader-content {
|
| 1262 |
+
max-width: 800px !important;
|
| 1263 |
+
max-height: 90vh;
|
| 1264 |
+
display: flex;
|
| 1265 |
+
flex-direction: column;
|
| 1266 |
+
}
|
| 1267 |
+
.ln-reader-body {
|
| 1268 |
+
padding: 24px 28px;
|
| 1269 |
+
background: #f9f7f4;
|
| 1270 |
+
color: #333;
|
| 1271 |
+
line-height: 1.9;
|
| 1272 |
+
font-size: 16px;
|
| 1273 |
+
overflow-y: auto;
|
| 1274 |
+
flex: 1;
|
| 1275 |
+
}
|
| 1276 |
+
.ln-reader-body p {
|
| 1277 |
+
margin-bottom: 16px;
|
| 1278 |
+
text-align: justify;
|
| 1279 |
+
}
|
| 1280 |
+
.ln-reader-body img {
|
| 1281 |
+
max-width: 100%;
|
| 1282 |
+
display: block;
|
| 1283 |
+
margin: 16px auto;
|
| 1284 |
+
border-radius: 4px;
|
| 1285 |
+
}
|
| 1286 |
+
.ln-reader-nav {
|
| 1287 |
+
display: flex;
|
| 1288 |
+
justify-content: space-between;
|
| 1289 |
+
padding: 12px 20px;
|
| 1290 |
+
background: #1a1a1a;
|
| 1291 |
+
gap: 10px;
|
| 1292 |
+
}
|
| 1293 |
+
.ln-reader-btn {
|
| 1294 |
+
padding: 10px 20px;
|
| 1295 |
+
border: none;
|
| 1296 |
+
border-radius: 6px;
|
| 1297 |
+
background: #667eea;
|
| 1298 |
+
color: #fff;
|
| 1299 |
+
font-size: 14px;
|
| 1300 |
+
font-weight: 600;
|
| 1301 |
+
cursor: pointer;
|
| 1302 |
+
font-family: inherit;
|
| 1303 |
+
transition: background 0.2s;
|
| 1304 |
+
}
|
| 1305 |
+
.ln-reader-btn:hover {
|
| 1306 |
+
background: #5a6fd6;
|
| 1307 |
+
}
|
| 1308 |
+
body.ui-mobile .ln-reader-body {
|
| 1309 |
+
padding: 16px 18px;
|
| 1310 |
+
font-size: 15px;
|
| 1311 |
+
}
|
| 1312 |
+
body.ui-mobile .ln-reader-nav {
|
| 1313 |
+
padding: 10px 12px;
|
| 1314 |
+
}
|
| 1315 |
+
body.ui-mobile .ln-reader-btn {
|
| 1316 |
+
font-size: 12px;
|
| 1317 |
+
padding: 8px 14px;
|
| 1318 |
+
}
|
| 1319 |
+
|
| 1320 |
+
/* Manga list layout */
|
| 1321 |
+
.manga-list {
|
| 1322 |
+
display: flex;
|
| 1323 |
+
flex-direction: column;
|
| 1324 |
+
gap: 12px;
|
| 1325 |
+
padding: 20px;
|
| 1326 |
+
}
|
| 1327 |
+
.manga-list-item {
|
| 1328 |
+
display: flex;
|
| 1329 |
+
gap: 16px;
|
| 1330 |
+
background: #fff;
|
| 1331 |
+
border-radius: 8px;
|
| 1332 |
+
padding: 12px;
|
| 1333 |
+
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
| 1334 |
+
transition: transform 0.2s, box-shadow 0.2s;
|
| 1335 |
+
text-decoration: none;
|
| 1336 |
+
color: inherit;
|
| 1337 |
+
}
|
| 1338 |
+
.manga-list-item:hover {
|
| 1339 |
+
transform: translateY(-2px);
|
| 1340 |
+
box-shadow: 0 6px 16px rgba(0,0,0,0.15);
|
| 1341 |
+
}
|
| 1342 |
+
.manga-list-thumb {
|
| 1343 |
+
position: relative;
|
| 1344 |
+
flex: 0 0 96px;
|
| 1345 |
+
width: 96px;
|
| 1346 |
+
aspect-ratio: 3 / 4;
|
| 1347 |
+
background: #222;
|
| 1348 |
+
border-radius: 6px;
|
| 1349 |
+
overflow: hidden;
|
| 1350 |
+
}
|
| 1351 |
+
.manga-list-thumb img {
|
| 1352 |
+
display: block;
|
| 1353 |
+
width: 100%;
|
| 1354 |
+
height: 100%;
|
| 1355 |
+
object-fit: cover;
|
| 1356 |
+
}
|
| 1357 |
+
.manga-list-badge {
|
| 1358 |
+
position: absolute;
|
| 1359 |
+
top: 6px;
|
| 1360 |
+
left: 6px;
|
| 1361 |
+
background: rgba(0,0,0,0.8);
|
| 1362 |
+
color: #fff;
|
| 1363 |
+
font-size: 10px;
|
| 1364 |
+
padding: 2px 6px;
|
| 1365 |
+
border-radius: 3px;
|
| 1366 |
+
text-transform: uppercase;
|
| 1367 |
+
}
|
| 1368 |
+
.manga-list-badge.vi {
|
| 1369 |
+
background: rgba(102,126,234,0.95);
|
| 1370 |
+
}
|
| 1371 |
+
.manga-list-info {
|
| 1372 |
+
flex: 1;
|
| 1373 |
+
min-width: 0;
|
| 1374 |
+
display: flex;
|
| 1375 |
+
flex-direction: column;
|
| 1376 |
+
gap: 6px;
|
| 1377 |
+
}
|
| 1378 |
+
.manga-list-name {
|
| 1379 |
+
font-size: 15px;
|
| 1380 |
+
font-weight: 700;
|
| 1381 |
+
line-height: 1.35;
|
| 1382 |
+
}
|
| 1383 |
+
.manga-list-orig {
|
| 1384 |
+
font-size: 12px;
|
| 1385 |
+
color: #888;
|
| 1386 |
+
font-style: italic;
|
| 1387 |
+
}
|
| 1388 |
+
.manga-list-meta {
|
| 1389 |
+
display: flex;
|
| 1390 |
+
flex-wrap: wrap;
|
| 1391 |
+
gap: 6px;
|
| 1392 |
+
}
|
| 1393 |
+
.manga-list-chip {
|
| 1394 |
+
font-size: 11px;
|
| 1395 |
+
color: #555;
|
| 1396 |
+
background: #f1f1f1;
|
| 1397 |
+
padding: 2px 8px;
|
| 1398 |
+
border-radius: 20px;
|
| 1399 |
+
}
|
| 1400 |
+
.ln-tags {
|
| 1401 |
+
display: flex;
|
| 1402 |
+
flex-wrap: wrap;
|
| 1403 |
+
gap: 6px;
|
| 1404 |
+
}
|
| 1405 |
+
.ln-tag {
|
| 1406 |
+
font-size: 11px;
|
| 1407 |
+
color: #667eea;
|
| 1408 |
+
background: #eef0fb;
|
| 1409 |
+
padding: 2px 8px;
|
| 1410 |
+
border-radius: 20px;
|
| 1411 |
+
}
|
| 1412 |
+
body.adult-mode .manga-list-item {
|
| 1413 |
+
background: #2a2a2a;
|
| 1414 |
+
box-shadow: 0 2px 8px rgba(0,0,0,0.5);
|
| 1415 |
+
}
|
| 1416 |
+
body.adult-mode .manga-list-name { color: #eee; }
|
| 1417 |
+
body.adult-mode .manga-list-orig { color: #999; }
|
| 1418 |
+
body.adult-mode .manga-list-chip { color: #ccc; background: #3a3a3a; }
|
| 1419 |
+
body.adult-mode .ln-tag { color: #9aa8ff; background: #333a55; }
|
| 1420 |
+
body.ui-mobile .manga-list-thumb {
|
| 1421 |
+
flex-basis: 80px;
|
| 1422 |
+
width: 80px;
|
| 1423 |
+
}
|
| 1424 |
+
body.ui-mobile .manga-list-item {
|
| 1425 |
+
gap: 12px;
|
| 1426 |
+
padding: 10px;
|
| 1427 |
+
}
|
| 1428 |
+
|
| 1429 |
+
/* ===== Home React (av-*) ===== */
|
| 1430 |
+
body:has(#root) {
|
| 1431 |
+
padding: 0;
|
| 1432 |
+
}
|
| 1433 |
+
#root .container {
|
| 1434 |
+
max-width: 1140px;
|
| 1435 |
+
margin: 0 auto;
|
| 1436 |
+
background: none;
|
| 1437 |
+
box-shadow: none;
|
| 1438 |
+
overflow: visible;
|
| 1439 |
+
}
|
| 1440 |
+
|
| 1441 |
+
/* Hero */
|
| 1442 |
+
.av-hero {
|
| 1443 |
+
background: linear-gradient(135deg, #ff9a9e, #fad0c4);
|
| 1444 |
+
color: #2a1a1c;
|
| 1445 |
+
}
|
| 1446 |
+
.av-hero-adult {
|
| 1447 |
+
background: linear-gradient(135deg, #1a0505, #4a0e0e);
|
| 1448 |
+
color: #ffd9d9;
|
| 1449 |
+
}
|
| 1450 |
+
.av-hero-title {
|
| 1451 |
+
font-size: 42px;
|
| 1452 |
+
font-weight: 800;
|
| 1453 |
+
letter-spacing: 1px;
|
| 1454 |
+
margin: 0;
|
| 1455 |
+
}
|
| 1456 |
+
.av-hero-badge {
|
| 1457 |
+
font-size: 13px;
|
| 1458 |
+
font-weight: 700;
|
| 1459 |
+
padding: 4px 12px;
|
| 1460 |
+
border-radius: 20px;
|
| 1461 |
+
}
|
| 1462 |
+
.av-hero-desc {
|
| 1463 |
+
font-size: 16px;
|
| 1464 |
+
opacity: 0.85;
|
| 1465 |
+
margin-bottom: 0;
|
| 1466 |
+
}
|
| 1467 |
+
.av-search {
|
| 1468 |
+
display: flex;
|
| 1469 |
+
gap: 8px;
|
| 1470 |
+
max-width: 480px;
|
| 1471 |
+
margin: 22px auto 0;
|
| 1472 |
+
}
|
| 1473 |
+
.av-search .form-control {
|
| 1474 |
+
border-radius: 24px;
|
| 1475 |
+
padding: 12px 18px;
|
| 1476 |
+
border: none;
|
| 1477 |
+
box-shadow: 0 4px 14px rgba(0,0,0,0.12);
|
| 1478 |
+
}
|
| 1479 |
+
.av-search-btn {
|
| 1480 |
+
border-radius: 24px;
|
| 1481 |
+
padding: 0 18px;
|
| 1482 |
+
background: #667eea;
|
| 1483 |
+
color: #fff;
|
| 1484 |
+
border: none;
|
| 1485 |
+
}
|
| 1486 |
+
.av-search-btn:hover { background: #5568d8; color: #fff; }
|
| 1487 |
+
.av-random-btn {
|
| 1488 |
+
border-radius: 24px;
|
| 1489 |
+
background: #fff;
|
| 1490 |
+
color: #667eea;
|
| 1491 |
+
border: 1px solid #e0e0ff;
|
| 1492 |
+
font-weight: 600;
|
| 1493 |
+
}
|
| 1494 |
+
.av-random-btn:hover { background: #eef0fb; color: #5568d8; }
|
| 1495 |
+
|
| 1496 |
+
/* Stats */
|
| 1497 |
+
.av-stats-inner {
|
| 1498 |
+
display: flex;
|
| 1499 |
+
flex-wrap: wrap;
|
| 1500 |
+
gap: 8px 18px;
|
| 1501 |
+
justify-content: center;
|
| 1502 |
+
background: #fff;
|
| 1503 |
+
border-radius: 12px;
|
| 1504 |
+
box-shadow: 0 4px 16px rgba(0,0,0,0.08);
|
| 1505 |
+
padding: 12px 20px;
|
| 1506 |
+
font-size: 13px;
|
| 1507 |
+
color: #555;
|
| 1508 |
+
margin-top: -14px;
|
| 1509 |
+
position: relative;
|
| 1510 |
+
z-index: 2;
|
| 1511 |
+
}
|
| 1512 |
+
.av-stat strong { color: #667eea; }
|
| 1513 |
+
body.adult-mode .av-stats-inner { background: #222; color: #bbb; }
|
| 1514 |
+
body.adult-mode .av-stat strong { color: #9aa8ff; }
|
| 1515 |
+
|
| 1516 |
+
/* Nav */
|
| 1517 |
+
.av-nav { gap: 6px; flex-wrap: wrap; }
|
| 1518 |
+
.av-nav .nav-link {
|
| 1519 |
+
border-radius: 20px;
|
| 1520 |
+
padding: 7px 16px;
|
| 1521 |
+
color: #555;
|
| 1522 |
+
font-weight: 600;
|
| 1523 |
+
font-size: 14px;
|
| 1524 |
+
}
|
| 1525 |
+
.av-nav .nav-link.active {
|
| 1526 |
+
background: linear-gradient(135deg, #667eea, #764ba2);
|
| 1527 |
+
color: #fff;
|
| 1528 |
+
}
|
| 1529 |
+
body.adult-mode .av-nav .nav-link { color: #bbb; }
|
| 1530 |
+
body.adult-mode .av-nav .nav-link.active { background: #8a0e0e; }
|
| 1531 |
+
|
| 1532 |
+
/* Movie card */
|
| 1533 |
+
.av-card { display: block; text-decoration: none; color: inherit; }
|
| 1534 |
+
.av-card:hover { color: inherit; }
|
| 1535 |
+
.av-card-thumb {
|
| 1536 |
+
position: relative;
|
| 1537 |
+
border-radius: 10px;
|
| 1538 |
+
overflow: hidden;
|
| 1539 |
+
aspect-ratio: 3 / 4;
|
| 1540 |
+
background: #222;
|
| 1541 |
+
box-shadow: 0 4px 12px rgba(0,0,0,0.12);
|
| 1542 |
+
transition: transform 0.25s;
|
| 1543 |
+
}
|
| 1544 |
+
.av-card:hover .av-card-thumb { transform: translateY(-3px); }
|
| 1545 |
+
.av-card-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
| 1546 |
+
.av-badge {
|
| 1547 |
+
position: absolute;
|
| 1548 |
+
top: 6px;
|
| 1549 |
+
left: 6px;
|
| 1550 |
+
font-size: 10px;
|
| 1551 |
+
font-weight: 700;
|
| 1552 |
+
padding: 2px 7px;
|
| 1553 |
+
border-radius: 6px;
|
| 1554 |
+
background: rgba(102,126,234,0.95);
|
| 1555 |
+
color: #fff;
|
| 1556 |
+
}
|
| 1557 |
+
.av-badge-ep {
|
| 1558 |
+
left: auto;
|
| 1559 |
+
right: 6px;
|
| 1560 |
+
background: rgba(0,0,0,0.7);
|
| 1561 |
+
color: #fff;
|
| 1562 |
+
max-width: calc(100% - 60px);
|
| 1563 |
+
overflow: hidden;
|
| 1564 |
+
text-overflow: ellipsis;
|
| 1565 |
+
white-space: nowrap;
|
| 1566 |
+
}
|
| 1567 |
+
.av-badge-adult { background: #e11; }
|
| 1568 |
+
.av-card-body { padding: 8px 2px 0; }
|
| 1569 |
+
.av-card-title {
|
| 1570 |
+
font-size: 13px;
|
| 1571 |
+
font-weight: 600;
|
| 1572 |
+
margin: 0;
|
| 1573 |
+
line-height: 1.35;
|
| 1574 |
+
display: -webkit-box;
|
| 1575 |
+
-webkit-line-clamp: 2;
|
| 1576 |
+
-webkit-box-orient: vertical;
|
| 1577 |
+
overflow: hidden;
|
| 1578 |
+
min-height: 2.7em;
|
| 1579 |
+
}
|
| 1580 |
+
.av-card-orig {
|
| 1581 |
+
font-size: 11px;
|
| 1582 |
+
color: #888;
|
| 1583 |
+
margin: 3px 0 0;
|
| 1584 |
+
white-space: nowrap;
|
| 1585 |
+
overflow: hidden;
|
| 1586 |
+
text-overflow: ellipsis;
|
| 1587 |
+
}
|
| 1588 |
+
|
| 1589 |
+
/* Manga row */
|
| 1590 |
+
.av-manga-list { display: flex; flex-direction: column; gap: 14px; }
|
| 1591 |
+
.av-manga-row {
|
| 1592 |
+
display: flex;
|
| 1593 |
+
gap: 16px;
|
| 1594 |
+
text-decoration: none;
|
| 1595 |
+
color: inherit;
|
| 1596 |
+
background: #fff;
|
| 1597 |
+
border-radius: 12px;
|
| 1598 |
+
padding: 12px;
|
| 1599 |
+
box-shadow: 0 2px 10px rgba(0,0,0,0.06);
|
| 1600 |
+
transition: all 0.2s;
|
| 1601 |
+
}
|
| 1602 |
+
.av-manga-row:hover {
|
| 1603 |
+
box-shadow: 0 6px 18px rgba(0,0,0,0.12);
|
| 1604 |
+
transform: translateY(-2px);
|
| 1605 |
+
color: inherit;
|
| 1606 |
+
}
|
| 1607 |
+
.av-manga-thumb {
|
| 1608 |
+
position: relative;
|
| 1609 |
+
flex-shrink: 0;
|
| 1610 |
+
width: 72px;
|
| 1611 |
+
border-radius: 8px;
|
| 1612 |
+
overflow: hidden;
|
| 1613 |
+
aspect-ratio: 3 / 4;
|
| 1614 |
+
background: #222;
|
| 1615 |
+
}
|
| 1616 |
+
.av-manga-thumb img { width: 100%; height: 100%; object-fit: cover; }
|
| 1617 |
+
.av-manga-badge {
|
| 1618 |
+
position: absolute;
|
| 1619 |
+
left: 4px;
|
| 1620 |
+
bottom: 4px;
|
| 1621 |
+
font-size: 9px;
|
| 1622 |
+
font-weight: 700;
|
| 1623 |
+
padding: 1px 6px;
|
| 1624 |
+
border-radius: 5px;
|
| 1625 |
+
background: #e22;
|
| 1626 |
+
color: #fff;
|
| 1627 |
+
}
|
| 1628 |
+
.av-manga-badge.vi { background: #28a745; }
|
| 1629 |
+
.av-manga-info { min-width: 0; flex: 1; }
|
| 1630 |
+
.av-manga-name { font-size: 15px; font-weight: 700; margin: 0; }
|
| 1631 |
+
.av-manga-orig { font-size: 12px; color: #888; margin: 2px 0 6px; }
|
| 1632 |
+
.av-manga-meta { display: flex; flex-wrap: wrap; gap: 6px; }
|
| 1633 |
+
.av-chip { font-size: 11px; color: #555; background: #f1f1f1; padding: 2px 9px; border-radius: 20px; }
|
| 1634 |
+
.av-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
|
| 1635 |
+
.av-tag { font-size: 10px; color: #667eea; background: #eef0fb; padding: 2px 8px; border-radius: 20px; }
|
| 1636 |
+
body.adult-mode .av-manga-row { background: #2a2a2a; }
|
| 1637 |
+
body.adult-mode .av-manga-name { color: #eee; }
|
| 1638 |
+
body.adult-mode .av-manga-orig { color: #999; }
|
| 1639 |
+
body.adult-mode .av-chip { color: #ccc; background: #3a3a3a; }
|
| 1640 |
+
body.adult-mode .av-tag { color: #9aa8ff; background: #333a55; }
|
| 1641 |
+
|
| 1642 |
+
/* Light Novel */
|
| 1643 |
+
.av-ln-list { display: flex; flex-direction: column; gap: 12px; }
|
| 1644 |
+
.av-ln-row {
|
| 1645 |
+
display: flex;
|
| 1646 |
+
gap: 16px;
|
| 1647 |
+
cursor: pointer;
|
| 1648 |
+
background: #fff;
|
| 1649 |
+
border-radius: 12px;
|
| 1650 |
+
padding: 12px;
|
| 1651 |
+
box-shadow: 0 2px 10px rgba(0,0,0,0.06);
|
| 1652 |
+
transition: all 0.2s;
|
| 1653 |
+
}
|
| 1654 |
+
.av-ln-row:hover { box-shadow: 0 6px 18px rgba(0,0,0,0.12); transform: translateY(-2px); }
|
| 1655 |
+
.av-ln-cover {
|
| 1656 |
+
flex-shrink: 0;
|
| 1657 |
+
width: 60px;
|
| 1658 |
+
border-radius: 8px;
|
| 1659 |
+
overflow: hidden;
|
| 1660 |
+
aspect-ratio: 3 / 4;
|
| 1661 |
+
background: #222;
|
| 1662 |
+
}
|
| 1663 |
+
.av-ln-cover img { width: 100%; height: 100%; object-fit: cover; }
|
| 1664 |
+
.av-ln-info { flex: 1; min-width: 0; }
|
| 1665 |
+
.av-ln-title { font-size: 15px; font-weight: 700; margin: 0; }
|
| 1666 |
+
.av-ln-author { font-size: 12px; color: #888; margin: 2px 0 6px; }
|
| 1667 |
+
.av-ln-desc { font-size: 12px; color: #666; line-height: 1.5; margin: 6px 0 0; }
|
| 1668 |
+
body.adult-mode .av-ln-row { background: #2a2a2a; }
|
| 1669 |
+
body.adult-mode .av-ln-title { color: #eee; }
|
| 1670 |
+
body.adult-mode .av-ln-author { color: #999; }
|
| 1671 |
+
body.adult-mode .av-ln-desc { color: #bbb; }
|
| 1672 |
+
|
| 1673 |
+
/* LN modal */
|
| 1674 |
+
.av-ln-modal {
|
| 1675 |
+
position: fixed;
|
| 1676 |
+
inset: 0;
|
| 1677 |
+
background: rgba(0,0,0,0.6);
|
| 1678 |
+
z-index: 1050;
|
| 1679 |
+
padding: 20px;
|
| 1680 |
+
overflow-y: auto;
|
| 1681 |
+
}
|
| 1682 |
+
.av-ln-modal .modal-dialog { margin: 0 auto; max-width: 720px; }
|
| 1683 |
+
.av-ln-modal-content { border-radius: 14px; }
|
| 1684 |
+
.av-ln-cover-lg {
|
| 1685 |
+
width: 150px;
|
| 1686 |
+
border-radius: 10px;
|
| 1687 |
+
aspect-ratio: 3 / 4;
|
| 1688 |
+
object-fit: cover;
|
| 1689 |
+
flex-shrink: 0;
|
| 1690 |
+
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
|
| 1691 |
+
}
|
| 1692 |
+
|
| 1693 |
+
/* ============ Dark theme (site-wide, non-adult) ============ */
|
| 1694 |
+
body:not(.adult-mode):not(.reader-mode) {
|
| 1695 |
+
background: #0f0f1a;
|
| 1696 |
+
color: #cfd3e8;
|
| 1697 |
+
}
|
| 1698 |
+
|
| 1699 |
+
body:not(.adult-mode):not(.reader-mode) .container {
|
| 1700 |
+
background: rgba(22,24,43,0.82);
|
| 1701 |
+
backdrop-filter: blur(12px);
|
| 1702 |
+
-webkit-backdrop-filter: blur(12px);
|
| 1703 |
+
box-shadow: 0 4px 24px rgba(0,0,0,0.45);
|
| 1704 |
+
border: 1px solid rgba(140,120,220,0.12);
|
| 1705 |
+
}
|
| 1706 |
+
|
| 1707 |
+
body:not(.adult-mode):not(.reader-mode) .header {
|
| 1708 |
+
background: linear-gradient(135deg, #2b1d54 0%, #6a2c91 60%, #a3419f 100%);
|
| 1709 |
+
color: #fff;
|
| 1710 |
+
}
|
| 1711 |
+
|
| 1712 |
+
body:not(.adult-mode):not(.reader-mode) .info { color: #e4e6f5; }
|
| 1713 |
+
body:not(.adult-mode):not(.reader-mode) .info h1 { color: #fff; }
|
| 1714 |
+
body:not(.adult-mode):not(.reader-mode) .alt-title { color: #b9bede; }
|
| 1715 |
+
body:not(.adult-mode):not(.reader-mode) .info-table td { color: #cfd3e8; }
|
| 1716 |
+
body:not(.adult-mode):not(.reader-mode) .info-table .label { color: #a9aed0; }
|
| 1717 |
+
|
| 1718 |
+
body:not(.adult-mode):not(.reader-mode) .description { color: #cfd3e8; }
|
| 1719 |
+
|
| 1720 |
+
body:not(.adult-mode):not(.reader-mode) .detail-item {
|
| 1721 |
+
background: #1e2136;
|
| 1722 |
+
color: #cfd3e8;
|
| 1723 |
+
}
|
| 1724 |
+
|
| 1725 |
+
body:not(.adult-mode):not(.reader-mode) .episodes {
|
| 1726 |
+
background: #14162a;
|
| 1727 |
+
}
|
| 1728 |
+
|
| 1729 |
+
body:not(.adult-mode):not(.reader-mode) .episodes h2 { color: #e4e6f5; }
|
| 1730 |
+
|
| 1731 |
+
body:not(.adult-mode):not(.reader-mode) .lang-note {
|
| 1732 |
+
background: #2a2f4e;
|
| 1733 |
+
color: #ffd75e;
|
| 1734 |
+
border: 1px solid #4a4f75;
|
| 1735 |
+
}
|
| 1736 |
+
|
| 1737 |
+
body:not(.adult-mode):not(.reader-mode) .episode-card {
|
| 1738 |
+
background: #1e2136;
|
| 1739 |
+
border: 1px solid #2a2e4a;
|
| 1740 |
+
color: #cfd3e8;
|
| 1741 |
+
}
|
| 1742 |
+
body:not(.adult-mode):not(.reader-mode) .episode-card:hover {
|
| 1743 |
+
background: #242850;
|
| 1744 |
+
border-color: #6a2c91;
|
| 1745 |
+
box-shadow: 0 6px 16px rgba(0,0,0,0.35);
|
| 1746 |
+
}
|
| 1747 |
+
body:not(.adult-mode):not(.reader-mode) .ep-num { color: #ff9aa9; }
|
| 1748 |
+
|
| 1749 |
+
body:not(.adult-mode):not(.reader-mode) .back-link {
|
| 1750 |
+
color: #a78bfa;
|
| 1751 |
+
border-color: rgba(140,120,220,0.3);
|
| 1752 |
+
}
|
| 1753 |
+
body:not(.adult-mode):not(.reader-mode) .back-link:hover { color: #ff9aa9; border-color: #6a2c91; }
|
| 1754 |
+
|
| 1755 |
+
body:not(.adult-mode):not(.reader-mode) .footer {
|
| 1756 |
+
color: #9aa0bf;
|
| 1757 |
+
border-top: 1px solid #232640;
|
| 1758 |
+
}
|
| 1759 |
+
|
| 1760 |
+
body:not(.adult-mode):not(.reader-mode) .tag-chip {
|
| 1761 |
+
background: #1e2136;
|
| 1762 |
+
color: #b9bede;
|
| 1763 |
+
border: 1px solid #2a2e4a;
|
| 1764 |
+
}
|
| 1765 |
+
body:not(.adult-mode):not(.reader-mode) .tags-row { border-top-color: #232640; }
|
| 1766 |
+
|
| 1767 |
+
body:not(.adult-mode):not(.reader-mode) .series-header { background: linear-gradient(135deg, #2b1d54, #4a2a7a); }
|
| 1768 |
+
body:not(.adult-mode):not(.reader-mode) .series-info h1 { color: #fff; }
|
| 1769 |
+
body:not(.adult-mode):not(.reader-mode) .series-desc { color: #b9bede; }
|
| 1770 |
+
body:not(.adult-mode):not(.reader-mode) .series-card { background: #1e2136; border-color: #232640; }
|
| 1771 |
+
body:not(.adult-mode):not(.reader-mode) .series-card:hover { box-shadow: 0 8px 20px rgba(0,0,0,0.4); }
|
| 1772 |
+
body:not(.adult-mode):not(.reader-mode) .series-card-body h3 { color: #e4e6f5; }
|
| 1773 |
+
body:not(.adult-mode):not(.reader-mode) .series-card-body p { color: #9aa0bf; }
|
| 1774 |
+
|
| 1775 |
+
body:not(.adult-mode):not(.reader-mode) .movie-card {
|
| 1776 |
+
background: #1e2136;
|
| 1777 |
+
border: 1px solid #2a2e4a;
|
| 1778 |
+
box-shadow: 0 4px 14px rgba(0,0,0,0.35);
|
| 1779 |
+
}
|
| 1780 |
+
body:not(.adult-mode):not(.reader-mode) .movie-card:hover {
|
| 1781 |
+
border-color: #6a2c91;
|
| 1782 |
+
box-shadow: 0 14px 30px rgba(0,0,0,0.45);
|
| 1783 |
+
}
|
| 1784 |
+
body:not(.adult-mode):not(.reader-mode) .movie-title { color: #e4e6f5; }
|
| 1785 |
+
body:not(.adult-mode):not(.reader-mode) .movie-original { color: #9aa0bf; }
|
| 1786 |
+
|
| 1787 |
+
body:not(.adult-mode):not(.reader-mode) .manga-list-item {
|
| 1788 |
+
background: #1e2136;
|
| 1789 |
+
border: 1px solid #2a2e4a;
|
| 1790 |
+
box-shadow: 0 3px 10px rgba(0,0,0,0.35);
|
| 1791 |
+
}
|
| 1792 |
+
body:not(.adult-mode):not(.reader-mode) .manga-list-item:hover { border-color: #6a2c91; }
|
| 1793 |
+
body:not(.adult-mode):not(.reader-mode) .manga-list-name { color: #e4e6f5; }
|
| 1794 |
+
body:not(.adult-mode):not(.reader-mode) .manga-list-orig { color: #9aa0bf; }
|
| 1795 |
+
body:not(.adult-mode):not(.reader-mode) .manga-list-chip { background: #2a2f4e; color: #b9bede; }
|
| 1796 |
+
|
| 1797 |
+
body:not(.adult-mode):not(.reader-mode) .ln-list-item { background: #1e2136; border-color: #2a2e4a; }
|
| 1798 |
+
body:not(.adult-mode):not(.reader-mode) .ln-list-name { color: #e4e6f5; }
|
| 1799 |
+
body:not(.adult-mode):not(.reader-mode) .ln-list-orig { color: #9aa0bf; }
|
| 1800 |
+
body:not(.adult-mode):not(.reader-mode) .ln-tag { background: #2a2f4e; color: #b9bede; }
|
| 1801 |
+
|
| 1802 |
+
body:not(.adult-mode):not(.reader-mode) .pg-btn {
|
| 1803 |
+
background: #1e2136;
|
| 1804 |
+
border: 1px solid #2a2e4a;
|
| 1805 |
+
color: #cfd3e8;
|
| 1806 |
+
}
|
| 1807 |
+
body:not(.adult-mode):not(.reader-mode) .pg-btn:hover:not(.disabled):not(.active) { background: #242850; border-color: #6a2c91; }
|
| 1808 |
+
body:not(.adult-mode):not(.reader-mode) .pg-btn.active { background: linear-gradient(135deg, #2b1d54, #6a2c91); color: #fff; }
|
| 1809 |
+
body:not(.adult-mode):not(.reader-mode) .pg-btn.disabled { color: #5a5f7e; background: #16182b; }
|
| 1810 |
+
|
| 1811 |
+
body:not(.adult-mode):not(.reader-mode) .settings-box { background: #1e2136; }
|
| 1812 |
+
body:not(.adult-mode):not(.reader-mode) .settings-title { color: #e4e6f5; }
|
| 1813 |
+
body:not(.adult-mode):not(.reader-mode) .settings-field input { background: #16182b; color: #e4e6f5; border-color: #2a2e4a; }
|
| 1814 |
+
|
| 1815 |
+
body:not(.adult-mode):not(.reader-mode) .donate-header,
|
| 1816 |
+
body:not(.adult-mode):not(.reader-mode) .donate-desc,
|
| 1817 |
+
body:not(.adult-mode):not(.reader-mode) .donate-content { color: #cfd3e8; }
|
| 1818 |
+
|
| 1819 |
+
body:not(.adult-mode):not(.reader-mode) .gallery-section { background: #14162a; }
|
| 1820 |
+
|
| 1821 |
+
body:not(.adult-mode):not(.reader-mode) .home-nav { background: #16182b; border-bottom-color: #232640; }
|
| 1822 |
+
body:not(.adult-mode):not(.reader-mode) .home-nav a { color: #b9bede; }
|
| 1823 |
+
body:not(.adult-mode):not(.reader-mode) .home-nav a:hover,
|
| 1824 |
+
body:not(.adult-mode):not(.reader-mode) .home-nav a.active { background: #6a2c91; color: #fff; }
|
| 1825 |
+
body:not(.adult-mode):not(.reader-mode) .stats-bar { background: #14162a; border-bottom-color: #232640; color: #9aa0bf; }
|
| 1826 |
+
body:not(.adult-mode):not(.reader-mode) .stats-bar .stat-item span { color: #ff9aa9; }
|
| 1827 |
+
body:not(.adult-mode):not(.reader-mode) .search-box input { background: #16182b; color: #e4e6f5; }
|
| 1828 |
+
|
| 1829 |
+
/* ===== Stage-1 poster-grid wallpaper (site-wide) ===== */
|
| 1830 |
+
body::before {
|
| 1831 |
+
content: "";
|
| 1832 |
+
position: fixed;
|
| 1833 |
+
inset: 0;
|
| 1834 |
+
z-index: -2;
|
| 1835 |
+
pointer-events: none;
|
| 1836 |
+
background-color: #151515;
|
| 1837 |
+
background-image:
|
| 1838 |
+
url("https://phim.nguonc.com/public/images/Film/76.jpg"),
|
| 1839 |
+
url("https://phim.nguonc.com/public/images/Film/666.jpg"),
|
| 1840 |
+
url("https://phim.nguonc.com/public/images/Film/77.jpg"),
|
| 1841 |
+
url("https://phim.nguonc.com/public/images/Film/667.jpg"),
|
| 1842 |
+
url("https://phim.nguonc.com/public/images/Film/259.jpg"),
|
| 1843 |
+
url("https://phim.nguonc.com/public/images/Film/2738.jpg"),
|
| 1844 |
+
url("https://phim.nguonc.com/public/images/Film/3485.jpg"),
|
| 1845 |
+
url("https://phim.nguonc.com/public/images/Film/141438.jpg"),
|
| 1846 |
+
url("https://phim.nguonc.com/public/images/Film/date-a-bullet-thumb.jpg"),
|
| 1847 |
+
url("https://phim.nguonc.com/public/images/Film/3103.jpg"),
|
| 1848 |
+
url("https://phim.nguonc.com/public/images/Post/5/aishiteru-game-wo-owarasetai.jpg"),
|
| 1849 |
+
url("https://phim.nguonc.com/public/images/Film/541.jpg"),
|
| 1850 |
+
url("https://phim.nguonc.com/public/images/Film/536.jpg"),
|
| 1851 |
+
url("https://phim.nguonc.com/public/images/Film/56167.jpg"),
|
| 1852 |
+
url("https://phim.nguonc.com/public/images/Film/56643.jpg"),
|
| 1853 |
+
url("https://phim.nguonc.com/public/images/Film/538.jpg"),
|
| 1854 |
+
url("https://phim.nguonc.com/public/images/Film/540.jpg"),
|
| 1855 |
+
url("https://phim.nguonc.com/public/images/Film/60351.jpg"),
|
| 1856 |
+
url("https://phim.nguonc.com/public/images/Film/2607.jpg"),
|
| 1857 |
+
url("https://phim.nguonc.com/public/images/Film/2661.jpg"),
|
| 1858 |
+
url("https://phim.nguonc.com/public/images/Film/5MgMCnslrxQfOfaq6T5ndkLlcCk.jpg"),
|
| 1859 |
+
url("https://phim.nguonc.com/public/images/Film/134808.jpg"),
|
| 1860 |
+
url("https://phim.nguonc.com/public/images/Film/138379.jpg"),
|
| 1861 |
+
url("https://phim.nguonc.com/public/images/Post/9/seishun-buta-yarou-wa-santa-claus-no-yume-wo-minai-1.jpg");
|
| 1862 |
+
background-size: 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%, 12.5% 33.333%;
|
| 1863 |
+
background-position:
|
| 1864 |
+
0% 0%,
|
| 1865 |
+
14.2857% 0%,
|
| 1866 |
+
28.5714% 0%,
|
| 1867 |
+
42.8571% 0%,
|
| 1868 |
+
57.1429% 0%,
|
| 1869 |
+
71.4286% 0%,
|
| 1870 |
+
85.7143% 0%,
|
| 1871 |
+
100% 0%,
|
| 1872 |
+
0% 50%,
|
| 1873 |
+
14.2857% 50%,
|
| 1874 |
+
28.5714% 50%,
|
| 1875 |
+
42.8571% 50%,
|
| 1876 |
+
57.1429% 50%,
|
| 1877 |
+
71.4286% 50%,
|
| 1878 |
+
85.7143% 50%,
|
| 1879 |
+
100% 50%,
|
| 1880 |
+
0% 100%,
|
| 1881 |
+
14.2857% 100%,
|
| 1882 |
+
28.5714% 100%,
|
| 1883 |
+
42.8571% 100%,
|
| 1884 |
+
57.1429% 100%,
|
| 1885 |
+
71.4286% 100%,
|
| 1886 |
+
85.7143% 100%,
|
| 1887 |
+
100% 100%;
|
| 1888 |
+
background-repeat: no-repeat;
|
| 1889 |
+
filter: brightness(0.42) saturate(0.65);
|
| 1890 |
+
}
|
| 1891 |
+
body::after {
|
| 1892 |
+
content: "";
|
| 1893 |
+
position: fixed;
|
| 1894 |
+
inset: 0;
|
| 1895 |
+
z-index: -1;
|
| 1896 |
+
pointer-events: none;
|
| 1897 |
+
background: linear-gradient(180deg, rgba(21,21,21,0.55), rgba(21,21,21,0.78) 68%, #151515 100%),
|
| 1898 |
+
radial-gradient(120% 90% at 50% 0%, rgba(21,21,21,0) 42%, rgba(21,21,21,0.5) 100%);
|
| 1899 |
}
|