PageSourceSearch

https://fresh-campaign-spark.lovable.app/assets/SpinTheWheel-CQHxasmd.js

js fresh-campaign-spark.lovable.app collected 2026-10-03 03:27:33 UTC 39,032 bytes, 726 lines download raw bytes

1import{r,j as t}from"./index-9ZedHETz.js";const a=`
2<div id="ctf-wheel-root" aria-live="polite"></div>
3
4<style>
5  .om-element-html:has(#ctf-wheel-root),
6  .om-html:has(#ctf-wheel-root),
7  [class*="col"]:has(#ctf-wheel-root),
8  [class*="row"]:has(#ctf-wheel-root),
9  [class*="html"]:has(#ctf-wheel-root) {
10    width:100% !important; max-width:100% !important; flex:1 1 100% !important;
11    padding:0 !important; margin:0 !important;
12  }
13
14  #ctf-wheel-root {
15    --bg-0:    #0a0608;
16    --bg-1:    #0f0f0f;
17    --glass:   rgba(255,255,255,.05);
18    --glass-2: rgba(255,255,255,.08);
19    --stroke:  rgba(239,32,32,.18);
20    --ink:     #f5f5f5;
21    --muted:   #a0a0a0;
22    --accent:  #ef2222;
23    --accent-2:#ff5252;
24    --danger:  #ff6b6b;
25    --lv-starter:  #ff5252;
26    --lv-explorer: #ef2222;
27    --lv-operator: #c81e1e;
28    --lv-elite:    #ffb347;
29
30    --r-lg: 22px; --r-md: 14px; --r-sm: 10px;
31    --sans: "Plus Jakarta Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
32    --orbitron: "Outfit", ui-sans-serif, system-ui, sans-serif;
33
34    position:relative; isolation:isolate;
35    font-family:var(--sans); color:var(--ink);
36    display:block; width:100%; max-width:100%;
37    margin:0; padding:32px clamp(20px, 4vw, 48px);
38    box-sizing:border-box; overflow:hidden;
39    background:
40      radial-gradient(130% 90% at 50% -20%, color-mix(in srgb, var(--accent) 28%, transparent), transparent 55%),
41      linear-gradient(180deg, var(--bg-0), var(--bg-1));
42    border:1px solid var(--stroke);
43    box-shadow: 0 24px 60px rgba(0,0,0,.5), inset 0 1px 0 rgba(255,255,255,.06);
44  }
45
46
47  #ctf-wheel-root .cw-grid{
48    display:grid; grid-template-columns: 1fr; gap:32px; align-items:center;
49  }
50  @media (min-width: 900px){
51    #ctf-wheel-root .cw-grid{ grid-template-columns: 1.05fr 1fr; gap:56px; align-items:center; }
52    #ctf-wheel-root .cw-left{ text-align:left; }
53  }
54  @media (min-width: 1200px){
55    #ctf-wheel-root .cw-grid{ grid-template-columns: 1fr 1fr; gap:72px; }
56  }
57  #ctf-wheel-root .cw-right{ display:flex; flex-direction:column; align-items:center; }
58
59  #ctf-wheel-root *, #ctf-wheel-root *::before, #ctf-wheel-root *::after { box-sizing:border-box; }
60
61  #ctf-wheel-root::before{
62    content:""; position:absolute; inset:-40% -20% auto -20%; height:80%; z-index:-1;
63    background:radial-gradient(50% 50% at 50% 50%, color-mix(in srgb, var(--lvc, var(--accent)) 30%, transparent), transparent 70%);
64    filter:blur(40px); opacity:.6; animation:cw-aura 7s ease-in-out infinite alternate;
65  }
66  @keyframes cw-aura { from{transform:translateY(-6%) scale(1)} to{transform:translateY(4%) scale(1.08)} }
67
68  #ctf-wheel-root .cw-head { text-align:center; margin-bottom:18px; }
69  #ctf-wheel-root .cw-eyebrow{
70    display:inline-block; font-size:10.5px; letter-spacing:.22em; text-transform:uppercase;
71    color:color-mix(in srgb, var(--lvc, var(--accent)) 80%, white 20%); font-weight:700; margin:0 0 8px;
72    padding:4px 11px; border-radius:999px; background:var(--glass); border:1px solid var(--stroke);
73    backdrop-filter:blur(6px);
74  }
75  #ctf-wheel-root .cw-title{
76    font-family:var(--orbitron); font-size:28px; font-weight:900; margin:0; letter-spacing:.02em; line-height:1.12;
77    background:linear-gradient(180deg,#fff, #c7cfe6); -webkit-background-clip:text; background-clip:text; color:transparent;
78  }
79  #ctf-wheel-root .cw-sub{ font-size:13px; color:var(--muted); margin:8px auto 0; line-height:1.5; max-width:30ch; }
80
81  /* Stats bar */
82  #ctf-wheel-root .cw-stats{
83    display:flex; justify-content:center; gap:12px; margin-bottom:18px;
84  }
85  #ctf-wheel-root .cw-stat{
86    flex:1; text-align:center; padding:12px;
87    background:rgba(255,255,255,.04); border:1px solid rgba(255,255,255,.08); border-radius:16px;
88  }
89  #ctf-wheel-root .cw-stat-label{
90    font-size:9px; text-transform:uppercase; letter-spacing:.15em; color:var(--muted); font-weight:700; margin-bottom:4px;
91  }
92  #ctf-wheel-root .cw-stat-val{
93    font-family:var(--orbitron); font-size:20px; font-weight:900;
94  }
95  #ctf-wheel-root .cw-stat-streak{ color:#facc15; text-shadow:0 0 12px rgba(250,204,21,.4); }
96  #ctf-wheel-root .cw-stat-jackpot{ color:#ff5252; text-shadow:0 0 14px rgba(239,32,32,.5); }
97
98  #ctf-wheel-root .cw-levels{ display:grid; gap:11px; margin-top:20px; }
99
100  /* Offer banner */
101  #ctf-wheel-root .cw-offer{
102    position:relative; text-align:center;
103    padding:22px 16px 20px; border-radius:18px; overflow:hidden;
104    background:
105      radial-gradient(120% 120% at 50% 0%, rgba(255,82,82,.18), transparent 60%),
106      linear-gradient(180deg, rgba(255,255,255,.04), rgba(255,255,255,.02));
107    border:1px solid rgba(255,82,82,.28);
108    box-shadow:0 10px 40px rgba(239,32,32,.18), inset 0 1px 0 rgba(255,255,255,.06);
109  }
110  #ctf-wheel-root .cw-offer::before{
111    content:""; position:absolute; inset:0; pointer-events:none; opacity:.35;
112    background:
113      radial-gradient(2px 2px at 12% 30%, #ff7575 50%, transparent 60%),
114      radial-gradient(2px 2px at 86% 22%, #ef2222 50%, transparent 60%),
115      radial-gradient(1.5px 1.5px at 22% 78%, #ffd166 50%, transparent 60%),
116      radial-gradient(1.5px 1.5px at 78% 82%, #ff7575 50%, transparent 60%);
117    filter:blur(.4px);
118  }
119  #ctf-wheel-root .cw-ribbon{
120    display:inline-flex; flex-direction:column; align-items:stretch; gap:0; margin-bottom:14px;
121    transform:rotate(-1.5deg);
122  }
123  #ctf-wheel-root .cw-ribbon-top{
124    align-self:center; background:#facc15;
124 color:#1a1306;
125    font-family:var(--orbitron); font-weight:900; font-size:11px; letter-spacing:.18em;
126    padding:5px 14px; border-radius:4px; text-transform:uppercase;
127    box-shadow:0 4px 14px rgba(250,204,21,.35);
128  }
129  #ctf-wheel-root .cw-ribbon-main{
130    margin-top:-3px; padding:9px 22px; border-radius:6px;
131    background:linear-gradient(135deg,#ff3b3b,#c81e1e);
132    color:#fff; font-family:'Georgia', serif; font-style:italic; font-weight:700; font-size:22px;
133    letter-spacing:.01em; box-shadow:0 8px 22px rgba(220,38,38,.45);
134    border-top:1px solid rgba(255,255,255,.2);
135  }
136  #ctf-wheel-root .cw-offer-pre{
137    font-family:var(--sans); font-style:italic; font-weight:600; font-size:13px;
138    color:#e9eef9; opacity:.95; margin:0 0 4px;
139  }
140  #ctf-wheel-root .cw-offer-h{
141    font-family:var(--orbitron); font-weight:900; font-size:20px; line-height:1.2;
142    margin:0; color:#fff;
143  }
144  #ctf-wheel-root .cw-offer-script{
145    font-family:'Brush Script MT','Snell Roundhand',cursive; font-style:italic;
146    font-size:26px; margin:8px 0 2px; line-height:1; letter-spacing:.01em;
147    background:linear-gradient(180deg,#ff7575,#ef2222);
148    -webkit-background-clip:text; background-clip:text; color:transparent;
149    text-shadow:0 2px 10px rgba(239,32,32,.25);
150  }
151  #ctf-wheel-root .cw-offer-cta{
152    display:inline-flex; align-items:center; gap:8px; margin-top:10px;
153    font-family:var(--sans); font-weight:800; font-size:13px; color:#ff7575;
154    letter-spacing:.04em; text-transform:uppercase;
155  }
156  #ctf-wheel-root .cw-offer-cta::before{ content:"\\1F3AF"; font-size:14px; }
157  #ctf-wheel-root .cw-offer-cta::after{
158    content:""; width:18px; height:1px; background:#ff7575; opacity:.6;
159  }
160
161  /* Left column — branded promo */
162  #ctf-wheel-root .cw-left{ display:flex; flex-direction:column; gap:22px; }
163  #ctf-wheel-root .cw-tag{
164    align-self:flex-start; display:inline-flex; align-items:center; gap:8px;
165    padding:6px 14px; border-radius:999px;
166    background:hsl(0 85% 50% / .12); border:1px solid hsl(0 85% 50% / .4);
167    color:#ff7575; font-family:var(--sans); font-weight:800; font-size:11px;
168    letter-spacing:.22em; text-transform:uppercase;
169  }
170  #ctf-wheel-root .cw-tag::before{
171    content:""; width:6px; height:6px; border-radius:50%;
172    background:#ff5252; box-shadow:0 0 10px #ff5252;
173    animation:cw-dot 1.4s ease-in-out infinite;
174  }
175  @keyframes cw-dot { 0%,100%{opacity:1} 50%{opacity:.35} }
176
177  #ctf-wheel-root .cw-promo-h{
178    font-family:var(--orbitron); font-weight:900;
179    font-size:clamp(34px, 4.4vw, 52px); line-height:1.02; letter-spacing:-.025em;
180    margin:0; color:#fff;
181  }
182  #ctf-wheel-root .cw-promo-h .cw-num{
183    background:linear-gradient(180deg, #ff7575, #ef2222);
184    -webkit-background-clip:text; background-clip:text; color:transparent;
185    text-shadow:0 0 30px hsl(0 85% 50% / .25);
186  }
187  #ctf-wheel-root .cw-promo-sub{
188    margin:0; color:var(--muted); font-size:15px; line-height:1.55; max-width:42ch;
189  }
190  #ctf-wheel-root .cw-promo-sub b{ color:#fff; font-weight:700; }
191
192  /* Inventory stat row */
193  #ctf-wheel-root .cw-inv{
194    display:grid; grid-template-columns:repeat(3, 1fr); gap:10px; margin-top:4px;
195  }
196  #ctf-wheel-root .cw-inv-cell{
197    padding:14px 12px; border-radius:14px; text-align:center;
198    background:linear-gradient(180deg, rgba(255,255,255,.04), rgba(255,255,255,.02));
199    border:1px solid rgba(255,82,82,.18);
200  }
201  #ctf-wheel-root .cw-inv-num{
202    font-family:var(--orbitron); font-weight:900; font-size:22px;
203    color:#fff; letter-spacing:-.02em; line-height:1;
204  }
205  #ctf-wheel-root .cw-inv-num.is-red{ color:#ff5252; text-shadow:0 0 14px rgba(239,32,32,.5); }
206  #ctf-wheel-root .cw-inv-lbl{
207    margin-top:6px; font-size:10px; font-weight:700; letter-spacing:.16em;
208    text-transform:uppercase; color:var(--muted);
209  }
210
211  /* Prize bullets */
212  #ctf-wheel-root .cw-perks{ display:flex; flex-direction:column; gap:9px; margin-top:4px; }
213  #ctf-wheel-root .cw-perk{
214    display:flex; align-items:center; gap:12px;
215    padding:11px 14px; border-radius:12px;
216    background:rgba(255,255,255,.03); border:1px solid rgba(255,255,255,.06);
217    font-size:13.5px; color:#e8e8e8;
218  }
219  #ctf-wheel-root .cw-perk-ic{
220    flex:0 0 auto; width:26px; height:26px; border-radius:8px;
221    display:flex; align-items:center; justify-content:center; font-size:14px;
222    background:hsl(0 85% 50% / .15); border:1px solid hsl(0 85% 50% / .35); color:#ff7575;
223  }
224  #ctf-wheel-root .cw-perk b{ color:#fff; font-weight:700; }
225
226  /* Trust line */
227  #ctf-wheel-root .cw-trust{
228    display:flex; align-items:center; gap:14px; flex-wrap:wrap;
229    margin-top:2px; color:var(--muted); font-size:12px;
230  }
231  #ctf-wheel-root .cw-trust b{ color:#fff; }
232  #ctf-wheel-root .cw-trust .dot{ width:3px; height:3px; border-radius:50%; background:var(--muted); opacity:.5; }
233
234
235  #ctf-wheel-root .cw-level{
236    --lvc:var(--accent);
237    position:relative; text-align:left; width:100%; cursor:pointer; overflow:hidden;
238    background:var(--glass); border:1px solid var(--stroke); border-radius:var(--r-md);
239    padding:14px 15px; color:var(--ink); font-family:var(--sans);
240    display:flex; align-items:center; gap:14px; backdrop-filter:blur(10px);
241    transition:transform .14s ease, border-color .18s ease, background .18s ease, box-shadow .18s ease;
242  }
243  #ctf-wheel-root .cw-level[data-l="Starter"]  { --lvc:var(--lv-starter); }
244  #ctf-wheel-root .cw-level[data-l="Explorer"] { --lvc:var(--lv-explorer); }
245  #ctf-wheel-root .cw-level[data-l="Operator"] { --lvc:var(--lv-operator); }
246  #ctf-wheel-root .cw-level[data-l="Elite"]    { --lvc:var(--lv-elite); }
247  #ctf-wheel-root .cw-level::after{
248    content:""; position:absolute; left:0; top:0; bottom:0; width:3px; background:var(--lvc);
249    box-shadow:0 0 16px var(--lvc); opacity:.0; transition:opacity .18s ease;
250  }
251  #ctf-wheel-root .cw-level:hover{ transform:translateY(-2px); background:var(--glass-2);
252    border-color:color-mix(in srgb, var(--lvc) 55%, transparent);
253    box-shadow:0 10px 30px rgba(0,0,0,.35); }
254  #ctf-wheel-root .cw-level:hover::after{ opacity:1; }
255  #ctf-wheel-root .cw-level:focus-visible{ outline:2px solid var(--lvc); outline-offset:3px; }
256  #ctf-wheel-root .cw-badge{
257    flex:0 0 auto; width:42px; height:42px; border-radius:12px;
258    display:flex; align-items:center; justify-content:center; font-weight:800; font-size:16px;
259    color:var(--lvc);
260    background:radial-gradient(120% 120% at 30% 20%, color-mix(in srgb, var(--lvc) 28%, transparent), transparent 70%), var(--glass);
261    border:1px solid color-mix(in srgb, var(--lvc) 45%, transparent);
262    box-shadow:0 0 18px color-mix(in srgb, var(--lvc) 30%, transparent);
263  }
264  #ctf-wheel-root .cw-level > .cw-txt{ display:flex; flex-direction:column; min-width:0; }
265  #ctf-wheel-root .cw-level-name{ font-weight:700; font-size:15px; }
266  #ctf-wheel-root .cw-level-arrow{ margin-left:auto; color:var(--lvc); font-size:18px; opacity:.7; transition:transform .18s ease; }
267  #ctf-wheel-root .cw-level:hover .cw-level-arrow{ transform:translateX(3px); opacity:1; }
268
269  #ctf-wheel-root .cw-back{ background:none; border:none;
269 color:var(--muted); font-size:13px; cursor:pointer; padding:0; margin-bottom:8px; display:inline-flex; align-items:center; gap:6px; }
270  #ctf-wheel-root .cw-back:hover{ color:var(--ink); }
271
272  /* Wheel stage - BIGGER */
273  #ctf-wheel-root .cw-stage{ position:relative; width:min(440px, 88vw); aspect-ratio:1/1; height:auto; margin:0 auto 14px; }
274  #ctf-wheel-root .cw-glow{
275    position:absolute; inset:-18px; border-radius:50%; z-index:0;
276    background:radial-gradient(closest-side, color-mix(in srgb, var(--lvc) 45%, transparent), transparent 78%);
277    filter:blur(10px); animation:cw-breathe 3.4s ease-in-out infinite;
278  }
279  @keyframes cw-breathe{ 0%,100%{opacity:.55; transform:scale(.98)} 50%{opacity:.9; transform:scale(1.03)} }
280  #ctf-wheel-root .cw-stage.is-spinning .cw-glow{ animation:cw-breathe-fast .6s ease-in-out infinite; }
281  @keyframes cw-breathe-fast{ 0%,100%{opacity:.8; transform:scale(1)} 50%{opacity:1; transform:scale(1.05)} }
282
283  /* Marquee rim with LED bulbs */
284  #ctf-wheel-root .cw-marquee{
285    position:absolute; inset:-18px; border-radius:50%; z-index:0;
286    border:14px solid #1a1c25;
287    box-shadow: 0 0 60px rgba(0,0,0,.8), inset 0 0 20px rgba(0,0,0,.5);
288  }
289  #ctf-wheel-root .cw-led{
290    position:absolute; width:10px; height:10px; border-radius:50%;
291    background:#ff5252;
292    box-shadow:0 0 10px #ff5252, 0 0 20px rgba(239,32,32,.5);
293    animation:cw-led-blink 1.2s ease-in-out infinite;
294  }
295  #ctf-wheel-root .cw-led:nth-child(1){ top:0; left:50%; transform:translate(-50%,-50%); animation-delay:0s; }
296  #ctf-wheel-root .cw-led:nth-child(2){ top:50%; right:0; transform:translate(50%,-50%); animation-delay:.15s; }
297  #ctf-wheel-root .cw-led:nth-child(3){ bottom:0; left:50%; transform:translate(-50%,50%); animation-delay:.3s; }
298  #ctf-wheel-root .cw-led:nth-child(4){ top:50%; left:0; transform:translate(-50%,-50%); animation-delay:.45s; }
299  #ctf-wheel-root .cw-led:nth-child(5){ top:15%; right:15%; transform:translate(50%,-50%); animation-delay:.6s; }
300  #ctf-wheel-root .cw-led:nth-child(6){ top:15%; left:15%; transform:translate(-50%,-50%); animation-delay:.75s; }
301  #ctf-wheel-root .cw-led:nth-child(7){ bottom:15%; right:15%; transform:translate(50%,50%); animation-delay:.9s; }
302  #ctf-wheel-root .cw-led:nth-child(8){ bottom:15%; left:15%; transform:translate(-50%,50%); animation-delay:1.05s; }
303  @keyframes cw-led-blink{
304    0%,100%{ opacity:1; filter:brightness(1.3); }
305    50%{ opacity:.2; filter:brightness(.4); }
306  }
307
308  /* The wheel */
309  #ctf-wheel-root .cw-wheel{
310    position:relative; z-index:1; width:100%; height:100%; border-radius:50%;
311    transition: transform 4.6s cubic-bezier(.16,.74,.12,1);
312    box-shadow:
313      0 0 0 6px rgba(255,255,255,.04),
314      0 0 0 7px color-mix(in srgb, var(--lvc) 60%, transparent),
315      0 0 40px color-mix(in srgb, var(--lvc) 45%, transparent),
316      0 18px 46px rgba(0,0,0,.6);
317  }
318  #ctf-wheel-root .cw-dome{
319    position:absolute; z-index:2; inset:7px; border-radius:50%; pointer-events:none;
320    background:radial-gradient(120% 80% at 32% 18%, rgba(255,255,255,.35), rgba(255,255,255,.06) 38%, transparent 60%);
321    mix-blend-mode:screen; opacity:.7;
322  }
323
324  /* Metallic pointer */
325  #ctf-wheel-root .cw-pointer{
326    position:absolute; z-index:5; top:-16px; left:50%; transform:translateX(-50%); transform-origin:50% 90%;
327    width:28px; height:44px; filter:drop-shadow(0 4px 6px rgba(0,0,0,.6));
328  }
329  #ctf-wheel-root .cw-pointer.tick{ animation:cw-tick .07s ease-out; }
330  @keyframes cw-tick{ 0%{transform:translateX(-50%) rotate(0)} 45%{transform:translateX(-50%) rotate(-13deg)} 100%{transform:translateX(-50%) rotate(0)} }
331
332  /* Center SPIN hub - BIG & TACTILE */
333  #ctf-wheel-root .cw-hub{
334    position:absolute; z-index:4; top:50%; left:50%; transform:translate(-50%,-50%);
335    width:110px; height:110px; border-radius:50%; cursor:pointer;
336    background:radial-gradient(120% 120% at 30% 25%, #2a1414, #14080a);
337    border:3px solid color-mix(in srgb, var(--lvc) 55%, transparent);
338    box-shadow:0 0 28px color-mix(in srgb, var(--lvc) 40%, transparent), inset 0 1px 2px rgba(255,255,255,.25), 0 8px 0 #0a0608;
339    display:flex; align-items:center; justify-content:center;
340    font-family:var(--orbitron); font-weight:900; font-size:18px;
341    color:color-mix(in srgb, var(--lvc) 80%, white); letter-spacing:.06em;
342    transition:transform .08s ease, box-shadow .18s ease;
343  }
344  #ctf-wheel-root .cw-hub:active{
345    transform:translate(-50%,-46%);
346    box-shadow:0 0 28px color-mix(in srgb, var(--lvc) 40%, transparent), inset 0 1px 2px rgba(255,255,255,.25), 0 2px 0 #0a0608;
347  }
348
349  /* Hero SPIN NOW button */
350  #ctf-wheel-root .cw-btn{
351    position:relative; display:block; width:100%; margin-top:20px; padding:18px 20px;
352    font-family:var(--orbitron); font-size:18px; font-weight:900; letter-spacing:.08em; overflow:hidden;
353    color:#fff; border:none; border-radius:18px; cursor:pointer;
354    background:linear-gradient(135deg, #ff5252, #c81e1e);
355    box-shadow:0 0 30px rgba(239,32,32,.4), 0 8px 0 #8a0f0f, inset 0 1px 0 rgba(255,255,255,.3);
356    transition:transform .08s ease, box-shadow .18s ease, opacity .2s ease;
357  }
358  #ctf-wheel-root .cw-btn::after{
359    content:""; position:absolute; top:0; left:-60%; width:40%; height:100%;
360    background:linear-gradient(100deg, transparent, rgba(255,255,255,.55), transparent);
361    transform:skewX(-18deg); animation:cw-sheen 3.2s ease-in-out infinite;
362  }
363  @keyframes cw-sheen{ 0%,60%{left:-60%} 85%,100%{left:140%} }
364  #ctf-wheel-root .cw-btn:hover:not(:disabled){ transform:translateY(-1px); }
365  #ctf-wheel-root .cw-btn:disabled{ opacity:.45; cursor:not-allowed; }
366  #ctf-wheel-root .cw-btn:disabled::after{ display:none; }
367  #ctf-wheel-root .cw-btn:active:not(:disabled){
368    transform:translateY(2px);
369    box-shadow:0 0 20px rgba(239,32,32,.3), 0 3px 0 #8a0f0f, inset 0 1px 0 rgba(255,255,255,.3);
370  }
371
372  #ctf-wheel-root .cw-msg{ margin-top:14px; font-size:13px; text-align:center; min-height:20px; transition:opacity .2s; }
373  #ctf-wheel-root .cw-msg.is-win{ color:var(--accent-2); font-weight:800; font-size:15px; }
374  #ctf-wheel-root .cw-msg.is-warn{ color:var(--danger); }
375  #ctf-wheel-root .cw-msg.pop{ animation:cw-pop .5s cubic-bezier(.2,1.4,.4,1); }
376  @keyframes cw-pop{ 0%{transform:scale(.6); opacity:0}
376 60%{transform:scale(1.12)} 100%{transform:scale(1); opacity:1} }
377
378  #ctf-wheel-root .cw-level-tag{
379    display:inline-block; font-size:11px; font-weight:700; padding:4px 11px; border-radius:999px; margin-bottom:6px;
380    color:var(--lvc); background:color-mix(in srgb, var(--lvc) 16%, transparent);
381    border:1px solid color-mix(in srgb, var(--lvc) 42%, transparent);
382  }
383
384  /* Legend - scoreboard style */
385  #ctf-wheel-root .cw-legend{ margin-top:18px; }
386  #ctf-wheel-root .cw-leg-head{
387    font-size:9px; letter-spacing:.15em; text-transform:uppercase; color:var(--muted);
388    font-weight:700; margin-bottom:12px; text-align:center;
389  }
390  #ctf-wheel-root .cw-leg-row{
391    display:flex; align-items:center; gap:10px; padding:10px 14px;
392    background:rgba(255,255,255,.03); border:1px solid rgba(255,255,255,.05); border-radius:12px;
393    margin-bottom:6px; transition:background .15s ease;
394  }
395  #ctf-wheel-root .cw-leg-row:hover{ background:rgba(255,255,255,.06); }
396  #ctf-wheel-root .cw-leg-dot{ flex:0 0 auto; width:10px; height:10px; border-radius:50%;
397    box-shadow:0 0 8px currentColor; }
398  #ctf-wheel-root .cw-leg-cat{ font-weight:700; font-size:13px; color:var(--ink); }
399  #ctf-wheel-root .cw-leg-count{ margin-left:auto; color:var(--muted); font-size:11.5px;
400    font-variant-numeric:tabular-nums; }
401
402  #ctf-wheel-root .cw-pending{ margin-top:18px; border-top:1px solid var(--stroke); padding-top:15px; }
403  #ctf-wheel-root .cw-pending h4{ font-size:11px; letter-spacing:.12em; text-transform:uppercase; color:var(--muted); margin:0 0 11px; }
404  #ctf-wheel-root .cw-lab{
405    display:flex; align-items:center; justify-content:space-between; gap:11px;
406    background:var(--glass); border:1px solid var(--stroke); border-radius:var(--r-md);
407    padding:11px 13px; margin-bottom:9px; backdrop-filter:blur(8px);
408  }
409  #ctf-wheel-root .cw-lab-name{ font-size:13px; font-weight:600; }
410  #ctf-wheel-root .cw-lab-exp{ font-size:11px; color:var(--danger); margin-top:3px; }
411  #ctf-wheel-root .cw-launch{
412    font-size:12px; font-weight:800; color:#0a0e1a; border:none; border-radius:9px; padding:9px 13px; cursor:pointer; white-space:nowrap;
413    background:linear-gradient(180deg, color-mix(in srgb, var(--accent-2) 90%, white), var(--accent-2));
414    box-shadow:0 4px 14px color-mix(in srgb, var(--accent-2) 35%, transparent);
415  }
416
417  #ctf-wheel-root .cw-burst{
418    position:absolute; z-index:6; top:50%; left:50%; width:10px; height:10px; border-radius:50%;
419    border:2px solid var(--lvc); transform:translate(-50%,-50%) scale(0); opacity:0; pointer-events:none;
420  }
421  #ctf-wheel-root .cw-burst.go{ animation:cw-burst .7s ease-out; }
422  @keyframes cw-burst{ 0%{transform:translate(-50%,-50%) scale(0); opacity:.9} 100%{transform:translate(-50%,-50%) scale(26); opacity:0} }
423
424  @media (prefers-reduced-motion: reduce){
425    #ctf-wheel-root::before,
426    #ctf-wheel-root .cw-glow,
427    #ctf-wheel-root .cw-btn::after,
428    #ctf-wheel-root .cw-led,
429    #ctf-wheel-root .cw-burst.go { animation:none !important; }
430    #ctf-wheel-root .cw-wheel{ transition:transform .3s linear; }
431  }
432</style>
433`,o=`
434(function(){
435  'use strict';
436
437  var CONFIG = {
438    API_BASE: 'https://your-api.example.com',
439    AUTH: 'cookie',
440    TOKEN_GETTER: function(){ return (window.MY_APP && window.MY_APP.token) || ''; },
441    SPIN_DURATION_MS: 4600,
442    DEMO_MODE: true
443  };
444
445  function demoReq(path, method, body){
446    return new Promise(function(resolve){
447      setTimeout(function(){
448        if(path.indexOf('/api/wheel/status')===0){
449          resolve({ canSpin:true, pendingLabs:[] });
450        } else if(path.indexOf('/api/wheel/spin')===0){
451          var segs = state.segs || [];
452          var idx = (typeof pickWeightedIndex==='function') ? pickWeightedIndex() : Math.floor(Math.random()*segs.length);
453          var seg = segs[idx] || {label:'Mystery',cat:'Drop',discount:'30% OFF',prize:'Mystery Drop'};
454          resolve({ segmentIndex: idx, prize:{ name: seg.prize || (seg.label+' — '+seg.cat), discount: seg.discount || seg.label } });
455        } else { resolve({}); }
456      }, 220);
457    });
458  }
459
460  var LEVELS = [
461    { id:'Starter',  badge:'01', desc:'' },
462    { id:'Explorer', badge:'02', desc:'' },
463    { id:'Operator', badge:'03', desc:'' },
464    { id:'Elite',    badge:'04', desc:'' }
465  ];
466
467  var SEED = {"Starter":[{"label":"Break the DB","cat":"SQL Injection","labs":5,"ctf":3},{"label":"Own the Server","cat":"LFI / RCE","labs":7,"ctf":5},{"label":"Become Root","cat":"Linux PrivEsc","labs":11,"ctf":7},{"label":"Dig the Memory","cat":"Forensics","labs":8,"ctf":6},{"label":"Hunt the Logs","cat":"Event Logs","labs":5,"ctf":4},{"label":"Break the Cipher","cat":"Cryptography","labs":8,"ctf":5},{"label":"First Blood","cat":"Tooling","labs":4,"ctf":2}],"Explorer":[{"label":"Break the DB","cat":"SQL Injection","labs":5,"ctf":3},{"label":"Own the Server","cat":"LFI / RCE","labs":7,"ctf":5},{"label":"Crack the CMS","cat":"WordPress","labs":8,"ctf":5},{"label":"Dig the Memory","cat":"Forensics","labs":8,"ctf":6},{"label":"Reverse It","cat":"Malware RE","labs":7,"ctf":5},{"label":"Find the Hidden","cat":"Steganography","labs":4,"ctf":3},{"label":"Track the Target","cat":"OSINT","labs":2,"ctf":1}],"Operator":[{"label":"Break the DB","cat":"SQL Injection","labs":5,"ctf":3},{"label":"Own the Server","cat":"LFI / RCE","labs":7,"ctf":5},{"label":"Sneak a Shell","cat":"File Upload","labs":3,"ctf":2},{"label":"Crack the CMS","cat":"WordPress","labs":8,"ctf":5},{"label":"Become Root","cat":"Linux PrivEsc","labs":11,"ctf":7},{"label":"Get SYSTEM","cat":"Win PrivEsc","labs":4,"ctf":2},{"label":"Read the Wire","cat":"Network Forensics","labs":5,"ctf":3},{"label":"Dig the Memory","cat":"Forensics","labs":8,"ctf":6}],"Elite":[{"label":"Seize the Domain","cat":"AD / Kerberos","labs":2,"ctf":1},{"label":"Read the Wire","cat":"Network Forensics","labs":5,"ctf":3},{"label":"Smash the Stack","cat":"Buffer Overflow","labs":2,"ctf":1},{"label":"Break the Cipher","cat":"Cryptography","labs":8,"ctf":5},{"label":"Breach the Cloud","cat":"Cloud","labs":1,"ctf":0},{"label":"Hack the Plant","cat":"ICS / OT","labs":2,"ctf":1},{"label":"Poison the Model","cat":"AI / ML","labs":3,"ctf":2}]};
468
469  var SEG_COLORS = ['#ef2222','#1a1a1a','#ff5252','#2a1414','#c81e1e','#0f0f0f','#ff7575','#8a0f0f'];
470  var LV_VAR = { Starter:'--lv-starter', Explorer:'--lv-explorer', Operator:'--lv-operator', Elite:'--lv-elite' };
471
472  var root = document.getElementById('ctf-wheel-root');
473  if(!root) return;
474  var state = { level:null, spinning:false, canSpin:false, segs:[] };
475
476  function headers(){ var h={'Content-Type':'application/json'};
477    if(CONFIG.AUTH==='token') h['Authorization']='Bearer '+CONFIG.TOKEN_GETTER(); return h; }
478  function req(path,method,body){
479    if(CONFIG.DEMO_MODE) return demoReq(path, method, body);
480    return fetch(CONFIG.API_BASE+path,{ method:method||'GET', headers:headers(),
481      credentials:CONFIG.AUTH==='cookie'?'include':'same-origin',
482      body:body?JSON.stringify(body):undefined
483    }).then(function(r){
484      if(r.status===401) throw new Error('NOT_LOGGED_IN');
485      if(!r.ok) throw new Error('HTTP_'+r.status); return r.json();
486    });
487  }
488
489  // Hybrid prize set — promotes labs + CTFs, every spin wins.
490  // weight = relative odds of landing here (higher = more frequent).
491  var PRIZES = [
492    { label:'70% OFF',     cat:'CTF Bundle',     discount:'70% OFF', prize:'Any CTF Bundle',           weight:1  },
493    { label:'50% OFF',     cat:'Labs Library',   discount:'50% OFF', prize:'Labs Library',             weight:3  },
494    { label:'FREE CTF',    cat:'1 Challenge',    discount:'FREE',    prize:'1 CTF Challenge',          weight:2  },
495    { label:'30% OFF',     cat:'All-Access',     discount:'30% OFF', prize:'All-Access Subscription',  weight:4  },
496    { label:'FREE LAB',    cat:'24h Access',     discount:'FREE',    prize:'Hands-On Lab (24h)',       weight:2  },
497    { label:'40% OFF',     cat:'Starter Pack',   discount:'40% OFF', prize:'Starter Pack',             weight:4  },
498    { label:'20% OFF',     cat:'Sitewide',       discount:'20% OFF', prize:'Sitewide Purchase',        weight:5  },
499    { label:'MYSTERY',     cat:'Surprise Drop',  discount:'60% OFF', prize:'Mystery Drop',             weight:1  }
500  ];
501
502  function buildCombinedSegs(){ return PRIZES; }
503
504  function pickWeightedIndex(){
505    var total = 0;
506    for(var i=0;i<PRIZES.length;i++) total += PRIZES[i].weight;
507    var r = Math.random()*total, acc = 0;
508    for(var j=0;j<PRIZES.length;j++){
509      acc += PRIZES[j].weight;
510      if(r <= acc) return j;
511    }
512    return PRIZES.length-1;
513  }
514
515
516  function buildWheelSVG(){
517    var segs = state.segs || [];
518    var n = segs.length || 1;
519    var cx=160, cy=160, r=160;
520    var paths='', labels='';
521    for(var i=0;i<n;i++){
522      var a0 = (i/n)*2*Math.PI - Math.PI/2;
523      var a1 = ((i+1)/n)*2*Math.PI - Math.PI/2;
524      var x0 = cx + r*Math.cos(a0), y0 = cy + r*Math.sin(a0);
525      var x1 = cx + r*Math.cos(a1), y1 = cy + r*Math.sin(a1);
526      var large = ((a1-a0) > Math.PI) ? 1 : 0;
527      var col = SEG_COLORS[i % SEG_COLORS.length];
528      paths += '<path d="M'+cx+' '+cy+' L'+x0+' '+y0+' A'+r+' '+r+' 0 '+large+' 1 '+x1+' '+y1+' Z" fill="'+col+'" stroke="rgba(0,0,0,.35)" stroke-width="1.5"/>';
529      var am = (a0+a1)/2;
530      var rot = (am*180/Math.PI) + 90;
531      var lx1 = cx + (r*0.70)*Math.cos(am);
532      var ly1 = cy + (r*0.70)*Math.sin(am);
533      var lx2 = cx + (r*0.50)*Math.cos(am);
534      var ly2 = cy + (r*0.50)*Math.sin(am);
535      var head = esc((segs[i] && segs[i].label) || '');
536      var sub  = esc((segs[i] && segs[i].cat) || '');
537      if(head.length>10) head = head.slice(0,9)+'…';
538      if(sub.length>14) sub = sub.slice(0,13)+'…';
539      labels += '<text x="'+lx1+'" y="'+ly1+'" fill="#fff" font-family="Outfit, sans-serif" font-size="13" font-weight="900" text-anchor="middle" dominant-baseline="middle" transform="rotate('+rot+' '+lx1+' '+ly1+')" style="paint-order:stroke;stroke:rgba(0,0,0,.45);stroke-width:2.5px">'+head+'</text>';
540      labels += '<text x="'+lx2+'" y="'+ly2+'" fill="rgba(255,255,255,.85)" font-family="Plus Jakarta Sans, sans-serif" font-size="9" font-weight="600" text-anchor="middle" dominant-baseline="middle" transform="rotate('+rot+' '+lx2+' '+ly2+')" style="paint-order:stroke;stroke:rgba(0,0,0,.4);stroke-width:1.8px">'+sub+'</text>';
541    }
542    return '<svg class="cw-wheel" viewBox="0 0 320 320" xmlns="http://www.w3.org/2000/svg">'+paths+labels+'</svg>';
543  }
544
545  function renderWheel(){
546    state.level='All';
547    state.segs=buildCombinedSegs();
548    root.style.setProperty('--lvc', getComputedStyle(root).getPropertyValue('--accent-2').trim());
549    root.innerHTML=
550      '<div class="cw-grid">'+
551        '<div class="cw-left">'+
552          '<div class="cw-offer">'+
553            '<div class="cw-ribbon">'+
554              '<span class="cw-ribbon-top">Limited Time</span>'+
555              '<span class="cw-ribbon-main">Special Offer</span>'+
556            '</div>'+
557            '<p class="cw-offer-pre">For the First Time Ever</p>'+
558            '<h3 class="cw-offer-h">Hands-On Virtual Labs<br/>&amp; Capture The Flags</h3>'+
559            '<div class="cw-offer-script">At Exclusive Discounts!</div>'+
560            '<span class="cw-offer-cta">Spin the Wheel &amp; Save Big</span>'+
561          '</div>'+
562          '<h1 class="cw-promo-h"><span class="cw-num">70% OFF</span> CTF Challenges &amp; Hands-On Labs</h1>'+
563          '<p class="cw-promo-sub">One spin. <b>Every wedge wins.</b> Unlock instant discounts &mdash; or a free lab &mdash; across our entire library of real-world cybersecurity challenges.</p>'+
564          '<div class="cw-inv">
564'+
565            '<div class="cw-inv-cell"><div class="cw-inv-num">500<span style="color:#ff5252">+</span></div><div class="cw-inv-lbl">Hands-On Labs</div></div>'+
566            '<div class="cw-inv-cell"><div class="cw-inv-num">400<span style="color:#ff5252">+</span></div><div class="cw-inv-lbl">CTF Challenges</div></div>'+
567            '<div class="cw-inv-cell"><div class="cw-inv-num is-red">70%</div><div class="cw-inv-lbl">Top Discount</div></div>'+
568          '</div>'+
569          '<div class="cw-perks">'+
570            '<div class="cw-perk"><span class="cw-perk-ic">⚑</span><span><b>Capture The Flag</b> &mdash; web, crypto, forensics, AD &amp; more</span></div>'+
571            '<div class="cw-perk"><span class="cw-perk-ic">⚡</span><span><b>Hands-on Virtual Labs</b> spin up in seconds</span></div>'+
572            '<div class="cw-perk"><span class="cw-perk-ic">🎯</span><span>Instant coupon &mdash; <b>redeem in checkout</b></span></div>'+
573          '</div>'+
574          '<div class="cw-trust">'+
575            '<span><b>2,400+</b> learners this month</span>'+
576            '<span class="dot"></span>'+
577            '<span>No signup needed</span>'+
578            '<span class="dot"></span>'+
579            '<span>Coupon valid for 10 min</span>'+
580          '</div>'+
581        '</div>'+
582        '<div class="cw-right">'+
583          '<div class="cw-stage" id="cw-stage">'+
584            '<div class="cw-glow"></div>'+
585            '<div class="cw-marquee">'+
586              '<div class="cw-led"></div><div class="cw-led"></div>'+
587              '<div class="cw-led"></div><div class="cw-led"></div>'+
588              '<div class="cw-led"></div><div class="cw-led"></div>'+
589              '<div class="cw-led"></div><div class="cw-led"></div>'+
590            '</div>'+
591            buildWheelSVG()+
592            '<div class="cw-dome"></div>'+
593            '<svg class="cw-pointer" id="cw-pointer" viewBox="0 0 28 44">'+
594              '<defs><linearGradient id="pg" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#fff"/><stop offset=".5" stop-color="#c3c9dd"/><stop offset="1" stop-color="#8890a8"/></linearGradient></defs>'+
595              '<path d="M14 44 L2 8 Q14 -4 26 8 Z" fill="url(#pg)"/>'+
596              '<circle cx="14" cy="10" r="4" fill="rgba(10,14,26,.5)"/>'+
597              '<rect x="12" y="0" width="4" height="8" rx="2" fill="#e2e8f0"/>'+
598            '</svg>'+
599            '<div class="cw-hub" id="cw-hub">SPIN</div>'+
600            '<div class="cw-burst" id="cw-burst"></div>'+
601          '</div>'+
602          '<button class="cw-btn" id="cw-spin" disabled>Loading…</button>'+
603          '<p class="cw-msg" id="cw-msg"></p>'+
604          '<div class="cw-pending" id="cw-pending" hidden></div>'+
605        '</div>'+
606      '</div>';
607
608    root.querySelector('#cw-spin').addEventListener('click', onSpin);
609    var hub=root.querySelector('#cw-hub');
610    if(hub) hub.addEventListener('click', onSpin);
611    renderPending([]);
612  }
613
614  function renderStatus(s){
615    state.canSpin=!!s.canSpin;
616    var btn=root.querySelector('#cw-spin'), msg=root.querySelector('#cw-msg');
617    if(!btn) return; msg.className='cw-msg';
618    if(s.canSpin){ btn.disabled=false; btn.textContent='SPIN NOW'; msg.textContent=''; }
619    else{
620      btn.disabled=true;
621      if(s.blockedReason==='UNLAUNCHED_LAB'){ btn.textContent='Launch yours first';
622        msg.className='cw-msg is-warn'; msg.textContent='You have one waiting. Launch it to earn your next spin.'; }
623      else if(s.blockedReason==='OUT_OF_STOCK'){ btn.textContent='All claimed';
624        msg.className='cw-msg is-warn'; msg.textContent='Everything at this level is taken right now — check back soon.'; }
625      else if(s.nextSpinInSeconds>0){ startCooldown(s.nextSpinInSeconds); }
626      else{ btn.textContent='SPIN NOW'; }
627    }
628    renderPending(s.pendingLabs||[]);
629  }
630
631  function renderPending(labs){
632    var box=root.querySelector('#cw-pending'); if(!box) return;
633    if(!labs.length){ box.hidden=true; box.innerHTML=''; return; }
634    box.hidden=false;
635    var h='<h4>Your challenges</h4>';
636    labs.forEach(function(l){
637      var exp=l.expiresInHours!=null?(l.expiresInHours<=24?'Expires in '+l.expiresInHours+'h — use it!':'Expires in '+Math.ceil(l.expiresInHours/24)+' days'):'';
638      h+='<div class="cw-lab"><div><div class="cw-lab-name">'+esc(l.name)+'</div>'+(exp?'<div class="cw-lab-exp">'+exp+'</div>':'')+'</div>'+
639         '<button class="cw-launch" data-url="'+esc(l.launchUrl)+'">Launch</button></div>';
640    });
641    box.innerHTML=h;
642    Array.prototype.forEach.call(box.querySelectorAll('.cw-launch'),function(b){
643      b.addEventListener('click',function(){ var u=b.getAttribute('data-url'); if(u) window.open(u,'_blank','noopener'); });
644    });
645  }
646
647  var cd=null;
648  function startCooldown(sec){
649    var btn=root.querySelector('#cw-spin'); if(cd) clearInterval(cd);
650    function tick(){ if(sec<=0){ clearInterval(cd); loadStatus(); return; }
651      var hh=Math.floor(sec/3600), m=Math.floor((sec%3600)/60), s=sec%60;
652      btn.textContent='Next spin in '+(hh?hh+'h ':'')+(m<10?'0':'')+m+'m '+(s<10?'0':'')+s+'s'; sec--; }
653    tick(); cd=setInterval(tick,1000);
654  }
655
656  function onSpin(){
657    if(state.spinning||!state.canSpin) return;
658    state.spinning=true;
659    var btn=root.querySelector('#cw-spin'), msg=root.querySelector('#cw-msg'), stage=root.querySelector('#cw-stage');
660    btn.disabled=true; btn.textContent='SPINNING…'; msg.className='cw-msg'; msg.textContent='';
661    stage.classList.add('is-spinning');
662    req('/api/wheel/spin','POST',{ level:state.level }).then(function(res){
663      animateTo(res.segmentIndex, function(){
664        stage.classList.remove('is-spinning');
665        burst();
666        msg.className='cw-msg is-win pop';
667        msg.innerHTML='🎉 You unlocked: '+esc(res.prize.name)+' — redirecting…';
668        var d = res.prize.discount || '30% OFF';
669        setTimeout(function(){
670          window.location.href='/spin-the-wheel/result?discount='+encodeURIComponent(d)+'&prize='+encodeURIComponent(res.prize.name);
671        }, 900);
672        state.spinning=false;
673      });
674    }).catch(function(err){
675      stage.classList.remove('is-spinning'); state.spinning=false;
676      msg.className='cw-msg is-warn';
677      msg.textContent= err.message==='NOT_LOGGED_IN'?'Please log in to spin.':'Something went wrong. Try again.';
678      btn.disabled=false; btn.textContent='SPIN NOW';
679    });
680  }
681
682  function animateTo(index, done){
683    var wheel=root.querySelector('.cw-wheel'), pointer=root.querySelector('#cw-pointer');
684    var n=state.segs.length, step=360/n;
685    var target=360*6-(index*step+step/2);
686    wheel.style.transform='rotate('+target+'deg)';
687    var dur=CONFIG.SPIN_DURATION_MS, start=performance.now();
688    function eased(t){ return 1-Math.pow(1-t,3); }
689    var lastWedge=-1;
690    function frame(now){
691      var t=Math.min(1,(now-start)/dur);
692      var deg=eased(t)*target;
693      var wedge=Math.floor((deg%360)/step);
694      if(wedge!==lastWedge){ lastWedge=wedge; flickPointer(pointer); }
695      if(t<1) requestAnimationFrame(frame);
696    }
697    requestAnimationFrame(frame);
698    setTimeout(done, dur+120);
699  }
700
701  function flickPointer(pointer){
702    pointer.classList.remove('tick'); void pointer.offsetWidth; pointer.classList.add('tick');
703    if(navigator.vibrate){ try{ navigator.vibrate(8); }catch(e){} }
704  }
705
706  function burst(){
707    var b=root.querySelector('#cw-burst'); if(!b) return;
708    b.classList.remove('go'); void b.offsetWidth; b.classList.add('go');
709  }
710
711  function loadStatus(){
712    req('/api/wheel/status?level='+encodeURIComponent(state.level)).then(renderStatus).catch(function(err){
713      var msg=root.querySelector('#cw-msg'), btn=root.querySelector('#cw-spin'); if(!msg) return;
714      msg.className='cw-msg is-warn';
715      msg.textContent= err.message==='NOT_LOGGED_IN'?'Please log in to spin.':'Couldn’t load the wheel. Refresh to retry.';
716      btn.textContent='Unavailable'; btn.disabled=true;
717    });
718  }
719
720  function esc(s){ return String(s==null?'':s).replace(/[&<>"']/g,function(c){
721    return ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'})[c]; }); }
722
723  renderWheel();
724  loadStatus();
725})();
726`;function i(){return r.useEffect(()=>{document.title="Spin the Wheel";const e=document.createElement("script");return e.textContent=o,document.body.appendChild(e),()=>{document.body.removeChild(e)}},[]),t.jsx("div",{style:{minHeight:"100vh",background:"hsl(var(--brand-dark))",display:"flex",alignItems:"center",justifyContent:"center",padding:"24px"},children:t.jsx("div",{style:{width:"100%",maxWidth:1240},dangerouslySetInnerHTML:{__html:a}})})}export{i as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.