LMS — Language App Landing
A gamified language-learning app landing page built with vanilla HTML, CSS and JavaScript. A warm green-to-teal palette and friendly rounded type frame a floating phone mockup that runs a live word-bank lesson: tap chips to build the translation, check your answer, lose a heart when wrong, then continue. Streak and XP counters animate up, language cards fill progress bars, and a monthly to yearly pricing toggle swaps plan prices, all with scroll reveals and toasts.
MCP
Code
:root{
--brand:#10b981; /* emerald */
--brand-d:#0d9b6e;
--teal:#0ea5a4;
--teal-d:#0b8d8c;
--brand-50:#e9fbf3;
--accent:#f59e0b;
--amber:#f59e0b;
--ink:#0f1f1a;
--ink-2:#324b41;
--muted:#6b7d76;
--bg:#ffffff;
--surface:#ffffff;
--soft:#f3fbf7;
--line:rgba(15,31,26,0.10);
--ok:#10b981;
--danger:#e05656;
--grad:linear-gradient(120deg,#10b981 0%,#0ea5a4 55%,#0891b2 100%);
--grad-soft:linear-gradient(135deg,#e9fbf3 0%,#e0f7f6 100%);
--r-sm:10px; --r-md:16px; --r-lg:24px; --r-xl:34px;
--sh-sm:0 2px 8px rgba(15,31,26,.06);
--sh-md:0 14px 34px rgba(13,155,110,.14);
--sh-lg:0 30px 60px rgba(8,90,80,.20);
--fs:"Inter",system-ui,-apple-system,sans-serif;
--fd:"Baloo 2","Inter",system-ui,sans-serif;
}
*,*::before,*::after{box-sizing:border-box}
html{scroll-behavior:smooth}
body{
margin:0;font-family:var(--fs);color:var(--ink);background:var(--bg);
line-height:1.5;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
}
h1,h2,h3,h4{font-family:var(--fd);margin:0;line-height:1.12;letter-spacing:-.01em}
p{margin:0}
a{color:inherit;text-decoration:none}
img{max-width:100%}
.wrap{width:min(1140px,92vw);margin-inline:auto}
.skip-link{position:absolute;left:-999px;top:0;background:var(--ink);color:#fff;padding:10px 16px;border-radius:0 0 8px 0;z-index:200}
.skip-link:focus{left:0}
/* ===== buttons ===== */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;
font-family:var(--fs);font-weight:700;font-size:.95rem;border:0;cursor:pointer;
padding:12px 20px;border-radius:999px;transition:transform .15s ease,box-shadow .2s ease,background .2s}
.btn:active{transform:translateY(1px) scale(.99)}
.btn-primary{background:var(--grad);color:#fff;box-shadow:0 8px 20px rgba(13,155,110,.32)}
.btn-primary:hover{box-shadow:0 12px 26px rgba(13,155,110,.42);transform:translateY(-1px)}
.btn-ghost{background:transparent;color:var(--ink-2)}
.btn-ghost:hover{background:var(--soft);color:var(--ink)}
.btn-soft{background:var(--brand-50);color:var(--brand-d)}
.btn-soft:hover{background:#d6f5e7}
.block{display:flex;width:100%}
/* ===== nav ===== */
.nav{position:sticky;top:0;z-index:100;background:rgba(255,255,255,.82);
backdrop-filter:saturate(140%) blur(12px);border-bottom:1px solid transparent;transition:border-color .25s,box-shadow .25s}
.nav.scrolled{border-color:var(--line);box-shadow:0 4px 18px rgba(15,31,26,.05)}
.nav-inner{display:flex;align-items:center;gap:18px;height:68px}
.brand{display:flex;align-items:center;gap:9px;font-family:var(--fd);font-weight:800;font-size:1.25rem}
.brand-mark{font-size:1.5rem;filter:drop-shadow(0 3px 4px rgba(13,155,110,.3))}
.brand-name{background:var(--grad);-webkit-background-clip:text;background-clip:text;color:transparent}
.nav-links{display:flex;gap:26px;margin-left:auto}
.nav-links a{font-weight:600;color:var(--ink-2);font-size:.96rem;position:relative}
.nav-links a::after{content:"";position:absolute;left:0;right:100%;bottom:-6px;height:2px;background:var(--brand);border-radius:2px;transition:right .25s}
.nav-links a:hover{color:var(--ink)}
.nav-links a:hover::after{right:0}
.nav-cta{display:flex;gap:8px;margin-left:8px}
.nav-toggle{display:none;flex-direction:column;gap:5px;background:none;border:0;cursor:pointer;padding:8px;margin-left:auto}
.nav-toggle span{width:24px;height:2.5px;background:var(--ink);border-radius:2px;transition:.25s}
.nav-toggle[aria-expanded="true"] span:nth-child(1){transform:translateY(7.5px) rotate(45deg)}
.nav-toggle[aria-expanded="true"] span:nth-child(2){opacity:0}
.nav-toggle[aria-expanded="true"] span:nth-child(3){transform:translateY(-7.5px) rotate(-45deg)}
.mobile-menu{display:flex;flex-direction:column;gap:6px;padding:14px 6vw 22px;border-top:1px solid var(--line);background:#fff}
.mobile-menu a{padding:11px 6px;font-weight:600;color:var(--ink-2);border-radius:10px}
.mobile-menu a:hover{background:var(--soft)}
.mobile-menu .btn{margin-top:6px}
/* ===== hero ===== */
.hero{position:relative;overflow:hidden;padding:54px 0 70px;background:
radial-gradient(900px 480px at 78% -8%, rgba(16,185,129,.16), transparent 60%),
radial-gradient(700px 500px at -6% 30%, rgba(8,145,178,.12), transparent 55%),
var(--bg)}
.hero-grid{display:grid;grid-template-columns:1.05fr .95fr;gap:46px;align-items:center}
.eyebrow{display:inline-flex;align-items:center;gap:8px;background:#fff;border:1px solid var(--line);
box-shadow:var(--sh-sm);padding:7px 14px;border-radius:999px;font-size:.84rem;font-weight:600;color:var(--ink-2)}
.hero-copy h1{font-size:clamp(2.3rem,5.4vw,3.7rem);margin:18px 0 16px}
.hl{background:var(--grad);-webkit-background-clip:text;background-clip:text;color:transparent}
.lede{font-size:1.12rem;color:var(--ink-2);max-width:30ch}
.hero-actions{display:flex;gap:12px;margin:26px 0 22px;flex-wrap:wrap}
.hero-actions.center{justify-content:center}
.store-btn{display:inline-flex;align-items:center;gap:11px;background:var(--ink);color:#fff;
padding:10px 18px;border-radius:14px;transition:transform .15s,box-shadow .2s}
.store-btn:hover{transform:translateY(-2px);box-shadow:0 12px 24px rgba(15,31,26,.22)}
.store-btn small{display:block;font-size:.65rem;opacity:.8;font-weight:500}
.store-btn strong{display:block;font-size:1.02rem;font-weight:700;margin-top:-1px}
.store-btn.light{background:#fff;color:var(--ink)}
.store-ic{width:22px;height:22px;border-radius:5px;background:#fff;-webkit-mask:center/contain no-repeat;mask:center/contain no-repeat;
-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='black' d='M16.5 1.6c.1 1-.3 2-1 2.8-.7.8-1.8 1.4-2.9 1.3-.1-1 .4-2 1-2.7.7-.8 1.9-1.4 2.9-1.4zM20 17.4c-.5 1.2-.8 1.7-1.5 2.7-1 1.4-2.4 3.2-4.1 3.2-1.5 0-1.9-1-3.9-1-2 0-2.5 1-4 1-1.7 0-3-1.6-4-3-2.8-4-3.1-8.7-1.4-11.2 1.2-1.8 3.1-2.9 4.9-2.9 1.8 0 3 1 4.5 1 1.5 0 2.4-1 4.5-1 1.6 0 3.3.9 4.5 2.4-4 2.2-3.3 7.9.5 9.8z'/%3E%3C/svg%3E")}
.store-btn.light .store-ic{background:var(--ink)}
.store-ic.play{-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='black' d='M3 2.3l11.2 9.7L3 21.7c-.4-.2-.6-.6-.6-1V3.3c0-.4.2-.8.6-1zM16 10.2l3.3 1.8c.9.5.9 1.5 0 2l-3.3 1.8-2.5-2.6 2.5-3zM4.2 1.7L15 8l-2.4 2.5L4.2 1.7zm0 20.6l8.4-8.8L15 16l-10.8 6.3z'/%3E%3C/svg%3E")}
.hero-trust{list-style:none;display:flex;gap:26px;padding:0;margin:0;flex-wrap:wrap}
.hero-trust li{font-size:.86rem;color:var(--muted)}
.hero-trust strong{display:block;font-size:1.15rem;color:var(--ink);font-family:var(--fd)}
/* ===== phone ===== */
.hero-phone{position:relative;display:flex;justify-content:center}
.phone{position:relative;width:300px;height:608px;border-radius:42px;background:#0b1714;
padding:11px;box-shadow:var(--sh-lg),inset 0 0 0 2px rgba(255,255,255,.06);animation:float 6s ease-in-out infinite}
@keyframes float{50%{transform:translateY(-12px)}}
.phone-notch{position:absolute;top:11px;left:50%;transform:translateX(-50%);width:120px;height:24px;background:#0b1714;border-radius:0 0 16px 16px;z-index:5}
.phone-screen{height:100%;background:linear-gradient(180deg,#f6fffb,#ffffff);border-radius:32px;overflow:hidden;display:flex;flex-direction:column;position:relative}
.app-top{display:flex;align-items:center;gap:10px;padding:20px 16px 6px}
.app-x{background:none;border:0;color:var(--muted);font-size:1rem;cursor:pointer;font-weight:700}
.app-progress{flex:1;height:12px;background:#e6efeb;border-radius:999px;overflow:hidden}
.app-progress span{display:block;height:100%;background:var(--grad);border-radius:999px;transition:width .4s ease}
.app-hearts{display:flex;gap:3px;color:var(--danger);font-size:.95rem}
.app-hearts span.dead{color:#dfe6e3}
.lesson{flex:1;padding:14px 18px 4px;display:flex;flex-direction:column}
.lesson-q{font-family:var(--fd);font-weight:700;font-size:1.18rem;margin-bottom:14px}
.lesson-prompt{display:flex;align-items:center;gap:10px;background:#fff;border:1.5px solid var(--line);border-radius:16px;padding:12px 13px;margin-bottom:18px}
.speaker{width:34px;height:34px;flex:0 0 34px;display:grid;place-items:center;background:var(--brand-50);border-radius:10px;cursor:pointer;font-size:1rem;transition:transform .15s}
.speaker:active{transform:scale(.9)}
.speaker.ping{animation:ping .5s ease}
@keyframes ping{0%,100%{box-shadow:0 0 0 0 rgba(16,185,129,.4)}50%{box-shadow:0 0 0 7px rgba(16,185,129,0)}}
.prompt-text{font-weight:600;color:var(--ink)}
.lesson-answer{min-height:50px;border-bottom:2px dashed var(--line);display:flex;flex-wrap:wrap;gap:7px;align-content:flex-start;padding-bottom:10px;margin-bottom:16px}
.lesson-bank{display:flex;flex-wrap:wrap;gap:8px}
.chip{font-family:var(--fs);font-weight:600;font-size:.9rem;background:#fff;border:1.5px solid var(--line);
border-bottom-width:3px;border-radius:12px;padding:8px 13px;cursor:pointer;color:var(--ink);transition:transform .12s,background .15s,opacity .15s}
.chip:hover{background:var(--soft)}
.chip:active{transform:translateY(1px)}
.chip.used{opacity:.32;pointer-events:none}
.chip.placed{border-color:var(--brand);background:var(--brand-50);color:var(--brand-d)}
.lesson-foot{padding:12px 18px 20px}
.check-btn{width:100%;border:0;border-radius:14px;padding:13px;font-family:var(--fs);font-weight:800;font-size:.98rem;
color:#fff;background:var(--grad);cursor:pointer;box-shadow:0 5px 0 rgba(11,141,114,.5);transition:.15s}
.check-btn:active{transform:translateY(3px);box-shadow:0 2px 0 rgba(11,141,114,.5)}
.check-btn:disabled{background:#dfe6e3;color:#9bb0a9;box-shadow:none;cursor:not-allowed}
.lesson-result{position:absolute;inset:auto 0 0 0;background:#ddfbe8;border-top:2px solid #9be7bd;
padding:20px 18px 22px;text-align:center;transform:translateY(100%);transition:transform .35s ease;border-radius:24px 24px 0 0}
.lesson-result.show{transform:translateY(0)}
.lesson-result.wrong{background:#ffe3e3;border-top-color:#f3a3a3}
.result-icon{width:48px;height:48px;margin:0 auto 6px;border-radius:50%;background:var(--ok);color:#fff;display:grid;place-items:center;font-size:1.4rem;font-weight:800}
.lesson-result.wrong .result-icon{background:var(--danger)}
.result-title{font-family:var(--fd);font-weight:800;font-size:1.2rem;color:var(--brand-d)}
.lesson-result.wrong .result-title{color:#c23b3b}
.result-sub{font-size:.85rem;color:var(--ink-2);margin:2px 0 12px}
.continue{box-shadow:0 5px 0 rgba(11,141,114,.5)}
.floaty{position:absolute;display:flex;align-items:center;gap:10px;background:#fff;border:1px solid var(--line);
box-shadow:var(--sh-md);padding:11px 15px;border-radius:18px;font-size:.84rem;color:var(--ink-2);animation:float 5s ease-in-out infinite}
.floaty strong{font-family:var(--fd);font-size:1.15rem;color:var(--ink)}
.fl-ic{font-size:1.5rem}
.floaty-streak{top:42px;left:-8px;animation-delay:.6s}
.floaty-xp{bottom:64px;right:-14px;animation-delay:1.4s}
/* ===== sections ===== */
.section{padding:84px 0}
.section-soft{background:var(--grad-soft)}
.sec-head{text-align:center;max-width:640px;margin:0 auto 46px}
.kicker{display:inline-block;font-weight:700;font-size:.8rem;letter-spacing:.06em;text-transform:uppercase;color:var(--brand-d);
background:var(--brand-50);padding:5px 12px;border-radius:999px;margin-bottom:14px}
.sec-head h2{font-size:clamp(1.7rem,3.6vw,2.5rem)}
.sec-head p{color:var(--ink-2);margin-top:12px;font-size:1.05rem}
/* features */
.feat-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}
.feat-card{background:#fff;border:1px solid var(--line);border-radius:var(--r-lg);padding:26px 24px;
box-shadow:var(--sh-sm);transition:transform .2s,box-shadow .2s,border-color .2s}
.feat-card:hover{transform:translateY(-5px);box-shadow:var(--sh-md);border-color:rgba(16,185,129,.3)}
.feat-ic{width:54px;height:54px;border-radius:16px;display:grid;place-items:center;font-size:1.55rem;margin-bottom:16px;
background:color-mix(in srgb,var(--c) 14%,#fff);box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--c) 25%,transparent)}
.feat-card h3{font-size:1.18rem;margin-bottom:8px}
.feat-card p{color:var(--ink-2);font-size:.95rem}
.feat-tag{display:inline-block;margin-top:14px;font-size:.74rem;font-weight:700;color:var(--brand-d);
background:var(--brand-50);padding:4px 11px;border-radius:999px}
/* languages */
.lang-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:16px}
.lang-card{background:#fff;border:1px solid var(--line);border-radius:var(--r-md);padding:18px 16px;cursor:pointer;
display:flex;flex-direction:column;gap:6px;transition:transform .18s,box-shadow .18s,border-color .18s;text-align:left}
.lang-card:hover{transform:translateY(-4px);box-shadow:var(--sh-md);border-color:rgba(14,165,164,.35)}
.lang-top{display:flex;align-items:center;justify-content:space-between}
.lang-flag{font-size:1.9rem;line-height:1}
.lang-pill{font-size:.66rem;font-weight:700;padding:3px 8px;border-radius:999px}
.pill-new{background:#fff4dc;color:#b97c0a}
.pill-pop{background:var(--brand-50);color:var(--brand-d)}
.lang-name{font-family:var(--fd);font-weight:700;font-size:1.05rem}
.lang-learners{font-size:.78rem;color:var(--muted)}
.lang-bar{height:6px;border-radius:999px;background:#eaf3ef;overflow:hidden;margin-top:4px}
.lang-bar span{display:block;height:100%;background:var(--grad);border-radius:999px;width:0;transition:width 1s ease}
/* stats + reviews */
.stats-band{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;background:var(--grad);color:#fff;
border-radius:var(--r-xl);padding:34px 24px;box-shadow:var(--sh-md);text-align:center}
.stat strong{display:block;font-family:var(--fd);font-size:2rem;font-weight:800}
.stat span{font-size:.86rem;opacity:.92}
.reviews{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}
.review{margin:0;background:#fff;border:1px solid var(--line);border-radius:var(--r-lg);padding:24px;box-shadow:var(--sh-sm);transition:transform .2s,box-shadow .2s}
.review:hover{transform:translateY(-4px);box-shadow:var(--sh-md)}
.stars{color:var(--amber);letter-spacing:2px;margin-bottom:10px}
.review blockquote{margin:0;font-size:1rem;color:var(--ink);line-height:1.55}
.review figcaption{display:flex;align-items:center;gap:10px;margin-top:16px;font-weight:600;font-size:.88rem;color:var(--ink-2)}
.av{width:38px;height:38px;border-radius:50%;display:grid;place-items:center;color:#fff;font-weight:700;font-size:.82rem;font-family:var(--fd)}
.av-1{background:linear-gradient(135deg,#10b981,#0891b2)}
.av-2{background:linear-gradient(135deg,#f59e0b,#ef6f3c)}
.av-3{background:linear-gradient(135deg,#8b5cf6,#0ea5a4)}
/* pricing */
.bill-toggle{display:inline-flex;background:#fff;border:1px solid var(--line);border-radius:999px;padding:5px;margin:0 auto 36px;gap:4px;display:flex;width:max-content}
.bill-opt{border:0;background:none;font-family:var(--fs);font-weight:700;font-size:.92rem;color:var(--ink-2);padding:9px 20px;border-radius:999px;cursor:pointer;transition:.2s}
.bill-opt.is-active{background:var(--grad);color:#fff;box-shadow:0 5px 14px rgba(13,155,110,.3)}
.save{font-size:.72rem;background:rgba(255,255,255,.25);padding:2px 7px;border-radius:999px;margin-left:5px}
.bill-opt:not(.is-active) .save{background:#fff4dc;color:#b97c0a}
.price-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:22px;align-items:stretch}
.price-card{background:#fff;border:1px solid var(--line);border-radius:var(--r-lg);padding:30px 26px;display:flex;flex-direction:column;position:relative;box-shadow:var(--sh-sm);transition:transform .2s}
.price-card:hover{transform:translateY(-4px)}
.price-card.featured{border:2px solid var(--brand);box-shadow:var(--sh-md);transform:scale(1.03)}
.price-card.featured:hover{transform:scale(1.03) translateY(-4px)}
.best{position:absolute;top:-13px;left:50%;transform:translateX(-50%);background:var(--accent);color:#fff;font-size:.72rem;font-weight:700;padding:5px 14px;border-radius:999px;box-shadow:0 4px 10px rgba(245,158,11,.4)}
.price-card h3{font-size:1.3rem;margin-bottom:10px}
.price{display:flex;align-items:baseline;gap:3px;margin-bottom:4px}
.amt{font-family:var(--fd);font-size:2.4rem;font-weight:800}
.per{color:var(--muted);font-weight:600}
.price-note{color:var(--muted);font-size:.85rem;margin-bottom:18px}
.price-feats{list-style:none;padding:0;margin:0 0 22px;display:flex;flex-direction:column;gap:11px;flex:1}
.price-feats li{position:relative;padding-left:26px;font-size:.92rem;color:var(--ink-2)}
.price-feats li::before{content:"✓";position:absolute;left:0;top:0;color:var(--ok);font-weight:800}
.price-feats li.off{color:var(--muted)}
.price-feats li.off::before{content:"–";color:var(--muted)}
/* final cta */
.cta-final{background:var(--grad);color:#fff;padding:80px 0;text-align:center;position:relative;overflow:hidden}
.cta-final::before{content:"";position:absolute;inset:0;background:radial-gradient(600px 300px at 20% 0%,rgba(255,255,255,.18),transparent 60%);pointer-events:none}
.cta-inner h2{font-size:clamp(1.8rem,4vw,2.7rem)}
.cta-inner p{margin:12px 0 26px;font-size:1.1rem;opacity:.94}
/* footer */
.footer{background:#08130f;color:#cfe1da;padding:56px 0 26px}
.foot-grid{display:grid;grid-template-columns:1.6fr 1fr 1fr 1fr;gap:30px}
.foot-brand .brand-name{color:#fff;background:none;-webkit-text-fill-color:#fff}
.foot-brand p{margin-top:12px;color:#8fa79e;font-size:.9rem;max-width:30ch}
.foot-col h4{font-family:var(--fs);font-size:.82rem;text-transform:uppercase;letter-spacing:.06em;color:#7e988e;margin-bottom:14px}
.foot-col a{display:block;color:#cfe1da;font-size:.92rem;padding:5px 0;transition:color .15s}
.foot-col a:hover{color:#5ee0a8}
.foot-bottom{display:flex;justify-content:space-between;align-items:center;margin-top:40px;padding-top:20px;border-top:1px solid rgba(255,255,255,.08);font-size:.84rem;color:#7e988e;flex-wrap:wrap;gap:12px}
.foot-social{display:flex;gap:10px}
.foot-social a{width:34px;height:34px;border-radius:10px;display:grid;place-items:center;background:rgba(255,255,255,.06);color:#cfe1da;transition:.2s}
.foot-social a:hover{background:#10b981;color:#fff}
/* toast */
.toast{position:fixed;left:50%;bottom:26px;transform:transl(-50%,140%) translateX(-50%);
transform:translate(-50%,140%);background:var(--ink);color:#fff;font-weight:600;font-size:.92rem;
padding:13px 20px;border-radius:14px;box-shadow:var(--sh-lg);z-index:300;opacity:0;transition:transform .35s,opacity .35s;pointer-events:none}
.toast.show{transform:translate(-50%,0);opacity:1}
/* reveal */
.reveal{opacity:0;transform:translateY(22px);transition:opacity .6s ease,transform .6s ease}
.reveal.in{opacity:1;transform:none}
/* ===== responsive ===== */
@media (max-width:920px){
.nav-links,.nav-cta{display:none}
.nav-toggle{display:flex}
.hero-grid{grid-template-columns:1fr;gap:40px;text-align:center}
.hero-copy .eyebrow,.hero-actions{justify-content:center}
.hero-copy{display:flex;flex-direction:column;align-items:center}
.lede,.hero-trust{justify-content:center}
.feat-grid,.reviews,.price-grid{grid-template-columns:repeat(2,1fr)}
.lang-grid{grid-template-columns:repeat(3,1fr)}
.foot-grid{grid-template-columns:1fr 1fr}
.price-card.featured{transform:none}
.price-card.featured:hover{transform:translateY(-4px)}
}
@media (max-width:560px){
.section{padding:60px 0}
.feat-grid,.reviews,.price-grid{grid-template-columns:1fr}
.lang-grid{grid-template-columns:repeat(2,1fr)}
.stats-band{grid-template-columns:repeat(2,1fr);gap:22px 14px}
.foot-grid{grid-template-columns:1fr 1fr}
.floaty-streak{left:-2px;top:20px}
.floaty-xp{right:-4px;bottom:48px}
.hero{padding:34px 0 56px}
}
@media (max-width:380px){
.phone{width:280px;height:572px}
.foot-bottom{flex-direction:column;align-items:flex-start}
}
@media (prefers-reduced-motion:reduce){
*{animation:none!important;transition-duration:.01ms!important}
.reveal{opacity:1;transform:none}
}(function () {
"use strict";
/* ---------- toast helper ---------- */
var toastEl = document.getElementById("toast");
var toastTimer;
function toast(msg) {
if (!toastEl) return;
toastEl.textContent = msg;
toastEl.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("show");
}, 2400);
}
/* ---------- sticky nav shadow ---------- */
var nav = document.getElementById("nav");
function onScroll() {
if (nav) nav.classList.toggle("scrolled", window.scrollY > 8);
}
window.addEventListener("scroll", onScroll, { passive: true });
onScroll();
/* ---------- mobile menu ---------- */
var toggle = document.getElementById("navToggle");
var menu = document.getElementById("mobileMenu");
if (toggle && menu) {
toggle.addEventListener("click", function () {
var open = toggle.getAttribute("aria-expanded") === "true";
toggle.setAttribute("aria-expanded", String(!open));
menu.hidden = open;
});
menu.addEventListener("click", function (e) {
if (e.target.closest("a")) {
toggle.setAttribute("aria-expanded", "false");
menu.hidden = true;
}
});
}
/* ---------- scroll reveal ---------- */
var reveals = document.querySelectorAll(".reveal");
if ("IntersectionObserver" in window) {
var io = new IntersectionObserver(
function (entries) {
entries.forEach(function (en, i) {
if (en.isIntersecting) {
var el = en.target;
setTimeout(function () {
el.classList.add("in");
}, Math.min(i * 50, 200));
io.unobserve(el);
}
});
},
{ threshold: 0.12, rootMargin: "0px 0px -40px 0px" }
);
reveals.forEach(function (el) {
io.observe(el);
});
} else {
reveals.forEach(function (el) {
el.classList.add("in");
});
}
/* ---------- languages grid ---------- */
var LANGS = [
{ flag: "🇪🇸", name: "Spanish", learners: "5.8M", pct: 92, tag: "pop" },
{ flag: "🇫🇷", name: "French", learners: "4.1M", pct: 84, tag: "pop" },
{ flag: "🇯🇵", name: "Japanese", learners: "3.7M", pct: 78, tag: "pop" },
{ flag: "🇩🇪", name: "German", learners: "2.9M", pct: 71 },
{ flag: "🇮🇹", name: "Italian", learners: "2.2M", pct: 66 },
{ flag: "🇰🇷", name: "Korean", learners: "3.3M", pct: 81, tag: "pop" },
{ flag: "🇧🇷", name: "Portuguese", learners: "1.6M", pct: 58 },
{ flag: "🇨🇳", name: "Mandarin", learners: "2.4M", pct: 63 },
{ flag: "🇳🇱", name: "Dutch", learners: "640K", pct: 44 },
{ flag: "🇸🇪", name: "Swedish", learners: "390K", pct: 38, tag: "new" },
{ flag: "🇸🇦", name: "Arabic", learners: "1.1M", pct: 52 },
{ flag: "🇰🇪", name: "Swahili", learners: "210K", pct: 31, tag: "new" }
];
var langGrid = document.getElementById("langGrid");
if (langGrid) {
var frag = document.createDocumentFragment();
LANGS.forEach(function (l) {
var card = document.createElement("button");
card.className = "lang-card reveal";
card.type = "button";
var pill = l.tag === "new"
? '<span class="lang-pill pill-new">New</span>'
: l.tag === "pop"
? '<span class="lang-pill pill-pop">Popular</span>'
: "";
card.innerHTML =
'<div class="lang-top"><span class="lang-flag">' + l.flag + "</span>" + pill + "</div>" +
'<span class="lang-name">' + l.name + "</span>" +
'<span class="lang-learners">' + l.learners + " learners</span>" +
'<div class="lang-bar"><span data-pct="' + l.pct + '"></span></div>';
card.addEventListener("click", function () {
toast("Starting your free " + l.name + " course 🦉");
});
frag.appendChild(card);
});
langGrid.appendChild(frag);
// animate bars when grid enters view
if ("IntersectionObserver" in window) {
var barObs = new IntersectionObserver(
function (entries, obs) {
entries.forEach(function (en) {
if (en.isIntersecting) {
langGrid.querySelectorAll(".lang-bar span").forEach(function (b) {
b.style.width = b.getAttribute("data-pct") + "%";
});
obs.disconnect();
}
});
},
{ threshold: 0.3 }
);
barObs.observe(langGrid);
} else {
langGrid.querySelectorAll(".lang-bar span").forEach(function (b) {
b.style.width = b.getAttribute("data-pct") + "%";
});
}
}
/* ---------- pricing toggle ---------- */
var PRICES = {
monthly: {
super: ["$9.99", "/mo", "Billed monthly"],
fam: ["$16.99", "/mo", "Up to 6 accounts"]
},
yearly: {
super: ["$5.99", "/mo", "Billed $71.88 yearly"],
fam: ["$9.99", "/mo", "Billed $119.88 yearly"]
}
};
var billOpts = document.querySelectorAll(".bill-opt");
function setPeriod(period) {
var p = PRICES[period];
var set = function (id, vals) {
var amt = document.getElementById("price" + id);
var per = document.getElementById("per" + id);
var note = document.getElementById("note" + id);
if (amt) amt.textContent = vals[0];
if (per) per.textContent = vals[1];
if (note) note.textContent = vals[2];
};
set("Super", p.super);
set("Fam", p.fam);
}
billOpts.forEach(function (opt) {
opt.addEventListener("click", function () {
billOpts.forEach(function (o) {
o.classList.remove("is-active");
});
opt.classList.add("is-active");
setPeriod(opt.getAttribute("data-period"));
});
});
/* ---------- store + plan CTAs ---------- */
document.querySelectorAll(".store-btn").forEach(function (b) {
b.addEventListener("click", function (e) {
e.preventDefault();
toast("App download is just a demo 🙂");
});
});
document.querySelectorAll(".price-card .btn").forEach(function (b) {
b.addEventListener("click", function (e) {
e.preventDefault();
var plan = b.closest(".price-card").querySelector("h3").textContent;
toast("You picked the " + plan + " plan!");
});
});
/* ---------- count-up streak/xp floaties ---------- */
function countUp(el, target, suffix) {
if (!el) return;
var start = 0;
var dur = 1100;
var t0 = null;
function step(ts) {
if (!t0) t0 = ts;
var prog = Math.min((ts - t0) / dur, 1);
var eased = 1 - Math.pow(1 - prog, 3);
var val = Math.round(eased * target);
el.textContent = val.toLocaleString() + (suffix || "");
if (prog < 1) requestAnimationFrame(step);
}
requestAnimationFrame(step);
}
countUp(document.getElementById("streakNum"), 137);
countUp(document.getElementById("xpNum"), 2480);
/* ---------- interactive phone lesson demo ---------- */
var BANK = ["The", "cat", "dog", "drinks", "eats", "milk", "water"];
var CORRECT = ["The", "cat", "drinks", "milk"];
var bankEl = document.getElementById("wordBank");
var answerEl = document.getElementById("answerSlots");
var checkBtn = document.getElementById("checkBtn");
var resultEl = document.getElementById("lessonResult");
var continueBtn = document.getElementById("continueBtn");
var lessonBar = document.getElementById("lessonBar");
var heartsEl = document.getElementById("appHearts");
var speaker = document.getElementById("speaker");
var answer = [];
function shuffle(arr) {
var a = arr.slice();
for (var i = a.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var tmp = a[i]; a[i] = a[j]; a[j] = tmp;
}
return a;
}
function renderBank() {
if (!bankEl) return;
bankEl.innerHTML = "";
shuffle(BANK).forEach(function (w) {
var chip = document.createElement("button");
chip.className = "chip";
chip.type = "button";
chip.textContent = w;
chip.addEventListener("click", function () {
if (chip.classList.contains("used")) return;
chip.classList.add("used");
addWord(w, chip);
});
bankEl.appendChild(chip);
});
}
function addWord(word, sourceChip) {
answer.push({ word: word, source: sourceChip });
var placed = document.createElement("button");
placed.className = "chip placed";
placed.type = "button";
placed.textContent = word;
placed.addEventListener("click", function () {
// remove from answer, restore bank chip
var idx = answer.findIndex(function (a) {
return a.placed === placed;
});
if (idx > -1) {
answer[idx].source.classList.remove("used");
answer.splice(idx, 1);
}
placed.remove();
updateCheck();
});
answer[answer.length - 1].placed = placed;
answerEl.appendChild(placed);
updateCheck();
}
function updateCheck() {
if (checkBtn) checkBtn.disabled = answer.length === 0;
}
function loseHeart() {
if (!heartsEl) return;
var alive = heartsEl.querySelectorAll("span:not(.dead)");
if (alive.length) alive[alive.length - 1].classList.add("dead");
}
function resetLesson() {
answer = [];
if (answerEl) answerEl.innerHTML = "";
if (resultEl) {
resultEl.classList.remove("show", "wrong");
resultEl.hidden = true;
}
if (lessonBar) lessonBar.style.width = "20%";
renderBank();
updateCheck();
}
if (checkBtn) {
checkBtn.addEventListener("click", function () {
var attempt = answer.map(function (a) { return a.word; });
var correct =
attempt.length === CORRECT.length &&
attempt.every(function (w, i) { return w === CORRECT[i]; });
resultEl.hidden = false;
// force reflow so transition runs
void resultEl.offsetWidth;
var icon = resultEl.querySelector(".result-icon");
var title = resultEl.querySelector(".result-title");
var sub = resultEl.querySelector(".result-sub");
if (correct) {
resultEl.classList.remove("wrong");
resultEl.classList.add("show");
icon.textContent = "✓";
title.textContent = "Nicely done!";
sub.textContent = "+12 XP · streak extended 🔥";
if (lessonBar) lessonBar.style.width = "40%";
toast("Correct! +12 XP earned ⚡");
} else {
resultEl.classList.add("wrong", "show");
icon.textContent = "✕";
title.textContent = "Not quite!";
sub.textContent = 'Answer: "The cat drinks milk"';
loseHeart();
toast("Oops — try arranging the words again");
}
});
}
if (continueBtn) {
continueBtn.addEventListener("click", resetLesson);
}
if (speaker) {
speaker.addEventListener("click", function () {
speaker.classList.remove("ping");
void speaker.offsetWidth;
speaker.classList.add("ping");
toast("🔊 El gato bebe leche");
});
}
renderBank();
updateCheck();
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Linguara — Learn a language in 5 minutes a day</title>
<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=Inter:wght@400;500;600;700;800&family=Baloo+2:wght@600;700;800&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<a class="skip-link" href="#main">Skip to content</a>
<!-- ===== NAV ===== -->
<header class="nav" id="nav">
<div class="wrap nav-inner">
<a class="brand" href="#" aria-label="Linguara home">
<span class="brand-mark" aria-hidden="true">🦉</span>
<span class="brand-name">Linguara</span>
</a>
<nav class="nav-links" aria-label="Primary">
<a href="#features">Features</a>
<a href="#languages">Languages</a>
<a href="#proof">Reviews</a>
<a href="#pricing">Pricing</a>
</nav>
<div class="nav-cta">
<a href="#pricing" class="btn btn-ghost">Log in</a>
<a href="#pricing" class="btn btn-primary">Get started free</a>
</div>
<button class="nav-toggle" id="navToggle" aria-label="Open menu" aria-expanded="false" aria-controls="mobileMenu">
<span></span><span></span><span></span>
</button>
</div>
<div class="mobile-menu" id="mobileMenu" hidden>
<a href="#features">Features</a>
<a href="#languages">Languages</a>
<a href="#proof">Reviews</a>
<a href="#pricing">Pricing</a>
<a href="#pricing" class="btn btn-primary">Get started free</a>
</div>
</header>
<main id="main">
<!-- ===== HERO ===== -->
<section class="hero">
<div class="wrap hero-grid">
<div class="hero-copy reveal">
<span class="eyebrow">🔥 Over 4.2M learners on a streak today</span>
<h1>Learn a language in just <span class="hl">5 minutes</span> a day.</h1>
<p class="lede">Bite-size lessons, playful challenges, and a friendly owl that
never lets your streak die. Free forever — upgrade only when you love it.</p>
<div class="hero-actions">
<a href="#pricing" class="store-btn" aria-label="Download on the App Store">
<span class="store-ic"></span>
<span><small>Download on the</small><strong>App Store</strong></span>
</a>
<a href="#pricing" class="store-btn" aria-label="Get it on Google Play">
<span class="store-ic play"></span>
<span><small>Get it on</small><strong>Google Play</strong></span>
</a>
</div>
<ul class="hero-trust">
<li><strong>4.9★</strong> 320k ratings</li>
<li><strong>40+</strong> languages</li>
<li><strong>#1</strong> Education</li>
</ul>
</div>
<!-- PHONE MOCKUP -->
<div class="hero-phone reveal">
<div class="phone" role="img" aria-label="Animated lesson screen inside a phone">
<div class="phone-notch" aria-hidden="true"></div>
<div class="phone-screen">
<div class="app-top">
<button class="app-x" aria-label="Close lesson">✕</button>
<div class="app-progress"><span id="lessonBar" style="width:20%"></span></div>
<div class="app-hearts" id="appHearts" aria-label="Lives remaining">
<span>♥</span><span>♥</span><span>♥</span>
</div>
</div>
<div class="lesson">
<p class="lesson-q">Translate this sentence</p>
<div class="lesson-prompt">
<span class="speaker" id="speaker" aria-label="Play audio">🔊</span>
<span class="prompt-text" id="promptText">El gato bebe leche</span>
</div>
<div class="lesson-answer" id="answerSlots" aria-live="polite"></div>
<div class="lesson-bank" id="wordBank"></div>
</div>
<div class="lesson-foot">
<button class="check-btn" id="checkBtn" disabled>Check</button>
</div>
<div class="lesson-result" id="lessonResult" hidden>
<div class="result-icon">✓</div>
<p class="result-title">Nicely done!</p>
<p class="result-sub">+12 XP · streak extended 🔥</p>
<button class="check-btn continue" id="continueBtn">Continue</button>
</div>
</div>
</div>
<div class="floaty floaty-streak" aria-hidden="true">
<span class="fl-ic">🔥</span>
<span><strong id="streakNum">137</strong><br><small>day streak</small></span>
</div>
<div class="floaty floaty-xp" aria-hidden="true">
<span class="fl-ic">⚡</span>
<span><strong id="xpNum">2,480</strong><br><small>XP this week</small></span>
</div>
</div>
</div>
</section>
<!-- ===== FEATURES ===== -->
<section class="section" id="features">
<div class="wrap">
<div class="sec-head reveal">
<span class="kicker">Why it sticks</span>
<h2>Designed to make learning feel like a game</h2>
<p>Science-backed lessons wrapped in streaks, XP and friendly competition.</p>
</div>
<div class="feat-grid">
<article class="feat-card reveal">
<div class="feat-ic" style="--c:#10b981">🔥</div>
<h3>Streaks that motivate</h3>
<p>Practice daily and watch your flame grow. Streak Freeze has your back on busy days.</p>
<span class="feat-tag">Habit-building</span>
</article>
<article class="feat-card reveal">
<div class="feat-ic" style="--c:#0ea5a4">⚡</div>
<h3>Earn XP & level up</h3>
<p>Every lesson rewards XP. Climb weekly leagues and unlock crowns as you progress.</p>
<span class="feat-tag">Gamified</span>
</article>
<article class="feat-card reveal">
<div class="feat-ic" style="--c:#22c55e">⏱️</div>
<h3>Bite-size lessons</h3>
<p>Just five focused minutes. Perfect for the bus, the queue, or your morning coffee.</p>
<span class="feat-tag">5-min sessions</span>
</article>
<article class="feat-card reveal">
<div class="feat-ic" style="--c:#14b8a6">🗣️</div>
<h3>Speak with confidence</h3>
<p>Speech-recognition drills and real conversations build pronunciation that lands.</p>
<span class="feat-tag">Listen & speak</span>
</article>
<article class="feat-card reveal">
<div class="feat-ic" style="--c:#16a34a">🧠</div>
<h3>Smart review</h3>
<p>Our spaced-repetition engine resurfaces words right before you forget them.</p>
<span class="feat-tag">Adaptive</span>
</article>
<article class="feat-card reveal">
<div class="feat-ic" style="--c:#06b6d4">🏆</div>
<h3>Friendly leagues</h3>
<p>Compete with learners in your division and finish the week in the promotion zone.</p>
<span class="feat-tag">Social</span>
</article>
</div>
</div>
</section>
<!-- ===== LANGUAGES ===== -->
<section class="section section-soft" id="languages">
<div class="wrap">
<div class="sec-head reveal">
<span class="kicker">Pick your path</span>
<h2>40+ languages, one delightful app</h2>
<p>From Spanish to Swahili — start any language for free, switch any time.</p>
</div>
<div class="lang-grid" id="langGrid">
<!-- injected by JS -->
</div>
</div>
</section>
<!-- ===== SOCIAL PROOF ===== -->
<section class="section" id="proof">
<div class="wrap">
<div class="stats-band reveal">
<div class="stat"><strong>14M+</strong><span>active learners</span></div>
<div class="stat"><strong>2.1B</strong><span>lessons completed</span></div>
<div class="stat"><strong>4.9★</strong><span>average rating</span></div>
<div class="stat"><strong>94%</strong><span>stick past week one</span></div>
</div>
<div class="sec-head reveal" style="margin-top:56px">
<span class="kicker">Loved by learners</span>
<h2>Real progress, real reviews</h2>
</div>
<div class="reviews">
<figure class="review reveal">
<div class="stars" aria-label="5 out of 5 stars">★★★★★</div>
<blockquote>I ordered coffee in Lisbon entirely in Portuguese on day 60. The streak
genuinely got me out of bed early.</blockquote>
<figcaption><span class="av av-1">MR</span> Mara Ribeiro · 61-day streak</figcaption>
</figure>
<figure class="review reveal">
<div class="stars" aria-label="5 out of 5 stars">★★★★★</div>
<blockquote>Five minutes on the train each way and I'm reading Japanese menus.
The little owl is weirdly motivating.</blockquote>
<figcaption><span class="av av-2">TC</span> Theo Caldwell · 128-day streak</figcaption>
</figure>
<figure class="review reveal">
<div class="stars" aria-label="5 out of 5 stars">★★★★★</div>
<blockquote>My partner and I do a nightly French league battle. Learning together
made it actually fun to keep up.</blockquote>
<figcaption><span class="av av-3">SN</span> Sofia Nakamura · 340-day streak</figcaption>
</figure>
</div>
</div>
</section>
<!-- ===== PRICING ===== -->
<section class="section section-soft" id="pricing">
<div class="wrap">
<div class="sec-head reveal">
<span class="kicker">Simple pricing</span>
<h2>Start free. Go Super when you're hooked.</h2>
<p>Cancel anytime. Family plan covers up to 6 learners.</p>
</div>
<div class="bill-toggle reveal" role="group" aria-label="Billing period">
<button class="bill-opt is-active" data-period="monthly">Monthly</button>
<button class="bill-opt" data-period="yearly">Yearly <span class="save">−40%</span></button>
</div>
<div class="price-grid">
<article class="price-card reveal">
<h3>Free</h3>
<p class="price"><span class="amt">$0</span></p>
<p class="price-note">Forever. No card needed.</p>
<ul class="price-feats">
<li>All core lessons</li>
<li>Streaks & daily goals</li>
<li>Weekly leagues</li>
<li class="off">Lessons include ads</li>
<li class="off">5 hearts per day</li>
</ul>
<a href="#" class="btn btn-soft block">Get started</a>
</article>
<article class="price-card featured reveal">
<span class="best">Most popular</span>
<h3>Super</h3>
<p class="price"><span class="amt" id="priceSuper">$9.99</span><span class="per" id="perSuper">/mo</span></p>
<p class="price-note" id="noteSuper">Billed monthly</p>
<ul class="price-feats">
<li>Everything in Free</li>
<li>No ads, ever</li>
<li>Unlimited hearts</li>
<li>Unlimited Streak Freeze</li>
<li>Personalized review path</li>
</ul>
<a href="#" class="btn btn-primary block">Try 14 days free</a>
</article>
<article class="price-card reveal">
<h3>Family</h3>
<p class="price"><span class="amt" id="priceFam">$16.99</span><span class="per" id="perFam">/mo</span></p>
<p class="price-note" id="noteFam">Up to 6 accounts</p>
<ul class="price-feats">
<li>All Super benefits</li>
<li>Up to 6 learners</li>
<li>Shared family streak</li>
<li>Parent progress view</li>
<li>Best value per person</li>
</ul>
<a href="#" class="btn btn-soft block">Choose Family</a>
</article>
</div>
</div>
</section>
<!-- ===== FINAL CTA ===== -->
<section class="cta-final">
<div class="wrap cta-inner reveal">
<h2>Your next language is 5 minutes away.</h2>
<p>Join millions learning the fun way. Free to start, today.</p>
<div class="hero-actions center">
<a href="#" class="store-btn light"><span class="store-ic"></span><span><small>Download on the</small><strong>App Store</strong></span></a>
<a href="#" class="store-btn light"><span class="store-ic play"></span><span><small>Get it on</small><strong>Google Play</strong></span></a>
</div>
</div>
</section>
</main>
<!-- ===== FOOTER ===== -->
<footer class="footer">
<div class="wrap foot-grid">
<div class="foot-brand">
<a class="brand" href="#"><span class="brand-mark">🦉</span><span class="brand-name">Linguara</span></a>
<p>Learn a language in 5 minutes a day. Made with curiosity (and a lot of owls).</p>
</div>
<div class="foot-col">
<h4>Learn</h4>
<a href="#languages">Spanish</a><a href="#languages">French</a>
<a href="#languages">Japanese</a><a href="#languages">All languages</a>
</div>
<div class="foot-col">
<h4>Company</h4>
<a href="#">About</a><a href="#">Careers</a><a href="#">Press</a><a href="#">Blog</a>
</div>
<div class="foot-col">
<h4>Support</h4>
<a href="#">Help center</a><a href="#">Privacy</a><a href="#">Terms</a><a href="#">Contact</a>
</div>
</div>
<div class="wrap foot-bottom">
<span>© 2026 Linguara Labs. Fictional product for demo purposes.</span>
<div class="foot-social"><a href="#" aria-label="Twitter">𝕏</a><a href="#" aria-label="Instagram">◎</a><a href="#" aria-label="YouTube">▶</a></div>
</div>
</footer>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Language App Landing
A full marketing page for a fictional language-learning app, Linguara. The hero pairs app-store call-to-actions with a floating phone mockup that runs a genuinely interactive lesson: tap words from the bank to assemble “The cat drinks milk,” hit Check, and a result sheet slides up with a green “Nicely done!” plus XP — or a red miss that costs a heart and reveals the correct sentence. The Continue button reshuffles the word bank so the demo can be replayed endlessly.
Around the hero sit the supporting sections: gamified feature cards (streaks, XP, bite-size sessions, speaking practice, smart review, leagues), a 12-language grid whose progress bars animate into view, a stats band with social-proof reviews, and a three-tier pricing block. A monthly/yearly toggle rewrites the Super and Family prices in place, the streak and weekly-XP floaties count up on load, and every primary action fires a small toast. The layout reflows from a desktop two-column hero down to a single mobile-first column at ~360px, with a hamburger menu, sticky-nav shadow, scroll-reveal animations, and reduced-motion support.
Illustrative UI only — fictional courses, not a real learning platform.