hypeorcode
Network offOpen alone (new tab)

Run details

Model
mistralai/mistral-large-4-0
Route
OpenRouter API, one user message
Reasoning
provider default, 99,773 reasoning tokens
Limits
60 min · 143,540 output tokens
Date
21 Oct 2026
Time
23 min 41 s
Size
84 kB
Tokens
91 in, 127,988 out
List price
$0.54 ($1.36 in / $4.18 out per M)
Paid
$0.54

Raw files: output · meta.json

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

84 kB 1,298 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 — Custom cargo bikes, built to order in Copenhagen</title>
<meta name="description" content="Lumen Cycles builds custom cargo bikes to order in Copenhagen. Configure yours from €3.950 — one builder, six to eight weeks, ten-year frame guarantee.">
<meta name="theme-color" content="#F4F1E9">
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' fill='%231B1813'/%3E%3Ccircle cx='10' cy='21' r='5' fill='none' stroke='%23F4F1E9' stroke-width='2.4'/%3E%3Ccircle cx='23' cy='21' r='5' fill='none' stroke='%23F4F1E9' stroke-width='2.4'/%3E%3Cpath d='M10 21 L15 11 H20 L23 21' fill='none' stroke='%23A9712B' stroke-width='2.4'/%3E%3C/svg%3E">
<script>document.documentElement.classList.add('js');</script>
<style>
  :root{
    --paper:#F4F1E9; --paper-2:#ECE8DC; --ink:#1B1813;
    --ink-60:rgba(27,24,19,.64); --ink-40:rgba(27,24,19,.45);
    --line:rgba(27,24,19,.16); --line-strong:rgba(27,24,19,.36);
    --brass:#A9712B; --red:#B4472E;
    --dark:#161310;
    --sans:"Helvetica Neue",Helvetica,Arial,"Segoe UI",sans-serif;
    --mono:ui-monospace,"SF Mono","Cascadia Mono",Menlo,Consolas,"Liberation Mono",monospace;
    --frame:#A9ABA3; --frame-contrast:#1B1813; --box-fill:#D8B98C;
  }
  *{box-sizing:border-box;margin:0;padding:0}
  html{scroll-behavior:smooth;scroll-padding-top:84px;-webkit-text-size-adjust:100%}
  body{background:var(--paper);color:var(--ink);font:400 16px/1.6 var(--sans);-webkit-font-smoothing:antialiased;overflow-x:hidden}
  body::after{content:"";position:fixed;inset:0;z-index:2000;pointer-events:none;opacity:.05;
    background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E")}
  body::before{content:"";position:fixed;top:0;left:0;right:0;height:3px;background:var(--brass);z-index:95}
  ::selection{background:var(--brass);color:var(--paper)}
  :focus-visible{outline:2px solid var(--brass);outline-offset:2px}
  button{font-family:inherit}
  .wrap{max-width:1300px;margin:0 auto;padding:0 clamp(20px,4vw,56px)}
  .mono{font-family:var(--mono)}
  .label{font:500 11px/1.5 var(--mono);letter-spacing:.18em;text-transform:uppercase;color:var(--ink-60)}
  .skip{position:absolute;left:-9999px}
  .skip:focus{left:12px;top:10px;z-index:300;background:var(--ink);color:var(--paper);padding:10px 14px}

  /* ---------- buttons ---------- */
  .btn{display:inline-flex;align-items:center;gap:10px;background:var(--ink);color:var(--paper);border:1px solid var(--ink);
    font:600 14.5px/1 var(--sans);letter-spacing:.01em;padding:16px 24px;cursor:pointer;text-decoration:none;border-radius:2px;
    transition:background .18s,color .18s,border-color .18s}
  .btn:hover{background:#000}
  .btn svg{transition:transform .18s}
  .btn:hover svg{transform:translateX(4px)}
  .btn.ghost{background:transparent;color:var(--ink)}
  .btn.ghost:hover{background:var(--ink);color:var(--paper)}
  .btn.sm{padding:11px 16px;font-size:13px}
  .btn.full{width:100%;justify-content:center}

  /* ---------- nav ---------- */
  .nav{position:sticky;top:0;z-index:80;background:rgba(244,241,233,.9);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);
    border-bottom:1px solid var(--line);transition:box-shadow .25s}
  .nav.scrolled{box-shadow:0 10px 28px rgba(27,24,19,.07)}
  .nav-in{display:flex;align-items:center;justify-content:space-between;gap:24px;height:66px}
  .brand{display:flex;align-items:center;gap:11px;text-decoration:none;color:var(--ink);font-weight:700;font-size:14px;letter-spacing:.22em}
  .brand svg{width:30px;height:auto;color:var(--ink);flex:none}
  .nav-links{display:flex;gap:26px}
  .nav-links a{font:500 11.5px var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--ink-60);text-decoration:none;padding:6px 0;border-bottom:1px solid transparent}
  .nav-links a:hover{color:var(--ink);border-color:var(--brass)}
  .nav-right{display:flex;align-items:center;gap:18px}
  .nav-tel{font:500 12px var(--mono);color:var(--ink-60);text-decoration:none}
  .nav-tel:hover{color:var(--ink)}
  .menu-btn{display:none;width:40px;height:40px;border:1px solid var(--line-strong);background:none;border-radius:2px;cursor:pointer;place-items:center;color:var(--ink)}

  /* ---------- hero ---------- */
  .hero{padding:clamp(52px,8vh,92px) 0 clamp(40px,6vw,72px);position:relative}
  .eyebrow{display:flex;align-items:center;gap:12px}
  .sq{width:9px;height:9px;background:var(--brass);display:inline-block;flex:none}
  h1{font-size:clamp(2.9rem,7vw,6.2rem);line-height:.99;letter-spacing:-.035em;font-weight:700;margin:26px 0 42px}
  .hline{display:block;overflow:hidden;padding-bottom:.07em;margin-bottom:-.07em}
  .hline>span{display:inline-block;transform:translateY(112%);animation:rise .85s cubic-bezier(.16,.8,.24,1) forwards}
  .hline:nth-child(2)>span{animation-delay:.12s}
  @keyframes rise{to{transform:translateY(0)}}
  .accent{color:var(--brass);font-style:italic}
  .hero-grid{display:grid;grid-template-columns:minmax(320px,5fr) 7fr;gap:clamp(28px,4vw,72px);align-items:end}
  .lede{font-size:clamp(16px,1.35vw,18px);line-height:1.68;color:var(--ink-60);max-width:44ch}
  .cta-row{display:flex;flex-wrap:wrap;gap:14px;margin:30px 0 26px}
  .trust{list-style:none;display:flex;flex-wrap:wrap;gap:8px 24px;padding-top:16px;border-top:1px solid var(--line);
    font:500 10.5px var(--mono);letter-spacing:.12em;text-transform:uppercase;color:var(--ink-60)}
  .trust li{display:flex;align-items:center;gap:9px}
  .trust li::before{content:"";width:5px;height:5px;background:var(--brass);transform:rotate(45deg)}
  .hero .eyebrow,.hero .lede,.cta-row,.trust,.hero .stage{opacity:0;transform:translateY(14px);animation:fadeUp .7s ease forwards}
  .hero .eyebrow{animation-delay:.05s}.hero .lede{animation-delay:.16s}.cta-row{animation-delay:.22s}
  .trust{animation-delay:.3s}.hero .stage{animation-delay:.24s}
  @keyframes fadeUp{to{opacity:1;transform:none}}
  .coords{position:absolute;right:16px;top:52%;writing-mode:vertical-rl;font:500 10px var(--mono);letter-spacing:.32em;text-transform:uppercase;color:var(--ink-40)}

  /* ---------- stage (drawing frame) ---------- */
  .stage{position:relative;border:1px solid var(--line-strong);padding:30px 16px 8px;margin:0;
    background:linear-gradient(rgba(27,24,19,.04) 1px,transparent 1px) 0 0/100% 44px,
               linear-gradient(90deg,rgba(27,24,19,.04) 1px,transparent 1px) 0 0/44px 100%,
               var(--paper)}
  .stage::before{content:"";position:absolute;inset:7px;border:1px solid var(--line);pointer-events:none}
  .stage-tag{position:absolute;top:-8px;left:18px;background:var(--paper);padding:0 10px;font:500 10px var(--mono);letter-spacing:.16em;text-transform:uppercase;color:var(--ink-60)}
  .stage svg{display:block;width:100%;height:auto}
  .fig{display:flex;justify-content:space-between;gap:12px;border-top:1px solid var(--line);margin-top:8px;padding:10px 2px 4px;
    font:500 10px var(--mono);letter-spacing:.12em;text-transform:uppercase;color:var(--ink-40)}

  /* ---------- bike drawing ---------- */
  .bike .frame{stroke:var(--frame);transition:stroke .5s}
  .bike .frame-fill{fill:var(--frame);transition:fill .5s}
  .bike .box-fill{fill:var(--box-fill);transition:fill .5s}
  .bike text{font-family:var(--mono)}
  .bike .engrave{font-size:12px;letter-spacing:1.5px;fill:var(--frame-contrast);transition:fill .5s}
  .bike .brand{font-size:10.5px;letter-spacing:2.5px;fill:var(--frame-contrast);opacity:.85;transition:fill .5s}
  .bike .grain,.bike .epp-dots{opacity:0;transition:opacity .45s}
  body[data-box="ply"] .bike .grain{opacity:1}
  body[data-box="epp"] .bike .epp-dots{opacity:1}
  .bike .canopy,.bike .h1,.bike .h2{opacity:0;transition:opacity .5s}
  .bike.cover-on .canopy{opacity:1}
  .bike.seats-1 .h1,.bike.seats-2 .h1{opacity:1}
  .bike.seats-2 .h2{opacity:1}
  .bike .beam,.bike .lamp-glow,.bike .rear-glow{opacity:0;transition:opacity .6s}
  .bike.lights-on .beam{opacity:1}
  .bike.lights-on .lamp-glow{opacity:.3}
  .bike.lights-on .rear-glow{opacity:.5;animation:pulse 2.4s ease-in-out 1.2s infinite}
  .bike .dynamo{fill:#1B1813;transition:fill .5s}
  .bike.lights-on .dynamo{fill:var(--brass)}
  .bike .anno polyline{fill:none;stroke:rgba(27,24,19,.45);stroke-width:1.3}
  .bike .adot{fill:var(--brass)}
  .bike .anno text{font-size:12.5px;fill:rgba(27,24,19,.78);letter-spacing:.03em}
  .bike .anno-lights{display:none}
  .bike.lights-on .anno-lights{display:block}
  .bike.go .draw{stroke-dasharray:1;stroke-dashoffset:1;animation:drawIn .9s cubic-bezier(.55,.06,.25,1) forwards}
  .bike.go .pop{opacity:0;animation:fadeIn .55s ease forwards}
  .bike.go .popfill{fill-opacity:0;animation:popFill .6s ease forwards}
  .bike.go .anno{opacity:0;animation:fadeIn .8s ease forwards}
  @keyframes drawIn{to{stroke-dashoffset:0}}
  @keyframes fadeIn{to{opacity:1}}
  @keyframes popFill{to{fill-opacity:1}}
  @keyframes pulse{0%,100%{opacity:.3}50%{opacity:.8}}

  /* ---------- marquee ---------- */
  .marquee{background:var(--ink);color:var(--paper);overflow:hidden}
  .marquee-track{display:flex;align-items:center;width:max-content;padding:13px 0;animation:mq 38s linear infinite}
  .marquee:hover .marquee-track{animation-play-state:paused}
  .marquee-track span{font:500 11.5px var(--mono);letter-spacing:.18em;text-transform:uppercase;white-space:nowrap}
  .marquee-track i{width:5px;height:5px;background:var(--brass);transform:rotate(45deg);margin:0 30px;flex:none}
  @keyframes mq{to{transform:translateX(-50%)}}

  /* ---------- sections ---------- */
  .sec{padding:clamp(76px,10vw,136px) 0}
  .sec-head{display:flex;align-items:center;gap:18px;border-top:1px solid var(--ink);padding-top:16px;margin-bottom:clamp(30px,4.5vw,54px)}
  .sec-num{font:700 12px var(--mono);color:var(--brass);letter-spacing:.1em}
  .sec-rule{flex:1;height:1px;background:var(--line)}
  .sec-label{font:500 11px var(--mono);letter-spacing:.2em;text-transform:uppercase;color:var(--ink-60)}
  h2{font-size:clamp(2rem,4.4vw,3.7rem);line-height:1.04;letter-spacing:-.03em;font-weight:700;max-width:24ch;text-wrap:balance}
  .sec-sub{margin-top:18px;font-size:16.5px;color:var(--ink-60);max-width:58ch}
  .js [data-reveal]{opacity:0;transform:translateY(24px);transition:opacity .7s ease,transform .75s cubic-bezier(.2,.7,.2,1)}
  .js [data-reveal].in{opacity:1;transform:none}

  /* ---------- why ---------- */
  .why-grid{display:grid;grid-template-columns:7fr 4fr;gap:clamp(36px,6vw,110px);margin-top:clamp(30px,4vw,54px)}
  .why-grid p{font-size:16.5px;line-height:1.72;color:var(--ink-60);max-width:58ch}
  .why-grid p+p{margin-top:16px}
  .stat{display:flex;align-items:baseline;gap:16px;padding:16px 0;border-bottom:1px solid var(--line)}
  .stat:first-child{border-top:1px solid var(--line-strong)}
  .stat strong{font-size:clamp(28px,3vw,38px);font-weight:700;letter-spacing:-.02em;min-width:3.4ch}
  .stat span{font:500 10.5px var(--mono);letter-spacing:.12em;text-transform:uppercase;color:var(--ink-60);line-height:1.6}
  .receipt{margin-top:28px;border:1px solid var(--line-strong);background:var(--paper-2);padding:18px 18px 20px}
  .receipt .label{color:var(--brass);margin-bottom:12px}
  .rrow{display:flex;align-items:baseline;font:500 12.5px var(--mono);padding:7px 0;letter-spacing:.02em}
  .rrow i{flex:1;border-bottom:1px dotted var(--line-strong);margin:0 10px;transform:translateY(-3px)}
  .rrow.total{border-top:1px solid var(--ink);margin-top:6px;padding-top:11px;font-weight:700}
  .rnote{font:400 10.5px/1.7 var(--mono);color:var(--ink-40);padding-bottom:4px}

  /* ---------- models ---------- */
  .models{margin-top:clamp(36px,5vw,64px);border-bottom:1px solid var(--line)}
  .model{display:grid;grid-template-columns:230px 1fr 210px;gap:clamp(20px,3vw,48px);padding:30px 0;border-top:1px solid var(--line);transition:background .2s}
  .model:hover{background:rgba(27,24,19,.03)}
  .model-num{display:block;font:700 11px var(--mono);color:var(--brass);letter-spacing:.15em;margin-bottom:10px}
  .model h3{font-size:24px;letter-spacing:-.02em}
  .model-tag{display:block;font:500 10.5px var(--mono);letter-spacing:.16em;text-transform:uppercase;color:var(--ink-60);margin-top:6px}
  .model-desc p{font-size:15px;line-height:1.65;color:var(--ink-60);max-width:54ch}
  .model-specs{list-style:none;display:flex;flex-wrap:wrap;margin-top:14px;font:500 11px var(--mono);letter-spacing:.06em;text-transform:uppercase}
  .model-specs li+li::before{content:"·";color:var(--brass);margin:0 9px}
  .model-buy{display:flex;flex-direction:column;align-items:flex-end;gap:14px;text-align:right}
  .model-price{font-size:24px;font-weight:700;letter-spacing:-.02em}
  .platform{margin-top:clamp(48px,6vw,80px)}
  .platform>.label{margin-bottom:10px}
  .platform dl{display:grid;grid-template-columns:1fr 1fr;column-gap:clamp(32px,5vw,80px)}
  .prow{display:grid;grid-template-columns:170px 1fr;gap:16px;padding:12px 0;border-top:1px solid var(--line)}
  .prow dt{font:500 10.5px var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--ink-60);padding-top:3px}
  .prow dd{font-size:14.5px;line-height:1.55}

  /* ---------- process ---------- */
  .proc-grid{display:grid;grid-template-columns:5fr 6fr;gap:clamp(36px,6vw,100px);margin-top:clamp(30px,4vw,54px)}
  .proc-left{position:sticky;top:100px;align-self:start}
  .proc-left p{margin-top:18px;color:var(--ink-60);font-size:16px;line-height:1.7;max-width:44ch}
  .mono-note{margin-top:22px;font:500 11px/1.8 var(--mono);letter-spacing:.06em;text-transform:uppercase;color:var(--ink-40);border-left:2px solid var(--brass);padding-left:14px;max-width:38ch}
  .steps{position:relative;padding-left:34px}
  .steps::before{content:"";position:absolute;left:5px;top:8px;bottom:8px;width:1px;background:var(--line)}
  .steps-fill{position:absolute;left:5px;top:8px;width:1px;height:0;background:var(--brass)}
  .steps ol{list-style:none}
  .steps li{position:relative;padding:26px 0;border-bottom:1px solid var(--line)}
  .steps li:last-child{border-bottom:0}
  .step-top{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}
  .step-num{font:700 12px var(--mono);color:var(--brass);letter-spacing:.15em}
  .step-icon{width:40px;height:40px;border:1px solid var(--line-strong);border-radius:2px;display:grid;place-items:center;color:var(--ink)}
  .step-icon svg{width:19px;height:19px}
  .steps h3{font-size:19px;letter-spacing:-.01em;margin-bottom:8px}
  .steps p{font-size:14.5px;line-height:1.65;color:var(--ink-60);max-width:54ch}

  /* ---------- configurator ---------- */
  .config{background:var(--paper-2);border-block:1px solid var(--line)}
  .config-grid{display:grid;grid-template-columns:7fr 5fr;gap:clamp(28px,4vw,60px);margin-top:clamp(30px,4vw,54px);align-items:start}
  .config-stage{position:sticky;top:92px}
  .price-line{display:flex;justify-content:space-between;align-items:baseline;padding-bottom:16px;border-bottom:1px solid var(--ink);margin-bottom:24px}
  .price-line strong{font-size:clamp(26px,2.6vw,32px);font-weight:700;letter-spacing:-.02em;font-variant-numeric:tabular-nums}
  .opt-group{margin-bottom:24px}
  .opt-head{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:10px}
  .hint{font:500 10px var(--mono);letter-spacing:.16em;text-transform:uppercase;color:var(--ink-40)}
  .opt{display:grid;grid-template-columns:18px 1fr auto;gap:12px;align-items:center;border:1px solid var(--line);border-radius:2px;
    padding:11px 14px;margin-bottom:8px;cursor:pointer;transition:border-color .15s,background .15s}
  .opt:hover{border-color:var(--line-strong)}
  .opt input{position:absolute;opacity:0;pointer-events:none}
  .tick{width:16px;height:16px;border:1px solid var(--line-strong);border-radius:50%;transition:.15s}
  .opt-body strong{display:flex;align-items:center;gap:8px;font-size:14.5px;font-weight:600;letter-spacing:-.01em}
  .opt-body small{display:block;font-size:12px;color:var(--ink-60);margin-top:2px;line-height:1.45}
  .opt-price{font:500 12px var(--mono);color:var(--ink-60);white-space:nowrap}
  .opt.sel{border-color:var(--ink);background:rgba(27,24,19,.045)}
  .opt.sel .tick{border-color:var(--ink);background:var(--ink);box-shadow:inset 0 0 0 3px var(--paper-2)}
  .swatch{width:13px;height:13px;border-radius:50%;border:1px solid rgba(27,24,19,.35);flex:none}
  .opt.stepper{grid-template-columns:1fr auto;cursor:default}
  .opt.stepper .tick{display:none}
  .stepper-ctl{display:flex;align-items:center;gap:3px}
  .stepper-ctl button{width:30px;height:30px;border:1px solid var(--line-strong);background:transparent;border-radius:2px;cursor:pointer;font:600 15px var(--mono);color:var(--ink);line-height:1}
  .stepper-ctl button:hover:not(:disabled){border-color:var(--ink)}
  .stepper-ctl button:disabled{opacity:.3;cursor:default}
  .stepper-ctl output{min-width:26px;text-align:center;font:600 14px var(--mono)}
  .engrave-input{width:100%;border:1px solid var(--line-strong);background:transparent;border-radius:2px;padding:13px 14px;
    font:600 15px var(--mono);letter-spacing:.12em;text-transform:uppercase;color:var(--ink)}
  .engrave-input:focus{outline:none;border-color:var(--ink)}
  .engrave-input::placeholder{color:var(--ink-40);font-weight:500;text-transform:none;letter-spacing:.02em}
  .hint-line{font:400 11px/1.7 var(--mono);color:var(--ink-40);margin-top:8px}
  .summary{border:1px solid var(--line-strong);background:var(--paper);padding:14px 16px;margin-bottom:14px;
    font:500 11px/1.9 var(--mono);letter-spacing:.05em;text-transform:uppercase;color:var(--ink-60);overflow-wrap:anywhere}
  .delivery{font:500 11.5px/1.8 var(--mono);border-left:2px solid var(--brass);padding-left:12px;margin-bottom:20px}
  .fine{font-size:12px;color:var(--ink-40);margin-top:12px;line-height:1.6}
  .row-between{display:flex;justify-content:space-between;margin-top:16px}
  .link-btn{background:none;border:none;border-bottom:1px solid var(--line-strong);padding:6px 2px;cursor:pointer;
    font:500 11px var(--mono);letter-spacing:.12em;text-transform:uppercase;color:var(--ink-60)}
  .link-btn:hover{color:var(--ink);border-color:var(--brass)}

  /* ---------- dark sections ---------- */
  .dark{background:var(--dark);color:var(--paper)}
  .dark .sec-head{border-top-color:rgba(244,241,233,.85)}
  .dark .sec-rule{background:rgba(244,241,233,.18)}
  .dark .sec-label{color:rgba(244,241,233,.55)}
  .dark .label,.dark .hint{color:rgba(244,241,233,.5)}
  .workshop-grid{display:grid;grid-template-columns:6fr 5fr;gap:clamp(36px,6vw,100px);margin-top:clamp(30px,4vw,54px)}
  .workshop-grid p{font-size:16px;line-height:1.75;color:rgba(244,241,233,.72)}
  .workshop-grid p+p{margin-top:14px}
  .ws-lines{font-size:15px;line-height:1.9;color:rgba(244,241,233,.78)}
  .workshop-cta{display:flex;flex-wrap:wrap;gap:14px;margin-top:28px}
  .dark .btn{background:var(--paper);color:var(--ink);border-color:var(--paper)}
  .dark .btn:hover{background:#fff}
  .dark .btn.ghost{background:transparent;color:var(--paper);border-color:rgba(244,241,233,.45)}
  .dark .btn.ghost:hover{background:var(--paper);color:var(--ink)}
  .wstats{display:grid;grid-template-columns:repeat(4,1fr);margin-top:clamp(40px,5vw,64px);border-top:1px solid rgba(244,241,233,.16)}
  .wstat{padding:22px 18px 6px 0}
  .wstat+.wstat{border-left:1px solid rgba(244,241,233,.14);padding-left:20px}
  .wstat strong{display:block;font-size:clamp(26px,2.6vw,34px);font-weight:700;letter-spacing:-.02em}
  .wstat span{display:block;margin-top:8px;font:500 10.5px var(--mono);letter-spacing:.14em;text-transform:uppercase;color:rgba(244,241,233,.5);line-height:1.6}
  .slots{margin-top:clamp(40px,5vw,64px);border-top:1px solid rgba(244,241,233,.16);padding-top:26px}
  .slots-head{display:flex;justify-content:space-between;align-items:baseline;flex-wrap:wrap;gap:10px;margin-bottom:18px}
  .slot-row{display:grid;grid-template-columns:repeat(5,1fr);gap:10px}
  .slot{border:1px solid rgba(244,241,233,.18);background:transparent;color:var(--paper);border-radius:2px;padding:14px 14px 12px;
    cursor:pointer;text-align:left;font-family:inherit;transition:border-color .15s,background .15s}
  .slot:hover:not(:disabled){border-color:var(--brass)}
  .slot:disabled{opacity:.4;cursor:not-allowed}
  .slot.sel{border-color:var(--brass);background:rgba(169,113,43,.14)}
  .slot-month{display:block;font:600 11px var(--mono);letter-spacing:.14em;text-transform:uppercase;margin-bottom:10px}
  .slot-dots{display:flex;gap:6px;margin-bottom:9px}
  .slot-dots i{width:9px;height:9px;border-radius:50%;border:1px solid rgba(244,241,233,.35)}
  .slot-dots i.b{background:rgba(244,241,233,.28);border-color:transparent}
  .slot-dots i.f{border-color:var(--brass)}
  .slot-left{font:500 9.5px var(--mono);letter-spacing:.1em;text-transform:uppercase;color:rgba(244,241,233,.5)}

  /* ---------- faq ---------- */
  .faq-wrap{max-width:880px;margin-top:clamp(28px,4vw,48px)}
  .faq-item{border-top:1px solid var(--line)}
  .faq-item:last-child{border-bottom:1px solid var(--line)}
  .faq-q{width:100%;display:flex;justify-content:space-between;align-items:center;gap:20px;padding:22px 2px;background:none;border:0;
    cursor:pointer;text-align:left;font:600 17px/1.4 var(--sans);letter-spacing:-.01em;color:var(--ink)}
  .faq-q svg{flex:none;width:16px;height:16px;color:var(--ink-60);transition:transform .3s}
  .faq-item.open .faq-q svg{transform:rotate(180deg)}
  .faq-a{max-height:0;overflow:hidden;transition:max-height .38s cubic-bezier(.4,0,.2,1)}
  .faq-a p{padding:0 2px 24px;font-size:15px;line-height:1.7;color:var(--ink-60);max-width:64ch}

  /* ---------- footer ---------- */
  footer{background:var(--dark);color:var(--paper);padding:clamp(56px,7vw,90px) 0 34px}
  .foot-word{font-size:clamp(64px,13.5vw,196px);font-weight:700;letter-spacing:-.02em;line-height:.85;color:transparent;
    -webkit-text-stroke:1px rgba(244,241,233,.28);user-select:none;margin-bottom:clamp(36px,5vw,64px)}
  .foot-grid{display:grid;grid-template-columns:2fr 1fr 1fr 1fr;gap:32px;padding-top:34px;border-top:1px solid rgba(244,241,233,.14)}
  .foot-grid h4{font:500 10.5px var(--mono);letter-spacing:.2em;text-transform:uppercase;color:rgba(244,241,233,.5);margin-bottom:14px}
  .foot-grid p,.foot-grid a{font-size:14px;line-height:1.9;color:rgba(244,241,233,.78);text-decoration:none;display:block}
  .foot-grid a:hover{color:#CE9445}
  .foot-bottom{display:flex;justify-content:space-between;flex-wrap:wrap;gap:10px;margin-top:44px;padding-top:18px;border-top:1px solid rgba(244,241,233,.14);
    font:500 10.5px var(--mono);letter-spacing:.12em;text-transform:uppercase;color:rgba(244,241,233,.45)}

  /* ---------- minibar / toasts / modal ---------- */
  .minibar{position:fixed;left:50%;bottom:18px;transform:translate(-50%,150%);display:flex;align-items:center;gap:16px;z-index:70;
    background:var(--ink);color:var(--paper);border:1px solid rgba(244,241,233,.18);border-radius:3px;padding:10px 12px 10px 20px;
    box-shadow:0 14px 34px rgba(20,16,10,.28);transition:transform .4s cubic-bezier(.2,.8,.2,1)}
  .minibar.show{transform:translate(-50%,0)}
  .minibar .lbl{font:500 9.5px var(--mono);letter-spacing:.18em;text-transform:uppercase;color:rgba(244,241,233,.55)}
  .minibar strong{font-size:17px;font-weight:700;font-variant-numeric:tabular-nums}
  .minibar .sep{width:1px;height:18px;background:rgba(244,241,233,.2)}
  .minibar .btn{padding:11px 14px;font-size:12.5px}
  .toasts{position:fixed;left:22px;bottom:22px;z-index:120;display:flex;flex-direction:column;gap:8px}
  .toast{background:var(--ink);color:var(--paper);border-left:3px solid var(--brass);padding:13px 18px;max-width:330px;
    font:500 12px/1.6 var(--mono);letter-spacing:.03em;opacity:0;transform:translateY(10px);transition:.3s;box-shadow:0 10px 26px rgba(20,16,10,.22)}
  .toast.show{opacity:1;transform:none}
  .modal{position:fixed;inset:0;z-index:110;display:none;align-items:center;justify-content:center;padding:20px}
  .modal.open{display:flex}
  .modal-back{position:absolute;inset:0;background:rgba(18,15,11,.62);-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}
  .modal-panel{position:relative;background:var(--paper);width:min(560px,100%);max-height:calc(100vh - 40px);overflow:auto;
    border:1px solid var(--ink);border-radius:3px;padding:clamp(24px,4vw,36px);animation:panelIn .35s cubic-bezier(.2,.8,.2,1)}
  @keyframes panelIn{from{opacity:0;transform:translateY(16px)}}
  .modal-x{position:absolute;top:14px;right:14px;width:36px;height:36px;border:1px solid var(--line-strong);background:none;border-radius:2px;
    display:grid;place-items:center;cursor:pointer;color:var(--ink)}
  .modal-x:hover{border-color:var(--ink)}
  .modal h3{font-size:26px;letter-spacing:-.02em;margin:10px 0 8px}
  .m-sub{font-size:14.5px;color:var(--ink-60);line-height:1.6}
  .m-build{margin:18px 0;border:1px solid var(--line-strong);background:var(--paper-2);padding:12px 14px;
    font:500 10.5px/1.9 var(--mono);letter-spacing:.05em;text-transform:uppercase;color:var(--ink-60);overflow-wrap:anywhere}
  .field{margin-bottom:16px}
  .field label{display:block;margin-bottom:7px}
  .opt-hint{text-transform:none;letter-spacing:.02em;color:var(--ink-40);font-weight:500}
  .field input,.field select,.field textarea{width:100%;border:1px solid var(--line-strong);background:transparent;border-radius:2px;
    padding:12px 13px;font:400 15px var(--sans);color:var(--ink);transition:border-color .15s}
  .field textarea{resize:vertical}
  .field input:focus,.field select:focus,.field textarea:focus{outline:none;border-color:var(--ink)}
  .field.err input{border-color:var(--red)}
  .ferr{display:none;font:500 10.5px var(--mono);color:var(--red);margin-top:6px;letter-spacing:.04em}
  .field.err .ferr{display:block}
  .frow.two{display:grid;grid-template-columns:1fr 1fr;gap:14px}
  .done-check{width:54px;height:54px;color:var(--brass);margin-bottom:18px}
  .done-check circle,.done-check path{stroke-dasharray:1;stroke-dashoffset:1;animation:drawIn .7s ease forwards}
  .done-check path{animation-duration:.45s;animation-delay:.4s}
  .ref{display:inline-block;border:1px solid var(--line-strong);padding:8px 12px;font:600 12px var(--mono);letter-spacing:.1em;margin:16px 0 22px}

  /* ---------- responsive ---------- */
  @media (max-width:1080px){
    .hero-grid,.why-grid,.proc-grid,.config-grid,.workshop-grid{grid-template-columns:1fr}
    .proc-left,.config-stage{position:static}
    .coords{display:none}
    .model{grid-template-columns:1fr;gap:16px}
    .model-buy{flex-direction:row;align-items:center;justify-content:space-between;text-align:left}
    .platform dl{grid-template-columns:1fr}
    .wstats{grid-template-columns:1fr 1fr;border-top:0}
    .wstat{border-top:1px solid rgba(244,241,233,.16);padding:16px 0}
    .wstat+.wstat{border-left:0;padding-left:0}
    .slot-row{grid-template-columns:repeat(3,1fr)}
    .foot-grid{grid-template-columns:1fr 1fr}
    .nav-tel{display:none}
  }
  @media (max-width:900px){
    .nav-links{position:absolute;top:66px;left:0;right:0;display:none;flex-direction:column;gap:2px;
      background:var(--paper);border-bottom:1px solid var(--line);padding:12px clamp(20px,4vw,56px) 20px}
    .nav-links.open{display:flex}
    .nav-links a{padding:11px 0;font-size:12.5px}
    .menu-btn{display:grid}
  }
  @media (max-width:720px){
    .bike .anno{display:none}
    .slot-row{grid-template-columns:1fr 1fr}
    .frow.two{grid-template-columns:1fr}
    .trust{flex-direction:column;gap:9px}
    .minibar{gap:12px;padding:9px 10px 9px 14px}
    .minibar .lbl,.minibar .sep{display:none}
    .toasts{left:14px;right:14px}
    .toast{max-width:none}
  }
  @media (prefers-reduced-motion:reduce){
    html{scroll-behavior:auto}
    .bike .draw,.bike.go .draw{stroke-dasharray:0;stroke-dashoffset:0;animation:none}
    .bike .pop,.bike.go .pop,.bike .anno,.bike.go .anno{opacity:1;animation:none}
    .bike .popfill,.bike.go .popfill{fill-opacity:1;animation:none}
    .hline>span{transform:none;animation:none}
    .hero .eyebrow,.hero .lede,.cta-row,.trust,.hero .stage{opacity:1;transform:none;animation:none}
    .js [data-reveal]{opacity:1;transform:none;transition:none}
    .marquee-track{animation:none}
    .bike.lights-on .rear-glow{animation:none}
    .done-check circle,.done-check path{stroke-dashoffset:0;animation:none}
  }
</style>
</head>
<body data-box="ply">
<a class="skip" href="#main">Skip to content</a>

<!-- ============ NAV ============ -->
<header class="nav" id="nav">
  <div class="wrap nav-in">
    <a class="brand" href="#top" aria-label="Lumen Cycles — home">
      <svg viewBox="0 0 32 30" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
        <circle cx="7.5" cy="21" r="5.5"/><circle cx="24.5" cy="21" r="5.5"/>
        <path d="M7.5 21 L13 10 H19.5 L24.5 21 M13 10 V7.5 M19.5 10 L22 7.5"/>
      </svg>
      <span>LUMEN&nbsp;CYCLES</span>
    </a>
    <nav class="nav-links" id="navLinks">
      <a href="#bike">The bike</a><a href="#process">Process</a><a href="#configurator">Configurator</a><a href="#workshop">Workshop</a><a href="#faq">FAQ</a>
    </nav>
    <div class="nav-right">
      <a class="nav-tel" href="tel:+4531442016">+45 31 44 20 16</a>
      <button class="btn sm" data-open-modal="visit">Book a fitting</button>
      <button class="menu-btn" id="menuBtn" aria-label="Open menu">
        <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M3 6h18M3 12h18M3 18h18"/></svg>
      </button>
    </div>
  </div>
</header>

<main id="main">
<!-- ============ HERO ============ -->
<section class="hero" id="top">
  <div class="wrap">
    <p class="eyebrow label"><i class="sq"></i>Custom cargo bikes · built to order · København</p>
    <h1>
      <span class="hline"><span>We don&rsquo;t keep stock.</span></span>
      <span class="hline"><span>We build <em class="accent">yours.</em></span></span>
    </h1>
    <div class="hero-grid">
      <div>
        <p class="lede">Lumen Cycles is a two-room workshop on Refshaleøen, Copenhagen. You configure your cargo bike below; a named builder cuts, brazes and builds it to order. Six to eight weeks later, you ride it home.</p>
        <div class="cta-row">
          <a class="btn" href="#configurator">Configure yours — from € 3.950
            <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 12h15M13 6l6 6-6 6"/></svg></a>
          <button class="btn ghost" data-open-modal="visit">Book a test ride</button>
        </div>
        <ul class="trust">
          <li>10-year frame guarantee</li>
          <li>Prices include 25% VAT</li>
          <li>Free service at 1,000 km</li>
        </ul>
      </div>
      <figure class="stage">
        <span class="stage-tag">Lumen One — side elevation · scale 1:8</span>
        <div data-bike="full"></div>
        <figcaption class="fig"><span data-fig></span><span>Sheet 01 / 01</span></figcaption>
      </figure>
    </div>
  </div>
  <span class="coords" aria-hidden="true">55.6889° N — 12.6069° E</span>
</section>

<!-- ============ MARQUEE ============ -->
<div class="marquee" aria-hidden="true">
  <div class="marquee-track" id="marqueeTrack">
    <span>Hand-built in København</span><i></i>
    <span>Est. 2016</span><i></i>
    <span>1,247 bikes delivered</span><i></i>
    <span>One builder per bike</span><i></i>
    <span>10-year frame guarantee</span><i></i>
    <span>6–8 week build time</span><i></i>
    <span>Prices include 25% Danish VAT</span><i></i>
    <span>Free 1,000 km service</span><i></i>
  </div>
</div>

<!-- ============ 01 WHY ============ -->
<section class="sec" id="why">
  <div class="wrap">
    <div class="sec-head" data-reveal><span class="sec-num">01</span><span class="sec-rule"></span><span class="sec-label">Why a cargo bike</span></div>
    <div class="why-grid">
      <div data-reveal>
        <h2>The second car is the one you don&rsquo;t need.</h2>
        <p style="margin-top:22px">One in four Copenhagen families with children under six owns a cargo bike. Not because they love cycling — because the arithmetic stops working any other way. A Lumen replaces the school run, the grocery run and 4,000–7,000 km of driving a year.</p>
        <p>It carries 90 litres and two children, locks to any lamppost, and parks exactly where you&rsquo;re going. In a city built for bikes, the bike you need is the one that carries your life.</p>
      </div>
      <aside data-reveal data-delay=".1s">
        <div class="stat"><strong>62%</strong><span>of Lumen owners sold a car within a year</span></div>
        <div class="stat"><strong>90 L</strong><span>lockable cargo volume, rain-proof</span></div>
        <div class="stat"><strong>200 kg</strong><span>max total load — rider, cargo, kids</span></div>
        <div class="receipt">
          <p class="label">The maths, honestly</p>
          <div class="rrow"><span>A second car, per year</span><i></i><span>€ 5.800</span></div>
          <p class="rnote">depreciation € 2.900 · insurance € 1.100 · parking € 840 · fuel € 960</p>
          <div class="rrow"><span>A Lumen One, once</span><i></i><span>€ 3.950</span></div>
          <div class="rrow total"><span>Break-even</span><i></i><span>8.2 months</span></div>
        </div>
      </aside>
    </div>
  </div>
</section>

<!-- ============ 02 THE BIKE ============ -->
<section class="sec" id="bike" style="padding-top:0">
  <div class="wrap">
    <div class="sec-head" data-reveal><span class="sec-num">02</span><span class="sec-rule"></span><span class="sec-label">The bike</span></div>
    <h2 data-reveal>One frame. Three honest builds. Endlessly yours.</h2>
    <p class="sec-sub" data-reveal>Every Lumen starts from the same fillet-brazed chromoly frame — the differences are the ones you touch every day. Load a build into the configurator, then make it yours.</p>

    <div class="models">
      <article class="model" data-reveal>
        <div><span class="model-num">01</span><h3>Lumen One</h3><span class="model-tag">The commuter</span></div>
        <div class="model-desc">
          <p>One rider, a week of groceries, a laptop and a small dog. The bike that replaces the second car — and pays for itself in about eight months.</p>
          <ul class="model-specs"><li>Raw steel</li><li>Marine ply box</li><li>Alfine 8 · belt</li><li>Pick-up tyres</li></ul>
        </div>
        <div class="model-buy"><span class="model-price">€ 3.950</span>
          <button class="btn ghost sm" data-configure="one">Configure this
            <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 12h15M13 6l6 6-6 6"/></svg></button></div>
      </article>
      <article class="model" data-reveal data-delay=".06s">
        <div><span class="model-num">02</span><h3>Lumen Family</h3><span class="model-tag">Two kids, one bike</span></div>
        <div class="model-desc">
          <p>Two child seats, a weather cover, dynamo lights and a drivetrain that doesn&rsquo;t care about hills. The school run, solved — in rain, January and uphill both ways.</p>
          <ul class="model-specs"><li>Copenhagen green</li><li>Marine ply box</li><li>Alfine 8 · belt</li><li>Weather cover + 2 seats</li></ul>
        </div>
        <div class="model-buy"><span class="model-price">€ 4.660</span>
          <button class="btn ghost sm" data-configure="family">Configure this
            <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 12h15M13 6l6 6-6 6"/></svg></button></div>
      </article>
      <article class="model" data-reveal data-delay=".12s">
        <div><span class="model-num">03</span><h3>Lumen Work</h3><span class="model-tag">Trades &amp; deliveries</span></div>
        <div class="model-desc">
          <p>Checker-plate box, Rohloff hub, studded winter tyres and lights that never quit. Built for people who carry tools, kegs and deadlines for a living.</p>
          <ul class="model-specs"><li>Signal black</li><li>Aluminium box</li><li>Rohloff 14 · belt</li><li>Dynamo + winter tyres</li></ul>
        </div>
        <div class="model-buy"><span class="model-price">€ 5.740</span>
          <button class="btn ghost sm" data-configure="work">Configure this
            <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 12h15M13 6l6 6-6 6"/></svg></button></div>
      </article>
    </div>

    <div class="platform" data-reveal>
      <p class="label">The platform — same bones on every build</p>
      <dl>
        <div class="prow"><dt>Frame</dt><dd>4130 chromoly, fillet-brazed and aligned in-house</dd></div>
        <div class="prow"><dt>Fork</dt><dd>Unicrown chromoly, 20&Prime;, steering linkage</dd></div>
        <div class="prow"><dt>Cargo box</dt><dd>90 L, lockable, quick-release — plywood, EPP or aluminium</dd></div>
        <div class="prow"><dt>Drivetrain</dt><dd>Gates CDN belt · Shimano Alfine 8 / 11 · Rohloff 14</dd></div>
        <div class="prow"><dt>Brakes</dt><dd>Hydraulic disc, 203 mm front / 180 mm rear</dd></div>
        <div class="prow"><dt>Wheels</dt><dd>Ryde Andra 40 rims · 20&Prime; front / 26&Prime; rear · Sapim spokes</dd></div>
        <div class="prow"><dt>Tyres</dt><dd>Schwalbe Pick-up 2.35&Prime; — studded winter set optional</dd></div>
        <div class="prow"><dt>Cockpit</dt><dd>720 mm bars · adjustable stem · saddle 780–1,050 mm</dd></div>
        <div class="prow"><dt>Weight</dt><dd>from 31.5 kg, build-dependent</dd></div>
        <div class="prow"><dt>Load &amp; guarantee</dt><dd>200 kg total · 10-year frame &amp; fork guarantee</dd></div>
      </dl>
    </div>
  </div>
</section>

<!-- ============ 03 PROCESS ============ -->
<section class="sec" id="process" style="padding-top:0">
  <div class="wrap">
    <div class="sec-head" data-reveal><span class="sec-num">03</span><span class="sec-rule"></span><span class="sec-label">Process</span></div>
    <div class="proc-grid">
      <div class="proc-left" data-reveal>
        <h2>Six to eight weeks. One builder. No surprises.</h2>
        <p>No two Lumen bikes are alike, but every one follows the same four steps — and you always know exactly where yours is.</p>
        <p class="mono-note">Forty-eight build slots a year. When they&rsquo;re gone, they&rsquo;re gone — the configurator always shows the next open month.</p>
      </div>
      <div class="steps" id="steps" data-reveal data-delay=".1s">
        <span class="steps-fill" id="stepsFill"></span>
        <ol>
          <li>
            <div class="step-top"><span class="step-num">Step 01</span>
              <span class="step-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M22 16.9v3a2 2 0 0 1-2.2 2 19.8 19.8 0 0 1-8.3-3 18.5 18.5 0 0 1-5.7-5.7 19 19 0 0 1-3-8.4A2 2 0 0 1 4.8 2h3a2 2 0 0 1 2 1.7c.13.96.36 1.9.7 2.8a2 2 0 0 1-.45 2.1L8.9 9.9a16 16 0 0 0 5.7 5.7l1.3-1.3a2 2 0 0 1 2.1-.45c.9.34 1.84.57 2.8.7A2 2 0 0 1 22 16.9z"/></svg></span></div>
            <h3>You configure, we call</h3>
            <p>Pick one of the three builds or start from zero. Within one working day a builder — an actual builder, not a form — calls you to talk sizes, colours and what you&rsquo;ll actually carry.</p>
          </li>
          <li>
            <div class="step-top"><span class="step-num">Step 02</span>
              <span class="step-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg></span></div>
            <h3>Your frame is built</h3>
            <p>Tubes are cut, mitred, aligned and fillet-brazed by hand in our workshop. One builder sees your bike from first tack to final torque — and you get a photo from the jig every Friday.</p>
          </li>
          <li>
            <div class="step-top"><span class="step-num">Step 03</span>
              <span class="step-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M3 17.2 17.2 3l3.8 3.8L6.8 21 3 17.2z"/><path d="m7.5 12.7 2 2M10.5 9.7l2 2M13.5 6.7l2 2"/></svg></span></div>
            <h3>Fitting &amp; handover</h3>
            <p>You come to Refshalevej. We set the saddle, bars and brakes to your body, load the box with your actual groceries, and you ride home the same day.</p>
          </li>
          <li>
            <div class="step-top"><span class="step-num">Step 04</span>
              <span class="step-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-3.6 8-10V5.5L12 2 4 5.5V12c0 6.4 8 10 8 10z"/></svg></span></div>
            <h3>The 1,000 km service</h3>
            <p>At 1,000 km we re-tension, true and adjust everything — free. Then again at 5,000 km. Then we stay in touch, because a Lumen is meant to outlast the car it replaced.</p>
          </li>
        </ol>
      </div>
    </div>
  </div>
</section>

<!-- ============ 04 CONFIGURATOR ============ -->
<section class="sec config" id="configurator">
  <div class="wrap">
    <div class="sec-head" data-reveal><span class="sec-num">04</span><span class="sec-rule"></span><span class="sec-label">Configurator</span></div>
    <h2 data-reveal>Build yours.</h2>
    <p class="sec-sub" data-reveal>The drawing and the price update as you choose. Nothing is charged until we&rsquo;ve spoken on the phone — and we always speak before we build.</p>

    <div class="config-grid">
      <figure class="stage config-stage" data-reveal>
        <span class="stage-tag">Live drawing — updates as you choose</span>
        <div data-bike="plain"></div>
        <figcaption class="fig"><span data-fig></span><span>Live · 1:8</span></figcaption>
      </figure>

      <div class="config-panel" data-reveal data-delay=".08s">
        <div class="price-line"><span class="label">Your build</span><strong id="price" aria-live="polite">€ 3.950</strong></div>

        <div class="opt-group">
          <div class="opt-head"><span class="label">Frame finish</span><span class="hint">A</span></div>
          <div id="frameOpts" role="radiogroup" aria-label="Frame finish"></div>
        </div>
        <div class="opt-group">
          <div class="opt-head"><span class="label">Cargo box</span><span class="hint">B</span></div>
          <div id="boxOpts" role="radiogroup" aria-label="Cargo box"></div>
        </div>
        <div class="opt-group">
          <div class="opt-head"><span class="label">Drivetrain</span><span class="hint">C</span></div>
          <div id="driveOpts" role="radiogroup" aria-label="Drivetrain"></div>
        </div>
        <div class="opt-group">
          <div class="opt-head"><span class="label">Tyres</span><span class="hint">D</span></div>
          <div id="tyreOpts" role="radiogroup" aria-label="Tyres"></div>
        </div>
        <div class="opt-group">
          <div class="opt-head"><span class="label">Add-ons</span><span class="hint">E</span></div>
          <div id="addonOpts"></div>
        </div>
        <div class="opt-group">
          <div class="opt-head"><span class="label">Engraving</span><span class="hint">Free</span></div>
          <input class="engrave-input" id="engraveInput" maxlength="14" placeholder="Up to 14 characters — e.g. FREJA" autocomplete="off" spellcheck="false" aria-label="Frame engraving text">
          <p class="hint-line">Laser-etched on the down tube while your frame is being built. Letters, numbers, spaces.</p>
        </div>

        <div class="summary" id="summary" aria-live="polite"></div>
        <p class="delivery" id="delivery"></p>
        <button class="btn full" id="requestBtn">Request this build
          <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 12h15M13 6l6 6-6 6"/></svg></button>
        <p class="fine">No payment now. A builder confirms every order by phone within one working day.</p>
        <div class="row-between">
          <button class="link-btn" id="copyLinkBtn">Copy build link</button>
          <button class="link-btn" id="resetBtn">Reset to standard</button>
        </div>
      </div>
    </div>
  </div>
</section>

<!-- ============ 05 WORKSHOP ============ -->
<section class="sec dark" id="workshop">
  <div class="wrap">
    <div class="sec-head" data-reveal><span class="sec-num">05</span><span class="sec-rule"></span><span class="sec-label">The workshop</span></div>
    <div class="workshop-grid">
      <div data-reveal>
        <h2>Refshalevej 167A.<br>The door is green.</h2>
        <p style="margin-top:22px">Lumen started in 2016, when Mette Lund — a frame builder by trade — wanted a cargo bike that fit her life: her height, her kids, her hill. She couldn&rsquo;t find one, so she built it in a former shipyard hall on Refshaleøen.</p>
        <p>Ten years and 1,247 bikes later, nothing about that has changed. Every Lumen is still built by a named builder, in the same room, one at a time. We build forty-eight frames a year. Not because we couldn&rsquo;t build more — because a bike built by someone who signs their work is a different thing from a bike built by a line.</p>
      </div>
      <div data-reveal data-delay=".1s">
        <p class="label">Find us</p>
        <p class="ws-lines">Lumen Cycles ApS<br>Refshalevej 167A<br>1432 København K<br><br>Tue–Fri 09–17 · Sat 10–14<br>Test rides by appointment</p>
        <div class="workshop-cta">
          <button class="btn" data-open-modal="visit">Book a test ride</button>
          <button class="btn ghost" id="copyAddrBtn">Copy address</button>
        </div>
      </div>
    </div>
    <div class="wstats" data-reveal>
      <div class="wstat"><strong>1,247</strong><span>bikes delivered since 2016</span></div>
      <div class="wstat"><strong>48</strong><span>frames a year — that&rsquo;s the limit</span></div>
      <div class="wstat"><strong>10 yr</strong><span>frame &amp; fork guarantee</span></div>
      <div class="wstat"><strong>97%</strong><span>would buy again</span></div>
    </div>
    <div class="slots" data-reveal>
      <div class="slots-head">
        <p class="label">Build slots — three per month, that&rsquo;s the limit</p>
        <p class="hint">Click one to hold it with your build</p>
      </div>
      <div class="slot-row" id="slotRow"></div>
    </div>
  </div>
</section>

<!-- ============ 06 FAQ ============ -->
<section class="sec" id="faq">
  <div class="wrap">
    <div class="sec-head" data-reveal><span class="sec-num">06</span><span class="sec-rule"></span><span class="sec-label">Questions</span></div>
    <h2 data-reveal>Asked every week, answered straight.</h2>
    <div class="faq-wrap" data-reveal>
      <div class="faq-item">
        <button class="faq-q" aria-expanded="false">Is € 3.950 really the whole price?
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 9l7 7 7-7"/></svg></button>
        <div class="faq-a"><p>Yes. The Lumen One is € 3.950 — frame, box, belt drive, mudguards, rack, lock, VAT and delivery in Denmark included. The extras in the configurator are optional, and if we think a paid add-on isn&rsquo;t worth it for your use, we&rsquo;ll say so when we call.</p></div>
      </div>
      <div class="faq-item">
        <button class="faq-q" aria-expanded="false">Why not just buy a Bullitt or a Babboe?
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 9l7 7 7-7"/></svg></button>
        <div class="faq-a"><p>Honestly — for a lot of people, a production bike is the right answer, and we&rsquo;ll tell you if it is. We exist for everyone the production range doesn&rsquo;t fit: the tall, the short, the left-handed, the ones carrying something unusual, the ones who want a bike that fits like a jacket. And our frame guarantee is ten years; most production frames carry two.</p></div>
      </div>
      <div class="faq-item">
        <button class="faq-q" aria-expanded="false">Six to eight weeks is a long time.
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 9l7 7 7-7"/></svg></button>
        <div class="faq-a"><p>It is. Your frame is built by one person, not a line — there are forty-eight build slots a year. You&rsquo;ll get a photo from the jig every Friday, and a call the day it&rsquo;s ready for your fitting. Things made to order take the time they take.</p></div>
      </div>
      <div class="faq-item">
        <button class="faq-q" aria-expanded="false">What if it breaks?
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 9l7 7 7-7"/></svg></button>
        <div class="faq-a"><p>The frame and fork are guaranteed for ten years. If you crash it, we repair at cost, forever — no small print. We stock spare parts for every bike we&rsquo;ve ever built, and we keep the drawings, so a part for a 2017 Lumen is still one phone call away.</p></div>
      </div>
      <div class="faq-item">
        <button class="faq-q" aria-expanded="false">Do you ship outside Denmark?
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 9l7 7 7-7"/></svg></button>
        <div class="faq-a"><p>Yes — anywhere in the EU and the UK, fully assembled, insured and tracked. Or collect it yourself in Copenhagen; the kettle is usually on.</p></div>
      </div>
      <div class="faq-item">
        <button class="faq-q" aria-expanded="false">Can I get a discount?
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 9l7 7 7-7"/></svg></button>
        <div class="faq-a"><p>No. We don&rsquo;t discount, we don&rsquo;t do Black Friday, and we don&rsquo;t pay for reviews. The price is the price because the bike is built the way it&rsquo;s built. What we do include: free fitting for life, a free 1,000 km service, and a builder who answers the phone.</p></div>
      </div>
    </div>
  </div>
</section>
</main>

<!-- ============ FOOTER ============ -->
<footer>
  <div class="wrap">
    <div class="foot-word" aria-hidden="true">LUMEN</div>
    <div class="foot-grid">
      <div>
        <h4>Lumen Cycles</h4>
        <p>Custom cargo bikes, built to order by named builders in Copenhagen since 2016. No stock, no discounts, no nonsense.</p>
      </div>
      <div>
        <h4>Workshop</h4>
        <p>Refshalevej 167A<br>1432 København K<br>Tue–Fri 09–17 · Sat 10–14</p>
      </div>
      <div>
        <h4>Talk</h4>
        <a href="mailto:hello@lumencycles.dk">hello@lumencycles.dk</a>
        <a href="tel:+4531442016">+45 31 44 20 16</a>
        <a href="#configurator">Configure yours</a>
      </div>
      <div>
        <h4>Menu</h4>
        <a href="#bike">The bike</a><a href="#process">Process</a><a href="#workshop">Workshop</a><a href="#faq">FAQ</a>
      </div>
    </div>
    <div class="foot-bottom">
      <span>© 2025 Lumen Cycles ApS · CVR 38 21 47 65</span>
      <span>Prices include 25% Danish VAT · Built by hand, ridden for decades</span>
    </div>
  </div>
</footer>

<!-- ============ MINIBAR / TOASTS / MODAL ============ -->
<div class="minibar" id="minibar" aria-hidden="true">
  <span class="lbl">Your build</span>
  <strong id="minibarPrice">€ 3.950</strong>
  <span class="sep"></span>
  <span class="lbl">6–8 weeks</span>
  <a class="btn sm" href="#configurator">Finish build</a>
</div>
<div class="toasts" id="toasts" aria-live="polite"></div>

<div class="modal" id="modal" aria-hidden="true">
  <div class="modal-back" data-close></div>
  <div class="modal-panel" role="dialog" aria-modal="true" aria-labelledby="mTitle">
    <button class="modal-x" data-close aria-label="Close">
      <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M5 5l14 14M19 5L5 19"/></svg></button>
    <div id="modalFormView">
      <p class="label" id="mKicker">Request</p>
      <h3 id="mTitle">Request your build</h3>
      <p class="m-sub" id="mSub"></p>
      <div class="m-build" id="mBuild"></div>
      <form id="reqForm" novalidate>
        <div class="frow two">
          <div class="field"><label class="label" for="fName">Name</label>
            <input id="fName" type="text" autocomplete="name"><p class="ferr">Please tell us your name.</p></div>
          <div class="field"><label class="label" for="fEmail">Email</label>
            <input id="fEmail" type="email" autocomplete="email"><p class="ferr">That email doesn&rsquo;t look right.</p></div>
        </div>
        <div class="field"><label class="label" for="fPhone">Phone <span class="opt-hint">— optional, but we do call</span></label>
          <input id="fPhone" type="tel" autocomplete="tel"></div>
        <div class="field"><label class="label" for="fSlot">Preferred build slot</label>
          <select id="fSlot"></select></div>
        <div class="field"><label class="label" for="fMsg">Anything we should know? <span class="opt-hint">— optional</span></label>
          <textarea id="fMsg" rows="3" placeholder="Two kids, one dog, a steep hill on the way home…"></textarea></div>
        <button class="btn full" type="submit">Send request
          <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 12h15M13 6l6 6-6 6"/></svg></button>
        <p class="fine">We reply within one working day. No payment, no newsletter, no nonsense.</p>
      </form>
    </div>
    <div id="modalDoneView" hidden>
      <svg class="done-check" viewBox="0 0 56 56" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round">
        <circle cx="28" cy="28" r="25" pathLength="1"/><path d="M17 29l8 8 15-17" pathLength="1"/></svg>
      <h3 id="doneTitle">Tak.</h3>
      <p class="m-sub" id="doneText"></p>
      <p class="ref" id="doneRef"></p><br>
      <button class="btn ghost" data-close>Close</button>
    </div>
  </div>
</div>

<script>
(function(){
"use strict";

/* ================= data ================= */
var BASE = 3950;
var CATALOG = {
  frame: [
    {id:'steel', name:'Raw steel',         desc:'Clear-coated 4130 chromoly — honest, and it ages well', price:0,   color:'#A9ABA3', contrast:'#1B1813'},
    {id:'black', name:'Signal black',      desc:'Powder-coated. The stealth option',                     price:120, color:'#211E19', contrast:'#F4F1E9'},
    {id:'green', name:'Copenhagen green',  desc:'Deep workshop green, gloss clear coat',                 price:120, color:'#33513F', contrast:'#F4F1E9'},
    {id:'brass', name:'Brass',             desc:'Polished brass, clear-coated. Patinas with you',        price:240, color:'#B07A33', contrast:'#1B1813'}
  ],
  box: [
    {id:'ply', name:'Marine plywood',    desc:'Okoumé, oiled, 90 L — warm, light, repairable', price:0},
    {id:'epp', name:'Recycled EPP',      desc:'Moulded, 2.1 kg, made from fish-box foam',      price:90},
    {id:'alu', name:'Aluminium plate',   desc:'Checker plate, 90 L — for tools and kegs',      price:180}
  ],
  drive: [
    {id:'a8',      name:'Shimano Alfine 8',       desc:'Gates belt · 307% range · fit and forget',        price:0},
    {id:'a11',     name:'Shimano Alfine 11',      desc:'Gates belt · 409% range · hills, loaded',         price:390},
    {id:'rohloff', name:'Rohloff Speedhub 14',    desc:'Gates belt · 526% range · the last hub you buy',  price:1240}
  ],
  tyre: [
    {id:'std',    name:'Schwalbe Pick-up',        desc:'Puncture-guard, 2.35″ — fast on asphalt',   price:0},
    {id:'winter', name:'Studded winter tyres',    desc:'Schwalbe Winter, 100 spikes — January, solved', price:140}
  ]
};
var ADDONS = {
  lights: {name:'Dynamo lighting',   desc:'SON hub + Edelux II headlight. Never charge a light again', price:110},
  cover:  {name:'Weather cover',     desc:'Canopy over the box — kids dry, snacks reachable',          price:220},
  seat:   {name:'Child seat + harness', desc:'Rated to 22 kg, mounted in the box',                      price:130}
};
var DEFAULTS = {frame:'steel', box:'ply', drive:'a8', tyre:'std', lights:false, cover:false, seats:0, engraving:'', slot:'', slotIdx:null};
var state = Object.assign({}, DEFAULTS);
var MONTHS = ['January','February','March','April','May','June','July','August','September','October','November','December'];
var fmt = function(n){ return n.toLocaleString('da-DK'); };
var eur = function(n){ return '€ ' + fmt(n); };
var pick = function(g, id){ return CATALOG[g].find(function(x){ return x.id === id; }); };

/* ================= bike drawing (injected twice: annotated + plain) ================= */
var BIKE = function(anno){
return '<svg class="bike" viewBox="0 0 1200 720" role="img" aria-label="Side-view technical drawing of a Lumen cargo bike">'
+'<defs>'
+'<pattern id="aluPat" width="22" height="22" patternUnits="userSpaceOnUse"><rect width="22" height="22" fill="#B3AEA1"/><path d="M0 0h22L0 22Z" fill="#98937F"/><path d="M22 0v22H0Z" fill="#847F72"/></pattern>'
+'<pattern id="eppPat" width="16" height="16" patternUnits="userSpaceOnUse"><rect width="16" height="16" fill="#2A2620"/><circle cx="4" cy="4" r="1.4" fill="rgba(244,241,233,.15)"/><circle cx="12" cy="12" r="1.4" fill="rgba(244,241,233,.15)"/></pattern>'
+'<linearGradient id="beamGrad" x1="604" y1="352" x2="368" y2="452" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#E9B44C" stop-opacity=".5"/><stop offset="1" stop-color="#E9B44C" stop-opacity="0"/></linearGradient>'
+'</defs>'
+'<ellipse class="pop" cx="940" cy="691" rx="175" ry="7" fill="rgba(27,24,19,.07)"/>'
+'<ellipse class="pop" cx="250" cy="691" rx="122" ry="6" fill="rgba(27,24,19,.07)"/>'
+'<line class="draw" pathLength="1" x1="60" y1="690" x2="1140" y2="690" stroke="rgba(27,24,19,.3)" stroke-width="1.5"/>'
+'<g>'
+'<circle class="draw" pathLength="1" cx="940" cy="530" r="150" fill="none" stroke="#1B1813" stroke-width="15"/>'
+'<circle class="draw" pathLength="1" cx="940" cy="530" r="127" fill="none" stroke="#8A857A" stroke-width="5"/>'
+'<circle class="pop" cx="940" cy="530" r="36" fill="none" stroke="#6E6A60" stroke-width="2.5" stroke-dasharray="5 7"/>'
+'<g class="spokes pop" stroke="#B7B1A2" stroke-width="1.4">'
+'<line x1="940" y1="522" x2="940" y2="410"/><line x1="940" y1="522" x2="940" y2="410" transform="rotate(30 940 530)"/>'
+'<line x1="940" y1="522" x2="940" y2="410" transform="rotate(60 940 530)"/><line x1="940" y1="522" x2="940" y2="410" transform="rotate(90 940 530)"/>'
+'<line x1="940" y1="522" x2="940" y2="410" transform="rotate(120 940 530)"/><line x1="940" y1="522" x2="940" y2="410" transform="rotate(150 940 530)"/>'
+'<line x1="940" y1="522" x2="940" y2="410" transform="rotate(180 940 530)"/><line x1="940" y1="522" x2="940" y2="410" transform="rotate(210 940 530)"/>'
+'<line x1="940" y1="522" x2="940" y2="410" transform="rotate(240 940 530)"/><line x1="940" y1="522" x2="940" y2="410" transform="rotate(270 940 530)"/>'
+'<line x1="940" y1="522" x2="940" y2="410" transform="rotate(300 940 530)"/><line x1="940" y1="522" x2="940" y2="410" transform="rotate(330 940 530)"/>'
+'</g>'
+'<circle class="pop" cx="940" cy="530" r="17" fill="none" stroke="#1B1813" stroke-width="5"/>'
+'<circle class="pop" cx="940" cy="530" r="9" fill="#1B1813"/>'
+'</g>'
+'<g>'
+'<circle class="draw" pathLength="1" cx="250" cy="582" r="100" fill="none" stroke="#1B1813" stroke-width="13"/>'
+'<circle class="draw" pathLength="1" cx="250" cy="582" r="79" fill="none" stroke="#8A857A" stroke-width="4"/>'
+'<g class="spokes pop" stroke="#B7B1A2" stroke-width="1.3">'
+'<line x1="250" y1="574" x2="250" y2="508"/><line x1="250" y1="574" x2="250" y2="508" transform="rotate(30 250 582)"/>'
+'<line x1="250" y1="574" x2="250" y2="508" transform="rotate(60 250 582)"/><line x1="250" y1="574" x2="250" y2="508" transform="rotate(90 250 582)"/>'
+'<line x1="250" y1="574" x2="250" y2="508" transform="rotate(120 250 582)"/><line x1="250" y1="574" x2="250" y2="508" transform="rotate(150 250 582)"/>'
+'<line x1="250" y1="574" x2="250" y2="508" transform="rotate(180 250 582)"/><line x1="250" y1="574" x2="250" y2="508" transform="rotate(210 250 582)"/>'
+'<line x1="250" y1="574" x2="250" y2="508" transform="rotate(240 250 582)"/><line x1="250" y1="574" x2="250" y2="508" transform="rotate(240 250 582)"/>'
+'<line x1="250" y1="574" x2="250" y2="508" transform="rotate(270 250 582)"/><line x1="250" y1="574" x2="250" y2="508" transform="rotate(300 250 582)"/>'
+'<line x1="250" y1="574" x2="250" y2="508" transform="rotate(330 250 582)"/>'
+'</g>'
+'<circle class="pop dynamo" cx="250" cy="582" r="10" fill="#1B1813"/>'
+'</g>'
+'<g class="frame" fill="none" stroke-width="10" stroke-linecap="round" stroke-linejoin="round">'
+'<line class="draw" pathLength="1" x1="641" y1="299" x2="629" y2="409" stroke-width="13"/>'
+'<line class="draw" pathLength="1" x1="644" y1="309" x2="836" y2="371"/>'
+'<line class="draw" pathLength="1" x1="631" y1="411" x2="807" y2="561" stroke-width="15"/>'
+'<line class="draw" pathLength="1" x1="810" y1="563" x2="837" y2="373"/>'
+'<line class="draw" pathLength="1" x1="841" y1="381" x2="937" y2="523"/>'
+'<line class="draw" pathLength="1" x1="812" y1="559" x2="936" y2="527"/>'
+'<line class="draw" pathLength="1" x1="629" y1="409" x2="252" y2="580" stroke-width="9"/>'
+'<line class="draw" pathLength="1" x1="641" y1="301" x2="613" y2="239" stroke-width="9"/>'
+'<line class="draw" pathLength="1" x1="613" y1="239" x2="637" y2="221" stroke-width="9"/>'
+'<line class="draw" pathLength="1" x1="847" y1="358" x2="1041" y2="368" stroke-width="7"/>'
+'<line class="draw" pathLength="1" x1="1002" y1="366" x2="939" y2="525" stroke-width="5"/>'
+'<line class="draw" pathLength="1" x1="624" y1="425" x2="519" y2="477" stroke-width="7"/>'
+'<line class="draw" pathLength="1" x1="641" y1="419" x2="641" y2="477" stroke-width="7"/>'
+'</g>'
+'<circle class="frame-fill pop" cx="629" cy="409" r="7"/>'
+'<g fill="none" stroke="#1B1813" stroke-linecap="round">'
+'<line class="draw" pathLength="1" x1="637" y1="221" x2="667" y2="214" stroke-width="11"/>'
+'<line class="draw" pathLength="1" x1="837" y1="373" x2="852" y2="331" stroke-width="8"/>'
+'<line class="draw" pathLength="1" x1="817" y1="569" x2="885" y2="687" stroke-width="6"/>'
+'<line class="draw" pathLength="1" x1="885" y1="687" x2="901" y2="687" stroke-width="6"/>'
+'<circle class="draw" pathLength="1" cx="810" cy="565" r="29" stroke-width="6"/>'
+'<line class="draw" pathLength="1" x1="810" y1="565" x2="845" y2="603" stroke-width="7"/>'
+'<line class="draw" pathLength="1" x1="833" y1="603" x2="857" y2="603" stroke-width="7"/>'
+'<line class="draw" pathLength="1" x1="810" y1="565" x2="775" y2="527" stroke-width="7"/>'
+'<line class="draw" pathLength="1" x1="763" y1="527" x2="787" y2="527" stroke-width="7"/>'
+'<line class="draw" pathLength="1" x1="812" y1="537" x2="938" y2="514" stroke-width="3"/>'
+'<line class="draw" pathLength="1" x1="812" y1="591" x2="938" y2="546" stroke-width="3"/>'
+'</g>'
+'<path class="pop" d="M822 331 Q852 316 892 325 Q860 339 822 331 Z" fill="#1B1813"/>'
+'<path class="draw" pathLength="1" d="M145 544 A 112 112 0 0 1 355 544" fill="none" stroke="#1B1813" stroke-width="5"/>'
+'<path class="draw" pathLength="1" d="M779 502 A 163 163 0 0 1 1101 502" fill="none" stroke="#1B1813" stroke-width="5"/>'
+'<path class="beam" d="M603 342 L368 406 L368 502 L603 362 Z" fill="url(#beamGrad)"/>'
+'<circle class="lamp-glow" cx="610" cy="352" r="26" fill="#E9B44C"/>'
+'<line class="draw" pathLength="1" x1="633" y1="352" x2="621" y2="352" stroke="#1B1813" stroke-width="5"/>'
+'<circle class="pop" cx="610" cy="352" r="12" fill="#A9712B" stroke="#1B1813" stroke-width="3"/>'
+'<circle class="pop" cx="610" cy="352" r="4.5" fill="#F4F1E9"/>'
+'<circle class="rear-glow" cx="1043" cy="372" r="13" fill="#E9B44C"/>'
+'<circle class="pop" cx="1043" cy="372" r="6" fill="#B4472E" stroke="#1B1813" stroke-width="2"/>'
+'<g>'
+'<path class="box-fill draw popfill" pathLength="1" d="M500 476 L686 476 L674 598 Q672 606 664 606 L524 606 Q516 606 514 598 Z" stroke="rgba(27,24,19,.4)" stroke-width="2"/>'
+'<path class="epp-dots" d="M500 476 L686 476 L674 598 Q672 606 664 606 L524 606 Q516 606 514 598 Z" fill="url(#eppPat)"/>'
+'<g class="grain pop" stroke="rgba(122,82,32,.4)" stroke-width="1.6">'
+'<line x1="512" y1="512" x2="676" y2="512"/><line x1="510" y1="544" x2="677" y2="544"/><line x1="511" y1="576" x2="675" y2="576"/>'
+'</g>'
+'<circle class="h1" cx="564" cy="455" r="13" fill="#B4472E" stroke="#1B1813" stroke-width="2.5"/>'
+'<circle class="h2" cx="628" cy="455" r="13" fill="#A9712B" stroke="#1B1813" stroke-width="2.5"/>'
+'<g class="canopy">'
+'<path class="draw" pathLength="1" d="M506 468 Q596 366 680 468 Z" fill="rgba(168,190,200,.35)" stroke="rgba(27,24,19,.45)" stroke-width="2"/>'
+'<line x1="594" y1="418" x2="594" y2="468" stroke="rgba(27,24,19,.35)" stroke-width="1.5"/>'
+'</g>'
+'<rect class="frame-fill pop" x="494" y="467" width="198" height="13" rx="6.5"/>'
+'<rect class="pop" x="676" y="492" width="11" height="18" rx="2" fill="#A9712B" stroke="rgba(27,24,19,.5)" stroke-width="1.5"/>'
+'<circle class="pop" cx="681.5" cy="501" r="2.2" fill="#1B1813"/>'
+'</g>'
+'<text class="engrave" x="719" y="490" transform="rotate(40.5 719 490)" text-anchor="middle">No. 1248</text>'
+'<text class="brand" x="740" y="344" transform="rotate(17.9 740 344)" text-anchor="middle">LUMEN · KØBENHAVN</text>'
+(anno ?
+'<g class="anno"><circle class="adot" cx="824" cy="470" r="3.5"/><polyline points="824,470 880,310 940,310"/><text x="948" y="314">Fillet-brazed chromoly frame</text></g>'
+'<g class="anno"><circle class="adot" cx="507" cy="540" r="3.5"/><polyline points="507,540 432,638 488,638"/><text x="496" y="642">90 L lockable cargo box</text></g>'
+'<g class="anno"><circle class="adot" cx="798" cy="590" r="3.5"/><polyline points="798,590 754,662 696,662"/><text x="688" y="666" text-anchor="end">Gates CDN belt drive</text></g>'
+'<g class="anno anno-lights"><circle class="adot" cx="598" cy="344" r="3.5"/><polyline points="598,344 560,292 502,292"/><text x="494" y="296" text-anchor="end">SON dynamo · Edelux II</text></g>'
:'')
+'</svg>';
};
document.querySelector('[data-bike="full"]').innerHTML = BIKE(true);
document.querySelector('[data-bike="plain"]').innerHTML = BIKE(false);
document.querySelectorAll('.bike').forEach(function(svg){
  svg.querySelectorAll('.draw').forEach(function(n,i){ n.style.animationDelay = (0.05 + i*0.055).toFixed(2)+'s'; });
  svg.querySelectorAll('.pop').forEach(function(n,i){ n.style.animationDelay = (1.0 + i*0.05).toFixed(2)+'s'; });
  svg.querySelectorAll('.anno').forEach(function(n,i){ n.style.animationDelay = (1.8 + i*0.25).toFixed(2)+'s'; });
});

/* ================= helpers ================= */
function el(html){ var t=document.createElement('template'); t.innerHTML=html.trim(); return t.content.firstElementChild; }
function toast(msg){
  var box=document.getElementById('toasts'), t=el('<div class="toast"></div>');
  t.textContent=msg; box.appendChild(t);
  requestAnimationFrame(function(){ t.classList.add('show'); });
  setTimeout(function(){ t.classList.remove('show'); setTimeout(function(){ t.remove(); },320); },3400);
}
function copyText(text){
  if(navigator.clipboard && navigator.clipboard.writeText){ return navigator.clipboard.writeText(text).catch(function(){ fallbackCopy(text); }); }
  fallbackCopy(text); return Promise.resolve();
}
function fallbackCopy(text){
  var ta=document.createElement('textarea'); ta.value=text; ta.style.position='fixed'; ta.style.opacity='0';
  document.body.appendChild(ta); ta.select();
  try{ document.execCommand('copy'); }catch(e){}
  ta.remove();
}
function total(){
  var t = BASE + pick('frame',state.frame).price + pick('box',state.box).price + pick('drive',state.drive).price + pick('tyre',state.tyre).price;
  if(state.lights) t += ADDONS.lights.price;
  if(state.cover)  t += ADDONS.cover.price;
  t += state.seats * ADDONS.seat.price;
  return t;
}

/* ================= build slots ================= */
var slots = [];
(function(){
  var now=new Date(), booked=[1,0,2,1,2];
  for(var i=0;i<5;i++){
    var d=new Date(now.getFullYear(), now.getMonth()+i, 1);
    slots.push({label: MONTHS[d.getMonth()]+' '+d.getFullYear(), total:3, booked:booked[i]});
  }
  slots.forEach(function(s){ s.free = s.total - s.booked; });
})();
var slotRow = document.getElementById('slotRow');
slots.forEach(function(s,idx){
  var b = el('<button type="button" class="slot" data-idx="'+idx+'"'+(s.free===0?' disabled':'')+'>'
    +'<span class="slot-month">'+s.label+'</span>'
    +'<span class="slot-dots">'+'<i class="b"></i>'.repeat(s.booked)+'<i class="f"></i>'.repeat(s.free)+'</span>'
    +'<span class="slot-left">'+(s.free===0?'Full — join the list':s.free+' of '+s.total+' open')+'</span></button>');
  b.addEventListener('click',function(){
    state.slot=s.label; state.slotIdx=idx; refresh();
    toast(s.label+' held — finish your build to confirm it.');
    document.getElementById('configurator').scrollIntoView({behavior:'smooth'});
  });
  slotRow.appendChild(b);
});
var fSlot = document.getElementById('fSlot');
fSlot.appendChild(el('<option value="">No preference — surprise me</option>'));
slots.forEach(function(s,i){ fSlot.appendChild(el('<option value="'+i+'">'+s.label+' — '+s.free+' of '+s.total+' slots open</option>')); });

/* ================= option rows ================= */
function radioRow(group, item){
  var price = item.price ? '+ '+eur(item.price) : 'incl.';
  var sw = item.color ? '<i class="swatch" style="background:'+item.color+'"></i>' : '';
  var row = el('<label class="opt"><input type="radio" name="'+group+'" value="'+item.id+'">'
    +'<span class="tick"></span>'
    +'<span class="opt-body"><strong>'+sw+item.name+'</strong><small>'+item.desc+'</small></span>'
    +'<span class="opt-price">'+price+'</span></label>');
  row.querySelector('input').addEventListener('change',function(){ state[group]=item.id; refresh(); });
  return row;
}
[['frame','frameOpts'],['box','boxOpts'],['drive','driveOpts'],['tyre','tyreOpts']].forEach(function(cfg){
  var g=cfg[0], mount=document.getElementById(cfg[1]);
  CATALOG[g].forEach(function(it){ mount.appendChild(radioRow(g,it)); });
});
function toggleRow(key){
  var a=ADDONS[key];
  var row = el('<label class="opt"><input type="checkbox" name="'+key+'">'
    +'<span class="tick"></span>'
    +'<span class="opt-body"><strong>'+a.name+'</strong><small>'+a.desc+'</small></span>'
    +'<span class="opt-price">+ '+eur(a.price)+'</span></label>');
  row.querySelector('input').addEventListener('change',function(e){ state[key]=e.target.checked; refresh(); });
  return row;
}
var addonMount = document.getElementById('addonOpts');
addonMount.appendChild(toggleRow('lights'));
addonMount.appendChild(toggleRow('cover'));
var stepRow = el('<div class="opt stepper"><span class="tick"></span>'
  +'<span class="opt-body"><strong>Child seat + harness</strong><small>Rated to 22 kg, mounted in the box — add one or two</small></span>'
  +'<span class="stepper-ctl"><button type="button" id="seatMinus" aria-label="Fewer child seats">−</button>'
  +'<output id="seatOut" aria-live="polite">0</output>'
  +'<button type="button" id="seatPlus" aria-label="More child seats">+</button></span></div>');
addonMount.appendChild(stepRow);
var seatOut=document.getElementById('seatOut'), seatMinus=document.getElementById('seatMinus'), seatPlus=document.getElementById('seatPlus');
seatMinus.addEventListener('click',function(){ if(state.seats>0){ state.seats--; refresh(); } });
seatPlus.addEventListener('click',function(){ if(state.seats<2){ state.seats++; refresh(); } });

/* ================= engraving ================= */
var engraveInput = document.getElementById('engraveInput');
engraveInput.addEventListener('input',function(){
  state.engraving = engraveInput.value.replace(/[^a-zA-Z0-9 .,'\-]/g,'').toUpperCase().slice(0,14);
  engraveInput.value = state.engraving;
  refresh();
});

/* ================= refresh ================= */
var priceEl=document.getElementById('price'), minibarPrice=document.getElementById('minibarPrice');
var summaryEl=document.getElementById('summary'), deliveryEl=document.getElementById('delivery');
var figEls=document.querySelectorAll('[data-fig]');
var shownPrice = BASE, priceRaf = null;
function animatePrice(to){
  var from=shownPrice, t0=performance.now(), dur=420;
  if(priceRaf) cancelAnimationFrame(priceRaf);
  var step=function(now){
    var p=Math.min(1,(now-t0)/dur), e=1-Math.pow(1-p,3);
    shownPrice=Math.round(from+(to-from)*e);
    priceEl.textContent=eur(shownPrice); minibarPrice.textContent=eur(shownPrice);
    if(p<1) priceRaf=requestAnimationFrame(step);
  };
  priceRaf=requestAnimationFrame(step);
}
function buildSummary(){
  var parts=['Lumen One', pick('frame',state.frame).name, pick('box',state.box).name+' box',
    pick('drive',state.drive).name, pick('tyre',state.tyre).name];
  if(state.lights) parts.push('dynamo lights');
  if(state.cover) parts.push('weather cover');
  if(state.seats) parts.push('seats ×'+state.seats);
  if(state.engraving) parts.push('“'+state.engraving+'”');
  return parts.join('  ·  ');
}
function buildCaption(){
  var s='Fig. 01 — Lumen One · '+pick('frame',state.frame).name+' / '+pick('box',state.box).name+' box';
  if(state.lights) s+=' · dynamo lighting';
  if(state.cover) s+=' · weather cover';
  if(state.seats) s+=' · '+state.seats+' child seat'+(state.seats>1?'s':'');
  return s;
}
function deliveryText(){
  var a=new Date(Date.now()+49*864e5), b=new Date(Date.now()+56*864e5);
  var f=function(d){ return d.getDate()+' '+MONTHS[d.getMonth()]; };
  var s='Order today → delivery between '+f(a)+' and '+f(b)+'.';
  if(state.slot) s+=' Build slot held: '+state.slot+'.';
  else { var next=slots.find(function(x){ return x.free>0; }); if(next) s+=' First open build slot: '+next.label+'.'; }
  return s;
}
function refresh(){
  [['frame','frameOpts'],['box','boxOpts'],['drive','driveOpts'],['tyre','tyreOpts']].forEach(function(cfg){
    document.querySelectorAll('#'+cfg[1]+' .opt').forEach(function(row){
      var input=row.querySelector('input'), on=input.value===state[cfg[0]];
      input.checked=on; row.classList.toggle('sel',on);
    });
  });
  ['lights','cover'].forEach(function(k){
    var input=document.querySelector('input[name="'+k+'"]');
    input.checked=state[k]; input.closest('.opt').classList.toggle('sel',state[k]);
  });
  seatOut.textContent=state.seats;
  seatMinus.disabled = state.seats===0;
  seatPlus.disabled  = state.seats>=2;
  var f=pick('frame',state.frame);
  document.body.style.setProperty('--frame',f.color);
  document.body.style.setProperty('--frame-contrast',f.contrast);
  document.body.dataset.box=state.box;
  document.querySelectorAll('.bike').forEach(function(svg){
    svg.classList.toggle('lights-on',state.lights);
    svg.classList.toggle('cover-on',state.cover);
    svg.classList.toggle('seats-1',state.seats>=1);
    svg.classList.toggle('seats-2',state.seats>=2);
  });
  var txt = state.engraving || 'No. 1248';
  document.querySelectorAll('.engrave').forEach(function(t){ t.textContent=txt; });
  document.querySelectorAll('.slot').forEach(function(s){
    s.classList.toggle('sel', state.slotIdx!==null && +s.dataset.idx===state.slotIdx);
  });
  animatePrice(total());
  summaryEl.textContent=buildSummary();
  figEls.forEach(function(c){ c.textContent=buildCaption(); });
  deliveryEl.textContent=deliveryText();
  saveHash();
}
function saveHash(){
  var p=new URLSearchParams({f:state.frame,b:state.box,d:state.drive,t:state.tyre,l:state.lights?1:0,c:state.cover?1:0,s:state.seats,e:state.engraving});
  history.replaceState(null,'','#'+p.toString());
}
function loadHash(){
  var h=new URLSearchParams(location.hash.slice(1));
  if(!h.has('f')) return;
  var ok=function(g,v){ return CATALOG[g].some(function(x){ return x.id===v; }); };
  if(ok('frame',h.get('f'))) state.frame=h.get('f');
  if(ok('box',h.get('b')))   state.box=h.get('b');
  if(ok('drive',h.get('d'))) state.drive=h.get('d');
  if(ok('tyre',h.get('t')))  state.tyre=h.get('t');
  state.lights = h.get('l')==='1';
  state.cover  = h.get('c')==='1';
  state.seats  = Math.min(2, Math.max(0, parseInt(h.get('s'),10)||0));
  state.engraving = (h.get('e')||'').toUpperCase().slice(0,14);
  engraveInput.value = state.engraving;
}

/* ================= presets (models) ================= */
var PRESETS = {
  one:    {frame:'steel', box:'ply', drive:'a8',      tyre:'std',    lights:false, cover:false, seats:0, engraving:''},
  family: {frame:'green', box:'ply', drive:'a8',      tyre:'std',    lights:true,  cover:true,  seats:2, engraving:''},
  work:   {frame:'black', box:'alu', drive:'rohloff', tyre:'winter', lights:true,  cover:false, seats:0, engraving:''}
};
var MODEL_NAMES = {one:'Lumen One', family:'Lumen Family', work:'Lumen Work'};
document.querySelectorAll('[data-configure]').forEach(function(btn){
  btn.addEventListener('click',function(){
    Object.assign(state, PRESETS[btn.dataset.configure]);
    engraveInput.value=state.engraving;
    refresh();
    document.getElementById('configurator').scrollIntoView({behavior:'smooth'});
    toast(MODEL_NAMES[btn.dataset.configure]+' loaded into the configurator — tweak anything.');
  });
});

/* ================= modal ================= */
var modal=document.getElementById('modal'), lastFocus=null;
function openModal(mode){
  lastFocus=document.activeElement;
  var isOrder = mode==='order';
  document.getElementById('mKicker').textContent = isOrder ? 'Request — no payment' : 'Workshop visit';
  document.getElementById('mTitle').textContent  = isOrder ? 'Request your build' : 'Book a test ride';
  document.getElementById('mSub').textContent = isOrder
    ? 'We confirm every order by phone before anything is cut. Nothing is charged today.'
    : 'Three bikes, one courtyard, coffee while you ride. Tell us when suits you.';
  var mb=document.getElementById('mBuild');
  mb.style.display = isOrder ? '' : 'none';
  if(isOrder) mb.textContent = buildSummary()+'  —  '+eur(total());
  document.getElementById('modalFormView').hidden=false;
  document.getElementById('modalDoneView').hidden=true;
  if(state.slotIdx!==null) fSlot.value=String(state.slotIdx);
  modal.classList.add('open'); modal.setAttribute('aria-hidden','false');
  document.body.style.overflow='hidden';
  setTimeout(function(){ document.getElementById('fName').focus(); },60);
}
function closeModal(){
  modal.classList.remove('open'); modal.setAttribute('aria-hidden','true');
  document.body.style.overflow='';
  if(lastFocus && lastFocus.focus) lastFocus.focus();
}
document.querySelectorAll('[data-open-modal]').forEach(function(b){
  b.addEventListener('click',function(){ openModal(b.dataset.openModal); });
});
modal.querySelectorAll('[data-close]').forEach(function(b){ b.addEventListener('click',closeModal); });
document.addEventListener('keydown',function(e){ if(e.key==='Escape' && modal.classList.contains('open')) closeModal(); });
document.getElementById('requestBtn').addEventListener('click',function(){ openModal('order'); });

document.getElementById('reqForm').addEventListener('submit',function(e){
  e.preventDefault();
  var nameF=document.getElementById('fName'), emailF=document.getElementById('fEmail'), ok=true;
  var nameOk=nameF.value.trim().length>=2;
  nameF.closest('.field').classList.toggle('err',!nameOk); ok=ok&&nameOk;
  var emailOk=/^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(emailF.value.trim());
  emailF.closest('.field').classList.toggle('err',!emailOk); ok=ok&&emailOk;
  if(!ok) return;
  var first=nameF.value.trim().split(/\s+/)[0];
  var builder=['Mette','Jonas','Astrid','Rasmus'][Math.floor(Math.random()*4)];
  var ref='LC-'+(2400+Math.floor(Math.random()*160));
  var isOrder=document.getElementById('mBuild').style.display!=='none';
  document.getElementById('doneTitle').textContent='Tak, '+first+'.';
  document.getElementById('doneText').textContent = isOrder
    ? 'Your '+eur(total())+' build is noted, '+first+'. '+builder+' will call you within one working day to confirm the details and your fitting.'
    : builder+' will call you within one working day to find a time. The kettle will be on, '+first+'.';
  document.getElementById('doneRef').textContent='Reference '+ref+' · a builder confirms by phone';
  document.getElementById('modalFormView').hidden=true;
  document.getElementById('modalDoneView').hidden=false;
  toast('Request sent — reference '+ref);
  e.target.reset();
});
['fName','fEmail'].forEach(function(id){
  document.getElementById(id).addEventListener('input',function(){ this.closest('.field').classList.remove('err'); });
});

/* ================= copy link / reset / address ================= */
document.getElementById('copyLinkBtn').addEventListener('click',function(){
  var h=new URLSearchParams({f:state.frame,b:state.box,d:state.drive,t:state.tyre,l:state.lights?1:0,c:state.cover?1:0,s:state.seats,e:state.engraving}).toString();
  copyText(location.href.split('#')[0]+'#'+h).then(function(){ toast('Build link copied — it opens your exact configuration.'); });
});
document.getElementById('resetBtn').addEventListener('click',function(){
  var slot=state.slot, slotIdx=state.slotIdx;
  Object.assign(state, DEFAULTS); state.slot=slot; state.slotIdx=slotIdx;
  engraveInput.value=''; refresh();
  toast('Back to the standard build.');
});
document.getElementById('copyAddrBtn').addEventListener('click',function(){
  copyText('Lumen Cycles, Refshalevej 167A, 1432 København K').then(function(){ toast('Address copied — the door is green.'); });
});

/* ================= nav / minibar / scroll ================= */
var nav=document.getElementById('nav'), navLinks=document.getElementById('navLinks');
document.getElementById('menuBtn').addEventListener('click',function(){ navLinks.classList.toggle('open'); });
navLinks.querySelectorAll('a').forEach(function(a){ a.addEventListener('click',function(){ navLinks.classList.remove('open'); }); });

var minibar=document.getElementById('minibar');
var configSec=document.getElementById('configurator'), heroEl=document.querySelector('.hero');
var cfgVisible=false;
new IntersectionObserver(function(es){ cfgVisible=es[0].isIntersecting; },{rootMargin:'-70px 0px -70px 0px'}).observe(configSec);
var stepsEl=document.getElementById('steps'), stepsFill=document.getElementById('stepsFill');
function onScroll(){
  nav.classList.toggle('scrolled', scrollY>8);
  minibar.classList.toggle('show', scrollY > heroEl.offsetHeight*0.7 && !cfgVisible);
  if(stepsEl){
    var r=stepsEl.getBoundingClientRect();
    var p=Math.min(1,Math.max(0,(innerHeight*0.72-r.top)/r.height));
    stepsFill.style.height=(p*(r.height-16))+'px';
  }
}
addEventListener('scroll',onScroll,{passive:true}); onScroll();

/* ================= reveal on scroll ================= */
var io=new IntersectionObserver(function(es){
  es.forEach(function(en){ if(en.isIntersecting){ en.target.classList.add('in'); io.unobserve(en.target); } });
},{threshold:.12, rootMargin:'0px 0px -40px 0px'});
document.querySelectorAll('[data-reveal]').forEach(function(x){
  if(x.dataset.delay) x.style.transitionDelay=x.dataset.delay;
  io.observe(x);
});

/* ================= marquee / faq / bike-go ================= */
var mt=document.getElementById('marqueeTrack'); mt.innerHTML+=mt.innerHTML;
document.querySelectorAll('.faq-item').forEach(function(item){
  var q=item.querySelector('.faq-q'), a=item.querySelector('.faq-a');
  q.addEventListener('click',function(){
    var wasOpen=item.classList.contains('open');
    document.querySelectorAll('.faq-item.open').forEach(function(o){
      o.classList.remove('open'); o.querySelector('.faq-a').style.maxHeight='0px';
      o.querySelector('.faq-q').setAttribute('aria-expanded','false');
    });
    if(!wasOpen){
      item.classList.add('open'); a.style.maxHeight=a.scrollHeight+'px';
      q.setAttribute('aria-expanded','true');
    }
  });
});
var heroBike=document.querySelector('.hero .bike');
requestAnimationFrame(function(){ if(heroBike) heroBike.classList.add('go'); });
var cfgBike=document.querySelector('.config-stage .bike');
if(cfgBike){
  new IntersectionObserver(function(es,ob){
    es.forEach(function(en){ if(en.isIntersecting){ cfgBike.classList.add('go'); ob.disconnect(); } });
  },{threshold:.2}).observe(cfgBike);
}

/* ================= init ================= */
loadHash();
refresh();
})();
</script>
</body>
</html>

Compare with

Mistral Large 4: other attempts