@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&family=Inter:wght@400;500;600;700&family=IBM+Plex+Mono:wght@500;600&display=swap');

:root{
  --white:#FFFFFF;
  --bg-soft:#F4F8F6;
  --ink:#0F241C;
  --ink-soft:#54655D;
  --ink-faint:#8A978F;
  --green-deep:#173A2C;
  --green:#2E9C6E;
  --green-dark:#227A56;
  --green-light:#DEF0E5;
  --blue:#215F91;
  --blue-light:#E3EDF6;
  --amber:#C67F05;
  --amber-light:#FBEDD6;
  --border:#DFE7E2;
  --shadow: 0 1px 2px rgba(15,36,28,.04), 0 6px 20px rgba(15,36,28,.06);
  --radius: 14px;
  --font-display:'Space Grotesk', sans-serif;
  --font-body:'Inter', sans-serif;
  --font-mono:'IBM Plex Mono', monospace;
}
*{box-sizing:border-box;}
html,body{margin:0;padding:0;}
body{
  font-family:var(--font-body);
  color:var(--ink);
  background:var(--white);
  -webkit-font-smoothing:antialiased;
  line-height:1.5;
}
a{color:inherit;text-decoration:none;}
button{font-family:inherit;cursor:pointer;}
img{max-width:100%;display:block;}
h1,h2,h3,h4{font-family:var(--font-display);margin:0;color:var(--green-deep);letter-spacing:-0.01em;}
p{margin:0;}
.mono{font-family:var(--font-mono);}
/* BUGFIX (gemeld met screenshot: "dit verschil in vakje ziet er niet uit" — framenummer-veld had
   een lichtblauwe achtergrond, framemaat ernaast gewoon wit): dat kwam niet uit onze eigen CSS
   (die geeft nergens een achtergrondkleur aan input/textarea op basis van hun waarde) — het is de
   browser zelf die een veld met een eerder onthouden/ingevulde waarde (autofill) standaard
   highlight, ongeacht wat de site zelf stylet. Hier globaal voor elk tekstveld/textarea
   genegeerd, zodat elk veld altijd dezelfde witte achtergrond houdt, ook na autofill. De
   duizelingwekkende "transition... 5000s"-truc is de gangbare manier om dit te doen (box-shadow
   i.p.v. background-color, want Chrome staat niet toe dat background-color los overschreven
   wordt op een autofilled veld — een inset box-shadow ter grootte van het hele veld wél). */
input:-webkit-autofill,input:-webkit-autofill:hover,input:-webkit-autofill:focus,
textarea:-webkit-autofill,textarea:-webkit-autofill:hover,textarea:-webkit-autofill:focus,
input:autofill,textarea:autofill{
  -webkit-text-fill-color:var(--ink);
  -webkit-box-shadow:0 0 0px 1000px var(--white) inset;
  box-shadow:0 0 0px 1000px var(--white) inset;
  transition:background-color 5000s ease-in-out 0s;
}

/* ---- layout shells ---- */
#app{min-height:100vh;display:flex;flex-direction:column;}
.wrap{max-width:1180px;margin:0 auto;padding:0 24px;}
@media(max-width:640px){.wrap{padding:0 16px;}}
/* BUGFIX (mobiel): vrijwel elk invoerveld in de app zit onder de 16px (13,5-14px, zie
   .filter-group/.form-row/.msg-textarea/.offer-input-row hieronder) — Safari op iOS zoomt
   automatisch in zodra je zo'n veld aantikt, op bijna elk formulier in de site (inloggen,
   wizard, zoekfilters, berichten, bieden). Alleen op mobiel-breedte overschreven, zodat de
   bestaande, compactere desktop-opmaak ongewijzigd blijft.
   HERSTELD: de eerste versie van deze regel (kale input/select/textarea-selector) werkte niet —
   elke class-gebonden regel hieronder (bv. .form-row input, specificiteit 0,1,1) wint altijd van
   een kale tag-selector (0,0,1), ongeacht mediaquery of volgorde in het bestand. CSS-cascade
   gaat op specificiteit, niet op "staat later in het bestand". !important is hier de juiste,
   gangbare oplossing voor precies dit soort altijd-moet-winnen mobiel-only overrides. */
@media(max-width:640px){input,select,textarea,.richtext-editor{font-size:16px !important;}}

/* ---- top nav ---- */
.topnav{
  position:sticky;top:0;z-index:40;background:rgba(255,255,255,.92);
  backdrop-filter:blur(8px);border-bottom:1px solid var(--border);
}
.topnav .row{display:flex;align-items:center;justify-content:space-between;height:68px;gap:12px;}
.logo{display:flex;align-items:center;gap:8px;font-family:var(--font-display);font-weight:700;font-size:20px;color:var(--green-deep);flex-shrink:0;}
.logo svg{flex-shrink:0;}
.navlinks{display:flex;align-items:center;gap:6px;}
.navlinks a{
  padding:9px 14px;border-radius:999px;font-size:14.5px;font-weight:500;color:var(--ink-soft);
  transition:background .15s,color .15s;
}
.navlinks a:hover{background:var(--bg-soft);color:var(--ink);}
.navlinks a.active{background:var(--green-light);color:var(--green-dark);font-weight:600;}
.nav-right{display:flex;align-items:center;gap:12px;flex-shrink:0;}
.mobile-menu-btn{display:none;background:none;border:none;font-size:22px;line-height:1;color:var(--ink);cursor:pointer;padding:6px;flex-shrink:0;}
.mobile-menu{display:none;flex-direction:column;gap:4px;padding:0 20px 18px;border-top:1px solid var(--border);}
/* BUGFIX (kritische esthetische blik, op verzoek): het open menu had geen eigen achtergrond/
   onderrand — het "vloeide" zo direct door in de gewone paginainhoud eronder (bv. de hero), en las
   daardoor niet als een los, zwevend paneel. Een lichte achtergrondtint + rand/schaduw onderaan
   geeft het menu een duidelijke eigen begrenzing. */
.mobile-menu.open{display:flex;background:var(--bg-soft);border-bottom:1px solid var(--border);box-shadow:0 8px 16px rgba(23,58,44,.06);}
.mobile-menu-links{display:flex;flex-direction:column;padding-top:12px;}
.mobile-menu-links a{padding:12px 4px;font-size:15px;color:var(--ink-soft);border-bottom:1px solid var(--border);}
.mobile-menu-links a.active{color:var(--green-dark);font-weight:600;}
.mobile-menu-right{display:flex;flex-direction:column;padding-top:4px;}
/* BUGFIX (kritische esthetische blik, op verzoek): "Beheer" en "Hoi, X" waren allebei een volle-
   breedte PIL met gecentreerde tekst — op een smal scherm vooral lege ruimte rond een paar woorden,
   en bovendien een heel ANDERE visuele taal (ronde, omlijnde knoppen) dan de platte, links
   uitgelijnde lijst-items erboven (Home/Fiets zoeken/...). Nu dezelfde rij-stijl als die
   lijst-items: volle breedte voor een groot tikgebied, maar de tekst zelf links uitgelijnd, geen
   pil-omlijning meer. Alleen .btn-outline (Beheer/Hoi X/Inloggen) — .btn-primary hieronder
   ("Advertentie plaatsen") blijft bewust wél een echte, opvallende knop: dat is hier de enige
   primaire call-to-action. */
.mobile-menu-right .btn-outline{
  display:flex;width:100%;justify-content:flex-start;align-items:center;gap:8px;
  background:none;border:none;border-radius:0;border-bottom:1px solid var(--border);
  padding:12px 4px;font-size:15px;font-weight:400;color:var(--ink-soft);
}
/* BUGFIX (mobiel menu voelde "niet fijn" ingedeeld): navRightHTML wordt gedeeld met de
   desktop-navbalk, waar de volgorde outline-knop-dan-primaire-knop klopt (leesrichting eindigt
   op de belangrijkste actie, rechts). Rechtdoor gestapeld op mobiel kwam de primaire, groene
   "Advertentie plaatsen"-actie (uitgelogde staat) daardoor juist ONDERAAN te staan — zwakke
   hiërarchie voor een verticaal menu. Puur CSS-gebaseerde herordening (geen wijziging aan de
   gedeelde HTML/JS), dus desktop blijft ongewijzigd. */
.mobile-menu-right .btn-primary{order:-1;width:100%;justify-content:center;margin-bottom:10px;}
/* "Beheer" is een beheerdersfunctie, geen persoonlijke accountactie — ziet er in de vorige opzet
   exact hetzelfde uit als "Hoi, X" eronder, zonder enige hiërarchie tussen de twee. Zelfde
   kleur-taal als .btn-blue elders in de app voor dit soort "net-even-anders"-acties. */
.mobile-menu-right .nav-admin-link{color:var(--blue);font-weight:600;}
@media(max-width:760px){
  .navlinks{display:none;}
  .nav-right{display:none;}
  .mobile-menu-btn{display:block;}
  /* BUGFIX (gemeld met screenshot: "als ik scroll heb ik een probleem met het menu"): .topnav is
     semi-transparant (92%) met een backdrop-filter-blur voor een "frosted glass"-effect — op
     desktop onopvallend, maar op mobiel gaf dat bij het scrollen zichtbaar doorschijnende/
     spookbeeld-content van foto's die er precies onderdoor scrolden (te zien op de screenshot: een
     fietswiel dat door de header heen "bloedde"). Op mobiel de header gewoon volledig ondoorzichtig
     maken en de blur weglaten lost dat op; desktop (waar dit niet gemeld is) blijft ongewijzigd. */
  .topnav{background:var(--white);backdrop-filter:none;}
}
.account-menu{position:relative;}
.account-menu summary{list-style:none;cursor:pointer;}
.account-menu summary::-webkit-details-marker{display:none;}
/* BUGFIX (gemeld met screenshot, op verzoek: "wellicht handig dat we duidelijk maken dat als je op
   Hoi, Joey drukt er een menu uitkomt of niet?"): een los "▾"-lettertekentje (11px, vrij onopvallend)
   was op mobiel — sinds "Hoi, X" daar is afgevlakt tot een gewone rij i.p.v. een omlijnde knop (zie
   de mobiel-menu-herziening hierboven) — niet duidelijk genoeg meer als "dit klapt open"-signaal.
   Nu hetzelfde .disclosure-chevron-component (grotere, gekleurde cirkel met pijltje-SVG, draait bij
   open/dicht) dat #/meldingen en "Nog te beoordelen" al gebruiken voor precies hetzelfde doel —
   herkenbaar, consistent signaal door de hele site. */
.account-menu summary .account-menu-avatar{width:24px;height:24px;border-radius:50%;object-fit:cover;font-size:11px;flex-shrink:0;}
.account-menu summary .disclosure-chevron{margin-left:auto;}
.account-menu[open] .disclosure-chevron{transform:rotate(180deg);}
.account-menu-dropdown{
  position:absolute;top:calc(100% + 8px);right:0;background:var(--white);border:1px solid var(--border);
  border-radius:10px;box-shadow:0 8px 24px rgba(23,58,44,.14);padding:8px;min-width:230px;
  display:flex;flex-direction:column;gap:2px;z-index:60;
}
.account-menu-dropdown a,.account-menu-dropdown button{
  display:block;width:100%;text-align:left;padding:9px 10px;border-radius:8px;
  font-size:13.5px;color:var(--ink);background:none;border:none;cursor:pointer;font-family:inherit;
}
.account-menu-dropdown a:hover,.account-menu-dropdown button:hover{background:var(--bg-soft);}
.account-menu-section{border-bottom:1px solid var(--border);margin-bottom:4px;padding-bottom:4px;}
.account-menu-notif{color:var(--blue) !important;font-weight:600;}
/* Overflow-menu voor risicovolle rij-acties (op verzoek, i.c.m. de ernst-badges hierboven: de
   beheermeldingen-rij had 4 knoppen met gelijk visueel gewicht, inclusief 2 onomkeerbare acties
   (verwijderen/blokkeren) pal naast 2 onschuldige — zelfde <details>/<summary>-dropdown-patroon
   als .account-menu hierboven, generiek herbruikbaar voor elke rij met "1 directe hoofdactie +
   een paar minder-vaak-gebruikte/risicovollere acties erachter". */
.row-actions-menu{position:relative;display:inline-block;}
.row-actions-menu summary{list-style:none;cursor:pointer;}
.row-actions-menu summary::-webkit-details-marker{display:none;}
.row-actions-menu-dropdown{
  position:absolute;top:calc(100% + 6px);right:0;background:var(--white);border:1px solid var(--border);
  border-radius:10px;box-shadow:0 8px 24px rgba(23,58,44,.14);padding:8px;min-width:200px;
  display:flex;flex-direction:column;gap:2px;z-index:30;
}
.row-actions-menu-dropdown button{
  display:block;width:100%;text-align:left;padding:9px 10px;border-radius:8px;
  font-size:13.5px;color:var(--ink);background:none;border:none;cursor:pointer;font-family:inherit;
}
.row-actions-menu-dropdown button:hover{background:var(--bg-soft);}
.row-actions-menu-dropdown button.danger{color:#a83a3a;}
.place-autocomplete{position:relative;}
.place-suggestions{
  position:absolute;top:calc(100% + 4px);left:0;right:0;background:var(--white);border:1px solid var(--border);
  border-radius:10px;box-shadow:0 8px 24px rgba(23,58,44,.14);padding:6px;z-index:40;max-height:220px;overflow-y:auto;
}
.place-suggestion-item{display:block;width:100%;text-align:left;padding:8px 10px;border-radius:8px;font-size:13.5px;color:var(--ink);background:none;border:none;cursor:pointer;font-family:inherit;}
.place-suggestion-item:hover,.place-suggestion-item:focus,.place-suggestion-item.active{background:var(--bg-soft);}
.place-suggestion-label{display:block;}
.place-suggestion-sub{display:block;font-size:11.5px;color:var(--ink-faint);margin-top:1px;}
@media(max-width:760px){
  /* .justify-content:flex-start voor "Hoi, X" komt inmiddels al van de generieke
     .mobile-menu-right .btn-outline-regel hierboven (zie de toelichting daar) — hier alleen nog
     de inspring/stijl van de uitgeklapte sub-items (Mijn advertenties/Mijn favorieten/...), zodat
     die duidelijk als ONDERGESCHIKT aan "Hoi, X" ogen i.p.v. als evenwaardige menu-items. */
  .mobile-menu-right .account-menu-dropdown{position:static;box-shadow:none;border:none;padding:4px 0 8px 30px;margin-top:0;display:flex;flex-direction:column;gap:0;}
  .mobile-menu-right .account-menu-dropdown a,.mobile-menu-right .account-menu-dropdown button{color:var(--ink-soft);font-size:14px;padding:8px 6px;}
}
/* BUGFIX (gemeld met screenshot: "zoeken tekst staat niet in het midden van het groene vak"):
   .btn centerde de inhoud alleen VERTICAAL (align-items:center) — horizontaal niet, want een knop
   is normaal gesproken precies zo breed als zijn eigen inhoud, dus viel dat nooit op. De
   "Zoeken"-knop in .searchbar zit echter als laatste kolom in een CSS Grid: als grid-item krijgt
   een knop standaard justify-self:stretch, dus werd 'm breder dan zijn eigen tekst — en zonder
   justify-content:center bleef die tekst daardoor links binnen die extra breedte hangen. */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;padding:10px 18px;border-radius:999px;
  font-weight:600;font-size:14.5px;border:1px solid transparent;transition:transform .12s, background .15s, box-shadow .15s;
}
.btn:active{transform:scale(.97);}
.btn-primary{background:var(--green);color:#fff;}
.btn-primary:hover{background:var(--green-dark);}
.btn-outline{background:transparent;border-color:var(--border);color:var(--ink);}
.btn-outline:hover{border-color:var(--green);color:var(--green-dark);}
.btn-blue{background:var(--blue);color:#fff;}
.btn-blue:hover{background:#184a72;}
.btn-sm{padding:7px 13px;font-size:13px;}
.btn-block{width:100%;justify-content:center;}
.btn[disabled]{opacity:.5;pointer-events:none;}
select[disabled],input[disabled],textarea[disabled]{opacity:.55;cursor:not-allowed;background-color:var(--bg-soft);color:var(--ink-faint);}
.filter-group select[disabled]:hover{border-color:var(--border);}

/* ---- page fade ---- */
/* BUGFIX: was `animation:fadeUp .45s ease both` met een transform:translateY-slide. Doordat
   fill-mode "both" de eindstand van de animatie (transform: translateY(0)) permanent laat staan,
   werd .page een "containing block" voor position:fixed-nakomelingen (een bekend CSS-mechanisme:
   elk element met een actieve of "bevroren" transform vangt fixed-position kinderen af, in plaats
   van dat die relatief aan het viewport blijven staan). Gevolg: de #compare-bar op de zoekpagina
   (position:fixed, hoort altijd onderin het scherm te zweven) zat vast aan de onderkant van de
   .page-container zelf, en verscheen dus pas als je helemaal naar beneden scrolde. Opgelost door
   de transform uit de animatie te halen — de fade blijft, het (toch al subtiele) schuifeffect
   niet meer, in ruil voor correcte position:fixed-werking overal op de site. */
.page{animation:fadeIn .45s ease both;}
@keyframes fadeIn{from{opacity:0;}to{opacity:1;}}
@media (prefers-reduced-motion: reduce){.page{animation:none;}}

/* ---- hero ---- */
.hero{
  background:
    radial-gradient(1100px 420px at 85% -10%, var(--green-light) 0%, transparent 60%),
    radial-gradient(900px 400px at 5% 0%, var(--blue-light) 0%, transparent 55%),
    var(--white);
  padding:64px 0 40px;border-bottom:1px solid var(--border);
}
.hero-grid{display:grid;grid-template-columns:1.1fr .9fr;gap:48px;align-items:center;}
@media(max-width:900px){.hero-grid{grid-template-columns:1fr;}}
/* BUGFIX (op verzoek, "zijn er gekke verschuivingen bij 0 advertenties?"): pickHeroListing() geeft
   dan null terug (geen advertentie om als voorbeeld te kiezen), dus stond .hero-visual leeg — het
   vaste 2-koloms grid hierboven bleef echter gewoon bestaan op elke breedte >900px, met als gevolg
   een breed leeg wit vlak rechts naast een onnodig smalle tekstkolom. Zelfde 1-koloms fallback als
   de bestaande mobiele breakpoint hierboven, maar dan gestuurd door "is er data", niet "is het
   scherm smal". */
.hero-grid.no-visual{grid-template-columns:1fr;}

/* BikeScore-uitleg op de homepage, direct onder de hero (op verzoek: BikeScore is "bij binnenkomst
   niet vanzelfsprekend" — zie de eerdere TODO in pageHome() — dit keer een eigen sectie met visuele
   aandacht i.p.v. een tekstregel in de hero, die versie werd eerder alweer teruggedraaid). */
.bikescore-explainer{display:grid;grid-template-columns:1.1fr .9fr;gap:48px;align-items:center;}
@media(max-width:900px){.bikescore-explainer{grid-template-columns:1fr;}}
.eyebrow{
  display:inline-flex;align-items:center;gap:7px;font-size:12.5px;font-weight:600;letter-spacing:.04em;
  color:var(--green-dark);background:var(--green-light);padding:6px 12px;border-radius:999px;margin-bottom:18px;
}
.hero h1{font-size:44px;line-height:1.08;margin-bottom:16px;}
@media(max-width:640px){.hero h1{font-size:32px;}}
.hero .sub{font-size:17px;color:var(--ink-soft);max-width:480px;margin-bottom:28px;}
.searchbar{
  background:var(--white);border:1px solid var(--border);border-radius:16px;padding:10px;
  display:grid;grid-template-columns:1.15fr 0.65fr 1.05fr 1.0fr auto;gap:8px;box-shadow:var(--shadow);
}
@media(max-width:720px){.searchbar{grid-template-columns:1fr;}}
.searchbar input,.searchbar select{
  border:none;background:var(--bg-soft);border-radius:10px;padding:12px 13px;font-size:14.5px;font-family:inherit;color:var(--ink);width:100%;
}
.searchbar input:focus,.searchbar select:focus{outline:2px solid var(--green);outline-offset:1px;}
.searchbar select{
  padding-right:30px;
  appearance:none;-webkit-appearance:none;-moz-appearance:none;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none'><path d='M5 7l5 5 5-5' stroke='%2354655D' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat:no-repeat;background-position:right 10px center;background-size:13px;
}
.searchbar .price-range-input{display:flex;gap:6px;}
.searchbar .price-range-input input{width:50%;min-width:0;padding-left:10px;padding-right:4px;font-size:13.5px;}
input[type=number]{-moz-appearance:textfield;}
input[type=number]::-webkit-outer-spin-button,
input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0;}

/* hero wheel visual */
.hero-visual{display:flex;justify-content:center;}
.score-card{
  background:var(--white);border:1px solid var(--border);border-radius:20px;padding:26px;box-shadow:var(--shadow);
  width:100%;max-width:340px;transition:box-shadow .18s, transform .18s;
}
.score-card:hover{box-shadow:0 8px 24px rgba(15,36,28,.12);transform:translateY(-2px);}
.score-card .top{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:14px;}
.score-card .top .name{font-family:var(--font-display);font-weight:700;font-size:17px;}
.score-card .top .loc{font-size:12.5px;color:var(--ink-faint);margin-top:2px;}
.score-card .price{font-family:var(--font-mono);font-weight:600;font-size:19px;color:var(--green-deep);}
.wheel-row{display:flex;align-items:center;gap:16px;margin-top:6px;}
.wheel-caption .label{font-size:11.5px;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-faint);font-weight:600;}

/* ---- sections ---- */
.section{padding:56px 0;}
/* Op verzoek (screenshot): de homepage oogde "enorm wit en dicht op elkaar" — met de twee nieuwe
   secties van vandaag (BikeScore-uitleg, Meest bekeken) stonden er inmiddels 3-4 witte vlakken
   zonder enige scheiding achter elkaar vóór je bij "Recent geplaatst" komt (de enige sectie met een
   eigen achtergrondkleur). .home-section geeft elk blok een duidelijke scheidingslijn erboven, zie
   pageHome()/bikeScoreExplainerHTML()/mostViewedHTML()/savedSearchNudgeHTML() — werkt ook correct
   als een tussenliggend blok (bv. "Meest bekeken" zonder data, of geen opgeslagen zoekopdracht)
   niet gerenderd wordt: de lijn verschijnt dan gewoon boven het eerstvolgende blok dat er wél is. */
.home-section{border-top:1px solid var(--border);}
.section-head{display:flex;align-items:flex-end;justify-content:space-between;margin-bottom:26px;gap:16px;flex-wrap:wrap;}
.section-head h2{font-size:26px;}
.section-head .desc{color:var(--ink-soft);font-size:14.5px;margin-top:4px;}

/* how it works — real 3-step sequence */
.steps{display:grid;grid-template-columns:repeat(3,1fr);gap:22px;}
@media(max-width:800px){.steps{grid-template-columns:1fr;}}
.step{
  background:var(--bg-soft);border:1px solid var(--border);border-radius:var(--radius);padding:24px;
}
.step .num{font-family:var(--font-mono);font-size:13px;color:var(--green-dark);font-weight:600;margin-bottom:10px;}
.step h3{font-size:16.5px;margin-bottom:6px;}
.step p{font-size:14px;color:var(--ink-soft);}

/* listing cards */
.grid-listings{display:grid;grid-template-columns:repeat(3,1fr);gap:20px;}
@media(max-width:980px){.grid-listings{grid-template-columns:repeat(2,1fr);}}

/* "Laatst bekeken" — bewust een compacte horizontale strip i.p.v. een volle grid-sectie zoals
   "Recent geplaatst": stond eerder als eigen volle sectie vlak boven "Recent geplaatst", wat al
   snel twee bijna identieke kaartenrijen na elkaar gaf (soms zelfs dezelfde fiets dubbel). */
.recently-viewed-strip{display:flex;gap:12px;overflow-x:auto;padding-bottom:2px;}
.recently-viewed-card{
  display:flex;align-items:center;gap:10px;flex:0 0 auto;width:230px;
  border:1px solid var(--border);border-radius:10px;padding:8px;background:var(--white);
  text-decoration:none;color:inherit;cursor:pointer;
}
.recently-viewed-card .thumb{width:52px;height:52px;border-radius:8px;overflow:hidden;flex-shrink:0;background:var(--bg-soft);}
.recently-viewed-card .thumb img{width:100%;height:100%;object-fit:cover;display:block;}
.recently-viewed-card .info{min-width:0;}
.recently-viewed-card .title{font-size:13px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.recently-viewed-card .price{font-size:12.5px;color:var(--ink-soft);}
@media(max-width:640px){.grid-listings{grid-template-columns:1fr;}}
.card{
  position:relative;border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;background:var(--white);
  transition:box-shadow .18s, transform .18s;cursor:pointer;
}
.card:hover{box-shadow:var(--shadow);transform:translateY(-2px);}
/* "Stretched link" (bekend patroon, oa. Bootstrap's .stretched-link): een onzichtbare <a> die de
   hele kaart overlapt, zodat de kaart een ECHTE link is (toetsenbord/rechtermuisklik/middelste-
   muisknop werken) i.p.v. een <div onclick>. z-index:1 houdt 'm ONDER .fav-btn/.compare-check
   (z-index:2 hieronder) zodat die twee eigen, apart klikbare/focusbare controls blijven — ze zijn
   bewust NIET in de <a> genest (interactieve content in <a> is ongeldige HTML, zie de toelichting
   bij listingCard()). outline-offset is negatief omdat .card zelf overflow:hidden heeft — een
   normale (naar buiten toe getekende) focusring zou daardoor deels wegvallen. */
.card .card-link{position:absolute;inset:0;z-index:1;}
.card .card-link:focus-visible{outline:2px solid var(--green);outline-offset:-3px;border-radius:var(--radius);}
/* Op verzoek: lijst en kaart (zie updateDistanceMapListingPins()) waren twee losse weergaven van
   dezelfde resultaten — hoveren over een pin op de kaart markeert nu de bijbehorende kaart hier
   (zie highlightResultCardForListing()), zodat je in één oogopslag ziet welke fiets bij welke pin
   hoort zonder elke pin los te hoeven aanklikken. Zelfde visuele taal als de bestaande :hover
   hierboven (dezelfde transform/box-shadow-overgang staat al op .card zelf), plus een dunne groene
   rand als extra, duidelijk "dit is 'm"-signaal — nodig omdat deze highlight NIET van een echte
   muis-hover op de kaart zelf komt, dus zonder eigen indicator zou 'm makkelijk over het hoofd
   gezien worden tussen de andere kaarten. */
.card.map-pin-hover{box-shadow:0 0 0 2px var(--green-dark), var(--shadow);transform:translateY(-2px);}
/* BUGFIX: was een VASTE hoogte (150px) bij een breedte die per pagina/kolomaantal verschilt
   (3 kolommen breed, 2 op de homepage, 1 op mobiel) — bij dezelfde object-fit:cover knipt een
   bredere kaart daardoor altijd meer van de foto weg dan een smallere. aspect-ratio houdt de
   hoogte verhoudingsgewijs mee met de breedte, dus de crop blijft overal even mild. */
.card .thumb{
  aspect-ratio:4/3;background:linear-gradient(135deg,var(--green-light),var(--blue-light));
  display:flex;align-items:center;justify-content:center;position:relative;overflow:hidden;
}
.card .thumb img{width:100%;height:100%;object-fit:cover;}
.card .thumb svg{width:64px;height:64px;opacity:.5;}
/* Stond eerst als overlay bovenop de foto (top-rechts) — op verzoek verplaatst naast de tekst
   in .body, zodat de foto zelf niet deels wordt bedekt en helemaal te zien is. */
.card .badge-score{
  background:var(--white);border:1px solid var(--border);border-radius:999px;padding:5px 12px 5px 6px;
  display:flex;align-items:center;gap:7px;font-size:15px;font-weight:700;font-family:var(--font-mono);flex-shrink:0;
}
.card .badge-score .unverified-dot{width:6px;height:6px;border-radius:50%;background:var(--amber);flex-shrink:0;margin-left:-1px;}
.card .cat-badge{
  position:absolute;top:10px;left:10px;background:rgba(255,255,255,.92);border-radius:999px;padding:3px 9px;
  font-size:11px;font-weight:600;color:var(--green-deep);
}
/* BUGFIX (kritische blik op mobiel, op verzoek): 30px is een krappe tikdoel voor een knop die
   vaak en snel aangetikt wordt (favoriet toevoegen), ruim onder de gangbare ~40-44px-richtlijn
   voor touch-targets — vergroot naar 40px, lettergrootte evenredig mee. */
.fav-btn{
  width:40px;height:40px;border-radius:50%;background:var(--white);border:none;
  display:flex;align-items:center;justify-content:center;box-shadow:var(--shadow);
  font-size:19px;line-height:1;color:var(--ink-faint);cursor:pointer;transition:color .15s, transform .1s;padding:0;flex-shrink:0;
}
.fav-btn:hover{transform:scale(1.1);}
.fav-btn.active{color:#E0455F;}
.card .fav-btn{position:absolute;bottom:10px;right:10px;z-index:2;}
.fav-toggle-chip{
  display:inline-flex;align-items:center;gap:6px;border:1px solid var(--border);border-radius:10px;
  padding:8px 12px;font-size:12.5px;line-height:1.35;color:var(--ink-soft);cursor:pointer;background:var(--white);
}
.fav-toggle-chip.active{background:#FCE8EC;border-color:#E0455F;color:#C23A54;font-weight:600;}
/* BUGFIX: stond eerst op top:10px;right:10px — exact dezelfde plek als .badge-score (de
   BikeScore-cirkel), die later in de DOM staat en 'm dus volledig overlapte. Het vinkje bestond
   wel, was alleen onzichtbaar (gevonden n.a.v. "waar is de vergelijkknop?"). Bottom-left is de
   enige hoek van de kaart die nog vrij was (top-links=categorie, top-rechts=BikeScore,
   rechtsonder=favoriet). */
.compare-check{position:absolute;bottom:10px;left:10px;z-index:2;display:flex;align-items:center;gap:5px;background:rgba(255,255,255,.92);border-radius:999px;padding:5px 10px 5px 8px;font-size:11.5px;font-weight:600;color:var(--ink-soft);box-shadow:var(--shadow);cursor:pointer;}
.compare-check input{margin:0;cursor:pointer;}
#compare-bar{position:fixed;left:0;right:0;bottom:0;z-index:40;background:var(--green-deep);color:var(--white);padding:14px 20px;display:none;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;box-shadow:0 -4px 16px rgba(0,0,0,.12);}
#compare-bar .btn-outline{border-color:rgba(255,255,255,.5);color:var(--white);}
/* BUGFIX (kritische blik, op verzoek: "vergelijking is niet symmetrisch", met concurrentie
   (Coolblue/Tweakers Pricewatch/Independer) als referentie): table-layout:fixed + colgroup (zie
   pageCompare()) i.p.v. content-gestuurde kolombreedtes — zonder dit maakte een lange titel
   ("Cube Reaction Hybrid Speed - Prachtige staat") z'n eigen kolom breder dan de andere twee. Nu
   altijd exact gelijke kolombreedtes, een lange titel wrapt gewoon over meerdere regels.
   BUGFIX (gemeld: "bij het scrollen zakken de foto's helemaal niet, die blijven nu structureel
   staan"): had hier ook position:sticky op de header-rij/labelkolom staan (zelfde idee als
   concurrerende vergelijkingstools) — in de praktijk verwarrend, de rijen eronder leken te
   "verdwijnen" achter de vastzittende foto's. Op verzoek weer weggehaald: de hele tabel scrolt nu
   gewoon normaal mee met de pagina. */
.compare-table{width:100%;border-collapse:collapse;table-layout:fixed;}
.compare-table td,.compare-table th{border:1px solid var(--border);padding:12px;font-size:13.5px;vertical-align:top;word-wrap:break-word;}
.compare-table td:first-child{color:var(--ink-soft);font-weight:600;background:var(--bg-soft);}
.card .body{padding:15px;display:flex;justify-content:space-between;align-items:flex-start;gap:10px;}
.card .body-main{min-width:0;}
.card .brand{font-size:12px;color:var(--ink-faint);text-transform:uppercase;letter-spacing:.05em;font-weight:600;}
/* BUGFIX (gemeld met screenshot, "niet uitgelijnd"): geen regel-limiet hier betekende dat de
   kaarthoogte (en dus de positie van prijs/rating/aantal eronder) meeschoof met de titellengte —
   een korte titel ("Trek Allant+ 8") en een lange ("Cube Reaction Hybrid Speed - Prachtige staat",
   wrapt naar 2 regels) gaven zo zichtbaar ongelijke kaarten in dezelfde grid. Vast op max. 2
   regels + "..." erna (zelfde patroon als vrijwel elke marktplaats), met min-height zodat ook een
   kaart met een KORTE titel (1 regel) evenveel ruimte reserveert als een met een lange (2 regels)
   — dat laatste is net zo belangrijk als de line-clamp zelf, anders blijft een korte titel alsnog
   een kortere kaart opleveren. */
.card .title{font-family:var(--font-display);font-weight:600;font-size:16px;line-height:1.3;margin:3px 0 8px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;min-height:calc(1.3em * 2);}
/* BUGFIX (gemeld met screenshot, "staat er nog steeds een beetje rommelig"): dit was
   display:flex + gap:12px met year/·/location/·/condition als 5 losse <span>-flex-items — elk van
   die 12px gaps rond een los liggend streepje telt gewoon mee als layoutbreedte, waardoor deze
   regel al bij een bescheiden kaartbreedte (het 2-koloms grid) moest wrappen. Bij het wrappen
   springt zo'n los "·"-flex-item mee naar de volgende regel in plaats van bij zijn buurwoord te
   blijven, met een kaal streepje aan het einde van regel 1 en het laatste woord alleen op regel 2
   als zichtbaar resultaat. Nu gewone inline tekst (zie listingCard()) i.p.v. losse flex-spans: geen
   kunstmatige gap-opslag meer, dus past de hele regel op vrijwel elke kaartbreedte gewoon op 1
   regel; wrapt 'm alsnog, dan doet de browser dat als gewone tekst, niet als losse blokjes. */
.card .meta{font-size:12.5px;color:var(--ink-soft);}
.card-rating{font-size:12px;color:var(--amber);font-weight:700;margin:-4px 0 10px;}
.card-rating span{color:var(--ink-faint);font-weight:400;}
.card .price{font-family:var(--font-mono);font-weight:700;font-size:17px;color:var(--green-deep);}

/* ---- search page ---- */
.search-layout{display:grid;grid-template-columns:260px 1fr;gap:28px;align-items:flex-start;}
@media(max-width:880px){.search-layout{grid-template-columns:1fr;}}
.filters{
  border:1px solid var(--border);border-radius:var(--radius);padding:20px;position:sticky;top:86px;
}
/* BUGFIX: bij het inklappen naar 1 kolom hierboven bleef .filters wél position:sticky houden —
   filters en resultaten staan dan in dezelfde kolom (filters eerst, resultaten eronder), dus de
   sticky-balk (incl. het kaartje) bleef bovenin "vastplakken" terwijl de resultaten eronderdoor
   scrolden: leek visueel alsof alles door elkaar liep. Op mobiel hoort een sidebar die boven de
   resultaten staat gewoon mee te scrollen, niet te blijven plakken. */
@media(max-width:880px){.filters{position:static;}}
/* BUGFIX (gemeld, met concrete meting: 13 filtervelden bij elkaar zijn 1436px hoog — op een
   gangbaar telefoonscherm (844px) moest je 2 volle schermhoogtes scrollen vóórdat je ook maar 1
   advertentie zag): op mobiel staan de filters nu standaard ingeklapt achter een knop (met een
   teller zodra er al filters actief zijn, zie updateFiltersToggleButton()), zodat de resultaten
   direct zichtbaar zijn — hetzelfde patroon als "Doe een bod"/"Bezichtiging voorstellen" elders.
   Desktop (>880px) blijft ongewijzigd: filters altijd zichtbaar in de vaste zijbalk. */
.filters-toggle-btn{display:none;width:100%;margin-bottom:14px;justify-content:center;}
@media(max-width:880px){
  .filters-toggle-btn{display:flex;}
  .filters{display:none;}
  .filters.expanded{display:block;}
}
.distance-map-wrap{position:relative;margin-top:6px;}
#distance-map{height:200px;border-radius:10px;overflow:hidden;background:var(--bg-soft);}
/* BUGFIX (gemeld: "Zoekpunt wissen" verschijnt niet in fullscreen, ook al hoort dat inmiddels wél
   zo): requestFullscreen() werd tot nu toe aangeroepen op #distance-map ZELF (zie
   toggleDistanceMapFullscreen()) — de Fullscreen API toont dan ALLEEN dat element en zijn eigen
   afstammelingen; alles daarbuiten, inclusief broertjes/zusjes in dezelfde .distance-map-wrap
   (de "Zoekpunt wissen"/"Toon alles"/fullscreen-knop en de zoom-hint, stuk voor stuk GEEN kind van
   #distance-map maar een sibling ervan), wordt in echte fullscreen simpelweg niet getekend — los van
   welke CSS-class ze wel of niet hebben. Waarschijnlijk al langer zo (ook de fullscreen-knop zelf
   zat hierdoor eigenlijk al onbereikbaar tijdens fullscreen), alleen nooit opgemerkt omdat Esc altijd
   als alternatief werkte om fullscreen te verlaten. Fix: het echte fullscreen-doelelement is nu
   #distance-map-wrap (de buitenste wrapper, zie toggleDistanceMapFullscreen() hieronder) — alle
   knoppen zijn daar wél een afstammeling van, dus blijven zichtbaar. #distance-map zelf (waar
   Leaflet z'n tegels in tekent) vult die wrapper gewoon volledig via height/width:100%. */
.distance-map-wrap:fullscreen,.distance-map-wrap:-webkit-full-screen{width:100vw;height:100vh;}
.distance-map-wrap:fullscreen #distance-map,.distance-map-wrap:-webkit-full-screen #distance-map{height:100%;width:100%;border-radius:0;}
.map-fullscreen-btn{
  position:absolute;top:8px;right:8px;z-index:1050;width:34px;height:34px;border:none;border-radius:8px;
  background:#fff;color:var(--ink);font-size:16px;line-height:1;cursor:pointer;box-shadow:0 1px 4px rgba(0,0,0,.3);
  display:flex;align-items:center;justify-content:center;
}
.map-fullscreen-btn:hover{background:var(--bg-soft);}
/* Op verzoek (gemeld met screenshot): een zoekpunt zetten via een kaartklik kon tot nu toe alleen
   VERPLAATST worden (opnieuw klikken), nooit echt verwijderd — er bestond al een manier om 'm te
   wissen (de "Binnen X km van ..."-filterchip bovenaan de pagina, zie removeFilter('radius')), maar
   die staat niet in de buurt van de kaart zelf, dus was voor een bezoeker die het zoekpunt daar net
   zette in de praktijk onvindbaar. Deze knop roept exact dezelfde, al bestaande removeFilter('radius')
   aan — geen nieuwe wis-logica, alleen een tweede, voor de hand liggende plek om 'm aan te roepen.
   Toont sowieso alleen in fullscreen (zie updateClearButtonVisibility()), dus RECHTS gepositioneerd,
   onder .map-fullscreen-btn — Leaflet's eigen +/- zoomknoppen blijven op hun standaard, werkende
   plek linksboven (zie de toelichting bij initDistanceMap() voor waarom die niet meer verplaatst
   worden), dus links is voor deze knop juist de kant om te vermijden. */
.map-clear-btn{
  position:absolute;top:50px;right:8px;z-index:1050;height:34px;border:none;border-radius:8px;padding:0 12px;
  background:#fff;color:var(--ink);font-size:12.5px;font-weight:600;cursor:pointer;box-shadow:0 1px 4px rgba(0,0,0,.3);
  display:none;align-items:center;gap:5px;
}
.map-clear-btn.visible{display:flex;}
.map-clear-btn:hover{background:var(--bg-soft);}
/* Op verzoek: na het klikken op een cluster (zie updateDistanceMapListingPins()) ben je ingezoomd —
   deze knop past de kaart weer op alle huidige pins (fitDistanceMapToAllPins()), zodat je niet zelf
   hoeft terug te scrollen om het volledige overzicht terug te krijgen. Onder .map-clear-btn
   gepositioneerd i.p.v. ernaast: beide kunnen tegelijk zichtbaar zijn (een zoekpunt EN pins), en
   ernaast zou op een smalle (mobiele) kaart al snel niet meer passen. Zelfde rechts-redenering als
   bij .map-clear-btn hierboven. */
.map-fitall-btn{
  position:absolute;top:92px;right:8px;z-index:1050;height:34px;border:none;border-radius:8px;padding:0 12px;
  background:#fff;color:var(--ink);font-size:12.5px;font-weight:600;cursor:pointer;box-shadow:0 1px 4px rgba(0,0,0,.3);
  display:none;align-items:center;gap:5px;
}
.map-fitall-btn.visible{display:flex;}
.map-fitall-btn:hover{background:var(--bg-soft);}
/* Op verzoek: scrollen/pinchen om te zoomen (muis + trackpad), zonder de klassieke valkuil dat een
   gewone paginascroll die toevallig over de kaart gaat, ongewild de kaart laat zoomen i.p.v. de
   pagina laat doorscrollen — zie de toelichting bij initDistanceMap() voor de Ctrl/Cmd-gate.
   Verschijnt alleen even (fade in/out) als iemand over de kaart scrollt zónder de modifier-toets,
   puur als korte hint, nooit blijvend in beeld. pointer-events:none zodat de hint zelf nooit de
   muis/scroll-interactie met de kaart eronder blokkeert. */
.map-zoom-hint{
  position:absolute;left:50%;bottom:12px;transform:translateX(-50%);z-index:1050;
  background:rgba(23,58,44,.92);color:#fff;font-size:12.5px;font-weight:600;padding:7px 14px;border-radius:20px;
  white-space:nowrap;pointer-events:none;opacity:0;transition:opacity .2s;
}
.map-zoom-hint.visible{opacity:1;}
/* Omgekeerde richting van de .card.map-pin-hover-highlight hierboven: hoveren over een kaartje in
   de resultatenlijst laat de bijbehorende pin hier opvallen (zie highlightMapPinForListing()).
   transform staat bewust op de SVG zelf, niet op de buitenste .leaflet-marker-icon-wrapper — Leaflet
   zet daar zelf al een inline transform op voor de positionering (translate3d), en een inline style
   wint altijd van een class in het stijlblad hier; de SVG erbinnen heeft geen eigen inline transform,
   dus is veilig om los te schalen. transform-origin onderin (de punt van de pin) houdt die punt op
   z'n plek terwijl de kop groter wordt, i.p.v. de hele pin uit het lood te laten groeien. */
.map-pin-marker svg{transition:transform .12s;}
.map-pin-marker.hover-highlight svg{transform:scale(1.35);transform-origin:50% 100%;}
/* Op verzoek, kritische blik op de kaart-popup: Leaflet's eigen sluitknop (X) heeft standaard geen
   achtergrond en overlapt de omslagfoto bovenin de popup — bij een lichte foto werd die onleesbaar.
   Een klein wit rondje erachter geeft altijd genoeg contrast.
   BUGFIX (gemeld met screenshot: "het kruisje valt in de foto"): top/right:6px zette de knop nog
   steeds BINNEN de contentruimte, dus bovenop de foto-hoek. Nu met een negatieve offset net BUITEN
   de afgeronde popup-kaart geplaatst (zwevend knopje op de rand), zodat 'm nooit meer over de foto
   zelf heen valt, ongeacht hoe die is uitgesneden. */
.leaflet-popup-close-button{
  background:var(--white);border-radius:50%;width:22px;height:22px;line-height:22px!important;
  text-align:center;top:-9px!important;right:-9px!important;box-shadow:0 1px 4px rgba(0,0,0,.25);
  transition:color .12s;
}
/* Op verzoek: duidelijke hover-feedback, zelfde rood als de rest van de site bij
   waarschuwingen/fouten (.field-invalid) gebruikt. */
.leaflet-popup-close-button:hover{color:#c0392b!important;}
.filters h3{font-size:14px;margin-bottom:14px;}
.filter-group{margin-bottom:20px;}
.filter-group:last-child{margin-bottom:0;}
.filter-group label{display:block;font-size:12.5px;font-weight:600;color:var(--ink-soft);margin-bottom:7px;}
.filter-group input[type=text],.filter-group input[type=number]{
  width:100%;padding:9px 10px;border:1px solid var(--border);border-radius:8px;font-size:13.5px;font-family:inherit;background:var(--white);
}
.filter-group select{
  width:100%;padding:9px 30px 9px 10px;border:1px solid var(--border);border-radius:8px;font-size:13.5px;font-family:inherit;background-color:var(--white);color:var(--ink);
  appearance:none;-webkit-appearance:none;-moz-appearance:none;cursor:pointer;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none'><path d='M5 7l5 5 5-5' stroke='%2354655D' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat:no-repeat;background-position:right 10px center;background-size:12px;
}
.filter-group select:hover{border-color:var(--green);}
.filter-group select[disabled]{cursor:not-allowed;color:var(--ink-faint);background-color:var(--bg-soft);}
.range-row{display:flex;gap:8px;}
.chips{display:flex;flex-wrap:wrap;gap:7px;}
.quiz-options{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:4px;}
@media(max-width:560px){.quiz-options{grid-template-columns:1fr;}}
.quiz-option{display:flex;align-items:center;gap:10px;border:1px solid var(--border);border-radius:10px;padding:12px 14px;cursor:pointer;font-size:14px;transition:border-color .15s,background .15s;}
.quiz-option:hover{border-color:var(--green);}
.quiz-option input{accent-color:var(--green);width:16px;height:16px;flex-shrink:0;}
.quiz-option:has(input:checked){border-color:var(--green);background:var(--green-light);font-weight:600;color:var(--green-deep);}
.quiz-entry-card{background:var(--green-deep);color:#fff;border-radius:var(--radius);padding:20px 24px;display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;margin-top:24px;}
.quiz-entry-card .txt h3{color:#fff;font-size:16px;margin-bottom:4px;}
.quiz-entry-card .txt p{color:rgba(255,255,255,.75);font-size:13.5px;}
/* Bewust NIET dezelfde stijl als .quiz-entry-card hierboven (op verzoek, kritische blik): dat is
   de primaire CTA om een anonieme bezoeker te activeren, dit is een lichtere, lagere-inzet
   herinnering voor een al terugkerende, ingelogde gebruiker — zelfde .label-stijl als elders
   (.wheel-caption .label/.footer-grid .col-label) i.p.v. een eigen nieuwe visuele taal. */
.saved-search-nudge{background:var(--bg-soft);border:1px solid var(--border);border-radius:var(--radius);padding:16px 20px;display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;}
.saved-search-nudge .label{font-size:11.5px;text-transform:uppercase;letter-spacing:.06em;color:var(--green-dark);font-weight:600;margin-bottom:4px;}
.saved-search-nudge p{font-size:13.5px;color:var(--ink-soft);}
.chip{
  border:1px solid var(--border);border-radius:999px;padding:6px 11px;font-size:12.5px;color:var(--ink-soft);cursor:pointer;background:var(--white);
}
.chip.active{background:var(--green-light);border-color:var(--green);color:var(--green-dark);font-weight:600;}
.filters-clear-link{background:none;border:none;color:var(--blue);font-size:12.5px;font-weight:600;padding:0;}
.filters-clear-link:hover{text-decoration:underline;}
.active-filters{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:16px;}
.active-filters:empty{display:none;margin-bottom:0;}
.active-filter-chip{
  display:inline-flex;align-items:center;gap:7px;background:var(--green-light);color:var(--green-dark);
  border-radius:999px;padding:6px 8px 6px 12px;font-size:12.5px;font-weight:600;
}
.active-filter-chip button{
  background:rgba(255,255,255,.6);border:none;color:var(--green-dark);width:16px;height:16px;border-radius:50%;
  font-size:10px;line-height:1;display:flex;align-items:center;justify-content:center;padding:0;font-weight:700;
}
.active-filter-chip button:hover{background:#fff;}
.results-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:16px;flex-wrap:wrap;gap:10px;min-width:0;}
.results-head .count{font-size:14.5px;color:var(--ink-soft);}
/* BUGFIX (gemeld met screenshot van een echt mobiel toestel: moest de pagina verkleinen om het
   hele scherm te zien): #sort-select had geen enkele breedtebeperking, en de langste optie
   ("Dichtstbijzijnde eerst (vul een postcode in)") duwde de NATIVE, gesloten select naar ~342px
   breed — flex-items krijgen standaard min-width:auto (niet 0), dus geen enkele voorouder mocht
   'm smaller maken dan die intrinsieke inhoudsbreedte. Dat overflow-effect cascadeerde omhoog door
   alle geneste flex-containers (.results-head, .filters, de hele .search-layout), waardoor de HELE
   pagina breder werd gerenderd dan het scherm — vandaar dat inzoomen/verkleinen nodig leek om de
   rand te zien, een klassiek "iets ergens is breder dan de viewport"-symptoom. min-width:0 (mag nu
   overal in de keten kleiner) + een concrete max-breedte op de select zelf (native <select>-
   gedrag toont de huidige optietekst dan met ellipsis i.p.v. de doos te verbreden) lossen dit op. */
#sort-select{min-width:0;width:190px;max-width:100%;}
.results-head select{
  border:1px solid var(--border);border-radius:8px;padding:8px 30px 8px 10px;font-size:13.5px;font-family:inherit;background-color:var(--white);color:var(--ink);
  appearance:none;-webkit-appearance:none;-moz-appearance:none;cursor:pointer;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none'><path d='M5 7l5 5 5-5' stroke='%2354655D' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat:no-repeat;background-position:right 10px center;background-size:12px;
}
.results-head select:hover{border-color:var(--green);}
.empty{text-align:center;padding:60px 20px;color:var(--ink-soft);}

/* ---- listing detail ---- */
.detail-grid{
  display:grid;grid-template-columns:1.5fr 1fr;gap:32px;align-items:start;
  grid-template-areas:"gallery buy" "details buy";
}
.listing-gallery{grid-area:gallery;}
.listing-details{grid-area:details;}
/* BUGFIX (gemeld: rare lege ruimte in het BikeScore-voorbeeld op de homepage): stond hier als kale
   ".buy-card{grid-area:buy;}" — .buy-card is echter ook hergebruikt op de homepage
   (bikeScoreExplainerHTML(), een gewone 2-koloms grid zonder named areas). Een grid-area die niet
   bestaat in de template van diens EIGEN grid-ouder laat de browser stilzwijgend extra impliciete
   kolommen aanmaken om die naam te huisvesten — hier zichtbaar als een brede lege kolom tussen de
   uitlegtekst en het voorbeeldkaartje. Scope daarom expliciet tot alleen .detail-grid's eigen
   directe .buy-card, niet elke plek die toevallig dezelfde class hergebruikt. */
/* BUGFIX-GESCHIEDENIS (samengevat, op verzoek "kritisch naar gebruiksvriendelijkheid/strakheid
   kijken" — de eerdere tussenstappen hieronder zijn niet meer in de code, maar wel de moeite
   waard om te weten voor de volgende keer dat hier iets aan verandert):
   1) .buy-card was zelf het grid-item van "buy" → de sticky kaart kon over "contact" (een aparte
      rij eronder) heen schuiven, want het "containing block" voor sticky-positionering bij een
      grid-item is de HELE grid-container, niet de eigen cel.
   2) .buy-card verhuisde naar een wrapper (.buy-col, met align-self:stretch) die zelf het
      grid-item werd — loste de overlap op, maar .buy-col rekte zich daarmee uit tot de volle
      hoogte van de LINKERKOLOM (gallery+details), terwijl de zichtbare koopkaart daarbinnen véél
      korter bleef. Bij een uitgebreide advertentie mat ik hierdoor tot 795px lege witruimte
      tussen de koopkaart en "Bericht aan de verkoper" eronder.
   3) .listing-contact verhuisde IN .buy-col, direct na .buy-card, met .buy-col weer zonder
      kunstmatige extra hoogte (geen stretch, geen flex-wrapper — beide wiskundig hetzelfde effect
      als stap 2). Dat loste de lege ruimte inderdaad op (0px gemeten), maar bracht de overlap uit
      stap 1 terug: zodra .listing-contact IN hetzelfde containing block zit als de sticky kaart,
      mag die kaart per spec net zo goed doorschuiven tot de ONDERKANT van dat hele blok (dus tot
      en met .listing-contact's eigen onderkant) — "containing block" kent geen concept van "stop
      bij de volgende sibling", alleen "stop bij mijn eigen rand". Gemeten: exact zoveel overlap als
      .listing-contact zelf hoog is, zodra er voorbij een bepaald punt gescrold werd.
   Kortom: "geen lege ruimte vóór het berichtenblok" en "geen overlap MET het berichtenblok" bleken
   met .listing-contact in hetzelfde containing block als de sticky kaart wiskundig gegarandeerd om
   en om elkaar te bijten — een sticky element heeft immers per definitie een containing block
   GROTER dan zichzelf nodig om te kunnen "reizen", en precies dat verschil is óf zichtbaar als lege
   ruimte (als het berichtenblok BUITEN dat containing block staat) óf beschikbaar als overlap-
   risico (als het er WEL in staat). Er is geen derde optie in zuivere CSS.
   Fix (huidige situatie): position:sticky op .buy-card weggehaald. De koopkaart scrollt nu gewoon
   normaal mee (net als de rest van de rechterkolom) i.p.v. bovenaan te blijven "plakken" tijdens
   het scrollen door een lange linkerkolom — dat "blijven plakken" was een leuke extra, geen
   kernfunctie, en was de rechtstreekse oorzaak van zowel de gat- als de overlap-bug. Zonder sticky
   is er domweg geen "reisruimte" meer nodig, dus ook geen lege ruimte én geen overlap mogelijk:
   .buy-col is nu gewoon zo hoog als "kaart + berichtenblok" samen, zoals een heel gewone 2-koloms
   lay-out met kolommen van verschillende hoogte (de rechterkolom eindigt dan simpelweg eerder dan
   de linker, net als bij de meeste sidebar-lay-outs op het web). */
.detail-grid>.buy-col{grid-area:buy;}
/* BUGFIX (kritische blik op de gebruiksvriendelijkheid van de advertentiepagina, op verzoek):
   op mobiel (1 kolom) stond het koopblok — prijs, BikeScore, "Bericht aan de verkoper" — pas
   NA de volledige linkerkolom (foto's, omschrijving, specificaties, onderhoud, beoordelingen).
   Gemeten met Playwright op 390px: de prijs verscheen pas na 1,6 schermhoogtes scrollen, het
   berichtenveld pas na 2,2 — bij een kortere testadvertentie dan gemiddeld. Grid-areas i.p.v.
   brute DOM-herordening: de HTML-volgorde (en dus de voorleesvolgorde voor screenreaders) blijft
   gallery → details → koopblok, alleen de VISUELE volgorde wisselt op mobiel naar
   gallery → koopblok → details, zodat prijs en contactmogelijkheid meteen na de foto's te zien
   zijn — hetzelfde patroon als Marktplaats/Vinted hanteren.
   BUGFIX (vervolg, gemeld met screenshot: "ik moet hartstikke ver naar beneden scrollen om ook
   maar even te kijken wat de specs zijn"): die vorige fix verplaatste het HELE koopblok, inclusief
   het potentieel schermvullende "Bericht aan de verkoper"-gesprek, vóór de specificaties — averechts
   voor iemand die juist snel de specs wil zien. "contact" (het berichtenblok, nu een aparte
   grid-area) komt op mobiel daarom NA "details", niet ervoor: prijs/verkoper/BikeScore blijven
   meteen zichtbaar, specificaties volgen kort daarna, en chatten met de verkoper (relevant zodra
   je al geïnteresseerd bent) staat onderaan.
   .listing-contact zit inmiddels (zie de toelichting bij .detail-grid>.buy-col hierboven) IN
   .buy-col, direct na .buy-card — nodig om op desktop zowel de lege-ruimte- als de overlap-bug
   op te lossen. Om op mobiel toch nog los van .buy-card gepositioneerd te kunnen worden, ontvouwt
   display:contents de wrapper hier: .buy-col zelf genereert dan geen eigen doos meer, en
   .buy-card/.listing-contact worden zo weer zelfstandige grid-items van .detail-grid, elk met
   hun eigen (mobiele) grid-area. */
@media(max-width:900px){
  .detail-grid{grid-template-columns:1fr;grid-template-areas:"gallery" "buy" "details" "contact";}
  .buy-col{display:contents;}
  /* BUGFIX (gemeld met screenshot vanaf mobiel: "Wat is de BikeScore?"-sectie op de homepage stond
     kapot — tekst gedrukt in een smalle kolom, groot leeg wit vlak, voorbeeldkaart half van het
     scherm af): stond hier als kale ".buy-card{grid-area:buy;}"/".listing-contact{grid-area:contact;}"
     — EXACT hetzelfde patroon als de al bestaande BUGFIX hierboven bij .detail-grid>.buy-card
     ("Een grid-area die niet bestaat in de template van diens EIGEN grid-ouder laat de browser
     stilzwijgend extra impliciete kolommen aanmaken"), maar dan zelf per ongeluk opnieuw
     geïntroduceerd: deze twee regels golden zonder ".detail-grid>"-scoping voor ELKE .buy-card/
     .listing-contact op de hele site, dus ook voor bikeScoreExplainerHTML() op de homepage — die
     heeft een heel andere, ongenaamde 2-koloms grid, dus kreeg zo een onbedoelde extra "buy"-kolom
     erbij. Scope daarom, zoals hierboven al gebeurde, expliciet tot alleen deze twee bínnen
     .detail-grid (via display:contents hierboven zijn ze daar nog steeds gewone directe kinderen
     van). */
  .detail-grid>.buy-card{grid-area:buy;}
  .detail-grid>.listing-contact{grid-area:contact;}
}
/* BUGFIX (na de eerdere cover->contain-fix): een VASTE hoogte (was 340px) gecombineerd met
   contain gaf bij een foto die niet toevallig exact die verhouding had een dikke lege
   kleurstrook boven/onder — "half" gevuld, zag er onafgemaakt uit. Nu min/max-hoogte i.p.v. een
   vaste hoogte: de box groeit/krimpt mee met de eigen verhouding van de foto (tot een maximum,
   zodat een heel smalle/hoge foto de pagina niet torenhoog maakt), dus geen kunstmatige lege
   ruimte meer bij een gewone liggende fietsfoto. min-height houdt de placeholder-staat (geen
   foto, alleen het fietsicoon) op een nette hoogte. */
.gallery-main{
  min-height:220px;max-height:420px;border-radius:16px;background:linear-gradient(135deg,var(--green-light),var(--blue-light));
  display:flex;align-items:center;justify-content:center;margin-bottom:10px;overflow:hidden;position:relative;
}
/* BUGFIX (gemeld met screenshot: een brede, liggende foto oogde "gekrompen" in een brede kolom):
   object-fit:contain (bewust gekozen, zie de toelichting hierboven — nooit bijsnijden) + de
   max-height hierboven betekent dat een brede foto zijn hoogte-limiet raakt vóórdat 'ie de volle
   boxbreedte vult. Het overschot links/rechts toonde tot nu toe gewoon de kale kleurgradient
   eronder — leeg ogende ruimte. Nu i.p.v. daarvan een vervaagde, uitvergrote kopie van DEZELFDE
   foto als achtergrond (zelfde patroon als Instagram/Google Photos), zodat de opvulling bewust
   oogt i.p.v. als lege ruimte — de zichtbare, scherpe foto zelf blijft ongewijzigd (niet
   bijgesneden, niet vervormd). Alleen aanwezig als er een echte foto is (zie
   galleryMainContentHTML) — de fietsicoon-placeholder behoudt gewoon de kale gradient. */
.gallery-main-backdrop{
  position:absolute;inset:-15%;background-size:cover;background-position:center;
  filter:blur(28px) brightness(0.8);transform:scale(1.15);z-index:0;
}
/* BUGFIX (kritische blik op de gebruiksvriendelijkheid, op verzoek): het klikdoel voor de
   lightbox was een kaal <img>, nooit met toetsenbord te bereiken of te activeren. Nu een echte
   <button> (all:unset + zelf weer opgebouwd) als klikdoel — native focus/Enter/Space, en
   :focus-visible hieronder geeft zichtbare focus voor toetsenbordgebruikers. */
.gallery-main-btn{
  all:unset;display:flex;align-items:center;justify-content:center;width:100%;height:100%;cursor:pointer;border-radius:16px;
  position:relative;z-index:1;
}
.gallery-main-btn:focus-visible{outline:2px solid var(--green);outline-offset:2px;}
.gallery-main img{max-width:100%;max-height:420px;width:auto;height:auto;object-fit:contain;display:block;}
.gallery-main svg{width:110px;height:110px;opacity:.45;}
/* Vervangt de systeem-vergrootglas-cursor (zoom-in) — die vond de gebruiker lelijk. Een expliciete
   knop met icoon is duidelijker als "klik om te vergroten"-signaal dan een cursor-stijl. */
.gallery-zoom-hint{
  position:absolute;bottom:12px;right:12px;background:rgba(23,58,44,.72);color:#fff;
  border:none;border-radius:999px;padding:7px 12px;font-size:12.5px;font-weight:600;
  display:flex;align-items:center;gap:5px;pointer-events:none;
}
/* Zelfde stijl als .gallery-zoom-hint, bewust in de TEGENOVERGESTELDE hoek (linksboven) zodat ze
   elkaar nooit overlappen. Zie de toelichting bij galleryMainContentHTML(). */
.gallery-count-badge{
  position:absolute;top:12px;left:12px;background:rgba(23,58,44,.72);color:#fff;
  border-radius:999px;padding:5px 11px;font-size:12.5px;font-weight:600;z-index:1;
}
.gallery-thumbs{display:grid;grid-template-columns:repeat(auto-fill,minmax(64px,1fr));gap:8px;}
.gallery-thumbs button{all:unset;height:52px;border-radius:8px;background:var(--bg-soft);border:1px solid var(--border);overflow:hidden;cursor:pointer;display:block;}
.gallery-thumbs button.active{border-color:var(--green);border-width:2px;}
.gallery-thumbs button:focus-visible{outline:2px solid var(--green);outline-offset:2px;}
.gallery-thumbs img{width:100%;height:100%;object-fit:cover;display:block;}
.lightbox-overlay{position:fixed;inset:0;background:rgba(10,20,15,.92);display:flex;align-items:center;justify-content:center;z-index:1100;}
.lightbox-overlay img{max-width:90vw;max-height:88vh;object-fit:contain;border-radius:6px;}
.lightbox-close{position:absolute;top:16px;right:20px;background:none;border:none;color:#fff;font-size:32px;line-height:1;cursor:pointer;padding:4px 10px;}
.lightbox-nav{position:absolute;top:50%;transform:translateY(-50%);background:rgba(255,255,255,.12);border:none;color:#fff;width:44px;height:44px;border-radius:50%;font-size:26px;cursor:pointer;display:flex;align-items:center;justify-content:center;}
.lightbox-nav.prev{left:16px;}
.lightbox-nav.next{right:16px;}
.lightbox-nav:hover{background:rgba(255,255,255,.25);}
.lightbox-count{position:absolute;bottom:18px;left:50%;transform:translateX(-50%);color:#fff;font-size:13px;background:rgba(0,0,0,.4);padding:4px 10px;border-radius:999px;}
.specs-table{width:100%;border-collapse:collapse;margin-top:6px;}
.specs-table tr{border-bottom:1px solid var(--border);}
.specs-table tr:last-child{border-bottom:none;}
.specs-table td{padding:11px 4px;font-size:14px;}
.specs-table td:first-child{color:var(--ink-soft);width:44%;}
.specs-table td:last-child{font-family:var(--font-mono);font-weight:600;color:var(--ink);}
/* BUGFIX (gemeld met screenshot, esthetische blik): de monospace-stijl hierboven is bedoeld voor
   korte, cijfer-/code-achtige waarden (bouwjaar, km, Wh, %, framenummer) — netjes uitlijnende,
   "specsheet"-achtige cijfers. Blind toegepast op ELKE waarde maakte ook lopende tekst (bv. een
   motor-omschrijving als "Himiway (naafmotor van eigen merk)", of gewoon "particulier") onnodig
   zwaar en "typemachine"-achtig leesbaar, terwijl dat gewoon proza is, geen code. `.plain`
   schakelt per rij terug naar het normale lettertype (blijft wel vet/gekleurd voor nadruk). */
.specs-table td.plain{font-family:var(--font-body);}
/* position:sticky stond hier ooit standaard aan (met een aparte mobiele override naar static) —
   zie de BUGFIX-GESCHIEDENIS bij .detail-grid>.buy-col hierboven voor waarom dat uiteindelijk is
   losgelaten: zodra "Bericht aan de verkoper" in hetzelfde containing block staat (nodig om de
   lege-ruimte-bug op te lossen), garandeert sticky's eigen spec-gedrag een overlap-risico met dat
   berichtenblok. Nu gewoon position:static (de default, hier niet meer expliciet gezet). */
.buy-card{border:1px solid var(--border);border-radius:16px;padding:22px;box-shadow:var(--shadow);}
.buy-card .price{font-family:var(--font-mono);font-size:30px;font-weight:700;color:var(--green-deep);margin-bottom:4px;}
.market-indicator{background:var(--bg-soft);border-radius:10px;padding:12px 14px;margin:16px 0;font-size:13px;}
.market-bar{height:8px;border-radius:99px;background:linear-gradient(90deg,var(--blue),var(--green));margin:8px 0 6px;position:relative;}
/* transform:translateX(-50%) (ontbrak): zonder dit begint de marker MET zijn linkerrand op
   "left:X%" i.p.v. daar gecentreerd te staan, dus wees hij altijd iets te ver naar rechts van
   zijn eigenlijke datapunt. Samen met de [2,98]-klem hierboven (zie pageListing()) kan de marker
   nooit meer half buiten de afgeronde balk hangen. */
.market-bar .marker{position:absolute;top:-4px;width:2px;height:16px;background:var(--ink);transform:translateX(-50%);}
.seller-box{border:1px solid var(--border);border-radius:12px;padding:14px;margin-top:16px;display:flex;gap:12px;align-items:center;}
.avatar{width:38px;height:38px;border-radius:50%;background:var(--blue-light);color:var(--blue);display:flex;align-items:center;justify-content:center;font-weight:700;font-family:var(--font-display);flex-shrink:0;}
.reveal-phone{margin-top:10px;font-size:13.5px;}
.msg-thread{max-height:320px;overflow-y:auto;margin-bottom:4px;}
.msg-bubble-row{margin-bottom:10px;display:flex;}
.msg-bubble-row.mine{justify-content:flex-end;}
/* BUGFIX (gemeld met screenshot: een kort bericht als "Bod gedaan: €1.000" viel al na een paar
   tekens uiteen over meerdere regels, duidelijk eerder dan de bubbel-breedte zou toestaan): de
   max-width:82% hieronder stond op .msg-bubble zelf, maar het eigenlijke flex-item van
   .msg-bubble-row is de ongestylede <div> daar NOG omheen (zie messageBubbleHTML — die bevat zowel
   de bubbel als .msg-meta eronder). Een percentage-breedte op een kind van een flex-item zonder
   eigen (expliciete) breedte heeft geen vaste basis om tegen te rekenen en wordt genegeerd, waardoor
   de bubbel terugviel op de krapste mogelijke flex-breedte i.p.v. daadwerkelijk 82%. max-width hoort
   dus op .msg-bubble-wrap (het echte flex-item) te staan, niet op .msg-bubble; min-width:0 is nodig
   omdat flex-items anders standaard niet smaller mogen worden dan hun eigen inhoud, wat lange,
   woordloze tekst (zoals een lange URL) zou laten overlopen i.p.v. normaal te wrappen. */
.msg-bubble-wrap{max-width:82%;min-width:0;}
.msg-bubble{padding:9px 13px;border-radius:12px;font-size:13.5px;line-height:1.4;background:var(--bg-soft);color:var(--ink);}
.msg-bubble-row.mine .msg-bubble{background:var(--green-light);color:var(--green-deep);}
.msg-meta{font-size:11px;color:var(--ink-faint);margin-top:3px;}
.msg-bubble-row.mine .msg-meta{text-align:right;}
.msg-read{color:var(--green-dark);font-weight:600;}
/* BUGFIX (gemeld met screenshot: een tekstvak kon oneindig ver naar beneden gesleept worden):
   resize:vertical zonder max-height laat de browser een tekstvak onbeperkt laten groeien — een
   max-height + overflow-y:auto begrenst het sleep-handvat, zonder de mogelijkheid om zelf iets te
   vergroten helemaal weg te nemen; wie meer tekst typt dan past scrollt gewoon binnen het vak. */
.msg-textarea{width:100%;min-height:70px;max-height:320px;padding:11px 12px;border:1px solid var(--border);border-radius:9px;font-family:inherit;font-size:14px;resize:vertical;overflow-y:auto;}
/* Sterrenwidget bij een beoordeling (koper/verkoper) — was losse inline styles, nu in de eigen
   stijl van de site: iets groter, een lichte hover-vulling zodat duidelijk is dat het klikbaar
   is, en een eigen kaartje i.p.v. kaal tegen de rest van de melding aan. */
.star-rating{display:inline-flex;gap:4px;font-size:28px;cursor:pointer;margin-bottom:10px;}
/* Elke ster is een eigen "slot": een grijze ster als achtergrond, met een tweede, amberkleurige
   ster er precies overheen die via width:0/50/100% wordt "afgesneden" (overflow:hidden) om een
   lege/halfgevulde/volle ster te tonen. Beide lagen zijn dezelfde eigen inline-SVG (starSVG(), zie
   STAR_PATH/starGlyphs() in de JS) — bewust GEEN lettertype-ster-teken (☆/★): die hebben geen
   gegarandeerd identieke geometrie tussen lettertypen/systemen, waardoor een knip op 50% breedte
   soms scheef oogde i.p.v. een nette halve ster (zie de toelichting bij STAR_PATH). Twee
   onzichtbare klikzones (linker-/rechterhelft) erover heen kiezen resp. X,5 of X sterren, zie
   setReviewStars(). */
/* Alleen-lezen weergave van een beoordeling (starGlyphs()) — hergebruikt .star-slot/.star-fg
   hieronder, zonder de klikzones/cursor van de invoerwidget. */
.star-display{display:inline-flex;gap:1px;vertical-align:middle;}
.star-slot{position:relative;display:inline-block;width:1em;height:1em;transition:transform .1s;}
.star-slot:hover{transform:scale(1.15);}
.star-slot .star-fg{position:absolute;left:0;top:0;overflow:hidden;white-space:nowrap;color:var(--amber);}
.star-slot .star-hit{position:absolute;top:0;width:50%;height:100%;}
/* BUGFIX (kritische blik op mobiel, op verzoek): elke .star-hit is (bij de 28px van .star-rating
   hierboven) maar 14px breed — ruim onder een vinger-tikbreedte, en juist hier extra vervelend
   omdat links/rechts van dezelfde ster het verschil is tussen X,5 en X sterren geven bij een
   beoordeling. Grotere sterren op mobiel (proportioneel schaalt .star-hit gewoon mee, via em/%)
   i.p.v. losse hitbox-aanpassingen. */
@media(max-width:640px){.star-rating{font-size:40px;gap:6px;}}
/* Geen eigen margin-top/border-top meer (had een dubbele lijn t.o.v. de nieuwe .notif-card-body,
   die zelf al voor de scheiding met de samenvatting zorgt via .notif-card[open] summary) — alleen
   nog gebruikt binnenin notifCardHTML's bodyHTML, zie reviewPendingCardHTML. */
.review-form-card{}
/* Universele, in-/uitklapbare meldingrij (zie notifCardHTML() verderop) — op verzoek, derde
   kritische ronde na de platte-lijst-herziening: "ik kan niet alle meldingen uitklappen", "datums
   staan niet georganiseerd", "prullenbakjes staan niet op dezelfde plek", "tekst die halverwege
   stopt". Eerst alleen voor "Nog te beoordelen" (.review-pending-card), daarna ook voor de
   dashListingCard-kaarten (.collapsible-notif-card) — beide zijn nu samengevoegd tot ÉÉN vaste
   opmaak die ELKE meldingtype op #/meldingen gebruikt: altijd dezelfde thumbnail/datum-kolom/
   prullenbak-plek/chevron, en de volledige (niet-afgekapte) tekst staat altijd in de uitklapbare
   inhoud eronder — dus nooit meer een doodlopende afgekapte regel. Native <details>, zelfde patroon
   als .account-menu/.comparables-details elders: geen eigen open/dicht-state nodig. */
.notif-card{border:1px solid var(--border);border-radius:var(--radius);margin-bottom:10px;background:var(--white);overflow:hidden;}
.notif-card summary{list-style:none;cursor:pointer;display:flex;gap:12px;align-items:center;padding:14px;}
.notif-card summary::-webkit-details-marker{display:none;}
.notif-card[open] summary{border-bottom:1px solid var(--border);}
.notif-card-body{padding:14px;}
.notif-card.unread{border-color:var(--green);background:var(--green-light);}
.notif-thumb{width:56px;height:44px;border-radius:8px;overflow:hidden;flex-shrink:0;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,var(--green-light),var(--blue-light));}
.notif-thumb img{width:100%;height:100%;object-fit:cover;}
/* Vaste breedte (i.t.t. .conversation-row .time elders, die meegroeit met de tekstlengte) — zodat
   de prullenbak/chevron erna altijd op exact dezelfde plek staan, ongeacht of de datum "1 okt" of
   "18 september 2026" leest. */
/* BUGFIX (gemeld met screenshot: "check de datums nog eens, deze staan vreemd"): een vaste `width`
   zonder `white-space:nowrap` laat de tekst gewoon wrappen zodra 'ie net niet past (bv. "26 sep
   2026" op een scherm met een iets andere font-rendering dan mijn eigen testomgeving) — dan springt
   die ene datum naar 2 regels terwijl alle andere op 1 regel staan, wat er inderdaad vreemd uitziet.
   `min-width` i.p.v. een harde `width`, plus `white-space:nowrap`: de kolom is voor bijna elke datum
   precies even breed (en dus nog steeds goed uitgelijnd), en groeit alleen mee op de zeldzame keer
   dat een datum net iets langer is, i.p.v. te wrappen. */
.notif-date{font-size:11.5px;color:var(--ink-faint);flex-shrink:0;min-width:80px;white-space:nowrap;text-align:right;}
/* BUGFIX (gemeld met screenshot vanaf een echte telefoon, op verzoek: "hoe kunnen we tactisch met
   de meldingenpagina omgaan via de telefoon?"): op een smal scherm (±390px) eet de vaste 80px-
   datumkolom zoveel ruimte op dat er maar ~108px overblijft voor titel+samenvatting — "Veel
   bekeken, weinig reactie" werd daardoor al na 2 woorden afgekapt tot onleesbaar "Veel …". De
   exacte datum is op dit formaat ook de minst waardevolle info in de samenvatting (de lijst staat
   toch al chronologisch gesorteerd) — die ruimte teruggeven aan de titel/samenvatting is hier een
   betere afweging. Datum blijft gewoon staan zodra er weer genoeg breedte is. */
@media(max-width:480px){
  .notif-date{display:none;}
}
.icon-btn-trash{background:none;border:none;width:40px;height:40px;padding:0;border-radius:8px;cursor:pointer;flex-shrink:0;color:var(--ink-faint);opacity:0.7;display:inline-flex;align-items:center;justify-content:center;transition:opacity .15s, background .15s, color .15s;}
.icon-btn-trash:hover{opacity:1;background:var(--border);color:#E0455F;}
/* Zichtbare uitklap-indicator (op verzoek: zonder pijltje was niet duidelijk dat je erop kunt
   klikken) — zelfde chevron-pad als de dropdown-pijltjes bij <select>/.account-menu elders, hier
   via currentColor kleurbaar en ronddraaiend bij open/dicht (net als .comparables-details ▸/▾). */
.disclosure-chevron{color:var(--green-dark);background:var(--green-light);width:26px;height:26px;border-radius:50%;flex-shrink:0;display:flex;align-items:center;justify-content:center;transition:transform .15s;}
.notif-card[open] .disclosure-chevron{transform:rotate(180deg);}
/* Confetti bij een verstuurde beoordeling (op verzoek) — puur CSS/DOM, geen library nodig:
   losse gekleurde blokjes die vanaf de bovenkant van het scherm vallen en na de animatie
   zichzelf weer verwijderen (zie launchConfetti()). */
.confetti-piece{position:fixed;top:-12px;width:8px;height:14px;pointer-events:none;z-index:9999;animation:confetti-fall linear forwards;}
@keyframes confetti-fall{to{transform:translateY(105vh) rotate(720deg);opacity:0.2;}}
.conversation-row{display:flex;align-items:center;gap:14px;padding:14px;border:1px solid var(--border);border-radius:var(--radius);text-decoration:none;color:inherit;margin-bottom:10px;background:var(--white);transition:box-shadow .15s;}
.conversation-row:hover{box-shadow:var(--shadow);}
.conversation-row .thumb{width:56px;height:44px;border-radius:8px;background:linear-gradient(135deg,var(--green-light),var(--blue-light));flex-shrink:0;overflow:hidden;}
.conversation-row .thumb img{width:100%;height:100%;object-fit:cover;}
.conversation-row .info{flex:1;min-width:0;}
/* BUGFIX (kritische blik op mobiel, gevonden via een simulatie van 100 gesprekken met realistische,
   langere titel+verkoperscombinaties op een smal scherm): geen regelbegrenzing hier betekende dat
   een lange titel (advertentietitel + verkopersnaam) op een smal scherm over 2-3 regels wrapte —
   sinds .conversation-row zelf align-items:center gebruikt, centreert dat de datum/prullenbak-
   kolom dan tegen de HELE (nu hogere) rij i.p.v. tegen de bovenkant van de titel, waardoor de datum
   los leek te zweven, los van zowel titel als bericht-snippet. Nu dezelfde behandeling als .snippet
   hieronder (max 1 regel + "...") — voorkomt zowel het wrap-probleem als de losse-datum-bug in één
   keer, en is bovendien het gangbare patroon voor een compacte gespreksrij (WhatsApp/Marktplaats
   doen dit ook). */
.conversation-row .info .title{font-weight:600;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.conversation-row .info .snippet{font-size:12.5px;color:var(--ink-soft);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.conversation-row .time{font-size:11.5px;color:var(--ink-faint);flex-shrink:0;}
.conversation-row.unread{border-color:var(--green);background:var(--green-light);}
.unread-dot{display:inline-block;width:8px;height:8px;border-radius:50%;background:var(--green-dark);margin-right:6px;flex-shrink:0;}
.nav-badge{background:#E0455F;color:#fff;font-size:10.5px;font-weight:700;padding:1px 6px;border-radius:999px;margin-left:6px;}
.offer-card{border:1px solid var(--border);border-radius:var(--radius);padding:14px 16px;margin-bottom:14px;background:var(--bg-soft);}
/* BUGFIX (esthetische blik: bod- en bezichtigingskaart in een berichtengesprek waren nauwelijks
   van elkaar te onderscheiden — zelfde grijze kader, zelfde badge/knoppenrij-opbouw). .offer-card
   is een generieke kaart-stijl die elders ook voor meldingen/rapportages/contactberichten wordt
   herbruikt (geen van die een "bod" is), dus de accentkleur hoort NIET in de basisregel maar
   alleen op de echte bod-kaart in een gesprek — vandaar deze losse modifier-class. */
.offer-card.offer-card-in-thread{border-left:3px solid var(--green);padding-left:13px;}
.offer-row{display:flex;justify-content:space-between;align-items:center;}
.offer-amount{font-family:var(--font-mono);font-size:20px;font-weight:700;color:var(--green-deep);}
.status-declined{background:#FCE8EC;color:#C23A54;}
/* BUGFIX (gemeld met screenshot: "het ziet er niet uit" + emoji weg): de bezichtiging/proefrit-
   kaart hergebruikte tot nu toe .offer-card/.offer-amount 1-op-1 — die laatste is een grote,
   vetgedrukte, monospace groene prijs-stijl, ontworpen voor een geldbedrag, niet voor een datum/
   tijd. Vandaar dat het misplaatst oogde. Eigen, lichtere stijl: kleinere padding, geen zwaar
   accentkleur op het tijdstip zelf (dat is hier geen "hoofdgetal" zoals een bod dat wel is), een
   platte in plaats van uitgesproken-uppercase label. */
.appointment-card{border:1px solid var(--border);border-left:3px solid var(--blue);border-radius:var(--radius);padding:11px 14px 11px 12px;margin-bottom:14px;background:var(--bg-soft);}
.appointment-label{font-size:12.5px;color:var(--ink-soft);font-weight:500;}
.appointment-datetime{font-size:14.5px;font-weight:600;color:var(--ink);margin-top:1px;}
/* Flatpickr in de eigen groene huisstijl i.p.v. de standaard blauwe kleuren (zie
   enhanceAppointmentDateInput()) — verder alleen kleur/afronding, geen structuurwijziging. Puur
   datum-kiezer (geen enableTime meer, zie de toelichting bij APPOINTMENT_TIME_OPTIONS), dus geen
   .flatpickr-time/.numInputWrapper-regels meer nodig. */
.flatpickr-calendar{border-radius:var(--radius);box-shadow:0 8px 24px rgba(0,0,0,.14);font-family:inherit;}
.flatpickr-day.selected,.flatpickr-day.selected:hover,.flatpickr-day.selected:focus{background:var(--green);border-color:var(--green);}
.flatpickr-day.today{border-color:var(--green);}
.flatpickr-day:hover{background:var(--green-light);}
/* BUGFIX (gemeld met screenshot, op verzoek: "kunnen we dit esthetisch beter maken?"): twee losse
   dingen. (1) #appt-time was een kale <select> zonder het site-eigen chevron-icoon (zie .form-row
   select) — gebruikte simpelweg de browser-standaardpijl, oogde daardoor net iets minder verzorgd
   dan elke andere dropdown op de site. (2) flatpickr's altInput (zie enhanceAppointmentDateInput(),
   altInput:true toont een EXTRA, apart <input>-element voor de leesbare datumtekst) kopieert alleen
   className van het origineel, geen inline style — de inline-styling die #appt-date zelf had, ging
   zo verloren zodra flatpickr actief is (zelfde categorie bug als eerder bij het tegenbod-veld:
   terugval op onopgemaakte kale styling). Beide velden delen nu .appt-field (rand/radius/padding/
   lettertype, gelijk aan de rest van de site se formulieren), en .appt-field wordt ook als
   altInputClass meegegeven zodat flatpickr's eigen kloon 'm gegarandeerd meekrijgt. */
/* min-width: zonder dit (gevonden tijdens het testen, en opnieuw nodig na #appt-time van <select>
   naar een gewoon tekstveld veranderen) kon dit veld, in de flex-rij met #appt-date, terugzakken
   tot een smal streepje — flex:1 samen met width:100% laat de browser een veel te smalle
   min-content-breedte berekenen zodra het element zelf geen bredere intrinsieke inhoud heeft om op
   te sturen (zoals een leeg/kort tekstveld, of — de eerdere variant van dit probleem — een
   select{appearance:none}, die z'n normale "breed genoeg voor de langste optie"-gedrag kwijtraakt).
   110px is ruim genoeg voor "UU:MM" + padding. Op .appt-field zelf i.p.v. een specifiek
   element-type, zodat elk veld dat de klasse gebruikt dit automatisch meekrijgt. */
.appt-field{width:100%;min-width:110px;padding:11px 12px;border:1px solid var(--border);border-radius:9px;font-family:inherit;font-size:14px;background:#fff;color:var(--ink);}
.appt-field:hover{border-color:var(--green);}
.appt-field:focus{outline:2px solid var(--green);outline-offset:1px;}
select.appt-field{
  padding-right:34px;appearance:none;-webkit-appearance:none;-moz-appearance:none;cursor:pointer;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none'><path d='M5 7l5 5 5-5' stroke='%2354655D' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat:no-repeat;background-position:right 12px center;background-size:13px;
}
.offer-input-row{display:flex;gap:8px;margin-top:8px;}
.offer-input-row input{flex:1;padding:11px 12px;border:1px solid var(--border);border-radius:9px;font-family:inherit;font-size:14px;}
.timeline{border-left:2px solid var(--border);padding-left:16px;margin-left:4px;}
.timeline .ev{position:relative;padding-bottom:18px;}
.timeline .ev:last-child{padding-bottom:0;}
.timeline .ev::before{content:'';position:absolute;left:-21px;top:3px;width:9px;height:9px;border-radius:50%;background:var(--green);}
.timeline .ev .d{font-family:var(--font-mono);font-size:11.5px;color:var(--ink-faint);}
.timeline .ev .t{font-size:14px;font-weight:500;margin-top:2px;}

/* ---- verification / trust badges ---- */
.verify-badge{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:600;padding:5px 10px;border-radius:999px;}
.verify-none{background:var(--bg-soft);color:var(--ink-soft);border:1px solid var(--border);}
.verify-seller{background:var(--blue-light);color:var(--blue);}
.verify-bikebase{background:var(--green-light);color:var(--green-dark);}
.verify-estimated{background:var(--amber-light);color:var(--amber);}
/* Ernst-badges voor meldingsredenen in #/admin (op verzoek, kritische blik: "een beheerder moet
   in één oogopslag de ernst kunnen zien, niet de rauwe database-waarde moeten lezen" — zie
   reportReasonBadgeHTML()). Zelfde .verify-badge-pilvorm, drie nieuwe kleurvarianten naast de
   bestaande verificatie-kleuren hierboven. */
.verify-danger{background:#fdeaea;color:#a83a3a;}
.verify-warn{background:var(--amber-light);color:var(--amber);}
.verify-neutral{background:var(--bg-soft);color:var(--ink-soft);}
.verify-badge .dot{width:6px;height:6px;border-radius:50%;background:currentColor;flex-shrink:0;}
.score-source-note{font-size:12px;color:var(--ink-faint);margin-top:8px;line-height:1.4;}
.score-breakdown{display:grid;grid-template-columns:1fr 1fr;gap:8px 16px;margin-top:14px;font-size:12.5px;}
.score-breakdown .row{display:flex;justify-content:space-between;color:var(--ink-soft);}
.score-breakdown .row b{color:var(--ink);font-family:var(--font-mono);}
/* Interactieve variant op de advertentiepagina (zie pageListing()): 1 kolom i.p.v. 2, met ruimte
   voor een concrete uitlegzin onder elk cijfer (scoreFactorNotes()). De 2-koloms versie hierboven
   blijft voor de statische homepage-voorbeeldkaart (bikeScoreExplainerHTML), die heeft geen losse
   uitleg nodig. */
.score-breakdown.with-notes{grid-template-columns:1fr;gap:12px;}
.score-breakdown.with-notes .row{flex-direction:column;align-items:flex-start;gap:2px;}
.score-breakdown.with-notes .row-label{display:flex;justify-content:space-between;width:100%;}
.score-breakdown.with-notes .row-note{color:var(--ink-faint);font-size:11.5px;line-height:1.4;}
.row-bar{width:100%;height:5px;border-radius:3px;background:var(--border);overflow:hidden;margin:2px 0 1px;}
.row-bar-fill{height:100%;border-radius:3px;}
/* De wheel-row wordt op de advertentiepagina een <button> (klik = op-/dichtklappen van de
   breakdown hierboven) — resets nodig omdat een <button> anders zijn eigen rand/achtergrond/
   uitlijning meebrengt, en :hover/[aria-expanded] geven een klikbaarheids-hint. */
.score-toggle{background:none;border:none;padding:0;margin-top:18px;font:inherit;text-align:left;width:100%;cursor:pointer;}
.score-toggle:hover .wheel-caption .label{color:var(--green-dark);}
.score-toggle-chevron{margin-left:auto;color:var(--ink-faint);flex-shrink:0;display:flex;transition:transform .15s;}
.score-toggle[aria-expanded="true"] .score-toggle-chevron{transform:rotate(180deg);}

/* ---- valuation page ---- */
.val-layout{display:grid;grid-template-columns:1fr 1fr;gap:36px;align-items:start;}
@media(max-width:880px){.val-layout{grid-template-columns:1fr;}}
.form-card{border:1px solid var(--border);border-radius:16px;padding:26px;}
/* Honeypot-veld tegen spambots: onzichtbaar voor mensen (off-screen, niet display:none — sommige
   bots slaan display:none-velden bewust over), maar een script dat "elk veld invullen" doet trapt
   er gewoon in. Blijft dit veld leeg, dan is de inzending vrijwel zeker door een mens gedaan. */
.hp-field{position:absolute;left:-9999px;top:-9999px;width:1px;height:1px;overflow:hidden;}
.form-row{margin-bottom:16px;}
.form-row label{display:block;font-size:13px;font-weight:600;color:var(--ink-soft);margin-bottom:6px;}
.form-row input,.form-row textarea,.form-row-2 input,.form-row-3 input{width:100%;padding:11px 12px;border:1px solid var(--border);border-radius:9px;font-size:14px;font-family:inherit;}
.form-row select,.form-row-2 select,.form-row-3 select{
  width:100%;padding:11px 34px 11px 12px;border:1px solid var(--border);border-radius:9px;font-size:14px;font-family:inherit;background-color:var(--white);color:var(--ink);
  appearance:none;-webkit-appearance:none;-moz-appearance:none;cursor:pointer;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none'><path d='M5 7l5 5 5-5' stroke='%2354655D' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat:no-repeat;background-position:right 12px center;background-size:13px;
}
.form-row select:hover,.form-row-2 select:hover,.form-row-3 select:hover{border-color:var(--green);}
.form-row select:focus,.form-row-2 select:focus,.form-row-3 select:focus{outline:2px solid var(--green);outline-offset:1px;}
.form-row select[disabled],.form-row-2 select[disabled],.form-row-3 select[disabled]{cursor:not-allowed;color:var(--ink-faint);background-color:var(--bg-soft);}
/* Zelfde BUGFIX als .msg-textarea hierboven: onbeperkt sleepbaar tekstvak begrensd met max-height. */
.form-row textarea{resize:vertical;min-height:80px;max-height:320px;overflow-y:auto;}
/* Opmaak-toolbar + contenteditable voor de beschrijving (zie wz-desc/sanitizeRichText()) — visueel
   gebouwd als één samenhangend veld (toolbar direct boven de editor, gedeelde rand), net als bij
   de meeste rich-text-editors. */
.richtext-toolbar{display:flex;gap:2px;border:1px solid var(--border);border-bottom:none;border-radius:9px 9px 0 0;padding:5px;background:var(--bg-soft);}
.richtext-toolbar button{width:40px;height:40px;border:none;border-radius:6px;background:transparent;color:var(--ink);font-size:16px;font-family:inherit;cursor:pointer;display:flex;align-items:center;justify-content:center;}
.richtext-toolbar button:hover{background:var(--white);}
.richtext-toolbar button.active{background:var(--green);color:#fff;}
.richtext-editor{width:100%;min-height:120px;max-height:340px;overflow-y:auto;padding:11px 12px;border:1px solid var(--border);border-radius:0 0 9px 9px;font-size:14px;font-family:inherit;line-height:1.5;}
.richtext-editor:focus{outline:none;border-color:var(--green);}
.richtext-editor:empty:before{content:attr(data-placeholder);color:var(--ink-faint);}
.richtext-editor ul,.listing-description-rich ul{margin:6px 0 6px 22px;padding:0;}
.richtext-editor li,.listing-description-rich li{margin-bottom:3px;}
.form-row-2{display:grid;grid-template-columns:1fr 1fr;gap:12px;}
.form-row-3{display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;}
/* BUGFIX (mobiele doorlichting): .form-row-3 had al een breakpoint, .form-row-2 miste 'm nog —
   bleef dus altijd 2 kolommen, ook op een smalle telefoon, waar velden onnodig krap werden. */
@media(max-width:560px){.form-row-2{grid-template-columns:1fr;}.form-row-3{grid-template-columns:1fr;}}
.result-card{border:1px solid var(--border);border-radius:16px;padding:28px;text-align:center;background:var(--bg-soft);}
.result-card .range{font-family:var(--font-mono);font-size:30px;font-weight:700;color:var(--green-deep);margin:14px 0 4px;}
.result-card .sub{font-size:13px;color:var(--ink-soft);}
.placeholder-result{border:1px dashed var(--border);border-radius:16px;padding:40px 24px;text-align:center;color:var(--ink-faint);font-size:14px;}
.info-tip{position:relative;display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;border-radius:50%;background:var(--border);color:var(--ink-soft);font-size:11px;font-weight:700;cursor:help;margin-left:6px;vertical-align:middle;}
.info-tip::before{content:'';position:absolute;inset:-14px;}
.info-tip .tip-bubble{
  visibility:hidden;opacity:0;
  /* BUGFIX (kritische blik op mobiel, n.a.v. echt-apparaat-onderzoek van de zoekpagina-overflow):
     stond hiervoor op position:absolute + left:50%/transform:translateX(-50%), gecentreerd op het
     16px-icoon zelf. Bij een icoon dicht bij de rechterrand (bv. "Framenummer"/"Batterijgezondheid"
     in de wizard op een smal scherm) stak deze vaste breedte van 230px alsnog voorbij de viewport
     uit — ONZICHTBAAR (visibility:hidden tot hover/focus) maar telt wél mee voor scrollWidth, dus
     de hele pagina kreeg een fantoom-horizontale-scroll (zelfde "moet de pagina verkleinen"-klacht
     als bij #sort-select, alleen via een andere route). Een ouder-element clippen (overflow-x)
     bleek geen veilige oplossing: getest, en dat breekt de bestaande sticky koopkaart/scorebalk
     (overflow-x op een niet-visible waarde dwingt overflow-y naar auto, wat de sticky-containing-
     block-keten van die balken kapotmaakt — leverde in Playwright een meetbare regressie op) én
     sommige tooltips (bv. "Algemene staat", stap 2) hebben net de ruimte BOVEN hun kaart nodig om
     zichtbaar te blijven, dus clippen op de kaart zelf sneed die juist af. In plaats daarvan nu
     position:fixed (viewport-relatief, dus immuun voor overflow/scroll-containers van voorouders)
     met de werkelijke positie berekend door positionTipBubble() bij hover/focus (event-delegatie,
     zie hieronder) — die klemt 'm altijd tussen 8px marge links/rechts van de viewport in. */
  position:fixed;
  background:var(--ink);color:#fff;font-size:12px;font-weight:400;line-height:1.45;padding:10px 12px;border-radius:8px;
  /* z-index ruim boven Leaflet's eigen controls/attributie (.leaflet-top/.leaflet-bottom, z-index:1000)
     zodat de tooltip ook boven een kaartje (bv. de afstandsfilter op de zoekpagina) gewoon één
     schone laag toont i.p.v. er half zichtbaar doorheen te breken. */
  width:230px;z-index:1100;transition:opacity .15s;pointer-events:none;
}
.info-tip:hover .tip-bubble,.info-tip:focus .tip-bubble{visibility:visible;opacity:1;}
.battery-estimate-link{background:none;border:none;color:var(--blue);font-size:12.5px;font-weight:600;padding:0;margin-top:6px;display:inline-block;}
.battery-estimate-link:hover{text-decoration:underline;}
.comparables-details summary{cursor:pointer;font-size:12.5px;color:var(--blue);font-weight:600;list-style:none;}
.comparables-details summary::-webkit-details-marker{display:none;}
.comparables-details summary::before{content:'▸ ';}
.comparables-details[open] summary::before{content:'▾ ';}
.comparable-row{display:flex;justify-content:space-between;gap:10px;font-size:13px;padding:7px 0;border-bottom:1px solid var(--border);text-align:left;}
.comparable-row:last-child{border-bottom:none;}

/* ---- dashboard ("Mijn advertenties") ---- */
.dash-tabs{display:flex;gap:6px;border-bottom:1px solid var(--border);margin-bottom:26px;}
.dash-tab{padding:11px 16px;font-size:14px;font-weight:600;color:var(--ink-soft);border-bottom:2px solid transparent;margin-bottom:-1px;cursor:pointer;}
.dash-tab.active{color:var(--green-dark);border-color:var(--green);}
/* box-shadow (op verzoek, kritische blik): badges zitten altijd bovenop een thumbnail (echte foto
   of, zonder foto, de groen-naar-blauw gradient hieronder) — een lichte kleur badge zonder eigen
   rand/schaduw kan daar makkelijk in wegvallen, ongeacht welke kleur de achtergrond toevallig
   heeft. Geldt voor alle 3 varianten, niet alleen .status-draft. */
.status-pill{font-size:11.5px;font-weight:600;padding:4px 9px;border-radius:999px;box-shadow:0 1px 4px rgba(0,0,0,.18);}
.status-live{background:var(--green-light);color:var(--green-dark);}
/* BUGFIX (gemeld, kritische blik op de dashboardkaart, 2 rondes): amber was hier gewoon een
   neutrale statuslabel-kleur ("dit is een concept"), terwijl diezelfde amber overal elders in de
   app (zie .verify-warn, ontbrekende-verplichte-velden, en de "dit concept staat al X dagen te
   wachten"-banner hieronder) juist "hier moet iets gebeuren" betekent. Elk vers concept oogde zo
   al als een waarschuwing, waardoor een ECHT stale concept zich niet meer onderscheidde. Neutraal
   grijs (zelfde tokens als .verify-neutral) i.p.v. amber — amber blijft zo exclusief voor een
   echte waarschuwing. RONDE 2 (eigen regressie, zelf gevonden): --bg-soft (#F4F8F6) bleek
   kleurmatig bijna identiek aan de thumbnail-gradient hierboven (--green-light/--blue-light) —
   de badge viel daardoor zelf bijna weg. De box-shadow hierboven lost het grootste deel al op;
   een dunne eigen rand bovenop geeft net dat beetje extra afbakening dat grijs-op-lichtgroen
   nodig heeft (live/sold hebben die niet nodig: hun kleur zit al ver genoeg van de gradient af). */
.status-draft{background:var(--bg-soft);color:var(--ink-soft);border:1px solid var(--border);}
.status-sold{background:var(--blue-light);color:var(--blue);}
.dash-stats{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-bottom:28px;}
@media(max-width:640px){.dash-stats{grid-template-columns:1fr;}}
.dash-stat{border:1px solid var(--border);border-radius:var(--radius);padding:20px 22px;background:var(--white);}
.dash-stat .k{font-family:var(--font-display);font-size:28px;font-weight:700;color:var(--green-deep);}
.dash-stat .l{font-size:13px;color:var(--ink-soft);margin-top:4px;}
.dash-listing-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px;}
@media(max-width:980px){.dash-listing-grid{grid-template-columns:repeat(2,1fr);}}
@media(max-width:640px){.dash-listing-grid{grid-template-columns:1fr;}}
/* BUGFIX (op verzoek, kritische esthetische blik): de stat-tegels bovenaan #/admin gebruikten
   grid-template-columns:repeat(auto-fit,minmax(140px,1fr)) — bij 6 tegels met wisselende
   tekstlengte (bv. "verkocht via TweedeWiel (waarvan X bekend)") kon dat bij bepaalde breedtes
   ongelijk afbreken. Vaste breekpunten, zelfde patroon als .dash-listing-grid hierboven: altijd
   een nette rechthoekige rij (3×2 op desktop, 2×3 op tablet, 1 kolom op mobiel), nooit een
   half-gevulde laatste rij door toeval van de beschikbare breedte. */
.admin-stat-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;}
@media(max-width:900px){.admin-stat-grid{grid-template-columns:repeat(2,1fr);}}
@media(max-width:480px){.admin-stat-grid{grid-template-columns:1fr;}}
.dash-card{border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;background:var(--white);transition:box-shadow .18s;}
.dash-card:hover{box-shadow:var(--shadow);}
/* BUGFIX (gemeld met screenshot, op verzoek: "kijk waar het prullenbakje staat" — stond per kaart
   op een andere hoogte, oogde chaotisch): in de 3-koloms-grid (.dash-listing-grid, display:grid)
   worden kaarten standaard op gelijke hoogte UITGEREKT (align-items:stretch is de grid-standaard),
   maar een kaart zonder "Volgende stap"/"staat al X dagen online"-tip heeft simpelweg minder
   inhoud — zonder dit hieronder bleef de actieknoppenrij (incl. prullenbak) gewoon direct ná die
   inhoud staan, dus hoger op een kortere kaart dan op een kaart mét zo'n tip. display:flex (alleen
   voor de GEWONE, verticale kaart — :not(.dash-card-horizontal) laat de aparte horizontale variant
   hieronder, met een heel andere layout, ongemoeid) + margin-top:auto op de actieknoppenrij
   (.dash-card-actions) duwt die altijd naar de onderkant van de kaart, ongeacht hoeveel tekst
   erboven staat — zo staat de knoppenrij/prullenbak bij elke kaart in de rij op exact dezelfde
   hoogte. */
.dash-card:not(.dash-card-horizontal){display:flex;flex-direction:column;}
.dash-card:not(.dash-card-horizontal) .dash-card-body{flex:1;display:flex;flex-direction:column;}
/* BUGFIX (gemeld: "het oranje gaat door de verder-afmaken-knop heen"): margin-top:auto hierboven
   verdeelt alleen ÉCHTE restruimte — zodra een kaart al vol staat (bv. de amber "dit concept
   staat al X dagen te wachten"-banner, die zelf al behoorlijk wat hoogte inneemt) is er niets te
   verdelen en wordt margin-top:auto in de praktijk 0px, waardoor de knoppenrij plakt tegen de
   banner erboven — precies het effect uit de melding. padding-top garandeert, los van hoeveel
   restruimte er is, altijd een minimale ademruimte. */
.dash-card:not(.dash-card-horizontal) .dash-card-actions{margin-top:auto;padding-top:10px;}
/* BUGFIX: zelfde reden als bij .card .thumb hierboven — aspect-ratio i.p.v. een vaste hoogte,
   zodat de object-fit:cover-crop niet harder toeslaat bij een bredere kaart. */
.dash-card-thumb{aspect-ratio:4/3;background:linear-gradient(135deg,var(--green-light),var(--blue-light));display:flex;align-items:center;justify-content:center;position:relative;overflow:hidden;}
.dash-card-thumb img{width:100%;height:100%;object-fit:cover;}
.dash-card-body{padding:14px;}
.dash-card-title{font-family:var(--font-display);font-weight:600;font-size:15px;margin-bottom:3px;}
.dash-card-meta{font-size:12.5px;color:var(--ink-soft);}
.dash-card-actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px;}
/* Horizontale variant (op verzoek, met screenshot: "deze balk is verticaal, de andere zijn
   horizontaal") — dezelfde dashListingCard() als op "Mijn advertenties" (foto boven, brede
   tekst-tips eronder die door de smalle 3-koloms-kaart al snel over meerdere regels braken en zo
   onnodig "log" oogden), maar dan met een kleine, vaste thumbnail links i.p.v. een volle-breedte
   foto bovenaan — zelfde bredere, horizontale opzet als de andere meldingkaarten (bv.
   priceSuggestionCardHTML) elders op #/meldingen. Gebruikt voor "Advertenties die aandacht nodig
   hebben"/"Concepten die wachten" (zie dash-listing-list hieronder), NIET voor de gewone
   fotogrid op "Mijn advertenties" — die blijft bewust ongewijzigd. */
.dash-listing-list{display:flex;flex-direction:column;gap:14px;}
.dash-card-horizontal{display:flex;align-items:stretch;}
/* BUGFIX (gemeld: "de foto is veelste slecht zichtbaar"): align-items:stretch hierboven rekte de
   thumb op tot de volle (vaak flink hogere, door de tip-/stale-tekstblokken) kaarthoogte — met
   aspect-ratio:auto werd dat dus een smalle, hoge kolom, en object-fit:cover croppede het grootste
   deel van de foto weg. align-self:flex-start + terug naar 4/3 houdt 'm klein en boven uitgelijnd,
   ongeacht hoe hoog de tekst ernaast wordt. */
.dash-card-horizontal .dash-card-thumb{width:120px;aspect-ratio:4/3;flex-shrink:0;align-self:flex-start;}
.dash-card-horizontal .dash-card-body{flex:1;min-width:0;padding:12px 14px;}
@media(max-width:560px){.dash-card-horizontal .dash-card-thumb{width:88px;}}
.dash-empty{text-align:center;padding:64px 24px;border:1px dashed var(--border);border-radius:var(--radius);background:var(--bg-soft);}
.dash-empty-icon{opacity:.45;margin-bottom:16px;display:flex;justify-content:center;}
.dash-empty h3{font-size:18px;margin-bottom:8px;}
.dash-empty p{color:var(--ink-soft);font-size:14px;max-width:380px;margin:0 auto 22px;}
/* Herontwerp (op verzoek, "kijk hier kritisch naar, kan het visueel aantrekkelijker"): was een
   vlakke grijze kaart zonder enige kleur-context — je zag wél het cijfer, maar niet in één
   oogopslag hoe dicht dat bij de volgende kleurgrens zit. Nu: een dunne kleurbalk bovenaan de
   kaart + een gesegmenteerde voortgangsbalk (zie .score-gauge-*) die de 60/80-grenzen letterlijk
   toont, allebei in de kleur van de HUIDIGE score (scoreColor(), dus dezelfde 3 niveaus als het
   wieltje zelf) — dit is puur visueel, geen nieuwe databron. */
.live-score-card{border:1px solid var(--border);border-top:4px solid var(--score-color, var(--green));border-radius:var(--radius);padding:16px 18px;margin-bottom:22px;background:var(--bg-soft);}
.live-score-top{display:flex;align-items:center;gap:14px;flex-wrap:wrap;}
.live-score-num{font-family:var(--font-mono);font-size:24px;font-weight:700;}
.live-score-num span{font-size:12px;color:var(--ink-faint);font-weight:500;}
.live-score-label{font-size:11px;color:var(--ink-soft);text-transform:uppercase;letter-spacing:.04em;font-weight:600;}
.live-score-market{margin-left:auto;text-align:right;}
.score-gauge-bar{position:relative;height:8px;border-radius:999px;overflow:hidden;margin:14px 0 5px;
  background:linear-gradient(to right, var(--amber-light) 0%, var(--amber-light) 60%, #E3EDF6 60%, #E3EDF6 80%, var(--green-light) 80%, var(--green-light) 100%);}
.score-gauge-fill{position:absolute;inset:0;width:0;border-radius:999px;transition:width .6s cubic-bezier(.2,0,.1,1);}
.score-gauge-tick{position:absolute;top:0;bottom:0;width:2px;background:rgba(255,255,255,.85);}
.score-gauge-next{font-size:11.5px;color:var(--ink-faint);margin-bottom:2px;}
.score-tips{margin-top:14px;padding-top:12px;border-top:1px solid var(--border);display:flex;flex-direction:column;gap:8px;}
.score-tip{font-size:12.5px;color:var(--ink-soft);display:flex;align-items:center;gap:10px;}
.score-tip-badge{flex-shrink:0;min-width:30px;height:24px;padding:0 6px;border-radius:999px;background:var(--green);color:#fff;font-family:var(--font-mono);font-weight:700;font-size:12px;display:flex;align-items:center;justify-content:center;}
.score-tip-body{flex:1;min-width:0;}
/* Live-score-preview naast de velden i.p.v. erboven (kritische blik op efficiëntere indeling, op
   verzoek): op stap 2-4 beïnvloedt bijna elk veld realtime de BikeScore/marktwaarde, maar die kaart
   stond steeds bovenaan, los van de velden die 'm veranderen — op een breed scherm moest je
   terugscrollen om te zien of een wijziging punten opleverde, terwijl er sowieso lege marge naast
   de smalle, gecentreerde kolom overbleef. Nu op brede schermen een 2-koloms indeling (velden
   links, score-kaart sticky rechts — zelfde patroon als de koopkaart op de advertentiepagina); op
   mobiel/smallere schermen (zie ook wizard-wrap's dynamische max-width in renderWizard()) blijft
   het gewoon gestapeld zoals voorheen. */
.wizard-step-layout{display:grid;grid-template-columns:1fr;gap:20px;align-items:start;}
@media(min-width:900px){
  .wizard-step-layout{grid-template-columns:1fr 280px;}
  .wizard-score-sidebar{position:sticky;top:86px;}
  .wizard-score-sidebar .live-score-card{margin-bottom:0;}
}
.checklist{margin:14px 0 4px;}
.checklist-item{display:flex;align-items:center;gap:10px;padding:8px 0;border-bottom:1px solid var(--border);font-size:13.5px;flex-wrap:wrap;}
.checklist-item:last-child{border-bottom:none;}
.checklist-item .mark{width:18px;height:18px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:11px;flex-shrink:0;font-weight:700;}
.checklist-item.filled .mark{background:var(--green-light);color:var(--green-dark);}
.checklist-item.missing-required .mark{background:var(--amber-light);color:var(--amber);}
.checklist-item.missing-optional .mark{background:var(--bg-soft);color:var(--ink-faint);}
.checklist-item .txt{flex:1;min-width:140px;}
.checklist-item .note{font-size:12px;color:var(--ink-faint);}
.wizard-steps{display:flex;gap:0;margin-bottom:26px;flex-wrap:wrap;}
/* BUGFIX (gemeld met screenshot van een echt mobiel toestel: "de nummering ziet er rommelig uit"):
   op smalle schermen wrappen sommige labels wél naar 2 regels ("Staat & techniek", "Onderhoud") en
   andere niet ("Fiets", "Foto's", "Publiceren") — allemaal in dezelfde flex-rij, dus technisch even
   hoog, maar de korte labels bleven bovenaan hun cel "plakken" terwijl de gewrapte ernaast wél 2
   regels vulden. Dat oogde grillig/ongelijk, ook al was de hoogte zelf identiek. Een vaste
   minimumhoogte (voor 2 regels) + verticaal centreren zorgt dat een kort, niet-wrappend label
   precies zo hoog in zijn cel staat als een gewrapt label — geen wrap-onderdrukking nodig, gewoon
   een consistente, voorspelbare cel ongeacht hoeveel regels een specifiek label toevallig nodig
   heeft. */
.wstep{flex:1;min-width:70px;text-align:center;font-size:11.5px;font-weight:600;color:var(--ink-faint);padding-bottom:10px;border-bottom:3px solid var(--border);cursor:pointer;transition:color .15s;display:flex;align-items:center;justify-content:center;min-height:32px;line-height:1.3;}
.wstep:hover{color:var(--green-dark);}
.wstep.active{color:var(--green-dark);border-color:var(--green);}
.wstep.done{color:var(--ink-soft);border-color:var(--green-light);}
/* Op verzoek, kritische blik op mobiel (zie de toelichting bij renderWizard()): op smalle
   schermen vervangt deze voortgangsbalk + huidige stapnaam de tikbare .wizard-steps-rij hierboven
   volledig — desktop (>760px) blijft ongewijzigd, .wizard-steps zelf is niet aangepast. */
.wizard-progress-mobile{display:none;}
.wizard-progress-track{width:100%;height:6px;border-radius:999px;background:var(--border);overflow:hidden;margin-bottom:8px;}
.wizard-progress-fill{height:100%;border-radius:999px;background:var(--green);transition:width .2s;}
.wizard-progress-label{font-size:13.5px;font-weight:600;color:var(--ink);}
@media(max-width:760px){
  .wizard-steps{display:none;}
  .wizard-progress-mobile{display:block;margin-bottom:22px;}
}
.field-note{font-size:12px;color:var(--ink-faint);margin-top:5px;}
/* BUGFIX: de globale "p{margin:0;}"-reset (zie hierboven) liet elke alinea in een langere
   tekstpagina (Ons verhaal, Veilig handelen, Privacy, Voorwaarden) zonder enige ruimte ertussen
   renderen — leek daardoor visueel één ononderbroken tekstblok, ook al stond de HTML gewoon in
   losse <p>-tags. Gedeelde class i.p.v. dezelfde inline style 4x herhalen. */
.longform-text p{margin-bottom:16px;}
.req{color:var(--amber);}
.field-invalid{border-color:#c0392b !important;background:#fdeaea !important;}
.toast{
  position:fixed;bottom:24px;left:50%;transform:translateX(-50%) translateY(20px);background:var(--green-deep);color:#fff;
  padding:13px 20px;border-radius:14px;font-size:14px;font-weight:500;box-shadow:var(--shadow);opacity:0;pointer-events:none;
  transition:opacity .25s, transform .25s;z-index:100;display:flex;align-items:center;gap:8px;max-width:min(85vw,380px);text-align:center;
}
/* BUGFIX (gemeld: "die zwarte berichten zijn heel hinderlijk, veel te veel tekst"): met de volle
   border-radius:999px (een pil) blies een langer bericht — wat sowieso al niet zou moeten
   voorkomen, zie toast() hierboven — op tot een storende, bijna schermvullende ovale bubbel.
   14px radius + een krappere max-width lossen dat op als vangnet, ook als een toekomstig
   berichtje toch iets langer uitvalt dan bedoeld. */
.toast.show{opacity:1;transform:translateX(-50%) translateY(0);}

/* Foto-upload: op verzoek herbouwd tot één doorlopend grid (Marktplaats-vergelijking, "kijk hier
   kritisch naar") i.p.v. drie aparte blokken die dezelfde foto's tot 3x toonden (6 categorie-
   vakjes + een losse "extra foto's"-sectie + een derde "volgorde bepalen"-strook). Nu is elke
   tegel gelijkwaardig: gevuld (.filled, een echte foto, versleepbaar om te herordenen) of de ene
   "+"-tegel aan het eind om meer toe te voegen. auto-fill i.p.v. een vast aantal kolommen, want
   het aantal foto's varieert nu (0 tot 18) i.p.v. altijd precies 6 vaste vakjes. */
.photo-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(96px,1fr));gap:10px;border-radius:10px;transition:background .15s;}
.photo-grid.drag-over{background:var(--green-light);}
.photo-tile{
  position:relative;border:2px dashed var(--border);border-radius:12px;aspect-ratio:1/1;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  cursor:pointer;overflow:hidden;background:var(--bg-soft);
  transition:border-color .15s, background .15s;
}
.photo-tile.add-tile input{position:absolute;inset:0;opacity:0;cursor:pointer;}
.photo-tile.add-tile .add-icon{font-size:26px;color:var(--ink-faint);pointer-events:none;}
.photo-tile.filled{border-style:solid;border-color:var(--green);cursor:grab;}
.photo-tile.filled:active{cursor:grabbing;}
.photo-tile.filled img{width:100%;height:100%;object-fit:cover;pointer-events:none;}
.photo-tile .cover-badge{position:absolute;bottom:0;left:0;right:0;background:var(--green-deep);color:#fff;font-size:9.5px;font-weight:700;text-align:center;padding:3px 0;text-transform:uppercase;letter-spacing:.03em;pointer-events:none;}
/* Permanent label per tegel (ipv alleen een transient toast na upload melden "Toegewezen:
   Linkerkant → Rechterkant → ...") — gemeld als "veel te veel tekst" in die ene grote toast-
   bubbel. De toewijzing is nu in één oogopslag op de foto zelf te zien, blijft zichtbaar na
   herordenen/verversen, en is nooit gemist omdat 'ie niet meer vanzelf verdwijnt. */
.photo-tile .type-badge{position:absolute;top:0;left:0;right:0;background:rgba(23,58,44,.72);color:#fff;font-size:8.5px;font-weight:600;text-align:center;padding:3px 4px;line-height:1.3;pointer-events:none;}
.photo-tile .check{position:absolute;top:6px;right:6px;background:var(--green);color:#fff;width:18px;height:18px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:11px;}
.photo-tile .remove-photo{position:absolute;top:6px;left:6px;z-index:2;background:rgba(0,0,0,.55);color:#fff;width:20px;height:20px;border-radius:50%;border:none;display:flex;align-items:center;justify-content:center;font-size:13px;line-height:1;cursor:pointer;padding:0;}
.photo-tile .remove-photo:hover{background:rgba(0,0,0,.75);}
/* BUGFIX (kritische blik op mobiel, op verzoek): het slepen van tegels ("photoDragStart" e.a.
   hieronder) is de native HTML5-drag-and-drop-API — die vuurt op een touchscreen (iOS Safari,
   de meeste Android-browsers) simpelweg niet vanuit een vinger, alleen vanuit een muis. Op een
   telefoon kon je dus NOOIT de omslagfoto wijzigen zodra je meerdere foto's had geüpload, terwijl
   de tekst eronder ("Sleep een foto...") iets belooft wat niet werkte. Deze knoppen zijn de
   tik-vriendelijke vervanger; slepen met de muis blijft daarnaast gewoon werken voor desktop. */
.photo-tile .move-btn{position:absolute;bottom:6px;z-index:2;background:rgba(0,0,0,.55);color:#fff;width:32px;height:32px;border-radius:50%;border:none;display:flex;align-items:center;justify-content:center;font-size:16px;line-height:1;cursor:pointer;padding:0;}
.photo-tile .move-btn:hover{background:rgba(0,0,0,.75);}
.photo-tile .move-btn.move-left{left:6px;}
.photo-tile .move-btn.move-right{right:6px;}
.photo-tile.confirm-remove .move-btn{visibility:hidden;}
/* Inline bevestiging i.p.v. de native confirm() (kritische blik op de rest van de site, zelfde
   patroon als showReopenConfirm elders) — hier als volledige overlay over de tegel zelf, want een
   los bevestigingsvakje past niet naast een foto van maar een paar centimeter breed. */
.photo-tile .photo-remove-confirm{
  display:none;position:absolute;inset:0;z-index:3;background:rgba(23,58,44,.85);color:#fff;
  flex-direction:column;align-items:center;justify-content:center;gap:8px;font-size:12px;font-weight:600;padding:8px;text-align:center;
}
.photo-tile.confirm-remove .photo-remove-confirm{display:flex;}
.photo-tile.confirm-remove .remove-photo,.photo-tile.confirm-remove .check{visibility:hidden;}
.photo-remove-confirm .actions{display:flex;gap:6px;}
.photo-remove-confirm button{background:#fff;color:var(--ink);border:none;border-radius:6px;padding:4px 10px;font-size:12px;font-weight:600;cursor:pointer;}
/* BUGFIX (kritische blik op de wizard, op verzoek): overal elders in de app toont
   withBusyButton() een "Bezig..."-status tijdens een async actie (bericht sturen, bod doen,
   publiceren) — foto-uploaden had dat niet: de tegel bleef gewoon leeg staan tot de upload
   (comprimeren + wegschrijven) ineens klaar was, zonder enig signaal dat er iets gebeurde. Bij een
   groter bestand of trage verbinding kon dat aanvoelen alsof de klik niet was aangekomen. Sinds het
   ene-grid-herontwerp hierboven bestaat er geen vooraf getekend leeg vakje per categorie meer om
   deze klasse op te zetten — assignFilesToEmptySlots() maakt nu zelf tijdelijke lege
   .photo-tile.uploading-tegels aan vlak vóór de upload start, die bij een geslaagde renderWizard()
   vanzelf door de echte foto-tegel vervangen worden. */
.photo-tile.uploading{pointer-events:none;}
.photo-tile.uploading::after{
  content:'Bezig...';position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  font-size:11.5px;font-weight:600;color:var(--ink-soft);background:rgba(255,255,255,.85);
}

/* demo account note */
.demo-note{background:var(--blue-light);border:1px solid #C6DBEC;border-radius:10px;padding:12px 14px;font-size:12.5px;color:var(--blue);margin-bottom:18px;}

/* maintenance entries */
.maint-entry{display:flex;align-items:center;gap:10px;border:1px solid var(--border);border-radius:8px;padding:9px 12px;margin-bottom:8px;font-size:13px;}
.maint-entry .d{font-family:var(--font-mono);color:var(--ink-faint);flex-shrink:0;}
.maint-entry button{margin-left:auto;background:none;border:none;color:var(--amber);font-weight:600;font-size:12px;}
.maint-add{display:grid;grid-template-columns:190px 1fr auto;gap:8px;margin-top:10px;align-items:end;}
@media(max-width:560px){.maint-add{grid-template-columns:1fr;}}
.maint-add input{width:100%;padding:11px 12px;border:1px solid var(--border);border-radius:9px;font-size:14px;font-family:inherit;}
.maint-add select{
  width:100%;padding:11px 30px 11px 12px;border:1px solid var(--border);border-radius:9px;font-size:14px;font-family:inherit;background-color:var(--white);color:var(--ink);
  appearance:none;-webkit-appearance:none;-moz-appearance:none;cursor:pointer;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none'><path d='M5 7l5 5 5-5' stroke='%2354655D' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat:no-repeat;background-position:right 10px center;background-size:12px;
}
.maint-add select:hover{border-color:var(--green);}
.maint-add label{display:block;font-size:12px;font-weight:600;color:var(--ink-soft);margin-bottom:6px;}

/* footer */
footer{border-top:1px solid var(--border);padding:32px 0;margin-top:auto;}
footer .row{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:12px;}
footer .brand{font-family:var(--font-display);font-weight:700;color:var(--green-deep);}
footer .note{font-size:12.5px;color:var(--ink-faint);}
/* Vervangt de vorige platte rij van 5 losse linkjes (voelde als een ongeorganiseerde grabbel-
   bak: merkverhaal, veiligheidsgids, contact en 2 juridische documenten allemaal even zwaar naast
   elkaar) door echte, gelabelde kolommen — hergebruikt dezelfde kleine-hoofdletters-labelstijl als
   .wheel-caption .label elders in de app, voor consistentie. */
.footer-grid{display:grid;grid-template-columns:1.3fr 1fr 1fr;gap:32px;}
@media(max-width:700px){.footer-grid{grid-template-columns:1fr;gap:28px;}}
.footer-grid .col-label{font-size:11.5px;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-faint);font-weight:600;margin-bottom:12px;}
.footer-grid a{display:block;font-size:13px;color:var(--ink-soft);margin-bottom:9px;}
.footer-grid a:hover{color:var(--green-dark);}

/* section subtitle icon bullets */
.iconrow{display:flex;align-items:center;gap:8px;font-size:13.5px;color:var(--ink-soft);}
.iconrow svg{flex-shrink:0;}
.field-grid-2{display:grid;grid-template-columns:1fr 1fr;gap:16px;}
@media(max-width:560px){.field-grid-2{grid-template-columns:1fr;}}

.section-alt{background:var(--bg-soft);border-top:1px solid var(--border);border-bottom:1px solid var(--border);}

/* Generieke invoer-modal (bv. "naam voor deze zoekopdracht") — vervangt de kale, niet te
   stylen browser-prompt() door iets dat bij de rest van de app past. */
.modal-overlay{position:fixed;inset:0;background:rgba(23,58,44,.35);display:flex;align-items:center;justify-content:center;padding:16px;z-index:1000;}
.modal-box{background:#fff;border-radius:14px;padding:24px;max-width:400px;width:100%;box-shadow:0 20px 50px -12px rgba(0,0,0,.35);}
.modal-box h3{font-size:16px;margin-bottom:14px;}
/* Zonder eigen styling viel dit terug op de kale browser-default (scherpe zwarte rand, geen
   afronding) — nu dezelfde stijl als elk ander invoerveld op de site (zie .form-row input). */
.modal-box input{width:100%;margin-bottom:16px;padding:11px 12px;border:1px solid var(--border);border-radius:9px;font-size:14px;font-family:inherit;}
.modal-box input:focus{outline:2px solid var(--green);outline-offset:1px;}
.modal-box .actions{display:flex;gap:8px;justify-content:flex-end;}
