Run details
- Model
- particle-ai/particle-1
- Route
- OpenRouter API, one user message
- Served by
- Particle AI
- Effort
- high, OpenRouter's default for this model
- Limits
- 60 min · 131,072 output tokens (the model's limit)
- Date
- 28 Oct 2026
- Time
- 2 min 19 s
- Size
- 50 kB
- Tokens
- 167 in, 33,141 out (16,809 of them thinking)
- List price
- $0.017 at today's price ($0.05 in / $0.5 out per M)
- Paid
- $0.033
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
50 kB 1,384 lines Raw
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Lumen Cycles — Custom cargo bikes, built by hand in Copenhagen</title>
<meta name="description" content="Lumen Cycles is a four-person workshop in Nørrebro, Copenhagen. Hand-brazed steel cargo bikes, built to order around what you actually carry. Base build from 4.000 €.">
<meta name="theme-color" content="#F4F0E6">
<style>
*,*::before,*::after{box-sizing:border-box}
:root{
--paper:#F4F0E6;
--paper-2:#EDE7D9;
--paper-3:#FBF8F1;
--ink:#17140F;
--ink-2:#4C463C;
--ink-3:#7C7466;
--terra:#B0472A;
--terra-2:#8E3720;
--line:rgba(23,20,15,.14);
--line-strong:rgba(23,20,15,.3);
--serif:"Iowan Old Style","Palatino Linotype",Palatino,"Book Antiqua",Georgia,"Times New Roman",serif;
--sans:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
--mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace;
--max:1200px;
}
html{scroll-behavior:smooth}
body{
margin:0;
background-color:var(--paper);
background-image:radial-gradient(rgba(23,20,15,.045) 1px,transparent 1px);
background-size:4px 4px;
color:var(--ink);
font-family:var(--sans);
font-size:17px;
line-height:1.62;
-webkit-font-smoothing:antialiased;
text-rendering:optimizeLegibility;
overflow-x:hidden;
}
h1,h2,h3,h4{font-family:var(--serif);font-weight:400;line-height:1.04;letter-spacing:-.02em;margin:0}
p{margin:0}
a{color:inherit}
img,svg{max-width:100%}
figure{margin:0}
.wrap{max-width:var(--max);margin:0 auto;padding:0 28px}
.eyebrow{
font-family:var(--mono);
font-size:11px;
letter-spacing:.18em;
text-transform:uppercase;
color:var(--ink-3);
margin:0 0 18px;
display:flex;
align-items:center;
gap:10px;
}
.eyebrow::before{
content:"";
width:22px;height:1px;
background:var(--terra);
display:inline-block;
flex:none;
}
/* ---------- buttons ---------- */
.btn{
display:inline-flex;align-items:center;justify-content:center;gap:.6em;
padding:15px 26px;
border-radius:2px;
font-family:var(--sans);
font-size:14.5px;font-weight:600;letter-spacing:.01em;
text-decoration:none;
border:1px solid transparent;
cursor:pointer;
transition:background .18s ease,color .18s ease,border-color .18s ease,transform .18s ease;
}
.btn-primary{background:var(--ink);color:var(--paper)}
.btn-primary:hover{background:var(--terra);transform:translateY(-1px)}
.btn-ghost{border-color:var(--line-strong);color:var(--ink);background:transparent}
.btn-ghost:hover{border-color:var(--ink);background:rgba(23,20,15,.04)}
.btn-arrow::after{content:"→";font-size:15px;transition:transform .18s ease}
.btn-arrow:hover::after{transform:translateX(3px)}
a:focus-visible,button:focus-visible,summary:focus-visible,
input:focus-visible,select:focus-visible,textarea:focus-visible{
outline:2px solid var(--terra);
outline-offset:2px;
}
/* ---------- header ---------- */
.site-header{
position:sticky;top:0;z-index:60;
background:rgba(244,240,230,.8);
-webkit-backdrop-filter:blur(14px);
backdrop-filter:blur(14px);
border-bottom:1px solid transparent;
transition:border-color .25s ease,background .25s ease;
}
.site-header.scrolled{border-bottom-color:var(--line);background:rgba(244,240,230,.95)}
.nav{
max-width:var(--max);margin:0 auto;
padding:15px 28px;
display:flex;align-items:center;justify-content:space-between;gap:24px;
}
.brand{display:flex;align-items:center;gap:11px;text-decoration:none;color:var(--ink)}
.brand svg{width:25px;height:25px;color:var(--terra);flex:none}
.brand .word{
font-family:var(--serif);
font-size:18px;
letter-spacing:.16em;
text-transform:uppercase;
line-height:1;
}
.brand .word small{
display:block;
font-family:var(--mono);
font-size:8.5px;
letter-spacing:.28em;
color:var(--ink-3);
margin-top:4px;
}
.nav-links{display:flex;align-items:center;gap:28px;list-style:none;margin:0;padding:0}
.nav-links a{
font-size:14px;
text-decoration:none;
color:var(--ink-2);
padding:4px 0;
position:relative;
transition:color .15s ease;
}
.nav-links a::after{
content:"";position:absolute;left:0;right:0;bottom:0;height:1px;
background:var(--terra);transform:scaleX(0);transform-origin:left;
transition:transform .22s ease;
}
.nav-links a:hover{color:var(--ink)}
.nav-links a:hover::after{transform:scaleX(1)}
.nav .btn{padding:11px 18px;font-size:13.5px}
/* ---------- hero ---------- */
.hero{
max-width:var(--max);margin:0 auto;
padding:60px 28px 34px;
display:grid;
grid-template-columns:1.02fr .98fr;
gap:58px;
align-items:center;
}
.hero h1{
font-size:clamp(2.5rem,5.1vw,4.15rem);
letter-spacing:-.03em;
margin:0 0 24px;
}
.hero h1 em{font-style:italic;color:var(--terra)}
.lede{
font-size:18px;
line-height:1.62;
color:var(--ink-2);
max-width:47ch;
margin:0 0 32px;
}
.hero-cta{display:flex;flex-wrap:wrap;gap:12px;margin-bottom:32px}
.hero-trust{
display:flex;flex-wrap:wrap;gap:10px 22px;
list-style:none;margin:0;padding:22px 0 0;
border-top:1px solid var(--line);
font-family:var(--mono);
font-size:10.5px;
letter-spacing:.11em;
text-transform:uppercase;
color:var(--ink-3);
}
.hero-trust li{display:flex;align-items:center;gap:8px}
.hero-trust li::before{
content:"";
width:5px;height:5px;border-radius:50%;
background:var(--terra);
flex:none;
}
/* ---------- hero art ---------- */
.art-panel{
position:relative;
background-color:var(--paper-2);
background-image:
linear-gradient(rgba(23,20,15,.055) 1px,transparent 1px),
linear-gradient(90deg,rgba(23,20,15,.055) 1px,transparent 1px);
background-size:30px 30px;
border:1px solid var(--line);
border-radius:3px;
padding:26px 22px 20px;
overflow:hidden;
}
.art-panel::after{
content:"";
position:absolute;inset:0;
box-shadow:inset 0 0 70px rgba(23,20,15,.06);
pointer-events:none;
}
.art-tag{
position:absolute;top:14px;left:16px;
font-family:var(--mono);
font-size:9.5px;
letter-spacing:.18em;
text-transform:uppercase;
color:var(--ink-3);
background:var(--paper-2);
padding:3px 7px;
z-index:1;
}
.bike-art{display:block;width:100%;height:auto;position:relative;z-index:0}
.art figcaption{
font-family:var(--mono);
font-size:10.5px;
letter-spacing:.12em;
text-transform:uppercase;
color:var(--ink-3);
margin-top:14px;
display:flex;
justify-content:space-between;
gap:16px;
flex-wrap:wrap;
}
/* ---------- strip ---------- */
.strip{
border-top:1px solid var(--line);
border-bottom:1px solid var(--line);
background:var(--paper-2);
}
.strip-inner{
max-width:var(--max);margin:0 auto;
padding:28px;
display:grid;grid-template-columns:repeat(4,1fr);gap:28px;
}
.strip .num{font-family:var(--serif);font-size:29px;line-height:1;letter-spacing:-.02em}
.strip .lbl{
font-family:var(--mono);
font-size:10px;letter-spacing:.14em;text-transform:uppercase;
color:var(--ink-3);margin-top:9px;
}
/* ---------- sections ---------- */
.section{max-width:var(--max);margin:0 auto;padding:96px 28px}
.section[id]{scroll-margin-top:86px}
.section-head{max-width:660px;margin-bottom:52px}
.section-head h2{font-size:clamp(1.9rem,3.4vw,2.9rem);margin:0 0 18px}
.section-head p{color:var(--ink-2);font-size:17px}
.two-col{
display:grid;
grid-template-columns:.85fr 1.15fr;
gap:64px;
align-items:start;
}
.two-col h2{font-size:clamp(1.9rem,3.3vw,2.75rem);margin:0;position:sticky;top:110px}
.body-copy p{color:var(--ink-2);margin-bottom:20px;font-size:17px}
.body-copy p strong{color:var(--ink);font-weight:600}
.spec-list{margin:36px 0 0;padding:0}
.spec{
display:grid;
grid-template-columns:170px 1fr;
gap:22px;
padding:14px 0;
border-top:1px solid var(--line);
font-size:15.5px;
}
.spec:last-child{border-bottom:1px solid var(--line)}
.spec dt{
font-family:var(--mono);
font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;
color:var(--ink-3);
padding-top:4px;
}
.spec dd{margin:0;color:var(--ink-2)}
/* ---------- models ---------- */
.models{display:grid;grid-template-columns:repeat(3,1fr);gap:22px}
.model{
border:1px solid var(--line);
background:var(--paper-3);
padding:30px 28px 28px;
display:flex;flex-direction:column;
transition:transform .25s ease,box-shadow .25s ease,border-color .25s ease;
}
.model:hover{
transform:translateY(-4px);
box-shadow:0 18px 40px -24px rgba(23,20,15,.45);
border-color:var(--line-strong);
}
.model .glyph{width:38px;height:38px;color:var(--terra);margin-bottom:26px}
.model h3{font-size:24px;margin:0 0 10px}
.model .from{
font-family:var(--mono);
font-size:11px;letter-spacing:.13em;text-transform:uppercase;
color:var(--ink-3);
margin-bottom:18px;
}
.model .from b{color:var(--ink);font-weight:600;letter-spacing:.06em}
.model p{color:var(--ink-2);font-size:15px;line-height:1.6;margin-bottom:22px}
.model ul{
list-style:none;margin:auto 0 0;padding:16px 0 0;
border-top:1px solid var(--line);
font-size:13.5px;color:var(--ink-2);
}
.model ul li{padding:3px 0;display:flex;gap:9px}
.model ul li::before{content:"—";color:var(--terra);flex:none}
/* ---------- dark band ---------- */
.band{
background:#14120E;
color:#EFEAE0;
--line:rgba(239,234,224,.16);
--line-strong:rgba(239,234,224,.34);
--ink-3:rgba(239,234,224,.55);
--ink-2:rgba(239,234,224,.74);
}
.band .section{padding-top:92px;padding-bottom:92px}
.band h2{color:#F8F4EB;font-size:clamp(1.9rem,3.4vw,2.9rem)}
.band .eyebrow{color:rgba(239,234,224,.55)}
.band-lead{display:grid;grid-template-columns:.9fr 1.1fr;gap:64px;align-items:start}
.band-body p{color:rgba(239,234,224,.74);margin-bottom:20px;font-size:17px}
.band-body p strong{color:#F8F4EB;font-weight:600}
.band-stats{
display:grid;grid-template-columns:repeat(3,1fr);gap:24px;
margin-top:40px;padding-top:32px;
border-top:1px solid rgba(239,234,224,.16);
}
.band-stats .num{font-family:var(--serif);font-size:36px;line-height:1;color:#F8F4EB;letter-spacing:-.02em}
.band-stats .lbl{
font-family:var(--mono);
font-size:10px;letter-spacing:.14em;text-transform:uppercase;
color:rgba(239,234,224,.5);margin-top:8px;
}
/* ---------- process ---------- */
.steps{display:grid;grid-template-columns:repeat(4,1fr);gap:26px;counter-reset:step}
.step{border-top:2px solid var(--ink);padding-top:20px}
.step .n{
font-family:var(--mono);
font-size:11px;letter-spacing:.14em;
color:var(--terra);
margin-bottom:14px;
display:block;
}
.step h3{font-size:21px;margin:0 0 10px}
.step p{font-size:14.5px;color:var(--ink-2);line-height:1.6}
/* ---------- quotes ---------- */
.quotes{display:grid;grid-template-columns:repeat(3,1fr);gap:30px}
.quote{border-top:2px solid var(--ink);padding-top:22px;display:flex;flex-direction:column}
.quote p{
font-family:var(--serif);
font-size:19px;
line-height:1.48;
letter-spacing:-.01em;
margin-bottom:24px;
}
.quote p::before{content:"“"}
.quote p::after{content:"”"}
.quote cite{
font-style:normal;
font-family:var(--mono);
font-size:10.5px;letter-spacing:.12em;text-transform:uppercase;
color:var(--ink-3);
margin-top:auto;
display:block;
padding-top:16px;
border-top:1px solid var(--line);
}
/* ---------- configurator ---------- */
.config{
display:grid;
grid-template-columns:1.35fr .85fr;
border:1px solid var(--line-strong);
background:var(--paper-3);
}
.config-left{padding:12px 0 0}
.config-head{
padding:26px 26px 22px;
border-bottom:1px solid var(--line);
}
.config-head h3{font-size:22px;margin:0 0 6px}
.config-head p{font-size:13.5px;color:var(--ink-3)}
.opt{
position:relative;
display:grid;
grid-template-columns:20px 1fr auto;
gap:16px;
align-items:center;
padding:17px 26px;
border-top:1px solid var(--line);
cursor:pointer;
transition:background .15s ease;
}
.opt:first-of-type{border-top:none}
.opt:hover{background:rgba(23,20,15,.025)}
.opt input{position:absolute;opacity:0;width:1px;height:1px;pointer-events:none}
.opt .box{
width:20px;height:20px;
border:1.5px solid var(--line-strong);
border-radius:2px;
position:relative;
transition:background .15s ease,border-color .15s ease;
flex:none;
}
.opt input:checked + .box{background:var(--terra);border-color:var(--terra)}
.opt input:checked + .box::after{
content:"";
position:absolute;left:6px;top:2px;
width:5px;height:10px;
border:solid #FBF8F1;
border-width:0 2px 2px 0;
transform:rotate(45deg);
}
.opt input:focus-visible + .box{outline:2px solid var(--terra);outline-offset:3px}
.opt-name{display:block;font-size:15.5px;font-weight:600;letter-spacing:-.01em;line-height:1.35}
.opt-desc{display:block;font-size:13px;color:var(--ink-3);line-height:1.45;margin-top:3px}
.opt-price{
font-family:var(--mono);
font-size:13px;
color:var(--ink-2);
white-space:nowrap;
}
.config-right{
border-left:1px solid var(--line-strong);
background:var(--paper-2);
padding:30px 26px;
display:flex;flex-direction:column;
}
.sum-label{
font-family:var(--mono);
font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;
color:var(--ink-3);
margin-bottom:12px;
}
.sum-total{
font-family:var(--serif);
font-size:46px;
line-height:1;
letter-spacing:-.03em;
margin-bottom:6px;
display:block;
}
.sum-total.pop{animation:pop .35s ease}
@keyframes pop{0%{transform:scale(1)}40%{transform:scale(1.045)}100%{transform:scale(1)}}
.sum-sub{font-size:12.5px;color:var(--ink-3);margin-bottom:20px}
.sum-list{list-style:none;margin:0 0 20px;padding:0;font-size:13px;color:var(--ink-2)}
.sum-list li{
padding:7px 0;
border-top:1px solid var(--line);
display:flex;gap:9px;
line-height:1.4;
}
.sum-list li::before{content:"·";color:var(--terra);font-weight:700;flex:none}
.sum-row{
display:flex;justify-content:space-between;gap:14px;
padding:11px 0;
border-top:1px solid var(--line);
font-size:13.5px;
color:var(--ink-2);
}
.sum-row strong{color:var(--ink);font-weight:600;font-family:var(--mono);font-size:12.5px;letter-spacing:.03em}
.sum-note{font-size:11.5px;line-height:1.5;color:var(--ink-3);margin-top:16px}
.config-right .btn{width:100%;margin-top:6px}
/* ---------- faq ---------- */
.faq{max-width:820px}
.faq details{border-top:1px solid var(--line)}
.faq details:last-of-type{border-bottom:1px solid var(--line)}
.faq summary{
list-style:none;
cursor:pointer;
padding:22px 46px 22px 0;
position:relative;
font-family:var(--serif);
font-size:19px;
letter-spacing:-.01em;
}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{
content:"+";
position:absolute;right:8px;top:18px;
font-size:22px;line-height:1;
color:var(--terra);
transition:transform .2s ease;
}
.faq details[open] summary::after{content:"−"}
.faq .answer{padding:0 46px 26px 0;color:var(--ink-2);font-size:15.5px;max-width:68ch}
/* ---------- contact ---------- */
.contact-grid{display:grid;grid-template-columns:1fr 1fr;gap:64px;align-items:start}
.contact-info .row{
display:flex;justify-content:space-between;gap:20px;
padding:13px 0;border-top:1px solid var(--line);
font-size:14.5px;
}
.contact-info .row span:first-child{
font-family:var(--mono);
font-size:10.5px;letter-spacing:.13em;text-transform:uppercase;
color:var(--ink-3);
padding-top:4px;
}
.contact-info .row span:last-child{text-align:right;color:var(--ink-2)}
.form-card{
border:1px solid var(--line-strong);
background:var(--paper-3);
padding:30px 28px;
}
.form{display:grid;grid-template-columns:1fr 1fr;gap:16px}
.field{display:flex;flex-direction:column;gap:7px}
.field.full{grid-column:1/-1}
.field label{
font-family:var(--mono);
font-size:10px;letter-spacing:.14em;text-transform:uppercase;
color:var(--ink-3);
}
.field input,.field select,.field textarea{
font-family:var(--sans);
font-size:15px;
padding:12px 13px;
background:var(--paper);
border:1px solid var(--line-strong);
border-radius:2px;
color:var(--ink);
width:100%;
transition:border-color .15s ease,background .15s ease;
}
.field textarea{resize:vertical;min-height:96px}
.field input:hover,.field select:hover,.field textarea:hover{border-color:rgba(23,20,15,.45)}
.form .btn{grid-column:1/-1;margin-top:6px;width:100%}
.form-note{grid-column:1/-1;font-size:11.5px;color:var(--ink-3);line-height:1.5;margin-top:2px}
.form-success{padding:14px 0 4px}
.form-success .tick{
width:42px;height:42px;border-radius:50%;
background:var(--terra);color:#FBF8F1;
display:flex;align-items:center;justify-content:center;
font-size:20px;margin-bottom:18px;
}
.form-success h3{font-size:24px;margin:0 0 12px}
.form-success p{color:var(--ink-2);font-size:15px;margin-bottom:10px}
/* ---------- footer ---------- */
.site-footer{
background:#14120E;
color:rgba(239,234,224,.72);
margin-top:40px;
--line:rgba(239,234,224,.14);
}
.footer-inner{
max-width:var(--max);margin:0 auto;
padding:68px 28px 34px;
}
.footer-top{
display:grid;
grid-template-columns:1.4fr 1fr 1fr 1fr;
gap:44px;
padding-bottom:44px;
border-bottom:1px solid var(--line);
}
.footer-brand .word{
font-family:var(--serif);
font-size:19px;letter-spacing:.16em;text-transform:uppercase;
color:#F8F4EB;
display:flex;align-items:center;gap:11px;
}
.footer-brand svg{width:24px;height:24px;color:var(--terra);flex:none}
.footer-brand p{font-size:13.5px;margin-top:16px;max-width:30ch;line-height:1.6;color:rgba(239,234,224,.58)}
.footer-col h4{
font-family:var(--mono);
font-size:10px;letter-spacing:.16em;text-transform:uppercase;
color:rgba(239,234,224,.45);
margin:0 0 16px;
font-weight:400;
}
.footer-col ul{list-style:none;margin:0;padding:0;font-size:14px}
.footer-col li{margin-bottom:9px;line-height:1.5}
.footer-col a{text-decoration:none;color:rgba(239,234,224,.76);transition:color .15s ease}
.footer-col a:hover{color:#F8F4EB}
.footer-bottom{
display:flex;justify-content:space-between;gap:20px;flex-wrap:wrap;
padding-top:24px;
font-family:var(--mono);
font-size:10.5px;letter-spacing:.09em;text-transform:uppercase;
color:rgba(239,234,224,.42);
}
.footer-bottom a{text-decoration:none;color:inherit}
.footer-bottom a:hover{color:#F8F4EB}
/* ---------- reveal ---------- */
.reveal{opacity:0;transform:translateY(18px);transition:opacity .7s cubic-bezier(.2,.7,.3,1),transform .7s cubic-bezier(.2,.7,.3,1)}
.reveal.in{opacity:1;transform:none}
.reveal[data-d="1"]{transition-delay:.07s}
.reveal[data-d="2"]{transition-delay:.14s}
.reveal[data-d="3"]{transition-delay:.21s}
@media (prefers-reduced-motion:reduce){
html{scroll-behavior:auto}
.reveal{opacity:1;transform:none;transition:none}
*{animation-duration:.001s !important;transition-duration:.001s !important}
}
/* ---------- responsive ---------- */
@media (max-width:1000px){
.hero{grid-template-columns:1fr;gap:44px;padding-top:44px}
.hero .art{order:2}
.two-col,.band-lead,.contact-grid{grid-template-columns:1fr;gap:36px}
.two-col h2{position:static}
.models,.quotes,.steps{grid-template-columns:1fr 1fr}
.config{grid-template-columns:1fr}
.config-right{border-left:none;border-top:1px solid var(--line-strong)}
.footer-top{grid-template-columns:1fr 1fr;gap:36px}
}
@media (max-width:860px){
.nav-links{display:none}
}
@media (max-width:680px){
body{font-size:16px}
.wrap,.nav,.hero,.section,.strip-inner,.footer-inner{padding-left:20px;padding-right:20px}
.section{padding-top:64px;padding-bottom:64px}
.hero h1{font-size:clamp(2.1rem,8.4vw,2.9rem)}
.strip-inner{grid-template-columns:1fr 1fr;gap:22px}
.models,.quotes,.steps,.band-stats{grid-template-columns:1fr}
.form{grid-template-columns:1fr}
.spec{grid-template-columns:1fr;gap:4px}
.spec dt{padding-top:0}
.form-card{padding:24px 20px}
.config-head,.opt{padding-left:18px;padding-right:18px}
.opt{grid-template-columns:20px 1fr;gap:14px}
.opt-price{grid-column:2;font-size:12px;color:var(--ink-3);margin-top:-4px}
.config-right{padding:26px 18px}
.sum-total{font-size:40px}
.footer-top{grid-template-columns:1fr}
.contact-info .row{flex-direction:column;gap:3px}
.contact-info .row span:last-child{text-align:left}
}
</style>
</head>
<body>
<header class="site-header" id="siteHeader">
<nav class="nav" aria-label="Primary">
<a class="brand" href="#top" aria-label="Lumen Cycles, home">
<svg viewBox="0 0 32 32" aria-hidden="true">
<circle cx="16" cy="16" r="13.4" fill="none" stroke="currentColor" stroke-width="2.6"/>
<path d="M16 2.6 A13.4 13.4 0 0 1 29.4 16 L16 16 Z" fill="currentColor"/>
</svg>
<span class="word">Lumen<small>Copenhagen</small></span>
</a>
<ul class="nav-links">
<li><a href="#workshop">The workshop</a></li>
<li><a href="#bikes">Bikes</a></li>
<li><a href="#process">Process</a></li>
<li><a href="#configure">Configure</a></li>
<li><a href="#faq">Questions</a></li>
</ul>
<a class="btn btn-primary" href="#contact">Book a build slot</a>
</nav>
</header>
<main id="top">
<!-- ============ HERO ============ -->
<section class="hero">
<div class="hero-copy">
<p class="eyebrow">Frame building since 2014 · Nørrebro</p>
<h1>Built by hand.<br>Built for <em>your</em> street.</h1>
<p class="lede">
Every Lumen is measured, brazed and finished by four people in a workshop on
Jægersborggade. Tell us what you actually carry — a cello, two children,
a week of groceries — and we build the bike around it.
Base build from 4.000 €, on the road in twelve weeks.
</p>
<div class="hero-cta">
<a class="btn btn-primary btn-arrow" href="#configure">Configure your build</a>
<a class="btn btn-ghost" href="#contact">Visit the workshop</a>
</div>
<ul class="hero-trust">
<li>5-year frame warranty</li>
<li>Free service at 500 km</li>
<li>30-day fit adjustment</li>
</ul>
</div>
<figure class="art reveal">
<div class="art-panel">
<span class="art-tag">Fig. 01 — The Hauler</span>
<svg class="bike-art" viewBox="40 100 880 420" role="img" aria-label="Side-view line drawing of a Lumen cargo bike with a birch cargo box and two wheels">
<ellipse cx="480" cy="474" rx="352" ry="11" fill="#17140F" opacity="0.07"/>
<!-- rear wheel -->
<g fill="none" stroke="#17140F">
<circle cx="205" cy="372" r="92" stroke-width="9"/>
<circle cx="205" cy="372" r="72" stroke-width="2.5" opacity="0.5"/>
<g stroke-width="1.6" opacity="0.42">
<line x1="205" y1="300" x2="205" y2="444"/>
<line x1="133" y1="372" x2="277" y2="372"/>
<line x1="154" y1="321" x2="256" y2="423"/>
<line x1="256" y1="321" x2="154" y2="423"/>
</g>
<circle cx="205" cy="372" r="6" fill="#17140F" stroke="none"/>
</g>
<!-- front wheel -->
<g fill="none" stroke="#17140F">
<circle cx="755" cy="372" r="92" stroke-width="9"/>
<circle cx="755" cy="372" r="72" stroke-width="2.5" opacity="0.5"/>
<g stroke-width="1.6" opacity="0.42">
<line x1="755" y1="300" x2="755" y2="444"/>
<line x1="683" y1="372" x2="827" y2="372"/>
<line x1="704" y1="321" x2="806" y2="423"/>
<line x1="806" y1="321" x2="704" y2="423"/>
</g>
<circle cx="755" cy="372" r="6" fill="#17140F" stroke="none"/>
</g>
<!-- chain -->
<g stroke="#17140F" stroke-width="2" opacity="0.38">
<line x1="352" y1="298" x2="205" y2="360"/>
<line x1="352" y1="346" x2="205" y2="384"/>
</g>
<!-- frame -->
<g fill="none" stroke="#17140F" stroke-linecap="round">
<path d="M690 300 L352 322" stroke-width="9"/>
<path d="M352 322 L302 206" stroke-width="9"/>
<path d="M302 212 L205 372" stroke-width="7"/>
<path d="M352 322 L205 372" stroke-width="7"/>
<path d="M686 300 L755 372" stroke-width="8"/>
<path d="M704 214 L688 302" stroke-width="12"/>
<path d="M704 214 L696 180" stroke-width="8"/>
<path d="M666 172 C 682 156 716 156 748 172" stroke-width="9"/>
<path d="M302 208 L298 196" stroke-width="8"/>
<path d="M270 200 L332 194" stroke-width="13"/>
</g>
<!-- crank -->
<g fill="none" stroke="#17140F">
<circle cx="352" cy="322" r="26" stroke-width="3"/>
<path d="M352 322 L330 352" stroke-width="7" stroke-linecap="round"/>
<path d="M318 356 L342 350" stroke-width="5" stroke-linecap="round"/>
<circle cx="352" cy="322" r="6.5" fill="#17140F" stroke="none"/>
</g>
<!-- cargo box -->
<g>
<rect x="398" y="156" width="250" height="150" rx="8" fill="#B0472A" opacity="0.14"/>
<rect x="398" y="156" width="250" height="150" rx="8" fill="none" stroke="#17140F" stroke-width="5"/>
<line x1="398" y1="186" x2="648" y2="186" stroke="#17140F" stroke-width="4"/>
<g stroke="#17140F" stroke-width="2" opacity="0.3">
<line x1="430" y1="192" x2="430" y2="300"/>
<line x1="460" y1="192" x2="460" y2="300"/>
<line x1="490" y1="192" x2="490" y2="300"/>
<line x1="556" y1="192" x2="556" y2="300"/>
<line x1="586" y1="192" x2="586" y2="300"/>
<line x1="616" y1="192" x2="616" y2="300"/>
</g>
<rect x="486" y="250" width="74" height="26" rx="2" fill="#F4F0E6" stroke="#17140F" stroke-width="2"/>
<text x="523" y="268" text-anchor="middle" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" font-size="10.5" letter-spacing="1.6" fill="#17140F">LUMEN</text>
</g>
</svg>
</div>
<figcaption>
<span>Reynolds 725 · brass-brazed · 22 kg complete</span>
<span>Jægersborggade 34</span>
</figcaption>
</figure>
</section>
<!-- ============ STRIP ============ -->
<div class="strip">
<div class="strip-inner">
<div><div class="num">412</div><div class="lbl">Bikes on the road</div></div>
<div><div class="num">12 weeks</div><div class="lbl">Deposit to delivery</div></div>
<div><div class="num">5 years</div><div class="lbl">Frame warranty</div></div>
<div><div class="num">100%</div><div class="lbl">Made in Copenhagen</div></div>
</div>
</div>
<!-- ============ WORKSHOP ============ -->
<section class="section" id="workshop">
<div class="two-col">
<div class="reveal">
<p class="eyebrow">Why we build to order</p>
<h2>An off-the-shelf cargo bike was designed for an average family that doesn’t exist.</h2>
</div>
<div class="body-copy reveal" data-d="1">
<p>
Your street is cobbled. Your cellar staircase turns twice. One child is seven,
the other is a dog, and the nearest shop is 1.8 km uphill.
</p>
<p>
<strong>So we start with the frame, not the catalogue.</strong> Every Lumen begins with
a measurement session — your height, your reach, your turning circle at the
bottom of the stairs — and ends with a dimensioned drawing you approve before
a single tube is cut.
</p>
<p>
There is no production line here. Four people, two jigs, a torch, and a build
sheet with your name on it that we keep for as long as you own the bike.
</p>
<dl class="spec-list">
<div class="spec">
<dt>Frame</dt>
<dd>Reynolds 725 steel, brass-brazed by hand in Nørrebro</dd>
</div>
<div class="spec">
<dt>Cargo box</dt>
<dd>18 mm birch ply, or 1.5 mm powder-coated aluminium for the salty months</dd>
</div>
<div class="spec">
<dt>Drivetrain</dt>
<dd>Shimano Deore 1×10, or Gates CDX belt with Bosch Cargo Line</dd>
</div>
<div class="spec">
<dt>Brakes</dt>
<dd>Magura MT5 four-piston, 180 mm rotors front and rear</dd>
</div>
<div class="spec">
<dt>Wheels</dt>
<dd>26″ rear / 20″ front, hand-built on Sapim spokes</dd>
</div>
<div class="spec">
<dt>Finish</dt>
<dd>Any RAL colour, powder-coated in Valby. Clear-coat over raw steel on request</dd>
</div>
</dl>
</div>
</div>
</section>
<!-- ============ BIKES ============ -->
<section class="section" id="bikes" style="padding-top:0">
<div class="section-head reveal">
<p class="eyebrow">Three starting points</p>
<h2>Every Lumen is different. These are where most of them begin.</h2>
<p>Prices are for a complete, rideable bike including VAT. Everything below can be changed, added or removed.</p>
</div>
<div class="models">
<article class="model reveal">
<svg class="glyph" viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<rect x="6" y="16" width="36" height="20" rx="2"/>
<path d="M16 16v20M24 16v20M32 16v20"/>
</svg>
<h3>The Hauler</h3>
<p class="from">From <b>4.000 €</b></p>
<p>The flatbed workhorse. Timber yard, tool run, dog, drum kit. Low load floor, short wheelbase, still fits a standard bike rack.</p>
<ul>
<li>100 kg payload</li>
<li>2.14 m overall length</li>
<li>Rigid fork, 26″ wheels</li>
</ul>
</article>
<article class="model reveal" data-d="1">
<svg class="glyph" viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<path d="M4 32h40"/>
<circle cx="18" cy="17" r="5"/>
<circle cx="33" cy="17" r="5"/>
<path d="M8 32V22M40 32V22"/>
</svg>
<h3>The School Run</h3>
<p class="from">From <b>4.450 €</b></p>
<p>A bench for two with real harnesses, footrests and a canopy mount. Low step-through so you can load it in a coat and boots.</p>
<ul>
<li>2 children + 25 kg of shopping</li>
<li>Canopy-ready frame mounts</li>
<li>Wider, lower step-through</li>
</ul>
</article>
<article class="model reveal" data-d="2">
<svg class="glyph" viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<path d="M27 4 12 28h10l-2 16 16-24H25z"/>
</svg>
<h3>The Long Haul</h3>
<p class="from">From <b>5.100 €</b></p>
<p>For the households that sold the car. Bosch Cargo Line motor, dual battery, belt drive and a full year of Copenhagen weather.</p>
<ul>
<li>250 kg payload</li>
<li>Dual battery, up to 160 km</li>
<li>Gates CDX belt, hydraulic brakes</li>
</ul>
</article>
</div>
</section>
<!-- ============ DARK BAND ============ -->
<section class="band">
<div class="section">
<div class="band-lead">
<div class="reveal">
<p class="eyebrow">The workshop</p>
<h2>Steel, brass<br>and patience.</h2>
</div>
<div class="band-body reveal" data-d="1">
<p>
Every joint in a Lumen frame is brazed by hand with a brass fillet, at
800 °C, one tube at a time. It is slower than TIG welding and it leaves a
small, honest fillet you can see under the paint.
</p>
<p>
<strong>It also means a frame you can repair in forty years</strong> instead of
replacing in ten. Steel tells you when it is tired. Aluminium just goes quiet.
</p>
<div class="band-stats">
<div><div class="num">1</div><div class="lbl">Builder per frame</div></div>
<div><div class="num">46 h</div><div class="lbl">Average build time</div></div>
<div><div class="num">0</div><div class="lbl">Frames made elsewhere</div></div>
</div>
</div>
</div>
</div>
</section>
<!-- ============ PROCESS ============ -->
<section class="section" id="process">
<div class="section-head reveal">
<p class="eyebrow">How it works</p>
<h2>Four steps. About twelve weeks. No surprises on the invoice.</h2>
</div>
<div class="steps">
<div class="step reveal">
<span class="n">01 — Conversation</span>
<h3>We ask what you carry</h3>
<p>Thirty minutes, in the workshop or by video. Your street, your stairs, your children, your knees. No deposit, no obligation.</p>
</div>
<div class="step reveal" data-d="1">
<span class="n">02 — Drawing</span>
<h3>A fixed quote in five days</h3>
<p>You get a dimensioned drawing and a price that does not move. We only start cutting tubes once you have signed it off.</p>
</div>
<div class="step reveal" data-d="2">
<span class="n">03 — Build</span>
<h3>Six to nine weeks on the bench</h3>
<p>Photos at brazing, paint and assembly. You are welcome to visit at any point — most people come twice.</p>
</div>
<div class="step reveal" data-d="3">
<span class="n">04 — Handover</span>
<h3>Fitted, then checked at 500 km</h3>
<p>We set saddle, bars and brake levers to you in Nørrebro, then service it free once you have ridden 500 km.</p>
</div>
</div>
</section>
<!-- ============ QUOTES ============ -->
<section class="section" id="voices" style="padding-top:0">
<div class="section-head reveal">
<p class="eyebrow">Owners</p>
<h2>What 412 bikes sound like after a few Danish winters.</h2>
</div>
<div class="quotes">
<blockquote class="quote reveal">
<p>We replaced a car with it. Two kids, a cello and a week of groceries, up the hill to Vanløse, in the rain. It just does it.</p>
<cite>Sofie & Rasmus · Vanløse · School Run, 2021</cite>
</blockquote>
<blockquote class="quote reveal" data-d="1">
<p>Four years, 19.000 km. One chain, one set of pads, one service a year. The frame still looks like the day I collected it.</p>
<cite>Anders K. · Islands Brygge · Hauler, 2020</cite>
</blockquote>
<blockquote class="quote reveal" data-d="2">
<p>They measured my daughter’s shoulders before they welded the bench. That is not a normal thing a bike shop does.</p>
<cite>Marie · Nørrebro · School Run, 2023</cite>
</blockquote>
</div>
</section>
<!-- ============ CONFIGURATOR ============ -->
<section class="section" id="configure">
<div class="section-head reveal">
<p class="eyebrow">Configure</p>
<h2>Build an estimate in thirty seconds.</h2>
<p>Start from The Hauler at 4.000 € and add what you need. This is an honest estimate, not a sales funnel — the real quote comes after the conversation.</p>
</div>
<div class="config reveal">
<div class="config-left">
<div class="config-head">
<h3>The Hauler — base build</h3>
<p>Reynolds 725 frame · rigid fork · Shimano Deore 1×10 · Magura MT5 · birch cargo box · 4.000 €</p>
</div>
<label class="opt">
<input type="checkbox" data-price="1450" data-weeks="0" data-label="Bosch Cargo Line motor + 500 Wh battery">
<span class="box" aria-hidden="true"></span>
<span class="opt-main">
<span class="opt-name">Bosch Cargo Line motor</span>
<span class="opt-desc">250 W, 85 Nm, 500 Wh integrated battery, 25 km/h</span>
</span>
<span class="opt-price">+1.450 €</span>
</label>
<label class="opt">
<input type="checkbox" data-price="390" data-weeks="0" data-label="Gates CDX belt drive">
<span class="box" aria-hidden="true"></span>
<span class="opt-main">
<span class="opt-name">Gates CDX belt drive</span>
<span class="opt-desc">No chain oil, no chain lube, roughly 30.000 km per belt</span>
</span>
<span class="opt-price">+390 €</span>
</label>
<label class="opt">
<input type="checkbox" data-price="280" data-weeks="0" data-label="Second child seat, harness and footrests">
<span class="box" aria-hidden="true"></span>
<span class="opt-main">
<span class="opt-name">Second child seat</span>
<span class="opt-desc">Padded bench, five-point harness, footrests and side rails</span>
</span>
<span class="opt-price">+280 €</span>
</label>
<label class="opt">
<input type="checkbox" data-price="340" data-weeks="0" data-label="Rain canopy with side panels">
<span class="box" aria-hidden="true"></span>
<span class="opt-main">
<span class="opt-name">Rain canopy</span>
<span class="opt-desc">Marine-grade canvas hood with clear side panels</span>
</span>
<span class="opt-price">+340 €</span>
</label>
<label class="opt">
<input type="checkbox" data-price="450" data-weeks="2" data-label="Custom RAL powder coat">
<span class="box" aria-hidden="true"></span>
<span class="opt-main">
<span class="opt-name">Custom RAL colour</span>
<span class="opt-desc">Any RAL shade, powder-coated in Valby — adds two weeks</span>
</span>
<span class="opt-price">+450 €</span>
</label>
<label class="opt">
<input type="checkbox" data-price="180" data-weeks="0" data-label="Brooks B67 saddle and matching grips">
<span class="box" aria-hidden="true"></span>
<span class="opt-main">
<span class="opt-name">Brooks B67 saddle + grips</span>
<span class="opt-desc">Black or honey, with copper rivets</span>
</span>
<span class="opt-price">+180 €</span>
</label>
</div>
<aside class="config-right">
<p class="sum-label">Your estimate</p>
<span class="sum-total" id="total">4.000 €</span>
<p class="sum-sub">Including 25% Danish VAT</p>
<ul class="sum-list" id="sumList">
<li>Base build — The Hauler</li>
</ul>
<div class="sum-row"><span>Estimated build time</span><strong><span id="weeks">12</span> weeks</strong></div>
<div class="sum-row"><span>Deposit to reserve a slot</span><strong>800 €</strong></div>
<div class="sum-row"><span>EU delivery from</span><strong>180 €</strong></div>
<a class="btn btn-primary btn-arrow" href="#contact" style="margin-top:20px">Reserve a build slot</a>
<p class="sum-note">Delivery inside Denmark is included. We are a four-person workshop and take twelve builds at a time.</p>
</aside>
</div>
</section>
<!-- ============ FAQ ============ -->
<section class="section" id="faq" style="padding-top:0">
<div class="section-head reveal">
<p class="eyebrow">Questions</p>
<h2>The things people ask before they visit.</h2>
</div>
<div class="faq reveal">
<details open>
<summary>How long does a build actually take?</summary>
<div class="answer">
Twelve weeks is typical from deposit to collection, and fourteen if you choose a
custom RAL colour. We give you a firm week number when you sign the drawing,
and we tell you immediately if it moves.
</div>
</details>
<details>
<summary>Can I ride one before I order?</summary>
<div class="answer">
Yes. We keep two demo bikes in Nørrebro — a rigid Hauler and a Bosch-assisted
School Run. Book a slot and take one for a proper hour, ideally with your
heaviest cargo on board. Bring the kids; the harnesses are already fitted.
</div>
</details>
<details>
<summary>Do you deliver outside Denmark?</summary>
<div class="answer">
We ship anywhere in the EU, fully assembled and checked, from 180 € depending on
distance. Bikes travel in a dedicated crate and arrive ready to ride apart from
straightening the bars.
</div>
</details>
<details>
<summary>What happens in year nine when something breaks?</summary>
<div class="answer">
Every standard part on a Lumen is a standard part you can buy anywhere — Shimano,
Magura, Sapim. We keep your build sheet on file indefinitely, so we can tell you
exactly which bearing, cable or rotor went into your bike even a decade later.
</div>
</details>
<details>
<summary>What is covered by the warranty?</summary>
<div class="answer">
Five years on the frame and fork against defects and brazing failure, two years on
the powder coat, and two years on components under the manufacturer's own terms.
Wear items — pads, tyres, belts — are not covered, which is normal.
</div>
</details>
<details>
<summary>Will you electrify a cargo bike I already own?</summary>
<div class="answer">
No, and it is the only thing we always decline. Retrofitting a motor to an
unknown frame means we cannot vouch for the brakes, the dropouts or the tubes
taking that torque. We only motorise frames we have brazed ourselves.
</div>
</details>
</div>
</section>
<!-- ============ CONTACT ============ -->
<section class="section" id="contact" style="padding-top:0">
<div class="contact-grid">
<div class="contact-info reveal">
<p class="eyebrow">Come by</p>
<h2 style="margin-bottom:22px">The workshop is open Tuesday to Saturday.</h2>
<p style="color:var(--ink-2);margin-bottom:28px;max-width:42ch">
No showroom, no sales desk — just the bench, the jigs and whoever is brazing
that morning. Coffee is usually on. Book a slot so we can put a demo bike aside.
</p>
<div class="row"><span>Address</span><span>Jægersborggade 34<br>2200 København N</span></div>
<div class="row"><span>Hours</span><span>Tue–Fri 10–18 · Sat 10–14<br>Workshop visits by appointment</span></div>
<div class="row"><span>Phone</span><span>+45 35 55 04 12</span></div>
<div class="row"><span>Email</span><span>hej@lumencycles.dk</span></div>
<div class="row"><span>Company</span><span>Lumen Cycles ApS · CVR 38 42 91 06</span></div>
</div>
<div class="form-card reveal" data-d="1" id="formWrap">
<form class="form" id="buildForm" novalidate>
<div class="field">
<label for="name">Your name</label>
<input type="text" id="name" name="name" autocomplete="name" required placeholder="Mads Højgaard">
</div>
<div class="field">
<label for="email">Email</label>
<input type="email" id="email" name="email" autocomplete="email" required placeholder="mads@example.dk">
</div>
<div class="field">
<label for="city">City</label>
<input type="text" id="city" name="city" autocomplete="address-level2" placeholder="København N">
</div>
<div class="field">
<label for="bike">Bike you're after</label>
<select id="bike" name="bike">
<option>The Hauler</option>
<option>The School Run</option>
<option>The Long Haul</option>
<option>Not sure yet</option>
</select>
</div>
<div class="field full">
<label for="message">What do you need to carry?</label>
<textarea id="message" name="message" placeholder="Two children (6 and 9), a dog, and the weekly shop. The street is cobbled and the cellar door is 78 cm wide."></textarea>
</div>
<button class="btn btn-primary btn-arrow" type="submit">Request a build slot</button>
<p class="form-note">
We are four people. Replies within two working days, usually sooner.
Your details stay in the workshop — we never pass them on.
</p>
</form>
</div>
</div>
</section>
</main>
<footer class="site-footer">
<div class="footer-inner">
<div class="footer-top">
<div class="footer-brand">
<div class="word">
<svg viewBox="0 0 32 32" aria-hidden="true">
<circle cx="16" cy="16" r="13.4" fill="none" stroke="currentColor" stroke-width="2.6"/>
<path d="M16 2.6 A13.4 13.4 0 0 1 29.4 16 L16 16 Z" fill="currentColor"/>
</svg>
Lumen
</div>
<p>Custom cargo bikes, brazed by hand in Nørrebro since 2014. Twelve builds at a time, no more.</p>
</div>
<div class="footer-col">
<h4>Bikes</h4>
<ul>
<li><a href="#bikes">The Hauler</a></li>
<li><a href="#bikes">The School Run</a></li>
<li><a href="#bikes">The Long Haul</a></li>
<li><a href="#configure">Configure a build</a></li>
</ul>
</div>
<div class="footer-col">
<h4>Workshop</h4>
<ul>
<li><a href="#workshop">How we build</a></li>
<li><a href="#process">The process</a></li>
<li><a href="#faq">Questions</a></li>
<li><a href="#contact">Book a visit</a></li>
</ul>
</div>
<div class="footer-col">
<h4>Find us</h4>
<ul>
<li>Jægersborggade 34<br>2200 København N</li>
<li><a href="tel:+4535550412">+45 35 55 04 12</a></li>
<li><a href="mailto:hej@lumencycles.dk">hej@lumencycles.dk</a></li>
<li>Tue–Fri 10–18 · Sat 10–14</li>
</ul>
</div>
</div>
<div class="footer-bottom">
<span>© 2025 Lumen Cycles ApS · CVR 38 42 91 06</span>
<span>Made in Nørrebro, Denmark</span>
</div>
</div>
</footer>
<script>
(function () {
'use strict';
/* ---------- sticky header ---------- */
var header = document.getElementById('siteHeader');
function onScroll() {
header.classList.toggle('scrolled', window.scrollY > 8);
}
onScroll();
window.addEventListener('scroll', onScroll, { passive: true });
/* ---------- reveal on scroll ---------- */
var reveals = document.querySelectorAll('.reveal');
if ('IntersectionObserver' in window) {
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
entry.target.classList.add('in');
io.unobserve(entry.target);
}
});
}, { threshold: 0.1, rootMargin: '0px 0px -60px 0px' });
reveals.forEach(function (el) { io.observe(el); });
} else {
reveals.forEach(function (el) { el.classList.add('in'); });
}
/* ---------- configurator ---------- */
var BASE_PRICE = 4000;
var BASE_WEEKS = 12;
var totalEl = document.getElementById('total');
var weeksEl = document.getElementById('weeks');
var listEl = document.getElementById('sumList');
var options = Array.prototype.slice.call(document.querySelectorAll('.opt input'));
function formatPrice(n) {
return String(Math.round(n)).replace(/\B(?=(\d{3})+(?!\d))/g, '.') + ' \u20AC';
}
function updateEstimate() {
var total = BASE_PRICE;
var weeks = BASE_WEEKS;
var items = ['Base build \u2014 The Hauler'];
options.forEach(function (input) {
if (input.checked) {
total += Number(input.getAttribute('data-price')) || 0;
weeks += Number(input.getAttribute('data-weeks')) || 0;
items.push(input.getAttribute('data-label'));
}
});
totalEl.textContent = formatPrice(total);
weeksEl.textContent = weeks;
listEl.innerHTML = '';
items.forEach(function (text) {
var li = document.createElement('li');
li.textContent = text;
listEl.appendChild(li);
});
totalEl.classList.remove('pop');
void totalEl.offsetWidth;
totalEl.classList.add('pop');
}
options.forEach(function (input) {
input.addEventListener('change', updateEstimate);
});
updateEstimate();
/* ---------- contact form ---------- */
var form = document.getElementById('buildForm');
var formWrap = document.getElementById('formWrap');
form.addEventListener('submit', function (event) {
event.preventDefault();
var nameField = document.getElementById('name');
var emailField = document.getElementById('email');
if (!nameField.value.trim()) {
nameField.focus();
return;
}
if (!emailField.value.trim() || emailField.value.indexOf('@') === -1) {
emailField.focus();
return;
}
var firstName = nameField.value.trim().split(' ')[0];
var bike = document.getElementById('bike').value;
formWrap.innerHTML =
'<div class="form-success">' +
'<div class="tick" aria-hidden="true">\u2713</div>' +
'<h3>Tak, ' + escapeHtml(firstName) + '.</h3>' +
'<p>Your request for <strong>' + escapeHtml(bike) + '</strong> is in the workshop inbox. ' +
'One of the four of us will reply within two working days \u2014 usually the same week.</p>' +
'<p style="font-size:13.5px;color:var(--ink-3)">If it is urgent, call the bench directly on +45 35 55 04 12, Tuesday to Friday.</p>' +
'<a class="btn btn-ghost" href="#configure" style="margin-top:18px">Back to the configurator</a>' +
'</div>';
formWrap.setAttribute('role', 'status');
formWrap.setAttribute('aria-live', 'polite');
});
function escapeHtml(str) {
return String(str).replace(/[&<>"']/g, function (ch) {
return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[ch];
});
}
})();
</script>
</body>
</html>

