/* toiletvergelijken.nl — "Frisse Winkel"
 *
 * Design direction chosen from design/moodboards/board-10-frisse-winkel.html.
 * One stylesheet, no framework, self-hosted fonts. Everything hangs off the token
 * block, so a future direction change is a token swap plus component tweaks.
 *
 * The one rule that carries the whole system: **amber means "you are leaving to
 * a shop to buy"**. Nothing else on the site is amber. Blue is for our own
 * actions (navigate, compare, subscribe). A reader learns that distinction in
 * two pages and then never has to think about it again.
 *
 * Light only. The brief was clean, fresh, bright; a dark variant would need its
 * own validated palette rather than an inversion, so it is deliberately absent.
 */

/* ---------- fonts (self-hosted: the CSP is font-src 'self', so a font never
     comes from someone else's server) ---------- */
@font-face {
  font-family: "Plus Jakarta Sans";
  src: url("/static/fonts/jakarta-var-latin.woff2") format("woff2");
  font-weight: 600 800;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
    U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Inter";
  src: url("/static/fonts/inter-var-latin.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
    U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  color-scheme: light;

  /* surfaces */
  --white: #ffffff;
  --tint: #f2f6fd;        /* page plane, table heads, quiet bands */
  --tint-2: #e9f0fb;
  --line: #cbd8ec;
  --line-soft: #e2eaf7;

  /* ink */
  --ink: #0b1b33;         /* headings and prices */
  --ink-2: #46566e;       /* body */
  --muted: #5f6e85;       /* captions — 4.9:1 on white, still AA */

  /* blue — our own actions */
  --blue: #0b57e3;        /* 5.6:1 on white */
  --blue-deep: #0a47b8;
  --blue-soft: #e8f0fe;

  /* amber — the outgoing buy button, and nothing else.
     White on amber is 1.83:1, which is why the label is near-black ink (9.3:1)
     and the ring makes the control shape itself clear 3:1 against white. */
  --amber: #ffb020;
  --amber-hover: #f5a000;
  --amber-ink: #2a1800;
  --amber-ring: #8a4b00;
  --amber-soft: #fff4de;

  /* status */
  --green: #067647;
  --green-soft: #e6f6ee;
  --red: #b3261e;
  --red-soft: #fdecea;

  /* shape — squarer and tighter than the calm directions */
  --r: 12px;
  --r-btn: 10px;
  --r-badge: 6px;
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px;
  --sp-5: 24px; --sp-6: 36px; --sp-7: 56px;
  --sh-1: 0 1px 2px rgba(11,27,51,.07);
  --sh-2: 0 10px 24px -16px rgba(11,27,51,.45);
  --dur: 140ms;
  --ease: cubic-bezier(.2,.7,.3,1);
  --wrap: 1200px;

  --font-display: "Plus Jakarta Sans", "Segoe UI", system-ui, -apple-system, Roboto, Helvetica, Arial, sans-serif;
  --font-text: "Inter", "Segoe UI", system-ui, -apple-system, Roboto, Helvetica, Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;

  /* test-environment banner only — deliberately its own colour. Not amber
     (that means "buy"), not red (that means "error"/"geen voorraad"), not blue
     (that means "our own action"): nothing else on the site may use this. */
  --test: #6d28d9;
  --test-soft: #ede9fe;
}

* { box-sizing: border-box; }
/* The `hidden` attribute has to beat any `display` we set ourselves.
   The browser's own rule is `[hidden] { display: none }`, which loses to any
   author rule of equal specificity — and `.card { display: flex }` is exactly
   that. Result: the /douchewc filter set `card.hidden = true` on every card it
   wanted gone and nothing moved, so filtering looked broken while working
   perfectly. Fix it once, here, rather than per component. */
[hidden] { display: none !important; }
html {
  scroll-behavior: smooth;
  /* Without this every anchor on the site lands underneath the sticky header:
     64px of chrome plus a line of breathing room. Counts for hash jumps and for
     scrollIntoView alike, so the rule only has to exist once. */
  scroll-padding-top: calc(64px + var(--sp-4));
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

body {
  margin: 0;
  background: var(--tint);
  color: var(--ink-2);
  font: 16px/1.62 var(--font-text);
  -webkit-text-size-adjust: 100%;
  -webkit-font-smoothing: antialiased;
}

.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 20px; }
.narrow-col { max-width: 760px; }
img, svg { max-width: 100%; height: auto; }

a { color: var(--blue); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: var(--blue-deep); text-decoration-thickness: 2px; }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  color: var(--ink);
  line-height: 1.14;
  letter-spacing: -.022em;
  margin: 0 0 .45em;
  font-weight: 800;
}
h1 { font-size: clamp(1.9rem, 1.25rem + 2.6vw, 2.85rem); }
h2 { font-size: clamp(1.35rem, 1.1rem + 1.1vw, 1.85rem); }
h3 { font-size: 1.12rem; font-weight: 700; }
h4 { font-size: 1rem; font-weight: 700; }
p { margin: 0 0 1em; }

.lede { font-size: 1.12rem; line-height: 1.6; color: var(--ink-2); max-width: 66ch; }
.muted { color: var(--muted); }
.small { font-size: .875rem; }
.strong { font-weight: 700; }
.block { display: block; }
.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.eyebrow {
  font-family: var(--font-display);
  font-size: .78rem; font-weight: 800; text-transform: uppercase;
  letter-spacing: .09em; color: var(--blue); margin: 0 0 .5em;
}

.sr-only, .skip:not(:focus) {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip:focus {
  position: fixed; top: 8px; left: 8px; z-index: 100; background: var(--blue);
  color: #fff; padding: 10px 16px; border-radius: var(--r-btn); font-weight: 700;
}
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }

:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 3px solid var(--blue); outline-offset: 2px; border-radius: 4px;
}

/* ---------- test-environment banner ----------
   Shown on every environment that is not production (preview deploys, local
   dev). Scrolls away with the page on purpose — the sticky header is what a
   reader expects to stay put, not a warning stripe. */
.env-bar { background: var(--test); color: #fff; }
.env-bar-inner {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: var(--sp-3); padding: var(--sp-2) 20px; text-align: center;
}
.env-bar-msg { margin: 0; font-size: .9rem; }
.env-bar-btn { background: #fff; color: var(--test); border-color: #fff; }
.env-bar-btn:hover { background: var(--test-soft); color: var(--test); }

/* ---------- header ---------- */
.site-header {
  position: sticky; top: 0; z-index: 40; background: rgba(255,255,255,.92);
  backdrop-filter: blur(10px); border-bottom: 1px solid var(--line-soft);
}
.header-inner { display: flex; align-items: center; gap: var(--sp-4); min-height: 64px; }
.brand {
  display: inline-flex; align-items: center; gap: 9px; font-family: var(--font-display);
  font-size: 1.06rem; font-weight: 700; color: var(--ink); text-decoration: none;
}
.brand:hover { color: var(--ink); }
.brand strong { font-weight: 800; }
.brand-mark { color: var(--blue); flex: none; }
.tld { color: var(--muted); font-weight: 600; }
.nav { margin-left: auto; display: flex; gap: 2px; align-items: center; flex-wrap: wrap; }
.nav a {
  color: var(--ink-2); text-decoration: none; padding: 9px 13px;
  border-radius: var(--r-btn); font-size: .94rem; font-weight: 600;
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.nav a:hover { background: var(--blue-soft); color: var(--blue-deep); }
.nav .nav-cta { background: var(--blue); color: #fff; }
.nav .nav-cta:hover { background: var(--blue-deep); color: #fff; }
.nav-toggle {
  display: none; margin-left: auto; background: var(--white); border: 1px solid var(--line);
  color: var(--ink); border-radius: var(--r-btn); padding: 8px; cursor: pointer;
  min-width: 44px; min-height: 44px; align-items: center; justify-content: center;
}
@media (max-width: 880px) {
  .nav-toggle { display: inline-flex; }
  .nav { display: none; width: 100%; flex-direction: column; align-items: stretch; padding-bottom: var(--sp-3); }
  .nav.open { display: flex; }
  .nav a { padding: 13px; }
}

/* ---------- buttons ----------
   Hierarchy: buy (amber, outgoing) > primary (blue, ours) > ghost > link. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 12px 20px; min-height: 44px; border-radius: var(--r-btn);
  border: 1px solid transparent; font: inherit; font-family: var(--font-display);
  font-weight: 700; text-decoration: none; cursor: pointer; white-space: nowrap;
  transition: background-color var(--dur) var(--ease), border-color var(--dur) var(--ease),
    transform 60ms var(--ease), box-shadow var(--dur) var(--ease);
}
.btn:active { transform: translateY(1px); }
.btn-primary { background: var(--blue); color: #fff; }
.btn-primary:hover { background: var(--blue-deep); color: #fff; }
.btn-buy {
  background: var(--amber); color: var(--amber-ink);
  border-color: var(--amber-ring); box-shadow: var(--sh-1);
}
.btn-buy:hover { background: var(--amber-hover); color: var(--amber-ink); }
.btn-buy::after {
  content: ""; width: 13px; height: 13px; flex: none;
  background: currentColor; opacity: .75;
  -webkit-mask: var(--icon-external) center/contain no-repeat;
  mask: var(--icon-external) center/contain no-repeat;
}
:root {
  --icon-external: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'/%3E%3Cpolyline points='15 3 21 3 21 9'/%3E%3Cline x1='10' y1='14' x2='21' y2='3'/%3E%3C/svg%3E");
}
.btn-ghost { background: var(--white); border-color: var(--line); color: var(--ink); }
.btn-ghost:hover { background: var(--tint); border-color: var(--muted); color: var(--ink); }
.btn-sm { padding: 8px 14px; min-height: 38px; font-size: .875rem; }
.btn-lg { padding: 15px 26px; min-height: 52px; font-size: 1.06rem; }
.linklike {
  background: none; border: 0; color: var(--blue); text-decoration: underline;
  cursor: pointer; font: inherit; padding: 0;
}

/* ---------- badges ---------- */
.badge {
  display: inline-flex; align-items: center; gap: 5px; font-family: var(--font-display);
  font-size: .72rem; font-weight: 800; letter-spacing: .02em; padding: 4px 9px;
  border-radius: var(--r-badge); line-height: 1.35;
}
.badge-deal { background: var(--amber-soft); color: #6b3a00; border: 1px solid #e8c88b; }
.badge-stock { background: var(--green-soft); color: var(--green); }
.badge-nostock { background: var(--red-soft); color: var(--red); }
.badge-score { background: var(--blue); color: #fff; }
.badge-new { background: var(--blue-soft); color: var(--blue-deep); }
/* A neutral fact, not a claim: unlike deal/stock/nostock this colour says
   nothing about whether it's good news, just that it's worth reading. */
.badge-info { background: var(--blue-soft); color: var(--blue-deep); }

/* ---------- hero ---------- */
.hero {
  background: linear-gradient(180deg, var(--blue-soft) 0%, rgba(232,240,254,0) 100%);
  border-bottom: 1px solid var(--line-soft); padding: var(--sp-7) 0 var(--sp-6);
}
.hero h1 { margin-bottom: .15em; }
.hero-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: var(--sp-4); flex-wrap: wrap; margin-bottom: var(--sp-5);
}
.hero-head .lede { margin: 0; }
/* Naast de kop een kolom, eronder een rij: op een brede pagina hoort dit naast
   de belofte te staan, op een telefoon eronder en niet in vier regels. */
.trust-row {
  list-style: none; display: flex; flex-direction: column; gap: 4px;
  padding: 0; margin: 0 0 6px; color: var(--ink-2); font-size: .9rem; font-weight: 500;
}
.trust-row li::before { content: "✓ "; color: var(--green); font-weight: 800; }
@media (max-width: 720px) {
  .trust-row { flex-direction: row; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4); }
}

/* De twee routes en de twee checks. Zelfde tegel, alleen de linkerkolom
   verschilt: een productfoto voor een categorie, een icoon voor een hulpmiddel. */
.routes { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: var(--sp-4); }
@media (max-width: 860px) { .routes { grid-template-columns: 1fr; } }
.route {
  position: relative; display: flex; gap: var(--sp-4); align-items: center;
  background: var(--white); border: 1px solid var(--line); border-radius: var(--r);
  padding: var(--sp-5); text-decoration: none; color: var(--ink); box-shadow: var(--sh-1);
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.route:hover { border-color: var(--blue); box-shadow: var(--sh-2); color: var(--ink); }
.route-img { width: 132px; height: 122px; object-fit: contain; flex: 0 0 auto; }
.route-ico {
  width: 66px; height: 66px; flex: 0 0 auto; border-radius: 14px;
  background: var(--blue-soft); color: var(--blue); display: grid; place-items: center;
}
.route-body { min-width: 0; }
.route-h {
  display: block; font-family: var(--font-display); font-size: 1.28rem; font-weight: 800;
  color: var(--ink); letter-spacing: -.022em; line-height: 1.14; margin-bottom: .25em;
}
.route-sub { display: block; color: var(--ink-2); font-size: .95rem; margin-bottom: .7em; }
.route-meta { display: flex; gap: 8px; flex-wrap: wrap; }
.route-meta span {
  background: var(--tint); border-radius: var(--r-badge); padding: 3px 10px;
  font-size: .8rem; font-weight: 700; color: var(--ink-2);
}
.route-meta .route-from { background: var(--green-soft); color: var(--green); }
.route-go { position: absolute; right: 16px; top: 14px; color: var(--blue); font-weight: 800; font-size: 1.05rem; }

/* ---------- sections ---------- */
.section { padding: var(--sp-6) 0; }
.page-head { padding: var(--sp-5) 0 var(--sp-2); }
.section-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--sp-4); flex-wrap: wrap; margin-bottom: var(--sp-4);
}
.section-link { font-size: .94rem; font-weight: 700; text-decoration: none; }
.band { background: var(--white); border-block: 1px solid var(--line-soft); padding: var(--sp-7) 0; margin: var(--sp-6) 0; }
.band-quiet { background: var(--blue-soft); border-color: transparent; }
.crumbs { font-size: .85rem; color: var(--muted); margin-bottom: var(--sp-2); }
.crumbs a { color: var(--muted); }
.crumbs a:hover { color: var(--blue); }

.grid { display: grid; gap: var(--sp-4); }
.cards { grid-template-columns: repeat(auto-fill, minmax(255px, 1fr)); }
.brand-grid { grid-template-columns: repeat(auto-fill, minmax(215px, 1fr)); }

/* ---------- product card ---------- */
.card {
  background: var(--white); border: 1px solid var(--line-soft); border-radius: var(--r);
  overflow: hidden; position: relative; display: flex; flex-direction: column;
  transition: border-color var(--dur) var(--ease);
}
/* Hover: rand + titel, geen schaduw en geen lift. De kaart hoort geen knop te
   worden — de amber koopknop erin is de knop, en die draagt al --sh-1. Zet je
   --sh-2 onder de hele kaart, dan wint de kaart het van zijn eigen knop.
   Toetsenbordfocus krijgt hetzelfde antwoord als de muis. */
.card:is(:hover, :focus-within) { border-color: var(--line); }
.card:is(:hover, :focus-within) .card-title { color: var(--blue-deep); }
.brand-card:is(:hover, :focus-within) h2 { color: var(--blue-deep); }
.card-link { text-decoration: none; color: inherit; display: block; }
.card-link:hover { color: inherit; }
.card-media {
  position: relative; background: var(--white); aspect-ratio: 4 / 3;
  display: grid; place-items: center; color: var(--ink); border-bottom: 1px solid var(--line-soft);
  overflow: hidden;
}
.card-media img { width: 100%; height: 100%; object-fit: contain; padding: 10px; }
/* De twee kaartfoto's liggen op elkaar: dicht in rust, open (of een ander
   aanzicht) op hover. Crossfade, want de packshots staan al op dezelfde plek in
   beeld — beweging zou alleen maar suggereren dat het toilet zelf beweegt.
   De rustfoto gaat pas een tel later weg, zodat je nooit door beide heen kijkt. */
.card-swap .card-img { position: absolute; inset: 0; transition: opacity .22s var(--ease); }
.card-img-hover { opacity: 0; }
.card-swap:is(:hover, :focus-within) .card-img-hover { opacity: 1; }
.card-swap:is(:hover, :focus-within) .card-img:not(.card-img-hover) {
  opacity: 0; transition-delay: .06s;
}
/* Eén bruikbare foto en dus niets om naar te wisselen: dan een trage zoom van 4%.
   De kaart moet antwoorden als je erop staat — een kaart die niets doet leest als
   kapot — maar zonder een tweede stand te suggereren die we niet hebben. Een
   product zonder foto krijgt ook dit niet: daar valt niets in te zoomen. */
.product-card:not(.card-swap) .card-img { transition: transform .4s var(--ease); }
.product-card:not(.card-swap):is(:hover, :focus-within) .card-img { transform: scale(1.04); }
/* Zonder muis is er geen hover: dan blijft de rustfoto staan en hoeft de tweede
   foto niet eens gedecodeerd te worden. */
@media (hover: none) {
  .card-img-hover { display: none; }
}
/* De globale reduced-motion-regel maakt elke transitie 0,01 ms; een zoom die
   erin springt is dan erger dan geen zoom. */
@media (prefers-reduced-motion: reduce) {
  .product-card:not(.card-swap):is(:hover, :focus-within) .card-img { transform: none; }
}
.card-media .ph { width: 100%; height: 100%; color: #8fa8cc; }
.card-media .badge-score { position: absolute; top: 10px; left: 10px; }
.card-body { padding: var(--sp-4) var(--sp-4) var(--sp-2); }
.card-brand {
  font-family: var(--font-display); font-size: .74rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin: 0 0 3px;
}
.card-title {
  font-size: 1rem; margin: 0 0 6px; line-height: 1.3;
  transition: color var(--dur) var(--ease);
}
.card-sum { font-size: .875rem; color: var(--ink-2); margin: 0 0 var(--sp-2); }
.chips { display: flex; flex-wrap: wrap; gap: 5px; list-style: none; padding: 0; margin: 0 0 var(--sp-2); }
.chip {
  font-size: .72rem; font-weight: 600; padding: 3px 8px; border-radius: var(--r-badge);
  background: var(--tint); color: var(--ink-2);
}
.chip-alt { background: var(--green-soft); color: var(--green); }
/* Price above, full-width buy button below. Side by side the price wrapped over
   three lines in a 255px card, which destroyed the price hierarchy the whole
   direction rests on. A full-width button is also a bigger tap target. */
.card-foot {
  margin-top: auto; padding: var(--sp-2) var(--sp-4) var(--sp-4);
  display: flex; flex-direction: column; align-items: stretch; gap: var(--sp-3);
}
.card-foot .btn { width: 100%; }
.price-block { display: flex; flex-direction: column; min-width: 0; }
.price {
  font-family: var(--font-display); font-size: 1.42rem; font-weight: 800;
  color: var(--ink); font-variant-numeric: tabular-nums; letter-spacing: -.02em;
  line-height: 1.1; white-space: nowrap;
}
.price-note { font-size: .78rem; color: var(--muted); margin-top: 3px; white-space: nowrap; }
.shop { font-size: .82rem; color: var(--muted); margin-top: 2px; }
/* Ticking this does nothing without JS, so do not offer it then. app.js sets
   `.js` on <html> as its first act. */
html:not(.js) .cmp-toggle { display: none; }
.cmp-toggle {
  position: absolute; top: 10px; right: 10px; font-size: .72rem; font-weight: 600;
  background: rgba(255,255,255,.94); border: 1px solid var(--line); border-radius: var(--r-badge);
  padding: 4px 8px; display: inline-flex; gap: 5px; align-items: center; cursor: pointer;
  min-height: 28px;
}
.brand-card { padding: var(--sp-4); text-decoration: none; color: inherit; }
.brand-card h2 { font-size: 1.05rem; margin-bottom: .2em; }

/* ---------- listing ---------- */
.listing { display: block; }
.filters {
  background: var(--white); border: 1px solid var(--line-soft); border-radius: var(--r);
  padding: var(--sp-4); margin-bottom: var(--sp-5);
}
/* The filter bar's scaffolding, inert everywhere but the layout it exists for.
   The two wrappers around each group hand their box back, so the phone sheet and
   the page without JavaScript lay the fieldsets out exactly as they did before
   the bar existed, and the buttons that need a script to do anything are not
   drawn until there is one. The bar's own two elements stay real boxes: one of
   them is the `.wrap` that keeps the panel inside the page's column, and giving
   that away would let the card run the full width of the window. */
.fgrp, .fpop { display: contents; }
.fpop-btn, .fchips { display: none; }
/* Without a script there is no bar, only the panel it holds — and a panel that is
   the full width of the page is a 1169px wall in front of the results if its
   groups stay in one column. Lay them out across it instead. */
@media (min-width: 941px) {
  html:not(.js) .filter-body {
    display: grid; grid-template-columns: repeat(4, minmax(0,1fr));
    gap: 0 var(--sp-5); align-items: start;
  }
}
/* .brand-filter is the one list still allowed its own scroll — it is a long tail of
   names, capped low enough that the rest of the panel stays reachable around it.
   The default OS scrollbar is a thick grey bar that reads as broken against a card
   this narrow — thin it down and match it to the line color instead. */
.brand-filter { scrollbar-width: thin; scrollbar-color: var(--line) transparent; }
.brand-filter::-webkit-scrollbar { width: 8px; }
.brand-filter::-webkit-scrollbar-track { background: transparent; }
.brand-filter::-webkit-scrollbar-thumb { background: var(--line); border-radius: 4px; }
.brand-filter::-webkit-scrollbar-thumb:hover { background: var(--muted); }
.filters fieldset { border: 0; padding: 0; margin: 0 0 var(--sp-4); }
.filter-hint { margin: 4px 0 0; }
/* Enough filters now that the long tail has to fold away. */
.filter-more { margin: 0 0 var(--sp-4); }
.filter-more summary {
  font-family: var(--font-display); font-weight: 800; font-size: .78rem;
  text-transform: uppercase; letter-spacing: .07em; color: var(--muted);
  cursor: pointer; padding: 5px 0; min-height: 32px; display: flex; align-items: center;
  /* `display: flex` drops the browser's disclosure triangle, so draw our own —
     without it the section reads as a dead heading. */
  list-style: none; gap: 6px;
}
.filter-more summary::-webkit-details-marker { display: none; }
.filter-more summary::after {
  content: ""; width: 7px; height: 7px; margin-left: auto;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform var(--dur) var(--ease);
}
.filter-more[open] summary::after { transform: rotate(-135deg) translate(-2px, -2px); }
.filter-more summary:hover { color: var(--ink); }
.brand-filter { max-height: 210px; overflow-y: auto; padding-right: 4px; }
.brand-filter label { font-size: .88rem; }
/* The honest half of the filter: models we cannot judge, offered back rather
   than silently binned. Set apart so it does not read as another feature. */
.unknown-filter {
  border-top: 1px solid var(--line-soft) !important; padding-top: var(--sp-3) !important;
}
.unknown-filter label { font-size: .88rem; color: var(--ink-2); align-items: flex-start; }
.filter-actions { margin: 0; display: flex; gap: var(--sp-2); }
.filters legend {
  font-family: var(--font-display); font-weight: 800; font-size: .78rem;
  text-transform: uppercase; letter-spacing: .07em; color: var(--muted); margin-bottom: 6px;
}
.filters label { display: flex; gap: 9px; align-items: center; padding: 5px 0; font-size: .94rem; cursor: pointer; min-height: 32px; }
.filters select, .filters input[type=range] { width: 100%; }
.filter-status { font-size: .875rem; color: var(--muted); margin: 0; font-weight: 600; }
.empty { padding: var(--sp-6); text-align: center; color: var(--muted); background: var(--white); border-radius: var(--r); }

/* Sorting reorders, it never drops a model — that is not what the filter panel
   does — so its control sits here, next to the count, on the one bar every
   viewport shares. Only one <select id="sort"> exists in the page; the phone
   "Sorteren" button in the sticky bar just scrolls and focuses this one. */
.results-bar {
  display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-4); margin-bottom: var(--sp-4);
}
.sort-control { display: flex; align-items: center; gap: var(--sp-2); }
.sort-control label { font-size: .875rem; color: var(--muted); font-weight: 600; }
.sort-control select {
  min-height: 40px; padding: 0 10px;
  border: 1px solid var(--line); border-radius: var(--r-btn);
  background: var(--white); color: var(--ink); font: 650 .9rem/1 var(--font-text);
}

/* ---------- the filter panel as a bar, above 940px ----------
   The panel is routinely 1169px tall and no desktop window is, so pinning it —
   top edge or bottom edge — always left one half of it unreachable: scroll into
   the results and Soort, Toiletpot and Stroom were simply gone until you scrolled
   all the way back up. A row does not have that problem at any window height, so
   above 940px the panel is not a column at all. It is one line of buttons that
   open the real fieldsets in popovers, which also gives the results the full
   width — four cards a row instead of three.

   All of it is guarded on `html.js`: the popovers need a script to open, so
   without one the buttons stay undrawn and every fieldset is simply in the flow,
   the way the server rendered it. */
@media (min-width: 941px) {
  html.js .filter-bar {
    display: block; position: sticky; top: 64px; z-index: 36;
    background: rgba(255,255,255,.97); backdrop-filter: blur(10px);
    border-bottom: 1px solid var(--line-soft); margin-bottom: var(--sp-5);
  }
  html.js .filter-bar-inner { display: block; padding-top: 10px; padding-bottom: 10px; }
  /* Not a card any more: the bar is the surface, the form is its contents. */
  html.js .filter-bar .filters {
    display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
    background: none; border: 0; border-radius: 0; padding: 0; margin: 0;
  }
  html.js .filter-bar .filter-body { display: contents; }
  html.js .fgrp { display: block; position: relative; }

  html.js .fpop-btn {
    display: inline-flex; align-items: center; gap: 7px; min-height: 40px; padding: 0 13px;
    border: 1px solid var(--line); border-radius: 999px;
    background: var(--white); color: var(--ink); cursor: pointer;
    font: 650 .9rem/1 var(--font-text);
  }
  html.js .fpop-btn:hover { border-color: var(--blue); color: var(--blue-deep); }
  html.js .fpop-btn.is-on { border-color: var(--blue); background: var(--blue-soft); color: var(--blue-deep); }
  html.js .fpop-btn[aria-expanded="true"] { border-color: var(--blue); box-shadow: 0 0 0 3px var(--blue-soft); }
  .fpop-n {
    display: inline-grid; place-items: center; min-width: 19px; height: 19px; padding: 0 5px;
    border-radius: 999px; background: var(--blue); color: #fff; font-size: .7rem; font-weight: 800;
  }
  .fpop-caret {
    width: 6px; height: 6px; flex: none;
    border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
    transform: rotate(45deg) translate(-2px, -2px);
    transition: transform var(--dur) var(--ease);
  }
  .fpop-btn[aria-expanded="true"] .fpop-caret { transform: rotate(-135deg) translate(-2px, -2px); }

  html.js .fpop {
    display: none; position: absolute; top: calc(100% + 9px); left: 0; z-index: 5;
    min-width: 246px; max-width: 340px; padding: 14px 16px 4px;
    background: var(--white); border: 1px solid var(--line); border-radius: var(--r);
    box-shadow: 0 18px 44px -20px rgba(11,27,51,.55);
    /* A popover is not the panel: it holds one group, and only Merk can outgrow
       a short window, so this is a backstop rather than the scrollbar the panel
       was cured of. */
    max-height: min(58vh, 440px); overflow-y: auto;
    scrollbar-width: thin; scrollbar-color: var(--line) transparent;
  }
  html.js .fpop.is-open { display: block; }
  /* The last buttons in the row sit against the edge of the window. */
  html.js .fpop.flip { left: auto; right: 0; }
  /* The button already carries the name, and the fold inside Merk is one click
     too many once the popover itself is the fold. */
  html.js .fpop legend, html.js .fpop .filter-more summary { display: none; }
  html.js .fpop fieldset, html.js .fpop .filter-more { margin: 0 0 10px; }
  html.js .fpop .brand-filter { max-height: none; overflow: visible; padding-right: 0; }

  /* Second line: what is switched on, in the reader's own words. An empty flex
     item the full width of the row is how a flex row is ended; app.js only asks
     for one when something is going on the line below. */
  html.js .filter-bar .filters.has-active::after {
    content: ""; order: 3; flex-basis: 100%; height: 0;
  }
  html.js .fchips { display: flex; flex-wrap: wrap; gap: 7px; order: 4; }
  .fchip {
    display: inline-flex; align-items: center; gap: 7px; min-height: 32px; padding: 0 11px;
    border: 1px solid var(--blue); border-radius: 999px;
    background: var(--blue-soft); color: var(--blue-deep);
    font: 600 .84rem/1 var(--font-text); cursor: pointer;
  }
  .fchip::after { content: "\00d7"; font-size: 1.05rem; line-height: 1; opacity: .8; }
  .fchip:hover { background: var(--blue); color: #fff; }
  /* "Niet opgegeven" is a sentence, not a chip: it rides along on the same line
     rather than claiming a third one for a checkbox that is usually off. */
  html.js .filter-bar .unknown-filter {
    order: 5; border-top: 0 !important; padding-top: 0 !important; margin: 0 0 0 4px;
  }
  html.js .filter-bar .unknown-filter legend { display: none; }
  html.js .filter-bar .unknown-filter label { padding: 0; min-height: 32px; font-size: .82rem; }
  html.js .filter-bar .filter-actions { order: 6; margin-left: auto; }
}

/* ---------- listing on a phone ----------
   Below 940px the two-column grid collapses and the filter panel stops being a
   sidebar: it becomes a 712px block standing in front of the results, which put
   the first product 1234px down the page on a 375px screen. Shrinking it does
   not help — eight fieldsets are eight fieldsets — so the panel leaves the flow
   and comes back as a sheet, opened from a sticky bar.

   Everything here is guarded on `html.js`. Without JavaScript there is nothing
   to open a sheet with, so the panel stays exactly where the server put it and
   the page behaves as it does today.

   The sheet body does scroll inside itself, which is the pattern the desktop
   panel was just cured of — the difference is that a sheet is a modal over a
   locked page, so there is no second scrollbar competing with the page's own.
   For the same reason the brand list gives up its own inner scroll here. */
.listing-bar { display: none; }
.filter-head { display: none; }
.sheet-apply { display: none; }
.sheet-scrim { display: none; }

@media (max-width: 940px) {
  html.js .listing-bar {
    display: block; position: sticky; top: 64px; z-index: 35;
    background: rgba(255,255,255,.97); backdrop-filter: blur(10px);
    border-bottom: 1px solid var(--line-soft); padding: 8px 0 9px;
    margin-bottom: var(--sp-4);
  }
  .lbar-row { display: flex; gap: 8px; padding-bottom: 8px; }
  .lbar-btn {
    flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 7px;
    min-height: 44px; border-radius: var(--r-btn); border: 1px solid var(--line);
    background: var(--white); color: var(--ink); cursor: pointer;
    font: 700 .9rem/1 var(--font-display);
  }
  .lbar-btn:active { transform: translateY(1px); }
  .lbar-btn:has(.lbar-n:not([hidden])) { border-color: var(--blue); background: var(--blue-soft); color: var(--blue-deep); }
  .lbar-n {
    display: inline-grid; place-items: center; min-width: 20px; height: 20px; padding: 0 6px;
    border-radius: 999px; background: var(--blue); color: #fff; font-size: .72rem; font-weight: 800;
  }
  /* Bleeds to both screen edges so it is visible that the row scrolls on. */
  .lbar-quick {
    display: flex; gap: 8px; overflow-x: auto; scrollbar-width: none;
    margin: 0 -20px; padding: 2px 20px;
  }
  .lbar-quick::-webkit-scrollbar { display: none; }
  .quick-chip {
    flex: none; min-height: 40px; padding: 0 14px; border-radius: 999px;
    border: 1px solid var(--line); background: var(--white); color: var(--ink);
    font: 650 .875rem/1 var(--font-text); white-space: nowrap; cursor: pointer;
  }
  .quick-chip[aria-pressed="true"] { background: var(--blue); border-color: var(--blue); color: #fff; }

  html.js .filters {
    position: fixed; left: 0; right: 0; bottom: 0; top: auto; z-index: 56;
    max-height: 88dvh; padding: 0; overflow: visible;
    display: flex; flex-direction: column;
    border: 1px solid var(--line); border-bottom: 0; border-radius: 18px 18px 0 0;
    box-shadow: 0 -12px 40px -18px rgba(11,27,51,.6);
    /* `visibility` rather than only a transform: an off-screen panel that is
       still in the tab order is a keyboard trap. */
    transform: translateY(100%); visibility: hidden;
  }
  /* The transition arrives a frame later, from script. Put it in the rule above
     and the *first* application of the transform — the moment app.js adds
     `html.js` — is itself an animation, so every page load opens with the panel
     filling the screen and then sliding away. */
  html.js .filters.sheet-ready {
    transition: transform 240ms var(--ease), visibility 0s linear 240ms;
  }
  html.js .filters.is-open {
    transform: none; visibility: visible; transition-delay: 0s;
  }
  html.js .filter-head {
    display: flex; align-items: center; gap: var(--sp-3); flex: none;
    padding: 6px 8px 6px var(--sp-4); border-bottom: 1px solid var(--line-soft);
  }
  html.js .filter-head h2 { margin: 0; font-size: 1.05rem; }
  html.js .filter-close {
    margin-left: auto; width: 44px; height: 44px; display: grid; place-items: center;
    background: none; border: 0; color: var(--ink-2); cursor: pointer; border-radius: var(--r-btn);
  }
  html.js .filter-body { flex: 1 1 auto; overflow-y: auto; padding: var(--sp-4) var(--sp-4) 0; }
  html.js .brand-filter { max-height: none; overflow: visible; padding-right: 0; }
  /* A row you can hit with a thumb, not a 32px label next to a 13px box. */
  html.js .filters label { min-height: 44px; font-size: .95rem; }
  html.js .filters input[type=checkbox],
  html.js .filters input[type=radio] { width: 20px; height: 20px; flex: none; accent-color: var(--blue); }
  html.js .filter-actions {
    flex: none; background: var(--white); border-top: 1px solid var(--line-soft);
    padding: 10px var(--sp-4) calc(10px + env(safe-area-inset-bottom, 0px));
  }
  html.js .sheet-apply { display: inline-flex; flex: 1; }

  html.js .sheet-scrim {
    display: block; position: fixed; inset: 0; z-index: 55;
    background: rgba(11,27,51,.45); opacity: 0; pointer-events: none;
    transition: opacity var(--dur) var(--ease);
  }
  html.js .sheet-scrim.is-open { opacity: 1; pointer-events: auto; }
  body.sheet-open { overflow: hidden; }
}

/* Two cards abreast instead of one. 71 single-column cards is 46 000px of
   scroll; the same list two-up with the summary dropped is under a third of
   that, and a phone reader is scanning brand + price, not reading blurbs. */
@media (max-width: 560px) {
  .listing .cards { grid-template-columns: 1fr 1fr; gap: 10px; }
  .listing .cards .card-body { padding: 10px 10px 2px; }
  .listing .cards .card-brand { font-size: .68rem; }
  .listing .cards .card-sum { display: none; }
  .listing .cards .card-title {
    font-size: .875rem; line-height: 1.25;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  }
  .listing .cards .chip { font-size: .66rem; padding: 2px 6px; }
  .listing .cards .card-foot { padding: 0 10px 10px; gap: 8px; }
  .listing .cards .price { font-size: 1.12rem; }
  .listing .cards .price-note, .listing .cards .shop { font-size: .74rem; }
  .listing .cards .card-foot .btn { padding: 8px 10px; min-height: 40px; font-size: .82rem; }
  .listing .cards .cmp-toggle { top: 6px; right: 6px; font-size: .68rem; padding: 3px 6px; }
}

/* ---------- product page ---------- */
.product-top { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1.05fr); gap: var(--sp-6); align-items: start; padding-top: var(--sp-2); }
@media (max-width: 940px) { .product-top { grid-template-columns: 1fr; } }
.product-media { background: var(--white); border: 1px solid var(--line-soft); border-radius: var(--r); padding: var(--sp-4); color: var(--ink); }
.product-media .media-main { display: block; }
.product-media img { width: 100%; height: auto; object-fit: contain; background: #fff; border-radius: var(--r-btn); }
.media-gallery { list-style: none; margin: var(--sp-3) 0 0; padding: 0; display: flex; gap: var(--sp-2); flex-wrap: wrap; }
.media-gallery li { width: 80px; }
.media-gallery img { width: 80px; height: 60px; object-fit: contain; border: 1px solid var(--line-soft); border-radius: var(--r-badge); background: #fff; }
.media-gallery a:hover img, .media-gallery a:focus-visible img { border-color: var(--blue); }
.media-credit { margin: var(--sp-2) 0 0; }
.spec-table caption { text-align: left; font-weight: 700; padding: 14px 16px 6px; color: var(--ink); }
.spec-table + .spec-table { margin-top: 14px; }
.spec-count { margin: 0 0 12px; }
.spec-unknown th[scope=row], .spec-unknown td { color: var(--ink-2); }
.spec-gaps { margin: 6px 0 16px; }
.cmp-differs th[scope=row] { position: relative; }
.cmp-differs th[scope=row]::before { content: ""; position: absolute; left: 0; top: 6px; bottom: 6px; width: 3px; background: var(--accent, currentColor); border-radius: 2px; }
.cmp-differs td { font-weight: 600; }
.empty-brand { max-width: 62ch; }
.spec-src { margin-left: 8px; font-size: 0.78em; text-decoration: none; border-bottom: 1px dotted currentColor; opacity: .65; }
.spec-src:hover, .spec-src:focus-visible { opacity: 1; }
.variant-table th[scope=col] { text-align: left; font-weight: 600; }
.rating-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.rating { display: flex; flex-wrap: wrap; gap: 4px 12px; align-items: baseline; padding: 10px 14px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); }
.rating-score b { font-size: 1.15em; }
.notice-discontinued { margin: 0 0 12px; padding: 10px 14px; border-radius: var(--radius); background: var(--surface-2); border: 1px solid var(--line); }
.doc-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-2); }
.doc { display: flex; flex-wrap: wrap; gap: 4px 12px; align-items: baseline; padding: 11px 15px; background: var(--white); border: 1px solid var(--line-soft); border-radius: var(--r-btn); }
.doc > a { font-weight: 700; }

.downside {
  background: var(--red-soft); border-left: 3px solid var(--red);
  padding: 11px 15px; border-radius: 0 var(--r-btn) var(--r-btn) 0; color: var(--ink);
}
.buybox {
  background: var(--white); border: 1px solid var(--line); border-radius: var(--r);
  padding: var(--sp-5); margin-top: var(--sp-5); box-shadow: var(--sh-2);
}
.buy-price {
  font-family: var(--font-display); font-size: clamp(2.3rem, 1.7rem + 2.6vw, 3.4rem);
  font-weight: 800; color: var(--ink); margin: 0 0 4px;
  font-variant-numeric: tabular-nums; letter-spacing: -.03em; line-height: 1.02;
}
.buy-meta { display: flex; flex-wrap: wrap; gap: 6px var(--sp-4); font-size: .875rem; color: var(--muted); margin: 0 0 var(--sp-3); }
.buy-meta b { color: var(--ink-2); font-weight: 650; }
.deal-note { color: #6b3a00; background: var(--amber-soft); border-radius: var(--r-badge); padding: 7px 11px; font-weight: 650; font-size: .9rem; margin: 0 0 var(--sp-3); display: inline-block; }
/* No shop to send the reader to. Deliberately NOT amber: amber on this site
   means "you are leaving to a shop to buy", and here there is nowhere to go. */
.buy-note { background: var(--blue-soft); color: var(--ink-2); border-radius: var(--r-btn);
  padding: 10px 13px; font-size: .9rem; line-height: 1.45; margin: var(--sp-4) 0 var(--sp-3); }
.buy-actions { display: flex; gap: var(--sp-3); flex-wrap: wrap; margin: var(--sp-4) 0 var(--sp-3); }
/* .badge already sets the pill shape and .badge-info the colour; this class
   only adds the spacing a stray badge needs when it sits alone on its own line. */
.range-note { margin: 0 0 var(--sp-3); }
.buy-trust { display: flex; flex-wrap: wrap; gap: 6px var(--sp-4); font-size: .82rem; color: var(--muted); margin: 0; }
.buy-trust span { display: inline-flex; align-items: center; gap: 5px; }

.offers { list-style: none; padding: 0; margin: var(--sp-4) 0; display: grid; gap: var(--sp-2); }
.offer {
  display: grid; grid-template-columns: 1fr auto auto auto; gap: var(--sp-3);
  align-items: center; padding: 11px 14px; border: 1px solid var(--line-soft);
  border-radius: var(--r-btn); background: var(--white);
}
.offer:hover { border-color: var(--line); }
.offer-shop { font-weight: 650; color: var(--ink); }
.offer-price { font-family: var(--font-display); font-weight: 800; color: var(--ink); font-variant-numeric: tabular-nums; }
@media (max-width: 560px) { .offer { grid-template-columns: 1fr auto; } .offer-stock { display: none; } }

.alert-form { display: flex; gap: var(--sp-2); flex-wrap: wrap; margin-top: var(--sp-4); padding-top: var(--sp-4); border-top: 1px solid var(--line-soft); }
.alert-form input[type=email] { flex: 1 1 200px; }
/* ---------- prijsverloop ----------
   The two amounts flank the line and are always on screen, so the figure says
   something even without a pointer, in print, and with JS off. The SVG is
   capped at its own viewBox width (430) so it renders 1:1 and the 104px height
   is real; below that it scales down proportionally. */
/* Bounded, not full-bleed: the section is as wide as the page, but a three-point
   line stretched over 1200 px is mostly empty card. The chart may grow past its
   430-unit viewBox — the height grows with it, which is why it is capped. */
.price-figure { background: var(--white); border: 1px solid var(--line-soft); border-radius: var(--r); padding: var(--sp-4); margin: 0; max-width: 760px; }
.pf-row { display: flex; align-items: center; gap: var(--sp-4); }
.pf-chart { position: relative; flex: 1 1 430px; min-width: 0; }
.pf-svg { display: block; width: 100%; max-width: 560px; height: auto; overflow: visible; }
.pf-end { display: flex; flex-direction: column; line-height: 1.25; white-space: nowrap; }
.pf-end b { font-family: var(--font-display); font-weight: 800; color: var(--ink); font-variant-numeric: tabular-nums; }
.pf-end span { font-size: .74rem; color: var(--muted); }
.pf-first b { font-weight: 600; color: var(--muted); font-size: .9rem; }
.pf-last { text-align: right; }
.pf-line { stroke: var(--blue); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.pf-dot-first { fill: var(--muted); }
.pf-dot-now { fill: var(--blue); stroke: var(--white); stroke-width: 2; }
/* A frame, not a measuring line: lighter than any other rule on the page. */
.pf-yearline { stroke: var(--line); stroke-width: 1; stroke-dasharray: 2 3; }
.pf-year { font-family: var(--font-text); font-size: 10px; font-weight: 700; fill: var(--muted); }
.pf-cross, .pf-cross-dot { opacity: 0; }
.pf-cross { stroke: var(--ink); stroke-width: 1; stroke-dasharray: 3 3; }
.pf-cross-dot { fill: var(--ink); stroke: var(--white); stroke-width: 2; }
.pf-chart.is-on .pf-cross, .pf-chart.is-on .pf-cross-dot { opacity: .85; }
.pf-tip {
  position: absolute; top: 0; transform: translateX(-50%); background: var(--ink); color: #fff;
  font-size: .76rem; font-family: var(--font-display); font-weight: 700; padding: 4px 9px;
  border-radius: 6px; white-space: nowrap; pointer-events: none;
}
.pf-tip span { font-weight: 400; opacity: .72; margin-left: 5px; }
/* On a phone the line plus two nowrap labels do not fit on one row, and forcing
   it pushes the whole page sideways. Line on top, the two amounts under it. */
@media (max-width: 620px) {
  .pf-row { flex-wrap: wrap; gap: var(--sp-2); }
  .pf-chart { order: -1; flex: 1 1 100%; }
  .pf-svg { max-width: 100%; }
  .pf-end { flex: 1 1 0; min-width: 0; }
}

/* Sticky mobile buy bar: on phones the price and the buy button must never
   scroll out of reach. Hidden on desktop, where the buybox stays visible. */
.buybar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 45; display: none;
  /* Fully opaque: at .97 the page heading showed through the bar on a phone. */
  background: var(--white); border-top: 1px solid var(--line);
  box-shadow: 0 -8px 24px -18px rgba(11,27,51,.6);
  padding: 10px 16px calc(10px + env(safe-area-inset-bottom, 0px));
}
.buybar-inner { display: flex; align-items: center; gap: var(--sp-3); max-width: var(--wrap); margin: 0 auto; }
.buybar .price { font-size: 1.3rem; }
.buybar .btn { margin-left: auto; }
@media (max-width: 940px) { .buybar { display: block; } body:has(.buybar) { padding-bottom: 76px; } }

/* ---------- tables ---------- */
.table-scroll { overflow-x: auto; border: 1px solid var(--line-soft); border-radius: var(--r); background: var(--white); }
table { border-collapse: collapse; width: 100%; font-size: .94rem; }
.spec-table, .fit-table, .cmp-table, .calc-table { background: var(--white); }
.spec-table, .fit-table { border: 1px solid var(--line-soft); border-radius: var(--r); overflow: hidden; }
th, td { text-align: left; padding: 11px 14px; border-bottom: 1px solid var(--line-soft); vertical-align: top; }
thead th {
  background: var(--tint); font-family: var(--font-display); font-size: .74rem;
  font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--muted);
}
tbody tr:last-child th, tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover { background: var(--tint); }
.spec-table th[scope=row] { width: 40%; font-weight: 650; color: var(--ink-2); }
/* ---------- comparison ----------
   Photo columns on top, the spec sheet underneath in one <details> per section.
   The header row and every table share one width formula, so a column heading and
   the values below it stay in the same place at any width:
   `--cmp-label` for the row labels, the rest split evenly over `--cmp-n` models. */
.cmp-grid { --cmp-label: 190px; --cmp-n: 2; }
.cmp-grid[data-n="1"] { --cmp-n: 1; }
.cmp-grid[data-n="3"] { --cmp-n: 3; }
.cmp-grid[data-n="4"] { --cmp-n: 4; }
.cmp-tools { display: flex; align-items: baseline; gap: var(--sp-4); flex-wrap: wrap; margin-bottom: var(--sp-3); }
.cmp-only-diff { display: inline-flex; align-items: center; gap: 9px; font-weight: 650; color: var(--ink); cursor: pointer; }
.cmp-only-diff input { width: 18px; height: 18px; accent-color: var(--blue); }
.cmp-legend { margin: 0; max-width: 60ch; }
.cmp-scroll { border: 1px solid var(--line-soft); border-radius: var(--r); background: var(--white); }
.cmp-cols, .cmp-table { min-width: calc(var(--cmp-label) + var(--cmp-n) * 150px); }

.cmp-cols {
  display: grid; grid-template-columns: var(--cmp-label) repeat(var(--cmp-n), 1fr);
  position: sticky; top: 64px; z-index: 3; padding: 14px 0;
  background: var(--white); border-bottom: 1px solid var(--line);
}
.cmp-cols-spacer { position: sticky; left: 0; background: var(--white); }
.cmp-col { position: relative; display: flex; flex-direction: column; gap: 5px; padding: 0 14px; min-width: 0; }
.cmp-col > * { max-width: 100%; }
.cmp-col-media { display: block; align-self: center; }
.cmp-col-media img, .cmp-col-media .ph { width: 132px; height: 100px; object-fit: contain; background: #fff; }
.cmp-col-media .ph { color: #8fa8cc; }
.cmp-col-brand {
  margin: 0; font-family: var(--font-display); font-size: .7rem; font-weight: 800;
  text-transform: uppercase; letter-spacing: .06em; color: var(--muted);
}
.cmp-col-name { font-family: var(--font-display); font-weight: 700; font-size: .92rem; line-height: 1.3; color: var(--ink); text-decoration: none; }
.cmp-col-name:hover, .cmp-col-name:focus-visible { color: var(--blue-deep); text-decoration: underline; }
.cmp-col-price { margin: 2px 0 0; display: flex; flex-direction: column; }
.cmp-col-price .price { font-size: 1.1rem; }
.cmp-col .btn { margin-top: auto; width: 100%; white-space: normal; padding-inline: 10px; }
.cmp-col-drop {
  position: absolute; top: -6px; right: 4px; width: 26px; height: 26px; border-radius: 8px;
  display: grid; place-items: center; color: var(--muted); text-decoration: none; font-size: 1rem; line-height: 1;
  border: 1px solid transparent;
}
.cmp-col-drop:hover, .cmp-col-drop:focus-visible { color: var(--red); border-color: var(--line); background: var(--white); }

.cmp-table { table-layout: fixed; width: 100%; }
/* Sticky so the row label survives scrolling the columns sideways on a phone:
   "ja / nee / onbekend" three columns to the right means nothing without it. */
.cmp-table th[scope=row] {
  width: var(--cmp-label); font-weight: 650; color: var(--ink-2);
  position: sticky; left: 0; z-index: 1; background: var(--white);
}
.cmp-table tbody tr:hover th[scope=row] { background: var(--tint); }
.cmp-summary { border-bottom: 1px solid var(--line-soft); }

/* One section per spec group. Sections where nothing differs start shut: there is
   nothing in them that can decide anything. `<details>` needs no JavaScript. */
.cmp-sec + .cmp-sec { border-top: 1px solid var(--line-soft); }
.cmp-sec > summary {
  display: flex; align-items: center; gap: 10px; cursor: pointer; list-style: none;
  padding: 12px 14px; background: var(--tint); color: var(--ink);
  font-family: var(--font-display); font-weight: 800; font-size: .95rem;
}
.cmp-sec > summary::-webkit-details-marker { display: none; }
.cmp-sec > summary::before { content: "▸"; color: var(--muted); font-size: .8rem; flex: none; transition: transform var(--dur) var(--ease); }
.cmp-sec[open] > summary::before { transform: rotate(90deg); }
.cmp-sec > summary:hover { background: var(--tint-2); }
.cmp-sec > summary:focus-visible { outline: 2px solid var(--blue); outline-offset: -2px; }
.cmp-sec-hint { margin-left: auto; font-family: var(--font-text); font-weight: 600; }
.cmp-badge {
  display: inline-grid; place-items: center; min-width: 22px; height: 22px; padding: 0 7px;
  border-radius: 999px; background: var(--blue-soft); color: var(--blue-deep);
  font-size: .74rem; font-weight: 800; flex: none;
}
.cmp-badge.is-zero { background: var(--tint-2); color: var(--muted); }
.cmp-foot { margin-top: var(--sp-3); }

/* "Toon alleen verschillen" is a checkbox, not a script: the rows that differ are
   already marked server-side, so hiding the rest is a selector. The two summary
   rows stay — they are the verdict, not a specification you look up. */
.cmp-grid:has(#cmp-diff-only:checked) .cmp-sec .cmp-table tbody tr:not(.cmp-differs) { display: none; }
.cmp-grid:has(#cmp-diff-only:checked) .cmp-sec[data-differing="0"] { display: none; }

@media (max-width: 900px) {
  /* Narrow screens scroll the comparison sideways; that scroll container is also
     what stops the header row from sticking, which is fine at this size. */
  .cmp-scroll { overflow-x: auto; }
  .cmp-cols { position: static; }
  .cmp-grid { --cmp-label: 128px; }
  .cmp-col-media img, .cmp-col-media .ph { width: 96px; height: 74px; }
}

.fit-tag { display: inline-block; padding: 3px 10px; border-radius: var(--r-badge); font-size: .78rem; font-weight: 700; background: var(--tint); color: var(--ink-2); }
.fit-yes .fit-tag { background: var(--green-soft); color: var(--green); }
.fit-with_adapter .fit-tag { background: var(--amber-soft); color: #6b3a00; }
.fit-no .fit-tag { background: var(--red-soft); color: var(--red); }
.fit-unknown .fit-tag { color: var(--muted); }

/* ---------- fit finder ---------- */
.finder { display: grid; grid-template-columns: 350px minmax(0,1fr); gap: var(--sp-5); align-items: start; padding-bottom: var(--sp-6); }
@media (max-width: 940px) { .finder { grid-template-columns: 1fr; } }
.finder-panel { background: var(--white); border: 1px solid var(--line-soft); border-radius: var(--r); padding: var(--sp-4); position: sticky; top: 80px; }
.finder-label { display: block; font-family: var(--font-display); font-weight: 700; margin-bottom: 6px; color: var(--ink); }
#bowl-search { width: 100%; }
.bowl-list { list-style: none; padding: 0; margin: var(--sp-3) 0 0; max-height: 52vh; overflow: auto; }
.bowl-list li a { display: block; padding: 10px 11px; border-radius: var(--r-btn); text-decoration: none; color: var(--ink); }
.bowl-list li a:hover, .bowl-list li[aria-selected=true] a { background: var(--blue-soft); }
.bowl-list strong { display: block; font-weight: 650; font-size: .94rem; }
.finder-results { min-height: 300px; }
.finder-empty { background: var(--white); border: 1px dashed var(--line); border-radius: var(--r); padding: var(--sp-6); }
.fit-list { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--sp-2); }
.fit-row { display: flex; flex-wrap: wrap; gap: var(--sp-3); align-items: center; padding: 11px 14px; border: 1px solid var(--line-soft); border-radius: var(--r-btn); background: var(--white); }
.fit-row .src { margin-left: auto; }

/* ---------- calculator ----------
   The questions are tiles rather than a stack of bare radios, but they are still
   real <input type=radio|checkbox> underneath: the whole selected state is
   `:checked + .calc-tile-face`, so it works with the keyboard, with a screen
   reader and with JS off. Only the running total needs JS. */
.calc-layout {
  display: grid; grid-template-columns: minmax(0,1.15fr) minmax(300px,.85fr);
  gap: var(--sp-5); align-items: start; padding-bottom: var(--sp-5);
  /* Part-to-whole ramp for the cost bar. Deliberately all blue: amber means
     "you are leaving to a shop" and violet is the test banner, so neither may
     appear here just because a chart wants a third colour. */
  --calc-seg-1: var(--blue);
  --calc-seg-2: #3f7ce9;
  --calc-seg-3: #7aa4f1;
  --calc-seg-4: #a8c4f7;
  --calc-seg-5: #cbdcfb;
  --calc-seg-6: var(--line);
}
@media (max-width: 940px) { .calc-layout { grid-template-columns: 1fr; } }
.calc, .calc-out { background: var(--white); border: 1px solid var(--line-soft); border-radius: var(--r); padding: var(--sp-5); }
.calc fieldset { border: 0; padding: 0; margin: 0 0 var(--sp-5); }
.calc legend { font-family: var(--font-display); font-weight: 800; margin-bottom: var(--sp-2); color: var(--ink); }
.radio, .check { display: flex; gap: 10px; align-items: flex-start; padding: 8px 0; cursor: pointer; min-height: 40px; }

.calc-q { margin: 0 0 var(--sp-6); }
.calc-q:last-of-type { margin-bottom: 0; }
/* Deliberately no flex/float on the <legend> itself: legend has its own box
   rules inside a fieldset and every trick to make it a flex container is a
   browser-quirk bet. An inline-grid badge inside a plain legend just works. */
.calc-legend {
  padding: 0; font-family: var(--font-display); font-weight: 800;
  font-size: 1.02rem; color: var(--ink); margin-bottom: var(--sp-3);
}
.calc-qn {
  display: inline-grid; place-items: center; width: 22px; height: 22px;
  margin-right: 8px; vertical-align: -4px;
  border-radius: 50%; background: var(--blue-soft); color: var(--blue-deep);
  font-size: .74rem; font-weight: 800;
}
.calc-hint { margin: 0 0 var(--sp-3); font-size: .84rem; color: var(--muted); }

.calc-tiles { display: grid; gap: var(--sp-3); }
.calc-cols-2 { grid-template-columns: repeat(2, minmax(0,1fr)); }
.calc-cols-3 { grid-template-columns: repeat(3, minmax(0,1fr)); }
.calc-cols-4 { grid-template-columns: repeat(4, minmax(0,1fr)); }
@media (max-width: 620px) {
  .calc-cols-3, .calc-cols-4 { grid-template-columns: repeat(2, minmax(0,1fr)); }
}

.calc-tile { display: block; cursor: pointer; }
/* Visually hidden, not display:none — a hidden input is not focusable and the
   keyboard would lose the whole question. */
.calc-tile input {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; border: 0; overflow: hidden; white-space: nowrap;
  clip: rect(0 0 0 0); clip-path: inset(50%);
}
.calc-tile-face {
  position: relative; height: 100%; display: flex; flex-direction: column;
  align-items: center; justify-content: flex-start; gap: 7px; text-align: center;
  padding: var(--sp-4) var(--sp-3); border: 1px solid var(--line-soft);
  border-radius: var(--r); background: var(--white); color: var(--ink-2);
  transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease),
              transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.calc-tile:hover input:not(:disabled) + .calc-tile-face { border-color: var(--line); transform: translateY(-2px); box-shadow: var(--sh-1); }
.calc-tile input:checked + .calc-tile-face { border-color: var(--blue); background: var(--blue-soft); box-shadow: inset 0 0 0 1px var(--blue); }
.calc-tile input:focus-visible + .calc-tile-face { outline: 3px solid var(--blue); outline-offset: 2px; }
.calc-tile-icon { width: 34px; height: 34px; color: var(--muted); flex: none; }
.calc-tile input:checked + .calc-tile-face .calc-tile-icon { color: var(--blue); }
.calc-tile-label { font-size: .88rem; font-weight: 600; color: var(--ink); line-height: 1.32; }
.calc-tile-sub { display: block; font-weight: 500; color: var(--muted); font-size: .8rem; margin-top: 2px; }
.calc-tile input:checked + .calc-tile-face .calc-tile-sub { color: var(--blue-deep); }
.calc-tile-check {
  position: absolute; top: -8px; right: -8px; width: 22px; height: 22px;
  display: grid; place-items: center; border-radius: 50%; background: var(--blue);
  opacity: 0; transform: scale(.6); transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.calc-tile-check svg { width: 15px; height: 15px; }
.calc-tile input:checked + .calc-tile-face .calc-tile-check { opacity: 1; transform: scale(1); }

/* An extra that cannot apply stays visible and says why, instead of vanishing:
   "why can I not tick this" is exactly the question the greying-out answers. */
.calc-tile input:disabled + .calc-tile-face {
  opacity: .62; cursor: default; background: var(--tint);
  border-style: dashed; box-shadow: none;
}
.calc-tile:has(input:disabled) { cursor: default; }
.calc-tile input:disabled + .calc-tile-face .calc-tile-label { color: var(--muted); }
.calc-tile-note { display: block; font-size: .74rem; line-height: 1.4; color: var(--muted); font-weight: 500; }
.calc-tile-note:empty { display: none; }
.calc-tile-note.is-included { color: var(--green); }

.calc-out { position: sticky; top: 80px; }
.calc-out-h { margin: 0 0 var(--sp-2); font-size: 1.05rem; }
.calc-chips { margin: 0 0 var(--sp-3); }
.calc-chips:empty { display: none; }
.calc-total-label {
  margin: 0; font-family: var(--font-display); font-size: .7rem; font-weight: 800;
  text-transform: uppercase; letter-spacing: .07em; color: var(--muted);
}
.calc-total { font-family: var(--font-display); font-size: 1.95rem; font-weight: 800; color: var(--ink); font-variant-numeric: tabular-nums; margin: 2px 0 var(--sp-3); letter-spacing: -.02em; }
.calc-bar { display: flex; gap: 2px; height: 9px; margin: 0 0 var(--sp-3); }
.calc-bar:empty { display: none; }
.calc-bar span { display: block; border-radius: 2px; min-width: 3px; }
.calc-table th { font-weight: 500; color: var(--ink-2); }
.calc-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 7px; vertical-align: baseline; }
/* Colour lives here, not in JS: the script only sets each segment's width, so
   nothing has to write a style attribute the CSP would refuse. */
.calc-seg-1 { background: var(--calc-seg-1); }
.calc-seg-2 { background: var(--calc-seg-2); }
.calc-seg-3 { background: var(--calc-seg-3); }
.calc-seg-4 { background: var(--calc-seg-4); }
.calc-seg-5 { background: var(--calc-seg-5); }
.calc-seg-6 { background: var(--calc-seg-6); }
.calc-cta { width: 100%; justify-content: center; margin-top: var(--sp-2); }

/* Mobile running total. Same trick as .buybar: sit on top of --compare-h so the
   compare tray and this bar stack instead of covering each other. */
.calc-mobile-bar { display: none; }
@media (max-width: 940px) {
  .calc-mobile-bar {
    position: fixed; left: 0; right: 0; bottom: var(--compare-h, 0px); z-index: 55;
    display: flex; align-items: center; gap: var(--sp-3);
    padding: 10px var(--sp-4); border: 0; border-top: 1px solid var(--line);
    background: var(--white); box-shadow: 0 -6px 20px -14px rgba(11,27,51,.5);
    font: inherit; text-align: left; cursor: pointer;
  }
  .calc-mobile-bar[hidden] { display: none; }
  .calc-mobile-text { display: flex; flex-direction: column; flex: 1; min-width: 0; }
  .calc-mobile-label { font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }
  .calc-mobile-amount { font-family: var(--font-display); font-size: 1.05rem; font-weight: 800; color: var(--ink); font-variant-numeric: tabular-nums; }
  .calc-mobile-go { font-weight: 700; font-size: .85rem; color: var(--blue); flex: none; }
  body:has(.calc-mobile-bar:not([hidden])) { padding-bottom: calc(64px + var(--compare-h, 0px)); }
}

/* ---------- forms ---------- */
input[type=email], input[type=text], input[type=tel], input[type=search], textarea, select {
  padding: 12px 14px; min-height: 46px; border-radius: var(--r-btn);
  border: 1px solid var(--line); background: var(--white); color: var(--ink);
  font: inherit; font-family: var(--font-text); width: 100%;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
input:hover, textarea:hover, select:hover { border-color: var(--muted); }
input:focus, textarea:focus, select:focus { border-color: var(--blue); box-shadow: 0 0 0 3px var(--blue-soft); outline: none; }
input:user-invalid { border-color: var(--red); }
textarea { resize: vertical; min-height: 96px; }
.card-form { background: var(--white); border: 1px solid var(--line-soft); border-radius: var(--r); padding: var(--sp-5); display: grid; gap: var(--sp-4); }
.card-form label { font-weight: 650; color: var(--ink); display: grid; gap: 6px; }
.card-form fieldset { border: 0; padding: 0; margin: 0; }
.card-form legend { font-family: var(--font-display); font-weight: 800; margin-bottom: 4px; color: var(--ink); }
.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-4); }
@media (max-width: 560px) { .two-col { grid-template-columns: 1fr; } }
.stack { display: grid; gap: var(--sp-3); }
.inline-form { display: flex; gap: var(--sp-3); flex-wrap: wrap; align-items: center; }
.inline-form input[type=email] { flex: 1 1 240px; width: auto; }
.form-note { flex-basis: 100%; margin: 0; }
.form-msg { margin: 6px 0 0; font-weight: 650; }
.form-msg.ok { color: var(--green); }
.form-msg.err { color: var(--red); }
.news-cta, .fit-cta { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr); gap: var(--sp-6); align-items: center; }
@media (max-width: 860px) { .news-cta, .fit-cta { grid-template-columns: 1fr; } }
.fit-visual { color: var(--blue); }

/* ---------- prose ---------- */
.article-layout { display: grid; grid-template-columns: minmax(0,1fr) 310px; gap: var(--sp-6); align-items: start; padding-bottom: var(--sp-5); }
@media (max-width: 940px) { .article-layout { grid-template-columns: 1fr; } }
.prose { max-width: 70ch; font-size: 1.03rem; line-height: 1.72; }
.prose h2 { margin-top: 1.9em; padding-top: .35em; border-top: 1px solid var(--line-soft); }
.prose h3 { margin-top: 1.5em; }
.prose ul, .prose ol { padding-left: 1.3em; }
.prose li { margin: .35em 0; }
.prose blockquote {
  margin: 1.5em 0; padding: var(--sp-4) var(--sp-5); background: var(--blue-soft);
  border-left: 3px solid var(--blue); border-radius: 0 var(--r-btn) var(--r-btn) 0; color: var(--ink);
}
.prose blockquote p:last-child { margin-bottom: 0; }
.prose table { margin: 1.5em 0; border: 1px solid var(--line-soft); border-radius: var(--r); overflow: hidden; background: var(--white); display: block; overflow-x: auto; }
.prose code { background: var(--tint); padding: 2px 6px; border-radius: 5px; font-size: .9em; font-family: var(--font-mono); }
.prose img { border-radius: var(--r); }
.aside { display: grid; gap: var(--sp-4); position: sticky; top: 80px; }
.aside-box { background: var(--white); border: 1px solid var(--line-soft); border-radius: var(--r); padding: var(--sp-4); }
.aside-box h2 { font-size: 1.02rem; }
.faq dt { font-family: var(--font-display); font-weight: 700; color: var(--ink); margin-top: 1.1em; }
.faq dd { margin: .3em 0 0; color: var(--ink-2); }
.ticks { list-style: none; padding: 0; }
.ticks li { padding-left: 28px; position: relative; margin: .55em 0; }
.ticks li::before { content: "✓"; position: absolute; left: 0; color: var(--green); font-weight: 800; }
.steps { padding-left: 1.2em; }
.steps li { margin: .6em 0; }
.kv-inline { display: flex; flex-wrap: wrap; gap: 6px var(--sp-5); margin: var(--sp-3) 0 0; font-size: .92rem; }
.kv-inline dt { color: var(--muted); }
.kv-inline dd { margin: 0 var(--sp-4) 0 0; font-weight: 650; color: var(--ink); }
.guide-list { list-style: none; padding: 0; display: grid; gap: var(--sp-3); grid-template-columns: repeat(auto-fill, minmax(285px,1fr)); }
.guide-list.big { grid-template-columns: 1fr; }
.guide-list a { display: block; padding: var(--sp-4); background: var(--white); border: 1px solid var(--line-soft); border-radius: var(--r); text-decoration: none; color: inherit; transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease); }
.guide-list a:hover { border-color: var(--blue); box-shadow: var(--sh-2); color: inherit; }
.guide-list h2, .guide-list h3 { margin-bottom: .3em; color: var(--ink); }
.guide-list a:hover h2, .guide-list a:hover h3 { color: var(--blue-deep); }
.guide-list p { margin-bottom: .4em; font-size: .92rem; }
.explain-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(235px,1fr)); gap: var(--sp-5); }
.pill-list { list-style: none; padding: 0; display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.pill { display: inline-block; padding: 7px 13px; border-radius: var(--r-badge); background: var(--white); border: 1px solid var(--line-soft); text-decoration: none; color: var(--ink-2); font-size: .875rem; }
.pill:hover { border-color: var(--blue); color: var(--blue-deep); }
.src-list { list-style: none; padding: 0; word-break: break-word; }
.report summary { cursor: pointer; font-weight: 700; padding: 11px 0; color: var(--blue); }
.comfort-headline { font-size: 1.15rem; }
.comfort-headline b { font-size: 1.6rem; }
/* ---------- comfort score: het cijfer op de productpagina ----------
   Het blauwe cijfer linksboven op elke thumb is waar een lezer het cijfer voor
   het eerst ziet; op de detailpagina komt datzelfde chip groot terug (op de
   foto, en als badge boven de uitleg) zodat het herkenbaar hetzelfde getal is.

   Kleur draagt hier geen oordeel dat woorden niet ook dragen: amber betekent op
   deze site "je gaat naar een winkel" en rood betekent "fout", dus een laag
   cijfer krijgt grijs en het woord ernaast doet het werk. */
.badge-score-lg {
  position: absolute; top: 12px; left: 12px; z-index: 2;
  display: inline-flex; flex-direction: column; align-items: center; line-height: 1.05;
  padding: 9px 13px; font-size: 1.45rem; font-weight: 800; font-family: var(--font-display);
  border-radius: var(--r-btn); text-decoration: none; box-shadow: var(--sh-2);
}
.badge-score-lg:hover { background: var(--blue-deep); color: #fff; }
.badge-score-sub { font-size: .58rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; opacity: .85; }
.product-media { position: relative; }

.cs h2 { margin-bottom: .25em; }
.cs-hero { display: flex; align-items: flex-start; gap: var(--sp-4); margin-bottom: var(--sp-5); }
.cs-hero-text { min-width: 0; }
.cs-badge {
  flex: none; margin: 0; display: inline-flex; flex-direction: column; align-items: center;
  justify-content: center; min-width: 84px; padding: 10px 14px; border-radius: var(--r);
  background: var(--blue); color: #fff; font-family: var(--font-display); font-weight: 800;
  font-size: 2rem; line-height: 1.05;
}
.cs-badge span { font-size: .72rem; font-weight: 700; letter-spacing: .06em; opacity: .85; }
.cs-badge-empty { background: var(--tint-2); color: var(--muted); }
.cs-line { font-size: 1.06rem; color: var(--ink-2); max-width: 62ch; margin-bottom: .5em; }

/* de balk: <progress> in plaats van een div, want de CSP verbiedt de inline
   width die een div-balk nodig heeft, en dit leest een screenreader vanzelf */
.cs-bar {
  -webkit-appearance: none; appearance: none; display: block; width: 100%; height: 8px;
  border: 0; border-radius: 5px; background: var(--tint-2); color: var(--blue);
}
.cs-bar::-webkit-progress-bar { background: var(--tint-2); border-radius: 5px; }
.cs-bar::-webkit-progress-value { background: var(--blue); border-radius: 5px; }
.cs-bar::-moz-progress-bar { background: var(--blue); border-radius: 5px; }
.cs-bar.is-top { color: var(--green); }
.cs-bar.is-top::-webkit-progress-value { background: var(--green); }
.cs-bar.is-top::-moz-progress-bar { background: var(--green); }
.cs-bar.is-matig, .cs-bar.is-zwak { color: var(--muted); }
.cs-bar.is-matig::-webkit-progress-value,
.cs-bar.is-zwak::-webkit-progress-value { background: var(--muted); }
.cs-bar.is-matig::-moz-progress-bar,
.cs-bar.is-zwak::-moz-progress-bar { background: var(--muted); }

.cs-chip {
  display: inline-block; padding: 3px 10px; border-radius: var(--r-badge); font-size: .8rem;
  font-weight: 700; background: var(--blue-soft); color: var(--blue-deep); white-space: nowrap;
}
.cs-chip.is-top { background: var(--green-soft); color: var(--green); }
.cs-chip.is-matig, .cs-chip.is-zwak { background: var(--tint-2); color: var(--ink-2); }

/* de vier onderdelen, elk een regel: naam, balk, oordeel in woorden, cijfer */
.cs-rows { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--sp-3); }
.cs-row {
  display: grid; grid-template-columns: minmax(120px, 1.1fr) minmax(90px, 2fr) auto auto;
  align-items: center; gap: var(--sp-3); padding: 10px 0; border-top: 1px solid var(--line-soft);
}
.cs-row:first-child { border-top: 0; }
.cs-row-label { font-weight: 650; color: var(--ink); }
.cs-row-verdict { font-size: .9rem; color: var(--ink-2); }
.cs-row-mark { font-family: var(--font-display); font-weight: 800; color: var(--ink); font-variant-numeric: tabular-nums; }
@media (max-width: 640px) {
  .cs-row { grid-template-columns: 1fr auto; }
  .cs-row .cs-bar { grid-column: 1 / -1; order: 3; }
  .cs-row-verdict { order: 4; }
}

.cs-more { margin-top: var(--sp-5); }
.cs-more summary { cursor: pointer; font-weight: 700; padding: 11px 0; color: var(--blue); }
.cs-more .spec-table { margin-top: var(--sp-2); }
.cs-block { margin-top: var(--sp-4); }
.cs-block h4 { margin-bottom: .35em; }

/* tabellen binnen de comfortsectie en op /comfortscore */
.cg-pts, .cg-wt { white-space: nowrap; }
/* een voetnoot onder een eigenschapsnaam is een voetnoot, geen kop: de rijkop
   is 650-vet, waardoor de "leeg is niet nee"-notities stonden te schreeuwen */
.spec-table th[scope=row] .small { font-weight: 400; }
.comfort-sum { margin-top: var(--sp-4); font-variant-numeric: tabular-nums; color: var(--ink-2); }
.comfort-sum b { color: var(--ink); font-size: 1.1rem; }
.score-group { margin-top: var(--sp-6); }
.score-weight { font-size: .82rem; font-weight: 650; color: var(--blue); background: var(--blue-soft); border-radius: var(--r-badge); padding: 3px 10px; margin-left: 6px; white-space: nowrap; vertical-align: middle; }
.spec-table td.thin { color: var(--amber-ring); font-weight: 650; }
.nowrap { white-space: nowrap; }
.crosses { list-style: none; padding: 0; }
.crosses li { padding-left: 28px; position: relative; margin: .55em 0; }
.crosses li::before { content: "✗"; position: absolute; left: 0; color: var(--muted); font-weight: 800; }
/* ---------- compare picker ----------
   Was a bare <select multiple>: unstyled, impossible to use on a phone (Ctrl-click)
   and wired to the wrong parameter. A checkbox list matches the "vergelijk"-toggle
   used everywhere else, and submits on its own without JavaScript. */
.picker-block { margin-top: var(--sp-6); }
.picker-block h2 { margin-bottom: .2em; }
.picker { margin-top: var(--sp-4); max-width: 620px; }
.picker-search { margin: 0 0 var(--sp-3); }
.picker-search input {
  width: 100%; padding: 11px 14px; border: 1px solid var(--line); border-radius: var(--r-btn);
  background: var(--white); font: inherit; color: var(--ink); min-height: 44px;
}
.picker-list {
  list-style: none; margin: 0; padding: 0; max-height: 420px; overflow-y: auto;
  border: 1px solid var(--line-soft); border-radius: var(--r); background: var(--white);
  scrollbar-width: thin;
}
.picker-list li + li { border-top: 1px solid var(--line-soft); }
.picker-list label {
  display: flex; align-items: center; gap: var(--sp-3); padding: 10px 14px;
  cursor: pointer; min-height: 46px;
}
.picker-list label:hover { background: var(--tint); }
.picker-label { display: flex; flex-direction: column; min-width: 0; flex: 1 1 auto; }
.picker-brand {
  font-family: var(--font-display); font-size: .7rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .06em; color: var(--muted);
}
.picker-name { font-size: .95rem; color: var(--ink); }
.picker-price { font-size: .92rem; color: var(--ink-2); white-space: nowrap; font-variant-numeric: tabular-nums; }
.picker-empty { color: var(--muted); padding: var(--sp-3) 0 0; }
.picker-actions { display: flex; align-items: center; gap: var(--sp-3); margin: var(--sp-3) 0 0; flex-wrap: wrap; }
.meta { color: var(--muted); }

/* ---------- compare tray ---------- */
.compare-bar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 50; background: var(--white);
  border-top: 1px solid var(--line); box-shadow: 0 -8px 24px -18px rgba(11,27,51,.6);
  padding: 11px 0 calc(11px + env(safe-area-inset-bottom, 0px));
}
.compare-inner { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.compare-label { font-weight: 650; color: var(--ink); margin: 0; white-space: nowrap; }
.compare-items { list-style: none; display: flex; gap: 8px; padding: 0; margin: 0; flex: 1 1 auto; overflow-x: auto; }
/* The photo, not just the name: you recognise these models by their shape long
   before you recognise "AquaClean Tuma Comfort wandcloset". */
.compare-items li {
  font-size: .8rem; padding: 4px 4px 4px 4px; border-radius: var(--r-badge);
  background: var(--tint); white-space: nowrap; color: var(--ink-2);
  display: inline-flex; align-items: center; gap: 8px;
}
.compare-thumb {
  width: 40px; height: 34px; object-fit: contain; flex: none;
  background: #fff; border: 1px solid var(--line-soft); border-radius: var(--r-badge);
}
.compare-name { max-width: 15ch; overflow: hidden; text-overflow: ellipsis; }
/* The card you ticked is usually on another page by the time you want it gone. */
.compare-drop {
  border: 0; background: none; color: var(--muted); cursor: pointer; line-height: 1;
  font-size: 1rem; padding: 0 6px; border-radius: var(--r-badge); min-height: 24px;
}
.compare-drop:hover, .compare-drop:focus-visible { color: var(--ink); background: rgba(11,27,51,.07); }
.btn.is-disabled { opacity: .55; cursor: not-allowed; }

/* The tray is fixed to the bottom on every page now, so give it room and stack
   it above the product page's buy bar instead of on top of it. --compare-h is
   the tray's measured height, set by app.js (0 when the tray is closed). */
body.has-compare-bar { padding-bottom: var(--compare-h, 0px); }
@media (max-width: 940px) {
  .buybar { bottom: var(--compare-h, 0px); }
  body:has(.buybar).has-compare-bar { padding-bottom: calc(76px + var(--compare-h, 0px)); }
}

/* A short message where alert() used to stop everything ("de tray is vol"). */
.tv-toast {
  position: fixed; left: 50%; bottom: calc(var(--compare-h, 0px) + 18px); z-index: 60;
  transform: translate(-50%, 10px); opacity: 0; pointer-events: none;
  max-width: min(92vw, 460px); margin: 0; padding: 11px 18px; border-radius: var(--r-btn);
  background: var(--ink); color: #fff; font-weight: 600; font-size: .9rem; text-align: center;
  box-shadow: 0 14px 30px -14px rgba(11,27,51,.7);
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.tv-toast.is-on { opacity: 1; transform: translate(-50%, 0); }

/* ---------- comparison window ----------
   "Vergelijk" used to take you off the catalogue: you compared, went back, and
   landed at the top of the list again. The tray now opens the same comparison over
   the page instead, so your place in the list survives it. It is a real <dialog>,
   so Escape, the backdrop and the focus trap come from the browser. Without
   JavaScript the button stays what it is in the markup: a link to /vergelijk. */
.cmp-dialog {
  width: min(1120px, 96vw); max-height: 92vh; padding: 0; border: 0;
  border-radius: 16px; background: var(--tint); color: var(--ink-2);
  box-shadow: 0 30px 80px -20px rgba(11,27,51,.55);
  overflow: hidden; display: flex; flex-direction: column;
}
.cmp-dialog::backdrop { background: rgba(11,27,51,.55); }
.cmp-dialog-head {
  display: flex; align-items: center; gap: var(--sp-3); flex: none;
  padding: 14px 18px; background: var(--white); border-bottom: 1px solid var(--line-soft);
}
.cmp-dialog-head h2 { margin: 0; font-size: 1.15rem; }
.cmp-dialog-close {
  margin-left: auto; width: 38px; height: 38px; flex: none; cursor: pointer;
  border: 1px solid var(--line); border-radius: var(--r-btn); background: var(--white);
  color: var(--muted); font-size: 1.05rem; line-height: 1;
}
.cmp-dialog-close:hover, .cmp-dialog-close:focus-visible { color: var(--ink); border-color: var(--muted); }
.cmp-dialog-body { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 16px 18px 20px; }
.cmp-dialog-body .cmp-cols { top: 0; }
.cmp-dialog-loading { padding: 30px 0; text-align: center; color: var(--muted); }
@media (max-width: 700px) {
  .cmp-dialog { width: 100vw; max-width: 100vw; max-height: 100vh; height: 100vh; border-radius: 0; }
}

/* ---------- image viewer ----------
   A photo used to open in a new tab, dropping the reader on a bare image file.
   The overlay is created by app.js on first use; without JS the links are still
   plain links to the file. */
/* `hidden` has to beat the `display` these rules set. The browser's own
   `[hidden] { display: none }` loses to any author rule of equal specificity, so
   without this the overlay would open once and never shut. */
.viewer[hidden], .viewer [hidden] { display: none !important; }
.viewer {
  position: fixed; inset: 0; z-index: 200; display: grid;
  grid-template-columns: auto 1fr auto; align-items: center; gap: var(--sp-2);
  padding: clamp(12px, 4vw, 40px);
  background: rgba(11, 27, 51, .82);
  backdrop-filter: blur(2px);
}
.viewer-figure {
  margin: 0; display: flex; flex-direction: column; align-items: center; gap: var(--sp-3);
  min-width: 0; max-height: 100%;
}
.viewer-figure img {
  max-width: 100%; max-height: calc(100vh - 150px); width: auto; height: auto;
  object-fit: contain; background: #fff; border-radius: var(--r); box-shadow: var(--sh-2);
}
.viewer-figure figcaption {
  color: #fff; font-size: .9rem; text-align: center; max-width: 60ch;
  text-wrap: balance;
}
.viewer-figure .muted { color: rgba(255,255,255,.7); }
.viewer button {
  border: 0; background: rgba(255,255,255,.14); color: #fff; cursor: pointer;
  border-radius: 50%; width: 46px; height: 46px; font-size: 1.6rem; line-height: 1;
  display: grid; place-items: center;
  transition: background var(--dur) var(--ease);
}
.viewer button:hover, .viewer button:focus-visible { background: rgba(255,255,255,.28); }
.viewer button:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.viewer-close { position: absolute; top: clamp(12px, 4vw, 40px); right: clamp(12px, 4vw, 40px); }
/* The page behind must not scroll under the overlay. */
body.viewer-open { overflow: hidden; }
@media (max-width: 640px) {
  .viewer { grid-template-columns: 1fr; grid-template-rows: 1fr auto; }
  .viewer-nav { position: absolute; bottom: clamp(12px, 4vw, 40px); }
  .viewer-prev { left: clamp(12px, 4vw, 40px); }
  .viewer-next { right: clamp(12px, 4vw, 40px); }
}
/* Say the photo can be enlarged, rather than leaving it to be discovered. */
.media-main { cursor: zoom-in; display: block; }
.media-gallery a { cursor: zoom-in; display: block; }

/* ---------- footer ---------- */
.site-footer { background: var(--white); border-top: 1px solid var(--line-soft); margin-top: var(--sp-7); padding: var(--sp-6) 0 var(--sp-5); }
.footer-grid { display: grid; grid-template-columns: 1.5fr repeat(3, 1fr); gap: var(--sp-5); }
@media (max-width: 860px) { .footer-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 520px) { .footer-grid { grid-template-columns: 1fr; } }
.foot-brand { font-family: var(--font-display); font-size: 1.06rem; font-weight: 700; color: var(--ink); margin-bottom: .35em; }
.foot-h { font-family: var(--font-display); font-size: .76rem; font-weight: 800; text-transform: uppercase; letter-spacing: .08em; color: var(--muted); margin-bottom: .7em; }
.foot-list { list-style: none; padding: 0; margin: 0; display: grid; gap: 7px; }
.foot-list a { color: var(--ink-2); text-decoration: none; font-size: .92rem; }
.foot-list a:hover { color: var(--blue); text-decoration: underline; }
.disclosure { margin-top: var(--sp-5); padding-top: var(--sp-4); border-top: 1px solid var(--line-soft); }
.disclosure p { max-width: 92ch; }

@media print {
  .site-header, .site-footer, .compare-bar, .buybar, .filters, .filter-bar, .aside, .btn,
  .viewer { display: none !important; }
  body { background: #fff; color: #000; }
}

/* ---------- consent banner ----------
   Only ever rendered when the page carries a measurement ID, i.e. in production.
   A corner card rather than a blocking modal: the reader can ignore it and read
   on, because nothing is loaded or stored until they choose. */
.consent {
  position: fixed; z-index: 60; right: var(--sp-4); bottom: var(--sp-4);
  width: min(400px, calc(100vw - 2 * var(--sp-4)));
  background: var(--white); color: var(--ink-2);
  border: 1px solid var(--line); border-radius: var(--r);
  box-shadow: 0 18px 44px -20px rgba(11,27,51,.5); padding: var(--sp-4);
  font-size: .9rem; line-height: 1.5;
}
.consent h2 {
  font-size: 1rem; margin: 0 0 6px; color: var(--ink);
}
.consent p { margin: 0 0 var(--sp-3); }
.consent-actions { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
.consent-actions .btn { flex: 1 1 auto; }
@media (max-width: 560px) {
  .consent { right: var(--sp-2); left: var(--sp-2); bottom: var(--sp-2); width: auto; }
}
