1159 lines
63 KiB
HTML
1159 lines
63 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8"/>
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||
<title>SOLAR — Autonomous AI Agent</title>
|
||
<meta name="description" content="SOLAR is a voice-activated autonomous AI agent for Windows. Wake word, local LLMs, call handling, messaging, code generation — all on your machine."/>
|
||
<link rel="preconnect" href="https://fonts.googleapis.com"/>
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin/>
|
||
<link href="https://fonts.googleapis.com/css2?family=Syne:wght@400;600;700;800&family=JetBrains+Mono:wght@300;400;500;600&family=Instrument+Sans:ital,wght@0,300;0,400;0,500;1,300&display=swap" rel="stylesheet"/>
|
||
|
||
<style>
|
||
/* ═══════════════════════════════════════════════
|
||
TOKENS
|
||
═══════════════════════════════════════════════ */
|
||
:root {
|
||
--void: #02040c;
|
||
--deep: #050813;
|
||
--card: rgba(8,12,26,0.92);
|
||
--cb: rgba(255,148,32,0.11); /* card border */
|
||
--cbh: rgba(255,148,32,0.30); /* card border hover */
|
||
|
||
--sol: #ff9820;
|
||
--sol2: #ff6100;
|
||
--sg: rgba(255,148,32,0.18); /* solar glow */
|
||
--sr: rgba(255,148,32,0.38); /* solar rim */
|
||
--sp: rgba(255,148,32,0.10); /* solar pale */
|
||
|
||
--ice: #4ddeff;
|
||
--ig: rgba(77,222,255,0.10);
|
||
--ir: rgba(77,222,255,0.28);
|
||
|
||
--grn: #21d45e;
|
||
--red: #ff4466;
|
||
--pur: #b47dff;
|
||
--pink: #ff6fa8;
|
||
|
||
--t1: #edf1fb;
|
||
--t2: #7a8baa;
|
||
--t3: #3a4a66;
|
||
--t4: #18253a;
|
||
--tc: #96ccff;
|
||
|
||
--nh: 64px;
|
||
--rc: 13px;
|
||
--rb: 8px;
|
||
|
||
--fd: 'Syne', sans-serif;
|
||
--fm: 'JetBrains Mono', monospace;
|
||
--fb: 'Instrument Sans', sans-serif;
|
||
|
||
--ease: cubic-bezier(.4,0,.2,1);
|
||
--ts: .22s;
|
||
}
|
||
|
||
/* ═══════════════════════════════════════════════
|
||
RESET
|
||
═══════════════════════════════════════════════ */
|
||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0;}
|
||
html{scroll-behavior:smooth;}
|
||
body{background:var(--void);color:var(--t1);font-family:var(--fb);font-size:16px;line-height:1.65;overflow-x:hidden;}
|
||
::selection{background:rgba(255,148,32,.28);color:#fff;}
|
||
a{color:inherit;text-decoration:none;}
|
||
::-webkit-scrollbar{width:4px;}
|
||
::-webkit-scrollbar-track{background:var(--void);}
|
||
::-webkit-scrollbar-thumb{background:rgba(255,148,32,.2);border-radius:2px;}
|
||
|
||
/* ═══════════════════════════════════════════════
|
||
BACKGROUND CANVAS
|
||
═══════════════════════════════════════════════ */
|
||
#bg{position:fixed;inset:0;z-index:0;pointer-events:none;}
|
||
|
||
/* ═══════════════════════════════════════════════
|
||
LAYOUT
|
||
═══════════════════════════════════════════════ */
|
||
.wrap{max-width:1140px;margin:0 auto;padding:0 5vw;}
|
||
section{position:relative;z-index:2;}
|
||
|
||
/* ═══════════════════════════════════════════════
|
||
NAV
|
||
═══════════════════════════════════════════════ */
|
||
nav{
|
||
position:fixed;inset:0 0 auto 0;height:var(--nh);z-index:300;
|
||
display:flex;align-items:center;justify-content:space-between;padding:0 5vw;
|
||
border-bottom:1px solid transparent;
|
||
transition:background var(--ts) var(--ease),border-color var(--ts) var(--ease);
|
||
}
|
||
nav.opaque{background:rgba(2,4,12,.92);backdrop-filter:blur(22px);border-bottom-color:var(--cb);}
|
||
|
||
.logo{
|
||
font-family:var(--fd);font-weight:800;font-size:1.25rem;letter-spacing:.16em;color:var(--sol);
|
||
display:flex;align-items:center;gap:10px;text-shadow:0 0 22px rgba(255,148,32,.5);
|
||
}
|
||
.logo-orb{
|
||
width:24px;height:24px;border-radius:50%;border:2px solid var(--sol);
|
||
display:flex;align-items:center;justify-content:center;
|
||
box-shadow:0 0 10px rgba(255,148,32,.4),inset 0 0 6px rgba(255,148,32,.18);
|
||
animation:spin 12s linear infinite;flex-shrink:0;
|
||
}
|
||
.logo-orb::before{content:'';width:6px;height:6px;background:var(--sol);border-radius:50%;box-shadow:0 0 8px var(--sol);}
|
||
@keyframes spin{to{transform:rotate(360deg);}}
|
||
|
||
.nl{display:flex;gap:2.2rem;list-style:none;}
|
||
.nl a{
|
||
font-family:var(--fm);font-size:.7rem;letter-spacing:.14em;text-transform:uppercase;
|
||
color:var(--t3);transition:color var(--ts);position:relative;
|
||
}
|
||
.nl a::after{content:'';position:absolute;bottom:-3px;left:0;right:0;height:1px;background:var(--sol);transform:scaleX(0);transform-origin:left;transition:transform var(--ts) var(--ease);}
|
||
.nl a:hover{color:var(--sol);}
|
||
.nl a:hover::after{transform:scaleX(1);}
|
||
|
||
.nav-cta{
|
||
font-family:var(--fm);font-size:.68rem;letter-spacing:.12em;text-transform:uppercase;
|
||
padding:7px 16px;border-radius:6px;border:1px solid var(--sr);color:var(--sol);
|
||
background:var(--sp);transition:all var(--ts) var(--ease);cursor:pointer;
|
||
}
|
||
.nav-cta:hover{background:rgba(255,148,32,.24);box-shadow:0 0 18px var(--sg);}
|
||
|
||
.ham{display:none;flex-direction:column;gap:5px;cursor:pointer;}
|
||
.ham span{display:block;width:22px;height:2px;background:var(--sol);border-radius:2px;transition:var(--ts) var(--ease);}
|
||
.ham.open span:nth-child(1){transform:translateY(7px) rotate(45deg);}
|
||
.ham.open span:nth-child(2){opacity:0;}
|
||
.ham.open span:nth-child(3){transform:translateY(-7px) rotate(-45deg);}
|
||
|
||
.mob{display:none;position:fixed;top:var(--nh);left:0;right:0;background:rgba(2,4,12,.97);border-bottom:1px solid var(--cb);padding:1.6rem 5vw 2rem;flex-direction:column;gap:1.4rem;z-index:299;}
|
||
.mob.open{display:flex;}
|
||
.mob a{font-family:var(--fm);font-size:.85rem;letter-spacing:.12em;text-transform:uppercase;color:var(--t2);transition:color var(--ts);}
|
||
.mob a:hover{color:var(--sol);}
|
||
|
||
/* ═══════════════════════════════════════════════
|
||
BUTTONS
|
||
═══════════════════════════════════════════════ */
|
||
.btn{
|
||
display:inline-flex;align-items:center;gap:8px;
|
||
padding:.78rem 1.8rem;border-radius:var(--rb);
|
||
font-family:var(--fm);font-size:.76rem;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
|
||
cursor:pointer;border:none;transition:all var(--ts) var(--ease);white-space:nowrap;
|
||
}
|
||
.btn-fire{
|
||
background:linear-gradient(130deg,var(--sol2) 0%,var(--sol) 100%);
|
||
color:#180800;
|
||
box-shadow:0 0 28px rgba(255,130,20,.38),0 2px 10px rgba(0,0,0,.5);
|
||
}
|
||
.btn-fire:hover{transform:translateY(-2px);box-shadow:0 0 44px rgba(255,130,20,.62),0 6px 20px rgba(0,0,0,.5);}
|
||
.btn-wire{background:transparent;color:var(--t2);border:1px solid var(--t4);}
|
||
.btn-wire:hover{border-color:var(--ir);color:var(--ice);box-shadow:0 0 18px var(--ig),inset 0 0 10px var(--ig);transform:translateY(-2px);}
|
||
|
||
/* ═══════════════════════════════════════════════
|
||
SHARED SECTION TYPOGRAPHY
|
||
═══════════════════════════════════════════════ */
|
||
.lbl{font-family:var(--fm);font-size:.62rem;letter-spacing:.28em;text-transform:uppercase;color:var(--sol);margin-bottom:.55rem;}
|
||
.sh{font-family:var(--fd);font-weight:700;font-size:clamp(1.65rem,3.4vw,2.5rem);letter-spacing:.06em;color:var(--t1);margin-bottom:.85rem;}
|
||
.sh span{color:var(--sol);}
|
||
.sb{font-size:.96rem;color:var(--t2);max-width:500px;line-height:1.82;}
|
||
|
||
/* ═══════════════════════════════════════════════
|
||
REVEAL
|
||
═══════════════════════════════════════════════ */
|
||
.rev{opacity:0;transform:translateY(26px);transition:opacity .65s var(--ease),transform .65s var(--ease);}
|
||
.rev.in{opacity:1;transform:translateY(0);}
|
||
.d1{transition-delay:.08s;}.d2{transition-delay:.16s;}.d3{transition-delay:.24s;}.d4{transition-delay:.32s;}
|
||
|
||
/* ═══════════════════════════════════════════════
|
||
HERO
|
||
═══════════════════════════════════════════════ */
|
||
#home{
|
||
min-height:100vh;display:flex;align-items:center;justify-content:center;
|
||
padding:calc(var(--nh) + 90px) 5vw 130px;text-align:center;
|
||
position:relative;overflow:hidden;
|
||
}
|
||
/* dot grid */
|
||
#home::before{
|
||
content:'';position:absolute;inset:0;
|
||
background-image:radial-gradient(rgba(255,148,32,.14) 1px,transparent 1px);
|
||
background-size:42px 42px;z-index:1;pointer-events:none;
|
||
mask-image:radial-gradient(ellipse 80% 80% at 50% 50%,black 30%,transparent 100%);
|
||
}
|
||
/* bottom fade */
|
||
#home::after{content:'';position:absolute;bottom:0;left:0;right:0;height:220px;background:linear-gradient(transparent,var(--void));z-index:2;pointer-events:none;}
|
||
|
||
/* concentric rings */
|
||
.rings{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);pointer-events:none;z-index:1;}
|
||
.ring{position:absolute;border-radius:50%;border:1px solid rgba(255,148,32,.06);top:50%;left:50%;transform:translate(-50%,-50%);animation:pulse-ring 8s ease-in-out infinite;}
|
||
.ring:nth-child(1){width:340px;height:340px;animation-delay:0s;}
|
||
.ring:nth-child(2){width:540px;height:540px;animation-delay:1.5s;border-color:rgba(255,148,32,.04);}
|
||
.ring:nth-child(3){width:740px;height:740px;animation-delay:3s;border-color:rgba(255,148,32,.025);}
|
||
/* glow blob */
|
||
.glow{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:320px;height:320px;border-radius:50%;background:radial-gradient(circle,rgba(255,160,40,.11) 0%,rgba(255,90,10,.04) 40%,transparent 70%);filter:blur(8px);animation:pulse-ring 6s ease-in-out infinite;}
|
||
@keyframes pulse-ring{0%,100%{opacity:.7;transform:translate(-50%,-50%) scale(1);}50%{opacity:1;transform:translate(-50%,-50%) scale(1.06);}}
|
||
|
||
.hero-inner{position:relative;z-index:3;max-width:min(880px,94vw);width:100%;overflow:hidden;}
|
||
|
||
.chip{
|
||
display:inline-flex;align-items:center;gap:8px;padding:5px 14px;
|
||
border-radius:999px;border:1px solid var(--cb);background:rgba(255,148,32,.04);
|
||
font-family:var(--fm);font-size:.66rem;letter-spacing:.18em;text-transform:uppercase;color:var(--t3);
|
||
margin-bottom:2rem;animation:fadeup .7s .1s both;
|
||
}
|
||
.chip-dot{width:6px;height:6px;border-radius:50%;background:var(--grn);box-shadow:0 0 7px var(--grn);animation:blink 2s ease-in-out infinite;}
|
||
@keyframes blink{0%,100%{opacity:1;}50%{opacity:.2;}}
|
||
|
||
.hero-h1{
|
||
font-family:var(--fd);font-weight:800;
|
||
font-size:clamp(2.6rem,min(9vw,8vh),7.5rem);letter-spacing:clamp(.08em,.15em,.2em);line-height:1;color:#fff;
|
||
text-shadow:0 0 90px rgba(255,148,32,.38),0 0 180px rgba(255,90,10,.12);
|
||
margin-bottom:1rem;animation:fadeup .8s .15s both;
|
||
}
|
||
.hero-ascii{
|
||
font-family:var(--fm);
|
||
font-size:clamp(0.52rem,1.8vw,1.35rem);
|
||
line-height:1.25;
|
||
letter-spacing:0;
|
||
white-space:pre;
|
||
color:var(--sol);
|
||
text-shadow:0 0 40px rgba(255,148,32,0.5),0 0 80px rgba(255,90,10,0.18);
|
||
margin-bottom:1rem;
|
||
animation:fadeup 0.8s 0.15s both;
|
||
display:block;
|
||
overflow:hidden;
|
||
max-width:100%;
|
||
}
|
||
.flk{color:var(--sol);animation:flicker 9s ease-in-out infinite;}
|
||
@keyframes flicker{0%,93%,95.5%,98%,100%{opacity:1;}94%{opacity:.35;}96%{opacity:.85;}99%{opacity:.25;}}
|
||
|
||
.hero-tagline{
|
||
font-family:var(--fm);font-size:clamp(.58rem,1.8vw,.85rem);letter-spacing:clamp(.1em,.22em,.22em);text-transform:uppercase;
|
||
color:var(--t3);margin-bottom:1.6rem;animation:fadeup .8s .25s both;
|
||
padding:0 1rem;
|
||
}
|
||
.hero-tagline em{color:var(--sol);font-style:normal;}
|
||
|
||
.hero-desc{
|
||
font-size:1.05rem;color:var(--t2);max-width:580px;margin:0 auto 2.8rem;
|
||
line-height:1.88;animation:fadeup .8s .35s both;
|
||
}
|
||
.hero-desc strong{color:var(--t1);font-weight:500;}
|
||
|
||
.hero-cta{display:flex;gap:1rem;justify-content:center;flex-wrap:wrap;animation:fadeup .8s .45s both;}
|
||
|
||
/* voice waveform — scaleY() only, no height changes → zero layout reflow/wobble */
|
||
.wave{display:flex;align-items:center;gap:3px;margin:3.2rem auto 0;width:fit-content;height:44px;animation:fadeup .8s .55s both;}
|
||
.wb{width:3px;height:100%;background:var(--sol);border-radius:2px;opacity:.45;transform-origin:center;transform:scaleY(.08);animation:wa var(--d,1s) ease-in-out infinite var(--dl,0s);will-change:transform,opacity;}
|
||
@keyframes wa{0%,100%{transform:scaleY(.08);opacity:.2;}50%{transform:scaleY(var(--s,.55));opacity:.75;}}
|
||
|
||
/* ─ SCROLL CUE ─
|
||
Fix: no transform on parent – use margin-left trick to centre
|
||
so child transform animation doesn't cause layout wobble */
|
||
.scroll-cue{
|
||
position:absolute;
|
||
bottom:40px;
|
||
left:50%;
|
||
margin-left:-24px; /* half of its natural width so it centres without transform */
|
||
z-index:3;
|
||
display:flex;flex-direction:column;align-items:center;gap:6px;
|
||
font-family:var(--fm);font-size:.58rem;letter-spacing:.22em;text-transform:uppercase;color:var(--t3);
|
||
width:48px; /* fixed width so margin-left works */
|
||
animation:fadeup 1s 1.1s both;
|
||
}
|
||
.scroll-arr{
|
||
width:16px;height:16px;
|
||
border-right:1px solid var(--t3);
|
||
border-bottom:1px solid var(--t3);
|
||
transform:rotate(45deg);
|
||
/* Only animates within its own stacking context – no parent transform interference */
|
||
animation:arr-bob 2s ease-in-out infinite;
|
||
will-change:transform;
|
||
}
|
||
@keyframes arr-bob{
|
||
0%,100%{transform:rotate(45deg) translate(0,0);}
|
||
50% {transform:rotate(45deg) translate(3px,3px);}
|
||
}
|
||
|
||
@keyframes fadeup{from{opacity:0;transform:translateY(22px);}to{opacity:1;transform:translateY(0);}}
|
||
|
||
/* ═══════════════════════════════════════════════
|
||
MARQUEE — scrolling voice commands / social proof
|
||
═══════════════════════════════════════════════ */
|
||
#proof{
|
||
padding:70px 0;
|
||
background:linear-gradient(to bottom,var(--void),var(--deep) 30%,var(--deep) 70%,var(--void));
|
||
overflow:hidden;position:relative;z-index:2;
|
||
}
|
||
.proof-label{text-align:center;margin-bottom:2rem;}
|
||
|
||
.marquee-wrap{display:flex;flex-direction:column;gap:1rem;overflow:hidden;}
|
||
.marquee-track{display:flex;gap:1rem;width:max-content;animation:marquee-l var(--spd,38s) linear infinite;}
|
||
.marquee-track.rev-dir{animation-direction:reverse;}
|
||
.marquee-wrap:hover .marquee-track{animation-play-state:paused;}
|
||
|
||
@keyframes marquee-l{from{transform:translateX(0);}to{transform:translateX(-50%);}}
|
||
|
||
.mq-card{
|
||
display:flex;align-items:center;gap:12px;
|
||
padding:12px 18px;border-radius:10px;
|
||
background:var(--card);border:1px solid var(--cb);
|
||
white-space:nowrap;flex-shrink:0;
|
||
transition:border-color .2s;
|
||
}
|
||
.mq-card:hover{border-color:var(--sr);}
|
||
.mq-icon{font-size:1rem;flex-shrink:0;}
|
||
.mq-cmd{font-family:var(--fm);font-size:.74rem;color:var(--tc);}
|
||
.mq-cmd .p{color:var(--sol);margin-right:6px;}
|
||
.mq-result{font-family:var(--fm);font-size:.68rem;color:var(--t3);margin-left:4px;}
|
||
|
||
/* ═══════════════════════════════════════════════
|
||
FEATURES
|
||
═══════════════════════════════════════════════ */
|
||
#features{padding:110px 0;}
|
||
.fh{text-align:center;margin-bottom:60px;}
|
||
.fh .sb{margin:0 auto;}
|
||
|
||
/* asymmetric bento grid */
|
||
.fg{display:grid;grid-template-columns:repeat(12,1fr);gap:1.1rem;}
|
||
.f7{grid-column:span 7;}
|
||
.f5{grid-column:span 5;}
|
||
.f4{grid-column:span 4;}
|
||
@media(max-width:900px){.f7,.f5,.f4{grid-column:span 12;}}
|
||
@media(min-width:901px) and (max-width:1060px){.f7{grid-column:span 6;}.f5{grid-column:span 6;}.f4{grid-column:span 6;}}
|
||
|
||
.fc{
|
||
background:var(--card);border:1px solid var(--cb);border-radius:var(--rc);
|
||
padding:1.9rem 1.9rem 2.1rem;position:relative;overflow:hidden;
|
||
transition:border-color .28s,box-shadow .28s,transform .28s;backdrop-filter:blur(4px);
|
||
}
|
||
.fc::before{content:'';position:absolute;top:0;left:12%;right:12%;height:1px;background:linear-gradient(90deg,transparent,var(--sr),transparent);opacity:0;transition:opacity .28s;}
|
||
.fc:hover{border-color:var(--cbh);box-shadow:0 14px 48px rgba(0,0,0,.45),0 0 26px var(--sp);transform:translateY(-3px);}
|
||
.fc:hover::before{opacity:1;}
|
||
|
||
.fc-blob{position:absolute;top:-50px;right:-50px;width:150px;height:150px;border-radius:50%;pointer-events:none;opacity:.05;filter:blur(40px);transition:opacity .28s;}
|
||
.fc:hover .fc-blob{opacity:.13;}
|
||
|
||
.fc-ico{width:42px;height:42px;border-radius:9px;display:flex;align-items:center;justify-content:center;font-size:1.15rem;margin-bottom:1rem;}
|
||
.ico-o{background:rgba(255,148,32,.08);border:1px solid rgba(255,148,32,.2);}
|
||
.ico-c{background:rgba(77,222,255,.07);border:1px solid rgba(77,222,255,.18);}
|
||
|
||
.fc-title{font-family:var(--fd);font-size:.92rem;font-weight:600;letter-spacing:.04em;color:var(--t1);margin-bottom:.5rem;}
|
||
.fc-body{font-size:.86rem;color:var(--t2);line-height:1.76;}
|
||
.fc-tag{display:inline-block;margin-top:1rem;padding:3px 10px;border-radius:999px;font-family:var(--fm);font-size:.6rem;letter-spacing:.14em;text-transform:uppercase;}
|
||
.tag-o{color:var(--sol);background:rgba(255,148,32,.08);border:1px solid var(--cb);}
|
||
.tag-c{color:var(--ice);background:rgba(77,222,255,.07);border:1px solid rgba(77,222,255,.15);}
|
||
|
||
.cmd-row{display:flex;align-items:center;gap:9px;margin-top:.85rem;padding:8px 13px;background:rgba(0,0,0,.38);border:1px solid var(--t4);border-radius:7px;font-family:var(--fm);font-size:.73rem;color:var(--tc);}
|
||
.cmd-row .p{color:var(--sol);flex-shrink:0;}
|
||
|
||
.mpill{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:6px;background:rgba(77,222,255,.05);border:1px solid rgba(77,222,255,.13);font-family:var(--fm);font-size:.63rem;color:var(--ice);letter-spacing:.07em;margin-top:.55rem;margin-right:.35rem;}
|
||
.mpill .gd{width:5px;height:5px;border-radius:50%;background:var(--grn);box-shadow:0 0 5px var(--grn);}
|
||
|
||
/* ═══════════════════════════════════════════════
|
||
HOW IT WORKS
|
||
═══════════════════════════════════════════════ */
|
||
#how{
|
||
padding:110px 0;
|
||
background:linear-gradient(to bottom,var(--void),var(--deep) 50%,var(--void));
|
||
}
|
||
.how-hd{text-align:center;margin-bottom:72px;}
|
||
.how-hd .sb{margin:0 auto;}
|
||
|
||
.flow{display:flex;align-items:flex-start;justify-content:center;flex-wrap:wrap;gap:0;}
|
||
.fs{display:flex;flex-direction:column;align-items:center;text-align:center;width:185px;position:relative;}
|
||
.fs:not(:last-child)::after{content:'→';position:absolute;right:-22px;top:17px;color:var(--sr);font-size:1.1rem;font-family:var(--fm);}
|
||
.fi{width:50px;height:50px;border-radius:11px;display:flex;align-items:center;justify-content:center;font-size:1.25rem;margin-bottom:.95rem;border:1px solid var(--cb);background:var(--card);box-shadow:0 0 18px var(--sp);transition:all .3s;}
|
||
.fs:hover .fi{border-color:var(--sr);box-shadow:0 0 28px var(--sg);transform:translateY(-3px);}
|
||
.fl{font-family:var(--fm);font-size:.68rem;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--t1);margin-bottom:.3rem;}
|
||
.fn{font-size:.77rem;color:var(--t3);line-height:1.5;}
|
||
|
||
@media(max-width:680px){.fs::after{display:none;}.flow{gap:1.6rem;}}
|
||
|
||
/* ═══════════════════════════════════════════════
|
||
MODEL STACK
|
||
═══════════════════════════════════════════════ */
|
||
#models{padding:110px 0;}
|
||
.mh{text-align:center;margin-bottom:60px;}
|
||
.mh .sb{margin:0 auto;}
|
||
|
||
.mg{max-width:1060px;margin:0 auto;display:grid;grid-template-columns:1fr 1fr;gap:1.3rem;}
|
||
@media(max-width:700px){.mg{grid-template-columns:1fr;}}
|
||
|
||
.mcat{border-radius:var(--rc);overflow:hidden;border:1px solid var(--cb);background:var(--card);position:relative;}
|
||
|
||
.mc-hd{display:flex;align-items:center;gap:10px;padding:13px 19px;border-bottom:1px solid var(--cb);}
|
||
.mc-hd.loc{background:rgba(33,212,94,.05);}
|
||
.mc-hd.cld{background:rgba(77,222,255,.05);}
|
||
|
||
.mcd{width:7px;height:7px;border-radius:50%;flex-shrink:0;}
|
||
.mcd.loc{background:var(--grn);box-shadow:0 0 7px var(--grn);animation:blink 2.2s ease-in-out infinite;}
|
||
.mcd.cld{background:var(--ice);box-shadow:0 0 7px var(--ice);animation:blink 2.8s ease-in-out infinite;}
|
||
|
||
.mcl{font-family:var(--fd);font-size:.78rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;}
|
||
.mcl.loc{color:var(--grn);}.mcl.cld{color:var(--ice);}
|
||
.mcn{font-family:var(--fm);font-size:.6rem;letter-spacing:.08em;color:var(--t3);margin-left:auto;}
|
||
|
||
.mth{display:grid;grid-template-columns:2fr 1.3fr 2.4fr;padding:7px 18px;background:rgba(255,255,255,.02);border-bottom:1px solid var(--t4);}
|
||
.mthc{font-family:var(--fm);font-size:.56rem;letter-spacing:.18em;text-transform:uppercase;color:var(--t3);}
|
||
.mtr{display:grid;grid-template-columns:2fr 1.3fr 2.4fr;padding:12px 18px;border-bottom:1px solid var(--t4);transition:background .2s;align-items:center;}
|
||
.mtr:last-child{border-bottom:none;}
|
||
.mtr:hover{background:rgba(255,255,255,.02);}
|
||
.mtc{font-family:var(--fm);font-size:.73rem;color:var(--t2);}
|
||
.mtc.nm{color:var(--tc);font-weight:500;}
|
||
|
||
.mcft{padding:9px 18px;font-family:var(--fm);font-size:.6rem;letter-spacing:.08em;color:var(--t3);border-top:1px solid var(--t4);}
|
||
.mcft a{color:var(--ice);transition:opacity .2s;}.mcft a:hover{opacity:.65;}
|
||
|
||
/* shared row styling for STT/TTS (dimmed = same as local) */
|
||
.shared-row{background:rgba(33,212,94,.03);}
|
||
.shared-row .mtc{opacity:.55;}
|
||
|
||
/* BADGE */
|
||
.bd{display:inline-block;padding:2px 8px;border-radius:4px;font-size:.58rem;letter-spacing:.1em;text-transform:uppercase;}
|
||
.bd-loc{background:rgba(33,212,94,.1);color:var(--grn);border:1px solid rgba(33,212,94,.24);}
|
||
.bd-cld{background:rgba(77,222,255,.09);color:var(--ice);border:1px solid rgba(77,222,255,.22);}
|
||
.bd-vis{background:rgba(255,148,32,.1);color:var(--sol);border:1px solid rgba(255,148,32,.24);}
|
||
.bd-stt{background:rgba(180,125,255,.1);color:var(--pur);border:1px solid rgba(180,125,255,.24);}
|
||
.bd-tts{background:rgba(255,111,168,.09);color:var(--pink);border:1px solid rgba(255,111,168,.22);}
|
||
|
||
/* ─── COMING SOON OVERLAY ─── */
|
||
.soon-wrap{position:relative;}
|
||
.soon-overlay{
|
||
position:absolute;inset:-1px; /* cover border too */
|
||
border-radius:var(--rc);
|
||
background:rgba(8,12,26,.78);
|
||
backdrop-filter:blur(6px);
|
||
-webkit-backdrop-filter:blur(6px);
|
||
display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;
|
||
z-index:10;
|
||
border:1px solid rgba(255,255,255,.06);
|
||
}
|
||
.soon-pill{
|
||
display:inline-flex;align-items:center;gap:8px;
|
||
padding:8px 20px;border-radius:999px;
|
||
background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.1);
|
||
font-family:var(--fm);font-size:.72rem;letter-spacing:.2em;text-transform:uppercase;color:rgba(255,255,255,.5);
|
||
}
|
||
.soon-pill .sd{width:6px;height:6px;border-radius:50%;background:rgba(255,255,255,.3);}
|
||
.soon-sub{font-family:var(--fm);font-size:.6rem;letter-spacing:.12em;color:rgba(255,255,255,.25);text-transform:uppercase;}
|
||
|
||
/* ═══════════════════════════════════════════════
|
||
INSTALL
|
||
═══════════════════════════════════════════════ */
|
||
#install{
|
||
padding:110px 0;
|
||
background:linear-gradient(to bottom,var(--void),var(--deep) 40%,var(--deep) 60%,var(--void));
|
||
}
|
||
.ih{text-align:center;margin-bottom:56px;}
|
||
.ih .sb{margin:0 auto;}
|
||
|
||
.ig{max-width:980px;margin:0 auto;display:grid;grid-template-columns:1fr 1fr;gap:1.3rem;}
|
||
@media(max-width:700px){.ig{grid-template-columns:1fr;}}
|
||
|
||
.icard{display:flex;flex-direction:column;}
|
||
|
||
/* coloured header above each terminal */
|
||
.ich{display:flex;align-items:center;gap:11px;padding:13px 17px;border-radius:10px 10px 0 0;border:1px solid;border-bottom:none;}
|
||
.ich.loc{background:rgba(33,212,94,.06);border-color:rgba(33,212,94,.22);}
|
||
.ich.cld{background:rgba(77,222,255,.06);border-color:rgba(77,222,255,.22);}
|
||
.ichd{width:8px;height:8px;border-radius:50%;flex-shrink:0;}
|
||
.ichd.loc{background:var(--grn);box-shadow:0 0 8px var(--grn);animation:blink 2.2s ease-in-out infinite;}
|
||
.ichd.cld{background:var(--ice);box-shadow:0 0 8px var(--ice);animation:blink 2.8s ease-in-out infinite;}
|
||
.icht{font-family:var(--fd);font-size:.78rem;font-weight:700;letter-spacing:.09em;text-transform:uppercase;}
|
||
.icht.loc{color:var(--grn);}.icht.cld{color:var(--ice);}
|
||
.ichs{font-family:var(--fm);font-size:.6rem;letter-spacing:.06em;color:var(--t3);margin-top:1px;}
|
||
.ichr{font-family:var(--fm);font-size:.6rem;letter-spacing:.08em;color:var(--t3);margin-left:auto;white-space:nowrap;}
|
||
|
||
/* terminal widget */
|
||
.term{background:#020510;border:1px solid;overflow:hidden;border-radius:0 0 10px 10px;}
|
||
.icard .ich.loc ~ .term{border-color:rgba(33,212,94,.22);}
|
||
.icard .ich.cld ~ .term{border-color:rgba(77,222,255,.22);}
|
||
.tbar{display:flex;align-items:center;gap:5px;padding:9px 15px;background:rgba(255,255,255,.025);border-bottom:1px solid rgba(255,255,255,.04);}
|
||
.td{width:10px;height:10px;border-radius:50%;}
|
||
.td-r{background:#ff5f57;}.td-y{background:#febc2e;}.td-g{background:#28c840;}
|
||
.tt{font-family:var(--fm);font-size:.65rem;letter-spacing:.07em;color:var(--t3);margin-left:6px;}
|
||
.tbody{padding:1.3rem 1.5rem;display:flex;align-items:center;gap:.9rem;}
|
||
.tline{display:flex;align-items:center;gap:9px;flex:1;min-width:0;}
|
||
.tps{font-family:var(--fm);font-size:.78rem;color:var(--sol);flex-shrink:0;user-select:none;}
|
||
.tcmd{font-family:var(--fm);font-size:.78rem;color:var(--tc);word-break:break-all;}
|
||
.tcur{display:inline-block;width:7px;height:13px;background:var(--sol);margin-left:2px;vertical-align:middle;animation:cblink 1.1s step-start infinite;}
|
||
@keyframes cblink{0%,100%{opacity:1;}50%{opacity:0;}}
|
||
|
||
.cpybtn{flex-shrink:0;display:flex;align-items:center;gap:6px;padding:6px 13px;border-radius:6px;background:rgba(255,148,32,.08);border:1px solid var(--cb);font-family:var(--fm);font-size:.66rem;letter-spacing:.1em;text-transform:uppercase;color:var(--sol);cursor:pointer;transition:all var(--ts) var(--ease);white-space:nowrap;}
|
||
.cpybtn:hover{background:rgba(255,148,32,.18);border-color:var(--sr);box-shadow:0 0 12px var(--sg);}
|
||
.cpybtn.ok{background:rgba(33,212,94,.1);border-color:rgba(33,212,94,.34);color:var(--grn);}
|
||
|
||
/* model pills below each card */
|
||
.imodels{display:flex;flex-wrap:wrap;gap:.45rem;margin-top:.85rem;}
|
||
.ipill{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:6px;font-family:var(--fm);font-size:.63rem;letter-spacing:.07em;}
|
||
.ipill.loc{background:rgba(33,212,94,.07);border:1px solid rgba(33,212,94,.17);color:var(--grn);}
|
||
.ipill.cld{background:rgba(77,222,255,.06);border:1px solid rgba(77,222,255,.17);color:var(--ice);}
|
||
.ipill .dot{width:5px;height:5px;border-radius:50%;}
|
||
.ipill .dot.g{background:var(--grn);box-shadow:0 0 5px var(--grn);}
|
||
.ipill .dot.c{background:var(--ice);box-shadow:0 0 5px var(--ice);}
|
||
|
||
/* shared note */
|
||
.inote{display:flex;align-items:center;justify-content:center;gap:7px;margin-top:1.4rem;font-family:var(--fm);font-size:.7rem;letter-spacing:.1em;color:var(--t3);}
|
||
|
||
/* req chips */
|
||
.reqs{display:flex;flex-wrap:wrap;gap:.65rem;justify-content:center;margin-top:2rem;}
|
||
.rchip{display:flex;align-items:center;gap:6px;padding:6px 13px;border-radius:8px;background:var(--card);border:1px solid var(--cb);font-family:var(--fm);font-size:.68rem;letter-spacing:.07em;color:var(--t2);}
|
||
|
||
/* ═══════════════════════════════════════════════
|
||
GITHUB CTA
|
||
═══════════════════════════════════════════════ */
|
||
#github{padding:110px 0;background:linear-gradient(to bottom,var(--void),var(--deep));}
|
||
.ghcard{
|
||
max-width:800px;margin:0 auto;background:var(--card);border:1px solid var(--cb);
|
||
border-radius:18px;padding:3.5rem;text-align:center;position:relative;overflow:hidden;
|
||
}
|
||
.ghcard::before{content:'';position:absolute;top:0;left:10%;right:10%;height:1px;background:linear-gradient(90deg,transparent,var(--sr),transparent);}
|
||
.ghcard::after{content:'';position:absolute;inset:0;background:radial-gradient(ellipse at 50% 0%,rgba(255,148,32,.05),transparent 60%);pointer-events:none;}
|
||
.gh-ico{display:block;margin:0 auto 1.4rem;filter:drop-shadow(0 0 14px rgba(255,148,32,.25));}
|
||
.gh-h{font-family:var(--fd);font-weight:700;font-size:1.9rem;letter-spacing:.06em;margin-bottom:.9rem;}
|
||
.gh-h span{color:var(--sol);}
|
||
.gh-p{font-size:.97rem;color:var(--t2);max-width:480px;margin:0 auto 2.2rem;line-height:1.85;}
|
||
.gh-stats{display:flex;gap:3rem;justify-content:center;flex-wrap:wrap;margin-bottom:2.2rem;}
|
||
.gh-stat .sv{font-family:var(--fd);font-size:1.3rem;font-weight:700;color:var(--sol);display:block;}
|
||
.gh-stat .sl{font-family:var(--fm);font-size:.6rem;letter-spacing:.15em;text-transform:uppercase;color:var(--t3);}
|
||
|
||
/* ═══════════════════════════════════════════════
|
||
FOOTER
|
||
═══════════════════════════════════════════════ */
|
||
footer{
|
||
padding:42px 5vw 30px;border-top:1px solid var(--cb);
|
||
display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:1rem;
|
||
position:relative;z-index:2;
|
||
}
|
||
.fl2{font-family:var(--fd);font-weight:800;font-size:.95rem;letter-spacing:.2em;color:var(--sol);opacity:.55;}
|
||
.fc2{font-family:var(--fm);font-size:.66rem;letter-spacing:.1em;color:var(--t3);}
|
||
.fa{font-family:var(--fm);font-size:.66rem;letter-spacing:.1em;color:var(--t3);transition:color var(--ts);}
|
||
.fa:hover{color:var(--sol);}
|
||
|
||
/* ═══════════════════════════════════════════════
|
||
RESPONSIVE
|
||
═══════════════════════════════════════════════ */
|
||
@media(max-width:768px){
|
||
.nl,.nav-cta{display:none;}
|
||
.ham{display:flex;}
|
||
.hero-cta{flex-direction:column;align-items:center;}
|
||
.tbody{flex-direction:column;align-items:flex-start;}
|
||
.cpybtn{align-self:flex-end;}
|
||
.ghcard{padding:2.4rem 1.5rem;}
|
||
.gh-stats{gap:1.6rem;}
|
||
footer{flex-direction:column;align-items:center;text-align:center;}
|
||
.mth,.mtr{grid-template-columns:1.4fr 1fr;}
|
||
.mthc:last-child,.mtc:last-child{display:none;}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<!-- ══════════════════════════════════════════════
|
||
BACKGROUND PARTICLE CANVAS
|
||
══════════════════════════════════════════════ -->
|
||
<canvas id="bg"></canvas>
|
||
|
||
<!-- ══════════════════════════════════════════════
|
||
NAV
|
||
══════════════════════════════════════════════ -->
|
||
<nav id="nav">
|
||
<a href="#home" class="logo">
|
||
<div class="logo-orb"></div>SOLAR
|
||
</a>
|
||
<ul class="nl">
|
||
<li><a href="#home">Home</a></li>
|
||
<li><a href="#features">Features</a></li>
|
||
<li><a href="#how">How It Works</a></li>
|
||
<li><a href="#models">Models</a></li>
|
||
<li><a href="#install">Install</a></li>
|
||
</ul>
|
||
<a href="https://github.com/Creative-Crafter/SOLAR" target="_blank" rel="noopener" class="nav-cta">GitHub →</a>
|
||
<div class="ham" id="ham"><span></span><span></span><span></span></div>
|
||
</nav>
|
||
<div class="mob" id="mob">
|
||
<a href="#home" onclick="closeMob()">Home</a>
|
||
<a href="#features" onclick="closeMob()">Features</a>
|
||
<a href="#how" onclick="closeMob()">How It Works</a>
|
||
<a href="#models" onclick="closeMob()">Models</a>
|
||
<a href="#install" onclick="closeMob()">Install</a>
|
||
<a href="#github" onclick="closeMob()">GitHub</a>
|
||
</div>
|
||
|
||
<!-- ══════════════════════════════════════════════
|
||
HERO
|
||
══════════════════════════════════════════════ -->
|
||
<section id="home">
|
||
<div class="rings">
|
||
<div class="ring"></div><div class="ring"></div><div class="ring"></div>
|
||
</div>
|
||
<div class="glow"></div>
|
||
|
||
<div class="hero-inner">
|
||
<div class="chip"><span class="chip-dot"></span>Voice-Driven · Local First · Open-Source</div>
|
||
|
||
<pre class="hero-ascii" aria-label="SOLAR"><span class="flk"> ____ ___ _ _ ____
|
||
/ ___| / _ \ | | / \ | _ \
|
||
\___ \ | | | | | | / _ \ | |_) |
|
||
___) | _ | |_| | _ | |___ _ / ___ \ _ | _ <
|
||
|____/ (_) \___/ (_) |_____| (_) /_/ \_\ (_) |_| \_\</span></pre>
|
||
|
||
<p class="hero-tagline"><em>Systematic</em> Online or Local <em>Autonomous Robot</em></p>
|
||
|
||
<p class="hero-desc">
|
||
Say its name. It listens. SOLAR is a <strong>voice-activated AI agent</strong> that runs fully on your Windows machine — handling calls, sending messages, writing code, and controlling your desktop through natural speech. No cloud required.
|
||
</p>
|
||
|
||
<div class="hero-cta">
|
||
<a href="https://github.com/Creative-Crafter/SOLAR" target="_blank" rel="noopener" class="btn btn-wire">
|
||
<svg width="15" height="15" viewBox="0 0 16 16" fill="currentColor"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg>
|
||
View on GitHub
|
||
</a>
|
||
<a href="#install" class="btn btn-fire">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
|
||
Install Now
|
||
</a>
|
||
</div>
|
||
|
||
<!-- voice wave decoration -->
|
||
<div class="wave" aria-hidden="true">
|
||
<div class="wb" style="--s:.13;--d:.95s;--dl:0s"></div>
|
||
<div class="wb" style="--s:.28;--d:.8s; --dl:.08s"></div>
|
||
<div class="wb" style="--s:.50;--d:1.1s;--dl:.04s"></div>
|
||
<div class="wb" style="--s:.74;--d:.75s;--dl:.18s"></div>
|
||
<div class="wb" style="--s:.46;--d:.95s;--dl:.13s"></div>
|
||
<div class="wb" style="--s:.84;--d:.85s;--dl:0s"></div>
|
||
<div class="wb" style="--s:.64;--d:.72s;--dl:.22s"></div>
|
||
<div class="wb" style="--s:.90;--d:1.1s;--dl:.09s"></div>
|
||
<div class="wb" style="--s:.50;--d:.88s;--dl:.27s"></div>
|
||
<div class="wb" style="--s:.70;--d:.95s;--dl:.04s"></div>
|
||
<div class="wb" style="--s:.35;--d:1.1s;--dl:.18s"></div>
|
||
<div class="wb" style="--s:.20;--d:.8s; --dl:.09s"></div>
|
||
<div class="wb" style="--s:.13;--d:.95s;--dl:0s"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- fixed-width centred, no parent transform so child won't wobble -->
|
||
<div class="scroll-cue">
|
||
<span>Scroll</span>
|
||
<div class="scroll-arr"></div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ══════════════════════════════════════════════
|
||
SOCIAL PROOF — scrolling voice commands
|
||
══════════════════════════════════════════════ -->
|
||
<section id="proof">
|
||
<div class="proof-label rev">
|
||
<p class="lbl">// voice commands</p>
|
||
</div>
|
||
|
||
<div class="marquee-wrap">
|
||
<!-- Row 1 -->
|
||
<div class="marquee-track" style="--spd:44s">
|
||
<!-- duplicated set so the loop is seamless -->
|
||
<script>
|
||
const row1=[
|
||
{i:'📞',c:'incoming call from Anna Müller',r:'→ Should I answer?'},
|
||
{i:'💬',c:'say happy birthday to Thomas on whatsapp',r:'→ Message sent'},
|
||
{i:'💻',c:'code a python web scraper',r:'→ Copied to clipboard'},
|
||
{i:'🖥️',c:'open youtube.com',r:'→ Opened on screen 1'},
|
||
{i:'📅',c:'deadline from 2pm to 4pm name it sprint review',r:'→ .ics created'},
|
||
{i:'🕐',c:'what time is it',r:'→ It\'s 3:42 PM'},
|
||
{i:'📧',c:'write a mail to cancel my gym membership',r:'→ Draft ready'},
|
||
{i:'🎙️',c:'[wake word detected]',r:'→ Beep — listening…'},
|
||
{i:'💬',c:'say good morning to Julia on discord',r:'→ Message sent'},
|
||
{i:'💻',c:'program a react button component',r:'→ Code generated'},
|
||
];
|
||
// render twice for seamless loop
|
||
let h='';
|
||
for(let pass=0;pass<2;pass++)
|
||
row1.forEach(m=>{h+=`<div class="mq-card"><span class="mq-icon">${m.i}</span><div class="mq-cmd"><span class="p">You:</span>${m.c}<span class="mq-result">${m.r}</span></div></div>`;});
|
||
document.write(h);
|
||
</script>
|
||
</div>
|
||
<!-- Row 2 (reversed) -->
|
||
<div class="marquee-track rev-dir" style="--spd:52s">
|
||
<script>
|
||
const row2=[
|
||
{i:'🖥️',c:'open spotify',r:'→ Moved to screen 2'},
|
||
{i:'📅',c:'deadline tomorrow from 9am to 11am name it client call',r:'→ Calendar updated'},
|
||
{i:'📞',c:'call from unknown number',r:'→ Voicemail left'},
|
||
{i:'💬',c:'say see you tomorrow to Mike on instagram',r:'→ Message sent'},
|
||
{i:'🗓️',c:'what is today\'s date',r:'→ Friday, March 13'},
|
||
{i:'💻',c:'code a sql query for top 10 customers',r:'→ Copied'},
|
||
{i:'📧',c:'mail to reschedule our meeting to next week',r:'→ Draft ready'},
|
||
{i:'🔇',c:'shutdown',r:'→ Shutting down. Goodbye!'},
|
||
{i:'🎙️',c:'[custom wake word triggered]',r:'→ Ready for command'},
|
||
{i:'🖥️',c:'open notion.so',r:'→ Chrome opened'},
|
||
];
|
||
let h2='';
|
||
for(let pass=0;pass<2;pass++)
|
||
row2.forEach(m=>{h2+=`<div class="mq-card"><span class="mq-icon">${m.i}</span><div class="mq-cmd"><span class="p">You:</span>${m.c}<span class="mq-result">${m.r}</span></div></div>`;});
|
||
document.write(h2);
|
||
</script>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ══════════════════════════════════════════════
|
||
FEATURES
|
||
══════════════════════════════════════════════ -->
|
||
<section id="features">
|
||
<div class="wrap">
|
||
<div class="fh rev">
|
||
<p class="lbl">// capabilities</p>
|
||
<h2 class="sh">What <span>SOLAR</span> Can Do</h2>
|
||
<p class="sb">Every feature below is implemented in real, running Python — not a roadmap.</p>
|
||
</div>
|
||
|
||
<div class="fg">
|
||
|
||
<!-- Call handler — wide -->
|
||
<div class="fc f7 rev">
|
||
<div class="fc-blob" style="background:var(--sol)"></div>
|
||
<div class="fc-ico ico-o">📞</div>
|
||
<div class="fc-title">Autonomous WhatsApp Call Handler</div>
|
||
<div class="fc-body">SOLAR monitors your desktop for incoming WhatsApp calls using Win32 <code style="color:var(--sol);font-family:var(--fm);font-size:.8em">EnumWindows</code>. It screenshots the notification, runs it through a vision model to read the caller's name, then asks you by voice whether to answer — or declines with a custom voicemail, all autonomously.</div>
|
||
<div class="cmd-row"><span class="p">SOLAR:</span>"Incoming call from Anna Müller. Should I answer?"</div>
|
||
<div class="cmd-row"><span class="p">You:</span>"No, I'm busy." → SOLAR answers, leaves VM, hangs up</div>
|
||
<span class="fc-tag tag-o">Vision AI · Win32</span>
|
||
</div>
|
||
|
||
<!-- Wake Word — narrow -->
|
||
<div class="fc f5 rev d1">
|
||
<div class="fc-blob" style="background:var(--ice)"></div>
|
||
<div class="fc-ico ico-c">👂</div>
|
||
<div class="fc-title">Custom Wake Word</div>
|
||
<div class="fc-body">Define your agent's name in <code style="color:var(--sol);font-family:var(--fm);font-size:.8em">config.yaml</code>. SOLAR listens 24/7 using fully offline Vosk STT — no internet, no cloud microphone, zero latency.</div>
|
||
<span class="fc-tag tag-c">Always On · Offline</span>
|
||
</div>
|
||
|
||
<!-- Messaging -->
|
||
<div class="fc f4 rev d1">
|
||
<div class="fc-blob" style="background:var(--sol)"></div>
|
||
<div class="fc-ico ico-o">💬</div>
|
||
<div class="fc-title">Cross-Platform Messaging</div>
|
||
<div class="fc-body">Send messages on WhatsApp, Discord, and Instagram using image recognition to navigate each app's UI. No official API required.</div>
|
||
<div class="cmd-row"><span class="p">You:</span>say happy birthday to Thomas on whatsapp</div>
|
||
<span class="fc-tag tag-o">WhatsApp · Discord · Instagram</span>
|
||
</div>
|
||
|
||
<!-- Code Gen -->
|
||
<div class="fc f4 rev d2">
|
||
<div class="fc-blob" style="background:var(--sol)"></div>
|
||
<div class="fc-ico ico-o">💻</div>
|
||
<div class="fc-title">Voice Code Generation</div>
|
||
<div class="fc-body">Say "code a…" and SOLAR generates it with DeepSeek Coder, auto-copies to clipboard, then describes what was built.</div>
|
||
<div class="cmd-row"><span class="p">You:</span>code a python web scraper</div>
|
||
<span class="fc-tag tag-o">DeepSeek Coder v2</span>
|
||
</div>
|
||
|
||
<!-- Window Manager -->
|
||
<div class="fc f4 rev d3">
|
||
<div class="fc-blob" style="background:var(--ice)"></div>
|
||
<div class="fc-ico ico-c">🖥️</div>
|
||
<div class="fc-title">App & Window Manager</div>
|
||
<div class="fc-body">Open any app or URL and move its window to any connected monitor. Multi-monitor aware, including negative-coordinate secondary screens.</div>
|
||
<div class="cmd-row"><span class="p">You:</span>open youtube.com</div>
|
||
<span class="fc-tag tag-c">Multi-Monitor · Win32</span>
|
||
</div>
|
||
|
||
<!-- Calendar — narrow -->
|
||
<div class="fc f5 rev d1">
|
||
<div class="fc-blob" style="background:var(--ice)"></div>
|
||
<div class="fc-ico ico-c">📅</div>
|
||
<div class="fc-title">Voice Calendar Events</div>
|
||
<div class="fc-body">Describe a meeting in plain speech. SOLAR parses the time with dateparser, creates an <code style="color:var(--sol);font-family:var(--fm);font-size:.8em">.ics</code> file, and opens it straight in your calendar app.</div>
|
||
<div class="cmd-row"><span class="p">You:</span>deadline from 2pm to 4pm name it sprint review</div>
|
||
<span class="fc-tag tag-c">Calendar · .ics</span>
|
||
</div>
|
||
|
||
<!-- Multi-model — wide -->
|
||
<div class="fc f7 rev">
|
||
<div class="fc-blob" style="background:var(--sol)"></div>
|
||
<div class="fc-ico ico-o">🧩</div>
|
||
<div class="fc-title">Multi-Model AI Routing</div>
|
||
<div class="fc-body">SOLAR automatically routes each task to the right model via Ollama on <code style="color:var(--sol);font-family:var(--fm);font-size:.8em">localhost:11434</code>. Vision tasks go to the multimodal model, code to DeepSeek Coder, writing to DeepSeek v2, and general chat to the main model. No API keys, no data leaving your machine.</div>
|
||
<div style="margin-top:.75rem;line-height:2.2">
|
||
<span class="mpill"><span class="gd"></span>llama3.2-vision:11b</span>
|
||
<span class="mpill"><span class="gd"></span>Malicus7862/deepseekcoder-6.7b-jarvis-gguf:latest</span>
|
||
<span class="mpill"><span class="gd"></span>deepseek-v2:16b</span>
|
||
<span class="mpill"><span class="gd"></span>Vosk STT</span>
|
||
<span class="mpill"><span class="gd"></span>Kokoro TTS</span>
|
||
</div>
|
||
<span class="fc-tag tag-o">Fully Local · Ollama</span>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ══════════════════════════════════════════════
|
||
HOW IT WORKS
|
||
══════════════════════════════════════════════ -->
|
||
<section id="how">
|
||
<div class="wrap">
|
||
<div class="how-hd rev">
|
||
<p class="lbl">// runtime flow</p>
|
||
<h2 class="sh">How <span>SOLAR</span> Works</h2>
|
||
<p class="sb">Every interaction starts with your voice. SOLAR routes it through local models — no cloud required.</p>
|
||
</div>
|
||
|
||
<div class="flow">
|
||
<div class="fs rev d1">
|
||
<div class="fi">🎙️</div>
|
||
<div class="fl">Wake Word</div>
|
||
<div class="fn">Vosk listens 24/7 for your custom name from <code style="color:var(--sol);font-family:var(--fm);font-size:.7em">config.yaml</code></div>
|
||
</div>
|
||
<div class="fs rev d2">
|
||
<div class="fi">🔊</div>
|
||
<div class="fl">Hear Command</div>
|
||
<div class="fn">A beep confirms activation. Full command transcribed offline</div>
|
||
</div>
|
||
<div class="fs rev d3">
|
||
<div class="fi">🧠</div>
|
||
<div class="fl">Route & Execute</div>
|
||
<div class="fn">Intent matched — messaging, coding, calls, apps, or chat</div>
|
||
</div>
|
||
<div class="fs rev d4">
|
||
<div class="fi">🔈</div>
|
||
<div class="fl">Speak Back</div>
|
||
<div class="fn">Kokoro TTS voices the response in your configured name & gender</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ══════════════════════════════════════════════
|
||
MODEL STACK
|
||
══════════════════════════════════════════════ -->
|
||
<section id="models">
|
||
<div class="wrap">
|
||
<div class="mh rev">
|
||
<p class="lbl">// ai stack</p>
|
||
<h2 class="sh">The <span>Model Stack</span></h2>
|
||
<p class="sb">Pick fully local Ollama models or Ollama's cloud models — STT and TTS are always local either way.</p>
|
||
</div>
|
||
|
||
<div class="mg">
|
||
|
||
<!-- LOCAL -->
|
||
<div class="mcat rev d1">
|
||
<div class="mc-hd loc">
|
||
<span class="mcd loc"></span>
|
||
<span class="mcl loc">Local Models</span>
|
||
<span class="mcn">localhost · no internet</span>
|
||
</div>
|
||
<div class="mth">
|
||
<div class="mthc">Model</div>
|
||
<div class="mthc">Type</div>
|
||
<div class="mthc">Used For</div>
|
||
</div>
|
||
<div class="mtr">
|
||
<div class="mtc nm">llama3.2-vision:11b</div>
|
||
<div class="mtc"><span class="bd bd-vis">Vision</span></div>
|
||
<div class="mtc">Call ID · general chat</div>
|
||
</div>
|
||
<div class="mtr">
|
||
<div class="mtc nm">Malicus7862/deepseekcoder-6.7b-jarvis-gguf:latest</div>
|
||
<div class="mtc"><span class="bd bd-loc">Code</span></div>
|
||
<div class="mtc">Voice code generation</div>
|
||
</div>
|
||
<div class="mtr">
|
||
<div class="mtc nm">deepseek-v2:16b</div>
|
||
<div class="mtc"><span class="bd bd-loc">Text</span></div>
|
||
<div class="mtc">Email · writing tasks</div>
|
||
</div>
|
||
<div class="mtr">
|
||
<div class="mtc nm">Vosk small-en-us</div>
|
||
<div class="mtc"><span class="bd bd-stt">STT</span></div>
|
||
<div class="mtc">Wake word · transcription</div>
|
||
</div>
|
||
<div class="mtr">
|
||
<div class="mtc nm">Kokoro TTS</div>
|
||
<div class="mtc"><span class="bd bd-tts">TTS</span></div>
|
||
<div class="mtc">Voice responses</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- CLOUD — with Coming Soon overlay -->
|
||
<div class="soon-wrap rev d2">
|
||
<div class="mcat">
|
||
<div class="mc-hd cld">
|
||
<span class="mcd cld"></span>
|
||
<span class="mcl cld">Ollama Cloud Models</span>
|
||
<span class="mcn">ollama.com · API key</span>
|
||
</div>
|
||
<div class="mth">
|
||
<div class="mthc">Model</div>
|
||
<div class="mthc">Type</div>
|
||
<div class="mthc">Used For</div>
|
||
</div>
|
||
<div class="mtr">
|
||
<div class="mtc nm">qwen3-vl:235b-cloud</div>
|
||
<div class="mtc"><span class="bd bd-vis">Vision</span></div>
|
||
<div class="mtc">Call ID · general chat</div>
|
||
</div>
|
||
<div class="mtr">
|
||
<div class="mtc nm">qwen3-coder:480b-cloud</div>
|
||
<div class="mtc"><span class="bd bd-cld">Code</span></div>
|
||
<div class="mtc">Voice code generation</div>
|
||
</div>
|
||
<div class="mtr">
|
||
<div class="mtc nm">deepseek-v3.1:671b-cloud</div>
|
||
<div class="mtc"><span class="bd bd-cld">Text</span></div>
|
||
<div class="mtc">Email · writing tasks</div>
|
||
</div>
|
||
<div class="mtr shared-row">
|
||
<div class="mtc nm">Vosk small-en-us</div>
|
||
<div class="mtc"><span class="bd bd-stt">STT</span></div>
|
||
<div class="mtc">Same as local</div>
|
||
</div>
|
||
<div class="mtr shared-row">
|
||
<div class="mtc nm">Kokoro TTS</div>
|
||
<div class="mtc"><span class="bd bd-tts">TTS</span></div>
|
||
<div class="mtc">Same as local</div>
|
||
</div>
|
||
<div class="mcft">Powered by ollama.com cloud GPUs · <a href="https://ollama.com/library" target="_blank" rel="noopener">Browse all →</a></div>
|
||
</div>
|
||
|
||
<!-- ─ COMING SOON OVERLAY ─ -->
|
||
<div class="soon-overlay">
|
||
<div class="soon-pill">
|
||
<span class="sd"></span>
|
||
Coming Soon
|
||
</div>
|
||
<div class="soon-sub">Ollama Cloud integration in progress</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ══════════════════════════════════════════════
|
||
INSTALL
|
||
══════════════════════════════════════════════ -->
|
||
<section id="install">
|
||
<div class="wrap">
|
||
<div class="ih rev">
|
||
<p class="lbl">// quick start</p>
|
||
<h2 class="sh">Choose Your <span>Setup</span></h2>
|
||
<p class="sb">Pick the install that fits your hardware. Both handle dependencies and config automatically.</p>
|
||
</div>
|
||
|
||
<div class="ig">
|
||
|
||
<!-- LOCAL INSTALL -->
|
||
<div class="icard rev d1">
|
||
<div class="ich loc">
|
||
<span class="ichd loc"></span>
|
||
<div>
|
||
<div class="icht loc">Local Install</div>
|
||
<div class="ichs">All models run on your GPU · No cloud inference</div>
|
||
</div>
|
||
<div class="ichr">~20 GB VRAM</div>
|
||
</div>
|
||
<div class="term">
|
||
<div class="tbar">
|
||
<span class="td td-r"></span><span class="td td-y"></span><span class="td td-g"></span>
|
||
<span class="tt">PowerShell — Administrator</span>
|
||
</div>
|
||
<div class="tbody">
|
||
<div class="tline">
|
||
<span class="tps">PS C:\></span>
|
||
<span class="tcmd" id="cmd-local">irm https://creative-crafter.de/solar/install-local.ps1 | iex</span>
|
||
<span class="tcur"></span>
|
||
</div>
|
||
<button class="cpybtn" id="btn-local" onclick="doCopy('local')">
|
||
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
|
||
Copy
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="imodels">
|
||
<span class="ipill loc"><span class="dot g"></span>llama3.2-vision:11b</span>
|
||
<span class="ipill loc"><span class="dot g"></span>Malicus7862/deepseekcoder-6.7b-jarvis-gguf:latest</span>
|
||
<span class="ipill loc"><span class="dot g"></span>deepseek-v2:16b</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- CLOUD INSTALL — with Coming Soon overlay -->
|
||
<div class="soon-wrap rev d2">
|
||
<div class="icard">
|
||
<div class="ich cld">
|
||
<span class="ichd cld"></span>
|
||
<div>
|
||
<div class="icht cld">Cloud Install</div>
|
||
<div class="ichs">Ollama cloud models · No powerful GPU needed</div>
|
||
</div>
|
||
<div class="ichr">Ollama account</div>
|
||
</div>
|
||
<div class="term">
|
||
<div class="tbar">
|
||
<span class="td td-r"></span><span class="td td-y"></span><span class="td td-g"></span>
|
||
<span class="tt">PowerShell — Administrator</span>
|
||
</div>
|
||
<div class="tbody">
|
||
<div class="tline">
|
||
<span class="tps">PS C:\></span>
|
||
<span class="tcmd" id="cmd-cloud">irm https://creative-crafter.de/solar/install-cloud.ps1 | iex</span>
|
||
<span class="tcur"></span>
|
||
</div>
|
||
<button class="cpybtn" id="btn-cloud" onclick="doCopy('cloud')">
|
||
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
|
||
Copy
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="imodels">
|
||
<span class="ipill cld"><span class="dot c"></span>qwen3-vl:235b-cloud</span>
|
||
<span class="ipill cld"><span class="dot c"></span>qwen3-coder:480b-cloud</span>
|
||
<span class="ipill cld"><span class="dot c"></span>deepseek-v3.1:671b-cloud</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ─ COMING SOON OVERLAY ─ -->
|
||
<div class="soon-overlay">
|
||
<div class="soon-pill">
|
||
<span class="sd"></span>
|
||
Coming Soon
|
||
</div>
|
||
<div class="soon-sub">Ollama Cloud install script in progress</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div><!-- /ig -->
|
||
|
||
<p class="inote rev d3">
|
||
🛡️ Both scripts require <strong style="color:var(--t2)">Administrator</strong> PowerShell · Vosk STT & Kokoro TTS are shared between both setups
|
||
</p>
|
||
|
||
<div class="reqs rev d4">
|
||
<div class="rchip">🪟 Windows 10 / 11</div>
|
||
<div class="rchip">🐍 Python 3.11.0</div>
|
||
<div class="rchip">🦙 Ollama v0.12+</div>
|
||
<div class="rchip">🎙️ Microphone</div>
|
||
<div class="rchip">💾 ~20 GB free (local models)</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ══════════════════════════════════════════════
|
||
GITHUB CTA
|
||
══════════════════════════════════════════════ -->
|
||
<section id="github">
|
||
<div class="wrap">
|
||
<div class="ghcard rev">
|
||
<svg class="gh-ico" width="50" height="50" viewBox="0 0 16 16" fill="rgba(255,152,32,.82)"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg>
|
||
<h2 class="gh-h">Open Source, <span>Always</span></h2>
|
||
<p class="gh-p">Every line of SOLAR is public. Fork it, extend it, add new skills in <code style="color:var(--sol);font-family:var(--fm);font-size:.85em">skills.py</code>, or swap in different Ollama models. The project is built to be hacked on.</p>
|
||
<div class="gh-stats">
|
||
<div class="gh-stat"><span class="sv">Apache 2.0</span><span class="sl">License</span></div>
|
||
<div class="gh-stat"><span class="sv">Python</span><span class="sl">Language</span></div>
|
||
<div class="gh-stat"><span class="sv">v0.1</span><span class="sl">Alpha</span></div>
|
||
</div>
|
||
<a href="https://github.com/Creative-Crafter/SOLAR" target="_blank" rel="noopener" class="btn btn-fire">
|
||
<svg width="15" height="15" viewBox="0 0 16 16" fill="currentColor"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg>
|
||
View on GitHub
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ══════════════════════════════════════════════
|
||
FOOTER
|
||
══════════════════════════════════════════════ -->
|
||
<footer>
|
||
<div class="fl2">SOLAR</div>
|
||
<div class="fc2">© 2025 SOLAR Project — Apache 2.0 License</div>
|
||
<a href="https://github.com/Creative-Crafter/SOLAR" target="_blank" rel="noopener" class="fa">GitHub →</a>
|
||
</footer>
|
||
|
||
<!-- ══════════════════════════════════════════════
|
||
JAVASCRIPT
|
||
══════════════════════════════════════════════ -->
|
||
<script>
|
||
/* ── PARTICLE CANVAS ── */
|
||
(function(){
|
||
const c=document.getElementById('bg');
|
||
if(!c||typeof c.getContext!=='function')return;
|
||
const ctx=c.getContext('2d');
|
||
let W,H,P=[];
|
||
function build(n){P=[];for(let i=0;i<n;i++)P.push({x:Math.random()*W,y:Math.random()*H,r:Math.random()*1.1+.2,a:Math.random()*.55+.08,vx:(Math.random()-.5)*.06,vy:-(Math.random()*.045+.008),w:Math.random()<.1});}
|
||
function resize(){W=c.width=innerWidth;H=c.height=innerHeight;build(160);}
|
||
function draw(){
|
||
ctx.clearRect(0,0,W,H);
|
||
const g=ctx.createRadialGradient(W*.5,H*.44,0,W*.5,H*.44,W*.48);
|
||
g.addColorStop(0,'rgba(28,10,3,.12)');g.addColorStop(1,'transparent');
|
||
ctx.fillStyle=g;ctx.fillRect(0,0,W,H);
|
||
P.forEach(p=>{
|
||
ctx.beginPath();ctx.arc(p.x,p.y,p.r,0,Math.PI*2);
|
||
ctx.fillStyle=p.w?`rgba(255,165,65,${p.a})`:`rgba(150,195,255,${p.a})`;ctx.fill();
|
||
p.x+=p.vx;p.y+=p.vy;
|
||
p.a=Math.max(.04,Math.min(.7,p.a+(Math.random()-.5)*.005));
|
||
if(p.y<-2)p.y=H+2;if(p.x<-2)p.x=W+2;if(p.x>W+2)p.x=-2;
|
||
});
|
||
requestAnimationFrame(draw);
|
||
}
|
||
window.addEventListener('resize',resize,{passive:true});
|
||
resize();draw();
|
||
})();
|
||
|
||
/* ── NAV SCROLL ── */
|
||
(function(){
|
||
const nav=document.getElementById('nav');
|
||
window.addEventListener('scroll',()=>nav.classList.toggle('opaque',scrollY>28),{passive:true});
|
||
})();
|
||
|
||
/* ── HAMBURGER ── */
|
||
const ham=document.getElementById('ham'),mob=document.getElementById('mob');
|
||
ham.addEventListener('click',()=>{const o=mob.classList.toggle('open');ham.classList.toggle('open',o);});
|
||
function closeMob(){mob.classList.remove('open');ham.classList.remove('open');}
|
||
|
||
/* ── COPY BUTTONS ── */
|
||
const CPY_ICON=`<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>`;
|
||
const CHK_ICON=`<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>`;
|
||
|
||
function doCopy(which){
|
||
const cmd=document.getElementById('cmd-'+which).textContent.trim();
|
||
const btn=document.getElementById('btn-'+which);
|
||
const reset=()=>{btn.classList.remove('ok');btn.innerHTML=CPY_ICON+' Copy';};
|
||
const ok=()=>{btn.classList.add('ok');btn.innerHTML=CHK_ICON+' Copied!';setTimeout(reset,2400);};
|
||
|
||
// Robust fallback: always works regardless of protocol (file://, http, https)
|
||
function legacyCopy(){
|
||
const ta=document.createElement('textarea');
|
||
ta.value=cmd;
|
||
ta.setAttribute('readonly','');
|
||
ta.style.cssText='position:fixed;top:0;left:0;opacity:0;pointer-events:none;';
|
||
document.body.appendChild(ta);
|
||
ta.focus();ta.select();
|
||
try{
|
||
const success=document.execCommand('copy');
|
||
success?ok():reset();
|
||
}catch(e){reset();}
|
||
document.body.removeChild(ta);
|
||
}
|
||
|
||
if(navigator.clipboard&&window.isSecureContext){
|
||
navigator.clipboard.writeText(cmd).then(ok).catch(legacyCopy);
|
||
} else {
|
||
legacyCopy();
|
||
}
|
||
}
|
||
|
||
/* ── SCROLL REVEAL ── */
|
||
(function(){
|
||
const obs=new IntersectionObserver(es=>es.forEach(e=>{if(e.isIntersecting)e.target.classList.add('in');}),{threshold:.09});
|
||
document.querySelectorAll('.rev').forEach(el=>obs.observe(el));
|
||
})();
|
||
|
||
/* ── SMOOTH SCROLL ── */
|
||
document.querySelectorAll('a[href^="#"]').forEach(a=>{
|
||
a.addEventListener('click',e=>{
|
||
const t=document.querySelector(a.getAttribute('href'));
|
||
if(t){e.preventDefault();window.scrollTo({top:t.getBoundingClientRect().top+scrollY-68,behavior:'smooth'});}
|
||
});
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|