PageSourceSearch

https://www.easyls.org/

html easyls.org collected 2026-10-05 20:33:18 UTC 101,862 bytes, 1,400 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en">
3<head>
4<meta charset="UTF-8" />
5<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6<title>EasyLS — The ELS System for Roblox</title>
7<meta name="description" content="EasyLS is an emergency lighting plugin for Roblox. Set up ELS lighting, rotators, sirens, sounds, headlights, turn signals and keybinds on A-Chassis vehicles without writing any code." />
8
9<meta http-equiv="Content-Security-Policy" content="
10  default-src 'self';
11  base-uri 'self';
12  object-src 'none';
13  form-action 'self';
14  script-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com https://cdn.jsdelivr.net;
15  style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
16  font-src 'self' https://fonts.gstatic.com;
17  img-src 'self' data: blob: https://cdn.discordapp.com;
18  media-src 'self';
19  connect-src 'self' https://easyls-api.easyls.workers.dev;
20  frame-src 'none';
21  upgrade-insecure-requests
22" />
23
24<meta name="referrer" content="strict-origin-when-cross-origin" />
25
26<link rel="preconnect" href="https://fonts.googleapis.com" />
27<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
28
29<link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin />
30<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
31
32<link rel="dns-prefetch" href="https://easyls-api.easyls.workers.dev" />
33
34<link rel="preload" as="image" href="ELS_Logo_Full.webp" type="image/webp" fetchpriority="high" />
35<link href="https://fonts.googleapis.com/css2?family=Sora:wght@400;600;700;800&family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet" />
36<style>
37*{margin:0;padding:0;box-sizing:border-box}
38:root{
39  --blue:#2b9bff; --blue-bright:#5cc8ff;
40  --red:#ff3b3b; --red-bright:#ff6b6b;
41  --green:#2ee06a;
42  --bg:#04060c; --bg2:#080c15; --panel:#0d1320;
43  --ink:#eef4ff; --muted:#8c99b3; --line:rgba(120,150,200,.14);
44  --maxw:1240px;
45}
46html,body{margin:0;padding:0}
47body{
48  background:var(--bg); color:var(--ink);
49  font-family:'Inter',system-ui,sans-serif;
50  -webkit-font-smoothing:antialiased; overflow-x:hidden; line-height:1.5;
51}
52html.lenis,html.lenis body{height:auto}
53.lenis.lenis-smooth{scroll-behavior:auto!important}
54.lenis.lenis-smooth [data-lenis-prevent]{overscroll-behavior:contain}
55.lenis.lenis-stopped{overflow:hidden}
56
57h1,h2,h3,.display{font-family:'Sora',sans-serif;line-height:1.02;letter-spacing:-.02em}
58.mono{font-family:'JetBrains Mono',monospace}
59a{color:inherit;text-decoration:none}
60img{display:block;max-width:100%;height:auto}
61section{position:relative}
62.wrap{max-width:var(--maxw);margin:0 auto;padding:0 28px;position:relative;z-index:2}
63.grad-rb{background:linear-gradient(100deg,var(--blue-bright),#fff 42%,var(--red-bright));-webkit-background-clip:text;background-clip:text;color:transparent}
64.blue{color:var(--blue-bright)} .red{color:var(--red-bright)}
65@media(hover:hover) and (pointer:fine){ *{cursor:none!important} }
66.cursor,.cursor-dot{position:fixed;top:0;left:0;z-index:9000;pointer-events:none;border-radius:50%;mix-blend-mode:screen;will-change:transform}
67.cursor{width:38px;height:38px;border:1.5px solid rgba(92,200,255,.8);box-shadow:0 0 18px rgba(92,200,255,.5);transform:translate(-50%,-50%);transition:width .25s,height .25s,border-color .25s,background .25s}
68.cursor-dot{width:7px;height:7px;background:var(--red-bright);box-shadow:0 0 12px var(--red-bright);transform:translate(-50%,-50%)}
69.cursor.grow{width:74px;height:74px;border-color:rgba(255,107,107,.8);background:rgba(92,200,255,.06)}
70@media(hover:none),(pointer:coarse){.cursor,.cursor-dot{display:none}}
71.progress{position:fixed;top:0;left:0;height:3px;width:100%;z-index:300;transform-origin:0 50%;transform:scaleX(0);
72  background:linear-gradient(90deg,var(--blue),#7c4dff,var(--red));box-shadow:0 0 14px rgba(124,77,255,.6)}
73#loader{position:fixed;inset:0;background:var(--bg);z-index:9999;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:26px;transition:opacity .8s ease,visibility .8s}
74#loader.done{opacity:0;visibility:hidden}
75.load-mark{font-family:'Sora';font-weight:800;font-size:clamp(38px,9vw,84px);letter-spacing:-.03em;display:flex;gap:.12em}
76.load-mark .e{color:var(--blue-bright);text-shadow:0 0 30px rgba(92,200,255,.6)}
77.load-mark .l{color:var(--red-bright);text-shadow:0 0 30px rgba(255,107,107,.6)}
78.load-bar{width:min(340px,70vw);height:3px;background:rgba(255,255,255,.08);border-radius:99px;overflow:hidden}
79.load-bar i{display:block;height:100%;width:0%;background:linear-gradient(90deg,var(--blue),var(--red));box-shadow:0 0 18px var(--blue)}
80.load-pct{font-family:'JetBrains Mono';font-size:12px;color:var(--muted);letter-spacing:.3em}
81#bg3d{position:fixed;inset:0;z-index:0;pointer-events:none}
82.vignette{position:fixed;inset:0;z-index:1;pointer-events:none;
83  background:radial-gradient(120% 90% at 50% 6%,transparent 38%,rgba(2,4,9,.5) 76%,#02040a 100%)}
84.ambient{position:fixed;inset:0;z-index:0;pointer-events:none;overflow:hidden;mix-blend-mode:screen;opacity:.5}
85.ambient .orb{position:absolute;width:60vw;
85height:60vw;border-radius:50%;filter:blur(95px)}
86.ambient .b{background:radial-gradient(circle,rgba(43,155,255,.55),transparent 65%);left:-15vw;top:8vh;animation:floatA 18s ease-in-out infinite}
87.ambient .r{background:radial-gradient(circle,rgba(255,59,59,.55),transparent 65%);right:-15vw;top:34vh;animation:floatB 21s ease-in-out infinite}
88@keyframes floatA{0%,100%{transform:translate(0,0)}50%{transform:translate(18vw,16vh)}}
89@keyframes floatB{0%,100%{transform:translate(0,0)}50%{transform:translate(-20vw,-12vh)}}
90.deco{position:absolute;z-index:1;pointer-events:none;border-radius:8px;opacity:.85;will-change:transform}
91.deco.bar{width:54px;height:16px;background:var(--blue);box-shadow:0 0 16px var(--blue),0 0 30px rgba(43,155,255,.5);animation:pulsed 2.4s ease-in-out infinite}
92.deco.bar.r{background:var(--red);box-shadow:0 0 16px var(--red),0 0 30px rgba(255,59,59,.5)}
93.deco.dotg{width:14px;height:14px;border-radius:50%;background:radial-gradient(circle,#fff,var(--blue-bright));box-shadow:0 0 20px var(--blue-bright)}
94.deco.ringd{width:46px;height:46px;border:2px solid rgba(255,107,107,.6);background:transparent;box-shadow:0 0 18px rgba(255,59,59,.35);border-radius:50%}
95.deco.plus{width:24px;height:24px;border:0;background:
96  linear-gradient(var(--blue-bright),var(--blue-bright)) center/100% 2px no-repeat,
97  linear-gradient(var(--blue-bright),var(--blue-bright)) center/2px 100% no-repeat;opacity:.5}
98@keyframes pulsed{0%,100%{opacity:.5}50%{opacity:1}}
99.boost-bar{position:fixed;top:0;left:0;right:0;z-index:210;display:none;align-items:center;justify-content:center;
100  gap:12px;height:32px;padding:0 18px;font-size:12.5px;color:#cdc2e6;white-space:nowrap;overflow:hidden;
101  background:linear-gradient(90deg,rgba(74,38,132,.62),rgba(52,30,104,.58),rgba(74,38,132,.62));
102  border-bottom:1px solid rgba(155,92,255,.2);backdrop-filter:blur(10px)}
103.boost-bar.on{display:flex}
104.boost-bar .pip{flex:none;width:6px;height:6px;border-radius:50%;background:#c79bff;box-shadow:0 0 8px #9b5cff;animation:blink 1.6s infinite}
105.boost-bar .names{overflow:hidden;text-overflow:ellipsis;min-width:0}
106.boost-bar b{color:#fff;font-weight:600}
107.boost-bar .cta{flex:none;color:#d3c2ff;border-bottom:1px solid rgba(211,194,255,.32);transition:.2s}
108.boost-bar .cta:hover{color:#fff;border-bottom-color:#fff}
109.boost-bar.scrolling{justify-content:flex-start}
110.boost-bar .label{flex:none}
111.boost-bar .mq{position:relative;flex:1;min-width:0;height:100%;overflow:hidden;
112  -webkit-mask-image:linear-gradient(90deg,transparent,#000 24px,#000 calc(100% - 24px),transparent);
113  mask-image:linear-gradient(90deg,transparent,#000 24px,#000 calc(100% - 24px),transparent)}
114.boost-bar .track{display:flex;align-items:center;height:100%;width:max-content;will-change:transform;
115  animation-name:boostSlide;animation-timing-function:linear;animation-iteration-count:infinite}
116.boost-bar .seq{display:flex;align-items:center;white-space:nowrap}
117.boost-bar .sep{flex:none;margin:0 15px;font-style:normal;font-size:8px;color:rgba(199,155,255,.5);transform:translateY(-1px)}
118@keyframes boostSlide{from{transform:translateX(0)}to{transform:translateX(-50%)}}
119@media(prefers-reduced-motion:reduce){.boost-bar .track{animation:none}}
120body.has-boostbar nav{top:32px}
121body.has-boostbar .nav-mobile{top:94px}
122body.has-boostbar .progress{top:32px}
123@media(max-width:640px){.boost-bar .cta{display:none}}
124nav{position:fixed;top:0;left:0;right:0;z-index:200;display:flex;align-items:center;justify-content:space-between;
125  padding:18px 28px;transition:top .4s,background .4s,padding .4s}
126nav.solid{background:rgba(5,8,15,.7);backdrop-filter:blur(14px);border-bottom:1px solid var(--line);padding:12px 28px}
127.nav-logo{display:flex;align-items:center;gap:10px;font-family:'Sora';font-weight:800;font-size:19px;letter-spacing:-.02em}
128.nav-logo .e{color:var(--blue-bright)} .nav-logo .l{color:var(--red-bright)}
129.nav-dot{width:9px;height:9px;border-radius:50%;background:var(--green);box-shadow:0 0 12px var(--green);animation:blink 1.6s infinite}
130@keyframes blink{0%,100%{opacity:1}50%{opacity:.3}}
131.nav-links{display:flex;gap:30px;font-size:14px;font-weight:500}
132.nav-links a{color:var(--muted);transition:.25s;position:relative}
133.nav-links a:hover{color:var(--ink)}
134.nav-links a::after{content:"";position:absolute;
134left:0;bottom:-6px;width:0;height:2px;background:linear-gradient(90deg,var(--blue),var(--red));transition:.3s}
135.nav-links a:hover::after{width:100%}
136.btn{display:inline-flex;align-items:center;gap:9px;font-weight:600;font-size:14px;padding:11px 20px;border-radius:11px;border:none;transition:background-position .4s,box-shadow .3s;position:relative;overflow:hidden}
137.btn-primary{color:#fff;background:linear-gradient(100deg,var(--blue),#7c4dff 55%,var(--red));background-size:220% 100%;box-shadow:0 6px 26px rgba(43,155,255,.32),0 6px 26px rgba(255,59,59,.2)}
138.btn-primary:hover{background-position:100% 0;box-shadow:0 12px 40px rgba(124,77,255,.55)}
139.btn-ghost{color:var(--ink);background:rgba(255,255,255,.05);border:1px solid var(--line)}
140.btn-ghost:hover{background:rgba(255,255,255,.1)}
141@media(max-width:920px){.nav-links{display:none}}
142.hero{min-height:100vh;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:120px 20px 90px;position:relative;z-index:2}
143.hero-badge{display:inline-flex;align-items:center;gap:9px;font-size:12.5px;font-weight:600;letter-spacing:.04em;color:var(--muted);
144  border:1px solid var(--line);background:rgba(255,255,255,.03);padding:8px 16px;border-radius:99px;margin-bottom:30px}
145.hero-badge .pip{width:7px;height:7px;border-radius:50%;background:var(--green);box-shadow:0 0 10px var(--green)}
146.hero-logo{width:min(620px,86vw);filter:drop-shadow(0 0 60px rgba(43,155,255,.35)) drop-shadow(0 0 70px rgba(255,59,59,.25))}
147.hero h1{font-size:clamp(30px,5.4vw,62px);font-weight:800;margin:16px 0 18px;max-width:16ch}
148.hero p.sub{font-size:clamp(15px,2vw,20px);color:var(--muted);max-width:54ch;margin:0 auto 36px}
149.hero-cta{display:flex;gap:16px;flex-wrap:wrap;justify-content:center}
150.hero-cta .btn{padding:15px 28px;font-size:15.5px}
151.lightbar{position:relative;width:min(560px,80vw);height:18px;margin:44px auto 0;border-radius:99px;overflow:hidden;background:#0a0e18;border:1px solid var(--line);display:flex}
152.lightbar .cell{flex:1;position:relative}
153.lightbar .cell::after{content:"";position:absolute;inset:3px;border-radius:4px;background:#0d1422}
154.lightbar .cell.on-b::after{background:var(--blue);box-shadow:0 0 14px var(--blue),0 0 26px var(--blue)}
155.lightbar .cell.on-r::after{background:var(--red);box-shadow:0 0 14px var(--red),0 0 26px var(--red)}
156.scrollcue{position:absolute;bottom:24px;left:50%;transform:translateX(-50%);display:flex;flex-direction:column;align-items:center;gap:8px;color:var(--muted);font-size:11px;letter-spacing:.3em;z-index:3}
157.scrollcue .m{width:22px;height:36px;border:2px solid var(--line);border-radius:14px;position:relative}
158.scrollcue .m::after{content:"";position:absolute;top:6px;left:50%;transform:translateX(-50%);width:4px;height:8px;border-radius:3px;background:var(--blue-bright);animation:scrolldot 1.6s infinite}
159@keyframes scrolldot{0%{opacity:0;transform:translate(-50%,0)}40%{opacity:1}100%{opacity:0;transform:translate(-50%,14px)}}
160.marquee{position:relative;z-index:2;border-top:1px solid var(--line);border-bottom:1px solid var(--line);background:rgba(5,8,15,.55);overflow:hidden;padding:20px 0}
161.marquee .track{display:flex;gap:46px;white-space:nowrap;width:max-content;font-family:'Sora';font-weight:800;font-size:30px;letter-spacing:-.01em;will-change:transform}
162.marquee .track span{color:transparent;-webkit-text-stroke:1.3px rgba(160,185,225,.5)}
163.marquee .track .fill{color:var(--blue-bright);-webkit-text-stroke:0}
164.marquee .track .fill.r{color:var(--red-bright)}
165.marquee .dot{color:var(--red-bright);-webkit-text-stroke:0;display:inline-flex;align-items:center}
166.marquee .dot svg{width:15px;height:15px}
167.sec{padding:140px 0;position:relative;z-index:2}
168.eyebrow{display:inline-flex;align-items:center;gap:10px;font-family:'Sora';font-weight:700;font-size:13px;letter-spacing:.18em;text-transform:uppercase;color:var(--blue-bright);margin-bottom:20px}
169.eyebrow::before{content:"";width:26px;height:2px;background:linear-gradient(90deg,var(--blue),var(--red))}
170.sec h2{font-size:clamp(28px,4.6vw,52px);font-weight:800;max-width:18ch;margin-bottom:22px}
171.sec .lead{font-size:clamp(15px,1.6vw,18.5px);color:var(--muted);max-width:56ch}
172.tag{display:inline-block;font-family:'Sora';font-weight:700;font-size:11.5px;letter-spacing:.14em;text-transform:uppercase;
173  color:var(--red-bright);background:rgba(255,59,59,.1);border:1px solid rgba(255,59,59,.25);padding:6px 13px;border-radius:8px;margin-bottom:18px}
174.tag.b{color:var(--blue-bright);background:rgba(43,155,255,.1);border-color:rgba(43,155,255,.25)}
175.lines .line{overflow:hidden;display:block}
176.lines .line >
176 span{display:block;will-change:transform}
177.statement{min-height:100vh;display:flex;align-items:center;justify-content:center;text-align:center;z-index:2}
178.statement .big{font-family:'Sora';font-weight:800;font-size:clamp(34px,7vw,92px);line-height:1.04;max-width:15ch}
179.statement .big .w{display:inline-block;opacity:.12}
180.statement .big .hl{background:linear-gradient(100deg,var(--blue-bright),var(--red-bright));-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}
181.feature{display:grid;grid-template-columns:1fr 1.05fr;gap:64px;align-items:center}
182.feature.rev{grid-template-columns:1.05fr 1fr}
183.feature.rev .f-copy{order:2}
184.f-list{margin-top:26px;display:flex;flex-direction:column;gap:14px}
185.f-list .item{display:flex;gap:13px;align-items:flex-start;font-size:15px;color:#cdd7ea}
186.f-list .item .ic{flex:none;width:26px;height:26px;border-radius:8px;display:grid;place-items:center;font-size:13px;font-weight:800;
187  background:linear-gradient(135deg,rgba(43,155,255,.25),rgba(255,59,59,.25));border:1px solid var(--line);color:var(--blue-bright)}
188@media(max-width:860px){.feature,.feature.rev{grid-template-columns:1fr;gap:34px}.feature.rev .f-copy{order:0}}
189.device{position:relative;border-radius:16px;overflow:hidden;border:1px solid var(--line);background:#05080f;box-shadow:0 30px 80px rgba(0,0,0,.6);will-change:transform;transform-style:preserve-3d}
190.device .bar{display:flex;align-items:center;gap:7px;padding:11px 14px;background:#0a0f1a;border-bottom:1px solid var(--line);position:relative;z-index:3}
191.device .bar i{width:11px;height:11px;border-radius:50%;display:block}
192.device .bar .r{background:#ff5f56} .device .bar .y{background:#ffbd2e} .device .bar .g{background:#27c93f}
193.device .bar span{margin-left:8px;font-family:'JetBrains Mono';font-size:11px;color:var(--muted)}
194.pimg{position:relative;overflow:hidden}
195.pimg .pimg-inner{will-change:transform}
196.pimg .pimg-inner img{width:100%;height:auto;display:block}
197.pimg .pimg-inner picture{display:block}
198.pimg .pimg-inner video{width:100%;height:auto;display:block}
199.f-glow{position:absolute;inset:-30% -10%;z-index:-1;filter:blur(70px);opacity:.55;border-radius:50%;will-change:transform}
200.f-glow.b{background:radial-gradient(circle,rgba(43,155,255,.7),transparent 60%)}
201.f-glow.r{background:radial-gradient(circle,rgba(255,59,59,.7),transparent 60%)}
202.ring-viz{position:relative;width:min(300px,72%);aspect-ratio:1;margin:0;border-radius:50%;
203  box-shadow:0 0 60px rgba(43,155,255,.4),0 0 60px rgba(255,59,59,.3);cursor:pointer;touch-action:none;user-select:none;-webkit-user-select:none}
204.ring-rot{position:absolute;inset:0;border-radius:50%;will-change:transform}
205.ring-disc{position:absolute;inset:0;border-radius:50%;background:conic-gradient(var(--blue) 0deg 180deg,var(--red) 180deg 360deg)}
206.ring-nodes{position:absolute;inset:0;z-index:3}
207.ring-node{position:absolute;inset:0;pointer-events:none;transform-origin:center center}
208.ring-node .dot{position:absolute;top:0;left:50%;width:19px;height:19px;border-radius:50%;background:#fff;border:2px solid rgba(0,0,0,.28);box-shadow:0 0 12px rgba(255,255,255,.85);transform:translate(-50%,-50%);pointer-events:auto;cursor:grab}
209.ring-node .dot:active{cursor:grabbing}
210.ring-viz::after{content:"";position:absolute;inset:26%;border-radius:50%;background:var(--bg2);box-shadow:inset 0 0 30px #000;z-index:2;pointer-events:none}
211@keyframes spin{to{transform:rotate(360deg)}}
212.ring-stage{position:relative;width:min(300px,72%)}
213.ring-stage .ring-viz{width:100%}
214.ring-hint{position:absolute;top:-50px;right:-22px;display:flex;flex-direction:column;align-items:center;gap:0;z-index:6;pointer-events:none;animation:hintBob 1.9s ease-in-out infinite;transition:opacity .45s ease,transform .45s ease}
215.ring-hint-txt{font-weight:800;font-size:19px;color:#fff;white-space:nowrap;transform:rotate(-7deg);text-shadow:0 2px 12px rgba(43,155,255,.75),0 0 16px rgba(255,59,59,.5)}
216.ring-hint-arrow{width:78px;height:auto;color:#fff;margin-top:-2px;filter:drop-shadow(0 0 8px rgba(43,155,255,.7))}
217.ring-hint-gone{opacity:0;transform:scale(.8) translateY(-8px);animation:none}
218@keyframes hintBob{0%,100%{transform:translateY(0)}50%{transform:translateY(-7px)}}
219.siren-wave{display:flex;align-items:flex-end;
219gap:6px;height:120px}
220.siren-wave i{width:8px;border-radius:6px;background:linear-gradient(var(--blue-bright),var(--blue));box-shadow:0 0 12px rgba(43,155,255,.6)}
221.siren-wave i:nth-child(even){background:linear-gradient(var(--red-bright),var(--red));box-shadow:0 0 12px rgba(255,59,59,.6)}
222.hscroll{height:100vh;overflow:hidden;position:relative;z-index:2}
223.hscroll .hhead{position:absolute;top:104px;left:0;right:0;text-align:center;z-index:5}
224.hscroll-track{display:flex;gap:34px;height:100%;align-items:center;width:max-content;padding:90px 8vw 0;will-change:transform}
225.hpanel{position:relative;width:min(78vw,540px);height:58vh;border-radius:22px;overflow:hidden;flex:none;
226  border:1px solid var(--line);background:linear-gradient(160deg,rgba(255,255,255,.05),rgba(255,255,255,.01));
227  display:flex;flex-direction:column;justify-content:flex-end;padding:40px;box-shadow:0 30px 80px rgba(0,0,0,.5)}
228.hpanel .pn{position:absolute;top:28px;left:36px;font-family:'Sora';font-weight:800;font-size:90px;line-height:1;
229  color:transparent;-webkit-text-stroke:1.5px rgba(160,185,225,.25)}
230.hpanel .pglow{position:absolute;width:60%;height:60%;border-radius:50%;filter:blur(70px);top:-10%;right:-10%}
231.hpanel.b .pglow{background:radial-gradient(circle,rgba(43,155,255,.5),transparent 65%)}
232.hpanel.r .pglow{background:radial-gradient(circle,rgba(255,59,59,.5),transparent 65%)}
233.hpanel .pico{margin-bottom:18px;line-height:0}
234.hpanel .pico svg{width:38px;height:38px}
235.hpanel.b .pico{color:var(--blue-bright)}
236.hpanel.r .pico{color:var(--red-bright)}
237.hpanel h3{font-size:30px;font-weight:800;margin-bottom:12px}
238.hpanel p{color:var(--muted);font-size:15.5px;max-width:34ch}
239.hpanel .ptag{position:absolute;top:34px;right:34px;font-family:'Sora';font-weight:700;font-size:11px;letter-spacing:.14em;color:var(--muted);text-transform:uppercase}
240.hpanel .pchips{display:flex;flex-wrap:wrap;gap:8px;margin-top:20px}
241.hpanel .pchips span{font-size:12px;font-weight:600;color:#cdd7ea;padding:5px 11px;border-radius:99px;border:1px solid var(--line);background:rgba(255,255,255,.04)}
242.hpanel > .pn,.hpanel > .ptag{z-index:2}
243.hpanel > .pico,.hpanel > h3,.hpanel > p,.hpanel > .pchips{position:relative;z-index:2}
244.hpanel .pvis{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden}
245.hpanel .pvis::after{content:"";position:absolute;inset:0;background:linear-gradient(180deg,transparent 40%,rgba(8,12,20,.5) 74%,rgba(8,12,20,.84) 100%)}
246.pv-bar{position:absolute;inset:0;display:flex;gap:18px;padding:0 30px;
247  -webkit-mask-image:linear-gradient(180deg,#000 0,#000 52%,transparent 92%);mask-image:linear-gradient(180deg,#000 0,#000 52%,transparent 92%)}
248.pv-bar i{flex:1;border-radius:0 0 44px 44px}
249.pv-bar i:nth-child(odd){background:linear-gradient(180deg,var(--blue),transparent 92%);color:var(--blue);animation:pvFlash 1.2s steps(1) infinite}
250.pv-bar i:nth-child(even){background:linear-gradient(180deg,var(--red),transparent 92%);color:var(--red);animation:pvFlash 1.2s steps(1) .6s infinite}
251@keyframes pvFlash{0%,49%{opacity:.3;box-shadow:0 0 36px currentColor}50%,100%{opacity:.06}}
252.pv-ring{position:absolute;width:500px;height:500px;top:0;bottom:0;right:-160px;left:auto;margin:auto 0;border-radius:50%;opacity:.17;
253  background:conic-gradient(var(--blue) 0 50%,var(--red) 50% 100%);animation:spin 8s linear infinite;
254  -webkit-mask-image:radial-gradient(circle,#000 18%,transparent 60%);mask-image:radial-gradient(circle,#000 18%,transparent 60%)}
255.pv-wave{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;gap:11px;
256  -webkit-mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent);mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent)}
257/* scaleY, not height: animating height ran a full layout pass every frame
258   on all 26 bars. No will-change here on purpose — the browser promotes a
259   transform animation while it runs and drops the layer when it's paused,
260   which is what we want for 26 elements. */
261.pv-wave i{width:8px;height:78%;border-radius:7px;background:linear-gradient(var(--blue-bright),transparent);opacity:.32;
262  transform:scaleY(.14);animation:pvEq 1.3s ease-in-out infinite}
263.pv-wave i:nth-child(even){background:linear-gradient(var(--red-bright),transparent)}
264.pv-wave i:nth-child(3n){animation-delay:-.15s}.pv-wave i:nth-child(3n+1){animation-delay:-.4s}.pv-wave i:nth-child(4n){animation-delay:-.65s}.pv-wave i:nth-child(5n){animation-delay:-.9s}
265@keyframes pvEq{0%,100%{transform:scaleY(.14)}50%{transform:scaleY(1)}}
266.pv-keys{position:absolute;inset:0;display:grid;grid-template-columns:repeat(6,1fr);gap:13px;padding:24px;align-content:start;opacity:.34;
267  -webkit-mask-image:linear-gradient(180deg,#000 0,#000 46%,transparent 82%);mask-image:linear-gradient(180deg,#000 0,#000 46%,transparent 82%)}
268.pv-keys span{aspect-ratio:1;display:grid;place-items:center;border-radius:12px;background:linear-gradient(180deg,rgba(28,37,55,.8),rgba(13,19,30,.32));border:1px solid var(--line);border-bottom:3px solid rgba(0,0,0,.5);font-family:'Sora';font-weight:700;font-size:18px;color:#aebbd4;transition:color .4s,border-color .4s,box-shadow .4s}
269.pv-keys span.lit{color:#fff;border-color:var(--red-bright);box-shadow:0 0 26px rgba(255,59,59,.45);background:linear-gradient(180deg,rgba(255,59,59,.22),rgba(13,19,30,.3))}
270.pv-config{position:absolute;inset:0;display:flex;flex-direction:column;gap:13px;justify-content:center;padding:0;opacity:.4;
271  -webkit-mask-image:linear-gradient(180deg,#000 0,#000 46%,transparent 82%);mask-image:linear-gradient(180deg,#000 0,#000 46%,transparent 82%)}
272.pv-config .crow{display:flex;width:max-content;will-change:transform}
273.pv-config .crow.l{animation:cSlideL 26s linear infinite}
274.pv-config .crow.r{animation:cSlideR 32s linear infinite}
275.pv-config .crow i{width:52px;height:52px;margin-right:13px;border-radius:12px;flex:none;box-shadow:0 0 16px rgba(0,0,0,.35)}
276@keyframes cSlideL{from{transform:translateX(0)}to{transform:translateX(-50%)}}
277@keyframes cSlideR{from{transform:translateX(-50%)}to{transform:translateX(0)}}
278@media(max-width:860px){.hscroll{height:auto;overflow:visible;padding:120px 0}.hscroll-track{flex-direction:column;width:auto;padding:0 24px;gap:22px;transform:none!important}.hpanel{width:auto;height:auto;min-height:340px}.hpanel .pn{font-size:60px}.hscroll .hhead{position:static;margin-bottom:30px}}
279.dash{position:relative;z-index:2;text-align:center}
280.dash-shot{position:relative;max-width:1000px;margin:46px auto 0;border-radius:20px;overflow:hidden;border:1px solid var(--line);
281  box-shadow:0 40px 120px rgba(0,0,0,.7),0 0 80px rgba(43,155,255,.12)}
282.dash-shot img{width:100%}
283.els-dash{--d-menubar:#1b1b20;--d-tabbar:#0e0e13;--d-card:#17171c;--d-cardb:#27272e;--d-title:#f2f2f4;--d-desc:#8d8d96;--d-mbg:#26262d;--d-mtext:#5a98e0;--d-green:#27d24a;--d-track:#3a3a43;--d-knoboff:#6c6c75;font-family:'Inter',system-ui,sans-serif;color:var(--d-title);text-align:left;line-height:1.5}
284.els-dash *{box-sizing:border-box}
285.els-dash .studio{width:100%;background:linear-gradient(180deg,#101015 0%,#0c0c11 22%,#0b0b10 100%)}
286.els-dash .menubar{height:22px;background:var(--d-menubar);display:flex;align-items:center;padding:0 9px;font-size:12px;color:#cfcfd4;gap:16px;border-bottom:1px solid #000}
287.els-dash .menubar .mleft{display:flex;gap:16px}
288.els-dash .menubar .mright{margin-left:auto;display:flex;gap:26px;font-family:'JetBrains Mono',monospace;font-size:11px;
288color:#dadade;padding-right:4px}
289.els-dash .menubar .mright b{font-weight:500}
290.els-dash .tabbar{background:var(--d-tabbar);height:34px;display:flex;align-items:center;padding:0 14px;border-bottom:1px solid #16161b;position:relative}
291.els-dash .tabs{display:flex;align-items:center;gap:26px;height:100%}
292.els-dash .d-tab{font-size:13.5px;font-weight:700;color:#fbfbfc;height:100%;display:flex;align-items:center;cursor:pointer;position:relative;text-transform:uppercase;background:none;border:none;font-family:inherit;letter-spacing:.01em}
293.els-dash .d-tab.active::after{content:"";position:absolute;left:0;right:0;bottom:0;height:2px;background:#fff}
294.els-dash .d-tab:hover{opacity:.85}
295.els-dash .tab-icons{margin-left:auto;display:flex;align-items:center;gap:14px}
296.els-dash .tab-icons .ic{width:26px;height:26px;display:grid;place-items:center;cursor:pointer;opacity:.92}
297.els-dash .tab-icons .ic:hover{opacity:1}
298.els-dash .tab-icons svg{display:block}
299.els-dash .plus-tab{position:absolute;right:14px;top:50%;transform:translateY(-50%);color:#5a5a62;font-size:22px}
300.els-dash .d-content{padding:29px 14px 34px}
301.els-dash .d-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:21px;max-width:818px;margin:0 auto}
302.els-dash .d-card{background:var(--d-card);border:1px solid var(--d-cardb);border-radius:8px;padding:16px 17px 14px;min-height:118px;display:flex;flex-direction:column;position:relative;transition:border-color .2s}
303.els-dash .d-card:hover{border-color:#34343c}
304.els-dash .d-card .head{display:flex;align-items:flex-start;justify-content:space-between;gap:10px}
305.els-dash .d-card h3{font-size:18px;font-weight:700;color:var(--d-title);font-family:'Inter',sans-serif;letter-spacing:0;line-height:1.1;margin:0}
306.els-dash .d-card .desc{font-size:12px;line-height:1.32;color:var(--d-desc);margin-top:8px}
307.els-dash .d-card .foot{margin-top:auto;display:flex;justify-content:flex-end;padding-top:11px}
308.els-dash .d-toggle{position:relative;width:34px;height:16px;flex:none;margin-top:4px;cursor:pointer}
309.els-dash .d-toggle input{position:absolute;inset:0;opacity:0;margin:0;cursor:pointer;z-index:2}
310.els-dash .d-toggle .track{position:absolute;inset:0;border-radius:99px;background:var(--d-track);transition:background .22s}
311.els-dash .d-toggle .knob{position:absolute;top:50%;left:27px;transform:translate(-50%,-50%);width:19px;height:19px;border-radius:50%;border:2.5px solid var(--d-green);background:#101015;transition:left .22s,border-color .22s,box-shadow .22s;box-shadow:0 0 6px rgba(39,210,74,.5)}
312.els-dash .d-toggle input:not(:checked) ~ .knob{left:7px;border-color:var(--d-knoboff);box-shadow:none}
313.els-dash .d-toggle input:not(:checked) ~ .track{background:#2c2c33}
314.els-dash .d-modify{background:var(--d-mbg);color:var(--d-mtext);border:none;font-family:'Inter',sans-serif;font-size:13px;font-weight:600;padding:6px 26px;border-radius:6px;cursor:pointer;transition:background .2s,transform .05s}
315.els-dash .d-modify:hover{background:#34343d}
316.els-dash .d-modify:active{transform:scale(.97)}
317.dash-live-badge{position:absolute;left:16px;bottom:14px;z-index:6;display:inline-flex;align-items:center;gap:7px;font-size:11.5px;font-weight:600;color:#cfe8ff;background:rgba(8,12,20,.6);border:1px solid var(--line);padding:6px 12px;border-radius:99px;backdrop-filter:blur(6px);pointer-events:none}
318.dash-live-badge i{width:7px;height:7px;border-radius:50%;background:var(--green);box-shadow:0 0 8px var(--green);animation:blink 1.6s infinite}
319.dash-note{text-align:center;max-width:54ch;margin:18px auto 0;font-size:12.5px;color:var(--muted);font-style:italic}
320.els-dash .lighting-view{padding:0}
321.els-dash .le{display:flex;min-height:520px;background:#090910;overflow-x:auto}
322.els-dash .le-side{width:226px;flex:none;margin:12px;border:2px solid #e23b3b;border-radius:12px;background:#0c0c12;display:flex;flex-direction:column;align-items:center;padding:18px 16px;box-shadow:0 0 20px rgba(226,59,59,.22)}
323.els-dash .le-logo{font-family:'Sora',sans-serif;font-weight:800;font-size:25px;letter-spacing:-.02em;margin-top:4px}
324.els-dash .le-logo .e{color:#5cc8ff}.els-dash .le-logo .l{color:#ff5e5e}
325.els-dash .le-subtabs{display:flex;gap:18px;margin:14px 0 18px;font-size:13px;font-weight:700;letter-spacing:.03em;color:#cfd6e6}
326.els-dash .le-subtabs span{cursor:pointer;padding-bottom:3px}
327.els-dash .le-subtabs .active{border-bottom:2px solid #fff;color:#fff}
328.els-dash .le-colorbox{width:100%;border:1px solid #2a2a33;border-radius:9px;background:#0e0e14;display:flex;align-items:center;justify-content:space-between;padding:7px 8px;gap:5px}
329.els-dash .le-arrow{width:26px;height:26px;border-radius:50%;border:1px solid #3a3a45;background:#15151c;color:#cfd6e6;display:grid;place-items:center;cursor:pointer;font-size:14px;flex:none;line-height:1}
330.els-dash .le-arrow:hover{background:#1e1e28}
331.els-dash .le-key{font-size:12px;color:#8089a0;width:13px;text-align:center;flex:none}
332.els-dash .le-color{font-size:14px;font-weight:600;color:#fff;flex:1;text-align:center}
333.els-dash .le-ps-label{font-size:13px;font-weight:700;color:#e7ebf4;margin:14px 0 8px;align-self:flex-start}
334.els-dash .le-pselect{width:100%;border:1px solid #2a2a33;border-radius:8px;background:#0e0e14;display:flex;align-items:center;gap:8px;padding:8px 10px;font-size:13px;color:#cfd6e6;
334cursor:pointer}
335.els-dash .le-pselect .chev{margin-left:auto;color:#7c8395}
336.els-dash .le-play{margin-top:auto;width:60px;height:60px;border-radius:50%;border:2px solid #cfd6e6;background:transparent;color:#eef2ff;display:grid;place-items:center;cursor:pointer;font-size:22px;transition:.2s;margin-bottom:4px}
337.els-dash .le-play:hover{border-color:#fff;box-shadow:0 0 16px rgba(255,255,255,.16)}
338.els-dash .le-main{flex:1;padding:14px 16px 18px;min-width:560px}
339.els-dash .le-stage{font-size:15px;font-weight:700;color:#fff;border-bottom:2px solid #fff;display:inline-block;padding-bottom:2px;margin-bottom:12px}
340.els-dash .le-groupname{width:100%;background:#10131c;border:1px solid #232838;border-radius:7px;padding:9px 12px;color:#cfd6e6;font-size:13px;font-family:inherit;margin-bottom:14px;outline:none}
341.els-dash .le-groupname:focus{border-color:#3a4a6a}
342.els-dash .le-board{display:flex;gap:14px;align-items:flex-start}
343.els-dash .le-grid{flex:1;min-width:0}
344.els-dash .le-controls{flex:none;width:74px;display:flex;flex-direction:column;gap:13px;padding-top:4px}
345.els-dash .le-ctrl{background:#10131c;border:1px solid #232838;border-radius:7px;padding:6px 4px}
346.els-dash .le-ctrl .t{font-size:11.5px;color:#aeb6c8;text-align:center;margin-bottom:2px}
347.els-dash .le-ctrl .row{display:flex;align-items:center;justify-content:space-between;padding:0 5px}
348.els-dash .le-ctrl button{background:none;border:none;color:#cfd6e6;font-size:16px;cursor:pointer;width:14px;line-height:1;padding:0}
349.els-dash .le-ctrl button:hover{color:#fff}
350.els-dash .le-ctrl .v{font-size:14px;font-weight:700;color:#fff}
351.els-dash .le-head-row{display:flex;gap:8px;margin-bottom:6px}
352.els-dash .le-rowlabel{width:34px;flex:none;display:flex;align-items:center;justify-content:flex-end;gap:5px}
353.els-dash .le-cols{flex:1;display:grid;gap:6px}
354.els-dash .le-colhead{text-align:center;min-width:0}
355.els-dash .le-colnum{background:#181820;border:1px solid #2a2a33;border-radius:5px;font-size:11px;color:#dfe4ee;padding:2px 0;margin-bottom:4px}
356.els-dash .le-live{height:9px;border-radius:4px;background:#15151c;margin-bottom:5px;transition:background .04s,box-shadow .04s}
357.els-dash .le-colname{font-size:11px;color:#8089a0;margin-bottom:3px}
358.els-dash .le-colicons{display:flex;justify-content:center;gap:9px;color:#7c8395;font-size:11px}
359.els-dash .le-grid-rows{display:flex;flex-direction:column;gap:6px}
360.els-dash .le-row{display:flex;align-items:center;gap:8px;border-radius:6px}
361.els-dash .le-rownum{font-size:12px;color:#8089a0}
362.els-dash .le-rowarrow{color:#7c8395;font-size:12px}
363.els-dash .le-cells{flex:1;display:grid;gap:6px}
364.els-dash .le-cell{height:20px;border-radius:5px;background:#12131a;border:1px solid #20232e;cursor:pointer;transition:background .12s,box-shadow .12s,border-color .12s}
365.els-dash .le-cell:hover{border-color:#3a4156}
366.els-dash .le-row.playing .le-rownum{color:#fff;font-weight:700}
367.els-dash .le-row.playing{background:rgba(255,255,255,.035)}
368@media(max-width:720px){.els-dash .d-grid{grid-template-columns:1fr;max-width:420px}.els-dash .menubar .mright{gap:12px}}
369.stats{display:grid;grid-template-columns:repeat(3,1fr);gap:22px;text-align:center}
370.stat{padding:34px 18px;border-radius:16px;border:1px solid var(--line);background:rgba(255,255,255,.02)}
371.stat .num{font-family:'Sora';font-weight:800;font-size:clamp(34px,5vw,56px);
372  background:linear-gradient(120deg,var(--blue-bright),var(--red-bright));-webkit-background-clip:text;background-clip:text;color:transparent}
373.stat .lbl{color:var(--muted);font-size:13.5px;margin-top:8px}
374@media(max-width:720px){.stats{grid-template-columns:repeat(2,1fr)}}
375.why-grid{display:flex;flex-wrap:wrap;justify-content:center;gap:20px;margin-top:50px}
376.why-card{flex:1 1 300px;max-width:calc((100% - 40px)/3);padding:28px 24px;border-radius:16px;border:1px solid var(--line);background:linear-gradient(180deg,rgba(255,255,255,.03),rgba(255,255,255,0));transition:transform .3s,border-color .3s}
377.why-card:hover{transform:translateY(-6px);border-color:rgba(255,59,59,.35)}
378.why-card .ic{margin-bottom:16px;display:block;line-height:0}
379.why-card .ic svg{width:28px;height:28px}
380.why-grid .why-card:nth-child(odd) .ic{color:var(--blue-bright)}
381.why-grid .why-card:nth-child(even) .ic{color:var(--red-bright)}
382.why-card h4{font-family:'Sora';font-weight:700;font-size:18px;margin-bottom:10px}
383.why-card p{color:var(--muted);font-size:14.5px;line-height:1.55}
384@media(max-width:820px){.why-card{max-width:100%;flex-basis:100%}}
385.cta{min-height:100vh;display:flex;align-items:center;justify-content:center;text-align:center;position:relative;z-index:2;overflow:hidden}
386.cta .flash{position:absolute;inset:0;z-index:-1;mix-blend-mode:screen;pointer-events:none;-webkit-mask-image:linear-gradient(to bottom,transparent 0%,#000 36%,#000 78%,transparent 100%);mask-image:linear-gradient(to bottom,transparent 0%,#000 36%,#000 78%,transparent 100%)}
387.cta .flash::before,.cta .flash::after{content:"";position:absolute;width:70vw;height:70vw;border-radius:50%;filter:blur(110px);top:50%;left:50%}
388.cta .flash::before{background:radial-gradient(circle,rgba(43,155,255,.7),transparent 60%);transform:translate(-90%,-50%);animation:ctaB 2.2s ease-in-out infinite}
389.cta .flash::after{background:radial-gradient(circle,rgba(255,59,59,.7),transparent 60%);transform:translate(-10%,-50%);animation:ctaR 2.2s ease-in-out infinite}
390@keyframes ctaB{0%,100%{opacity:.35}50%{opacity:.9}}
391@keyframes ctaR{0%,100%{opacity:.9}50%{opacity:.35}}
392.cta h2{font-size:clamp(38px,8vw,104px);font-weight:800;line-height:1.08;margin-bottom:26px}
393.cta p{color:var(--muted);font-size:clamp(16px,2vw,21px);max-width:50ch;margin:0 auto 40px}
394.cta .btn{padding:18px 40px;font-size:17px}
395.cta-mark{font-family:'Sora';font-weight:800}
396footer{position:relative;z-index:2;border-top:1px solid var(--line);background:rgba(4,6,12,.85);padding:60px 0 40px}
397.foot{display:flex;justify-content:space-between;flex-wrap:wrap;gap:30px;align-items:flex-start}
398.foot .brand{font-family:'Sora';font-weight:800;font-size:22px}
399.foot .brand .e{color:var(--blue-bright)} .foot .brand .l{color:var(--red-bright)}
400.foot p{color:var(--muted);font-size:13.5px;max-width:34ch;margin-top:12px}
401.foot-links{display:flex;gap:60px;flex-wrap:wrap}
402.foot-col h5{font-size:12px;letter-spacing:.16em;text-transform:uppercase;color:var(--muted);margin-bottom:16px;font-family:'Sora';font-weight:700}
403.foot-col a{display:block;color:#c4cee0;font-size:14px;margin-bottom:11px;transition:.2s}
404.foot-col a:hover{color:var(--blue-bright)}
405.foot-bottom{margin-top:46px;padding-top:24px;border-top:1px solid var(--line);display:flex;justify-content:space-between;flex-wrap:wrap;gap:12px;color:var(--muted);font-size:12.5px}
406
407.reveal{opacity:0;transform:translateY(34px)}
408.nav-burger{display:none;flex-direction:column;align-items:center;justify-content:center;gap:5px;width:42px;height:42px;background:rgba(255,255,255,.05);border:1px solid var(--line);border-radius:11px;cursor:pointer;padding:0}
409.nav-burger span{width:18px;height:2px;background:var(--ink);border-radius:2px;transition:transform .3s,opacity .2s}
410.nav-burger.open span:nth-child(1){transform:translateY(7px) rotate(45deg)}
411.nav-burger.open span:nth-child(2){opacity:0}
411
412.nav-burger.open span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}
413.nav-mobile{position:fixed;top:62px;left:12px;right:12px;z-index:199;background:rgba(8,11,18,.96);backdrop-filter:blur(16px);border:1px solid var(--line);border-radius:16px;padding:12px;display:flex;flex-direction:column;gap:4px;transform:translateY(-14px);opacity:0;visibility:hidden;transition:.3s;box-shadow:0 24px 60px rgba(0,0,0,.6)}
414.nav-mobile.open{transform:translateY(0);opacity:1;visibility:visible}
415.nav-mobile a:not(.btn){padding:13px 14px;border-radius:10px;font-size:15px;font-weight:600;color:#dbe4f2}
416.nav-mobile a:not(.btn):active{background:rgba(255,255,255,.07)}
417.nav-mobile .btn{justify-content:center;margin-top:6px;padding:13px}
418.nav-cta{display:flex;align-items:center;gap:10px}
419.nav-cta .btn{white-space:nowrap}
420@media(max-width:1120px){.nav-cta .btn-ghost{display:none}}
421@media(max-width:920px){
422  .nav-burger{display:flex}
423  nav .nav-cta{display:none}
424}
425@media(max-width:640px){
426  .wrap{padding:0 20px}
427  .sec{padding:84px 0}
428  .hero{padding:104px 18px 72px}
429  .hero h1{margin-top:10px}
430  .statement{min-height:auto;padding:92px 0}
431  .statement .big{font-size:clamp(30px,8.5vw,52px)}
432  .feature,.feature.rev{gap:26px}
433  .cta{min-height:86vh}
434  .marquee{padding:16px 0}
435  .marquee .track{font-size:22px;gap:32px}
436  nav{padding:11px 16px}
437  .dash-shot{margin-top:34px}
438  .els-dash .menubar{gap:9px;padding:0 8px}
439  .els-dash .menubar .mright{gap:9px;font-size:10px}
440  .els-dash .menubar .mright span:first-child{display:none}
441  .els-dash .tabbar{overflow-x:auto;scrollbar-width:none;padding:0 12px}
442  .els-dash .tabbar::-webkit-scrollbar{display:none}
443  .els-dash .tabs{flex:none;gap:17px}
444  .els-dash .tab-icons,.els-dash .plus-tab{display:none}
445  .els-dash .d-content{padding:18px 12px 22px}
446  .els-dash .d-card{min-height:0}
447  .els-dash .le{flex-direction:column;min-height:0;overflow-x:hidden}
448  .els-dash .le-side{width:auto;margin:10px 10px 0;padding:14px 14px 16px}
449  .els-dash .le-logo{font-size:22px}
450  .els-dash .le-play{margin-top:14px;width:52px;height:52px;font-size:18px}
451  .els-dash .le-main{min-width:0;padding:12px}
452  .els-dash .le-board{flex-direction:column;gap:12px}
453  .els-dash .le-grid{overflow-x:auto;scrollbar-width:none}
454  .els-dash .le-grid::-webkit-scrollbar{display:none}
455  .els-dash .le-controls{flex-direction:row;width:auto;padding-top:4px;gap:10px}
456  .els-dash .le-ctrl{flex:1}
457  .dash-live-badge{font-size:10px;left:12px;bottom:10px;padding:5px 10px}
458}
459@media(max-width:430px){
460  .els-dash .le-cell{height:18px}
461  .els-dash .menubar .mleft{gap:12px}
462}
463html{-webkit-text-size-adjust:100%;text-size-adjust:100%}
464/* svh, not dvh: dvh tracks the iOS URL bar as it collapses, which changes
465   the document height mid-scroll and invalidates every ScrollTrigger
466   position. svh is the stable small-viewport height. */
467@supports(min-height:100svh){
468  .hero{min-height:100svh}
469  .cta{min-height:100svh}
470  @media(max-width:640px){ .cta{min-height:86svh} }
471}
472@media(max-width:820px){
473  .deco{display:none!important}
474}
475@media(max-width:860px){
476  .hpanel{padding:30px 24px 28px}
477  .hpanel .pn{top:22px;left:24px;font-size:56px}
478  .hpanel .ptag{top:26px;right:22px;font-size:12px}
479  .hpanel .pvis::after{background:linear-gradient(180deg,rgba(8,12,20,.42) 0,rgba(8,12,20,.76) 44%,rgba(8,12,20,.94) 100%)}
480  .pv-bar{gap:10px;padding:0 18px}
481  .pv-ring{width:290px;height:290px;right:-96px;opacity:.13}
482  .pv-wave{gap:7px}
483  .pv-wave i{width:6px}
484  .pv-keys{grid-template-columns:repeat(5,1fr);gap:9px;padding:16px;opacity:.18;
485    -webkit-mask-image:linear-gradient(180deg,#000 0,rgba(0,0,0,.45) 30%,transparent 58%);
486            mask-image:linear-gradient(180deg,#000 0,rgba(0,0,0,.45) 30%,transparent 58%)}
487  .pv-keys span{font-size:12px;border-radius:9px;border-bottom-width:2px}
488  .pv-config{opacity:.24}
489  .pv-config .crow i{width:38px;height:38px;margin-right:9px}
490}
491@media(max-width:720px){
492  .device .pimg-inner{overflow-x:auto;overscroll-behavior-x:contain;-webkit-overflow-scrolling:touch;scrollbar-width:none}
493  .device .pimg-inner::-webkit-scrollbar{display:none}
494  .device .pimg-inner picture img,.device .pimg-inner video{min-width:620px}
495  .device:has(picture)::after,.device:has(video)::after{content:"swipe to explore";position:absolute;right:10px;bottom:10px;z-index:4;
496    font-family:'JetBrains Mono';font-size:10.5px;letter-spacing:.06em;color:#cdd8ea;
497    background:rgba(8,12,20,.8);border:1px solid var(--line);border-radius:99px;padding:5px 11px;pointer-events:none}
498}
499@media(max-width:920px){
500  .nav-logo{padding:8px 0}
501  .foot-col a{padding:12px 0;margin-bottom:0}
502  .els-dash .tabbar{height:42px}
503  .els-dash .d-modify{padding:9px 26px}
504}
505@media(max-width:640px){
506  .tag{font-size:12px;letter-spacing:.12em}
507  .scrollcue{font-size:12px;letter-spacing:.22em}
508  .hpanel .pchips span{font-size:12.5px}
509}
510@media(max-width:720px){
511  .feature,.feature.rev{grid-template-columns:minmax(0,1fr)}
512  .f-copy,.f-media{min-width:0;max-width:100%}
513  .device{min-width:0;max-width:100%}
514  .device .pimg{min-width:0;max-width:100%}
515  .device .pimg-inner{width:100%;max-width:100%;min-width:0}
516}
517@media(max-width:920px){
518  .nav-burger{width:44px;height:44px}
519  .btn{min-height:44px;padding:12px 20px}
520}
521@media(max-width:640px){
522  .device .bar span{font-size:12px}
523  .dash-live-badge{font-size:12px}
524}
525
526/* ── mobile performance ─────────────────────────────────────────────
527   iOS Safari: full-viewport fixed layers with mix-blend-mode force a
528   re-blend of the whole page on every scrolled frame, and large
529   animated blurs re-rasterize constantly. Both are dropped below 860px.
530   The backdrop under these layers is near-black (#04060c) and #bg3d is
531   disabled on mobile, so screen-blend ≈ normal-blend here: the look is
532   effectively unchanged, the compositing cost is not. */
533@media(max-width:860px){
534  .ambient{mix-blend-mode:normal;opacity:.45}
535  .ambient .orb{width:70vw;height:70vw;filter:blur(45px)}
536  .cta .flash{mix-blend-mode:normal}
537  .cta .flash::before,.cta .flash::after{filter:blur(55px);animation:none;opacity:.6}
538}
539
540/* Offscreen module panels are parked rather than left ticking. Applied
541   by IntersectionObserver; if JS never runs, nothing is paused. */
542.hpanel.pv-idle,.hpanel.pv-idle *,
543.hpanel.pv-idle *::before,.hpanel.pv-idle *::after{animation-play-state:paused!important}
544
545/* The loader is a full-screen overlay; without this the page scrolls
546   underneath it while its content is still opacity:0. */
547html.is-loading,html.is-loading body{overflow:hidden}
548
549@media(prefers-reduced-motion:reduce){
550  .ambient .orb,.cta .flash::before,.cta .flash::after,
551  .pv-wave i,.pv-bar i,.pv-ring,.pv-config .crow,.deco.bar,.scrollcue .m::after{animation:none!important}
552}
553</style>
554</head>
555<body>
556
557<div class="boost-bar" id="boostBar"></div>
558
559<div class="cursor" id="cursor"></div>
560<div class="cursor-dot" id="cursorDot"></div>
561<div class="progress" id="progress"></div>
562
563<div id="loader">
564  <div class="load-mark"><span class="e">EASY</span><span class="l">LS</span></div>
565  <div class="load-bar"><i id="loadFill"></i></div>
566  <div class="load-pct mono" id="loadPct">INITIALIZING 0%</div>
567</div>
568
569<canvas id="bg3d"></canvas>
570<div class="ambient"><div class="orb b"></div><div class="orb r"></div></div>
571<div class="vignette"></div>
572
573<nav id="nav">
574  <a class="nav-logo" data-link href="#top"><span class="nav-dot"></span><span class="e">EASY</span><span class="l">LS</span></a>
575  <div class="nav-links">
576    <a data-link href="#lighting">Lighting</a>
577    <a data-link href="#rotators">Rotators</a>
578    <a data-link href="#modules-h">Modules</a>
579    <a data-link href="#why">Why EasyLS</a>
580    <a href="about.html">About</a>
581    <a data-group href="#">Roblox Group</a>
582  </div>
583  <div class="nav-cta">
584    <a class="btn btn-ghost" data-discord data-magnetic href="#">Discord</a>
585    <a class="btn btn-primary" data-magnetic href="get.html">Get EasyLS</a>
586  </div>
587  <button class="nav-burger" id="navBurger" aria-label="Open menu"><span></span><span></span><span></span></button>
588</nav>
589<div class="nav-mobile" id="navMobile">
590  <a data-link href="#lighting">Lighting</a>
591  <a data-link href="#rotators">Rotators</a>
592  <a data-link href="#modules-h">Modules</a>
593  <a data-link href="#why">Why EasyLS</a>
594  <a href="about.html">About</a>
595  <a data-group href="#">Roblox Group</a>
596  <a class="btn btn-ghost" data-discord href="#">Join the Discord</a>
597  <a class="btn btn-primary" href="get.html">Get EasyLS &rarr;</a>
598</div>
599
600<header class="hero" id="top">
601  <div class="hero-badge" data-speed="1.25">EMERGENCY LIGHTING SYSTEM · ROBLOX</div>
602
603  <picture>
604    <source srcset="ELS_Logo_Full.webp" type="image/webp" />
605    <img class="hero-logo" id="heroLogo" src="[email protected]"
606         width="1240" height="536" alt="EasyLS logo"
607         decoding="async" fetchpriority="high" data-speed="1.15" />
608  </picture>
609  <h1 data-speed="1.4">The whole <span class="grad-rb">ELS system</span>, none of the scripting.</h1>
610  <p class="sub" data-speed="1.55">Lighting, rotators, sirens, sounds, headlights, turn signals and ke
610ybinds for any A-Chassis vehicle, all from one plugin inside Roblox Studio.</p>
611  <div class="hero-cta" data-speed="1.7">
612    <a class="btn btn-primary" data-magnetic href="get.html">Get EasyLS →</a>
613    <a class="btn btn-ghost" data-link data-magnetic href="#lighting">See it in action</a>
614  </div>
615  <div class="lightbar" id="lightbar" data-speed="1.85"></div>
616  <div class="scrollcue"><div class="m"></div>SCROLL</div>
617
618  <div class="deco bar" data-speed="0.5" style="left:8%;top:24%"></div>
619  <div class="deco bar r" data-speed="0.7" style="right:10%;top:30%"></div>
620  <div class="deco dotg" data-speed="0.4" style="left:16%;top:62%"></div>
621  <div class="deco ringd" data-speed="0.6" style="right:14%;top:60%"></div>
622  <div class="deco plus" data-speed="0.45" style="left:78%;top:20%"></div>
623  <div class="deco plus" data-speed="0.55" style="left:22%;top:40%"></div>
624</header>
625
626<div class="marquee"><div class="track" id="marqueeTrack"></div></div>
627
628<section class="statement sec">
629  <div class="wrap">
630    <p class="big" id="statementText">If you can <span class="hl">picture the pattern</span>, you can build it.</p>
631    <div class="deco bar" data-speed="0.55" style="left:6%;top:30%"></div>
632    <div class="deco bar r" data-speed="0.65" style="right:8%;top:64%"></div>
633    <div class="deco dotg" data-speed="0.4" style="right:18%;top:24%"></div>
634  </div>
635</section>
636
637<section class="sec" id="lighting">
638  <div class="deco bar" data-speed="0.5" style="left:3%;top:18%"></div>
639  <div class="deco ringd" data-speed="0.65" style="left:6%;top:74%"></div>
640  <div class="wrap feature">
641    <div class="f-copy reveal">
642      <span class="tag">Module 01 · Lighting</span>
643      <div class="eyebrow">Pattern Editor</div>
644      <h2>Design any pattern, light by light.</h2>
645      <p class="lead">The lighting editor is a step sequencer. Each row is a step, each column is a light, and clicking a cell sets that light's color on that step. Add as many groups and stages as you need, then hit play to watch the pattern run before you test it in game.</p>
646      <div class="f-list">
647        <div class="item"><span class="ic"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7" rx="1.2"/><rect x="14" y="3" width="7" height="7" rx="1.2"/><rect x="3" y="14" width="7" height="7" rx="1.2"/><rect x="14" y="14" width="7" height="7" rx="1.2"/></svg></span><div>Set the color of every light on every step</div></div>
648        <div class="item"><span class="ic"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="13" r="8"/><path d="M12 13V8.5"/><path d="M9.5 2.5h5"/></svg></span><div>Change the tempo and step count to speed a pattern up or slow it down</div></div>
649        <div class="item"><span class="ic"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><rect x="8" y="8" width="12" height="12" rx="2"/><path d="M16 8V5a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h3"/></svg></span><div>Save light groups and switch between stages</div></div>
650      </div>
651    </div>
652    <div class="f-copy">
653      <div class="device tilt" id="tilt1">
654        <div class="bar"><i class="r"></i><i class="y"></i><i class="g"></i><span>EasyLS — Lighting Editor</span></div>
655        <div class="pimg"><div class="pimg-inner"><picture>
656          <source srcset="lighting_page.webp" type="image/webp" />
657          <img src="lighting_page.png" width="1022" height="705" loading="lazy" decoding="async" alt="EasyLS lighting pattern editor" />
658        </picture></div></div>
659        <div class="f-glow r" style="top:auto;bottom:-30%"></div>
660      </div>
661    </div>
662  </div>
663</section>
664
665<section class="sec" style="padding-top:0">
666  <div class="wrap" style="text-align:center">
667    <span class="tag b reveal">Live Preview</span>
668    <h2 class="reveal" style="margin:0 auto 14px">Watch the pattern run, live in Studio.</h2>
669    <p class="lead reveal" style="margin:0 auto 40px">The preview renders in real time, so what you see here is what shows up in game.</p>
670    <div class="device tilt" id="gifFrame" style="max-width:900px;margin:0 auto">
671      <div class="bar"><i class="r"></i><i class="y"></i><i class="g"></i><span>EasyLS — Live ELS Render</span></div>
672
673      <div class="pimg"><div class="pimg-inner">
674        <video class="livegif" width="1022" height="708" autoplay loop muted playsinline
675               preload="none" poster="LightingGif_poster.jpg"
676               aria-label="EasyLS lighting in action">
677          <source src="LightingGif.webm" type="video/webm" />
678          <source src="LightingGif.mp4" type="video/mp4" />
679          <img src="LightingGif_poster.jpg" width="1022" height="708" loading="lazy"
680               decoding="async" alt="EasyLS lighting in action" />
681        </video>
682      </div></div>
683    </div>
684  </div>
685</section>
686
687<section class="sec" id="rotators">
688  <div class="deco bar r" data-speed="0.5" style="right:4%;top:20%"></div>
689  <div class="deco dotg" data-speed="0.7" style="right:9%;top:70%"></div>
690  <div class="wrap feature rev">
691    <div class="f-copy">
692      <div class="device tilt" id="tilt2">
693        <div class="bar"><i class="r"></i><i class="y"></i><i class="g"></i><span>EasyLS — Rotators</span></div>
694        <div class="pimg"><div class="pimg-inner"><picture>
695          <source srcset="Rotators_page.webp" type="image/webp" />
696          <img src="Rotators_page.png" width="1057" height="727" loading="lazy" decoding="async" alt="EasyLS rotator angle color regions" />
697        </picture></div></div>
698        <div class="f-glow b"></div>
699      </div>
700    </div>
701    <div class="f-copy reveal">
702      <span class="tag b">Module 02 · Rotators</span>
703      <div class="eyebrow">Angle Color Regions</div>
704      <h2>Beacons that actually rotate.</h2>
705      <p class="lead">Split the rotation ring into colored sections, then set the top speed, acceleration, deceleration and direction. As the beacon turns it picks up the color of whichever section it's pointing at.</p>
706      <div class="f-list">
707        <div class="item"><span class="ic"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M12 12V3"/><path d="M12 12l7.8 4.5"/></svg></span><div>Click anywhere on the ring to add a color region</div></div>
708        <div class="item"><span class="ic"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3.2"/><path d="M12 2.5v3M12 18.5v3M2.5 12h3M18.5 12h3M5.2 5.2l2.1 2.1M16.7 16.7l2.1 2.1M18.8 5.2l-2.1 2.1M7.3 16.7l-2.1 2.1"/></svg></span><div>Control top speed, acceleration, deceleration and direction</div></div>
709        <div class="item"><span class="ic"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M5 20L15 10"/><path d="M16 3.5l1 2 2 1-2 1-1 2-1-2-2-1 2-1z"/><path d="M6 4.5l.6 1.2 1.2.6-1.2.6L6 8.1l-.6-1.2L4.2 6.3l1.2-.6z"/></svg></span><div>A setup wizard that finds the beacons on your vehicle for you</div></div>
710      </div>
711      <div style="margin-top:48px"><div class="ring-stage">
712        <div class="ring-hint" id="ringHint">
713          <span class="ring-hint-txt">Click me!</span>
714          <svg class="ring-hint-arrow" viewBox="0 0 140 120" fill="none" aria-hidden="true"><path d="M120 14 C 140 48, 98 62, 80 50 C 66 41, 78 24, 90 34 C 98 41, 88 62, 58 76 C 42 84, 30 94, 24 106" stroke="currentColor" stroke-width="4" stroke-linecap="round"/><path d="M24 106 L 41 100 M24 106 L 30 89" stroke="currentColor" stroke-width="4" stroke-linecap="round"/></svg>
715        </div>
716        <div class="ring-viz" id="ringViz"><div class="ring-rot" id="ringRot"><div class="ring-disc" id="ringDisc"></div><div class="ring-nodes" id="ringNodes"></div></div></div>
717      </div></div>
718    </div>
719  </div>
720</section>
721
722<section class="hscroll" id="modules-h">
723  <div class="hhead">
724    <div class="eyebrow" style="justify-content:center">One Plugin · Five Modules</div>
725    <h2 style="font-size:clamp(26px,4vw,44px);font-weight:800">Everything in the box.</h2>
726  </div>
727  <div class="hscroll-track" id="htrack">
728    <div class="hpanel b"><div class="pglow"></div><div class="pn">01</div><span class="ptag">Lighting</span><div class="pvis"><div class="pv-bar"><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div></div><div class="pico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18h6"/><path d="M10 21.5h4"/><path d="M12 2.5a6.5 6.5 0 0 0-4 11.6c.8.7 1 1.4 1 2.4h6c0-1 .2-1.7 1-2.4A6.5 6.5 0 0 0 12 2.5z"/></svg></div><h3>Lighting</h3><p>A pattern sequencer for ELS, headlights and any other lighting you put on the car.</p><div class="pchips"><span>Per-step color</span><span>Multi-stage</span><span>Live preview</span></div></div>
729    <div class="hpanel r"><div class="pglow"></div><div class="pn">02</div><span class="ptag">Rotators</span><div class="pvis"><div class="pv-ring"></div></div><div class="pico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M20.5 12a8.5 8.5 0 1 1-2.6-6.1"/><path d="M20.5 3.5V9h-5.5"/></svg></div><h3>Rotators</h3><p>Rotating warning lights that change color by angle, with a wizard to get them set up.</p><div class="pchips"><span>Angle-based</span><span>Speed control</span><span>Setup wizard</span></div></div>
730    <div class="hpanel b"><div class="pglow"></div><div class="pn">03</div><span class="ptag">Sirens &amp; Sounds</span><div class="pvis"><div class="pv-wave"><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div></div><div class="pico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4 9v6h3.5L13 19.5V4.5L7.5 9H4z"/><path d="M16.5 8.5a5 5 0 0 1 0 7"/><path d="M19 6a8.5 8.5 0 0 1 0 12"/></svg></div><h3>Sirens &amp; Sounds</h3><p>Add siren tones and your own vehicle sounds, running off the same system as the lights.</p><div class="pchips"><span>Custom tones</span><span>Per-vehicle</span><span>Light-synced</span></div></div>
731    <div class="hpanel r"><div class="pglow"></div><div class="pn">04</div><span class="ptag">Keybinds</span><div class="pvis"><div class="pv-keys" id="pvKeys"></div></div><div class="pico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="6" width="20" height="12" rx="2.2"/><path d="M6 10h.01M10 10h.01M14 10h.01M18 10h.01M6.5 14h11"/></svg></div><h3>Keybinds</h3><p>Assign any key to any function: lights, stages, sirens, rotators, headlights and turn signals.</p><div class="pchips"><span>Any key</span><span>Every function</span><span>Per-car</span></div></div>
732    <div class="hpanel b"><div class="pglow"></div><div class="pn">05</div><span class="ptag">Configuration</span><div class="pvis"><div class="pv-config" id="pvConfig"></div></div><div class="pico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3.2"/>
732<path d="M19.4 15a1.6 1.6 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.6 1.6 0 0 0-1.8-.3 1.6 1.6 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.6 1.6 0 0 0-1-1.5 1.6 1.6 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.6 1.6 0 0 0 .3-1.8 1.6 1.6 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.6 1.6 0 0 0 1.5-1 1.6 1.6 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.6 1.6 0 0 0 1.8.3H9a1.6 1.6 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.6 1.6 0 0 0 1 1.5 1.6 1.6 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.6 1.6 0 0 0-.3 1.8V9a1.6 1.6 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.6 1.6 0 0 0-1.5 1z"/></svg></div><h3>Configuration</h3><p>Set the color palette, lighting method and other settings that apply to the whole vehicle.</p><div class="pchips"><span>Color palette</span><span>Lighting method</span><span>Reusable</span></div></div>
733  </div>
734</section>
735
736<section class="sec dash" id="modules">
737  <div class="wrap">
738    <div class="eyebrow" style="justify-content:center">The Dashboard</div>
739    <h2 style="margin:0 auto 16px;max-width:20ch">It all lives in one panel.</h2>
740    <p class="lead" style="margin:0 auto">Turn on the modules a vehicle needs, then open each one to edit it. Everything for that car stays in the same place instead of spread across scripts and settings.</p>
741    <div class="dash-shot" id="dashShot">
742      <div class="els-dash">
743        <div class="studio">
744          <div class="menubar">
745            <div class="mleft"><span>File</span><span>Edit</span><span>Help</span></div>
746            <div class="mright"></div>
747          </div>
748          <div class="tabbar">
749            <div class="tabs">
750              <button class="d-tab active">Modules</button>
751              <button class="d-tab">Lighting</button>
752              <button class="d-tab">Rotators</button>
753              <button class="d-tab">Sirens</button>
754              <button class="d-tab">Keybinds</button>
755              <button class="d-tab">Configuration</button>
756            </div>
757            <div class="tab-icons">
758              <div class="ic" title="Plugin"><svg width="22" height="24" viewBox="0 0 22 24" fill="none"><path d="M11 1.6 20 6.6v10.8L11 22.4 2 17.4V6.6z" stroke="#c2c2c8" stroke-width="1.6" stroke-linejoin="round"/><path d="M11 7.2 15.5 9.8v5.2L11 17.6 6.5 15V9.8z" stroke="#c2c2c8" stroke-width="1.3" stroke-linejoin="round"/></svg></div>
759              <div class="ic" title="Team"><svg width="26" height="22" viewBox="0 0 26 22" fill="none"><circle cx="9" cy="7" r="3.4" stroke="#c2c2c8" stroke-width="1.6"/><path d="M3 18.5c0-3.3 2.7-6 6-6s6 2.7 6 6" stroke="#c2c2c8" stroke-width="1.6" stroke-linecap="round"/><circle cx="17.5" cy="7.6" r="2.8" stroke="#c2c2c8" stroke-width="1.5"/><path d="M16 13c3 .1 6 2.3 6 5.5" stroke="#c2c2c8" stroke-width="1.5" stroke-linecap="round"/></svg></div>
760              <div class="ic" title="Home"><svg width="26" height="24" viewBox="0 0 26 24" fill="none"><path d="M4 11 13 3.5 22 11" stroke="#c2c2c8" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/><path d="M6 10v9.5h14V10" stroke="#c2c2c8" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
761            </div>
762            <div class="plus-tab">+</div>
763          </div>
764          <div class="d-content modules-view">
765            <div class="d-grid">
766              <div class="d-card"><div class="head"><h3>Lighting</h3><label class="d-toggle"><input type="checkbox" checked><span class="track"></span><span class="knob"></span></label></div><p class="desc">Modify this vehicle's ELS, headlights, or other lighting</p><div class="foot"><button class="d-modify">Modify</button></div></div>
767              <div class="d-card"><div class="head"><h3>Rotators</h3><label class="d-toggle"><input type="checkbox" checked><span class="track"></span><span class="knob"></span></label></div><p class="desc">Configure rotating warning lights and angle-based color overrides</p><div class="foot"><button class="d-modify">Modify</button></div></div>
768              <div class="d-card"><div class="head"><h3>Sirens</h3><label class="d-toggle"><input type="checkbox" checked><span class="track"></span><span class="knob"></span></label></div><p class="desc">Modify this vehicle's sirens</p><div class="foot"><button class="d-modify">Modify</button></div></div>
769              <div class="d-card"><div class="head"><h3>Keybinds</h3></div><p class="desc">Map all keybinds for all functions of this car.</p><div class="foot"><button class="d-modify">Modify</button></div></div>
770              <div class="d-card"><div class="head"><h3>Car Configuration</h3></div><p class="desc">Modify this vehicles color palette, lighting method, etc</p><div class="foot"><button class="d-modify">Modify</button></div></div>
771            </div>
772          </div>
773          <div class="d-content lighting-view" hidden>
774            <div class="le">
775              <div class="le-side">
776                <div class="le-logo"><span class="e">EASY</span> <span class="l">LS</span></div>
777                <div class="le-subtabs"><span class="active">EDITOR</span><span>GROUPS</span></div>
778                <div class="le-colorbox">
779                  <button class="le-arrow" id="leColorPrev">&lsaquo;</button>
780                  <span class="le-key">Q</span>
781                  <span class="le-color" id="leColorName">Red</span>
782                  <span class="le-key">E</span>
783                  <button class="le-arrow" id="leColorNext">&rsaquo;</button>
784                </div>
785                <div class="le-ps-label">Pattern Selector</div>
786                <div class="le-pselect"><svg width="13" height="13" viewBox="0 0 24 24" fill="none"><circle cx="11" cy="11" r="7" stroke="#7c8395" stroke-width="2"/><path d="M16 16l5 5" stroke="#7c8395" stroke-width="2" stroke-linecap="round"/></svg> Stage 1 <span class="chev">&#9662;</span></div>
787                <button class="le-play" id="lePlay" aria-label="Play / Pause">&#9654;</button>
788              </div>
789              <div class="le-main">
790                <div class="le-stage">Stage 1</div>
791                <input class="le-groupname" placeholder="Light Group Name">
792                <div class="le-board">
793                  <div class="le-grid">
794                    <div class="le-head-row"><div class="le-rowlabel"></div><div class="le-cols" id="leCols"></div></div>
795                    <div class="le-grid-rows" id="leRows"></div>
796                  </div>
797                  <div class="le-controls">
798                    <div class="le-ctrl"><div class="t">Lights</div><div class="row"><button data-ctrl="lights" data-d="-1">&minus;</button><span class="v" id="leLights">8</span><button data-ctrl="lights" data-d="1">+</button></div></div>
799                    <div class="le-ctrl"><div class="t">Tempo</div><div class="row"><button data-ctrl="tempo" data-d="-1">&minus;</button><span class="v" id="leTempo">600</span><button data-ctrl="tempo" data-d="1">+</button></div></div>
800                    <div class="le-ctrl"><div class="t">Steps</div><div class="row"><button data-ctrl="steps" data-d="-1">&minus;</button><span class="v" id="leSteps">16</span><button data-ctrl="steps" data-d="1">+</button></div></div>
801                  </div>
802                </div>
803              </div>
804            </div>
805          </div>
806        </div>
807      </div>
808      <span class="dash-live-badge"><i></i>Live demo, try the toggles</span>
809    </div>
810    <p class="dash-note">This dashboard is a web recreation for the site. The real one runs inside Roblox Studio.</p>
811  </div>
812</section>
813
814<section class="sec" style="padding:90px 0">
815  <div class="wrap">
816    <div class="stats">
817      <div class="stat"><div class="num" data-count="5">0</div><div class="lbl">Modules</div></div>
818      <div class="stat"><div class="num">∞</div><div class="lbl">Light groups per vehicle</div></div>
819      <div class="stat"><div class="num">∞</div><div class="lbl">Pattern steps per stage</div></div>
820      <div class="stat"><div class="num" data-count="100" data-suffix="%">0</div><div class="lbl">No-code setup</div></div>
821      <div class="stat"><div class="num" data-count="80" data-suffix="%">0</div><div class="lbl">Less time building patterns</div></div>
822      <div class="stat"><div class="num">1k+</div><div class="lbl">Purchases to date</div></div>
823    </div>
824  </div>
825</section>
826
827<section class="sec" id="why">
828  <div class="wrap" style="text-align:center">
829    <div class="eyebrow" style="justify-content:center">Why EasyLS</div>
830    <h2 style="margin:0 auto;max-width:20ch">Why people keep using it.</h2>
831    <div class="why-grid">
832      <div class="why-card"><span class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M8 8l-4 4 4 4"/><path d="M16 8l4 4-4 4"/><path d="M14 5l-4 14"/></svg></span><h4>Actually zero code</h4><p>Plenty of plugins call themselves no-code and still leave you a couple of scripts to wire up at the end. With EasyLS you never open <em>one</em>.</p></div>
833      <div class="why-card"><span class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19a8 8 0 0 1 16 0"/><path d="M12 19l4.5-6.5"/><circle cx="12" cy="19" r="1.4"/></svg></span><h4>Easy on your server</h4><p>The runtime is written to keep overhead low, so a car loaded with lights and effects won't drag your server down.</p></div>
834      <div class="why-card"><span class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M3 16.5V13l2-5.5A2 2 0 0 1 6.9 6h10.2a2 2 0 0 1 1.9 1.5L21 13v3.5"/><path d="M3 16.5h18v2.5a1 1 0 0 1-1 1h-1.5a1 1 0 0 1-1-1v-1H6.5v1a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1z"/><path d="M6 13h3M15 13h3"/></svg></span><h4>More than lighting</h4><p>It's built for A-Chassis vehicles, so alongside the ELS you get sirens, sounds, headlights, turn signals and ke
834ybinds in the same interface.</p></div>
835      <div class="why-card"><span class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M20.5 14.5a2 2 0 0 1-2 2h-9L5 20.5v-4h-.5a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/><path d="M8 9h8M8 12h5"/></svg></span><h4>An active Discord</h4><p>Ask questions, show off builds and request features alongside everyone else using it. There's a marketplace channel too, where members post the cars they've made.</p></div>
836      <div class="why-card"><span class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2.8l2.6 5.6 6.1.8-4.5 4.2 1.2 6-5.4-3-5.4 3 1.2-6L3.3 9.2l6.1-.8z"/></svg></span><h4>What you make is yours</h4><p>Rather than paying someone to build assets for you, you build your own at no extra cost. Anything you make with EasyLS belongs to you: redistribute it, resell it, upload it to Roblox.</p></div>
837      <div class="why-card"><span class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M12 7.4v4.9l3.4 2"/></svg></span><h4>Support, 24/7</h4><p>Help with the plugin costs nothing extra and it's there around the clock. If something breaks or a feature won't behave, ask and someone will work through it with you.</p></div>
838    </div>
839  </div>
840</section>
841
842<section class="cta" id="join">
843  <div class="flash"></div>
844  <div class="wrap">
845    <h2 data-speed="1.3"><span class="cta-mark blue">EASY</span><span class="cta-mark red">LS</span><br>Light it up.</h2>
846    <p data-speed="1.45">Grab the plugin, then join the Discord if you want a hand setting up your first car.</p>
847    <div class="hero-cta" style="justify-content:center" data-speed="1.6">
848      <a class="btn btn-primary" data-magnetic href="get.html">Get EasyLS →</a>
849      <a class="btn btn-ghost" data-discord data-magnetic href="#">Join the Discord</a>
850    </div>
851  </div>
852</section>
853
854<footer>
855  <div class="wrap">
856    <div class="foot">
857      <div>
858        <div class="brand"><span class="e">EASY</span><span class="l">LS</span></div>
859        <p>An emergency lighting plugin for Roblox, plus the rest of what an A-Chassis vehicle needs: sirens, sounds, headlights, turn signals and keybinds. No scripting required.</p>
860      </div>
861      <div class="foot-links">
862        <div class="foot-col"><h5>Modules</h5>
863          <a data-link href="#lighting">Lighting</a><a data-link href="#rotators">Rotators</a><a data-link href="#modules-h">Sirens &amp; Sounds</a><a data-link href="#modules-h">Keybinds</a><a data-link href="#modules-h">Configuration</a>
864        </div>
865        <div class="foot-col"><h5>Community</h5>
866          <a data-discord href="#">Discord</a><a data-group href="#">Roblox Group</a><a href="about.html">About</a><a data-link href="#why">Why EasyLS</a>
867        </div>
868        <div class="foot-col"><h5>Get EasyLS</h5>
869          <a href="get.html">Pricing</a><a href="get.html#buy">How to buy</a><a href="get.html#install">Installation</a>
870        </div>
871      </div>
872    </div>
873    <div class="foot-bottom">
874      <span>© <span id="year"></span> EasyLS. Not affiliated with Roblox Corporation.</span>
875    </div>
876  </div>
877</footer>
878
879<script>
880/* three.js (~580KB parse+compile) is only ever used by the desktop starfield.
881   It used to load unconditionally and render-block ahead of GSAP/Lenis on
882   phones, where the consumer bailed out immediately. Now it is fetched
883   async, and only when it will actually be used. */
884(function(){
885  var reduce   = matchMedia('(prefers-reduced-motion: reduce)').matches;
886  var lowPower = matchMedia('(max-width:860px)').matches ||
887                 matchMedia('(hover:none) and (pointer:coarse)').matches;
888  if (reduce || lowPower) {
889    var c = document.getElementById('bg3d');
890    if (c) c.style.display = 'none';
891    return;
892  }
893  var s = document.createElement('script');
894  s.src = 'https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js';
895  s.integrity = 'sha384-CI3ELBVUz9XQO+97x6nwMDPosPR5XvsxW2ua7N1Xeygeh1IxtgqtCkGfQY9WWdHu';
896  s.crossOrigin = 'anonymous';
897  s.referrerPolicy = 'no-referrer';
898  s.async = true;
899  s.onload = function(){
900    window.__threeReady = true;
901    if (window.__initBg3d) window.__initBg3d();
902  };
903  document.head.appendChild(s);
904})();
905</script>
vendor: 1 bytes, line 905
905
906<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"
907  integrity="sha384-g4NTh/Iv5PPU4xPyhEWqPcwtNXOvdaDI8LLnyYfyNZOjKJeYQyjzQ9X5275eBjpt"
908  crossorigin="anonymous" referrerpolicy="no-referrer"></script>
vendor: 1 bytes, line 908
908
909<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/ScrollTrigger.min.js"
910  integrity="sha384-Z3REaz79l2IaAZqJsSABtTbhjgOUYyV3p90XNnAPCSHg3EMTz1fouunq9WZRtj3d"
911  crossorigin="anonymous" referrerpolicy="no-referrer"></script>
vendor: 1 bytes, line 911
911
912<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/lenis.min.js"
913  integrity="sha384-O55L/6rhHr9CFvrxqv5luxOCcmVaBmETbZbJDP+Do8T0pztTACsFBD/IXCNkj7DV"
914  crossorigin="anonymous" referrerpolicy="no-referrer"></script>
vendor: 1 bytes, line 914
914
915<script>
916var DISCORD_URL = "https://discord.gg/NbKz94hMcA";
917var GROUP_URL   = "https://www.roblox.com/communities/12392211/EasyLS#!/about";
918document.querySelectorAll('[data-discord]').forEach(function(a){ a.href=DISCORD_URL; a.target="_blank"; a.rel="noopener"; });
919document.querySelectorAll('[data-group]').forEach(function(a){ a.href=GROUP_URL; a.target="_blank"; a.rel="noopener"; });
920document.getElementById('year').textContent=new Date().getFullYear();
921
922(function(){
923  var c=document.getElementById('cursor'), d=document.getElementById('cursorDot');
924  var x=innerWidth/2,y=innerHeight/2,cx=x,cy=y;
925  if(!matchMedia('(hover:hover) and (pointer:fine)').matches){c.style.display='none';d.style.display='none';return;}
926  addEventListener('mousemove',function(e){ x=e.clientX;y=e.clientY; d.style.transform='translate('+x+'px,'+y+'px) translate(-50%,-50%)'; },{passive:true});
927  (function loop(){ cx+=(x-cx)*0.18; cy+=(y-cy)*0.18; c.style.transform='translate('+cx+'px,'+cy+'px) translate(-50%,-50%)'; requestAnimationFrame(loop); })();
928  document.addEventListener('mouseover',function(e){
929    if(e.target.closest('a,button,[data-magnetic],.why-card,.hpanel,.stat,.d-toggle')) c.classList.add('grow'); });
930  document.addEventListener('mouseout',function(e){
931    if(e.target.closest('a,button,[data-magnetic],.why-card,.hpanel,.stat,.d-toggle')) c.classList.remove('grow'); });
932})();
933
934(function(){
935  var fill=document.getElementById('loadFill'),pct=document.getElementById('loadPct');
936  var MIN_MS=450, MAX_MS=3500, started=Date.now(), p=0, done=false;
937
938  function paint(){ fill.style.width=p+'%'; pct.textContent='INITIALIZING '+Math.floor(p)+'%'; }
939
940  document.documentElement.classList.add('is-loading');
941
942  var iv=setInterval(function(){ if(done)return; p+=(90-p)*0.12;
942 paint(); },90);
943
944  function finish(){
945    if(done)return; done=true;
946    document.documentElement.classList.remove('is-loading');
947    clearInterval(iv);
948    p=100; paint();
949    var wait=Math.max(0, MIN_MS-(Date.now()-started));
950    setTimeout(function(){
951      document.getElementById('loader').classList.add('done');
952      init();
953    }, wait+180);
954  }
955
956  if(document.readyState==='complete') finish();
957  else addEventListener('load',finish);
958  setTimeout(finish, MAX_MS);
959})();
960
961(function(){
962  var bar=document.getElementById('lightbar'),n=14,cells=[];
963  for(var i=0;i<n;i++){var el=document.createElement('div');el.className='cell';bar.appendChild(el);cells.push(el);}
964  var t=0,timer=null;
965  function step(){ t++; cells.forEach(function(c,i){ c.classList.remove('on-b','on-r');
966    var on=(t%4<2); if(i<n/2){ if(on)c.classList.add('on-b'); } else { if(!on)c.classList.add('on-r'); } }); }
967  function startBar(){ if(!timer) timer=setInterval(step,180); }
968  function stopBar(){ if(timer){ clearInterval(timer); timer=null; } }
969  if('IntersectionObserver' in window){ new IntersectionObserver(function(es){ es[0].isIntersecting?startBar():stopBar(); },{threshold:0}).observe(bar); }
970  else startBar();
971})();
972
973(function(){
974  var words=['LIGHTING','ROTATORS','SIRENS','KEYBINDS','CONFIGURATION'];
975  function block(){var s='';words.forEach(function(w,i){s+='<span class="'+(i%2?'fill r':'fill')+'">'+w+'</span><span class="dot"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 1.5l2.4 8.1 8.1 2.4-8.1 2.4L12 22.5l-2.4-8.1L1.5 12l8.1-2.4z"/></svg></span>';});return s;}
976  document.getElementById('marqueeTrack').innerHTML=block()+block()+block()+block();
977})();
978
979/* Called by the async three.js loader above; the gating (mobile /
980   reduced-motion) already happened there, so this only runs on desktop. */
981window.__initBg3d=function(){
982  if(!window.THREE || window.__bg3dStarted) return;
983  window.__bg3dStarted=true;
984  var canvas=document.getElementById('bg3d');
985  if(!canvas) return;
986
987  var renderer=new THREE.WebGLRenderer({canvas:canvas,alpha:true,antialias:true,powerPreference:'low-power'});
988  renderer.setPixelRatio(Math.min(devicePixelRatio,1.5)); renderer.setSize(innerWidth,innerHeight);
989  var scene=new THREE.Scene(), camera=new THREE.PerspectiveCamera(60,innerWidth/innerHeight,0.1,1000); camera.position.z=60;
990  function glowTex(){var cv=document.createElement('canvas');cv.width=cv.height=128;var x=cv.getContext('2d');
991    var g=x.createRadialGradient(64,64,0,64,64,64);g.addColorStop(0,'rgba(255,255,255,1)');g.addColorStop(.25,'rgba(255,255,255,.65)');
992    g.addColorStop(.5,'rgba(255,255,255,.18)');g.addColorStop(1,'rgba(255,255,255,0)');x.fillStyle=g;x.fillRect(0,0,128,128);
993    return new THREE.CanvasTexture(cv);}
994  var tex=glowTex(), group=new THREE.Group(); scene.add(group);
995
996  var COUNT=innerWidth>1600?300:(innerWidth>1200?200:140);
997  var mats=[],data=[],blue=new THREE.Color(0x3aa6ff),red=new THREE.Color(0xff3b3b),white=new THREE.Color(0xbfe4ff);
998  for(var i=0;i<COUNT;i++){var r=Math.random(),col=r<0.45?blue:(r<0.9?red:white);
999    var m=new THREE.SpriteMaterial({map:tex,color:col,transparent:true,blending:THREE.AdditiveBlending,opacity:0,depthWrite:false});
1000    var s=new THREE.Sprite(m),sc=Math.random()*4+1.4;s.scale.set(sc,sc,1);
1001    s.position.set((Math.random()-.5)*160,(Math.random()-.5)*120,(Math.random()-.5)*130);
1002    data.push({base:s.position.clone(),sp:Math.random()*0.4+0.1,ph:Math.random()*6.28,tw:Math.random()*0.8+0.2,bo:Math.random()*0.5+0.25});
1003    group.add(s);mats.push(m);}
1004  for(var j=0;j<6;j++){var col2=j%2?red:blue;
1005    var m2=new THREE.SpriteMaterial({map:tex,color:col2,transparent:true,blending:THREE.AdditiveBlending,opacity:0.18,depthWrite:false});
1006    var s2=new THREE.Sprite(m2),sc2=Math.random()*32+26;s2.scale.set(sc2,sc2,1);
1007    s2.position.set((Math.random()-.5)*120,(Math.random()-.5)*80,-40-Math.random()*40);group.add(s2);}
1008  var mx=0,my=0,tmx=0,tmy=0,scrollY=0,intro=0,clock=new THREE.Clock();
1009  addEventListener('mousemove',function(e){tmx=e.clientX/innerWidth-.5;tmy=e.clientY/innerHeight-.5;},{passive:true});
1010  window.__setScroll=function(v){scrollY=v;};
1011  (function anim(){requestAnimationFrame(anim);var t=clock.getElapsedTime();intro=Math.min(intro+0.012,1);
1012    mx+=(tmx-mx)*0.04;my+=(tmy-my)*0.04;
1013    group.rotation.y=t*0.03+mx*0.6; group.rotation.x=my*0.45;
1014    camera.position.z=60+scrollY*0.004; camera.position.y=-scrollY*0.003;
1015    for(var i=0;i<data.length;i++){var dd=data[i],sp=group.children[i];
1016      sp.position.y=dd.base.y+Math.sin(t*dd.sp+dd.ph)*4; sp.position.x=dd.base.x+Math.cos(t*dd.sp*0.7+dd.ph)*3;
1017      mats[i].opacity=(dd.bo*(0.5+0.5*Math.sin(t*dd.tw*2+dd.ph)))*intro;}
1018    renderer.render(scene,camera);})();
1019  addEventListener('resize',function(){camera.aspect=innerWidth/innerHeight;camera.updateProjectionMatrix();renderer.setSize(innerWidth,innerHeight);},{passive:true});
1020};
1021if(window.__threeReady) window.__initBg3d();
1022
1023(function(){
1024  var v=document.querySelector('video.livegif'); if(!v) return;
1025  if(matchMedia('(prefers-reduced-motion: reduce)').matches){ v.removeAttribute('autoplay'); v.controls=true; return; }
1026  if(!('IntersectionObserver' in window)){ v.play().catch(function(){}); return; }
1027  new IntersectionObserver(function(es){
1028    if(es[0].isIntersecting) v.play().catch(function(){});
1029    else v.pause();
1030  },{threshold:0.15}).observe(v);
1031})();
1032
1033var __navEl=document.getElementById('nav'), __navSolid=false;
1034function navUpdate(s){ var solid=s>60; if(solid!==__navSolid){ __navSolid=solid; __navEl.classList.toggle('solid',solid); } }
1035
1036function init(){
1037  if(!window.gsap){ document.querySelectorAll('.reveal').forEach(function(e){e.style.opacity=1;e.style.transform='none';}); return; }
1038  gsap.registerPlugin(ScrollTrigger);
1039
1040  /* THE mobile fix. iOS collapses/expands the URL bar as you scroll, which
1041     fires `resize`. Without this, every one of those fires a full
1042     ScrollTrigger.refresh() — re-measuring ~45 triggers synchronously
1043     mid-scroll. That is the lag spike. Orientation changes still refresh. */
1044  ScrollTrigger.config({ignoreMobileResize:true});
1045
1046  var scrollIdleT=null;
1047  function markScrolling(){ window.__isScrolling=true; if(scrollIdleT)clearTimeout(scrollIdleT); scrollIdleT=setTimeout(function(){window.__isScrolling=false;},140); }
1048  addEventListener('wheel',markScrolling,{passive:true});
1049  addEventListener('touchmove',markScrolling,{passive:true});
1050
1051  var lenis=null;
1052  if(window.Lenis){
1053    lenis=new Lenis({duration:1.15,easing:function(t){return Math.min(1,1.001-Math.pow(2,-10*t));},smoothWheel:true,lerp:0.1});
1054    lenis.on('scroll',function(e){ markScrolling();
1054 ScrollTrigger.update(); window.__setScroll&&window.__setScroll(e.animatedScroll||e.scroll||window.scrollY); navUpdate(e.scroll||window.scrollY); });
1055    gsap.ticker.add(function(time){ lenis.raf(time*1000); });
1056    /* lagSmoothing(0) was here. With it off, any blocked frame made every
1057       scrub tween apply its whole accumulated delta at once — turning a
1058       stall into a visible snap. Left at the GSAP default (500, 33). */
1059  } else {
1060    addEventListener('scroll',function(){ markScrolling(); window.__setScroll&&window.__setScroll(window.scrollY); navUpdate(window.scrollY); },{passive:true});
1061  }
1062
1063  gsap.to('#progress',{scaleX:1,ease:'none',scrollTrigger:{trigger:document.body,start:'top top',end:'bottom bottom',scrub:true}});
1064
1065  gsap.utils.toArray('.reveal').forEach(function(el){
1066    gsap.to(el,{opacity:1,y:0,duration:1,ease:'power3.out',scrollTrigger:{trigger:el,start:'top 88%'}});
1067  });
1068
1069  /* .deco elements are display:none below 820px, but all 13 still carried
1070     data-speed — 13 scrub triggers doing nothing but getting re-measured. */
1071  var decoHidden=matchMedia('(max-width:820px)').matches;
1072  gsap.utils.toArray('[data-speed]').forEach(function(el){
1073    if(decoHidden && el.classList.contains('deco')) return;
1074    var sp=parseFloat(el.dataset.speed)||1; var amt=(sp-1)*220;
1075    gsap.fromTo(el,{y:amt},{y:-amt,ease:'none',
1076      scrollTrigger:{trigger:el.closest('section,header')||el,start:'top bottom',end:'bottom top',scrub:true}});
1077  });
1078
1079  gsap.utils.toArray('.pimg').forEach(function(frame){
1080    var inner=frame.querySelector('.pimg-inner');
1081    gsap.fromTo(frame,{clipPath:'inset(8% 0% 8% 0% round 16px)'},{clipPath:'inset(0% 0% 0% 0% round 16px)',ease:'power2.out',
1082      scrollTrigger:{trigger:frame,start:'top 92%',end:'top 50%',scrub:true}});
1083  });
1084
1085  gsap.utils.toArray('.tilt').forEach(function(el,k){
1086    gsap.fromTo(el,{rotateY:(k%2?-14:14),rotateX:8,y:60,opacity:0.4},
1087      {rotateY:0,rotateX:0,y:0,opacity:1,ease:'power2.out',
1088       scrollTrigger:{trigger:el,start:'top 90%',end:'top 45%',scrub:true}});
1089    el.addEventListener('mousemove',function(e){ if(window.__isScrolling) return; var r=el.getBoundingClientRect();
1090      gsap.to(el,{rotateX:((e.clientY-r.top)/r.height-.5)*-9,rotateY:((e.clientX-r.left)/r.width-.5)*9,
1091        duration:.5,transformPerspective:1000,ease:'power2.out'});});
1092    el.addEventListener('mouseleave',function(){gsap.to(el,{rotateX:0,rotateY:0,duration:.6});});
1093  });
1094
1095  gsap.utils.toArray('.f-glow').forEach(function(g){
1096    gsap.fromTo(g,{y:-60},{y:60,ease:'none',scrollTrigger:{trigger:g.closest('section'),start:'top bottom',end:'bottom top',scrub:true}});
1097  });
1098
1099  gsap.to('#heroLogo',{scale:0.9,opacity:0.7,ease:'none',scrollTrigger:{trigger:'.hero',start:'top top',end:'bottom top',scrub:true}});
1100
1101  (function(){
1102    var el=document.getElementById('statementText'),frag=[];
1103    el.childNodes.forEach(function(node){
1104      if(node.nodeType===3){ node.textContent.split(/(\s+)/).forEach(function(tk){
1105          if(tk.trim()==='')frag.push(document.createTextNode(tk));
1106          else{var s=document.createElement('span');s.className='w';s.textContent=tk;frag.push(s);} });
1107      } else { node.classList.add('w'); frag.push(node); }});
1108    el.innerHTML=''; frag.forEach(function(f){el.appendChild(f);});
1109    gsap.to(el.querySelectorAll('.w'),{opacity:1,stagger:0.08,ease:'none',
1110      scrollTrigger:{trigger:el,start:'top 78%',end:'bottom 58%',scrub:true}});
1111  })();
1112
1113  (function(){
1114    var track=document.getElementById('marqueeTrack');
1115    var loop=gsap.to(track,{xPercent:-50,repeat:-1,duration:24,ease:'none'});
1116    var vSkew=gsap.quickTo(track,'skewX',{duration:0.4,ease:'power2'});
1117    ScrollTrigger.create({onUpdate:function(self){
1118      var v=gsap.utils.clamp(-0.6,0.6,self.getVelocity()/-3000);
1119      var ts=gsap.utils.clamp(0.2,4,1+Math.abs(self.getVelocity())/2200);
1120      loop.timeScale(ts); vSkew(v*14);
1121    }});
1122  })();
1123
1124  (function(){
1125    if(matchMedia('(max-width:860px)').matches) return;
1126    var track=document.getElementById('htrack');
1127    var endLen=function(){return track.scrollWidth-innerWidth+40;};
1128    gsap.to(track,{x:function(){return -endLen();},ease:'none',
1129      scrollTrigger:{trigger:'#modules-h',start:'top top',end:function(){return '+='+endLen();},
1130        pin:true,scrub:1,invalidateOnRefresh:true}});
1131  })();
1132
1133  gsap.fromTo('#dashShot',{y:80,rotateX:12,scale:0.94},{y:0,rotateX:0,scale:1,ease:'power2.out',
1134    scrollTrigger:{trigger:'#dashShot',start:'top 90%',end:'top 45%',scrub:true}});
1135
1136  gsap.utils.toArray('.num[data-count]').forEach(function(el){
1137    var end=+el.getAttribute('data-count'),suf=el.getAttribute('data-suffix')||'',obj={v:0};
1138    gsap.to(obj,{v:end,duration:1.6,ease:'power2.out',scrollTrigger:{trigger:el,start:'top 90%'},
1139      onUpdate:function(){el.textContent=Math.floor(obj.v)+suf;}});
1140  });
1141  gsap.from('.stat',{y:40,opacity:0,stagger:0.1,duration:.7,ease:'power2.out',scrollTrigger:{trigger:'.stats',start:'top 85%'}});
1142  gsap.from('.why-card',{y:40,opacity:0,stagger:0.08,duration:.7,ease:'power2.out',scrollTrigger:{trigger:'.why-grid',start:'top 85%'}});
1143
1144  gsap.utils.toArray('[data-magnetic]').forEach(function(el){
1145    el.addEventListener('mousemove',function(e){ if(window.__isScrolling) return; var r=el.getBoundingClientRect();
1146      gsap.to(el,{x:(e.clientX-r.left-r.width/2)*0.4,y:(e.clientY-r.top-r.height/2)*0.5,duration:.4,ease:'power2.out'});});
1147    el.addEventListener('mouseleave',function(){gsap.to(el,{x:0,y:0,duration:.5,ease:'elastic.out(1,0.4)'});});
1148  });
1149
1150  document.querySelectorAll('a[data-link]').forEach(function(a){
1151    a.addEventListener('click',function(e){
1152      var id=a.getAttribute('href'); if(!id||id.length<2)return;
1153      var t=document.querySelector(id); if(!t)return; e.preventDefault();
1154      if(lenis) lenis.scrollTo(t,{offset:-60,duration:1.2});
1155      else t.scrollIntoView({behavior:'smooth'});
1156    });
1157  });
1158
1159  ScrollTrigger.refresh();
1160  addEventListener('load',function(){ScrollTrigger.refresh();});
1161  setTimeout(function(){ScrollTrigger.refresh();},900);
1162}
1163</script>
vendor: 1 bytes, line 1163
1163
1164<script>
1165(function(){
1166  var dash=document.querySelector('.els-dash'); if(!dash) return;
1167
1168  var COLORS=[['Red','#ff3b3b'],['Blue','#2b9bff'],['Green','#2ee06a'],['Amber','#ffb13b'],['White','#eef2ff']];
1169  var ci=0, lights=8, steps=16, tempo=600, grid=[], liveEls=[], playingRow=null;
1170  var colsEl=dash.querySelector('#leCols'), rowsEl=dash.querySelector('#leRows'), playBtn=dash.querySelector('#lePlay'), colorNameEl=dash.querySelector('#leColorName');
1171  function hexA(hex,a){var n=parseInt(hex.slice(1),16);return 'rgba('+((n>>16)&255)+','+((n>>8)&255)+','+(n&255)+','+a+')';}
1172  function build(){
1173    var old=grid; grid=[];
1174    for(var s=0;s<steps;s++){var row=[];for(var c=0;c<lights;c++){
1175      if(old[s]&&old[s][c]) row.push(old[s][c]);
1176      else row.push({on:((Math.floor(s/2)+c)%2===0),color:COLORS[0][1]});
1177    } grid.push(row);}
1178  }
1179  function paintCell(el,cell){
1180    if(cell.on){el.style.background=hexA(cell.color,0.30);el.style.borderColor=hexA(cell.color,0.55);el.style.boxShadow='inset 0 0 6px '+hexA(cell.color,0.28);}
1181    else{el.style.background='';el.style.borderColor='';el.style.boxShadow='';}
1182  }
1183  function render(){
1184    colsEl.style.gridTemplateColumns='repeat('+lights+',1fr)';
1185    var h='';
1186    for(var c=0;c<lights;c++){h+='<div class="le-colhead"><div class="le-colnum">'+(c+1)+'</div><div class="le-live" data-live="'+c+'"></div><div class="le-colname">Name</div><div class="le-colicons"><span>&#9881;</span><span>&#128465;</span></div></div>';}
1187    colsEl.innerHTML=h;
1188    var r='';
1189    for(var st=0;st<steps;st++){
1190      r+='<div class="le-row" data-step="'+st+'"><div class="le-rowlabel"><span class="le-rownum">'+(st+1)+'</span><span class="le-rowarrow">&rsaquo;</span></div><div class="le-cells" style="grid-template-columns:repeat('+lights+',1fr)">';
1191      for(var c2=0;c2<lights;c2++){r+='<div class="le-cell" data-s="'+st+'" data-c="'+c2+'"></div>';}
1192      r+='</div></div>';
1193    }
1194    rowsEl.innerHTML=r;
1195    liveEls=[].slice.call(colsEl.querySelectorAll('.le-live'));
1196    playingRow=null;
1197    dash.querySelectorAll('.le-cell').forEach(function(el){paintCell(el,grid[+el.dataset.s][+el.dataset.c]);});
1198  }
1199  rowsEl.addEventListener('click',function(e){
1200    var el=e.target.closest('.le-cell'); if(!el) return;
1201    var cell=grid[+el.dataset.s][+el.dataset.c], col=COLORS[ci][1];
1202    if(!cell.on){cell.on=true;cell.color=col;}
1203    else if(cell.color===col){cell.on=false;}
1204    else{cell.color=col;}
1205    paintCell(el,cell);
1206  });
1207  function setColor(d){ci=(ci+d+COLORS.length)%COLORS.length;colorNameEl.textContent=COLORS[ci][0];}
1208  dash.querySelector('#leColorPrev').addEventListener('click',function(){setColor(-1);});
1209  dash.querySelector('#leColorNext').addEventListener('click',function(){setColor(1);});
1210  dash.querySelectorAll('[data-ctrl]').forEach(function(b){
1211    b.addEventListener('click',function(){
1212      var k=b.dataset.ctrl, d=+b.dataset.d;
1213      if(k==='lights'){lights=Math.max(1,Math.min(12,lights+d));dash.querySelector('#leLights').textContent=lights;build();render();}
1214      else if(k==='steps'){steps=Math.max(4,Math.min(32,steps+d));dash.querySelector('#leSteps').textContent=steps;build();render();if(playing){restart();}}
1215      else if(k==='tempo'){tempo=Math.max(100,Math.min(1500,tempo+d*50));dash.querySelector('#leTempo').textContent=tempo;if(playing){restart();}}
1216    });
1217  });
1218  var playing=false, timer=null, head=0;
1219  function tick(){
1220    if(playingRow) playingRow.classList.remove('playing');
1221    playingRow=rowsEl.children[head]||null; if(playingRow) playingRow.classList.add('playing');
1222    for(var c=0;c<lights;c++){
1223      var live=liveEls[c]; if(!live) continue;
1224      var cell=grid[head][c];
1225      if(cell.on){live.style.background=cell.color;live.style.boxShadow='0 0 8px '+cell.color+',0 0 15px '+hexA(cell.color,0.6);}
1226      else{live.style.background='';live.style.boxShadow='';}
1227    }
1228    head=(head+1)%steps;
1229  }
1230  function start(){if(playing)return;playing=true;playBtn.innerHTML='&#10073;&#10073;';head=0;tick();timer=setInterval(tick,Math.round(132000/tempo));}
1231  function stop(){playing=false;playBtn.innerHTML='&#9654;';clearInterval(timer);
1232    if(playingRow){playingRow.classList.remove('playing');playingRow=null;}
1233    for(var i=0;i<liveEls.length;i++){liveEls[i].style.background='';liveEls[i].style.boxShadow='';}
1234  }
1235  function restart(){clearInterval(timer);timer=setInterval(tick,Math.round(132000/tempo));}
1236  playBtn.addEventListener('click',function(){playing?stop():start();});
1237  build(); render(); setColor(0);
1238
1239  var modulesView=dash.querySelector('.modules-view'), lightingView=dash.querySelector('.lighting-view');
1240  function show(lighting){
1241    modulesView.hidden=lighting; lightingView.hidden=!lighting;
1242    if(lighting) start(); else stop();
1243    if(window.ScrollTrigger){try{ScrollTrigger.refresh();}catch(e){}}
1244  }
1245  function activate(tab){dash.querySelectorAll('.d-tab').forEach(function(x){x.classList.remove('active');});if(tab)tab.classList.add('active');}
1246  dash.querySelectorAll('.d-tab').forEach(function(t){
1247    t.addEventListener('click',function(){activate(t);show(t.textContent.trim().toLowerCase()==='lighting');});
1248  });
1249  dash.querySelectorAll('.d-card').forEach(function(card){
1250    var h=card.querySelector('h3');
1251    if(h && h.textContent.trim()==='Lighting'){
1252      var m=card.querySelector('.d-modify');
1253      if(m) m.addEventListener('click',function(){
1254        var lt=null; dash.querySelectorAll('.d-tab').forEach(function(x){if(x.textContent.trim()==='Lighting')lt=x;});
1255        activate(lt); show(true);
1256      });
1257    }
1258  });
1259})();
1260</script>
vendor: 1 bytes, line 1260
1260
1261<script>
1262(function(){
1263  var burger=document.getElementById('navBurger'), menu=document.getElementById('navMobile');
1264  if(!burger||!menu) return;
1265  function close(){burger.classList.remove('open');menu.classList.remove('open');}
1266  burger.addEventListener('click',function(e){e.stopPropagation();burger.classList.toggle('open');menu.classList.toggle('open');});
1267  menu.querySelectorAll('a').forEach(function(a){a.addEventListener('click',close);});
1268  document.addEventListener('click',function(e){if(menu.classList.contains('open')&&!menu.contains(e.target)&&e.target!==burger&&!burger.contains(e.target))close();});
1269})();
1270</script>
vendor: 1 bytes, line 1270
1270
1271<script>
1272(function(){
1273  var kg=document.getElementById('pvKeys');
1274  if(kg){
1275    var letters='QWERTYUIOPASDFGHJKLZXCVBNM'.split('');
1276    var labels=['Q','W','E','R','T','Y','A','S','D','F','G','H','J','K','L','Z','X','C','V','B','N','M','↑','↓','Ctrl','Shift','Alt','Tab','1','2'];
1277    var keys=[];
1278    for(var i=0;i<30;i++){ var s=document.createElement('span'); s.textContent=labels[i%labels.length]; kg.appendChild(s); keys.push(s); }
1279    var lit=-1;
1280    function hop(){ if(lit>=0) keys[lit].classList.remove('lit');
1281      lit=Math.floor(Math.random()*keys.length);
1282      keys[lit].textContent=letters[Math.floor(Math.random()*letters.length)];
1283      keys[lit].classList.add('lit'); }
1284    hop();
1285    setInterval(function(){
1286      var p=kg.closest('.hpanel');
1287      if(p && p.classList.contains('pv-idle')) return;   /* offscreen: skip */
1288      hop();
1289    },2600);
1290  }
1291
1292  var cf=document.getElementById('pvConfig');
1293  if(cf){
1294    var palette=['#2b9bff','#ff3b3b','#ffb13b','#eef2ff','#2ee06a','#9b6bff','#ff6bd6','#5cc8ff'];
1295    for(var r=0;r<6;r++){
1296      var row=document.createElement('div'); row.className='crow '+(r%2?'r':'l');
1297      var html='';
1298      for(var c=0;c<10;c++){ var col=palette[(c+r)%palette.length]; html+='<i style="background:'+col+'"></i>'; }
1299      row.innerHTML=html+html;
1300      cf.appendChild(row);
1301    }
1302  }
1303
1304  /* Below 860px the five module panels stack vertically, so every one of
1305     them used to animate continuously whether onscreen or not: 26 wave
1306     bars, 8 box-shadow flashers, a spinning conic-gradient ring and 6
1307     marquee rows, all at once, forever. Park the offscreen ones. */
1308  var panels=[].slice.call(document.querySelectorAll('.hpanel'));
1309  if(panels.length && 'IntersectionObserver' in window){
1310    panels.forEach(function(p){ p.classList.add('pv-idle'); });
1311    var pio=new IntersectionObserver(function(entries){
1312      entries.forEach(function(e){
1313        e.target.classList.toggle('pv-idle', !e.isIntersecting);
1314      });
1315    },{rootMargin:'150px 0px'});
1316    panels.forEach(function(p){ pio.observe(p); });
1317  }
1318})();
1319</script>
vendor: 1 bytes, line 1319
1319
1320<script>
1321(function(){
1322  var viz=document.getElementById('ringViz'); if(!viz) return;
1323  var rot=document.getElementById('ringRot'), disc=document.getElementById('ringDisc'), nodesEl=document.getElementById('ringNodes'), hint=document.getElementById('ringHint');
1324  function hideHint(){ if(hint) hint.classList.add('ring-hint-gone'); }
1325  var COLORS=['#ff3b3b','#2b9bff','#ffb13b','#eef2ff'];
1326  var GAP=12, count=2, regions=[];
1327  function norm(a){a%=360; if(a<0)a+=360; return a;}
1328  function distribute(){ regions=[]; for(var i=0;i<count;i++) regions.push({color:COLORS[i],start:i*(360/count)}); }
1329  function renderDisc(){
1330    var rs=regions.slice().sort(function(a,b){return a.start-b.start;}), n=rs.length, parts=[];
1331    parts.push(rs[n-1].color+' 0deg '+rs[0].start.toFixed(2)+'deg');
1332    for(var i=0;i<n;i++){ var a=rs[i].start, b=(i<n-1?rs[i+1].start:360); parts.push(rs[i].color+' '+a.toFixed(2)+'deg '+b.toFixed(2)+'deg'); }
1333    disc.style.background='conic-gradient('+parts.join(',')+')';
1334  }
1335  function renderNodes(){
1336    nodesEl.innerHTML='';
1337    regions.forEach(function(r,i){
1338      var node=document.createElement('div'); node.className='ring-node'; node.style.transform='rotate('+r.start+'deg)';
1339      var dot=document.createElement('div'); dot.className='dot'; node.appendChild(dot);
1340      dot.addEventListener('mousedown',startDrag(i));
1341      dot.addEventListener('touchstart',startDrag(i),{passive:false});
1342      nodesEl.appendChild(node);
1343    });
1344  }
1345  function renderAll(){ renderDisc(); renderNodes(); }
1346
1347  var SPIN=0.4;
1348  var rotation=0, speed=SPIN, target=SPIN, dragging=false, hovering=false, raf=null;
1349  function frame(){ if(!dragging){ speed+=(target-speed)*0.05; if(target===0&&speed<0.012)speed=0; rotation=norm(rotation+speed); rot.style.transform='rotate('+rotation+'deg)'; } raf=requestAnimationFrame(frame); }
1350  viz.addEventListener('mouseenter',function(){ hovering=true; target=0; });
1351  viz.addEventListener('mouseleave',function(){ hovering=false; if(!dragging) target=SPIN; });
1352
1353  var dragIdx=-1, moved=false, downPt=null, lowerB=0, upperB=0, lastInteract=0;
1354  function pt(ev){ var t=ev.touches&&ev.touches[0]; return {x:(t?t.clientX:ev.clientX), y:(t?t.clientY:ev.clientY)}; }
1355  function angleAt(p){ var rc=viz.getBoundingClientRect(), cx=rc.left+rc.width/2, cy=rc.top+rc.height/2; var a=Math.atan2(p.x-cx,-(p.y-cy))*180/Math.PI; return norm(a); }
1356  function startDrag(i){ return function(ev){ ev.preventDefault(); ev.stopPropagation(); hideHint();
1357    dragIdx=i; dragging=true; speed=0; moved=false; downPt=pt(ev);
1358
1359    var cur=regions[i].start, upD=999, loD=999; lowerB=cur; upperB=cur;
1360    for(var k=0;k<regions.length;k++){ if(k===i) continue;
1361      var dCW=norm(regions[k].start-cur); if(dCW<upD){upD=dCW; upperB=regions[k].start;}
1362      var dCCW=norm(cur-regions[k].start); if(dCCW<loD){loD=dCCW; lowerB=regions[k].start;}
1363    }
1364    document.addEventListener('mousemove',onMove); document.addEventListener('mouseup',onUp);
1365    document.addEventListener('touchmove',onMove,{passive:false}); document.addEventListener('touchend',onUp);
1366  }; }
1367  function onMove(ev){ if(dragIdx<0) return; ev.preventDefault(); var p=pt(ev);
1368    if(downPt && (Math.abs(p.x-downPt.x)+Math.abs(p.y-downPt.y))>3) moved=true;
1369    var local=norm(angleAt(p)-rotation);
1370    var span=norm(upperB-lowerB); if(span===0) span=360;
1371    var rel=norm(local-lowerB);
1372    if(rel>span){
1373      rel = (rel > (span+360)/2) ? GAP : (span-GAP);
1374    } else {
1375      rel = Math.max(GAP, Math.min(span-GAP, rel));
1376    }
1377    var ns=norm(lowerB+rel); regions[dragIdx].start=ns;
1378    nodesEl.children[dragIdx].style.transform='rotate('+ns+'deg)';
1379    renderDisc();
1380  }
1381  function onUp(){
1382    document.removeEventListener('mousemove',onMove); document.removeEventListener('mouseup',onUp);
1383    document.removeEventListener('touchmove',onMove); document.removeEventListener('touchend',onUp);
1384    if(!moved) cycle();
1385    lastInteract=Date.now();
1386    dragIdx=-1; dragging=false; if(!hovering) target=SPIN;
1387  }
1388  viz.addEventListener('click',function(e){ if(Date.now()-lastInteract<400) return;  cycle(); });
1389  function cycle(){ hideHint(); count=count>=4?2:count+1; distribute(); renderAll(); }
1390
1391  distribute(); renderAll();
1392  if('IntersectionObserver' in window){
1393    new IntersectionObserver(function(es){ if(es[0].isIntersecting){ if(!raf) raf=requestAnimationFrame(frame); } else if(raf){ cancelAnimationFrame(raf); raf=null; } },{threshold:0}).observe(viz);
1394  } else raf=requestAnimationFrame(frame);
1395})();
1396</script>
vendor: 1 bytes, line 1396
1396
1397<script src="boosters.js"></script>
vendor: 1 bytes, line 1397
1397
1398<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v31edd6df95cf4e85bb4c19e7a9bdbcba1788362987495" integrity="sha512-iIg7k2xntmwu6/uSb5tpc/hySgZc4eoL31yB29W6tJFo2akwjPWcEqnCEdJvGexCL0KEQwVYv5BlowfhVz26hg==" data-cf-beacon='{"version":"2024.11.0","token":"3071eb0ef69f49ad9e8d8ac6d6fbc075","r":1,"spa":2}' crossorigin="anonymous"></script>
1398
1399</body>
1400</html>

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.