Last active
March 1, 2026 18:52
-
-
Save pieter/01895ce51672d7be7f0d3f5279f1d65d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="nl"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>BMW R80GS Configurator</title> | |
| <style> | |
| * { box-sizing: border-box; margin: 0; padding: 0; } | |
| body { | |
| font-family: ‘Courier New’, Courier, monospace; | |
| background: #f0f0f0; | |
| color: #1a1a1a; | |
| min-height: 100vh; | |
| padding: 0 0 120px 0; | |
| } | |
| .container { | |
| max-width: 720px; | |
| margin: 0 auto; | |
| padding: 40px 20px 0; | |
| } | |
| header { | |
| margin-bottom: 48px; | |
| border-left: 6px solid #1a5eb8; | |
| padding-left: 20px; | |
| } | |
| header h1 { | |
| font-family: Georgia, ‘Times New Roman’, serif; | |
| font-size: 80px; | |
| line-height: 0.9; | |
| font-weight: bold; | |
| color: #1a1a1a; | |
| } | |
| header h1 span { | |
| color: #1a5eb8; | |
| } | |
| header p { | |
| margin-top: 10px; | |
| font-size: 11px; | |
| letter-spacing: 3px; | |
| text-transform: uppercase; | |
| color: #888; | |
| } | |
| .base-price { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| padding: 20px 24px; | |
| background: #ffffff; | |
| border: 1px solid #d0d0d0; | |
| border-left: 6px solid #1a5eb8; | |
| margin-bottom: 32px; | |
| } | |
| .base-price .label { | |
| font-size: 11px; | |
| letter-spacing: 3px; | |
| text-transform: uppercase; | |
| color: #888; | |
| } | |
| .base-price .sub { | |
| font-size: 13px; | |
| color: #888; | |
| margin-top: 4px; | |
| } | |
| .base-price .amount { | |
| font-family: Georgia, serif; | |
| font-size: 36px; | |
| color: #1a5eb8; | |
| font-weight: bold; | |
| } | |
| .section-label { | |
| font-size: 10px; | |
| letter-spacing: 4px; | |
| text-transform: uppercase; | |
| color: #888; | |
| margin-bottom: 12px; | |
| } | |
| .items { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 2px; | |
| margin-bottom: 16px; | |
| } | |
| .item { | |
| display: grid; | |
| grid-template-columns: 28px 1fr 80px 44px; | |
| align-items: center; | |
| gap: 12px; | |
| padding: 14px 16px; | |
| background: #ffffff; | |
| border: 1px solid #d0d0d0; | |
| cursor: pointer; | |
| } | |
| .item.active { | |
| border: 1px solid #1a5eb8; | |
| background: #eaf0fb; | |
| } | |
| .item-num { font-size: 10px; color: #aaa; text-align: right; } | |
| .item-name { font-size: 13px; color: #1a1a1a; } | |
| .item-price { font-size: 13px; color: #555; text-align: right; } | |
| .item.active .item-price { color: #1a5eb8; } | |
| .toggle { | |
| width: 36px; | |
| height: 20px; | |
| background: #ccc; | |
| border-radius: 10px; | |
| position: relative; | |
| } | |
| .toggle-knob { | |
| position: absolute; | |
| width: 14px; | |
| height: 14px; | |
| border-radius: 50%; | |
| background: #fff; | |
| top: 3px; | |
| left: 3px; | |
| } | |
| .item.active .toggle { | |
| background: #1a5eb8; | |
| } | |
| .item.active .toggle-knob { | |
| left: 19px; | |
| } | |
| .intro-section { | |
| background: #1a5eb8; | |
| color: #ffffff; | |
| padding: 32px 28px; | |
| margin-bottom: 48px; | |
| } | |
| .intro-section h2 { | |
| font-family: Georgia, ‘Times New Roman’, serif; | |
| font-size: 22px; | |
| font-weight: bold; | |
| margin-bottom: 20px; | |
| line-height: 1.3; | |
| border-bottom: 1px solid rgba(255,255,255,0.3); | |
| padding-bottom: 16px; | |
| } | |
| .intro-section p { | |
| font-size: 14px; | |
| line-height: 1.8; | |
| color: rgba(255,255,255,0.9); | |
| margin-bottom: 14px; | |
| } | |
| .intro-section p:last-child { | |
| margin-bottom: 0; | |
| } | |
| .note { | |
| display: flex; | |
| align-items: flex-start; | |
| gap: 12px; | |
| background: #fff8e6; | |
| border: 2px solid #e8a020; | |
| border-left: 6px solid #e8a020; | |
| padding: 14px 18px; | |
| margin-bottom: 24px; | |
| font-size: 13px; | |
| color: #5a3d00; | |
| font-weight: bold; | |
| } | |
| .note-icon { | |
| font-size: 18px; | |
| flex-shrink: 0; | |
| margin-top: 1px; | |
| } | |
| .copy-btn { | |
| display: block; | |
| width: 100%; | |
| padding: 14px 24px; | |
| background: #ffffff; | |
| border: 2px solid #1a5eb8; | |
| color: #1a5eb8; | |
| font-family: ‘Courier New’, Courier, monospace; | |
| font-size: 12px; | |
| letter-spacing: 3px; | |
| text-transform: uppercase; | |
| cursor: pointer; | |
| margin-bottom: 32px; | |
| text-align: center; | |
| } | |
| .copy-btn.copied { | |
| background: #1a5eb8; | |
| color: #ffffff; | |
| } | |
| .total-bar { | |
| position: fixed; | |
| bottom: 0; | |
| left: 0; | |
| right: 0; | |
| background: #1a5eb8; | |
| padding: 12px 16px; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| gap: 12px; | |
| } | |
| .t-label { | |
| font-size: 11px; | |
| letter-spacing: 3px; | |
| text-transform: uppercase; | |
| color: #ffffff; | |
| font-weight: bold; | |
| white-space: nowrap; | |
| } | |
| .extras-count { | |
| font-size: 10px; | |
| color: rgba(255,255,255,0.6); | |
| letter-spacing: 1px; | |
| text-transform: uppercase; | |
| margin-top: 2px; | |
| white-space: nowrap; | |
| } | |
| .t-extras { | |
| font-size: 10px; | |
| color: rgba(255,255,255,0.6); | |
| text-align: right; | |
| white-space: nowrap; | |
| } | |
| .t-amount { | |
| font-family: Georgia, serif; | |
| font-size: clamp(22px, 5vw, 36px); | |
| color: #ffffff; | |
| font-weight: bold; | |
| line-height: 1; | |
| text-align: right; | |
| white-space: nowrap; | |
| } | |
| @media (max-width: 480px) { | |
| header h1 { font-size: 56px; } | |
| .item { grid-template-columns: 22px 1fr 58px 38px; gap: 6px; padding: 12px 10px; } | |
| .item-name { font-size: 12px; } | |
| .item-price { font-size: 11px; } | |
| .base-price { padding: 16px; } | |
| .base-price .amount { font-size: 26px; } | |
| .intro-section { padding: 24px 16px; } | |
| .intro-section h2 { font-size: 17px; } | |
| .intro-section p { font-size: 13px; } | |
| .copy-btn { font-size: 11px; letter-spacing: 1px; padding: 12px 16px; } | |
| .extras-count { display: none; } | |
| .t-extras { display: none; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="intro-section"> | |
| <div style="max-width: 720px; margin: 0 auto;"> | |
| <h2>SCHLACHTFEST – R80GS Basic € 7.790,-</h2> | |
| <p>Vaak zie je dat bij de komende verkoop/inruil van een motorfiets, dat de eigenaar gewilde accessoires via de losse verkoop aanbiedt op fora en veilingsites. De nieuwe eigenaar van de 2e hands motor had mogelijk interesse in die gebruikte onderdelen, maar vist achter het net.</p> | |
| <p>Ik ga het eens omdraaien en wil te koop aanbieden een BMW R80GS Basic voor € 7.790,-</p> | |
| <p>De koper van mijn Basic heeft de keuze om accessoires op de motor te laten zitten en die worden bij de vraagprijs opgeteld. Accessoires waar geen interesse voor is worden vervolgens deskundig gedemonteerd en later door mij verkocht.</p> | |
| </div> | |
| </div> | |
| <div class="container"> | |
| <header> | |
| <h1>BMW<br><span>R80GS</span></h1> | |
| <p>Basic + Accessoires Configurator</p> | |
| </header> | |
| <div class="base-price"> | |
| <div> | |
| <div class="label">Basisprijs</div> | |
| <div class="sub">R80GS Basic</div> | |
| </div> | |
| <div class="amount">€ 7.790</div> | |
| </div> | |
| <div class="section-label">Accessoires — tik om te selecteren</div> | |
| <div class="items" id="items"></div> | |
| <div class="note"> | |
| <span class="note-icon">⚠️</span> | |
| <span>Spatbords (items 8, 9, 10): meerdere spatborden kunnen worden gekocht, maar slechts één wordt gemonteerd geleverd.</span> | |
| </div> | |
| <button class="copy-btn" id="copyBtn" onclick="copySelection()"> | |
| 📋 Kopieer selectie als tekst | |
| </button> | |
| </div> | |
| <div class="total-bar"> | |
| <div style="flex-shrink:1; min-width:0;"> | |
| <div class="t-label">Totaalprijs</div> | |
| <div class="extras-count" id="extrasCount">0 accessoires geselecteerd</div> | |
| </div> | |
| <div style="flex-shrink:0; text-align:right;"> | |
| <div class="t-extras" id="extrasTotal"></div> | |
| <div class="t-amount" id="totalAmount">€ 7.790</div> | |
| </div> | |
| </div> | |
| <script> | |
| var BASE = 7790; | |
| var accessories = [ | |
| { id: 1, name: 'Zijstandaard, BMW', price: 60 }, | |
| { id: 2, name: 'Valbeugels, chroom, BMW', price: 200 }, | |
| { id: 3, name: 'Kofferrek, chroom, BMW', price: 200 }, | |
| { id: 4, name: 'Bagagerek, chroom, BMW', price: 100 }, | |
| { id: 5, name: 'Toerenteller, BMW', price: 290 }, | |
| { id: 6, name: 'Windscherm, Alpinwei\u00df, BMW', price: 100 }, | |
| { id: 7, name: 'Voorspatbord laag, Alpinwei\u00df, BMW', price: 60 }, | |
| { id: 8, name: 'Voorspatbord hoog, Alpinwei\u00df, BMW', price: 60 }, | |
| { id: 9, name: 'Voorspatbord hoog, wit, Acerbis', price: 60 }, | |
| { id: 10, name: 'Boordgereedschap in etui, BMW', price: 50 }, | |
| { id: 11, name: 'Stuurverhogers, 25mm, zwart', price: 30 }, | |
| ]; | |
| var selected = {}; | |
| var container = document.getElementById('items'); | |
| accessories.forEach(function(acc) { | |
| var el = document.createElement('div'); | |
| el.className = 'item'; | |
| el.id = 'item-' + acc.id; | |
| el.innerHTML = | |
| '<span class="item-num">' + acc.id + '</span>' + | |
| '<span class="item-name">' + acc.name + '</span>' + | |
| '<span class="item-price">+ € ' + acc.price + '</span>' + | |
| '<div class="toggle"><div class="toggle-knob"></div></div>'; | |
| el.onclick = function() { | |
| if (selected[acc.id]) { | |
| delete selected[acc.id]; | |
| el.classList.remove('active'); | |
| } else { | |
| selected[acc.id] = acc; | |
| el.classList.add('active'); | |
| } | |
| updateTotal(); | |
| }; | |
| container.appendChild(el); | |
| }); | |
| function updateTotal() { | |
| var extras = 0; | |
| var count = 0; | |
| for (var id in selected) { extras += selected[id].price; count++; } | |
| var total = BASE + extras; | |
| document.getElementById('totalAmount').textContent = '\u20AC\u00A0' + total.toLocaleString('nl-NL'); | |
| document.getElementById('extrasCount').textContent = count + ' accessoire' + (count !== 1 ? 's' : '') + ' geselecteerd'; | |
| document.getElementById('extrasTotal').textContent = extras > 0 ? '+ € ' + extras + ' extras' : ''; | |
| } | |
| function copySelection() { | |
| var lines = []; | |
| lines.push('BMW R80GS Basic — Prijsoverzicht'); | |
| lines.push('================================'); | |
| lines.push(''); | |
| lines.push('Basisprijs R80GS Basic € 7.790'); | |
| lines.push(''); | |
| var extras = 0; | |
| var count = 0; | |
| for (var id in selected) { | |
| var acc = selected[id]; | |
| var priceStr = '€ ' + acc.price; | |
| var padding = ''; | |
| var nameLen = acc.name.length; | |
| for (var i = nameLen; i < 36; i++) { padding += '.'; } | |
| lines.push(acc.name + padding + priceStr); | |
| extras += acc.price; | |
| count++; | |
| } | |
| if (count === 0) { | |
| lines.push('(geen accessoires geselecteerd)'); | |
| } | |
| lines.push(''); | |
| lines.push('================================'); | |
| var total = BASE + extras; | |
| lines.push('TOTAAL € ' + total.toLocaleString('nl-NL')); | |
| lines.push(''); | |
| lines.push('* Eén spatbord naar keuze gemonteerd'); | |
| var text = lines.join('\n'); | |
| if (navigator.clipboard && navigator.clipboard.writeText) { | |
| navigator.clipboard.writeText(text).then(function() { | |
| showCopied(); | |
| }).catch(function() { | |
| fallbackCopy(text); | |
| }); | |
| } else { | |
| fallbackCopy(text); | |
| } | |
| } | |
| function fallbackCopy(text) { | |
| var ta = document.createElement('textarea'); | |
| ta.value = text; | |
| ta.style.position = 'fixed'; | |
| ta.style.top = '0'; | |
| ta.style.left = '0'; | |
| ta.style.opacity = '0'; | |
| document.body.appendChild(ta); | |
| ta.focus(); | |
| ta.select(); | |
| try { document.execCommand('copy'); showCopied(); } catch(e) { alert(text); } | |
| document.body.removeChild(ta); | |
| } | |
| function showCopied() { | |
| var btn = document.getElementById('copyBtn'); | |
| btn.textContent = '✓ Gekopieerd!'; | |
| btn.classList.add('copied'); | |
| setTimeout(function() { | |
| btn.textContent = '📋 Kopieer selectie als tekst'; | |
| btn.classList.remove('copied'); | |
| }, 2500); | |
| } | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment