<!DOCTYPE html>
<html lang="vi">
<head>
<meta
charset="UTF-8">
<meta
name="viewport" content="width=device-width,
initial-scale=1.0">
<meta
name="theme-color" content="#fdf9f3">
<title>DealVerse - Săn
deal xịn, tiêu tiền thông minh</title>
<meta
name="description" content="DealVerse - voucher mua sắm, đồ ăn,
đi lại, xem phim. Review thật từ cộng đồng săn deal, cập nhật mỗi
ngày.">
<link rel="icon"
href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 64 64'%3E%3Ccircle cx='32' cy='32' r='30' fill='%23ffd6e8'
stroke='%231e1b2e' stroke-width='3'/%3E%3Cpath d='M20 16 Q15 6 22 4 Q20 11 26
14 Z' fill='%23ffc24b'/%3E%3Cpath d='M44 16 Q49 6 42 4 Q44 11 38 14 Z'
fill='%23ffc24b'/%3E%3Cellipse cx='32' cy='32' rx='13' ry='14'
fill='%23fff'/%3E%3Ccircle cx='26.5' cy='29' r='2'
fill='%231e1b2e'/%3E%3Ccircle cx='37.5' cy='29' r='2' fill='%231e1b2e'/%3E%3Cellipse
cx='32' cy='40' rx='6.5' ry='5' fill='%23e3d7ff'/%3E%3Ccircle cx='30' cy='39.5'
r='1' fill='%231e1b2e'/%3E%3Ccircle cx='34' cy='39.5' r='1'
fill='%231e1b2e'/%3E%3C/svg%3E">
<!-- Google Analytics 4
-->
<script async
src="https://www.googletagmanager.com/gtag/js?id=G-B8TBEYXJ3G"></script>
<script>
window.dataLayer =
window.dataLayer || [];
function
gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-B8TBEYXJ3G');
</script>
<style>
:root{
--bg:#fdf9f3; --card:#ffffff; --ink:#1e1b2e; --muted:#6f6a80;
--dim:#a39fb0;
--line:#e5e0d8; --pink:#ff5fa2; --violet:#8b5cf6; --mint:#c8f4e4;
--lilac:#e3d7ff;
--rose:#ffd6e8; --butter:#fde68a; --sky:#c8e4ff; --star:#f7a400;
--red:#ff2e63;
--grad:linear-gradient(90deg,#ff5fa2,#8b5cf6);
}
*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html{scroll-behavior:smooth}
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe
UI",Roboto,Helvetica,Arial,sans-serif;background:var(--bg);color:var(--ink)}
button,input{font-family:inherit}
a{text-decoration:none;color:inherit}
.container{max-width:1080px;margin:0
auto;padding:0 20px}
/* Header */
header{position:sticky;top:0;z-index:50;background:rgba(255,255,255,.92);backdrop-filter:blur(8px);border-bottom:1.5px
solid var(--ink)}
.hd{display:flex;align-items:center;justify-content:space-between;height:60px;gap:10px}
.brand{display:flex;align-items:center;gap:9px;font-size:17px;font-weight:800}
.brand
.logo{width:34px;height:34px;border-radius:50%;background:linear-gradient(135deg,var(--rose),var(--lilac));border:1.5px
solid var(--ink);display:flex;align-items:center;justify-content:center}
nav.menu{display:flex;gap:18px;font-size:13.5px;color:var(--muted);font-weight:600}
nav.menu
a:hover{color:var(--ink)}
.hd-btns{display:flex;gap:8px}
.btn{border:1.5px solid
var(--ink);border-radius:999px;padding:8px
15px;font-size:12.5px;font-weight:800;cursor:pointer;background:#fff;transition:.12s}
.btn:active{transform:translate(1px,1px)}
.btn.bell{box-shadow:2px 2px 0
var(--rose)}
.btn.join{background:var(--grad);color:#fff;box-shadow:2px
2px 0 var(--ink)}
/* Hero */
.hero{background:linear-gradient(115deg,#ffe1ee
0%,#e6d9ff 45%,#cdf5e7 100%);border-bottom:1.5px solid
var(--ink);overflow:hidden}
.hero-in{display:flex;align-items:stretch;min-height:280px}
.hero-l{flex:0 0
61.8%;padding:38px 0 30px}
.hero-r{flex:1;position:relative;min-height:220px}
.badge{display:inline-block;background:#fff;border:1.5px
solid var(--ink);border-radius:999px;padding:5px
13px;font-size:11px;font-weight:800;box-shadow:2px 2px 0 var(--ink)}
.hero
h1{font-size:38px;font-weight:800;line-height:1.2;margin-top:14px;letter-spacing:-.5px}
.hero
.sub{font-size:15px;color:#4a4560;margin-top:8px;max-width:430px}
.stats{display:flex;gap:10px;margin-top:20px;flex-wrap:wrap}
.stat{background:#fff;border:1.5px
solid var(--ink);border-radius:14px;padding:9px 14px;box-shadow:2px 2px 0
var(--ink)}
.stat b{font-size:17px}
.stat
div{font-size:9.5px;color:var(--muted);font-weight:800;letter-spacing:.4px}
.hero-r
svg.scene{position:absolute;inset:0;width:100%;height:100%}
/* Section shells */
.sect{padding:26px 0 30px}
.sect-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px}
.sect-head
h2{font-size:20px;font-weight:800}
.band-lilac{background:#f6f0ff;border-top:1.5px
solid var(--ink);border-bottom:1.5px solid var(--ink)}
.band-mint{background:#e9fbf2;border-bottom:1.5px
solid var(--ink)}
/* Tabs */
.tabs{display:flex;gap:8px;overflow-x:auto;scrollbar-width:none;padding-bottom:4px;margin-bottom:14px}
.tabs::-webkit-scrollbar{display:none}
.tab{flex:0 0 auto;padding:8px
17px;border-radius:999px;border:1.5px solid
var(--line);background:#fff;font-size:13px;font-weight:700;color:var(--muted);cursor:pointer;transition:.15s}
.tab.active{background:var(--ink);color:#fff;border-color:var(--ink)}
/* Deal grid */
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:14px}
.state{padding:50px
18px;text-align:center;color:var(--muted);font-size:14px;grid-column:1/-1}
.card{background:#fff;border:1.5px
solid
var(--ink);border-radius:16px;padding:14px;position:relative;box-shadow:3px 3px
0 var(--rose)}
.card:nth-child(3n+2){box-shadow:3px
3px 0 var(--mint)}
.card:nth-child(3n){box-shadow:3px
3px 0 var(--lilac)}
.hotb{position:absolute;top:-10px;right:12px;background:linear-gradient(90deg,#ff5fa2,#ff9d2e);color:#fff;font-size:10px;font-weight:800;padding:3px
11px;border-radius:999px;border:1.5px solid var(--ink)}
.row1{display:flex;align-items:center;justify-content:space-between;gap:8px}
.chip-p{font-size:11px;font-weight:800;padding:3px
10px;border-radius:99px}
.rating{font-size:11.5px;color:var(--muted);white-space:nowrap}
.rating b{color:var(--ink)}
.star-on{color:var(--star)}
.star-off{color:#dcd7cd}
.card
h3{font-size:14.5px;font-weight:700;line-height:1.35;margin-top:9px}
.card
.desc{font-size:12px;color:var(--muted);margin-top:2px;line-height:1.45}
.meta{display:flex;justify-content:space-between;margin-top:9px;font-size:11px;color:var(--muted)}
.meta b{color:var(--ink)}
.meta
.low{color:var(--red);font-weight:700}
.bar{height:6px;background:#f1ece4;border:1px
solid var(--line);border-radius:99px;margin-top:6px;overflow:hidden}
.bar
i{display:block;height:100%;background:var(--grad);border-radius:99px}
.actions{display:flex;gap:8px;margin-top:11px}
.code-box{flex:1;display:flex;align-items:center;justify-content:space-between;border:1.5px
dashed var(--violet);border-radius:10px;padding:8px
11px;background:#f6f2ff;cursor:pointer;user-select:none}
.code-box
.code{font-family:ui-monospace,Menlo,monospace;font-weight:800;font-size:12.5px;color:var(--violet);letter-spacing:.5px}
.code-box
.cp{font-size:10.5px;font-weight:800;color:var(--violet)}
.btn-go{background:var(--ink);color:#fff;border-radius:10px;padding:9px
15px;font-size:12.5px;font-weight:800;cursor:pointer;border:none;display:inline-flex;align-items:center}
.btn-go.full{flex:1;justify-content:center}
.btn-go:active{transform:scale(.97)}
/* Comments */
.cmt-toggle{margin-top:11px;padding-top:9px;border-top:1.5px
dashed
var(--line);display:flex;justify-content:space-between;align-items:center;cursor:pointer}
.cmt-toggle
.lbl{font-size:11.5px;font-weight:800}
.cmt-toggle
.act{font-size:10.5px;color:var(--violet);font-weight:700}
.cmt-wrap{display:none;margin-top:8px}
.cmt-wrap.open{display:block}
.cmt-list{max-height:230px;overflow-y:auto;display:flex;flex-direction:column;gap:6px}
.cmt{background:#faf7f0;border-radius:10px;padding:8px
10px}
.cmt .who{font-size:10.5px}
.cmt .who span{color:var(--dim)}
.cmt
.txt{font-size:12px;color:#4a4560;margin-top:2px;line-height:1.45;word-break:break-word}
.cmt-form{display:flex;gap:6px;margin-top:8px}
.cmt-form input{border:1.5px
solid var(--line);border-radius:99px;padding:8px
12px;font-size:12px;background:#fff;outline:none;min-width:0}
.cmt-form
input:focus{border-color:var(--violet)}
.cmt-form .nick{flex:0 0 30%}
.cmt-form .body{flex:1}
.cmt-form
button{background:var(--grad);color:#fff;border:none;border-radius:99px;padding:8px
15px;font-size:11.5px;font-weight:800;cursor:pointer;flex:0 0 auto}
.rate-row{display:none;margin-top:9px;align-items:center;justify-content:space-between}
.rate-row.show{display:flex}
.rate-row
.q{font-size:11.5px;color:var(--muted)}
.rate-stars{font-size:18px;letter-spacing:2px;cursor:pointer;color:#dcd7cd;user-select:none}
.rate-thanks{font-size:11.5px;color:#009e4f;font-weight:700}
/* Tips */
.tgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:14px}
.tcard{background:#fff;border:1.5px
solid var(--ink);border-radius:16px;padding:13px;box-shadow:3px 3px 0
var(--butter);cursor:pointer;transition:.12s}
.tcard:nth-child(2n){box-shadow:3px
3px 0 var(--sky)}
.tcard:hover{transform:translate(-1px,-1px)}
.tchips{display:flex;gap:6px;flex-wrap:wrap}
.tchip{font-size:10px;font-weight:800;padding:3px
9px;border-radius:99px}
.tchip.save{color:#b47b00;background:#fdf3d7}
.tcard
h3{font-size:13.5px;font-weight:700;margin-top:8px;line-height:1.4}
.tcard
.sum{font-size:11.5px;color:var(--muted);margin-top:4px;line-height:1.5;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.tfoot{display:flex;justify-content:space-between;align-items:center;margin-top:10px;padding-top:8px;border-top:1.5px
dashed var(--line);font-size:10.5px;color:var(--muted)}
.tfoot
.more{color:var(--violet);font-weight:800}
/* Jobs */
.jgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:14px}
.jcard{background:#fff;border:1.5px
solid var(--ink);border-radius:16px;padding:15px;box-shadow:3px 3px 0
var(--ink)}
.jcard
h3{font-size:14.5px;font-weight:800}
.jcard
p{font-size:12px;color:var(--muted);margin-top:4px;line-height:1.5}
.jtags{display:flex;gap:6px;margin-top:9px;flex-wrap:wrap}
.jtag{font-size:10px;font-weight:800;border:1px
solid var(--ink);padding:3px 9px;border-radius:99px}
.jcard.creator
.jtag{background:#fff0f7}
.jcard.affiliate
.jtag{background:#eefff6}
.jcard
a{display:block;margin-top:12px;background:var(--ink);color:#fff;text-align:center;border-radius:10px;padding:9px;font-size:12px;font-weight:800}
/* Newsletter strip */
.news{background:var(--grad);padding:20px
0}
.news-in{display:flex;align-items:center;gap:14px;flex-wrap:wrap}
.news
h3{font-size:15px;font-weight:800;color:#fff}
.news
p{font-size:11.5px;color:#ffe3f0}
.news
form{flex:1;display:flex;gap:7px;min-width:260px}
.news
input{flex:1;background:#fff;border:1.5px solid
var(--ink);border-radius:99px;padding:10px
15px;font-size:12.5px;outline:none;min-width:0}
.news
button{background:var(--ink);color:#fff;border:1.5px solid
var(--ink);border-radius:99px;padding:10px
17px;font-size:12px;font-weight:800;cursor:pointer;flex:0 0 auto}
footer.ft{text-align:center;padding:20px;font-size:11.5px;color:var(--dim)}
/* Modal */
.overlay{display:none;position:fixed;inset:0;background:rgba(30,27,46,.55);z-index:100;align-items:center;justify-content:center;padding:20px}
.overlay.open{display:flex}
.modal{background:#fff;border:1.5px
solid var(--ink);border-radius:18px;box-shadow:4px 4px 0
var(--ink);max-width:560px;width:100%;max-height:85vh;overflow-y:auto;padding:20px}
.modal
.close{float:right;font-size:20px;cursor:pointer;color:var(--muted);line-height:1}
.modal
h3{font-size:17px;font-weight:800;padding-right:26px}
.modal
.body{font-size:13.5px;color:#4a4560;line-height:1.65;margin-top:12px;white-space:pre-line}
.modal .src
a{color:var(--violet);font-weight:700;font-size:12px}
.like-btn{display:inline-flex;align-items:center;gap:6px;border:1.5px
solid var(--ink);background:#fff;border-radius:99px;padding:7px
15px;font-size:12px;font-weight:800;cursor:pointer;box-shadow:2px 2px 0
var(--butter);margin-top:14px}
.like-btn.liked{background:var(--butter)}
#toast{position:fixed;bottom:24px;left:50%;transform:translateX(-50%)
translateY(90px);background:var(--ink);color:#fff;padding:10px
20px;border-radius:999px;font-size:13px;font-weight:700;transition:.25s;opacity:0;z-index:200;max-width:90vw;text-align:center}
#toast.show{transform:translateX(-50%)
translateY(0);opacity:1}
/* Mobile */
@media (max-width:760px){
nav.menu{display:none}
.hero h1{font-size:26px}
.hero-in{flex-direction:column;min-height:0}
.hero-l{flex:1;padding:26px 0 6px}
.hero-r{min-height:170px}
.btn{padding:7px 11px;font-size:11px}
}
</style>
</head>
<body>
<header>
<div class="container hd">
<a class="brand"
href="#top">
<span class="logo">
<svg viewBox="0 0 64 64"
width="22" height="22"
aria-hidden="true"><path d="M20 16 Q15 6 22 4 Q20 11 26 14
Z" fill="#f7a400"/><path d="M44 16 Q49 6 42 4 Q44 11
38 14 Z" fill="#f7a400"/><ellipse cx="32"
cy="34" rx="15" ry="16" fill="#fff"
stroke="#1e1b2e" stroke-width="2.5"/><circle cx="26"
cy="30" r="2.2" fill="#1e1b2e"/><circle
cx="38" cy="30" r="2.2"
fill="#1e1b2e"/><ellipse cx="32" cy="42"
rx="7" ry="5.5" fill="#e3d7ff"/><circle
cx="29.5" cy="41" r="1.1"
fill="#1e1b2e"/><circle cx="34.5" cy="41" r="1.1"
fill="#1e1b2e"/></svg>
</span>
<span
id="siteName">DealVerse</span>
</a>
<nav class="menu">
<a href="#deals">Deal
hot</a>
<a href="#tips">Mẹo chi
tiêu</a>
<a href="#crew">Kiếm
thêm</a>
</nav>
<div class="hd-btns">
<button class="btn bell"
id="bellBtn">🔔 Nhận deal
tuần</button>
<button class="btn join"
id="joinBtn">Gia nhập</button>
</div>
</div>
</header>
<div class="hero"
id="top">
<div class="container hero-in">
<div class="hero-l">
<span class="badge"
id="heroBadge">🚀 MỚI CẬP
NHẬT</span>
<h1 id="heroTitle">Săn
deal xịn,<br>tiêu tiền thông minh 💸</h1>
<p class="sub"
id="heroSub">Voucher thật, review thật từ cộng đồng săn deal. Cập
nhật tự động mỗi ngày, bấm là dùng luôn.</p>
<div class="stats">
<div class="stat"><b
id="statDeals">--</b><div>DEAL ĐANG
CHẠY</div></div>
<div class="stat"><b
id="statClaims">--</b><div>LƯỢT
LẤY</div></div>
<div class="stat"><b
id="statRating">--</b><div>ĐÁNH GIÁ
TB</div></div>
</div>
</div>
<div class="hero-r">
<svg class="scene"
viewBox="0 0 340 250" preserveAspectRatio="xMidYMid meet"
role="img" aria-label="Dê vũ trụ giữa các hành tinh và vé
voucher">
<defs>
<linearGradient id="pl"
x1="0" y1="0" x2="1" y2="1">
<stop offset="0"
stop-color="#ffd166"/><stop offset="1"
stop-color="#ff9d2e"/>
</linearGradient>
</defs>
<text x="30"
y="34" font-size="13" fill="#8b5cf6"
opacity=".8">✦</text>
<text x="290"
y="26" font-size="10" fill="#ff5fa2"
opacity=".8">✦</text>
<text x="52"
y="200" font-size="10" fill="#1e1b2e"
opacity=".5">✦</text>
<text x="300"
y="215" font-size="12" fill="#8b5cf6"
opacity=".7">✦</text>
<circle cx="60"
cy="70" r="14" fill="#c8e4ff"
stroke="#1e1b2e" stroke-width="1.5"/>
<circle cx="55"
cy="66" r="3" fill="#fff"
opacity=".8"/>
<circle cx="285"
cy="180" r="10" fill="#e3d7ff"
stroke="#1e1b2e" stroke-width="1.5"/>
<circle cx="170"
cy="118" r="58" fill="url(#pl)"
stroke="#1e1b2e" stroke-width="1.5"/>
<ellipse cx="170"
cy="122" rx="86" ry="20" fill="none"
stroke="#1e1b2e" stroke-width="1.5" opacity=".75"
transform="rotate(-14 170 122)"/>
<g transform="translate(170
100) rotate(6)">
<circle cx="0"
cy="0" r="34" fill="#fff"
stroke="#1e1b2e" stroke-width="2"/>
<path d="M-14 -26 Q-20 -40
-10 -42 Q-13 -33 -6 -29 Z" fill="#f7a400"/>
<path d="M14 -26 Q20 -40 10
-42 Q13 -33 6 -29 Z" fill="#f7a400"/>
<ellipse cx="-22"
cy="-12" rx="7" ry="4" fill="#fff"
stroke="#1e1b2e" stroke-width="1.5"
transform="rotate(-30 -22 -12)"/>
<ellipse cx="22"
cy="-12" rx="7" ry="4" fill="#fff"
stroke="#1e1b2e" stroke-width="1.5"
transform="rotate(30 22 -12)"/>
<ellipse cx="0"
cy="2" rx="19" ry="21" fill="#fff"
stroke="#1e1b2e" stroke-width="1.5"/>
<circle cx="-7"
cy="-3" r="2.6" fill="#1e1b2e"/>
<circle cx="7"
cy="-3" r="2.6" fill="#1e1b2e"/>
<ellipse cx="0"
cy="12" rx="9" ry="7"
fill="#e3d7ff"/>
<circle cx="-3"
cy="11" r="1.4" fill="#1e1b2e"/>
<circle cx="3"
cy="11" r="1.4" fill="#1e1b2e"/>
<path d="M-3 19 L0 25 L3 19
Z" fill="#fff" stroke="#1e1b2e"
stroke-width="1"/>
</g>
<g transform="translate(268 60)
rotate(10)">
<rect x="-34"
y="-13" width="68" height="26" rx="7"
fill="#fff" stroke="#ff5fa2" stroke-width="1.5"
stroke-dasharray="4 3"/>
<text x="0"
y="4.5" text-anchor="middle" font-size="11"
font-weight="800" fill="#ff5fa2">-50K 🎟️</text>
</g>
<g transform="translate(68 160)
rotate(-9)">
<rect x="-40"
y="-13" width="80" height="26" rx="7"
fill="#fff" stroke="#8b5cf6" stroke-width="1.5"
stroke-dasharray="4 3"/>
<text x="0"
y="4.5" text-anchor="middle" font-size="10.5"
font-weight="800" fill="#8b5cf6">FREESHIP 🚚</text>
</g>
<g transform="translate(262
222) rotate(6)">
<rect x="-40"
y="-13" width="80" height="26" rx="7"
fill="#fff" stroke="#009e4f" stroke-width="1.5"
stroke-dasharray="4 3"/>
<text x="0"
y="4.5" text-anchor="middle" font-size="10.5"
font-weight="800" fill="#009e4f">HOÀN XU 💰</text>
</g>
</svg>
</div>
</div>
</div>
<section
class="sect" id="deals">
<div class="container">
<div
class="sect-head"><h2>Deal đang hot 🔥</h2></div>
<div class="tabs"
id="tabs"></div>
<div class="grid"
id="dealGrid"><div class="state">Đang tải
deal...</div></div>
</div>
</section>
<section
class="band-lilac sect" id="tips">
<div class="container">
<div
class="sect-head"><h2>Mẹo chi tiêu thông minh 💡</h2></div>
<div class="tgrid"
id="tipGrid"><div class="state">Đang tải mẹo
hay...</div></div>
</div>
</section>
<section
class="band-mint sect" id="crew">
<div class="container">
<div
class="sect-head"><h2>Gia nhập phi hành đoàn 🐐🚀</h2></div>
<div class="jgrid"
id="jobGrid"><div class="state">Đang tải
job...</div></div>
</div>
</section>
<div
class="news">
<div class="container news-in">
<div>
<h3 id="newsTitle">🔔
Bản tin deal tuần</h3>
<p id="newsSub">Mỗi thứ
2, gửi 5 deal ngon nhất tuần</p>
</div>
<form id="newsForm">
<input type="text"
id="newsContact" placeholder="Email hoặc số Zalo của
bạn..." maxlength="120">
<button
type="submit">Đăng ký nhận</button>
</form>
</div>
</div>
<footer class="ft"
id="footNote">Deal bay nhanh lắm - nhanh tay kẻo lỡ 🐐</footer>
<!-- Modal: tip detail -->
<div
class="overlay" id="tipOverlay">
<div class="modal">
<span class="close"
data-close="tipOverlay">✕</span>
<div class="tchips"
id="tipMChips"></div>
<h3 id="tipMTitle"
style="margin-top:10px"></h3>
<div class="body"
id="tipMBody"></div>
<div class="src"
id="tipMSrc" style="margin-top:10px"></div>
<button class="like-btn"
id="tipLikeBtn">👍 Hữu ích
(<span id="tipLikeN">0</span>)</button>
<div class="cmt-toggle"
style="cursor:default"><span class="lbl">💬
Bình luận (<span
id="tipCmtN">0</span>)</span></div>
<div class="cmt-wrap open">
<div class="cmt-list"
id="tipCmtList"></div>
<div class="cmt-form">
<input class="nick"
id="tipNick" placeholder="Nickname..."
maxlength="30">
<input class="body"
id="tipCmtInput" placeholder="Bình luận (bỏ trống tên = Ẩn
danh)" maxlength="300">
<button
id="tipCmtSend">Gửi</button>
</div>
</div>
</div>
</div>
<!-- Modal: newsletter -->
<div
class="overlay" id="bellOverlay">
<div class="modal" style="max-width:420px">
<span class="close"
data-close="bellOverlay">✕</span>
<h3>🔔
Nhận bản tin deal tuần</h3>
<p
style="font-size:13px;color:var(--muted);margin-top:6px">Mỗi thứ 2
nhận 5 deal ngon nhất tuần, không spam, hủy lúc nào cũng được.</p>
<div class="cmt-form"
style="margin-top:14px">
<input class="body"
id="bellContact" placeholder="Email hoặc số Zalo..."
maxlength="120">
<button
id="bellSend">Đăng ký</button>
</div>
</div>
</div>
<div
id="toast">Đã copy mã</div>
<script>
/*
=========================================================
CẤU HÌNH - chỉ cần sửa 2 dòng dưới
- SHEET_ID: id Google Sheet
- SCRIPT_URL: URL Web App của Apps Script
(Deploy > Web app)
Để trống '' thì web vẫn chạy, chỉ tắt gửi
comment/đánh giá/đăng ký
=========================================================
*/
const SHEET_ID =
'1DyzadS-R-xdwondrUrrTVfopYM-RS5u9NXXQEEDmdf8';
const SCRIPT_URL = ' https://script.google.com/macros/s/AKfycbzRtpTLMWjCSRi5HjiaBGoxoVaK2MF83tlGoVO42ZHxX0otZvUSZ2lwUjqLNTvZ1SGg/exec';
/* Kênh nguồn: link bio dạng
?src=tiktok_kenh1 */
const src = new
URLSearchParams(location.search).get('src') || sessionStorage.getItem('src') ||
'direct';
sessionStorage.setItem('src',
src);
const PCOLORS = [
['shopeefood',['#ee4d2d','#feeee9']], ['shopee',['#ee4d2d','#feeee9']],
['lazada',['#0f156d','#ebecf7']], ['tiktok',['#1e1b2e','#eeeef0']],
['tiki',['#1a94ff','#e6f2ff']], ['grab',['#009e4f','#e5f6ec']],
['be',['#b58900','#fdf4dc']], ['xanh',['#0a7c5a','#e3f4ec']],
['momo',['#a50064','#f9e6f1']], ['galaxy',['#e8590c','#fdeee3']],
['cgv',['#e03131','#fdeaea']], ['ngân hàng',['#1b5fa8','#e8f0f9']],
['bank',['#1b5fa8','#e8f0f9']],
];
const TIPCOLORS = { 'ăn
uống':['#009e4f','#e5f6ec'], 'mua sắm':['#ee4d2d','#feeee9'], 'tài
chính':['#1b5fa8','#e8f0f9'], 'đi lại':['#b58900','#fdf4dc'], 'giải
trí':['#a50064','#f9e6f1'] };
function pColor(name){
const n = (name||'').toLowerCase();
for (const [k,c] of PCOLORS) if (n.includes(k)) return c;
return ['#5f5e5a','#f1efe8'];
}
function tColor(name){ return
TIPCOLORS[(name||'').toLowerCase()] || ['#5f5e5a','#f1efe8']; }
function esc(s){
return (s||'').toString().replace(/[&<>"']/g, m =>
({'&':'&','<':'<','>':'>','"':'"',"'":'''}[m]));
}
function fmtNum(n){
const x = parseFloat(n);
return isNaN(x) ? (n||'0') : x.toLocaleString('vi-VN');
}
function stars(r){
let s = '';
for (let i=1;i<=5;i++) s += '<span
class="'+(i<=Math.round(r)?'star-on':'star-off')+'">★</span>';
return s;
}
function buildLink(base, id){
try{ const u = new URL(base); u.searchParams.set('sub1', src);
u.searchParams.set('sub2', id); return u.toString(); }
catch{ return base; }
}
function track(ev, params){ try{
gtag('event', ev, Object.assign({channel:src}, params||{})); }catch(e){} }
function toast(msg){
const t = document.getElementById('toast');
t.textContent = msg; t.classList.add('show');
setTimeout(() => t.classList.remove('show'), 1800);
}
/* ============ ĐỌC SHEET (gviz
theo tab) ============ */
async function readTab(name){
const url =
'https://docs.google.com/spreadsheets/d/'+SHEET_ID+'/gviz/tq?tqx=out:json&headers=1&sheet='+encodeURIComponent(name)+'&t='+Date.now();
const res = await fetch(url);
const text = await res.text();
const json = JSON.parse(text.substring(text.indexOf('(')+1,
text.lastIndexOf(')')));
const cols = json.table.cols.map(c =>
(c.label||'').trim().toLowerCase());
return json.table.rows.map(r => {
const o = {};
cols.forEach((name2, i) => {
const c = r.c && r.c[i];
o[name2] = c ? ((c.f != null ? c.f : (c.v
!= null ? c.v : '')) + '').trim() : '';
});
return o;
});
}
async function safeTab(name){
try{ return await readTab(name); }catch(e){ return []; } }
/* ============ GỬI DỮ LIỆU
(Apps Script) ============ */
let lastPost = 0;
function postData(payload){
if (!SCRIPT_URL){ toast('Tính năng đang bảo trì, thử lại sau nhé');
return false; }
const now = Date.now();
if (now - lastPost < 30000){ toast('Từ từ thôi, 30 giây gửi 1 lần
nhé'); return false; }
lastPost = now;
const body = new URLSearchParams(Object.assign({src:src}, payload));
fetch(SCRIPT_URL, { method:'POST', mode:'no-cors', body:body });
return true;
}
function cleanText(s){
s = (s||'').trim().substring(0,300);
if (/https?:\/\/|www\./i.test(s)) return null;
return s;
}
/* ============ STATE
============ */
let DEALS = [], TIPS = [], JOBS
= [], CFG = {}, COMMENTS = [], RATINGS = [];
let currentTip = null;
function ratingFor(loai, id,
seedSao, seedN){
const rows = RATINGS.filter(r => r.loai===loai && r.id===id
&& parseFloat(r.diem)>0);
const sSao = parseFloat(seedSao)||0, sN = parseFloat(seedN)||0;
const sum = rows.reduce((a,r)=>a+parseFloat(r.diem),0);
const n = sN + rows.length;
if (!n) return {avg:0, n:0};
return { avg: Math.round(((sSao*sN + sum)/n)*10)/10, n:n };
}
function likesFor(id, seed){
return (parseFloat(seed)||0) + RATINGS.filter(r => r.loai==='tip'
&& r.id===id && r.diem==='like').length;
}
function commentsFor(loai, id){
return COMMENTS.filter(c => c.loai===loai && c.id===id
&& c.an==='').reverse();
}
function cmtHTML(c){
return '<div class="cmt"><div
class="who"><b>'+esc(c.nickname||'Ẩn danh')+'</b>
<span>· '+esc(c.time||'')+'</span></div><div
class="txt">'+esc(c.noi_dung)+'</div></div>';
}
/* ============ DEALS
============ */
function renderTabs(){
const cats = [...new Set(DEALS.map(d => d.nganh).filter(Boolean))];
const tabs = document.getElementById('tabs');
tabs.innerHTML = '';
['Tất cả', ...cats].forEach((c,i) => {
const b = document.createElement('button');
b.className = 'tab'+(i===0?' active':'');
b.textContent = c;
b.addEventListener('click', () => {
document.querySelectorAll('.tab').forEach(t =>
t.classList.remove('active'));
b.classList.add('active');
renderDeals(c);
});
tabs.appendChild(b);
});
}
function renderDeals(cat){
const grid = document.getElementById('dealGrid');
grid.innerHTML = '';
const items = DEALS.filter(d => cat==='Tất cả' || d.nganh===cat);
if (!items.length){ grid.innerHTML = '<div
class="state">Chưa có deal trong mục này, quay lại sau
nhé.</div>'; return; }
items.forEach(d => {
const [pc,pbg] = pColor(d.nen_tang);
const rt = ratingFor('deal', d.id, d.sao,
d.luot_danh_gia);
const cmts = commentsFor('deal', d.id);
const conLai =
parseFloat(d.con_lai_phan_tram)||0;
const low = conLai>0 &&
conLai<=20;
const rated =
localStorage.getItem('rated_'+d.id);
const el = document.createElement('div');
el.className = 'card';
el.innerHTML =
(d.hot!=='' ? '<div
class="hotb">HOT 🔥</div>'
: '') +
'<div
class="row1"><span class="chip-p"
style="color:'+pc+';background:'+pbg+';">'+esc(d.nen_tang)+'</span>'
+
(rt.n ? '<span
class="rating">'+stars(rt.avg)+' <b>'+rt.avg+'</b>
('+fmtNum(rt.n)+')</span>' : '') + '</div>' +
'<h3>'+esc(d.tieu_de)+'</h3>'
+
'<div
class="desc">'+esc(d.mo_ta)+(d.hsd?' · HSD
'+esc(d.hsd):'')+'</div>' +
'<div
class="meta"><span><b>'+fmtNum(d.luot_lay)+'</b>
lượt lấy</span>' +
(conLai>0 ? '<span
class="'+(low?'low':'')+'">'+(low?'Sắp hết, còn ':'Còn
')+conLai+'%</span>' : '') + '</div>' +
(conLai>0 ? '<div
class="bar"><i
style="width:'+(100-conLai)+'%"></i></div>' : '') +
'<div class="actions">' +
(d.ma
? '<div class="code-box
act-claim" data-id="'+esc(d.id)+'"><span
class="code">'+esc(d.ma)+'</span><span
class="cp">COPY</span></div>' +
'<a class="btn-go
act-claim" data-id="'+esc(d.id)+'"
href="'+esc(buildLink(d.link,d.id))+'" target="_blank"
rel="noopener">Dùng ngay</a>'
: '<a class="btn-go full
act-claim" data-id="'+esc(d.id)+'"
href="'+esc(buildLink(d.link,d.id))+'" target="_blank"
rel="noopener">Lấy deal</a>') +
'</div>' +
'<div class="rate-row'+(rated?'
show':'')+'" id="rate-'+esc(d.id)+'">' +
(rated
? '<span
class="rate-thanks">Bạn đã đánh giá '+rated+' sao, cảm ơn
nhé</span>'
: '<span class="q">Mã
dùng ổn không? Chấm sao nhé</span><span class="rate-stars"
data-id="'+esc(d.id)+'"><span data-s="1">★</span><span
data-s="2">★</span><span
data-s="3">★</span><span
data-s="4">★</span><span
data-s="5">★</span></span>')
+
'</div>' +
'<div class="cmt-toggle"
data-id="'+esc(d.id)+'"><span class="lbl">💬
Bình luận ('+cmts.length+')</span><span
class="act">Xem</span></div>' +
'<div class="cmt-wrap"
id="cw-'+esc(d.id)+'">' +
'<div
class="cmt-list">'+(cmts.length ? cmts.map(cmtHTML).join('') :
'<div class="cmt"><div class="txt">Chưa có bình
luận, làm người đầu tiên nhé!</div></div>')+'</div>' +
'<div
class="cmt-form"><input class="nick"
placeholder="Nickname..." maxlength="30"><input
class="body" placeholder="Bình luận (bỏ trống tên = Ẩn
danh)" maxlength="300"><button
data-id="'+esc(d.id)+'">Gửi</button></div>' +
'</div>';
grid.appendChild(el);
});
bindDealEvents(grid);
}
function bindDealEvents(grid){
grid.querySelectorAll('.act-claim').forEach(el => {
el.addEventListener('click', () => {
const d = DEALS.find(x =>
x.id===el.dataset.id);
if (!d) return;
if (el.classList.contains('code-box')){
navigator.clipboard.writeText(d.ma).then(() => toast('Đã copy mã
'+d.ma));
}
track('claim_voucher', {deal_id:d.id,
deal_title:d.tieu_de, platform:d.nen_tang, nganh:d.nganh});
const rr =
document.getElementById('rate-'+d.id);
if (rr &&
!localStorage.getItem('rated_'+d.id)) rr.classList.add('show');
});
});
grid.querySelectorAll('.rate-stars span').forEach(st => {
st.addEventListener('click', e => {
const wrap = e.target.parentElement, id =
wrap.dataset.id, n = parseInt(e.target.dataset.s);
if (postData({type:'rating', loai:'deal',
id:id, diem:n})){
localStorage.setItem('rated_'+id, n);
track('rate_voucher', {deal_id:id,
stars:n});
wrap.parentElement.innerHTML =
'<span class="rate-thanks">Bạn đã đánh giá '+n+' sao, cảm ơn
nhé</span>';
toast('Cảm ơn bạn đã đánh giá '+n+'
sao');
}
});
});
grid.querySelectorAll('.cmt-toggle').forEach(tg => {
tg.addEventListener('click', () => {
const w =
document.getElementById('cw-'+tg.dataset.id);
w.classList.toggle('open');
tg.querySelector('.act').textContent =
w.classList.contains('open') ? 'Thu gọn' : 'Xem';
});
});
grid.querySelectorAll('.cmt-form button').forEach(btn => {
btn.addEventListener('click', () => {
const form = btn.parentElement;
const nick =
form.querySelector('.nick').value.trim().substring(0,30) || 'Ẩn danh';
const body =
cleanText(form.querySelector('.body').value);
if (!body){ toast('Bình luận trống hoặc
chứa link, sửa lại nhé'); return; }
if (postData({type:'comment',
loai:'deal', id:btn.dataset.id, nickname:nick, noi_dung:body})){
const list =
form.parentElement.querySelector('.cmt-list');
list.insertAdjacentHTML('afterbegin',
cmtHTML({nickname:nick, time:'vừa xong', noi_dung:body}));
form.querySelector('.body').value = '';
track('comment', {loai:'deal',
id:btn.dataset.id});
toast('Đã gửi bình luận');
}
});
});
}
/* ============ TIPS
============ */
function renderTips(){
const grid = document.getElementById('tipGrid');
grid.innerHTML = '';
const items = TIPS.filter(t => t.an==='');
if (!items.length){ grid.innerHTML = '<div
class="state">Mẹo hay đang được cập nhật.</div>'; return; }
items.forEach(t => {
const [tc,tbg] = tColor(t.danh_muc);
const likes = likesFor(t.id, t.huu_ich);
const nc = commentsFor('tip', t.id).length;
const el = document.createElement('div');
el.className = 'tcard';
el.innerHTML =
'<div
class="tchips"><span class="tchip"
style="color:'+tc+';background:'+tbg+';">'+esc(t.danh_muc)+'</span>'
+
(t.tiet_kiem ? '<span
class="tchip save">Tiết kiệm '+esc(t.tiet_kiem)+'</span>' :
'') + '</div>' +
'<h3>'+esc(t.tieu_de)+'</h3>'
+
'<div
class="sum">'+esc(t.tom_tat)+'</div>' +
'<div
class="tfoot"><span>👍
Hữu ích ('+fmtNum(likes)+') · 💬
'+nc+'</span><span class="more">Đọc tiếp
→</span></div>';
el.addEventListener('click', () =>
openTip(t));
grid.appendChild(el);
});
}
function openTip(t){
currentTip = t;
const [tc,tbg] = tColor(t.danh_muc);
document.getElementById('tipMChips').innerHTML =
'<span class="tchip"
style="color:'+tc+';background:'+tbg+';">'+esc(t.danh_muc)+'</span>'
+
(t.tiet_kiem ? '<span class="tchip
save">Tiết kiệm '+esc(t.tiet_kiem)+'</span>' : '') +
(t.do_kho ? '<span
class="tchip"
style="color:#6f6a80;background:#f1efe8;">Độ khó:
'+esc(t.do_kho)+'</span>' : '');
document.getElementById('tipMTitle').textContent = t.tieu_de;
document.getElementById('tipMBody').textContent = t.noi_dung ||
t.tom_tat;
document.getElementById('tipMSrc').innerHTML = t.nguon ? 'Nguồn tham
khảo: <a href="'+esc(t.nguon)+'" target="_blank"
rel="noopener">'+esc(t.nguon)+'</a>' : '';
document.getElementById('tipLikeN').textContent = fmtNum(likesFor(t.id,
t.huu_ich));
const likeBtn = document.getElementById('tipLikeBtn');
likeBtn.classList.toggle('liked',
!!localStorage.getItem('liked_'+t.id));
const cmts = commentsFor('tip', t.id);
document.getElementById('tipCmtN').textContent = cmts.length;
document.getElementById('tipCmtList').innerHTML = cmts.length ?
cmts.map(cmtHTML).join('') : '<div class="cmt"><div
class="txt">Chưa có bình luận, làm người đầu tiên nhé!</div></div>';
document.getElementById('tipOverlay').classList.add('open');
track('tip_read', {tip_id:t.id, tip_title:t.tieu_de});
}
document.getElementById('tipLikeBtn').addEventListener('click',
() => {
if (!currentTip || localStorage.getItem('liked_'+currentTip.id)) return;
if (postData({type:'rating', loai:'tip', id:currentTip.id,
diem:'like'})){
localStorage.setItem('liked_'+currentTip.id, '1');
const n =
document.getElementById('tipLikeN');
n.textContent =
fmtNum(parseFloat(n.textContent.replace(/\./g,''))+1);
document.getElementById('tipLikeBtn').classList.add('liked');
toast('Cảm ơn bạn!');
}
});
document.getElementById('tipCmtSend').addEventListener('click',
() => {
if (!currentTip) return;
const nick =
document.getElementById('tipNick').value.trim().substring(0,30) || 'Ẩn danh';
const body = cleanText(document.getElementById('tipCmtInput').value);
if (!body){ toast('Bình luận trống hoặc chứa link, sửa lại nhé');
return; }
if (postData({type:'comment', loai:'tip', id:currentTip.id,
nickname:nick, noi_dung:body})){
document.getElementById('tipCmtList').insertAdjacentHTML('afterbegin',
cmtHTML({nickname:nick, time:'vừa xong', noi_dung:body}));
document.getElementById('tipCmtInput').value = '';
toast('Đã gửi bình luận');
}
});
/* ============ JOBS
============ */
function renderJobs(){
const grid = document.getElementById('jobGrid');
grid.innerHTML = '';
const items = JOBS.filter(j => j.an==='');
if (!items.length){ grid.innerHTML = '<div
class="state">Job đang được cập nhật.</div>'; return; }
items.forEach(j => {
const icon =
(j.nhom||'').toLowerCase()==='creator' ? '🎬'
: '🔗';
const el = document.createElement('div');
el.className = 'jcard
'+esc((j.nhom||'').toLowerCase());
el.innerHTML =
'<h3>'+icon+'
'+esc(j.tieu_de)+'</h3>' +
'<p>'+esc(j.mo_ta)+'</p>' +
'<div
class="jtags">'+(j.tags||'').split('|').filter(Boolean).map(t
=> '<span
class="jtag">'+esc(t.trim())+'</span>').join('')+'</div>'
+
'<a
href="'+esc(j.link_dang_ky||'#')+'" target="_blank"
rel="noopener" data-id="'+esc(j.id)+'">Đăng ký
ngay</a>';
el.querySelector('a').addEventListener('click', () =>
track('job_signup_click', {job_id:j.id, nhom:j.nhom}));
grid.appendChild(el);
});
}
/* ============ NEWSLETTER + NÚT
HEADER ============ */
function subscribe(inputId){
const v = document.getElementById(inputId).value.trim();
if (v.length < 5){ toast('Nhập email hoặc số Zalo hợp lệ nhé');
return; }
if (postData({type:'subscribe', contact:v.substring(0,120)})){
document.getElementById(inputId).value =
'';
document.getElementById('bellOverlay').classList.remove('open');
track('subscribe', {});
toast('Đăng ký thành công, hẹn gặp thứ 2
nhé!');
}
}
document.getElementById('newsForm').addEventListener('submit',
e => { e.preventDefault(); subscribe('newsContact'); });
document.getElementById('bellSend').addEventListener('click',
() => subscribe('bellContact'));
document.getElementById('bellBtn').addEventListener('click',
() => document.getElementById('bellOverlay').classList.add('open'));
document.getElementById('joinBtn').addEventListener('click',
() => {
if (CFG.join_link){ track('join_click', {}); window.open(CFG.join_link,
'_blank'); }
else
document.getElementById('crew').scrollIntoView({behavior:'smooth'});
});
document.querySelectorAll('.overlay
.close').forEach(c => c.addEventListener('click', () =>
document.getElementById(c.dataset.close).classList.remove('open')));
document.querySelectorAll('.overlay').forEach(o
=> o.addEventListener('click', e => { if (e.target===o)
o.classList.remove('open'); }));
/* ============ CONFIG
============ */
function applyConfig(){
const set = (id, key, html) => { if (CFG[key]){ const el =
document.getElementById(id); if (html) el.innerHTML = CFG[key]; else
el.textContent = CFG[key]; } };
set('siteName','site_name'); set('heroBadge','badge');
set('heroSub','hero_sub');
set('newsTitle','newsletter_title'); set('newsSub','newsletter_sub');
set('footNote','footer_note');
if (CFG.hero_title) document.getElementById('heroTitle').innerHTML =
esc(CFG.hero_title).replace(/\n/g,'<br>');
document.getElementById('statDeals').textContent = CFG.stat_deals ||
DEALS.length;
document.getElementById('statClaims').textContent = CFG.stat_claims ||
fmtNum(DEALS.reduce((a,d)=>a+(parseFloat(d.luot_lay)||0),0));
if (CFG.stat_rating) document.getElementById('statRating').textContent =
CFG.stat_rating;
else {
const rs = DEALS.map(d =>
ratingFor('deal', d.id, d.sao, d.luot_danh_gia)).filter(r => r.n);
const avg = rs.length ?
Math.round(rs.reduce((a,r)=>a+r.avg,0)/rs.length*10)/10 : '--';
document.getElementById('statRating').textContent = avg + '★';
}
const d = new Date();
if (!CFG.badge) document.getElementById('heroBadge').textContent = '🚀
MỚI CẬP NHẬT '+d.getDate()+'/'+(d.getMonth()+1);
}
/* ============ KHỞI ĐỘNG
============ */
(async function init(){
const [vouchers, auto, tips, jobs, config, comments, ratings] = await
Promise.all([
safeTab('vouchers'),
safeTab('auto_voucher'), safeTab('tips'),
safeTab('jobs'), safeTab('config'),
safeTab('comments'), safeTab('ratings'),
]);
COMMENTS = comments; RATINGS = ratings;
const norm = d => Object.assign({hot:'',an:''}, d,
{hot:(d.hot||'').trim(), an:(d.an||'').trim()});
DEALS = [...vouchers, ...auto].map(norm).filter(d => d.id &&
d.an==='' && d.tieu_de);
TIPS = tips.map(norm).filter(t => t.id && t.tieu_de);
JOBS = jobs.map(norm).filter(j => j.id && j.tieu_de);
config.forEach(r => { if (r.key) CFG[r.key.trim()] =
(r.value||'').trim(); });
if (!DEALS.length){
document.getElementById('dealGrid').innerHTML = '<div
class="state">Không tải được deal, thử tải lại trang
nhé.</div>';
} else { renderTabs(); renderDeals('Tất cả'); }
renderTips();
renderJobs();
applyConfig();
})();
</script>
</body>
</html>