Run details
- Model
- elorian/glimpse
- Route
- OpenRouter API, one user message
- Served by
- Together
- Reasoning
- provider default, 16,208 reasoning tokens
- Limits
- 60 min · 200,000 output tokens ($0.60 at $3/M)
- Date
- 22 Oct 2026
- Time
- 2 min 54 s
- Size
- 59 kB
- Tokens
- 294 in, 37,168 out
- List price
- $0.11 ($0.6 in / $3 out per M)
- Paid
- $0.11
Exact prompt
Build the landing page for Lumen Cycles, a Copenhagen workshop that builds custom cargo bikes to order. Make it look like a real brand you would trust with 4,000 euros. It must be one complete, self-contained HTML file: inline all CSS and JavaScript, and make no network requests of any kind (no CDNs, web fonts or remote images). Reply with the file in a single ```html code block.
Source
59 kB 982 lines Raw
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lumen Cycles — Hand-built cargo bikes, Copenhagen</title>
<meta name="description" content="A twelve-person workshop in Nørrebro building hand-welded cargo bikes to order. Numbered, made for your street.">
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Ccircle cx='16' cy='16' r='13' fill='none' stroke='%2315211E' stroke-width='2.5'/%3E%3Ccircle cx='16' cy='16' r='5' fill='%23FFB43C'/%3E%3C/svg%3E">
<style>
:root{
--paper:#F4EFE5; --paper-2:#ECE6D9; --ink:#15211E; --ink-2:#0C1513;
--amber:#FFB43C; --amber-deep:#E08A00; --sage:#6E8B7E;
--line:rgba(21,33,30,.16);
--display:"Futura","Century Gothic","Avenir Next","Trebuchet MS",Verdana,sans-serif;
--body:"Iowan Old Style","Palatino Linotype",Palatino,Georgia,"Times New Roman",serif;
--mono:ui-monospace,"SF Mono","Segoe UI Mono","Roboto Mono",Consolas,"Courier New",monospace;
}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
body{margin:0;background:var(--paper);color:var(--ink);font:18px/1.65 var(--body);-webkit-font-smoothing:antialiased}
h1,h2,h3,h4{font-family:var(--display);font-weight:700;line-height:1.04;letter-spacing:.01em;text-transform:uppercase;margin:0}
p{margin:0 0 1em}
a{color:inherit}
img,svg{display:block}
::selection{background:var(--amber);color:var(--ink)}
:focus-visible{outline:2px solid var(--amber-deep);outline-offset:2px}
.wrap{max-width:1220px;margin:0 auto;padding:0 28px}
section{scroll-margin-top:90px}
/* progress */
#progress{position:fixed;top:0;left:0;height:3px;background:var(--amber);width:0;z-index:1000}
/* top bar */
.topbar{background:var(--ink-2);color:var(--paper-2);font:500 11.5px/1.4 var(--mono);letter-spacing:.14em;text-transform:uppercase;padding:9px 0;text-align:center}
.topbar .dot{display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--amber);margin-right:8px;animation:blink 1.6s ease-in-out infinite;vertical-align:middle}
@keyframes blink{0%,100%{opacity:1}50%{opacity:.25}}
/* nav */
.nav{position:sticky;top:0;z-index:900;background:rgba(244,239,229,.92);backdrop-filter:blur(8px);border-bottom:1px solid transparent;transition:border-color .3s,box-shadow .3s}
.nav.scrolled{border-color:var(--line);box-shadow:0 6px 24px rgba(21,33,30,.06)}
.nav .inner{max-width:1220px;margin:0 auto;padding:18px 28px;display:flex;align-items:center;justify-content:space-between;gap:24px;transition:padding .3s}
.nav.scrolled .inner{padding:12px 28px}
.brand{display:flex;align-items:center;gap:11px;text-decoration:none}
.brand svg{width:34px;height:34px;flex:none}
.brand .wm{font-family:var(--display);font-weight:700;font-size:17px;letter-spacing:.22em;text-transform:uppercase;line-height:1}
.brand .wm small{display:block;font:500 9px/1.6 var(--mono);letter-spacing:.32em;color:var(--sage)}
.menu{display:flex;align-items:center;gap:30px;list-style:none;margin:0;padding:0}
.menu a{font:600 12px/1 var(--display);letter-spacing:.16em;text-transform:uppercase;text-decoration:none;position:relative;padding:4px 0}
.menu a::after{content:"";position:absolute;left:0;bottom:0;height:2px;width:0;background:var(--amber);transition:width .3s}
.menu a:hover::after{width:100%}
.burger{display:none;background:none;border:1px solid var(--line);width:44px;height:40px;cursor:pointer;border-radius:2px;padding:0}
.burger span{display:block;width:20px;height:2px;background:var(--ink);margin:4px auto;transition:transform .3s}
.skip{position:absolute;left:-999px}
.skip:focus{left:8px;top:8px;z-index:1100;background:var(--amber);padding:8px 12px}
/* buttons */
.btn{display:inline-flex;align-items:center;gap:.6em;font:700 12.5px/1 var(--display);letter-spacing:.15em;text-transform:uppercase;padding:17px 28px;border:1.5px solid var(--ink);background:var(--ink);color:var(--paper);text-decoration:none;cursor:pointer;border-radius:2px;transition:background .25s,color .25s,border-color .25s}
.btn:hover{background:var(--amber);color:var(--ink);border-color:var(--amber)}
.btn .ar{transition:transform .3s}
.btn:hover .ar{transform:translateX(5px)}
.btn-ghost{background:transparent;color:var(--ink)}
.btn-ghost:hover{background:var(--ink);color:var(--paper)}
.btn-amber{background:var(--amber);border-color:var(--amber);color:var(--ink)}
.btn-amber:hover{background:var(--ink);border-color:var(--ink);color:var(--paper)}
.kicker{font:500 11px/1.4 var(--mono);letter-spacing:.24em;text-transform:uppercase;color:var(--sage);display:inline-flex;align-items:center;gap:10px}
.kicker::before{content:"";width:26px;height:1px;background:currentColor}
.kicker.light{color:var(--amber)}
/* reveal + mask */
.reveal{opacity:0;transform:translateY(26px);transition:opacity .8s cubic-bezier(.16,1,.3,1),transform .8s cubic-bezier(.16,1,.3,1)}
.reveal.in{opacity:1;transform:none}
.mask .line{display:block;overflow:hidden;padding:.06em 0}
.mask .line>span{display:inline-block;transform:translateY(115%);transition:transform 1s cubic-bezier(.16,1,.3,1) var(--d,0s)}
.mask.in .line>span{transform:none}
/* hero */
.hero{padding:72px 0 90px;position:relative}
.hero .grid{display:grid;grid-template-columns:1.02fr .98fr;gap:60px;align-items:center}
.hero h1{font-size:clamp(42px,6.2vw,86px);margin:26px 0 28px}
.hero h1 em{font-style:normal;color:var(--amber-deep)}
.hero .lede{max-width:46ch;font-size:19px;color:#3a4643}
.hero .cta-row{display:flex;flex-wrap:wrap;gap:14px;margin:34px 0 44px}
.hero-stats{display:flex;gap:38px;flex-wrap:wrap;border-top:1px solid var(--line);padding-top:26px}
.hero-stats div span{display:block;font-family:var(--display);font-weight:700;font-size:34px;line-height:1}
.hero-stats div small{font:500 10.5px/1.5 var(--mono);letter-spacing:.18em;text-transform:uppercase;color:var(--sage)}
.hero-art{border-radius:3px;overflow:hidden;border:1px solid var(--line);box-shadow:0 30px 70px -30px rgba(21,33,30,.35)}
.hero-art svg{width:100%;height:auto}
/* ticker */
.ticker{border-top:1px solid var(--line);border-bottom:1px solid var(--line);overflow:hidden;background:var(--paper-2)}
.ticker .track{display:flex;width:max-content;animation:mq 36s linear infinite;padding:15px 0}
.ticker:hover .track{animation-play-state:paused}
.ticker .track span{font:500 11.5px/1 var(--mono);letter-spacing:.2em;text-transform:uppercase;padding:0 30px;white-space:nowrap}
.ticker .track span::after{content:"✳";color:var(--amber-deep);margin-left:60px}
@keyframes mq{to{transform:translateX(-50%)}}
/* section head */
.sec-head{margin:0 0 56px}
.sec-head h2{font-size:clamp(30px,4.4vw,58px);margin-top:18px}
.pad{padding:110px 0}
/* models */
.mrow{display:grid;grid-template-columns:1fr 1fr;gap:56px;align-items:center;padding:64px 0;border-top:1px solid var(--line)}
.mrow.rev .m-art{order:2}
.m-art{background:var(--paper-2);border:1px solid var(--line);border-radius:2px;padding:26px;position:relative;transition:transform .4s,box-shadow .4s}
.m-art:hover{transform:translateY(-4px);box-shadow:0 20px 40px -24px rgba(21,33,30,.3)}
.m-art svg{width:100%;height:auto}
.m-art .tag{position:absolute;top:14px;left:14px;font:500 10px/1 var(--mono);letter-spacing:.2em;text-transform:uppercase;color:var(--sage)}
.m-body h3{font-size:clamp(28px,3.4vw,44px)}
.m-body .sub{font:500 11px/1 var(--mono);letter-spacing:.22em;text-transform:uppercase;color:var(--sage);margin:12px 0 20px;display:block}
.m-body .desc{color:#3a4643;max-width:44ch}
.spec-grid{display:grid;grid-template-columns:1fr 1fr;gap:0;margin:28px 0;border-top:1px solid var(--line)}
.spec-grid div{border-bottom:1px solid var(--line);padding:13px 0}
.spec-grid div:nth-child(odd){border-right:1px solid var(--line);padding-right:18px}
.spec-grid div:nth-child(even){padding-left:18px}
.spec-grid dt{font:500 10px/1.4 var(--mono);letter-spacing:.18em;text-transform:uppercase;color:var(--sage);margin:0}
.spec-grid dd{margin:4px 0 0;font-family:var(--display);font-weight:700;font-size:17px;text-transform:uppercase;letter-spacing:.04em}
.m-foot{display:flex;align-items:center;gap:26px;flex-wrap:wrap;margin-top:26px}
.m-price{font-family:var(--mono);font-weight:700;font-size:22px;letter-spacing:.04em}
.m-price small{display:block;font:500 9.5px/1.6 var(--mono);letter-spacing:.22em;text-transform:uppercase;color:var(--sage)}
/* build / configurator */
.build{background:var(--ink);color:var(--paper);position:relative;overflow:hidden}
.build::before{content:"";position:absolute;inset:0;background-image:radial-gradient(rgba(255,180,60,.07) 1px,transparent 1.5px);background-size:26px 26px;pointer-events:none}
.build .build-grid{display:grid;grid-template-columns:1.05fr .95fr;gap:60px;padding:110px 0}
.preview-top{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:14px}
.serial{font:500 10.5px/1 var(--mono);letter-spacing:.2em;color:var(--amber)}
.pv-bike{background:var(--ink-2);border:1px solid rgba(244,239,229,.14);border-radius:2px;padding:34px;transition:transform .35s cubic-bezier(.34,1.56,.64,1)}
.pv-bike.pulse{transform:scale(1.015)}
.pv-bike svg{width:100%;height:auto}
.pv-meta{font:500 10.5px/1.8 var(--mono);letter-spacing:.16em;text-transform:uppercase;color:var(--sage);margin-top:14px}
.hl{animation:hl 2.2s ease-in-out infinite;transform-box:fill-box;transform-origin:center}
@keyframes hl{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.45;transform:scale(.8)}}
.options-col h2{font-size:clamp(30px,3.6vw,48px);margin:18px 0 34px;color:var(--paper)}
.ogroup{margin-bottom:34px}
.ogroup>label,.ogroup>.glab{display:block;font:600 11px/1 var(--mono);letter-spacing:.24em;text-transform:uppercase;color:var(--amber);margin-bottom:14px}
.model-btns{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.mbtn{background:transparent;border:1px solid rgba(244,239,229,.2);color:var(--paper);text-align:left;padding:15px 16px;cursor:pointer;border-radius:2px;font-family:var(--body);transition:border-color .25s,background .25s}
.mbtn:hover{border-color:var(--amber)}
.mbtn.active{border-color:var(--amber);background:rgba(255,180,60,.08)}
.mbtn .name{display:block;font-family:var(--display);font-weight:700;font-size:15px;letter-spacing:.08em;text-transform:uppercase}
.mbtn .sub{display:block;font:500 10px/1.6 var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--sage)}
.mbtn .price{display:block;font-family:var(--mono);font-size:13px;color:var(--amber);margin-top:4px}
.swatches{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}
.swatch{display:flex;align-items:center;gap:10px;border:1px solid rgba(244,239,229,.16);padding:10px 12px;cursor:pointer;border-radius:2px;transition:border-color .25s}
.swatch:hover,.swatch:has(input:checked){border-color:var(--amber)}
.swatch input{position:absolute;opacity:0;pointer-events:none}
.swatch .chip{width:24px;height:24px;border-radius:50%;border:1.5px solid rgba(244,239,229,.35);flex:none}
.swatch .nm{font:500 10.5px/1.3 var(--mono);letter-spacing:.06em;text-transform:uppercase}
.swatch .pr{font:500 10px/1 var(--mono);color:var(--sage);margin-left:auto}
.acc-list{display:grid;gap:8px}
.acc{display:flex;align-items:center;gap:12px;border:1px solid rgba(244,239,229,.14);padding:13px 14px;cursor:pointer;border-radius:2px;font:500 12.5px/1.3 var(--body);transition:border-color .25s}
.acc:hover,.acc:has(input:checked){border-color:var(--amber)}
.acc input{position:absolute;opacity:0;pointer-events:none}
.acc .box{width:17px;height:17px;border:1.5px solid rgba(244,239,229,.4);border-radius:2px;flex:none;position:relative}
.acc input:checked+.box{background:var(--amber);border-color:var(--amber)}
.acc input:checked+.box::after{content:"";position:absolute;left:5px;top:1.5px;width:5px;height:10px;border:solid var(--ink);border-width:0 2.5px 2.5px 0;transform:rotate(42deg)}
.acc .pr{font-family:var(--mono);font-size:13px;margin-left:auto;color:var(--amber)}
.acc[hidden]{display:none}
.summary{border:1px solid var(--amber);border-radius:2px;padding:26px;margin-top:8px;background:rgba(255,180,60,.05)}
.summary ul{list-style:none;margin:0 0 18px;padding:0;font:500 12px/2 var(--mono);letter-spacing:.03em}
.summary ul li{display:flex;justify-content:space-between;border-bottom:1px dashed rgba(244,239,229,.15);padding:3px 0}
.summary ul li span:last-child{color:var(--amber)}
.total-row{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:6px}
.total-row span{font:600 11px/1 var(--mono);letter-spacing:.24em;text-transform:uppercase;color:var(--sage)}
.total-row strong{font-family:var(--display);font-size:38px;color:var(--amber);letter-spacing:.02em}
.ship{font:500 10.5px/1.8 var(--mono);letter-spacing:.12em;text-transform:uppercase;color:var(--sage);margin:0 0 20px}
.confirm{border:1px solid var(--amber);padding:34px;text-align:center}
.confirm h3{color:var(--amber);font-size:22px;margin-bottom:12px}
/* process */
.process .grid{display:grid;grid-template-columns:.8fr 1.2fr;gap:70px;align-items:start}
.process .left{position:sticky;top:110px}
.process .left h2{font-size:clamp(30px,4vw,52px);margin:18px 0 20px}
.process .left p{color:#3a4643;max-width:32ch}
.step{display:grid;grid-template-columns:76px 1fr;gap:24px;border-top:1px solid var(--line);padding:34px 0}
.step .no{font-family:var(--display);font-weight:700;font-size:30px;color:var(--amber-deep)}
.step h3{font-size:20px;letter-spacing:.06em;margin-bottom:10px}
.step p{color:#3a4643;margin:0}
/* stats band */
.stats{background:var(--ink-2);color:var(--paper);padding:88px 0}
.stats .grid{display:grid;grid-template-columns:repeat(4,1fr);gap:32px}
.stats .grid>div{border-left:1px solid rgba(244,239,229,.16);padding-left:26px}
.stats .num{font-family:var(--display);font-weight:700;font-size:clamp(38px,4.4vw,56px);color:var(--amber);line-height:1}
.stats small{display:block;font:500 10.5px/1.7 var(--mono);letter-spacing:.2em;text-transform:uppercase;color:var(--sage);margin-top:8px}
/* journal */
.jgrid{display:grid;grid-template-columns:1.35fr 1fr;gap:34px}
.post{border:1px solid var(--line);border-radius:2px;background:var(--paper);overflow:hidden;transition:transform .35s,box-shadow .35s;display:flex;flex-direction:column}
.post:hover{transform:translateY(-5px);box-shadow:0 24px 44px -26px rgba(21,33,30,.3)}
.post .thumb{background:var(--paper-2);border-bottom:1px solid var(--line);display:flex;align-items:center;justify-content:center}
.post .thumb svg{width:100%;height:auto}
.post .body{padding:24px 26px 28px}
.post h3{font-size:clamp(19px,2vw,26px);margin:12px 0 10px;letter-spacing:.03em}
.post p{color:#3a4643;font-size:15.5px}
.post .meta{font:500 10px/1 var(--mono);letter-spacing:.18em;text-transform:uppercase;color:var(--sage);display:flex;gap:18px;margin-top:14px}
.jcol{display:grid;gap:34px;align-content:start}
.jcol .post .body{padding:20px 22px 24px}
.jcol .post .thumb{min-height:120px}
/* voices */
.voices{background:var(--paper-2)}
.vgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:34px}
.vgrid blockquote{margin:0;background:var(--paper);border:1px solid var(--line);padding:36px 32px;border-radius:2px}
.vgrid blockquote:nth-child(2){transform:translateY(34px)}
.qm{font-family:var(--display);font-size:56px;color:var(--amber-deep);line-height:1;display:block;margin-bottom:10px}
.vgrid p{font-size:16.5px;color:#33413d}
.vgrid footer{font:500 11px/1.7 var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--sage);margin-top:18px}
/* visit */
.visit .grid{display:grid;grid-template-columns:1fr 1fr;gap:60px;align-items:stretch}
.visit-info dl{margin:26px 0;display:grid;grid-template-columns:auto 1fr;gap:6px 26px}
.visit-info dt{font:500 10.5px/2.4 var(--mono);letter-spacing:.2em;text-transform:uppercase;color:var(--sage)}
.visit-info dd{margin:0;font-family:var(--display);font-weight:700;letter-spacing:.04em;text-transform:uppercase;font-size:14px}
.map-panel{border:1px solid var(--line);border-radius:2px;background:var(--paper-2);padding:0;overflow:hidden}
.map-panel svg{width:100%;height:auto}
.pulse-dot{animation:pd 2s ease-out infinite}
@keyframes pd{0%{opacity:.6;transform:scale(.4)}70%{opacity:0;transform:scale(2.6)}100%{opacity:0}}
/* faq */
.faq-list{max-width:860px;border-top:1px solid var(--line)}
.faq-list details{border-bottom:1px solid var(--line)}
.faq-list summary{display:flex;justify-content:space-between;align-items:center;gap:20px;cursor:pointer;padding:26px 0;list-style:none;font-family:var(--display);font-weight:700;font-size:clamp(16px,1.8vw,20px);letter-spacing:.04em;text-transform:uppercase}
.faq-list summary::-webkit-details-marker{display:none}
.faq-list .plus{width:20px;height:20px;flex:none;position:relative;transition:transform .3s}
.faq-list .plus::before,.faq-list .plus::after{content:"";position:absolute;background:var(--amber-deep)}
.faq-list .plus::before{left:0;top:9px;width:20px;height:2px}
.faq-list .plus::after{top:0;left:9px;width:2px;height:20px}
.faq-list details[open] .plus{transform:rotate(45deg)}
.faq-list .ans{padding:0 0 26px;color:#3a4643;max-width:66ch}
/* cta band */
.ctaband{background:var(--ink);color:var(--paper);padding:100px 0;text-align:center}
.ctaband h2{font-size:clamp(30px,4.4vw,56px);margin:18px 0 14px}
.ctaband p{color:var(--sage);font-size:15px}
.cta-form{display:flex;gap:12px;justify-content:center;flex-wrap:wrap;margin-top:34px}
.cta-form input{border:1.5px solid rgba(244,239,229,.25);background:transparent;color:var(--paper);font:500 13px var(--mono);padding:16px 20px;border-radius:2px;min-width:280px}
.cta-form input::placeholder{color:rgba(244,239,229,.4)}
.cta-form input:focus{outline:none;border-color:var(--amber)}
.form-msg{font:500 12px/1.8 var(--mono);letter-spacing:.1em;color:var(--amber);margin-top:22px;min-height:1.5em}
/* footer */
footer.site{background:var(--ink-2);color:rgba(244,239,229,.75);padding:80px 0 30px}
.fgrid{display:grid;grid-template-columns:1.4fr 1fr 1fr 1fr;gap:50px;padding-bottom:60px;border-bottom:1px solid rgba(244,239,229,.12)}
.fgrid h4{color:var(--paper);font-size:12px;letter-spacing:.2em;margin-bottom:18px}
.fgrid ul{list-style:none;margin:0;padding:0}
.fgrid li{margin-bottom:10px;font-size:15px}
.fgrid a{text-decoration:none;transition:color .2s}
.fgrid a:hover{color:var(--amber)}
.fgrid .blurb{font-size:15px;max-width:30ch;color:rgba(244,239,229,.6)}
.fbar{display:flex;justify-content:space-between;gap:20px;flex-wrap:wrap;padding-top:26px;font:500 10.5px/1.8 var(--mono);letter-spacing:.14em;text-transform:uppercase;color:rgba(244,239,229,.4)}
/* responsive */
@media(max-width:980px){
.hero .grid,.process .grid,.visit .grid,.build .build-grid,.jgrid{grid-template-columns:1fr}
.hero{padding-top:44px}
.process .left{position:static}
.stats .grid{grid-template-columns:1fr 1fr}
.vgrid{grid-template-columns:1fr}
.vgrid blockquote:nth-child(2){transform:none}
.fgrid{grid-template-columns:1fr 1fr}
.mrow{grid-template-columns:1fr;gap:30px}
.mrow.rev .m-art{order:0}
}
@media(max-width:760px){
.menu{display:none;position:absolute;top:100%;left:0;right:0;background:var(--paper);border-bottom:1px solid var(--line);flex-direction:column;padding:24px 28px;gap:18px;align-items:flex-start}
.menu.open{display:flex}
.burger{display:block}
.swatches{grid-template-columns:1fr 1fr}
.model-btns{grid-template-columns:1fr}
.pad{padding:74px 0}
}
@media(prefers-reduced-motion:reduce){
html{scroll-behavior:auto}
*,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}
.reveal{opacity:1;transform:none}
.mask .line>span{transform:none}
}
</style>
</head>
<body>
<div id="progress"></div>
<a class="skip" href="#build">Skip to the bike builder</a>
<div class="topbar"><span class="dot"></span>Currently booking builds for Q3 2025 — 6 slots left · Free delivery in Greater Copenhagen</div>
<header class="nav" id="nav">
<div class="inner">
<a class="brand" href="#top">
<svg viewBox="0 0 34 34" aria-hidden="true"><circle cx="17" cy="17" r="14.5" fill="none" stroke="#15211E" stroke-width="2"/><circle cx="17" cy="17" r="5.5" fill="#FFB43C"/><path d="M17 1.5v6M17 26.5v6M1.5 17h6M26.5 17h6" stroke="#15211E" stroke-width="2" stroke-linecap="round"/></svg>
<span class="wm">Lumen<small>Cycles · Copenhagen</small></span>
</a>
<button class="burger" id="burger" aria-label="Toggle menu" aria-expanded="false"><span></span><span></span><span></span></button>
<ul class="menu" id="menu">
<li><a href="#models">The Range</a></li>
<li><a href="#build">Build Yours</a></li>
<li><a href="#process">Process</a></li>
<li><a href="#visit">Visit</a></li>
<li><a href="#faq">Questions</a></li>
<li><a class="btn" style="padding:12px 18px" href="#build">Build yours <span class="ar">→</span></a></li>
</ul>
</div>
</header>
<main id="top">
<!-- HERO -->
<section class="hero">
<div class="wrap grid">
<div>
<span class="kicker">Nørrebro · Copenhagen · Est. 2014</span>
<h1 class="mask">
<span class="line"><span>Your city.</span></span>
<span class="line"><span style="--d:.1s">Your load.</span></span>
<span class="line"><span style="--d:.2s">Your <em>bike</em>.</span></span>
</h1>
<p class="lede">Lumen Cycles is a twelve-person workshop building hand-welded cargo bikes to order — one frame at a time, numbered, made for the streets you actually ride and the people you actually carry.</p>
<div class="cta-row">
<a class="btn" href="#build">Build yours <span class="ar">→</span></a>
<a class="btn btn-ghost" href="#visit">Book a test ride</a>
</div>
<div class="hero-stats">
<div><span>212</span><small>Bikes delivered</small></div>
<div><span>11</span><small>Years at the bench</small></div>
<div><span>10 yr</span><small>Frame warranty</small></div>
</div>
</div>
<div class="hero-art reveal">
<svg id="heroArt" viewBox="0 0 680 560" role="img" aria-label="A Lumen cargo bike at dusk on the Copenhagen quay, headlight glowing">
<defs>
<linearGradient id="sky" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#0E211D"/><stop offset=".55" stop-color="#17332C"/><stop offset="1" stop-color="#33473C"/>
</linearGradient>
<radialGradient id="sunGlow"><stop offset="0" stop-color="#FFB43C" stop-opacity=".55"/><stop offset="1" stop-color="#FFB43C" stop-opacity="0"/></radialGradient>
<linearGradient id="beam" x1="0" y1="0" x2="1" y2="0"><stop offset="0" stop-color="#FFB43C" stop-opacity=".5"/><stop offset="1" stop-color="#FFB43C" stop-opacity="0"/></linearGradient>
</defs>
<rect width="680" height="560" fill="url(#sky)"/>
<g id="sunG">
<circle cx="470" cy="300" r="110" fill="url(#sunGlow)"/>
<circle cx="470" cy="300" r="38" fill="#FFB43C"/>
</g>
<g fill="#0A1815">
<rect x="20" y="330" width="70" height="70"/><rect x="90" y="300" width="52" height="100"/><rect x="142" y="340" width="84" height="60"/><rect x="226" y="286" width="46" height="114"/><rect x="272" y="326" width="90" height="74"/><rect x="362" y="348" width="60" height="52"/>
</g>
<g fill="#FFB43C" opacity=".5">
<rect x="104" y="314" width="6" height="9"/><rect x="122" y="334" width="6" height="9"/><rect x="238" y="300" width="6" height="9"/><rect x="238" y="322" width="6" height="9"/><rect x="296" y="340" width="6" height="9"/><rect x="316" y="360" width="6" height="9"/><rect x="40" y="352" width="6" height="9"/>
</g>
<rect y="400" width="680" height="160" fill="#081613"/>
<line x1="0" y1="400" x2="680" y2="400" stroke="#FFB43C" stroke-opacity=".35" stroke-width="2"/>
<g fill="#FFB43C">
<rect x="430" y="418" width="80" height="4" opacity=".3"/><rect x="450" y="436" width="52" height="4" opacity=".22"/><rect x="466" y="454" width="34" height="4" opacity=".15"/><rect x="455" y="472" width="46" height="3" opacity=".1" class="shimmer"/>
</g>
<g id="bikeG">
<path d="M178 250 L520 150 L520 330 Z" fill="url(#beam)"/>
<g transform="translate(60,120)" fill="none" stroke="#04100D" stroke-width="9" stroke-linecap="round">
<path d="M225 200 L258 116"/><path d="M330 200 L256 122"/><path d="M225 200 L330 200"/><path d="M225 200 L178 122 L96 122"/><path d="M120 200 L102 124"/>
<path d="M102 124 C102 102 130 90 156 88 L170 88"/>
</g>
<rect x="115" y="236" width="142" height="66" rx="4" fill="#04100D" stroke="#FFB43C" stroke-width="1.5" stroke-opacity=".7"/>
<g transform="translate(60,120)" stroke="#04100D" fill="none">
<circle cx="120" cy="200" r="56" stroke-width="9"/><circle cx="330" cy="200" r="56" stroke-width="9"/>
<circle cx="120" cy="200" r="8" fill="#04100D"/><circle cx="330" cy="200" r="8" fill="#04100D"/>
<path d="M246 110 q16 -8 30 0" stroke-width="7" stroke-linecap="round"/>
</g>
<circle class="hl" cx="160" cy="242" r="7" fill="#FFB43C"/>
</g>
</svg>
</div>
</div>
</section>
<!-- TICKER -->
<div class="ticker" aria-hidden="true">
<div class="track">
<span>Hand-welded in Nørrebro</span><span>Every frame numbered</span><span>10-year frame warranty</span><span>96 builds per year</span><span>Test ride before you commit</span><span>Made for your street</span>
<span>Hand-welded in Nørrebro</span><span>Every frame numbered</span><span>10-year frame warranty</span><span>96 builds per year</span><span>Test ride before you commit</span><span>Made for your street</span>
</div>
</div>
<!-- MODELS -->
<section class="pad" id="models">
<div class="wrap">
<div class="sec-head">
<span class="kicker">The range</span>
<h2 class="mask"><span class="line"><span>Four frames.</span></span><span class="line"><span style="--d:.12s">Infinite streets.</span></span></h2>
</div>
<div class="mrow reveal">
<div class="m-art"><span class="tag">Front loader</span><div class="m-bike" data-model="fjord"></div></div>
<div class="m-body">
<h3>Fjord Mk II</h3>
<span class="sub">Long-john · The family one</span>
<p class="desc">Two kids up front, the weekly shop behind them, and still room for the bmx. The Fjord's low centre of gravity keeps it steady with a full box, and 74 cm of width still fits through the courtyard gate on Jagtgade.</p>
<dl class="spec-grid">
<div><dt>Payload</dt><dd>60 kg</dd></div>
<div><dt>Width</dt><dd>74 cm</dd></div>
<div><dt>Drive</dt><dd>Shimano E6100</dd></div>
<div><dt>Weight</dt><dd>32 kg</dd></div>
</dl>
<div class="m-foot">
<span class="m-price">€5,900<small>From, ex VAT</small></span>
<button class="btn choose" data-model="fjord">Start with this <span class="ar">→</span></button>
</div>
</div>
</div>
<div class="mrow rev reveal">
<div class="m-art"><span class="tag">Rear loader</span><div class="m-bike" data-model="havn"></div></div>
<div class="m-body">
<h3>Havn</h3>
<span class="sub">Long-tail · The workhorse</span>
<p class="desc">Flatbed out back for beer kegs, plant trays and flat-pack furniture. The Havn is the bike you hand to your intern on day one — steel platform, hydraulic disc brakes front and rear, Nuvinci N360 hub that never needs a thought.</p>
<dl class="spec-grid">
<div><dt>Payload</dt><dd>85 kg</dd></div>
<div><dt>Deck</dt><dd>60 × 40 cm</dd></div>
<div><dt>Brakes</dt><dd>Hydraulic discs</dd></div>
<div><dt>Weight</dt><dd>28 kg</dd></div>
</dl>
<div class="m-foot">
<span class="m-price">€4,400<small>From, ex VAT</small></span>
<button class="btn choose" data-model="havn">Start with this <span class="ar">→</span></button>
</div>
</div>
</div>
<div class="mrow reveal">
<div class="m-art"><span class="tag">Compact</span><div class="m-bike" data-model="sol"></div></div>
<div class="m-body">
<h3>Sol</h3>
<span class="sub">Compact · The city one</span>
<p class="desc">The tightest turning circle we make, built for Vesterbro courtyards and narrow stairwell corridors. Small enough to live indoors, strong enough to carry a 30 kg crate of apples from Torvehallerne home without a wobble.</p>
<dl class="spec-grid">
<div><dt>Payload</dt><dd>40 kg</dd></div>
<div><dt>Width</dt><dd>62 cm</dd></div>
<div><dt>Drive</dt><dd>Shimano E500</dd></div>
<div><dt>Weight</dt><dd>22 kg</dd></div>
</dl>
<div class="m-foot">
<span class="m-price">€3,900<small>From, ex VAT</small></span>
<button class="btn choose" data-model="sol">Start with this <span class="ar">→</span></button>
</div>
</div>
</div>
<div class="mrow rev reveal">
<div class="m-art"><span class="tag">Winter edition</span><div class="m-bike" data-model="vinter"></div></div>
<div class="m-body">
<h3>Vinter</h3>
<span class="sub">Winter edition · −15 °C tested</span>
<p class="desc">Insulated cargo box, heated grips, belt drive and full mudguards as standard. The Vinter has been riding the lakes from February to February for six winters straight. Kids in there stay warm, thermoses stay hot, and the frame stays dry.</p>
<dl class="spec-grid">
<div><dt>Box</dt><dd>Insulated</dd></div>
<div><dt>Grips</dt><dd>Heated</dd></div>
<div><dt>Drive</dt><dd>Carbon belt</dd></div>
<div><dt>Weight</dt><dd>34 kg</dd></div>
</dl>
<div class="m-foot">
<span class="m-price">€6,400<small>From, ex VAT</small></span>
<button class="btn choose" data-model="vinter">Start with this <span class="ar">→</span></button>
</div>
</div>
</div>
</div>
</section>
<!-- CONFIGURATOR -->
<section class="build" id="build">
<div class="wrap build-grid">
<div class="preview-col">
<div class="preview-top">
<span class="kicker light" id="pvModel">Fjord Mk II</span>
<span class="serial" id="pvSerial">LMN-25-FJ01</span>
</div>
<div class="pv-bike" id="pvBike"></div>
<p class="pv-meta"><span id="pvPaint">Signal Amber</span> · hand-built in Nørrebro · serial engraved on head tube · zinc-dipped + powder-coated</p>
</div>
<div class="options-col">
<span class="kicker light">Build yours</span>
<h2 class="mask"><span class="line"><span>Configure your Lumen</span></span></h2>
<div class="ogroup">
<span class="glab">01 · Choose a frame</span>
<div class="model-btns" id="modelBtns"></div>
</div>
<div class="ogroup">
<span class="glab">02 · Choose a paint</span>
<div class="swatches" id="swatches"></div>
</div>
<div class="ogroup">
<span class="glab">03 · Equip it</span>
<div class="acc-list" id="accList"></div>
</div>
<div id="summaryWrap"></div>
</div>
</div>
</section>
<!-- PROCESS -->
<section class="pad process" id="process">
<div class="wrap grid">
<div class="left">
<span class="kicker">How it works</span>
<h2 class="mask"><span class="line"><span>Five stages,</span></span><span class="line"><span style="--d:.1s">eight to fourteen weeks.</span></span></h2>
<p>No conveyor belt. Every bike passes through the same two pairs of hands from cut tube to final road test on the lakes.</p>
</div>
<div>
<div class="step reveal"><span class="no">01</span><div><h3>The conversation</h3><p>Ninety minutes at the workshop — or a video call if you're not in Copenhagen. We measure your street, your stairwell, your kids, and what you actually carry on a Friday.</p></div></div>
<div class="step reveal" style="transition-delay:.08s"><span class="no">02</span><div><h3>The frame</h3><p>Hand-cut and TIG-welded 4130 steel in Nørrebro. Each frame gets a number engraved into the head tube before it leaves the bench. Nothing is welded on a Sunday.</p></div></div>
<div class="step reveal" style="transition-delay:.16s"><span class="no">03</span><div><h3>The paint</h3><p>Zinc-dipped against salt, powder-coated in six standard colours or any RAL you like. Cure-tested at 200 °C, then checked under a loupe for dust and orange peel.</p></div></div>
<div class="step reveal" style="transition-delay:.24s"><span class="no">04</span><div><h3>The build</h3><p>Drivetrain, hydraulic brakes, lighting, box. A 12-point checklist and a loaded road test along the lakes — with 60 kg of sandbags where your kids will sit.</p></div></div>
<div class="step reveal" style="transition-delay:.32s"><span class="no">05</span><div><h3>Delivery</h3><p>Hand-delivered in Greater Copenhagen, or crated by freight for the rest of the EU. We adjust everything to you, then we ride home with you to make sure it's right.</p></div></div>
</div>
</div>
</section>
<!-- STATS -->
<section class="stats">
<div class="wrap grid">
<div class="reveal"><span class="num count" data-to="212">0</span><small>Bikes delivered since 2014</small></div>
<div class="reveal" style="transition-delay:.1s"><span class="num count" data-to="11">0</span><small>Winters at the bench</small></div>
<div class="reveal" style="transition-delay:.2s"><span class="num count" data-to="96">0</span><small>Builds per year, maximum</small></div>
<div class="reveal" style="transition-delay:.3s"><span class="num count" data-to="10">0</span><small>Year frame warranty</small></div>
</div>
</section>
<!-- JOURNAL -->
<section class="pad" id="journal">
<div class="wrap">
<div class="sec-head">
<span class="kicker">From the workshop</span>
<h2 class="mask"><span class="line"><span>The journal</span></span></h2>
</div>
<div class="jgrid">
<article class="post reveal">
<div class="thumb"><svg viewBox="0 0 640 300" aria-hidden="true"><rect width="640" height="300" fill="none"/><g stroke="#15211E" stroke-width="3" fill="none"><path d="M80 220 L260 120 L400 120"/><path d="M260 120 L280 220"/><path d="M400 120 L420 220"/><circle cx="450" cy="160" r="26"/><circle cx="450" cy="160" r="8" fill="#15211E"/></g><g stroke="#FFB43C" stroke-width="3" stroke-linecap="round"><path d="M450 118 L456 100 M436 130 L418 118 M464 130 L482 118"/><path d="M450 100 L450 86"/></g></svg></div>
<div class="body">
<span class="kicker" style="color:var(--amber-deep)">Craft</span>
<h3>Why we still weld by hand in 2025</h3>
<p>A robot can lay a weld faster. It can't feel when a joint is about to tell you a story in three winters. Our head of frames, Anders, has been welding these joints since the first Fjord — here's what he checks, and why he keeps a notebook of every mistake he's ever made.</p>
<div class="meta"><span>14 Mar 2025</span><span>7 min read</span></div>
</div>
</article>
<div class="jcol">
<article class="post reveal" style="transition-delay:.1s">
<div class="thumb"><svg viewBox="0 0 300 130" aria-hidden="true"><g stroke="#15211E" stroke-width="2.5" fill="none"><path d="M20 90 Q80 40 150 80 T280 70"/><path d="M20 105 Q80 60 150 95 T280 85" opacity=".4"/><path d="M60 30 v18 M51 39 h18" stroke="#FFB43C" stroke-width="3"/><path d="M230 24 v14 M223 31 h14" stroke="#FFB43C" stroke-width="3"/></g></svg></div>
<div class="body">
<span class="kicker" style="color:var(--amber-deep)">Field notes</span>
<h3>A winter on the lakes with the Vinter</h3>
<div class="meta"><span>02 Feb 2025</span><span>5 min</span></div>
</div>
</article>
<article class="post reveal" style="transition-delay:.2s">
<div class="thumb"><svg viewBox="0 0 300 130" aria-hidden="true"><g stroke="#15211E" stroke-width="2.5" fill="none"><circle cx="150" cy="65" r="40"/><circle cx="150" cy="65" r="14"/><path d="M150 25 v-12 M150 117 v-12"/></g><path d="M150 45 L168 65 L150 85" stroke="#FFB43C" stroke-width="3" fill="none"/></svg></div>
<div class="body">
<span class="kicker" style="color:var(--amber-deep)">Engineering</span>
<h3>How we test a 60 kg load at 35 km/h</h3>
<div class="meta"><span>19 Jan 2025</span><span>6 min</span></div>
</div>
</article>
</div>
</div>
</div>
</section>
<!-- VOICES -->
<section class="pad voices">
<div class="wrap">
<div class="sec-head">
<span class="kicker">Owners</span>
<h2 class="mask"><span class="line"><span>What riders say</span></span></h2>
</div>
<div class="vgrid">
<blockquote class="reveal">
<span class="qm">“</span>
<p>Our Fjord has carried everything from a full IKEA run to my mother's armchair. Two years, 11,000 km, and the only thing that's needed replacing is the brake pads. It feels like it was cut to fit our street — because it was.</p>
<footer>Mette & Jonas · Islands Brygge<br>Fjord Mk II, Copenhagen Green</footer>
</blockquote>
<blockquote class="reveal" style="transition-delay:.1s">
<span class="qm">“</span>
<p>We run three Havns for our café deliveries. The kids' armchair of bike shops would sell us cheaper ones in stock. Lumen took four months, asked about our routes, and the bikes have been faultless since. That's worth paying for.</p>
<footer>Anders Høj · Vesterbro<br>Havn ×3, Ink Black</footer>
</blockquote>
<blockquote class="reveal" style="transition-delay:.2s">
<span class="qm">“</span>
<p>I tested the Sol in the rain before ordering — forty-five minutes, no pressure, just coffee. My daughter now calls it “the apple bike”. It fits in our Vesterbro courtyard where nothing else would.</p>
<footer>Priya K. · Nørrebro<br>Sol, Signal Amber</footer>
</blockquote>
</div>
</div>
</section>
<!-- VISIT -->
<section class="pad visit" id="visit">
<div class="wrap grid">
<div class="visit-info">
<span class="kicker">The workshop</span>
<h2 class="mask" style="margin:18px 0 16px"><span class="line"><span>Visit us in Nørrebro</span></span></h2>
<p style="color:#3a4643;max-width:40ch">Come and see the next build on the jig, drink a coffee, and take a Fjord around the block. Test rides are 30 minutes and free — book ahead, the workshop is small and we like to give you the time.</p>
<dl>
<dt>Address</dt><dd>Jagtgade 24, 2200 København N</dd>
<dt>Open</dt><dd>Tue–Fri 09–18 · Sat 10–15</dd>
<dt>Phone</dt><dd>+45 31 44 09 12</dd>
<dt>Email</dt><dd>hello@lumencycles.dk</dd>
</dl>
<a class="btn" href="#build">Book a test ride <span class="ar">→</span></a>
</div>
<div class="map-panel reveal">
<svg viewBox="0 0 520 420" role="img" aria-label="Map of the Lumen Cycles workshop in Nørrebro, Copenhagen">
<rect width="520" height="420" fill="#ECE6D9"/>
<g stroke="#15211E" stroke-opacity=".14" stroke-width="1.5">
<path d="M0 60 H520 M0 130 H520 M0 200 H520 M0 270 H520 M0 340 H520"/>
<path d="M60 0 V420 M130 0 V420 M200 0 V420 M270 0 V420 M340 0 V420 M410 0 V420 M470 0 V420"/>
</g>
<path d="M-10 330 Q140 300 240 340 T520 300" stroke="#6E8B7E" stroke-opacity=".55" stroke-width="16" fill="none" stroke-linecap="round"/>
<path d="M40 0 Q100 140 90 420" stroke="#6E8B7E" stroke-opacity=".35" stroke-width="10" fill="none"/>
<text x="30" y="30" font-family="ui-monospace,monospace" font-size="11" letter-spacing="2" fill="#6E8B7E">THE LAKES</text>
<text x="396" y="392" font-family="ui-monospace,monospace" font-size="11" letter-spacing="2" fill="#6E8B7E">NØRREBRO</text>
<g transform="translate(270,200)">
<circle class="pulse-dot" r="14" fill="#FFB43C"/>
<circle r="9" fill="#15211E"/><circle r="3.5" fill="#FFB43C"/>
<rect x="18" y="-14" width="118" height="28" rx="2" fill="#15211E"/>
<text x="28" y="4" font-family="ui-monospace,monospace" font-size="10" letter-spacing="1.5" fill="#F4EFE5">JAGTGADE 24</text>
</g>
</svg>
</div>
</div>
</section>
<!-- FAQ -->
<section class="pad" id="faq">
<div class="wrap">
<div class="sec-head">
<span class="kicker">Questions</span>
<h2 class="mask"><span class="line"><span>Before you commit</span></span></h2>
</div>
<div class="faq-list">
<details><summary>How long is the wait?<span class="plus"></span></summary><p class="ans">Eight to fourteen weeks from deposit to delivery, depending on the season — winter is busy. We build a maximum of 96 bikes a year, so we publish the current queue on the workshop door and at the top of this page. You'll get a build date the day you order, and we tell you if it moves.</p></details>
<details><summary>What does the warranty cover?<span class="plus"></span></summary><p class="ans">Every frame carries a 10-year warranty against manufacturing defects, with the serial number on the head tube as proof. Moving parts, batteries and electronics carry 2 years. If something breaks because of how it was made, fixing it is on us — anywhere in the EU.</p></details>
<details><summary>Can I test ride before ordering?<span class="plus"></span></summary><p class="ans">Yes — that's why we exist in a workshop, not a warehouse. Book a free 30-minute test ride at Jagtgade 24. Bring your kids, your crate, your helmet. We also have the Fjord and Havn fitted with child benches so you can feel how they load.</p></details>
<details><summary>Do you deliver outside Copenhagen?<span class="plus"></span></summary><p class="ans">We hand-deliver in Greater Copenhagen at no charge. Elsewhere in the EU, your bike ships in a reusable wooden crate via DSV; we pre-assemble 90% of it, and a local dealer or our service partner finishes the last 10%. Crating and freight are quoted at order, typically €380–€620.</p></details>
<details><summary>Can I change the spec after I order?<span class="plus"></span></summary><p class="ans">Paint and accessories can be changed freely until the frame is welded — usually about three weeks in. After the weld, changes to geometry aren't possible (it's steel, not plastic), but we can always add accessories later at the workshop.</p></details>
<details><summary>Do you do company or fleet orders?<span class="plus"></span></summary><p class="ans">Yes, and VAT is deductible for business use. We've outfitted cafés, florists and two bike-share pilots. From three bikes you get fleet numbering, a service schedule and priority workshop slots. Talk to us on hello@lumencycles.dk.</p></details>
</div>
</div>
</section>
<!-- CTA -->
<section class="ctaband">
<div class="wrap">
<span class="kicker light" style="justify-content:center">Reserve</span>
<h2 class="mask"><span class="line"><span>Hold your build slot</span></span></h2>
<p>Leave your email and we'll hold one of the six remaining Q3 slots for 72 hours — no deposit, no commitment.</p>
<form class="cta-form" id="ctaForm">
<input type="email" required placeholder="your@email.dk" aria-label="Email address">
<button class="btn btn-amber" type="submit">Reserve a slot <span class="ar">→</span></button>
</form>
<p class="form-msg" id="ctaMsg"></p>
</div>
</section>
</main>
<footer class="site">
<div class="wrap">
<div class="fgrid">
<div>
<a class="brand" href="#top" style="margin-bottom:18px">
<svg viewBox="0 0 34 34" aria-hidden="true"><circle cx="17" cy="17" r="14.5" fill="none" stroke="#F4EFE5" stroke-width="2"/><circle cx="17" cy="17" r="5.5" fill="#FFB43C"/></svg>
<span class="wm" style="color:#F4EFE5">Lumen<small style="color:#6E8B7E">Cycles · Copenhagen</small></span>
</a>
<p class="blurb">Hand-built cargo bikes, made to order in Nørrebro since 2014. Numbered, zinc-dipped, tested on the lakes.</p>
</div>
<div><h4>Shop</h4><ul>
<li><a href="#models">The range</a></li>
<li><a href="#build">Build yours</a></li>
<li><a href="#visit">Test rides</a></li>
<li><a href="#faq">Fleet & company</a></li>
</ul></div>
<div><h4>Workshop</h4><ul>
<li><a href="#process">Our process</a></li>
<li><a href="#journal">Journal</a></li>
<li><a href="#visit">Visit Jagtgade 24</a></li>
<li><a href="#faq">Warranty</a></li>
</ul></div>
<div><h4>Monthly letter</h4>
<p style="font-size:14px;color:rgba(244,239,229,.6);margin-bottom:14px">One email a month: new builds, workshop notes, no noise.</p>
<form id="newsForm" style="display:flex;gap:8px;flex-wrap:wrap">
<input type="email" required placeholder="your@email.dk" aria-label="Email for newsletter" style="border:1.5px solid rgba(244,239,229,.25);background:transparent;color:#F4EFE5;font:500 12px var(--mono);padding:10px 14px;border-radius:2px;min-width:180px">
<button class="btn btn-amber" style="padding:10px 16px;font-size:11px" type="submit">Join</button>
</form>
<p class="form-msg" id="newsMsg" style="font-size:11px"></p>
</div>
</div>
<div class="fbar">
<span>© 2025 Lumen Cycles ApS · CVR 38 91 22 07</span>
<span>Jagtgade 24, 2200 København N · hello@lumencycles.dk</span>
<span>Privacy · Terms · Cookies (we don't)</span>
</div>
</div>
</footer>
<script>
(function(){
"use strict";
var reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
/* progress bar */
var bar = document.getElementById("progress");
function onScroll(){
var h = document.documentElement.scrollHeight - window.innerHeight;
bar.style.width = (h > 0 ? (window.scrollY / h) * 100 : 0) + "%";
document.getElementById("nav").classList.toggle("scrolled", window.scrollY > 40);
}
window.addEventListener("scroll", onScroll, {passive:true});
onScroll();
/* burger */
var burger = document.getElementById("burger"), menu = document.getElementById("menu");
burger.addEventListener("click", function(){
var open = menu.classList.toggle("open");
burger.setAttribute("aria-expanded", open);
});
menu.addEventListener("click", function(e){ if(e.target.tagName === "A") menu.classList.remove("open"); });
/* reveal + mask */
var io = new IntersectionObserver(function(es){
es.forEach(function(e){ if(e.isIntersecting){ e.target.classList.add("in"); io.unobserve(e.target); } });
}, {threshold:.15});
document.querySelectorAll(".reveal,.mask").forEach(function(el){ io.observe(el); });
/* counters */
var cio = new IntersectionObserver(function(es){
es.forEach(function(e){
if(!e.isIntersecting) return;
cio.unobserve(e.target);
var el = e.target, to = +el.dataset.to, t0 = performance.now(), dur = reduce ? 0 : 1200;
function tick(t){
var p = dur === 0 ? 1 : Math.min((t - t0) / dur, 1);
var ease = 1 - Math.pow(1 - p, 3);
el.textContent = Math.round(to * ease);
if(p < 1) requestAnimationFrame(tick);
}
requestAnimationFrame(tick);
});
}, {threshold:.5});
document.querySelectorAll(".count").forEach(function(el){ cio.observe(el); });
/* hero parallax */
if(!reduce && window.matchMedia("(pointer:fine)").matches){
var art = document.getElementById("heroArt"), sun = document.getElementById("sunG"), bg = document.getElementById("bikeG");
art.parentElement.addEventListener("pointermove", function(e){
var r = art.parentElement.getBoundingClientRect();
var x = (e.clientX - r.left) / r.width - .5, y = (e.clientY - r.top) / r.height - .5;
sun.setAttribute("transform", "translate(" + (x * -14) + " " + (y * -8) + ")");
bg.setAttribute("transform", "translate(" + (x * 10) + " " + (y * 6) + ")");
});
}
/* ---------- bike drawing ---------- */
var PAINTS = {
amber:{name:"Signal Amber", hex:"#FFB43C", hexD:"#FFB43C", add:120},
green:{name:"Copenhagen Green", hex:"#2F5D49", hexD:"#5C937B", add:0},
fog:{name:"Fog Grey", hex:"#9AA69E", hexD:"#9AA69E", add:0},
ink:{name:"Ink Black", hex:"#15211E", hexD:"#39524A", add:0},
milk:{name:"Milk", hex:"#EFE9DC", hexD:"#EFE9DC", add:0},
coral:{name:"Coral Red", hex:"#D9583B", hexD:"#D9583B", add:80}
};
var MODELS = {
fjord:{name:"Fjord Mk II", base:5900, tag:"Long-john · Family", code:"FJ"},
havn:{name:"Havn", base:4400, tag:"Long-tail · Utility", code:"HV"},
sol:{name:"Sol", base:3900, tag:"Compact · City", code:"SO"},
vinter:{name:"Vinter", base:6400, tag:"Winter edition", code:"VT"}
};
var ACC = {
canopy:{name:"Waterproof canopy", p:480, front:true},
bench:{name:"Child bench + harness", p:320, front:true},
winterbox:{name:"Insulated winter box", p:690, front:true},
battery:{name:"750 Wh battery upgrade", p:540},
tracker:{name:"GPS tracker + alarm", p:240}
};
function fmt(n){ return "€" + n.toLocaleString("en-US"); }
function wheel(cx, cy, r, col){
var sp = "";
for(var i = 0; i < 10; i++){
var a = i * Math.PI / 5;
sp += '<line x1="' + (cx + Math.cos(a) * 9).toFixed(1) + '" y1="' + (cy + Math.sin(a) * 9).toFixed(1) +
'" x2="' + (cx + Math.cos(a) * (r - 7)).toFixed(1) + '" y2="' + (cy + Math.sin(a) * (r - 7)).toFixed(1) +
'" stroke="' + col + '" stroke-width="1.6" opacity=".4"/>';
}
return '<g><circle cx="' + cx + '" cy="' + cy + '" r="' + r + '" fill="none" stroke="' + col + '" stroke-width="6.5"/>' +
sp + '<circle cx="' + cx + '" cy="' + cy + '" r="7" fill="' + col + '"/></g>';
}
function bikeSVG(o){
var P = PAINTS[o.paint];
var col = o.dark ? (P.hexD || P.hex) : P.hex;
var dk = o.dark ? "#E9E4D8" : "#15211E";
var soft = o.dark ? "rgba(233,228,216,.09)" : "rgba(21,33,30,.07)";
var m = o.model, acc = o.acc || {}, parts = [];
var front = m !== "havn";
parts.push('<ellipse cx="225" cy="266" rx="168" ry="10" fill="' + soft + '"/>');
if(!front){
parts.push('<rect x="238" y="126" width="130" height="12" rx="3" fill="' + col + '"/>');
parts.push('<path d="M244 126 v-16 h7 M362 126 v-16 h-7" fill="none" stroke="' + col + '" stroke-width="4.5" stroke-linecap="round"/>');
} else {
var bx = m === "sol" ? [72,132,96,52] : m === "vinter" ? [50,110,150,72] : [55,116,142,66];
var x = bx[0], y = bx[1], w = bx[2], h = bx[3];
parts.push('<rect x="' + x + '" y="' + y + '" width="' + w + '" height="' + h + '" rx="4" fill="' + col + '"/>');
var sl = "";
for(var sx = x + 15; sx < x + w - 8; sx += 16){
sl += '<line x1="' + sx + '" y1="' + (y + 8) + '" x2="' + sx + '" y2="' + (y + h - 8) + '" stroke="' + dk + '" stroke-width="1.4" opacity=".25"/>';
}
parts.push(sl);
if(m === "vinter" || acc.winterbox){
parts.push('<rect x="' + (x - 6) + '" y="' + (y - 17) + '" width="' + (w + 12) + '" height="14" rx="4" fill="' + col + '" stroke="' + dk + '" stroke-width="1.2" stroke-opacity=".3"/>');
}
if(acc.bench){
parts.push('<path d="M' + (x + 14) + ' ' + (y + h - 18) + ' H' + (x + w - 14) + ' M' + (x + 14) + ' ' + (y + h - 18) + ' V' + (y + 12) + '" fill="none" stroke="' + dk + '" stroke-width="3" opacity=".4"/>');
}
if(acc.canopy){
parts.push('<path d="M' + (x + 4) + ' ' + y + ' Q' + (x + w / 2) + ' ' + (y - 78) + ' ' + (x + w - 4) + ' ' + y + '" fill="none" stroke="' + dk + '" stroke-width="5" stroke-linecap="round"/>');
}
}
parts.push('<g fill="none" stroke="' + col + '" stroke-width="7" stroke-linecap="round">' +
'<path d="M225 200 L258 116"/><path d="M330 200 L256 122"/><path d="M225 200 L330 200"/>' +
'<path d="M225 200 L178 122 L96 122"/><path d="M120 200 L102 124"/></g>');
parts.push('<path d="M102 124 C102 102 130 90 156 88 L170 88 q12 0 14 10" fill="none" stroke="' + col + '" stroke-width="6" stroke-linecap="round"/>');
parts.push('<path d="M246 110 q16 -8 30 0" fill="none" stroke="' + dk + '" stroke-width="6" stroke-linecap="round"/>');
parts.push('<circle cx="225" cy="200" r="17" fill="none" stroke="' + dk + '" stroke-width="3" opacity=".65"/>');
parts.push('<path d="M225 200 L239 216" stroke="' + dk + '" stroke-width="4" stroke-linecap="round"/><rect x="236" y="216" width="14" height="5" rx="2" fill="' + dk + '"/>');
if(acc.battery){
parts.push('<rect x="191" y="142" width="16" height="46" rx="4" fill="' + dk + '"/><rect x="195" y="150" width="8" height="16" rx="2" fill="#FFB43C"/>');
}
if(acc.tracker){
parts.push('<rect x="248" y="120" width="22" height="13" rx="2" fill="' + dk + '"/><circle cx="254" cy="126.5" r="2.4" fill="#FFB43C"/>');
}
if(front){ parts.push('<circle class="hl" cx="100" cy="120" r="5.5" fill="#FFB43C"/>'); }
parts.push(wheel(120, 200, 56, dk));
parts.push(wheel(330, 200, 56, dk));
return '<svg viewBox="0 0 440 280" role="img" aria-label="Lumen ' + MODELS[m].name + ' cargo bike preview">' + parts.join("") + "</svg>";
}
/* model card illustrations */
document.querySelectorAll(".m-bike").forEach(function(el){
el.innerHTML = bikeSVG({model: el.dataset.model, paint: el.dataset.model === "vinter" ? "fog" : (el.dataset.model === "sol" ? "amber" : "green"), acc: {}});
});
/* ---------- configurator ---------- */
var state = {model:"fjord", paint:"amber", acc:{canopy:false, bench:false, winterbox:false, battery:false, tracker:false}};
var pvBike = document.getElementById("pvBike"), pvModel = document.getElementById("pvModel"),
pvPaint = document.getElementById("pvPaint"), pvSerial = document.getElementById("pvSerial");
var mb = document.getElementById("modelBtns");
Object.keys(MODELS).forEach(function(k){
var b = document.createElement("button");
b.type = "button"; b.className = "mbtn" + (k === state.model ? " active" : ""); b.dataset.model = k;
b.innerHTML = '<span class="name">' + MODELS[k].name + '</span><span class="sub">' + MODELS[k].tag + '</span><span class="price">' + fmt(MODELS[k].base) + "</span>";
b.addEventListener("click", function(){ setModel(k); });
mb.appendChild(b);
});
var sw = document.getElementById("swatches");
Object.keys(PAINTS).forEach(function(k, i){
var l = document.createElement("label"); l.className = "swatch";
l.innerHTML = '<input type="radio" name="paint" value="' + k + '"' + (k === state.paint ? " checked" : "") + '>' +
'<span class="chip" style="background:' + PAINTS[k].hex + '"></span>' +
'<span class="nm">' + PAINTS[k].name + '</span><span class="pr">' + (PAINTS[k].add ? "+" + fmt(PAINTS[k].add) : "incl.") + "</span>";
l.querySelector("input").addEventListener("change", function(){ state.paint = k; render(); });
sw.appendChild(l);
});
var al = document.getElementById("accList");
Object.keys(ACC).forEach(function(k){
var l = document.createElement("label"); l.className = "acc"; l.dataset.acc = k;
l.innerHTML = '<input type="checkbox" data-acc="' + k + '"><span class="box"></span>' +
ACC[k].name + '<span class="pr">' + fmt(ACC[k].p) + "</span>";
l.querySelector("input").addEventListener("change", function(e){ state.acc[k] = e.target.checked; render(); });
al.appendChild(l);
});
function total(){
var t = MODELS[state.model].base + PAINTS[state.paint].add;
Object.keys(state.acc).forEach(function(k){ if(state.acc[k] && (!ACC[k].front || state.model !== "havn")) t += ACC[k].p; });
return t;
}
var currentTotal = MODELS[state.model].base + PAINTS[state.paint].add;
function render(){
/* hide front-only options for havn */
document.querySelectorAll('.acc').forEach(function(l){
if(ACC[l.dataset.acc].front && state.model === "havn"){
l.hidden = true;
var inp = l.querySelector("input"); inp.checked = false; state.acc[l.dataset.acc] = false;
} else l.hidden = false;
});
pvBike.innerHTML = bikeSVG({model: state.model, paint: state.paint, acc: state.acc, dark: true});
if(!reduce){
pvBike.classList.add("pulse");
setTimeout(function(){ pvBike.classList.remove("pulse"); }, 380);
}
pvModel.textContent = MODELS[state.model].name;
pvPaint.textContent = PAINTS[state.paint].name;
var pi = Object.keys(PAINTS).indexOf(state.paint) + 1;
var ac = Object.keys(state.acc).filter(function(k){ return state.acc[k]; }).length;
pvSerial.textContent = "LMN-25-" + MODELS[state.model].code + (pi < 10 ? "0" : "") + pi + "-" + (ac < 10 ? "0" : "") + ac;
/* summary */
var rows = '<li><span>' + MODELS[state.model].name + '</span><span>' + fmt(MODELS[state.model].base) + "</span>" +
'<li><span>' + PAINTS[state.paint].name + ' paint</span><span>' + (PAINTS[state.paint].add ? "+" + fmt(PAINTS[state.paint].add) : "included") + "</span>";
Object.keys(state.acc).forEach(function(k){
if(state.acc[k] && !(ACC[k].front && state.model === "havn"))
rows += '<li><span>' + ACC[k].name + '</span><span>+' + fmt(ACC[k].p) + "</span>";
});
rows += '<li><span>Frame engraving + road test</span><span>included</span>';
var t = total();
document.getElementById("summaryWrap").innerHTML =
'<div class="summary"><ul>' + rows + "</ul>" +
'<div class="total-row"><span>Total</span><strong id="totalVal">' + fmt(currentTotal) + "</strong></div>" +
'<p class="ship">Ships in 10–14 weeks · free delivery in Greater Copenhagen</p>' +
'<button class="btn btn-amber" id="enquireBtn" type="button">Hold my slot for 72h <span class="ar">→</span></button></div>';
var el = document.getElementById("totalVal");
var from = currentTotal, t0 = performance.now(), dur = reduce ? 0 : 450;
function tick(now){
var p = dur === 0 ? 1 : Math.min((now - t0) / dur, 1);
var ease = 1 - Math.pow(1 - p, 3);
el.textContent = fmt(Math.round(from + (t - from) * ease));
if(p < 1) requestAnimationFrame(tick);
}
if(from !== t) requestAnimationFrame(tick);
currentTotal = t;
document.getElementById("enquireBtn").addEventListener("click", function(){
var n = 120 + Object.keys(state.acc).filter(function(k){ return state.acc[k]; }).length;
document.getElementById("summaryWrap").innerHTML =
'<div class="confirm"><h3>Slot held — thank you</h3>' +
'<p style="color:var(--paper)">Your configuration is saved as <strong style="color:var(--amber)">' + pvSerial.textContent + "</strong> — slot #" + n + " of 96 for 2025.</p>" +
'<p style="color:var(--sage);font:500 11px/1.8 var(--mono);letter-spacing:.12em;text-transform:uppercase">A build sheet is on its way to your email. We\'ll call you within one working day to book your 90-minute conversation.</p></div>';
});
}
function setModel(k){
state.model = k;
document.querySelectorAll(".mbtn").forEach(function(b){ b.classList.toggle("active", b.dataset.model === k); });
render();
}
render();
/* model card CTA → configurator */
document.querySelectorAll(".choose").forEach(function(b){
b.addEventListener("click", function(){
setModel(b.dataset.model);
document.getElementById("build").scrollIntoView({behavior: reduce ? "auto" : "smooth"});
});
});
/* forms */
document.getElementById("ctaForm").addEventListener("submit", function(e){
e.preventDefault();
document.getElementById("ctaMsg").textContent = "Slot held for 72 hours — check your inbox for the build sheet.";
this.reset();
});
document.getElementById("newsForm").addEventListener("submit", function(e){
e.preventDefault();
document.getElementById("newsMsg").textContent = "You're on the list — one email a month, that's it.";
this.reset();
});
})();
</script>
</body>
</html>

