/* ── components ─────────────────────────────────────────────────────────
   Conventie: BEM met "zg-"-prefix, bv. .zg-card, .zg-card__title,
   .zg-card--featured. Eén sectie-kop per component (bv. "Card"),
   alleen tokens uit tokens.css (geen kale hex/px), max nesting-diepte 2. */

/* Sitebreed staat elke container op 1280px, gelijk aan de header- en footerbalk (besluit 2026-08-17: content lijnde per template anders uit — 1120/1160/1200/1280 — waardoor de linkerrand per pagina verschoof). Onderstaande toelichting beschrijft de oorspronkelijke, per-template afgeleide breedte uit de designbron; de wáárde is bewust gelijkgetrokken. */


/* ── Header ──────────────────────────────────────────────────────────────
   Varianten: --solid (standaard, on-page) en --hero (transparant over een
   hero-afbeelding, wordt bij scroll "gestuck" via .zg-is-stuck — zie
   assets/js/header.js). Kleur- en logo-wissels lopen via lokale custom
   properties op .zg-hdr, zodat elke selector binnen de specificiteitslimiet
   (max 3 klasses) blijft en er geen !important nodig is: een compound
   selector als .zg-hdr--hero.zg-is-stuck heeft vanzelf hogere specificiteit
   dan .zg-hdr--hero alleen. */
.zg-hdr {
  --hdr-link-color: var(--text-subtle);
  --hdr-link-hover: var(--espresso-700);
  --hdr-link-active: var(--espresso-700);
  --hdr-underline: transparent;
  --hdr-logo-wit-display: none;
  --hdr-logo-dark-display: block;
  --hdr-toggle-color: var(--text-heading);

  font-family: var(--font-body);
  transition:
    transform 0.28s ease,
    background 0.28s ease,
    box-shadow 0.28s ease;
}

.zg-hdr__bar {
  max-width: 1280px;
  margin: 0 auto;
  padding: 22px 24px;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 32px;
}

.zg-hdr__nav {
  display: flex;
  gap: 26px;
  align-items: center;
}

.zg-hdr__nav--left {
  justify-self: start;
}

.zg-hdr__nav--right {
  justify-self: end;
}

.zg-hdr__logo {
  justify-self: center;
  display: inline-flex;
}

.zg-hdr__logo-img {
  height: 48px;
  width: auto;
  display: block;
}

.zg-hdr__logo-img--wit {
  display: var(--hdr-logo-wit-display);
}

.zg-hdr__logo-img--dark {
  display: var(--hdr-logo-dark-display);
}

.zg-hdr__link {
  position: relative;
  font-family: var(--font-body);
  font-size: 20px;
  text-decoration: none;
  white-space: nowrap;
  color: var(--hdr-link-color);
  transition: color 0.24s ease;
}

.zg-hdr__link::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -5px;
  height: 2px;
  background: var(--hdr-underline);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.24s ease;
}

.zg-hdr__link:hover {
  color: var(--hdr-link-hover);

  /* De globale `a:hover { text-decoration: underline }` (base.css) lekt anders
     een tweede, bruine onderstreping bovenop de eigen `::after`-underline —
     dubbele lijn onder het menu-item. De `::after` (hieronder bruin) is de
     enige onderstreping. */
  text-decoration: none;
}

.zg-hdr__link:hover::after {
  transform: scaleX(1);
}

.zg-hdr__link.zg-is-active {
  color: var(--hdr-link-active);
  font-weight: var(--fw-semibold);
}

/* solid variant — standaard, opake balk bovenaan elke pagina */
.zg-hdr--solid {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--surface-raised);
  border-bottom: 1px solid var(--border);
  margin-bottom: 40px;

  /* Bruine hover-onderstreping (was sand-100): de gebruiker koos bewust de
     bruine variant boven de lichte. Zelfde bruin als de hover-tekstkleur. */
  --hdr-underline: var(--espresso-700);
}

/* hero variant — transparant over een hero-afbeelding. Position: absolute
   in de niet-gestuckte staat zodat de header uit de document-flow valt en
   over de aansluitende .zg-site-hero-sectie heen ligt (zie "Site hero"
   hieronder) — pas bij .zg-is-stuck (scroll voorbij headerhoogte) wordt hij
   position:fixed en opaak. */
.zg-hdr--hero {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 50;
  background: transparent;

  --hdr-link-color: var(--paper-100);
  --hdr-link-hover: var(--paper-50);
  --hdr-link-active: var(--paper-50);
  --hdr-logo-wit-display: block;
  --hdr-logo-dark-display: none;
  --hdr-toggle-color: var(--paper-50);
}

.zg-hdr--hero.zg-is-stuck {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 50;
  background: var(--surface-raised);
  box-shadow: var(--shadow-md);
  border-bottom: 1px solid var(--border);
  animation: zg-hdr-drop 0.32s ease;

  --hdr-link-color: var(--text-subtle);
  --hdr-link-hover: var(--text-heading);
  --hdr-link-active: var(--text-heading);
  --hdr-logo-wit-display: none;
  --hdr-logo-dark-display: block;
  --hdr-toggle-color: var(--text-heading);
}

@keyframes zg-hdr-drop {
  from {
    transform: translateY(-100%);
  }

  to {
    transform: translateY(0);
  }
}

/* ── Mobiel-menu: toggle + drawer ────────────────────────────────────────
   Poort van DS components/layout/Header.jsx. Toggle verschijnt ≤860px;
   drawer schuift van rechts in met scrim. Kleur van de toggle volgt de
   headervariant via --hdr-toggle-color (zie .zg-hdr / --hero / .is-stuck). */
.zg-hdr__toggle {
  display: none;
  justify-self: end;
  position: relative;
  z-index: 70;
  width: 44px;
  height: 44px;
  margin: 0 -8px 0 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--hdr-toggle-color);
  cursor: pointer;
  align-items: center;
  justify-content: center;
}

.zg-hdr__toggle.zg-is-open {
  color: var(--text-heading);
}

.zg-hdr__burger {
  position: relative;
  width: 22px;
  height: 16px;
}

/* .zg-hdr__burger-bar (i.p.v. een kaal "span"-type-selector): voorkomt
   selector-max-specificity/no-descending-specificity-conflicten met de vele
   andere "X span"/"X svg"-patronen elders in dit bestand — zie markup in
   parts/header.html / header-hero.html (elke bar heeft de klasse). */
.zg-hdr__burger-bar {
  position: absolute;
  left: 0;
  right: 0;
  height: 2px;
  border-radius: 2px;
  background: currentcolor;
  transition:
    transform 0.34s cubic-bezier(0.22, 0.61, 0.36, 1),
    opacity 0.2s ease;
}

.zg-hdr__burger-bar:nth-child(1) {
  top: 0;
}

.zg-hdr__burger-bar:nth-child(2) {
  top: 7px;
}

.zg-hdr__burger-bar:nth-child(3) {
  top: 14px;
}

.zg-is-open .zg-hdr__burger-bar:nth-child(1) {
  transform: translateY(7px) rotate(45deg);
}

.zg-is-open .zg-hdr__burger-bar:nth-child(2) {
  opacity: 0;
}

.zg-is-open .zg-hdr__burger-bar:nth-child(3) {
  transform: translateY(-7px) rotate(-45deg);
}

.zg-hdr__scrim {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: color-mix(in oklab, var(--espresso-900) 52%, transparent);
  backdrop-filter: blur(3px);
  opacity: 0;
  visibility: hidden;
  transition:
    opacity 0.34s ease,
    visibility 0.34s ease;
}

.zg-hdr__scrim.zg-is-open {
  opacity: 1;
  visibility: visible;
}

.zg-hdr__drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 65;
  width: min(87vw, 400px);
  display: flex;
  flex-direction: column;
  background: var(--surface-raised);
  box-shadow: -24px 0 60px -20px color-mix(in oklab, var(--espresso-900) 45%, transparent);
  transform: translateX(102%);
  visibility: hidden;
  transition:
    transform 0.42s cubic-bezier(0.22, 0.61, 0.36, 1),
    visibility 0.42s;
}

.zg-hdr__drawer.zg-is-open {
  transform: none;
  visibility: visible;
}

.zg-hdr__drawer-top {
  display: flex;
  align-items: center;
  height: 74px;
  padding: 0 24px;
  border-bottom: 1px solid var(--border);
}

.zg-hdr__drawer-top img {
  height: 34px;
  width: auto;
  display: block;
}

.zg-hdr__drawer-nav {
  flex: 1;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 22px 24px 8px;
  display: flex;
  flex-direction: column;
}

.zg-hdr__drawer-eyebrow {
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: var(--fw-semibold);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-subtle);
  margin: 0 0 6px;
}

.zg-hdr__drawer-link {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  font-family: var(--font-display);
  font-variation-settings: var(--display-vsettings);
  font-size: 1.55rem;
  line-height: 1;
  color: var(--text-heading);
  text-decoration: none;
  padding: 16px 0;
  border-bottom: 1px solid var(--border);
  opacity: 0;
  transform: translateX(22px);
  transition:
    opacity 0.5s ease,
    transform 0.5s cubic-bezier(0.22, 0.61, 0.36, 1),
    color 0.2s ease;
}

/* .zg-hdr__drawer-arrow (i.p.v. een kaal "svg"-type-selector): zelfde reden
   als .zg-hdr__burger-bar hierboven — zie markup (elke pijl-svg heeft de
   klasse). */
.zg-hdr__drawer-arrow {
  flex: none;
  width: 19px;
  height: 19px;
  color: var(--accent);
  opacity: 0;
  transform: translateX(-8px);
  transition:
    opacity 0.22s ease,
    transform 0.22s ease;
}

.zg-hdr__drawer-link:hover {
  color: var(--accent);
}

.zg-hdr__drawer-link:hover .zg-hdr__drawer-arrow {
  opacity: 1;
  transform: none;
}

/* Actieve staat, tegenhanger van .zg-hdr__link.zg-is-active hierboven: het
   server-side gerenderde menu (zie src/blocks/site-header/render.php) zet
   `zg-is-active` op zowel de desktop-link als de drawer-link, dus heeft de
   drawer een eigen "je bent hier"-kleur nodig. Dezelfde accentkleur als
   hover; de pijl blijft hover-only, want die is een affordance en geen
   staat. */
.zg-hdr__drawer-link.zg-is-active {
  color: var(--accent);
}

.zg-hdr__drawer.zg-is-open .zg-hdr__drawer-link {
  opacity: 1;
  transform: none;
}

.zg-hdr__drawer-foot {
  padding: 20px 24px calc(24px + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--border);
}

.zg-hdr__drawer-cta {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  box-sizing: border-box;
  background: var(--accent);
  color: var(--text-on-forest);
  font-family: var(--font-body);
  font-size: 17px;
  font-weight: var(--fw-semibold);
  text-decoration: none;
  padding: 16px;
  border-radius: var(--radius-pill);
  transition: background 0.2s ease;
}

.zg-hdr__drawer-cta:hover {
  background: var(--accent-hover);
}

@media (width <= 860px) {
  .zg-hdr__bar {
    grid-template-columns: 1fr auto;
    gap: 16px;

    /* Horizontale gutter blijft 24px, gelijk aan de inline padding op de
       <main> van elke template. Stond hier op 20px, waardoor logo en nav op
       mobiel 4px verder naar buiten stonden dan de content eronder. */
    padding: 16px 24px;
  }

  .zg-hdr__nav {
    display: none;
  }

  .zg-hdr__logo {
    justify-self: start;
  }

  .zg-hdr__logo-img {
    height: 40px;
  }

  .zg-hdr--solid {
    margin-bottom: 18px;
  }

  .zg-hdr__toggle {
    display: inline-flex;
  }
}

@media (width >= 861px) {
  .zg-hdr__scrim,
  .zg-hdr__drawer {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .zg-hdr__drawer,
  .zg-hdr__scrim,
  .zg-hdr__drawer-link,
  .zg-hdr__burger-bar {
    transition: none;
  }

  .zg-hdr__drawer-link {
    transform: none;
    opacity: 1;
  }
}

/* ── Footer ──────────────────────────────────────────────────────────────
   Nieuwsbrief-blok, drie link-kolommen, social-iconen, reuze-wordmark en
   legal-balk. Achtergrond leunt op de diepe zand-surface. */
.zg-footer {
  background: var(--surface-sand-deep);
  overflow: hidden;
  font-family: var(--font-body);
}

/* De footer sluit strak aan op de laatste sectie. `.wp-site-blocks` zet via de
   default block-gap 24px tussen `main` en de footer-template-part; tussen een
   gekleurde slotsectie en de (zand-)footer lekt die 24px als een streepje
   paginakleur (zichtbaar op bv. /merken, waar een zandband direct boven de
   footer staat). De footer-template-part is altijd het laatste kind van
   `.wp-site-blocks`. */
/* stylelint-disable-next-line selector-class-pattern -- WP-core layout-vendorclasses (.wp-site-blocks/.wp-block-template-part); geen zg-namespace beschikbaar. */
.wp-site-blocks > .wp-block-template-part:last-child {
  margin-top: 0;
}

/* Pagina-onderritme. Elke designsectie declareert in dit theme alléén
   TOP-spacing (`margin`/`padding: var(--section-y) 0 0`), dus de laatste
   sectie van een pagina levert zelf geen onderruimte. Samen met de
   flush-footer-regel hierboven (bedoeld voor gekleurde full-bleed
   slotbanden) eindigde daardoor élke pagina die niet op zo'n band eindigt
   strak tegen de footer: de cta-band-kaart op /molens en /koopgidsen, de
   FAQ-onderrand op een blogpost, de byline-kaart op een review, de
   paginering op een taxonomie-archief. `main` levert dat onderritme nu
   centraal, zodat losse blocks er niet elk een eigen `margin-bottom` voor
   hoeven mee te nemen. */
/* stylelint-disable-next-line selector-class-pattern -- WP-core layout-vendorclass (.wp-site-blocks); geen zg-namespace beschikbaar. */
.wp-site-blocks > main {
  padding-bottom: var(--section-y);
}

/* Uitzondering: eindigt de pagina wél op een full-bleed band (bv. de
   zandband op /merken en /over-ons of de blog-band op /blog), dan hoort die
   juist strak tegen de footer te liggen — zie de regel hierboven. De band is
   ofwel een direct kind van `main` (archive-templates) ofwel het laatste
   blok binnen `post_content` (pagina-templates). `:where()` houdt de
   post-content-vendorklasse buiten de specificiteit. */
/* stylelint-disable selector-class-pattern -- WP-core alignment-/layout-vendorclasses (.alignfull/.wp-block-post-content); geen zg-namespace beschikbaar. */
main:has(> .alignfull:last-child),
main:has(> :where(.wp-block-post-content) > .alignfull:last-child) {
  padding-bottom: 0;
}
/* stylelint-enable selector-class-pattern */

.zg-footer__inner {
  max-width: 1280px;
  margin: 0 auto;
  padding: 72px 24px 0;
}

.zg-footer__top {
  display: grid;

  /* Twee kolommen zolang de social-iconen verborgen zijn (zie het commentaar
     in parts/footer.html). De derde kolom was `auto` en droeg die iconen; met
     het blok uit de DOM valt die kolom op 0 terug, maar bleef de bijbehorende
     48px-gap wél staan als loze ruimte tegen de rechterrand. Terugzetten op
     `1.15fr 1fr auto` zodra de iconen terugkomen. */
  grid-template-columns: 1.15fr 1fr;
  gap: 48px;
}

.zg-footer h2 {
  margin: 0 0 16px;
  max-width: 14ch;
}

.zg-footer__lead {
  color: var(--text-subtle);
  max-width: 42ch;
  margin-bottom: 26px;
}

.zg-footer__news {
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--surface-raised);
  border: 1.5px solid var(--border-control);
  border-radius: var(--radius-pill);
  padding: 6px 6px 6px 22px;
  max-width: 460px;
}

.zg-footer__news input {
  flex: 1;
  font-family: var(--font-body);
  font-size: 18px;
  color: var(--text);
  background: transparent;
  border: none;
  outline: none;
  min-width: 0;
}

.zg-footer__news button {
  flex: none;
  width: 44px;
  height: 44px;
  border: none;
  border-radius: var(--radius-pill);
  background: var(--accent);
  color: var(--text-on-forest);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

.zg-footer__news button[disabled] {
  opacity: 0.6;
  cursor: progress;
}

/* Honeypot: moet voor mensen onbereikbaar zijn maar wel invulbaar blijven voor
   bots, dus geen `display:none` (dat slaan sommige bots over) maar uit beeld
   positioneren. De `position:absolute` haalt hem ook uit de flex-rij, anders
   zou `.zg-footer__news input { flex:1 }` hem breedte geven. */
.zg-footer__news-hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  opacity: 0;
}

.zg-footer__news-status {
  margin: 10px 0 0;
  font-size: 15px;
  color: var(--text-muted);
  max-width: 460px;
}

.zg-footer__news-status--error {
  color: var(--danger);
}

.zg-footer__cols {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 32px;
}

.zg-footer h4 {
  font-family: var(--font-display);
  font-size: var(--fs-h4);
  color: var(--text-heading);
  font-weight: var(--fw-semibold);
  margin: 0 0 16px;
  font-variation-settings: var(--display-vsettings);
}

.zg-footer__col {
  display: flex;
  flex-direction: column;
  gap: 11px;
}

.zg-footer__link {
  color: var(--text-subtle);
  font-family: var(--font-body);
  font-size: 18px;
  text-decoration: none;
}

.zg-footer__link:hover {
  color: var(--text-heading);
}

.zg-footer__social {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.zg-footer__social a {
  width: 40px;
  height: 40px;
  border-radius: var(--radius-pill);
  border: 1.5px solid var(--border-control);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-subtle);
}

.zg-footer__logo {
  display: block;
  width: 100%;
  height: auto;

  /* De negatieve ondermarge trekt de scheidingslijn van `__bottom` tot tegen
     de onderkant van het woordmerk aan, zodat "Zetgoed" op die lijn staat in
     plaats van er los boven te zweven.

     Waarom een percentage en geen px: de SVG heeft een viewBox van 1683x500
     met 31 eenheden loze ruimte onder de laagste inkt (de staart van de 'g',
     gemeten met een alfa-histogram). Bij `width:100%` is de gerenderde hoogte
     altijd breedte x 500/1683, en een procentuele marge rekent óók tegen de
     breedte van hetzelfde containing block. 31/1683 = 1,842% haalt die loze
     ruimte er dus op elke schermbreedte exact uit, waar een vaste px-waarde
     alleen op één breedte zou kloppen.

     7,13% en niet 1,842%: de basislijn van de letters ligt op y=380 van de
     500, dus (500-380)/1683 = 7,13% zet de lijn op die basislijn en laat de
     staart van de 'g' er doorheen zakken. Op 1,842% (de laagste inkt) raakt
     de lijn alleen die staart en blijven de letters er zichtbaar boven
     zweven, wat niet is wat "op de lijn" betekent.

     Dat de staart door de lijn zakt kan hier: hij hangt 89/1683 = 5,29% van
     de breedte onder de basislijn, zit horizontaal op ongeveer 35% van de
     breedte, en daar staat in `__bottom` niets. De copyrightregel staat
     links, de juridische links rechts. Verplaats je die, kijk dan opnieuw. */
  margin: 44px 0 -7.13%;
}

.zg-footer__bottom {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 16px;
  border-top: 1px solid color-mix(in oklab, var(--espresso-900) 14%, transparent);
  padding: 22px 0 28px;
}

.zg-footer__bottom p {
  color: var(--text-muted);
  font-size: var(--fs-xs);
  margin: 0;
}

.zg-footer__legal {
  display: flex;
  gap: 28px;
}

.zg-footer__legal .zg-footer__link {
  font-size: var(--fs-sm);
}

@media (width <= 900px) {
  .zg-footer__top {
    grid-template-columns: 1fr;
    gap: 40px;
  }

  .zg-footer__cols {
    grid-template-columns: 1fr 1fr;
  }

  .zg-footer__social {
    flex-direction: row;
  }
}

/* ── Button ──────────────────────────────────────────────────────────────
   Gedeeld knop-primitief (design system `Button`-component), niet
   hero-specifiek: `zetgoed/review-hero` is de eerste afnemer, latere
   blocks (cta-band, review-byline, voor-wie) hergebruiken dezelfde
   `.zg-btn`-basis + variant-modifiers in plaats van eigen knopstijlen. */
.zg-btn {
  --btn-pad-y: 0.56em;
  --btn-pad-x: 1.35em;

  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.55em;
  font-family: var(--font-body);
  font-weight: var(--fw-regular);
  font-size: var(--fs-body);
  line-height: 1;
  padding: calc(var(--btn-pad-y) + 0.05em) var(--btn-pad-x) calc(var(--btn-pad-y) - 0.05em);
  border: 1.5px solid transparent;
  border-radius: var(--radius-pill);
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  transition:
    background 0.18s ease,
    color 0.18s ease,
    border-color 0.18s ease,
    transform 0.06s ease;
}

.zg-btn:active {
  transform: translateY(1px);
}

.zg-btn:focus-visible {
  outline: var(--ring-width) solid var(--focus-ring);
  outline-offset: 2px;
}

.zg-btn[disabled],
.zg-btn[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
}

.zg-btn--sm {
  --btn-pad-y: 0.42em;
  --btn-pad-x: 1em;

  font-size: var(--fs-sm);
}

.zg-btn--lg {
  --btn-pad-y: 0.72em;
  --btn-pad-x: 1.7em;

  font-size: var(--fs-lead);
}

.zg-btn--block {
  display: flex;
  width: 100%;
}

/* primary — forest fill */
.zg-btn--primary {
  background: var(--accent);
  color: var(--text-on-forest);
}

.zg-btn--primary:hover:not([disabled]) {
  background: var(--accent-hover);
  color: var(--text-on-forest);
  text-decoration: none;
}

.zg-btn--primary:active:not([disabled]) {
  background: var(--accent-press);
}

/* secondary — outline
   Kleuren lopen via `var(..., fallback)` i.p.v. een vaste waarde, zodat
   `.zg-btn-accent-wrap` hieronder de kleur van een geneste secondary-knop
   kan herdefiniëren zonder een `.zg-btn-accent-wrap .zg-btn--secondary`-
   nakomeling-selector nodig te hebben — die zou met een `:hover:not(...)`
   erbij de project-cap van 3 klassen specificiteit overschrijden. Anders
   dan bij `.zg-hdr`/`.zg-voor-wie__card` hierboven wordt de custom property
   hier NIET ook op ditzelfde element (her)gedeclareerd: een property die
   op het element zelf staat wint altijd van een geërfde waarde, ongeacht
   specificiteit — dat zou de override vanuit de wrapper juist onschadelijk
   maken. De fallback in `var(--x, fallback)` levert het standaardgedrag
   zolang geen voorouder de custom property zet. */
.zg-btn--secondary {
  color: var(--btn-secondary-color, var(--text-heading));
  background: transparent;
  border-color: var(--btn-secondary-border, var(--border-control));
}

.zg-btn--secondary:hover:not([disabled]) {
  color: var(--btn-secondary-hover-color, var(--text-heading));
  background: var(--btn-secondary-hover-bg, var(--surface-raised));
  border-color: var(--btn-secondary-hover-border, var(--espresso-700));
  text-decoration: none;
}

/* ghost — text only */
.zg-btn--ghost {
  background: transparent;
  color: var(--accent);
  border-color: transparent;
  padding-left: 0.6em;
  padding-right: 0.6em;
}

.zg-btn--ghost:hover:not([disabled]) {
  background: var(--forest-100);
  color: var(--accent-press);
  text-decoration: none;
}

/* Accent-variant wrapper voor `.zg-btn--secondary`: op een sand/donkere
   achtergrond (cta-band, review-byline) leest de standaard
   `--border-control`-outline te zwak; deze wrapper herdefinieert de
   custom properties die `.zg-btn--secondary` hierboven consumeert, zodat
   elke geneste secondary-knop de `--accent`-kleur krijgt. Letterlijk het
   effect van `.zg-btn-accent-wrap` in het `<helmet>`-`\3c style>`-blok van
   ProductReview.dc.html, maar via cascade i.p.v. een nakomeling-selector.
   Eerste afnemers: `zetgoed/cta-band` en `zetgoed/review-byline`. */
.zg-btn-accent-wrap {
  --btn-secondary-color: var(--accent);
  --btn-secondary-border: var(--accent);
  --btn-secondary-hover-bg: var(--forest-100);
  --btn-secondary-hover-border: var(--accent);
  --btn-secondary-hover-color: var(--accent-press);
}

/* Gedeelde hover-lift voor linkbare kaarten (bv. `zetgoed/related-reviews`):
   letterlijk `.zg-card-link` uit hetzelfde `<helmet>`-`\3c style>`-blok als
   hierboven. Geen eigen sectie omdat dit — net als `.zg-btn`— een
   klein, bloknaam-onafhankelijk primitief is dat meerdere blocks kunnen
   hergebruiken. */
.zg-card-link {
  text-decoration: none;
  transition:
    transform 0.16s ease,
    box-shadow 0.16s ease,
    border-color 0.16s ease;
}

/* `text-decoration: none` óók op :hover — de kaart is een `<a>`, dus de
   globale `a:hover { text-decoration: underline }` (specificiteit 0,1,1)
   wint anders van de kale klasse (0,1,0) en onderstreept élke tekst in de
   kaart. Zelfde neutralisatie als `.zg-rc:hover`/`.zg-brandcard:hover`. */
.zg-card-link:hover {
  text-decoration: none;
  transform: translateY(-3px);
  box-shadow: var(--shadow-md);
  border-color: var(--border-control);
}

/* ── Badge ────────────────────────────────────────────────────────────────
   Klein, blocknaam-onafhankelijk primitief (docs/conventions/reuse.md §1
   noemt "badge" met naam als gedeeld primitief) voor een los, opvallend
   label bovenop media — bv. de "Onze keuze"-badge op de hoogst scorende
   kaart in `zetgoed/review-archive` (zie inc/review-card.php,
   `zetgoed_render_review_archive_card()`), eerste afnemer. De bron
   (ReviewOverzicht.dc.html) importeert hiervoor een los Badge-component
   (`variant="solid" eyebrow`) waarvan de eigen CSS niet in dit brondocument
   staat; `.zg-badge`/`--solid` hieronder zijn de BEM-vertaling van dat
   uiterlijk (espresso-fill, paper-tekst, pill, eyebrow-tracking) zodat een
   volgende afnemer 'm hergebruikt in plaats van een eigen badge-variant te
   bouwen. */
.zg-badge {
  display: inline-flex;
  align-items: center;
  padding: 0.35em 0.9em;
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  border-radius: var(--radius-pill);
}

.zg-badge--solid {
  background: var(--espresso-900);
  color: var(--paper-50);
}

/* Sage-variant + eyebrow-modifier: tweede afnemer van dit primitief is de
   sage-badge ("Waar {merk} voor staat") in `zetgoed/merk-verhaal` (zie
   src/blocks/merk-verhaal/render.php), bron `Badge variant="sage"
   eyebrow="true"` (components/display/Badge.jsx in het Claude
   Design-project). Die bron-CSS geeft de badge zelf al
   `text-transform:uppercase;letter-spacing:...` via een aparte
   `.zg-badge--eyebrow`-modifier i.p.v. dat in `--sage` te bakken, zodat een
   toekomstige niet-eyebrow sage-badge (bv. een losse pil-badge) `--sage`
   zonder tracking kan hergebruiken. `--forest-200`/`--forest-700` zijn
   dezelfde tokens als de bron zelf gebruikt. */
.zg-badge--sage {
  background: var(--forest-200);
  color: var(--forest-700);
}

.zg-badge--eyebrow {
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
}

/* ── Breadcrumb ───────────────────────────────────────────────────────────
   Broodkruimel van `zetgoed/breadcrumb` (data-gebonden block, zie
   src/blocks/breadcrumb/render.php). 1-op-1 geport uit de
   broodkruimel-strook boven de hero in ProductReview.dc.html naar BEM +
   tokens; de bron gebruikt daar de gedeelde `a.zg-link`-utility, hier
   BEM-scoped als `.zg-breadcrumb__link` (zie docs/conventions/css.md
   regel 2 — geen ongeprefixte utility-classes). */
.zg-breadcrumb {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 28px 0 0;
  color: var(--text-subtle);
  font-size: var(--fs-sm);
}

.zg-breadcrumb__link {
  color: var(--accent);
  font-weight: var(--fw-regular);
}

.zg-breadcrumb__link:hover {
  color: var(--accent-press);
}

.zg-breadcrumb__current {
  color: var(--text-muted);
}

/* ── Review hero ─────────────────────────────────────────────────────────
   Hero + scorecard van `zetgoed/review-hero` (data-gebonden block, zie
   src/blocks/review-hero/render.php). 1-op-1 geport uit de inline-styles
   van ProductReview.dc.html naar BEM + tokens. De `#d9b48a`-koperkleur van
   de scorecard-cirkel (border/label) bestaat niet als token; `--sand-400`
   ligt er het dichtst bij (zie docs/conventions/css.md §1: kleuren zijn
   hard, altijd via token — nooit een nieuwe ad-hoc hex toevoegen). */
.zg-review-hero {
  position: relative;
  margin-top: 28px;
  padding: 56px;
  background: var(--sand-100);
  border-radius: var(--radius-xl);
}

.zg-review-hero__seal {
  position: absolute;
  top: -42px;

  /* -24 (was -30) houdt de seal binnen de 24px main-inset → geen +6px
     page-overflow @1280; hangt af van de container max-width @1280 */
  right: -24px;
  z-index: 3;
  filter: drop-shadow(var(--shadow-lg));
  will-change: transform;
}

.zg-review-hero__seal-bg {
  fill: var(--accent);
}

.zg-review-hero__seal-text {
  font-family: var(--font-body);
  font-size: 15px;
  font-weight: var(--fw-semibold);
  letter-spacing: 3.5px;
  text-transform: uppercase;
  fill: var(--paper-50);
}

/* De niet-getest-zegel draagt "Op basis van reviews van anderen · Zetgoed ·",
   44 tekens tegen 30 voor de getest-variant. Op de standaard 15px/3.5px liep
   dat voorbij het einde van de boog (2πr = circa 478px bij r=76) en overlapte
   de staart de kop. Deze waarden houden 'm binnen één omwenteling. */
.zg-review-hero__seal-text--compact {
  font-size: 11.5px;
  letter-spacing: 1.35px;
}

.zg-review-hero__seal-icon {
  stroke: var(--paper-50);
  stroke-width: 2.6;
}

.zg-review-hero__grid {
  display: grid;
  grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.1fr);
  align-items: center;
  gap: 56px;
}

.zg-review-hero__image {
  display: block;
  width: 100%;
  height: 420px;
  object-fit: contain;
  background: var(--sand-300);
  border-radius: var(--radius-lg);
}

.zg-review-hero__image--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-subtle);
  font-size: var(--fs-sm);
}

.zg-review-hero__title {
  margin: 16px 0 18px;
  font-size: 3.2rem;
  line-height: 1.04;
}

.zg-review-hero__lead {
  max-width: 46ch;
  margin: 0 0 18px;
  color: var(--text-subtle);
  font-size: var(--fs-lead);
}

.zg-review-hero__rating {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 26px;
}

.zg-review-hero__stars {
  color: var(--warning);
  font-size: 20px;
  letter-spacing: 3px;
}

.zg-review-hero__rating-count {
  color: var(--text-subtle);
  font-size: var(--fs-sm);
}

.zg-review-hero__scorecard {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 24px;
  padding: 26px;
  background: var(--espresso-900);
  border-radius: var(--radius-lg);
}

.zg-review-hero__score-circle {
  display: flex;
  flex: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 104px;
  height: 104px;
  color: var(--paper-50);
  border: 2px solid var(--sand-400);
  border-radius: var(--radius-pill);
}

.zg-review-hero__score-value {
  font-family: var(--font-display);
  font-size: 2.1rem;
  line-height: 1;
  font-variation-settings: var(--display-vsettings);
}

.zg-review-hero__score-label {
  margin-top: 5px;
  color: var(--sand-400);
  font-family: var(--font-body);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.zg-review-hero__score-body {
  flex: 1;
  min-width: 200px;
}

.zg-review-hero__score-heading {
  margin: 0 0 6px;
  color: var(--paper-50);
  font-family: var(--font-display);
  font-size: var(--fs-h4);
  font-variation-settings: var(--display-vsettings);
}

.zg-review-hero__score-blurb {
  margin: 0 0 16px;
  color: var(--paper-100);
  font-size: var(--fs-sm);
  line-height: 1.5;
  opacity: 0.82;
}

.zg-review-hero__score-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.zg-review-hero__anchor {
  align-self: center;
  color: var(--paper-100);
  font-size: var(--fs-sm);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.zg-review-hero__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 40px;
  margin-top: 24px;
  padding-top: 22px;
  border-top: 1px solid color-mix(in oklab, var(--espresso-900) 16%, transparent);
}

.zg-review-hero__meta-label {
  margin: 0;
  color: var(--text-subtle);
  font-size: var(--fs-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.zg-review-hero__meta-value {
  margin: 4px 0 0;
  color: var(--text-heading);
  font-weight: var(--fw-semibold);
}

@media (width <= 900px) {
  .zg-review-hero {
    padding: 32px;
  }

  .zg-review-hero__grid {
    grid-template-columns: 1fr;
    gap: 28px;
  }

  .zg-review-hero__title {
    font-size: 2.2rem;
  }
}

/* ── Voor wie ─────────────────────────────────────────────────────────────
   Twee kaarten van `zetgoed/voor-wie` (data-gebonden block, zie
   src/blocks/voor-wie/render.php). 1-op-1 geport uit de "Kies 'm als /
   Kijk verder als"-sectie van ProductReview.dc.html naar BEM + tokens. De
   kleurwissel tussen de "wel"- en "niet"-kaart loopt via lokale custom
   properties op .zg-voor-wie__card (zelfde patroon als .zg-hdr hierboven),
   zodat elke selector op één class blijft en er geen `--niet`-varianten
   van elk kind-element nodig zijn. */
.zg-voor-wie {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-6);
  padding: var(--section-y) 0 0;
}

.zg-voor-wie--single {
  grid-template-columns: 1fr;
  max-width: 640px;
}

.zg-voor-wie__card {
  --vw-icon-color: var(--success);
  --vw-text-color: var(--text-muted);

  padding: var(--space-8);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}

.zg-voor-wie__card--niet {
  --vw-icon-color: var(--text-subtle);
  --vw-text-color: var(--text-subtle);
}

.zg-voor-wie__title {
  margin: 0 0 var(--space-5);
  color: var(--text-heading);
  font-family: var(--font-display);
  font-size: var(--fs-h4);
  font-variation-settings: var(--display-vsettings);
}

.zg-voor-wie__list {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.zg-voor-wie__item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
}

.zg-voor-wie__icon {
  flex: none;
  margin-top: 2px;
  color: var(--vw-icon-color);
}

.zg-voor-wie__text {
  color: var(--vw-text-color);
}

@media (width <= 900px) {
  .zg-voor-wie {
    grid-template-columns: 1fr;
  }

  .zg-voor-wie--single {
    max-width: none;
  }
}

/* ── Score bars ───────────────────────────────────────────────────────────
   "Hoe we scoorden"-sectie van `zetgoed/score-bars` (editor-content block,
   `rows`-attribute beheerd in edit.js). Full-bleed sand-achtergrond +
   2-koloms scoregrid, 1-op-1 geport uit de "Beoordeling / scores"-sectie
   van ProductReview.dc.html naar BEM + tokens. `.zg-score-track` en
   `.zg-score-fill` zijn letterlijk de bron-classnamen (ook zo genoemd in
   het `data-dc-script`) — de vulanimatie in assets/js/score-bars.js zet de
   breedte naar `data-w` zodra de sectie in beeld scrollt
   (IntersectionObserver, met stagger per rij). De 1120px-containerbreedte
   is de bron-waarde (zie ook de root-`<div>` in ProductReview.dc.html) —
   geen token, want breekt bewust uit de 720px-`contentSize` van de
   pagina-layout (zie theme.json). */
.zg-score-bars {
  margin: var(--section-y) calc(50% - 50vw) 0;
  padding: var(--section-y) 0;
  background: var(--sand-100);
}

.zg-score-bars__inner {
  /* content-box: max-width telt hier de CONTENTbreedte, net als
     .zg-hdr__bar en .zg-footer__inner. Zonder deze regel erven de inners
     die als wp:group in een template staan WP's border-box, waardoor hun
     inhoud 24px verder naar binnen valt dan de rest van de pagina. */
  box-sizing: content-box;
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 24px;
}

.zg-score-bars__heading {
  margin: 12px 0 var(--space-8);
  font-size: var(--fs-h1);
}

.zg-score-bars__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 28px 64px;
}

.zg-score-bars__row-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 9px;
}

.zg-score-bars__label {
  color: var(--text-heading);
  font-weight: var(--fw-semibold);
}

.zg-score-bars__value {
  color: var(--text-heading);
  font-family: var(--font-display);
  font-variation-settings: var(--display-vsettings);
}

/* Editor-only rij-chrome (number-input + verwijder-knop): geen
   bron-equivalent, dus geen ProductReview.dc.html-herkomst, maar wel
   dezelfde token-discipline. */
.zg-score-bars__score-input {
  width: 4.5em;
  padding: var(--space-1) var(--space-2);
  color: var(--text-heading);
  font-family: var(--font-display);
  background: var(--surface-raised);
  border: 1px solid var(--border-control);
  border-radius: var(--radius-sm);
}

.zg-score-track {
  height: 8px;
  overflow: hidden;
  background: var(--surface-raised);
  border-radius: var(--radius-pill);
}

.zg-score-fill {
  width: 0;
  height: 100%;
  background: var(--accent);
  border-radius: var(--radius-pill);
  transition: width 1.1s cubic-bezier(0.22, 1, 0.36, 1);
}

@media (prefers-reduced-motion: reduce) {
  .zg-score-fill {
    transition: none;
  }
}

@media (width <= 900px) {
  .zg-score-bars__grid {
    grid-template-columns: 1fr;
  }
}

/* ── Kenmerk-badges ───────────────────────────────────────────────────────
   1-op-1 geport uit ProductReview.dc.html: de rechterkolom (58%) van de
   "De cijfers"-sectie opent met deze 4-tegel icoon-grid, vóór het
   Kenmerken-paneel (`.zg-specs-table`, zie hieronder) — `zetgoed/
   kenmerk-badges` (src/blocks/kenmerk-badges/render.php), eigen
   icoon-whitelist (molen/klok/pid/watertank), geen vrije SVG-input.

   Achtergrond `--stone-100` (#e2dcd2), dezelfde tint als het
   "De machine kort"-paneel (`.zg-panel--sand`). Stond eerder op
   `--sand-100`. Voor dit blok is nooit een bron-hex vastgelegd; de eerste
   versie van deze docblock zei alleen "1-op-1 geport" zonder waarde. De
   tint is daarom op 2026-09-03 door de gebruiker bevestigd in plaats van
   uit de bron afgeleid. Wijkt de bron hiervan af, dan is dit de plek om
   te corrigeren. */
.zg-kenmerk-badges {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 14px;
}

.zg-kenmerk-badge {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  padding: 22px 12px;
  text-align: center;
  background: var(--stone-100);
  border-radius: var(--radius-lg);
}

.zg-kenmerk-badge__icon {
  display: flex;
}

.zg-kenmerk-badge svg {
  color: var(--text-subtle);
}

.zg-kenmerk-badge__label {
  color: var(--text-subtle);
  font-size: var(--fs-md);
  line-height: var(--lh-snug);
}

/* Smalle tabletband: tussen de WordPress-kolomklap (kolommen staan vanaf
   782px naast elkaar) en ~900px staan de vier chips in de 58%-kolom en wordt
   een chip smaller dan de 99px die het langste label nodig heeft op 16px
   ("temperatuur" meet 75px + 2x12px chip-padding). Bij 800px viewport is een
   chip ~98px, dus daar loopt dat woord net buiten zijn padding. Onder 640px
   klapt de grid naar 2 kolommen (zie de media query hieronder) en is er weer
   ruim genoeg plek voor 16px. */
@media (782px <= width <= 900px) {
  .zg-kenmerk-badge__label {
    font-size: var(--fs-sm);
  }
}

/* Editor-only samenvatting (geen bron-equivalent) — zie edit.js voor de
   read-only-preview-afweging, zelfde patroon als
   `.zg-koopgids-koopwijzer__preview-note`/`__preview-list` hieronder. */
.zg-kenmerk-badges__preview-note {
  max-width: 60ch;
  margin: 0 0 12px;
  color: var(--text-muted);
}

.zg-kenmerk-badges__preview-list {
  margin: 0;
  padding-left: 1.2em;
  color: var(--text-muted);
}

/* Mobiel: 2-koloms i.p.v. 4, zelfde afweging als andere 4-tegel-grids in
   dit bestand (bv. `.zg-score-bars__grid` hierboven) — 0-overflow op
   smalle viewports. */
@media (width <= 640px) {
  .zg-kenmerk-badges {
    grid-template-columns: 1fr 1fr;
  }
}

/* ── Specs table ──────────────────────────────────────────────────────────
   1-op-1 geport uit het "Kenmerken"-paneel van de "Specificaties"-sectie
   in ProductReview.dc.html. De bron gebruikt een kale `background:#eaddcc`
   voor dit paneel — kale hex is hier hoe dan ook niet toegestaan
   (stylelint color-no-hex). `--paper-300` (#ece0c8) is het gekozen token:
   de dichtstbijzijnde in het palet (afstand 5,4 tegen 10,0 voor
   `--sand-100`, dat hier eerder stond en dit paneel zichtbaar te geel
   maakte). Voor andere panelen gebruikt dit bestand de sand-familie
   (`.zg-review-hero`, `.zg-score-bars`). De
   `.zg-srow`-rijstijl komt woordelijk uit de bron en blijft daarom die
   naam in plaats van een `.zg-specs-table__row`-BEM-variant — puur voor
   traceerbaarheid naar de bron, niet omdat er JS aan hangt. */
.zg-specs-table {
  padding: var(--space-5) 44px 26px;
  background: var(--paper-300);
  border-radius: var(--radius-xl);
}

.zg-specs-table__heading {
  margin: 18px 0 6px;
  color: var(--text-heading);
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-variation-settings: var(--display-vsettings);
}

.zg-specs-table__table {
  width: 100%;
  border-collapse: collapse;
}

.zg-srow th,
.zg-srow td {
  padding: var(--space-5) 0;
  color: var(--text-heading);
  font-weight: var(--fw-semibold);
  font-size: 1.0625rem;
  text-align: right;
  vertical-align: middle;
  border-top: 1px solid var(--border);
}

/* Labelcel is een `<th scope="row">` (zie render.php) voor een programmatisch
   vastgelegde label/waarde-relatie voor schermlezers; visueel identiek aan de
   bron (links uitgelijnd, gedempte kleur, normaal gewicht). `--text-subtle`
   i.p.v. de brontekst-token `--text-muted`: die laatste haalt op deze
   `--sand-100`-achtergrond maar ~3,7:1 contrast (onder de WCAG AA-eis van
   4,5:1 voor normale tekst); `--text-subtle` haalt ruim boven 4,5:1 en blijft
   visueel dezelfde "gedempte label"-rol vervullen. */
.zg-srow th {
  width: 40%;
  color: var(--text-subtle);
  font-weight: var(--fw-regular);
  text-align: left;
}

.zg-srow:first-child th,
.zg-srow:first-child td {
  border-top: none;
}

/* Hogere specificiteit dan `.zg-srow td` nodig, anders wint de generieke
   rij-regel altijd (beide zijn class+element, maar bronvolgorde beslist
   dan willekeurig) en verschijnt de accentkleur nooit. */
.zg-srow td.zg-specs-table__value--accent {
  color: var(--accent);
}

/* ── Panel ────────────────────────────────────────────────────────────────
   Generieke sand-panel-utility. Eerste gebruik: het "De machine kort"-
   verhaalblok naast de Kenmerken-tabel in de "Specificaties"-sectie van
   ProductReview.dc.html. De bron gebruikt daar een kale
   `background:#e2dcd2;border-radius:var(--radius-xl);padding:32px 44px 36px`
   — kale hex mag hier niet (stylelint color-no-hex). `--stone-100` matcht
   die bron exact. Stond eerder op `--sand-100` (afstand 17,9), gekozen om
   dit paneel gelijk te houden aan het aangrenzende `.zg-specs-table`. Dat
   was een verkeerde afweging: de bron gééft die twee panelen bewust net
   andere tinten (#e2dcd2 koel greige tegen #eaddcc warm), dus ze
   gelijktrekken was juist de drift. Beide staan nu op hun eigen
   dichtstbijzijnde token.
   LET OP: de klassenaam `--sand` dekt de kleur daarmee niet meer letterlijk;
   bewust zo gelaten om een rename door de seed-scripts te vermijden.
   `.zg-panel` is de kale utility (radius); `--sand` is de kleurvariant,
   zodat een eventuele toekomstige `--paper`-variant zonder herhaling kan. */
.zg-panel {
  border-radius: var(--radius-xl);
}

.zg-panel--sand {
  padding: 32px 44px 36px;
  background: var(--stone-100);
}

/* ── FAQ ──────────────────────────────────────────────────────────────────
   1-op-1 geport uit de "FAQ"-sectie in ProductReview.dc.html: native
   `<details class="zg-faq">`-accordions, geen JS — de `+`→`×`-icoonrotatie
   gebeurt puur via CSS op `[open]`. De bron gebruikt geen `!important`,
   dus hier ook niet. `.zg-faq-ico` komt woordelijk uit de bron (zelfde
   traceerbaarheids-reden als `.zg-srow` hierboven). */
.zg-faq {
  padding: 0;
  border-bottom: 1px solid var(--border);
}

.zg-faq summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 26px 0;
  overflow: clip;
  overflow-wrap: anywhere;
  color: var(--text-heading);
  font-family: var(--font-display);
  font-size: var(--fs-h4);
  font-variation-settings: var(--display-vsettings);
  list-style: none;
  cursor: pointer;
}

.zg-faq summary::-webkit-details-marker {
  display: none;
}

.zg-faq .zg-faq-ico {
  flex: none;
  transition: transform 0.2s ease;
}

.zg-faq[open] .zg-faq-ico {
  transform: rotate(45deg);
}

.zg-faq p {
  max-width: 76ch;
  margin: 0 0 26px;
  color: var(--text-subtle);
  font-size: 18px;
  line-height: 1.55;
}

/* Editor-only preview-chrome voor het FAQ-block: geen bron-equivalent (de
   editor toont vraag/antwoord altijd volledig i.p.v. als echte
   `<details>`, zie de toelichting in src/blocks/faq/edit.js), maar wel
   dezelfde token-discipline. Eigen naamgeving (geen bron-afkomst), dus hier
   gewoon BEM: `.zg-faq-group` is de bloknaam voor de lijst-wrapper. */
.zg-faq-group {
  display: flex;
  flex-direction: column;
}

/* Optionele FAQ-sectiekop (eyebrow + h2, gezet via de eyebrow/heading-attrs
   op koopgids/vergelijker). De bron (ProductReview.dc.html FAQ-sectie) geeft
   de h2 een `--fs-h1`-override + `margin:12px 0 24px`; zonder deze class
   erft de kale `wp:heading`-h2 de generieke `--fs-h2` uit base.css.

   `__head` draagt het sectieritme. Dat stond eerder als `.zg-section-gap` op
   de eyebrow zelf, maar die is een inline `<span>` en negeerde de
   `margin-top`, waardoor de FAQ overal tegen het blok erboven plakte. Deze
   wrapper is block-level, hoort bij het block zelf (dus werkt óók in een
   FSE-template, waar `.zg-section-gap` niet reikt) en valt weg zodra er geen
   eigen kop is: staat de kop als losse core-blocks vóór het block
   (`.zg-faq-section` op archive-zg_machine), dan levert die groep het ritme.
   `:first-child` is uitgezonderd: opent een sectie mét een FAQ, dan zorgt de
   sectie zelf al voor de aanloop. */
.zg-faq-group__head {
  margin-top: var(--section-y);
}

.zg-faq-group__head:first-child {
  margin-top: 0;
}

.zg-faq-group__heading {
  margin: 12px 0 24px;
  font-size: var(--fs-h1);
}

.zg-faq__summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 26px 0 0;
}

/* ── Related reviews ─────────────────────────────────────────────────────
   "Overweeg ook"-kaarten van `zetgoed/related-reviews` (data-gebonden
   block: een eigen `WP_Query` naar 3 andere `zg_machine`-posts, zie
   src/blocks/related-reviews/render.php). 1-op-1 geport uit de
   "Overweeg ook"-sectie van ProductReview.dc.html naar BEM + tokens. De
   hover-lift op elke kaart komt van de gedeelde `.zg-card-link`-primitief
   (zie de Button-sectie hierboven), niet van een eigen block-specifieke
   regel. `rgb(223,203,166)` uit de bron (featured-image-achtergrond) is
   letterlijk `--sand-300` (`#dfcba6`) — geen ad-hoc hex nodig.

   `.zg-related-reviews__grid`/`__card` zijn het gedeelde "related-grid"-
   primitief (zie docs/conventions/reuse.md §1): `zetgoed/review-grid`
   hergebruikt `__card` 1-op-1 op de frontend (in een `#zg-reviews-track`-
   carousel i.p.v. dit grid, zie de "Review-grid"-sectie verderop) — de
   `--cols-4`-modifier hieronder wordt alleen nog gebruikt door dat blocks
   `edit.js` (statische editor-preview, geen carousel-JS in de admin). */
.zg-related-reviews {
  padding: var(--section-y) 0 0;
}

.zg-related-reviews__heading {
  margin: 12px 0 32px;
  font-size: var(--fs-h1);
}

.zg-related-reviews__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-6);
}

/* Modifier voor `zetgoed/review-grid`: zelfde grid-primitief, 4 kolommen
   i.p.v. 3. Compound selector (twee classes) zodat de kolomaantal-override
   op elke breedte wint van de basisregel, ongeacht brondocument-volgorde —
   blijft ruim onder de project-cap van 3 klassen specificiteit. */
.zg-related-reviews__grid.zg-related-reviews__grid--cols-4 {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

.zg-related-reviews__card {
  display: block;
  overflow: hidden;
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  text-decoration: none;
}

.zg-related-reviews__image {
  display: block;
  width: 100%;
  height: 240px;
  object-fit: contain;
  background: var(--sand-300);
  border-bottom: 1px solid var(--border);
}

.zg-related-reviews__image--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-subtle);
  font-size: var(--fs-sm);
}

.zg-related-reviews__body {
  padding: var(--space-5);
}

.zg-related-reviews__rating {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: 6px;
}

.zg-related-reviews__stars {
  color: var(--warning);
}

.zg-related-reviews__rating-value {
  color: var(--text-subtle);
  font-size: var(--fs-sm);
}

.zg-related-reviews__title {
  margin: 0 0 4px;
  color: var(--text-heading);
  font-weight: var(--fw-semibold);
}

.zg-related-reviews__tagline {
  margin: 0 0 var(--space-4);
  color: var(--text-muted);
}

.zg-related-reviews__link {
  color: var(--accent);
  font-weight: var(--fw-semibold);
}

@media (width <= 900px) {
  .zg-related-reviews__grid {
    grid-template-columns: 1fr;
  }
}

@media (width <= 960px) {
  .zg-related-reviews__grid.zg-related-reviews__grid--cols-4 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (width <= 640px) {
  .zg-related-reviews__grid.zg-related-reviews__grid--cols-4 {
    grid-template-columns: 1fr;
  }
}

/* ── CTA band ─────────────────────────────────────────────────────────────
   Sand cross-sell-paneel van `zetgoed/cta-band` (herbruikbaar, niet aan
   een post gebonden block — alle inhoud komt uit block-attributen, zie
   src/blocks/cta-band/render.php). 1-op-1 geport uit de "Volgende
   stap"-sectie van ProductReview.dc.html naar BEM + tokens.
   `--grid--no-image` is een eigen toevoeging (geen bron-equivalent): de
   bron toont het beeld altijd, maar `imageUrl` is in dit block bewust
   optioneel (herbruik op homepage/koopgids zonder passend beeld). */
.zg-cta-band {
  margin: var(--section-y) 0 0;
  padding: var(--space-10);
  background: var(--sand-100);
  border-radius: var(--radius-xl);
}

.zg-cta-band__grid {
  display: grid;
  grid-template-columns: 200px 1fr auto;
  align-items: center;
  gap: 36px;
}

.zg-cta-band__grid--no-image {
  grid-template-columns: 1fr auto;
}

.zg-cta-band__image {
  display: block;
  width: 100%;
  height: 170px;
  object-fit: cover;
  border-radius: var(--radius-lg);
}

.zg-cta-band__image--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--sand-300);
  color: var(--text-subtle);
  font-size: var(--fs-sm);
  text-align: center;
}

.zg-cta-band__heading {
  margin: 8px 0;
  color: var(--text-heading);
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-variation-settings: var(--display-vsettings);
}

.zg-cta-band__text {
  max-width: 60ch;
  margin: 0;
  color: var(--text-muted);
}

.zg-cta-band__action {
  justify-self: end;
}

@media (width <= 900px) {
  .zg-cta-band__grid,
  .zg-cta-band__grid--no-image {
    grid-template-columns: 1fr;
  }

  .zg-cta-band__action {
    justify-self: start;
  }
}

/* Dark/centered variant — "keuzehulp-band", tweede afnemer van
   `zetgoed/cta-band` naast de sand-variant hierboven (zie
   src/blocks/cta-band/render.php, `variant="dark"`). 1-op-1 geport uit de
   "Keuzehulp-band"-sectie van ReviewOverzicht.dc.html (Fase 4b Task 2):
   espresso-900-achtergrond, amber eyebrow (`.eyebrow--amber`, zie base.css),
   witte kop, gecentreerde lead en een primary-knop (i.p.v. de secondary/
   accent-knop van de sand-variant) — geen beeldkolom, geen grid.
   Bewust een `variant`-attribuut op het bestaande block i.p.v. een nieuw
   `zetgoed/cta-dark`-block: dezelfde attributen (eyebrow/heading/text/
   buttonLabel/buttonUrl) beschrijven beide varianten, alleen layout en
   kleurstelling wijken af. Modifier-classes staan direct op elk kind-
   element (`__heading--dark`/`__text--dark`/`__action--dark`) i.p.v. een
   `.zg-cta-band--dark .zg-cta-band__heading`-nakomeling-selector, conform
   docs/conventions/css.md §2/§5 (platte, één-class-selectors). */
.zg-cta-band--dark {
  position: relative;
  overflow: hidden;
  padding: 64px 40px;
  background: var(--espresso-900);
  text-align: center;
}

/* Decoratieve koffiebonen-laag (1-op-1 uit de KeuzehulpCta-DS-component):
   absoluut in de band, achter de inhoud (zie de `position: relative` op de
   inhoud hieronder), afgekapt op de radius van de band. */
.zg-cta-band__beans {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  border-radius: inherit;
}

.zg-cta-band__bean {
  position: absolute;
  stroke: color-mix(in oklab, var(--paper-50) 44%, transparent);
  opacity: 0.5;
  will-change: transform;
}

/* `non-scaling-stroke` houdt de lijndikte gelijk ongeacht de per-boon
   verschillende width/height (zie de inline-styles in render.php). */
.zg-cta-band__bean * {
  vector-effect: non-scaling-stroke;
  stroke-width: 1.4;
}

/* Inhoud boven de bonen-laag: de bonen zijn absoluut (dus geschilderd na
   niet-gepositioneerde siblings); door de inhoud `position: relative` te geven
   wint die op DOM-volgorde, ook als een langere kop tot in de marges reikt. */
.zg-cta-band--dark > :not(.zg-cta-band__beans) {
  position: relative;
}

.zg-cta-band__heading--dark {
  margin: 18px 0 14px;
  color: var(--paper-50);
  font-size: var(--fs-h2);
}

.zg-cta-band__text--dark {
  max-width: 52ch;
  margin: 0 auto 30px;
  color: color-mix(in oklab, var(--paper-50) 78%, transparent);
}

/* `justify-self: center` overschrijft hier bewust `justify-self: end` van
   de sand-variant hierboven — beide classes staan samen op dit element, en
   `justify-self` werkt in moderne browsers ook op gewone block-level boxes
   (niet alleen grid-items), dus zonder deze override zou de knop bij dit
   blok rechts blijven hangen i.p.v. centreren. */
.zg-cta-band__action--dark {
  display: flex;
  justify-content: center;
  justify-self: center;
}

@media (width <= 640px) {
  .zg-cta-band--dark {
    margin-top: 2.75rem;
    padding: 44px 24px;
  }

  .zg-cta-band__heading--dark {
    font-size: 1.7rem;
  }
}

/* ── Review byline ────────────────────────────────────────────────────────
   "Over deze review"-auteursblok van `zetgoed/review-byline` (herbruikbaar,
   grotendeels vaste content — zie src/blocks/review-byline/render.php).
   1-op-1 geport uit de "Over deze review"-sectie van ProductReview.dc.html
   naar BEM + tokens. `.zg-review-byline__step-num`/`__steps`/`__step` zijn
   niet data-gebonden: de 3 stappen liggen vast in render.php, dit zijn dus
   gewoon structurele BEM-classes, geen bron-letterlijke namen zoals
   `.zg-srow`. */
.zg-review-byline {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr) minmax(0, 1fr);
  align-items: stretch;
  gap: 56px;
  margin: var(--section-y) 0 0;
  padding: var(--space-16);
  background: var(--surface-raised);
  border-radius: var(--radius-xl);
}

.zg-review-byline__col--steps {
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  padding-top: var(--space-6);
}

.zg-review-byline__steps-heading {
  margin: 0 0 28px;
  color: var(--text-heading);
  font-family: var(--font-display);
  font-size: 2.6rem;
  line-height: 1.05;
  font-variation-settings: var(--display-vsettings);
}

.zg-review-byline__steps {
  display: flex;
  flex-direction: column;
  gap: 18px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.zg-review-byline__step {
  display: flex;
  align-items: center;
  gap: 14px;
}

.zg-review-byline__step-num {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  color: var(--text-heading);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  background: var(--sand-200);
  border: 1px solid var(--border-control);
  border-radius: var(--radius-pill);
}

.zg-review-byline__step-text {
  color: var(--text-muted);
}

.zg-review-byline__portrait {
  display: block;
  width: 100%;
  height: 100%;
  min-height: 600px;
  object-fit: cover;
  border-radius: var(--radius-lg);
}

.zg-review-byline__portrait--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 320px;
  background: var(--sand-300);
  color: var(--text-subtle);
  font-size: var(--fs-sm);
}

.zg-review-byline__col--content {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding-bottom: var(--space-6);
}

.zg-review-byline__heading {
  margin: 12px 0 var(--space-5);
  font-size: var(--fs-h2);
  line-height: 1.1;
  overflow-wrap: break-word;
  text-wrap: balance;
}

.zg-review-byline__intro {
  max-width: 40ch;
  margin: 0 0 28px;
  color: var(--text-subtle);
  font-size: 1.25rem;
}

.zg-review-byline__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
}

.zg-review-byline__disclaimer {
  max-width: 44ch;
  margin: 28px 0 0;
  color: var(--text-muted);
  font-size: var(--fs-sm);
}

@media (width <= 900px) {
  .zg-review-byline {
    grid-template-columns: 1fr;
    gap: 32px;
  }

  .zg-review-byline__portrait {
    min-height: 320px;
  }

  .zg-review-byline__col--steps,
  .zg-review-byline__col--content {
    padding: 0;
  }
}

@media (width <= 640px) {
  .zg-review-byline {
    padding: var(--space-8); /* 32 — spiegelt DS .zg-panel{padding:26px} */
  }
}

/* ── Site hero ────────────────────────────────────────────────────────────
   Full-bleed homepage-hero van `zetgoed/site-hero` (zie
   src/blocks/site-hero/render.php). 1-op-1 geport uit de
   "Hero (full-bleed)"-sectie van Homepage.dc.html naar BEM + tokens. Het
   achtergrondbeeld zelf komt via een inline `style` van render.php (per-post
   instelbaar); hier alleen de vaste opmaak eromheen.
   De overlay-gradient gebruikt kale `rgba()`-stops i.p.v. een token: dit is
   geen losstaande hex-kleur maar een site-specifieke, meerstops-gradient op
   een `background`-property (zie docs/conventions/css.md §1 — de
   hex-only-regel geldt voor kleur-properties, niet voor zo'n gradient-
   samenstelling die niet 1-op-1 op een bestaand token valt).
   De header erboven (.zg-hdr--hero, zie "Header" hierboven) staat
   position:absolute zodat hij over deze sectie heen ligt; de padding-top
   van .zg-site-hero__inner houdt daar rekening mee. */
.zg-site-hero {
  position: relative;
  width: 100vw;
  margin-left: calc(50% - 50vw);
  background-size: cover;
  background-position: right center;
  background-color: var(--espresso-900);
}

.zg-site-hero__overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    90deg,
    rgb(30 24 17 / 82%) 0%,
    rgb(30 24 17 / 55%) 38%,
    rgb(30 24 17 / 12%) 62%,
    rgb(30 24 17 / 0%) 100%
  );
}

.zg-site-hero__inner {
  /* content-box: max-width telt hier de CONTENTbreedte, net als
     .zg-hdr__bar en .zg-footer__inner. Zonder deze regel erven de inners
     die als wp:group in een template staan WP's border-box, waardoor hun
     inhoud 24px verder naar binnen valt dan de rest van de pagina. */
  box-sizing: content-box;
  position: relative;
  max-width: 1280px;
  margin: 0 auto;

  /* De 92px hoge .zg-hdr--hero ligt absoluut over deze sectie heen, dus de
     eerste ~92px van de padding-top is geen lucht maar headerruimte. Met de
     oude 96px stond de H1 vier pixels onder het menu geplakt. Op een groot
     scherm is er ruimte zat, dus daar mag de hero hoger en krijgt de kop
     boven én onder echt lucht; onder 1080px stapt hij terug (zie hieronder)
     zodat de hero op een laptop niet de hele viewport opeet. */
  padding: 168px 24px 152px;
}

.zg-site-hero__content {
  max-width: 560px;
}

.zg-site-hero__title {
  max-width: 15ch;
  margin: 0 0 26px;
  color: var(--paper-50);
  font-size: 3.4rem;
}

.zg-site-hero__lead {
  max-width: 46ch;
  margin: 0;
  color: var(--paper-100);
  font-size: var(--fs-lead);
  opacity: 0.9;
}

.zg-site-hero__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 28px;
  margin: 26px 0 0;
}

.zg-site-hero__link {
  color: var(--paper-50);
  font-family: var(--font-body);
  font-weight: var(--fw-regular);
  font-size: var(--fs-lead);
  text-decoration-line: underline;
  text-underline-offset: 4px;
}

@media (width <= 1080px) {
  .zg-site-hero__inner {
    padding: 128px 24px 112px;
  }

  .zg-site-hero__title {
    margin-bottom: 20px;
  }
}

@media (width <= 640px) {
  .zg-site-hero__inner {
    /* Horizontale gutter 24px, gelijk aan de header en de <main> van elke
       template; alleen de verticale waarden zijn hier hero-specifiek. */
    padding: 116px 24px 60px;
  }

  .zg-site-hero__title {
    margin-top: 0;
    font-size: 2.3rem;
  }
}

/* ── Instap-kaarten ──────────────────────────────────────────────────────
   Drie instap-kaarten van `zetgoed/instap-cards` (editor-content block,
   zie src/blocks/instap-cards/render.php). 1-op-1 geport uit de "Drie
   instap-kaarten"-sectie van Homepage.dc.html naar BEM + tokens. Het beeld
   overlapt de kaart-rand met een negatieve margin (`-44px`, bron-letterlijk)
   — vandaar dat de kaart zelf geen top-padding heeft.
   Op mobiel (`<= 640px`) verbergt de bron de omschrijving en de checklist
   (`.zg-beginner-desc`/`.zg-beginner-list` in de bron → hier `__text`/
   `__checklist`) om de kaart compact te houden — een bewuste ontwerpkeuze
   uit de bron, geen WP-injectie-workaround (zie docs/conventions/css.md
   §4). */
.zg-instap {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  align-items: stretch;
  gap: var(--space-8);
  padding: calc(var(--section-y) + 44px) 0 var(--space-6);
}

.zg-instap__card {
  display: flex;
  flex-direction: column;
  padding: 0 var(--space-8) var(--space-8);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
}

.zg-instap__image {
  display: block;
  width: 100%;
  height: 210px;
  margin: -44px 0 0;
  object-fit: cover;
  border-radius: var(--radius-md);
}

.zg-instap__title {
  margin: 28px 0 14px;
  color: var(--text-heading);
  font-family: var(--font-display);
  font-size: 1.6rem;
  line-height: 1.15;
  font-variation-settings: var(--display-vsettings);
}

.zg-instap__text {
  margin: 0 0 14px;
  color: var(--text-muted);
}

.zg-instap__checklist {
  display: flex;
  flex-direction: column;
  margin: 0 0 var(--space-4);
  padding: 0;
  list-style: none;
}

.zg-instap__checklist-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 2px 0;
  color: var(--text-muted);
  line-height: 1.25;
}

.zg-instap__checklist-icon {
  flex: none;
  color: var(--accent);
}

.zg-instap__price {
  margin: auto 0 10px;
  color: var(--text-subtle);
}

.zg-instap__cta {
  align-self: flex-start;
}

@media (width <= 960px) {
  .zg-instap {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (width <= 640px) {
  .zg-instap {
    gap: 14px;
  }

  .zg-instap__card {
    padding: 0 14px 18px;
  }

  .zg-instap__image {
    height: 130px;
    margin-top: -16px;
  }

  .zg-instap__title {
    margin: 16px 0 8px;
    font-size: 17px;
  }

  .zg-instap__text,
  .zg-instap__checklist {
    display: none;
  }

  .zg-instap__cta {
    padding-left: 0.9em;
    padding-right: 0.9em;
    font-size: 0.85rem;
    text-align: center;
    white-space: normal;
  }
}

/* ── USP-strip ───────────────────────────────────────────────────────────
   Horizontale USP-strook van `zetgoed/usp-strip` (editor-content block,
   zie src/blocks/usp-strip/render.php). 1-op-1 geport uit de "USP-strip"-
   sectie van Homepage.dc.html naar BEM + tokens. Op mobiel/tablet (`<= 860px`)
   verbergt de bron de verticale scheidingslijnen en legt de items in een
   rij náást elkaar i.p.v. gespreid over de volle breedte — een bewuste
   ontwerpkeuze uit de bron, geen WP-injectie-workaround (zie
   docs/conventions/css.md §4). */
.zg-usp {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 36px 0 0;
}

.zg-usp__item {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  padding: 0 56px;
}

.zg-usp__icon {
  flex: none;
  color: var(--text-heading);
}

.zg-usp__text {
  margin: 0;
  color: var(--text-heading);
  font-size: var(--fs-lead);
  line-height: 1.35;
  white-space: nowrap;
}

.zg-usp__divider {
  flex: none;
  width: 1px;
  height: 56px;
  background: var(--border-control);
}

@media (width <= 860px) {
  .zg-usp {
    align-items: flex-start;
    gap: var(--space-2);
  }

  .zg-usp__divider {
    display: none;
  }

  .zg-usp__item {
    flex: 1 1 0;
    flex-direction: column;
    justify-content: flex-start;
    gap: var(--space-2);
    padding: 0 4px;
    text-align: center;
  }

  .zg-usp__icon {
    width: 24px;
    height: 24px;
  }

  .zg-usp__text {
    font-size: 0.82rem;
    line-height: 1.25;
    white-space: normal;
  }
}

/* ── Gids-grid ────────────────────────────────────────────────────────────
   4 gids-kaarten van `zetgoed/gids-grid` (editor-content block, `cards`-
   attribute beheerd in edit.js). 1-op-1 geport uit de "Waar wil je mee
   beginnen?"-sectie van Homepage.dc.html naar BEM + tokens — inclusief de
   categorie-FOTO per kaart (`.zg-gids-grid__image`, ex-icoon: zie de
   docblock in render.php voor de DS-drift-fix die 'm rechtzette). De
   hover-lift komt van de gedeelde `.zg-card-link`-primitief (Button-sectie
   hierboven), niet van een eigen regel.

   `.zg-gids-grid__section` (Fase 6, Task 8): de buitenste `<section>` van
   de `query`-tak (render.php) — "Andere koopgidsen" op
   `single-zg_koopgids.html`, eyebrow + kop + de kale `.zg-gids-grid`
   hieronder. Zelfde patroon als `.zg-merk-grid`/`.zg-verder-lezen`
   (plain section zonder eigen achtergrond, direct vóór de footer): alleen
   een top-`padding`, geen bottom — de footer-template-part zorgt zelf voor
   visuele scheiding. */
.zg-gids-grid__section {
  padding: var(--section-y) 0 0;
}

.zg-gids-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-8);
}

.zg-gids-grid__card {
  display: flex;
  flex-direction: column;
  padding: var(--space-6);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  text-decoration: none;
}

.zg-gids-grid__image {
  display: block;
  width: 100%;
  height: 200px;
  margin-bottom: var(--space-5);
  background: var(--paper-100);
  border-radius: var(--radius-lg);
  object-fit: cover;
}

.zg-gids-grid__title {
  margin: 0 0 var(--space-2);
  color: var(--text-heading);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-h3);
  overflow-wrap: break-word;
}

.zg-gids-grid__text {
  margin: 0 0 var(--space-5);
  color: var(--text-muted);
}

.zg-gids-grid__link {
  margin-top: auto;
  align-self: flex-start;
  color: var(--accent);
  font-weight: var(--fw-semibold);
}

/* Twee kaarten naast elkaar op /koopgidsen/ (render.php, `source:"archive"`).
   Daar is de gids het onderwerp van de pagina en niet een kaartje in een rij
   van vier, dus krijgt hij de halve breedte. Het beeld gaat mee omhoog: op
   ~620px kaartbreedte wordt een strook van 200px een reepje. */
.zg-gids-grid--cols-2 {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.zg-gids-grid--cols-2 .zg-gids-grid__image {
  height: 280px;
}

@media (width <= 960px) {
  .zg-gids-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (width <= 640px) {
  .zg-gids-grid,
  .zg-gids-grid--cols-2 {
    grid-template-columns: 1fr;
  }

  .zg-gids-grid--cols-2 .zg-gids-grid__image {
    height: 200px;
  }
}

/* ── Review-grid ──────────────────────────────────────────────────────────
   "Veelgelezen reviews" van `zetgoed/review-grid` (data-gebonden block:
   een eigen `WP_Query` naar de 4 hoogst-scorende `zg_machine`-posts, zie
   src/blocks/review-grid/render.php). 1-op-1 geport uit de "Veelgelezen
   reviews"-sectie van Homepage.dc.html naar BEM + tokens, inclusief de
   carousel-pijlen (`.zg-rev-arrows`/`.zg-arrow`, gedreven door
   assets/js/review-carousel.js). De kaarten zelf hergebruiken
   `.zg-related-reviews__card` 1-op-1 (gerenderd via de gedeelde
   `zetgoed_render_review_card()`-helper, inc/review-card.php) maar staan
   nu in `.zg-review-grid__track` (horizontale scroll-snap-track, ook
   `id="zg-reviews-track"` als JS-hook — zie render.php) i.p.v. het
   `.zg-related-reviews__grid--cols-4`-grid — alleen de sectie-kop, intro,
   pijlen en CTA zijn hier eigen.
   Kleuren: de DS-bron gebruikt kale hex voor `.zg-arrow` (ink `#4b3e3b`,
   paper `#faf6f0`, koper-hover `#b06a3a`) — hier gemapt naar
   `--text-heading`/`--paper-50`/`--accent`. De koper-hover is bewust
   vervangen door `--accent` (forest): op de homepage is `--accent` al
   forest (zie tokens.css), en dat blijft consistent met de overige
   homepage-accentkleur i.p.v. een eenmalige koper-uitzondering. */
.zg-review-grid {
  position: relative;
  padding: var(--section-y) 0 0;
}

.zg-review-grid__heading {
  margin: 0 0 var(--space-5);
  text-align: center;
  font-size: var(--fs-h1);
}

.zg-review-grid__intro {
  max-width: 60ch;
  margin: 0 auto var(--space-12);
  color: var(--text-muted);
  font-size: 1.25rem;
  text-align: center;
}

.zg-rev-arrows {
  position: absolute;
  top: var(--section-y);
  right: 0;
  display: flex;
  gap: var(--space-3);
}

.zg-arrow {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border: 1.5px solid var(--text-heading);
  border-radius: var(--radius-pill);
  background: var(--paper-50);
  color: var(--text-heading);
  cursor: pointer;
  transition:
    background 0.16s ease,
    color 0.16s ease,
    border-color 0.16s ease,
    opacity 0.16s ease;
}

.zg-arrow:disabled {
  cursor: default;
  opacity: 0.35;
}

.zg-arrow:hover:not(:disabled) {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--paper-50);
}

.zg-review-grid__track {
  display: flex;
  gap: var(--space-5);
  padding: 8px 4px 20px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
}

.zg-review-grid__track > * {
  flex: 0 0 300px;
  scroll-snap-align: start;
}

.zg-review-grid__cta {
  display: flex;
  justify-content: center;
  margin-top: 36px; /* bron-letterlijk (Homepage.dc.html `.zg-btn-accent-wrap`), geen token op-de-grid */
}

@media (width <= 640px) {
  .zg-rev-arrows {
    display: none;
  }

  .zg-review-grid__track > * {
    flex: 0 0 200px;
  }
}

/* ── Merk-pills ───────────────────────────────────────────────────────────
   "Per merk"-pills van `zetgoed/merk-pills` (data-gebonden block: een
   eigen `get_terms()`-query op de `zg_merk`-taxonomie, zie
   src/blocks/merk-pills/render.php). De bron (Homepage.dc.html,
   "Per merk"-sectie) toont logo-tegels (`.zg-brand`); dit block toont
   tekst-pills zodat elke geregistreerde merk-term automatisch verschijnt
   zonder een los logo-asset per merk — zie de toelichting in render.php.
   Pill-vorm/hover lenen de tokens van de bron z'n `.zg-brand`-regel
   (`border-radius:var(--radius-pill)`, hover naar `--accent`/
   `--forest-100`), niet de tegel-vorm zelf. */
.zg-merk-pills {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

.zg-merk-pills__pill {
  display: inline-flex;
  align-items: center;
  padding: 0.5em 1.25em;
  background: var(--surface-raised);
  border: 1.5px solid var(--border-control);
  border-radius: var(--radius-pill);
  color: var(--text-heading);
  font-weight: var(--fw-semibold);
  text-decoration: none;
  transition:
    background 0.16s ease,
    border-color 0.16s ease,
    color 0.16s ease;
}

.zg-merk-pills__pill:hover {
  background: var(--forest-100);
  border-color: var(--accent);
  color: var(--accent-press);
}

/* ── Merk-logogrid ────────────────────────────────────────────────────────
   "Per merk"-logo-tegelgrid van `zetgoed/merk-logogrid` (data-gebonden
   block: eigen `get_terms()`-query op `zg_merk`, gefilterd/gesorteerd op
   `zg_merk_volgorde` — zelfde curatie als `zetgoed/merkoverzicht-grid`,
   zie src/blocks/merk-logogrid/render.php). Vervangt `.zg-merk-pills`
   hierboven op de homepage; 1-op-1 geport uit `.zg-brand-grid`/`.zg-brand`
   (Homepage.dc.html, "Per merk"-sectie), hernoemd naar deze block-
   namespace. `--space-4`/`--space-5` = 16/20px (tokens.css), zoals de
   DS-bron. */
.zg-merk-logogrid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: var(--space-4);
}

.zg-merk-logogrid__tile {
  height: 96px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-5);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  text-decoration: none;
  transition:
    transform 0.16s ease,
    border-color 0.16s ease,
    background 0.16s ease;
}

.zg-merk-logogrid__tile:hover {
  border-color: var(--accent);
  background: var(--forest-100);
}

.zg-merk-logogrid__tile img {
  max-height: 26px;
  max-width: 100%;
  width: auto;
  object-fit: contain;
}

@media (width <= 900px) {
  .zg-merk-logogrid {
    grid-template-columns: repeat(3, 1fr);
  }
}

@media (width <= 560px) {
  .zg-merk-logogrid {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* ── Lees-verder ──────────────────────────────────────────────────────────
   "Lees verder"-blogsectie van `zetgoed/lees-verder` (data-gebonden block:
   eigen `WP_Query` naar de 3 nieuwste posts, zie
   src/blocks/lees-verder/render.php). 1-op-1 geport uit de "Lees verder"-
   sectie van Homepage.dc.html naar BEM + tokens — een CONSTRAINED sectie
   (geen full-bleed band, i.t.t. `.zg-overons-gerelateerd` dat dezelfde
   soort kop+CTA+3-koloms-grid-vorm heeft maar wél full-bleed is, zie de
   docblock in src/blocks/overons-gerelateerd/render.php). De kaarten zelf
   hergebruiken `.zg-bc.zg-bc--blog` (gerenderd via
   `zetgoed_render_blog_card()`, inc/blog.php) — dit blok stijlt alleen de
   kop-rij en de grid-wrapper eromheen. @640: geen 1-koloms-stack maar een
   horizontale swipe-carousel (scroll-snap), zelfde mobiele patroon als
   `#zg-reviews-track` (zie "Review-grid"/carousel hieronder).
   Alleen TOP-spacing: dit blok is altijd de slotsectie van de homepage en de
   onderruimte naar de footer komt van het pagina-onderritme op `main` (zie
   "Pagina-onderritme" bij de footer). Zou dit blok zijn eigen
   `padding-bottom` houden, dan telde die er dubbel bovenop. */
.zg-lees-verder {
  padding: var(--section-y) 0 0;
}

.zg-lees-verder__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  margin-bottom: var(--space-8);
}

.zg-lees-verder__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-8);
}

@media (width <= 960px) {
  .zg-lees-verder__grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (width <= 640px) {
  .zg-lees-verder__grid {
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    gap: var(--space-4);
    padding-bottom: var(--space-2);
  }

  .zg-lees-verder__grid > * {
    flex: 0 0 240px;
    scroll-snap-align: start;
  }
}

/* ── Front-page compositie ────────────────────────────────────────────────
   Kleine, blocknaam-onafhankelijke utilities voor de front-page-
   compositie (wordpress/scripts/seed-homepage.php): sommige secties van
   Homepage.dc.html hebben een sectiekop/-intro/full-bleed-band die NIET bij
   een los block hoort (bv. "Waar wil je mee beginnen?" boven
   `zetgoed/gids-grid`, "Per merk" boven `zetgoed/merk-pills` — zie de
   docblocks in die render.php's: de kop komt bewust als losse
   core/heading in de compositie). `.zg-fp-band` is 1-op-1 dezelfde
   full-bleed-truc als `.zg-score-bars`/`.zg-site-hero` hierboven
   (`width:100vw;margin-left:calc(50% - 50vw)`), maar hier als generieke
   utility omdat 2 verschillende secties ("Waar wil je mee beginnen?" en
   "Onderhoud & bonen") 'm nodig hebben zonder aan een specifiek block
   gebonden te zijn. `.zg-fp-heading`/`.zg-fp-intro` zijn dezelfde
   gecentreerde kop/intro-stijl als `.zg-review-grid__heading`/`__intro`
   hierboven, ditmaal generiek toepasbaar op een los core/heading +
   core/paragraph-paar. */
.zg-fp-band {
  width: 100vw;
  margin-top: var(--section-y);
  margin-left: calc(50% - 50vw);
  padding: var(--section-y) 0;
  background: var(--surface-raised);
}

.zg-fp-band__inner {
  /* content-box: max-width telt hier de CONTENTbreedte, net als
     .zg-hdr__bar en .zg-footer__inner. Zonder deze regel erven de inners
     die als wp:group in een template staan WP's border-box, waardoor hun
     inhoud 24px verder naar binnen valt dan de rest van de pagina. */
  box-sizing: content-box;
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 var(--space-6);
}

.zg-fp-heading {
  margin: 0 0 var(--space-5);
  text-align: center;
  font-size: var(--fs-h1);
}

.zg-fp-intro {
  max-width: 60ch;
  margin: 0 auto 56px;
  color: var(--text-muted);
  font-size: 1.25rem;
  text-align: center;
}

/* ── Homepage: uniform sectie-ritme ───────────────────────────────────────
   De front-page-compositie mixt losse blocks die élk hun eigen verticale
   ritme meebrengen: full-bleed banden (`.zg-fp-band`) met ZOWEL een
   `margin-top: var(--section-y)` ALS een interne `padding: var(--section-y) 0`,
   plus buur-secties die daar bovenop nóg een eigen `padding-top` zetten. Dat
   stapelt op tot 2–3× het ritme rond elke gekleurde band (gemeten: 190–320px
   tegenover ~100px tussen gewone secties). Hier neutraliseren we die
   stapeling zodat elke sectie-overgang één ritme-eenheid is: de band ligt
   flush tegen zijn buren en zijn eigen interne padding ÍS de tussenruimte.
   Bewust `.home`-scoped: dezelfde blocks staan op andere templates en houden
   daar hun eigen ritme. `.home` (zonder `body`) houdt de specificiteit op
   0,3,0. */
/* stylelint-disable-next-line selector-class-pattern -- WP-core vendorclasses (.home/.wp-block-post-content); geen zg-namespace beschikbaar. */
.home .wp-block-post-content > .zg-fp-band {
  margin-top: 0;
}

/* De sectie direct ná een gekleurde band/over-blok voegt geen eigen
   top-ritme meer toe — de `padding-bottom` van die band (gekleurd) is al de
   tussenruimte. `.zg-review-byline` is óók zo'n gekleurd over-blok, dus die
   telt mee als bandgrens. */
/* stylelint-disable-next-line selector-class-pattern -- WP-core vendorclasses (.home/.wp-block-post-content); geen zg-namespace beschikbaar. */
.home .wp-block-post-content > :is(.zg-fp-band, .zg-review-byline) + * {
  margin-top: 0;
  padding-top: 0;
}

/* Homepage: mobiele type-schaal (DS Homepage.dc.html .zg-root @640).
   body.home houdt dit homepage-only; blocks met een eigen expliciete
   @640-font-size winnen op specificiteit → geen dubbele verkleining. */
@media (width <= 640px) {
  /* stylelint-disable-next-line selector-class-pattern -- WP-core `.home`-vendor-classname (automatische voorpagina-class op <body>). */
  body.home {
    --fs-display: 2.6rem;
    --fs-h1: 2.1rem;
    --fs-h2: 1.7rem;
    --fs-h3: 1.35rem;
  }
}

/* ── Media-panel ──────────────────────────────────────────────────────────
   Herbruikbaar beeld+tekst+CTA-paneel van `zetgoed/media-panel` (editor-
   content, alle inhoud uit attributen — zelfde patroon als
   `zetgoed/cta-band`/`zetgoed/site-hero`). 1-op-1 geport uit de "Onderhoud
   & bonen"-sectie van Homepage.dc.html naar BEM + tokens. Dezelfde
   split-layout draagt in de bron ook de "Molen-panel"-sectie, maar die
   homepage-instantie hergebruikt bewust het bestaande `zetgoed/cta-band`-
   block i.p.v. dit block (zie seed-homepage.php) — dit block is dus vooral
   voor "Onderhoud & bonen" gebouwd, met `imagePosition`/`background` zodat
   het ook elders (ander beeld links/rechts, sand i.p.v. wit paneel)
   herbruikt kan worden zonder een tweede, bijna-identiek block. */
.zg-media-panel {
  padding: var(--space-16);
  background: var(--surface-raised);
  border-radius: var(--radius-xl);
}

.zg-media-panel--paper {
  background: var(--paper-200);
}

.zg-media-panel__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;

  /* `stretch` (default): de foto rekt mee met de hoogte van de tekstkolom
     i.p.v. andersom. Zo staat de kop bovenaan (ritme strookt met de overige
     secties) zónder dat een hoge foto óf de kop omlaag duwt (centreren) óf
     een leeg vlak onder de tekst achterlaat (bovenaan uitlijnen). Zie de
     `height:0; min-height:100%`-truc op `.zg-media-panel__image`. */
  align-items: stretch;
  gap: 56px;
}

.zg-media-panel__heading {
  margin: 0 0 var(--space-5);
  font-size: var(--fs-h1);
  line-height: 1.08;
}

.zg-media-panel__text {
  max-width: 44ch;
  margin: 0 0 var(--space-8);
  color: var(--text-subtle);
  font-size: 1.25rem;
}

.zg-media-panel__image {
  display: block;
  width: 100%;

  /* `height:0; min-height:100%`: de foto claimt geen eigen intrinsieke hoogte
     (anders dicteert een vierkante 548px-foto de panelhoogte en ontstaat er
     een leeg vlak naast de kortere tekstkolom), maar rekt via het grid mee
     tot de hoogte van de tekstkolom. object-fit dekt de bijsnede af. Op
     mobiel (≤960, gestapeld) krijgt de foto weer een eigen hoogte. */
  height: 0;
  min-height: 100%;
  object-fit: cover;
  border-radius: var(--radius-lg);
}

.zg-media-panel__image--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 320px;
  background: var(--sand-300);
  color: var(--text-subtle);
  font-size: var(--fs-sm);
}

/* `imagePosition:"links"` wisselt de kolommen via `order` i.p.v. de
   DOM-volgorde te veranderen — zelfde reden als de `--grid--no-image`-
   toevoeging bij CTA-band: één markup-vorm, minder vertakking in
   render.php. Ná de basisregels hierboven (`no-descending-specificity`:
   deze compound selectors moeten later staan dan de kale classes die ze
   overrulen). */
.zg-media-panel--reverse .zg-media-panel__content {
  order: 2;
}

.zg-media-panel--reverse .zg-media-panel__image {
  order: 1;
}

@media (width <= 960px) {
  .zg-media-panel {
    padding: 40px;
  }

  .zg-media-panel__grid {
    grid-template-columns: 1fr;
    gap: 40px;
  }

  .zg-media-panel__image {
    height: auto;
    min-height: 280px;
  }

  .zg-media-panel--reverse .zg-media-panel__content,
  .zg-media-panel--reverse .zg-media-panel__image {
    order: 0;
  }
}

@media (width <= 640px) {
  .zg-media-panel {
    padding: 26px;
  }
}

/* ── Over-blok ────────────────────────────────────────────────────────────
   Homepage-variant van "Over Zetgoed" (`zetgoed/over-blok`, herbruikbaar,
   alle inhoud uit attributen — zie render.php). Geen eigen `.zg-over-blok__
   *`-classes voor de `card`-variant (default): de layout (stappenkolom,
   portret, contentkolom) is 1-op-1 identiek aan `zetgoed/review-byline`
   (beide zijn 1-op-1 poorten van dezelfde drieluik-sectie, resp. uit
   Homepage.dc.html en ProductReview.dc.html), dus dit block hergebruikt de
   volledige `.zg-review-byline__*`-set hierboven — zie de "Review byline"-
   sectie verderop in dit bestand. De render.php zet `.zg-over-blok` als
   extra hook-class op de wrapper (naast `.zg-review-byline`) voor het geval
   een toekomstige homepage-only-afwijking nodig is; die class heeft bewust
   nog geen eigen regel hier (leeg CSS-block is niet toegestaan door
   stylelint) — pas toevoegen zodra er echt een verschil nodig is.

   `band`-variant (Fase 5, Task 9): de merkhub's "Over Zetgoed" is géén
   kaart maar een full-bleed sand-band, 1-op-1 geport uit de "Over Zetgoed"-
   sectie van Merkhub.dc.html — dezelfde drieluik-grid-inhoud als hierboven,
   alleen de sectie-chrome verschilt (`background:var(--sand-100);
   width:100vw;margin-left:calc(50% - 50vw);padding:var(--section-y) 0` om
   de bron, met daarbinnen een transparante 1200px-grid, `.zg-over` in de
   bron).

   `.zg-review-byline` combineert in de `card`-variant zélf de grid ÉN de
   kaart-chrome (padding/achtergrond/radius) in één element — dat element
   kan niet zomaar `width:100vw` krijgen: dit theme heeft geen globale
   `box-sizing:border-box`-reset (geverifieerd live: WP-kern zet die alleen
   op `.wp-block-group`, zie global-styles-inline-css). `width:100vw` +
   eigen horizontale padding op hetzelfde element telt in `content-box` op
   tot méér dan 100vw en veroorzaakt horizontale overflow — precies het
   #1-overflow-risico dat deze band moet vermijden. Vandaar, net als
   `.zg-merk-lineup`/`.zg-merk-lineup__inner` hierboven (Task 6): render.php
   zet voor `band` een extra binnenwrapper (`.zg-over-blok__grid`,
   hergebruikt `.zg-review-byline` voor de grid-eigenschappen) binnen de
   full-bleed buitenste `<section class="zg-over-blok--band alignfull">` —
   de buitenste draagt alléén verticale padding (geen horizontale, dus geen
   overflow-risico van de `width:100vw`-truc), de binnenste centreert
   zichzelf op max-width 1200px + horizontale padding, exact zoals de bron
   z'n `.zg-over`. Zie ook de docblock in render.php voor de volledige
   afweging en de `alignfull`-noodzaak (zelfde `is-layout-constrained`-
   probleem als `.zg-merk-lineup`). */
.zg-over-blok--band {
  width: 100vw;
  margin: var(--section-y) calc(50% - 50vw) 0;
  padding: var(--section-y) 0;
  background: var(--sand-100);
}

.zg-over-blok__grid {
  /* content-box: max-width telt hier de CONTENTbreedte, net als
     .zg-hdr__bar en .zg-footer__inner. Zonder deze regel erven de inners
     die als wp:group in een template staan WP's border-box, waardoor hun
     inhoud 24px verder naar binnen valt dan de rest van de pagina. */
  box-sizing: content-box;
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 var(--space-6);
  background: none;
  border-radius: 0;
}


/* ── Review archive (filter) ───────────────────────────────────────────────
   Facet-filter-sidebar + filterbaar reviewkaart-grid van
   `zetgoed/review-archive` (data-gebonden block: een eigen `WP_Query` naar
   alle `zg_machine`-reviews + `get_terms()` voor de facet-chips, zie
   src/blocks/review-archive/render.php). 1-op-1 geport uit de "Filters +
   resultaten"-sectie van ReviewOverzicht.dc.html naar BEM + tokens; het
   clientside filtergedrag zelf zit in assets/js/archive-filter.js
   (poort van de `data-dc-script`-`renderVals()`/`match()`-logica). De hero/
   breadcrumb/keuzehulp-band/FAQ uit hetzelfde brondocument horen niet bij
   dit block — die landen in Fase 4b Task 2 (archive-template) als losse
   secties eromheen.

   De bron gebruikt in zijn mobiele media-query `!important` op een aantal
   van deze regels (o.a. `.zg-rc-body`/`.zg-rc-title`/`.zg-resbar`) om
   desktop-declaraties op dezelfde classes te overschrijven — hier bewust
   weggelaten (docs/conventions/css.md §4 verbiedt `!important`): de
   mobiele overrides staan gewoon ná de basisregels, in dezelfde
   bronvolgorde-aanpak als `.zg-instap`/`.zg-related-reviews__grid`
   hierboven, dus latere regel wint zonder dat `!important` nodig is.

   Terracotta (`--terracotta-600`/`--terracotta-700`) is hier het accent
   (categorie-eyebrow, "Lees review →", "Wis alles") i.p.v. het
   gebruikelijke `--accent` (forest) — 1-op-1 met de bron, die dit
   reviewoverzicht bewust een ander accent geeft dan de rest van de site.

   Classnamen die letterlijk uit ReviewOverzicht.dc.html komen (traceerbaar
   naar de bron, zelfde patroon als `.zg-srow`/`.zg-faq-ico` hierboven):
   `.zg-side`, `.zg-side-head`, `.zg-fgroup`, `.zg-fgroup-title`,
   `.zg-fchips`, `.zg-fchip`, `.zg-wis`, `.zg-filter-toggle`, `.zg-ft-ico`,
   `.zg-ft-count`, `.zg-layout`, `.zg-resbar`, `.zg-grid`, `.zg-rc`,
   `.zg-rc-media`, `.zg-foto`, `.zg-rc-badge`, `.zg-rc-body`, `.zg-rc-title`,
   `.zg-rc-stars`, `.zg-star`, `.zg-score`, `.zg-rc-desc`, `.zg-rc-foot`,
   `.zg-rc-price`, `.zg-rc-cta`, `.zg-page`. De categorie-eyebrow gebruikt
   bewust het gedeelde `.eyebrow`-primitief (+ nieuwe `.eyebrow--terracotta`-
   modifier, zie base.css) i.p.v. de bron-literal `.zg-eyebrow-terra` te
   herbouwen. `.zg-review-archive__results`/`.zg-side-head__title`/
   `.zg-resbar__*`/`.zg-empty*`/`.zg-pagination` zijn eigen BEM-toevoegingen
   voor structuur die de bron via inline `style=""` regelde. */

/* Mobiele filter-schakelaar: standaard verborgen, verschijnt pas onder de
   820px-breakpoint hieronder (samen met het inklappen van .zg-side). */
.zg-filter-toggle {
  display: none;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  width: 100%;
  font-family: var(--font-body);
  font-size: var(--fs-body);
  font-weight: var(--fw-semibold);
  color: var(--text-heading);
  background: var(--surface-raised);
  border: 1.5px solid var(--border-control);
  border-radius: var(--radius-md);
  padding: 15px 18px;
  cursor: pointer;
  margin-bottom: var(--space-2);
}

.zg-filter-toggle__label {
  margin-right: auto;
}

.zg-ft-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  height: 24px;
  padding: 0 var(--space-2);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  color: var(--paper-50);
  background: var(--espresso-900);
  border-radius: var(--radius-pill);
}

/* Verberg het teller-badge wanneer er geen actieve filters zijn (JS zet [hidden]);
   nodig omdat de expliciete display: inline-flex anders het [hidden]-default verslaat. */
.zg-ft-count[hidden] {
  display: none;
}

.zg-ft-ico {
  flex: none;
  transition: transform 0.22s ease;
}

.zg-filter-toggle[aria-expanded="true"] .zg-ft-ico {
  transform: rotate(180deg);
}

/* Layout: 264px-sidebar + resultatenkolom */
.zg-layout {
  display: grid;
  grid-template-columns: 264px minmax(0, 1fr);
  align-items: start;
  gap: var(--space-12);
  margin-top: 36px;
  padding-top: 36px;
  border-top: 1px solid var(--border);
}

.zg-side {
  position: sticky;
  top: var(--space-8);
}

.zg-side-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  margin-bottom: 22px;
}

.zg-side-head__title {
  margin: 0;
  font-size: var(--fs-h3);
}

/* `min-height` + verticale padding brengen het aanraakvlak op 44px; met
   `padding: 0` mat de knop 47×21px, ruim onder de norm voor een tap-target.
   De negatieve verticale marge houdt het optische ritme in `.zg-side-head`
   gelijk: het vlak groeit naar buiten, de tekst blijft op z'n oude baseline
   liggen naast `.zg-side-head__title`. */
.zg-wis {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  margin-block: -11px;
  padding: 0;
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  color: var(--terracotta-600);
  background: none;
  border: none;
  cursor: pointer;
}

/* `display:inline-flex` hierboven verslaat de browser-default
   `[hidden]{display:none}` op gelijke specificiteit, dus zonder deze regel
   stond "Wis alles" er altijd, ook bij het laden zonder actieve filters. Dat
   is precies wat commit 51b87ec wilde voorkomen. Zelfde patroon als elders
   in dit bestand voor blocks met een expliciete `display` (zie
   docs/conventions/css.md). */
.zg-wis[hidden] {
  display: none;
}

.zg-wis:hover {
  color: var(--terracotta-700);
}

.zg-fgroup {
  padding: var(--space-6) 0;
  border-top: 1px solid var(--border);
}

.zg-fgroup:first-of-type {
  padding-top: 0;
  border-top: none;
}

.zg-fgroup-title {
  margin: 0 0 14px;
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--text-muted);
}

.zg-fchips {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

/* `padding-block: 10px` (was 9px) tilt de chip van 42 naar 44px: 14px/1.6 =
   22px tekst + 2×10px + 2×1,5px rand. Twee pixels, maar het is precies het
   verschil tussen wel en niet voldoen aan de tap-targetnorm — en deze chips
   staan op mobiel in een dichte rij naast elkaar. */
.zg-fchip {
  padding: 10px var(--space-4);
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  color: var(--text-subtle);
  background: var(--surface-raised);
  border: 1.5px solid var(--border-control);
  border-radius: var(--radius-pill);
  cursor: pointer;
  transition:
    background 0.14s ease,
    color 0.14s ease,
    border-color 0.14s ease;
}

.zg-fchip:hover {
  border-color: var(--espresso-700);
  color: var(--text-heading);
}

/* Een facetgroep kan uit LINK-chips bestaan in plaats van schakelaars: de
   niveaugroep op /reviews/niveau/{term}/ navigeert tussen de drie
   niveaupagina's (zie inc/archive-filter.php). Zelfde pil, alleen moet de
   `<a>` z'n onderstreping en linkkleur kwijt. */
a.zg-fchip {
  display: inline-block;
  color: var(--text-subtle);
  text-decoration: none;
}

/* Actieve chip: `aria-pressed` bij een schakelaar, `aria-current` bij een
   navigatie-chip. Eén regel voor allebei, zodat de actieve staat er overal
   hetzelfde uitziet. */
.zg-fchip[aria-pressed="true"],
a.zg-fchip[aria-current="page"] {
  background: var(--espresso-900);
  border-color: var(--espresso-900);
  color: var(--paper-50);
}

/* Resultaatbalk */
.zg-resbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-4);
  padding: 0 0 28px;
}

.zg-resbar__text {
  margin: 0;
  font-size: var(--fs-body);
  color: var(--text-subtle);
}

.zg-resbar__sort {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--fs-body);
  color: var(--text-subtle);
}

.zg-resbar__sort-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
}

.zg-resbar__select {
  padding: 9px 44px 9px var(--space-4);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  font-weight: var(--fw-semibold);
  color: var(--text-heading);
  background: var(--surface-raised);
  appearance: none;
  border: 1.5px solid var(--border-control);
  border-radius: var(--radius-pill);
  cursor: pointer;
}

.zg-resbar__select-ico {
  position: absolute;
  right: var(--space-4);
  color: var(--text-subtle);
  pointer-events: none;
}

/* Grid + kaart. `.zg-grid`/`.zg-rc` zetten allebei een expliciete `display`
   (grid/flex) — zonder de `[hidden]`-regels hieronder wint die auteur-regel
   van de browser z'n ingebouwde `[hidden]{display:none}`-standaardregel bij
   gelijke specificiteit, puur omdat onze stylesheet ná de UA-stylesheet
   laadt. assets/js/archive-filter.js zet `.hidden` op individuele
   kaarten (filter) én op de hele grid (lege-staat), dus beide hebben hun
   eigen `[hidden]`-override nodig — geen `!important`, gewoon een iets
   specifiekere selector (class+attribuut) die wél wint. */
.zg-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-6);
}

.zg-grid[hidden] {
  display: none;
}

.zg-rc {
  display: flex;
  flex-direction: column;
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  text-decoration: none;
  transition:
    transform 0.16s ease,
    box-shadow 0.16s ease,
    border-color 0.16s ease;
}

.zg-rc[hidden] {
  display: none;
}

.zg-rc:hover {
  text-decoration: none;
  transform: translateY(-3px);
  box-shadow: var(--shadow-md);
  border-color: var(--border-control);
}

.zg-rc-media {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--sand-300);
  aspect-ratio: 4 / 3;
}

.zg-rc-media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.zg-foto {
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  color: var(--sand-400);
}

.zg-rc-badge {
  position: absolute;
  top: var(--space-4);
  left: var(--space-4);
}

.zg-rc-body {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: 26px 26px 28px;
}

.zg-rc-title {
  margin: var(--space-2) 0 10px;
  font-family: var(--font-display);
  font-size: var(--fs-h4);
  color: var(--text-heading);
  font-variation-settings: var(--display-vsettings);
}

.zg-rc-stars {
  display: flex;
  align-items: center;
  gap: 9px;
  margin-bottom: var(--space-3);
}

.zg-star {
  font-size: 1rem;
  letter-spacing: 2px;
  color: var(--warning);
}

.zg-score {
  font-size: var(--fs-body);
  color: var(--text-subtle);
}

.zg-rc-desc {
  margin: 0 0 22px;
  font-size: var(--fs-body);
  line-height: 1.55;
  color: var(--text-muted);
}

.zg-rc-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  margin-top: auto;
}

.zg-rc-price {
  font-family: var(--font-display);
  font-size: 1.2rem;
  color: var(--text-heading);
  font-variation-settings: var(--display-vsettings);
}

.zg-rc-cta {
  font-family: var(--font-body);
  font-size: var(--fs-body);
  font-weight: var(--fw-semibold);
  color: var(--terracotta-600);
  white-space: nowrap;
}

.zg-rc:hover .zg-rc-cta {
  color: var(--terracotta-700);
}

/* Lege-staat: verborgen tot het filter alle kaarten wegfiltert (zie
   assets/js/archive-filter.js), of direct zichtbaar als de query
   in render.php zelf al 0 gepubliceerde machines oplevert. */
.zg-empty {
  padding: 72px var(--space-6);
  text-align: center;
  background: var(--sand-100);
  border: 1px dashed var(--border-control);
  border-radius: var(--radius-xl);
}

.zg-empty__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 64px;
  height: 64px;
  margin: 0 auto 22px;
  color: var(--espresso-700);
  background: var(--sand-200);
  border-radius: var(--radius-pill);
}

.zg-empty__title {
  margin: 0 0 10px;
  font-size: var(--fs-h3);
}

.zg-empty__text {
  max-width: 44ch;
  margin: 0 auto 26px;
  font-size: var(--fs-body);
  color: var(--text-subtle);
}

/* Terracotta-700 i.p.v. de -600 van `.zg-wis`: die haalt op `--paper-50` wel
   4,7:1, maar deze knop staat op de `--sand-100`-vlakte van `.zg-empty` en
   komt daar op 3,7:1 — onder de 4,5:1 die 18px/600 nodig heeft (grote tekst
   begint pas bij 18,66px bold). Op zand haalt -700 ruim 5:1.
   De negatieve marge van `.zg-wis` hoort bij de uitlijning in
   `.zg-side-head`; hier staat de knop op zichzelf, dus die gaat eraf. */
.zg-empty__clear {
  margin-block: 0;
  font-size: var(--fs-body);
  color: var(--terracotta-700);
}

/* Paginering: statisch/visueel (zie de brief) — echte paginering landt
   zodra het aantal machines dat rechtvaardigt. */
.zg-pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 52px 0 0;
}

.zg-page {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  font-family: var(--font-body);
  font-size: var(--fs-body);
  color: var(--text-subtle);
  background: var(--surface-raised);
  border: 1.5px solid var(--border-control);
  border-radius: var(--radius-md);
  cursor: pointer;
  text-decoration: none;
  transition:
    background 0.14s ease,
    color 0.14s ease,
    border-color 0.14s ease;
}

.zg-page:hover {
  color: var(--text-heading);
  border-color: var(--espresso-700);
}

.zg-page[aria-current="true"] {
  color: var(--paper-50);
  background: var(--espresso-900);
  border-color: var(--espresso-900);
}

@media (width <= 1080px) {
  .zg-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (width <= 820px) {
  .zg-layout {
    grid-template-columns: 1fr;
    gap: 0;
  }

  .zg-filter-toggle {
    display: flex;
  }

  /* `visibility` hoort hier bij `max-height`/`opacity`: zonder die derde
     eigenschap is het ingeklapte paneel wél onzichtbaar maar nog stééds
     focusbaar en leesbaar — `max-height:0` + `overflow:hidden` haalt niets uit
     de tab-volgorde of de accessibility tree. Eén Tab vanaf de
     filter-schakelaar landde daardoor op een chip die niet op het scherm
     staat. `visibility:hidden` lost beide op.
     De vertraging (`visibility 0s linear 0.32s`) laat het paneel pas
     verdwijnen nádat het is dichtgeklapt, zodat de animatie intact blijft; bij
     openen schakelt hij per direct (`visibility 0s` in de open-staat).
     Bewust in CSS en niet met `inert` vanuit archive-filter.js: een
     JS-gezette vlag zou bij het passeren van deze breakpoint moeten worden
     bijgehouden, en blijft anders hangen op desktop waar het paneel altijd
     open hoort te staan. */
  .zg-side {
    position: static;
    overflow: hidden;
    max-height: 0;
    margin-bottom: 0;
    opacity: 0;
    visibility: hidden;
    transition:
      max-height 0.32s ease,
      opacity 0.2s ease,
      margin 0.32s ease,
      visibility 0s linear 0.32s;
  }

  .zg-side.zg-is-open {
    max-height: 1600px;
    margin-bottom: var(--space-8);
    opacity: 1;
    visibility: visible;
    transition:
      max-height 0.32s ease,
      opacity 0.2s ease,
      margin 0.32s ease,
      visibility 0s;
  }

  .zg-side-head {
    margin-bottom: var(--space-4);
  }
}

@media (width <= 620px) {
  .zg-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 14px;
  }
}

@media (width <= 400px) {
  .zg-grid {
    grid-template-columns: 1fr;
  }
}

@media (width <= 640px) {
  .zg-resbar {
    padding-bottom: 22px;
  }

  .zg-rc-body {
    padding: 16px 15px 18px;
  }

  .zg-rc-title {
    margin: 6px 0 8px;
    font-size: 1.05rem;
  }

  .zg-rc-desc {
    margin: 0 0 16px;
    font-size: 0.9rem;
    line-height: 1.45;
  }

  .zg-rc-stars {
    gap: 6px;
    margin-bottom: 10px;
  }

  .zg-score {
    font-size: 0.85rem;
  }

  .zg-rc-price {
    font-size: 1.05rem;
  }

  .zg-rc-cta {
    font-size: 0.9rem;
  }

  .zg-rc-foot {
    flex-direction: column;
    align-items: flex-start;
    gap: 8px;
  }
}

/* ── Archief-hero ─────────────────────────────────────────────────────────
   Titelsectie boven het filter-grid van `archive-zg_machine.html` (Fase 4b
   Task 2). Template-only markup: geen apart block, gewoon core
   heading/paragraph met deze BEM-classes — de copy is vast (niet per-post)
   en verschijnt precies één keer, dus een block zou hier alleen overhead
   toevoegen (zie docs/conventions/reuse.md). 1-op-1 geport uit de
   hero-sectie van ReviewOverzicht.dc.html naar BEM + tokens. De eyebrow
   hergebruikt het gedeelde `.eyebrow`/`.eyebrow--terracotta`-primitief (zie
   base.css) i.p.v. de bron-literal `.zg-eyebrow-terra` te herbouwen —
   zelfde afweging als de kaart-categorie in de "Review archive
   (filter)"-sectie hierboven. */
.zg-archive-hero {
  padding-top: 34px;
}

.zg-archive-hero__title {
  max-width: 16ch;
  margin: 14px 0 18px;
  font-size: 3.4rem;
  line-height: 1.04;
}

.zg-archive-hero__lead {
  max-width: 52ch;
  margin: 0;
  color: var(--text-subtle);
  font-size: var(--fs-lead);
}

@media (width <= 640px) {
  .zg-archive-hero__title {
    font-size: 2.15rem;
  }

  .zg-archive-hero__lead {
    font-size: 1.05rem;
  }
}

/* ── FAQ-sectie (archief) ─────────────────────────────────────────────────
   Sectiekop ("Veelgestelde vragen" + "Voordat je kiest") boven
   `zetgoed/faq` op `archive-zg_machine.html` (Fase 4b Task 2). Net als
   `.zg-archive-hero` hierboven: template-only markup (core heading/
   paragraph), geen apart block — `zetgoed/faq` rendert zelf alleen de
   vraag/antwoord-accordions, geen sectiekop (zie src/blocks/faq/render.php).
   1-op-1 geport uit de "FAQ"-sectie van ReviewOverzicht.dc.html. */
.zg-faq-section {
  padding-top: var(--section-y);
}

.zg-faq-section__title {
  margin: 12px 0 24px;
  font-size: var(--fs-h1);
}

@media (width <= 640px) {
  .zg-faq-section {
    padding-bottom: 2.5rem;
  }

  .zg-faq-section__title {
    margin: 10px 0 12px;
    font-size: 1.65rem;
  }
}

/* ── Merk-hero ────────────────────────────────────────────────────────────
   Hero + donkere 4-stat-balk van `zetgoed/merk-hero` (data-gebonden,
   term-gedreven block: leest de huidige zg_merk-term, zie
   src/blocks/merk-hero/render.php). 1-op-1 geport uit de "Hero"-sectie
   (herobox + stat-balk) van Merkhub.dc.html naar BEM + tokens — de
   broodkruimel erboven is een apart block (`zetgoed/breadcrumb`).

   Classnamen zijn BEM (`.zg-merk-hero__*`) i.p.v. de bron-literal
   `.zg-mh-*`, zelfde afweging als `.zg-breadcrumb`/`.zg-archive-hero`
   t.o.v. hun bron-classes hierboven (zie render.php-docblock in
   src/blocks/merk-hero/render.php). De eyebrow "Merk-hub" hergebruikt om
   dezelfde reden het gedeelde `.eyebrow`/`.eyebrow--terracotta`-primitief
   (zie base.css) i.p.v. de bron-literal `.zg-eyebrow-terra` te herbouwen —
   zelfde afweging als de categorie-eyebrow in `zetgoed/review-archive` en
   de archief-hero.

   De "Onze keuze"-statwaarde heeft in de bron de kopertint `#d9b48a`; die
   bestaat niet als token. `--sand-400` ligt er het dichtst bij — zelfde
   token-vervanging als de scorecard-cirkel in `.zg-review-hero` hierboven
   (zie die docblock, ook daar geen ad-hoc hex toegevoegd). */
.zg-merk-hero__box {
  margin-top: 28px;
  padding: 56px;
  background: var(--sand-100);
  border-radius: var(--radius-xl);
}

.zg-merk-hero__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.95fr);
  align-items: start;
  gap: 56px;
  margin-bottom: 44px;
}

.zg-merk-hero__logo {
  display: block;
  width: auto;
  height: 34px;
  margin: 0 0 24px;
}

.zg-merk-hero__logo--placeholder {
  display: flex;
  align-items: center;
  width: fit-content;
  height: 34px;
  padding: 0 10px;
  color: var(--text-subtle);
  font-size: var(--fs-sm);
  background: var(--sand-200);
  border-radius: var(--radius-sm);
}

.zg-merk-hero__title {
  max-width: 14ch;
  margin: 14px 0 18px;
  font-size: 3.1rem;
  line-height: 1.05;
}

.zg-merk-hero__lead {
  max-width: 48ch;
  margin: 0;
  color: var(--text-subtle);
  font-size: var(--fs-lead);
}

.zg-merk-hero__media {
  display: flex;
  align-items: center;
  justify-content: center;
}

.zg-merk-hero__media--placeholder {
  width: 100%;
  height: 420px;
  color: var(--text-subtle);
  font-size: var(--fs-sm);
  background: var(--sand-300);
  border-radius: var(--radius-lg);
}

.zg-merk-hero__image {
  display: block;
  width: 100%;
  height: 420px;
  object-fit: contain;
  background: var(--sand-300);
  border-radius: var(--radius-lg);
}

.zg-merk-hero__stats {
  display: grid;

  /* `minmax(0, 1fr)` en niet `1fr`: een grid-track weigert standaard kleiner
     te worden dan de min-content van z'n inhoud, dus een lang statlabel als
     "Modellen in de line-up" duwde de balk breder dan de pagina. */
  grid-template-columns: repeat(4, minmax(0, 1fr));
  padding: 32px 8px;
  background: var(--espresso-900);
  border-radius: var(--radius-lg);
}

.zg-merk-hero__stat {
  padding: 0 28px;
  text-align: center;
}

.zg-merk-hero__stat:not(:first-child) {
  border-left: 1px solid color-mix(in oklab, var(--paper-50) 16%, transparent);
}

.zg-merk-hero__stat-value {
  margin: 0;
  font-family: var(--font-display);
  font-size: 2rem;
  color: var(--paper-50);
  font-variation-settings: var(--display-vsettings);
}

.zg-merk-hero__stat-value--accent {
  color: var(--sand-400);
}

.zg-merk-hero__stat-label {
  margin: 8px 0 0;
  color: var(--paper-100);
  font-size: var(--fs-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.7;
}

/* Bron heeft hier een gedeelde regel (`.zg-hero-grid,.zg-story-grid{grid-
   template-columns:1fr!important}` op @media(max-width:900px)) die de
   hero-grid tussen 641–899px naar één gestapelde kolom klapt — zonder dit
   knijpt de tekstkolom + het 420px-beeld op tablet-breedte (bv. 768px) in
   ~256–296px-tracks. Hier scoped op de eigen `.zg-merk-hero__grid`-selector
   i.p.v. de bron-gedeelde class (zie de classnamen-afweging hierboven);
   geen `!important` nodig, we bezitten de selector. */
@media (width <= 900px) {
  .zg-merk-hero__grid {
    grid-template-columns: 1fr;
  }

  /* Twee kolommen vanaf dezelfde breedte waarop de hero-grid stapelt. Stond
     eerder pas op 640px, waardoor de balk tussen 641 en 900px vier kolommen
     hield: op 768px werd dat een rij van vier smalle tracks met tekst die er
     niet in paste (gemeten 50px overflow op /merk/rocket/). */
  .zg-merk-hero__stats {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 24px 0;
    padding: 24px 8px;
  }

  .zg-merk-hero__stat {
    padding: 0 12px;
  }

  .zg-merk-hero__stat:not(:first-child) {
    border-left: 0;
  }
}

@media (width <= 640px) {
  .zg-merk-hero__box {
    padding: 24px;
  }

  .zg-merk-hero__title {
    font-size: 2rem;
  }

  .zg-merk-hero__stat-value {
    font-size: 1.5rem;
  }
}

/* ── Merk-verhaal ─────────────────────────────────────────────────────────
   "Waar {merk} voor staat"-sectie van `zetgoed/merk-verhaal` (data-
   gebonden, term-gedreven block: leest de huidige zg_merk-term, zie
   src/blocks/merk-verhaal/render.php). 1-op-1 geport uit de
   `.zg-story-grid`-sectie van Merkhub.dc.html naar BEM + tokens — beeld
   links, sage-badge + kop + twee alinea's + secondary-knop rechts.

   Classnamen zijn BEM (`.zg-merk-verhaal__*`) i.p.v. de bron-literal
   `.zg-story-grid`, zelfde afweging als `.zg-merk-hero__*` hierboven. De
   sage-badge hergebruikt het `.zg-badge`-primitief (zie de "Badge"-sectie
   hierboven) met de nieuwe `--sage`/`--eyebrow`-modifiers i.p.v. een eigen
   block-specifieke badge-class. De secondary-knop hergebruikt
   `.zg-btn`/`.zg-btn--secondary` binnen het gedeelde
   `.zg-btn-accent-wrap` (zie de "Button"-sectie hierboven), zelfde patroon
   als `zetgoed/over-blok`. */
.zg-merk-verhaal {
  margin: var(--section-y) 0 0;
}

.zg-merk-verhaal__grid {
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
  align-items: center;
  gap: 56px;
}

.zg-merk-verhaal__image {
  display: block;
  width: 100%;
  height: 100%;
  min-height: 360px;
  object-fit: cover;
  border-radius: var(--radius-lg);
}

.zg-merk-verhaal__media--placeholder {
  width: 100%;
  min-height: 360px;
  color: var(--text-subtle);
  font-size: var(--fs-sm);
  background: var(--sand-300);
  border-radius: var(--radius-lg);
  display: flex;
  align-items: center;
  justify-content: center;
}

.zg-merk-verhaal__badge {
  margin-bottom: 16px;
}

.zg-merk-verhaal__title {
  max-width: 18ch;
  margin: 0 0 22px;
  font-size: var(--fs-h2);
}

.zg-merk-verhaal__text {
  max-width: 52ch;
  margin: 0 0 16px;
  color: var(--text-subtle);
  line-height: 1.65;
}

/* Tweede alinea krijgt in de bron een grotere onder-marge (30px i.p.v.
   16px) vóór de knop — via de aangrenzende-sibling-combinator i.p.v. een
   `--last`-modifier-class, zodat dit blijft kloppen ongeacht of er 1 of 2
   alinea's worden gerenderd (`zg_merk_verhaal_1`/`_2` zijn beide optioneel,
   zie render.php). */
.zg-merk-verhaal__text + .zg-merk-verhaal__text {
  margin-bottom: 30px;
}

/* Bron heeft hier dezelfde gedeelde regel als bij `.zg-merk-hero__grid`
   hierboven (`.zg-hero-grid,.zg-story-grid{grid-template-columns:1fr!
   important}` op @media(max-width:900px)) die de story-grid tussen
   641–899px naar één gestapelde kolom klapt — zonder dit knijpt de
   tekstkolom + het beeld op tablet-breedte (bv. 768px) in twee te smalle
   kolommen. Hier scoped op de eigen `.zg-merk-verhaal__grid`-selector i.p.v.
   de bron-gedeelde class (zie de classnamen-afweging hierboven); geen
   `!important` nodig, we bezitten de selector. */
@media (width <= 900px) {
  .zg-merk-verhaal__grid {
    grid-template-columns: 1fr;
  }
}

/* ── Merk-lineup ──────────────────────────────────────────────────────────
   Full-bleed sand-band van `zetgoed/merk-lineup` (Fase 5, Task 6). 1-op-1
   geport uit de "Line-up"-sectie (`.zg-lineup`, "Alle Lelit-modellen") van
   Merkhub.dc.html: alle machines van het huidige merk, hoogste `zg_score`
   eerst, met een "Onze keuze"-badge op het model dat matcht met de
   `zg_merk_onze_keuze`-term-meta (zie render.php).

   `.zg-lineup`/`.zg-model-card`/`.zg-model-cta`/`.zg-ph` zijn de bron z'n
   eigen literale classnamen — Merkhub.dc.html definieert ze zelf in de
   \3c style>, i.t.t. bv. de merk-hero/merk-verhaal-secties die alleen inline
   styles gebruikten (zie de classnamen-afweging in die render.php's). Waar
   de bron al een eigen classname-systeem heeft, wordt dat 1-op-1
   overgenomen — zelfde afweging als `.zg-rc`/`.zg-grid`/`.zg-foto` in de
   "Review archive"-sectie hierboven. De overige modelkaart-onderdelen
   (`.zg-model-media`/`-badge`/`-body`/`-title`/`-desc`/`-foot`/`-price`)
   krijgen een eigen naamgeving in dezelfde stijl als de `.zg-rc-*`-set.
   Let op voor een toekomstige tweede afnemer van deze bron-classnamen: de
   "Lees verder"-grid onderaan Merkhub.dc.html hergebruikt `.zg-lineup`/
   `.zg-model-card`/`.zg-model-cta` óók, maar dan als hele-kaart-`<a>` met
   alléén een eyebrow/titel/tekst/CTA (geen media/prijs) — wie die sectie
   ooit bouwt, moet de overlap met dit BEM-blok expliciet afwegen i.p.v. de
   classnamen kritiekloos te hergebruiken.

   De eyebrow en de "Onze keuze"-badge zijn de gedeelde primitieven
   (`.eyebrow`/`.eyebrow--terracotta`, `.zg-badge`/`.zg-badge--solid`) i.p.v.
   de bron-literal `.zg-eyebrow-terra` opnieuw te bouwen (zie reuse.md §1) —
   zelfde keuze als `zetgoed/merk-hero` en de review-archiefkaart.

   In de bron heeft een model mét foto een media-hoogte van 230px (de
   afbeelding zelf, `object-fit:contain`) en een model zonder foto een
   `.zg-ph`-placeholder van 281px — bewust verschillend (de placeholder
   vult meer ruimte omdat er geen echte foto-inhoud is), hier behouden via
   de `--photo`-modifier i.p.v. één gedeelde hoogte te forceren. De
   modelkaart is bewust géén hele-kaart-link (zie de docblock in
   inc/merk-cards.php): alleen "Lees review →" is een `<a>`, de hover-lift
   hieronder triggert op de kaart-`<div>` zelf.

   `.zg-merk-lineup` alleen krijgt hier geen "het werkt niet"-verrassing:
   `taxonomy-zg_merk.html` plaatst dit block als directe child van een
   `layout:"constrained"`-<main>, en WP's core `.is-layout-constrained > *`
   forceert `margin-left/right:auto !important` op elke child zonder
   `.alignfull`/`.alignleft`/`.alignright` — dat wint altijd van deze
   `width:100vw;margin-left:calc(...)`-regel, `!important` mogen we zelf niet
   gebruiken (stylelint). render.php voegt daarom een kale `alignfull`-class
   toe (geen `supports.align`) om exact díe WP-uitzondering te triggeren; zie
   de docblock in render.php. */

/* Zelfde full-bleed-bandpatroon als `.zg-over-blok--band`, inclusief de
   `margin-top`. Die ontbrak hier, en bij een band met een eigen
   achtergrondkleur telt alleen de marge: de `padding-top` valt binnen het
   zand en levert dus geen witruimte tussen de band en het blok erboven. Op
   /merk/rocket/ begon het zand daardoor 24px onder de knop van
   "Een blikvanger die ook echt presteert". */
.zg-merk-lineup {
  width: 100vw;
  margin: var(--section-y) calc(50% - 50vw) 0;
  background: var(--sand-100);
  padding: var(--section-y) 0;
}

.zg-merk-lineup__inner {
  /* content-box: max-width telt hier de CONTENTbreedte, net als
     .zg-hdr__bar en .zg-footer__inner. Zonder deze regel erven de inners
     die als wp:group in een template staan WP's border-box, waardoor hun
     inhoud 24px verder naar binnen valt dan de rest van de pagina. */
  box-sizing: content-box;
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 var(--space-6);
}

.zg-merk-lineup__heading {
  margin: 12px 0 8px;
  font-size: var(--fs-h1);
}

.zg-merk-lineup__lead {
  margin: 0 0 40px;
  color: var(--text-muted);
  font-size: 1.125rem;
}

.zg-lineup {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-6);
}

/* Het /molens-archief (`zetgoed/molen-grid`) filtert client-side door
   `hidden` te zetten op losse kaarten en, bij nul resultaten, op de grid zelf
   — zie assets/js/archive-filter.js. Zonder deze twee overrides winnen de
   `display:grid`/`display:flex` hierboven van de UA-regel
   `[hidden]{display:none}` en verdwijnt er niets. Zelfde noodzaak (en zelfde
   reden) als bij `.zg-grid`/`.zg-rc` in het review-archief hierboven.
   In de merk-line-up en de "Lees verder"-grid staat nooit een `hidden`, dus
   deze regels raken daar niets. */
.zg-lineup[hidden] {
  display: none;
}

.zg-model-card {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  transition:
    transform 0.16s ease,
    box-shadow 0.16s ease,
    border-color 0.16s ease;
}

.zg-model-card[hidden] {
  display: none;
}

.zg-model-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-md);
  border-color: var(--border-control);
}

.zg-model-media {
  position: relative;
}

.zg-model-media--photo {
  background: var(--sand-300);
  border-bottom: 1px solid var(--border);
}

.zg-model-media__link {
  display: block;
}

.zg-model-image {
  display: block;
  width: 100%;
  height: 230px;
  object-fit: contain;
}

/* Sinds de badge een `<a>` is (inc/merk-cards.php) draagt hij het enige
   zichtbare linkteken dat we hier willen: een onderstreping. Kleur, vorm en
   vulling van `.zg-badge--solid` blijven ongewijzigd, zodat de badge er in
   rust precies zo uitziet als voorheen. */
.zg-model-badge {
  position: absolute;
  top: 14px;
  left: 14px;
  z-index: 2;
}

/* De onderstreping staat op de badge zelf en niet op de `<a>` eromheen:
   `.zg-badge` is `display:inline-flex`, en tekstdecoratie van een ouder
   erft niet door naar een inline-flex kind. */
.zg-model-badge .zg-badge {
  text-decoration-line: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}

/* `.zg-ph`-reconcile (Fase 10a Task 1): dit was een vast-hoge (281px),
   tekst-gecentreerde placeholder — alleen geschikt voor de merk-lineup-
   modelkaart hierboven. Het blog-overzicht (Fase 10a) hergebruikt dezelfde
   bron-class (`.zg-ph`) voor hero-/featured-/kaart-media mét een echte
   `<img>`-fallback en een label-pill, dus deze regel wordt hier bijgewerkt
   naar de volledige bron-versie (Blogoverzicht.dc.html z'n `\3c style>`):
   hatch-achtergrond + `img{object-fit:cover}` + `.zg-ph-lbl`-pill, GEEN
   vaste hoogte meer (blog-blocks zetten zelf `aspect-ratio`). De vorige
   `height:281px` viel weg — merk-lineup rekende daarop voor de hoogte van
   z'n "foto"-placeholder-modelkaarten; dat wordt hieronder lokaal
   teruggezet (`.zg-model-media .zg-ph`) zodat de 4 placeholder-modelkaarten
   op `/merk/lelit/` niet inklappen (regressie-check, zie ledger). De
   merk-lineup-placeholder rendert nog steeds kale tekst ("foto", niet in
   een `.zg-ph-lbl`-span — zie inc/merk-cards.php, buiten scope van deze
   taak) en toont die nu onderaan i.p.v. gecentreerd (`align-items:
   flex-end` uit de bron) — een kleine, geaccepteerde cosmetische afwijking;
   de kaart blijft even groot en leesbaar. */
.zg-ph {
  position: relative;
  display: flex;
  align-items: flex-end;
  overflow: hidden;
  background: var(--sand-200);
  background-image: repeating-linear-gradient(
    135deg,
    transparent 0 14px,
    color-mix(in oklab, var(--sand-300) 55%, transparent) 14px 15px
  );
}

.zg-ph img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.zg-ph .zg-ph-lbl {
  position: relative;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.02em;
  color: var(--espresso-500);
  background: color-mix(in oklab, var(--paper-50) 82%, transparent);
  border-radius: var(--radius-pill);
  padding: 5px 12px;
  margin: 14px;
}

/* Regressie-fix: merk-lineup's modelkaart-placeholder (`.zg-model-media
   .zg-ph`, zie inc/merk-cards.php) rekende op de globale `height:281px`
   hierboven — die is met de reconcile vervallen, dus lokaal terugzetten
   zodat de kaart niet inklapt. 281px is bewust hoger dan de 230px van een
   echte foto (`.zg-model-image`) — zie de "Merk-lineup"-docblock verderop
   in dit bestand. */
.zg-model-media .zg-ph {
  width: 100%;
  height: 281px;
}

/* Merken zonder eigen machinefoto (`zg_merk_hero_image` leeg — bv. ECM,
   Rocket, Profitec) krijgen in src/blocks/merk-hero/render.php diezelfde
   gedeelde `.zg-ph` in het hero-media-slot. Afmetingen 1-op-1 die van
   `.zg-merk-hero__image`, zodat de hero even hoog blijft met of zonder foto.
   Staat hier — ná de basis-`.zg-ph` en naast het `.zg-model-media`-precedent
   hierboven — en niet bij de overige `.zg-merk-hero__*`-regels: een scoped
   `.zg-ph`-override vóór de basisregel laat die basisregel winnen op gelijke
   specificiteit (stylelint `no-descending-specificity` vangt dat). */
.zg-merk-hero__media .zg-ph {
  width: 100%;
  height: 420px;
  border-radius: var(--radius-lg);
}

.zg-model-body {
  display: flex;
  flex: 1;
  flex-direction: column;
  padding: 22px;
}

.zg-model-title {
  margin: var(--space-2) 0 8px;
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  color: var(--text-heading);
  font-variation-settings: var(--display-vsettings);
}

.zg-model-desc {
  margin: 0 0 22px;
  color: var(--text-muted);
  line-height: 1.55;
}

.zg-model-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: auto;
}

.zg-model-price {
  color: var(--text-heading);
  font-weight: var(--fw-semibold);
}

.zg-model-cta {
  color: var(--terracotta-600);
  font-weight: var(--fw-semibold);
  text-decoration: none;
  transition: color 0.16s ease;
}

.zg-model-cta:hover {
  color: var(--terracotta-700);
}

/* Bron-`@media(max-width:900px)` klapt `.zg-lineup` naar 2 kolommen (samen
   met de merk-hero/-verhaal-grids, zie die secties hierboven — hier alleen
   het line-up-deel van diezelfde bron-media-query). */
@media (width <= 900px) {
  .zg-lineup {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* Bron-`@media(max-width:640px)` houdt 2 kolommen aan maar verdicht de
   kaart: kleinere gap/padding/media-hoogte/radius/lettergrootte. */
@media (width <= 640px) {
  .zg-lineup {
    grid-template-columns: repeat(2, 1fr);
    gap: var(--space-3);
  }

  .zg-model-card {
    border-radius: var(--radius-md);
  }

  .zg-model-body {
    padding: 14px;
  }

  .zg-model-image,
  .zg-model-media .zg-ph {
    height: 130px;
  }

  .zg-model-desc {
    margin-bottom: var(--space-4);
    font-size: 0.9rem;
    line-height: 1.45;
  }

  .zg-model-cta,
  .zg-model-price {
    font-size: 0.9rem;
  }

  .zg-model-eyebrow {
    font-size: 11px;
  }
}

/* ── Archief-line-up ──────────────────────────────────────────────────────
   `.zg-lineup--archief` is de line-up zoals `zetgoed/molen-grid` hem op
   /molens rendert: náást de 264px-facet-sidebar in plaats van over de volle
   contentbreedte. Vandaar een modifier op de grid zelf en geen
   `.zg-layout .zg-lineup`-descendant — het smallere raster hoort bij déze
   variant van het component, niet bij de voorouder waar hij toevallig in
   staat, en een platte klasse kan niet stilletjes latere `.zg-lineup`-regels
   overrulen.
   De breekpunten volgen die van `.zg-grid` in het reviewarchief (2 kolommen
   zodra de sidebar de resultaatkolom smal maakt, 1 kolom op de kleinste
   telefoons), zodat de twee archieven zich op elke breedte hetzelfde
   gedragen; zonder het 400px-punt stonden hier twee geperste kaarten van
   ~155px naast elkaar waar /reviews er één op volle breedte toont.
   Staat bewust ná de generieke `.zg-lineup`-media-queries hierboven: gelijke
   specificiteit, dus bronvolgorde beslist. */
@media (width <= 1080px) {
  .zg-lineup--archief {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (width <= 400px) {
  .zg-lineup--archief {
    grid-template-columns: 1fr;
  }
}

/* ── Merk-grid ────────────────────────────────────────────────────────────
   "Andere merken"-sectie van `zetgoed/merk-grid` (Fase 5, Task 7). 1-op-1
   geport uit de "Andere merken"-sectie (`.zg-brand-grid`) van
   Merkhub.dc.html: logo-tegels van de overige zg_merk-termen, zie
   src/blocks/merk-grid/render.php.

   `.zg-brand-grid`/`.zg-brand` zijn de bron z'n eigen literale classnamen
   (Merkhub.dc.html definieert ze zelf in de \3c style>) — 1-op-1 overgenomen,
   zelfde afweging als `.zg-lineup`/`.zg-model-card` in de "Merk-lineup"-
   sectie hierboven. Alleen de wrapper (`.zg-merk-grid`) en de kop
   (`.zg-merk-grid__heading`) krijgen een eigen BEM-naam; de eyebrow
   hergebruikt het gedeelde `.eyebrow`/`.eyebrow--terracotta`-primitief
   i.p.v. de bron-literal `.zg-eyebrow-terra` te herbouwen (zie reuse.md §1),
   zelfde keuze als overal elders in dit thema.

   Dit block zit — anders dan `.zg-merk-lineup` hierboven — niet full-bleed:
   de bron plaatst "Andere merken" als gewone `<section>` binnen de
   1200px-container, dus geen `alignfull`/`width:100vw`-truc nodig. */
.zg-merk-grid {
  padding: var(--section-y) 0 0;
}

.zg-merk-grid__heading {
  margin: 12px 0 32px;
  font-size: var(--fs-h1);
}

.zg-brand-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: var(--space-4);
}

.zg-brand {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 88px;
  padding: 18px;
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  text-decoration: none;
  transition:
    border-color 0.16s ease,
    background 0.16s ease;
}

.zg-brand:hover {
  background: var(--forest-100);
  border-color: var(--accent);
}

.zg-brand img {
  width: auto;
  max-width: 100%;
  max-height: 26px;
  object-fit: contain;
  filter: saturate(0);
  opacity: 0.72;
  transition:
    filter 0.16s ease,
    opacity 0.16s ease;
}

.zg-brand:hover img {
  filter: none;
  opacity: 1;
}

/* Bron-`@media(max-width:900px)` klapt de grid naar 3 kolommen. */
@media (width <= 900px) {
  .zg-brand-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* Bron-`@media(max-width:560px)` klapt de grid naar 2 kolommen. */
@media (width <= 560px) {
  .zg-brand-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* ── Verder-lezen ─────────────────────────────────────────────────────────
   "Lees verder"-sectie van `zetgoed/verder-lezen` (Fase 5, Task 8). 1-op-1
   geport uit de "Lees verder"-sectie (`.zg-lineup`, "Handig bij Lelit") van
   Merkhub.dc.html: drie redactionele linkkaarten (koopgidsen/vergelijkers)
   die passen bij het huidige merk, gezet via block-attributen i.p.v.
   post-data (zie render.php) — een taxonomy-template heeft geen eigen
   editor-content.

   Classnamen-afweging (zie ook de docblock hierboven bij "Merk-lineup",
   die dit al vooraankondigde): de bron hergebruikt letterlijk dezelfde
   `.zg-lineup`/`.zg-model-card`/`.zg-model-cta`-classnamen voor deze
   sectie als voor de model-line-up — inclusief de bron-eigen
   `@media(max-width:640px)`-compactering (`.zg-lineup .zg-model-card>div:
   last-child{padding:14px}` enz.). Die verregaande koppeling kritiekloos
   overnemen zou deze kaart (geen media/prijs, hele kaart is een `<a>`,
   padding direct op de kaart i.p.v. op een `.zg-model-body`) onbedoeld
   laten meebewegen met toekomstige line-up-only-wijzigingen. Vandaar een
   eigen grid-wrapper (`.zg-verder-lezen__grid`, i.p.v. het bron-`.zg-lineup`
   te hergebruiken) — maar de kaart zelf hergebruikt wél `.zg-model-card`
   (zelfde achtergrond/rand/radius/`display:flex;flex-direction:column`/
   hover-lift als de line-up-kaart, zie die sectie) plus de gedeelde
   `.zg-card-link`-hover-primitief (zelfde class als `zetgoed/
   related-reviews`, zie de "Gedeelde hover-lift"-docblock hierboven) — dat
   laatste is hier functioneel gelijk aan `.zg-model-card`'s eigen
   `:hover`-regel (beide kaart-typen delen dezelfde transform/shadow/
   border-kleur), maar wordt toch meegegeven zodat elke hele-kaart-`<a>` in
   het thema consistent dezelfde hover-primitief draagt, ongeacht of de
   basis-kaartclass toevallig al een eigen hover heeft.

   `.zg-model-card--text` is de enige echte delta t.o.v. de line-up-kaart:
   `padding:28px` direct op de kaart (bron) i.p.v. een aparte
   `.zg-model-body`-child, en `text-decoration:none` omdat de kaart hier
   zelf een `<a>` is (de line-up-kaart is een `<div>` met alleen "Lees
   review →" als link). `.zg-model-eyebrow`/`.zg-model-title`/`.zg-model-
   desc`/`.zg-model-cta` zijn 1-op-1 hergebruikt uit de "Merk-lineup"-sectie
   — geen tweede set BEM-namen voor identieke tekstonderdelen. */
.zg-verder-lezen {
  padding: var(--section-y) 0 0;
}

.zg-verder-lezen__heading {
  margin: 12px 0 32px;
  font-size: var(--fs-h1);
}

.zg-verder-lezen__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-6);
}

.zg-model-card--text {
  padding: 28px;
  text-decoration: none;
}

/* CTA onderaan pinnen (bron: `margin-top:auto` op de `.zg-model-cta`-span
   zelf) zodat de "…→"-links over de rij uitlijnen bij ongelijke tekstlengte
   — anders schuift de CTA direct onder de alinea. */
.zg-model-card--text .zg-model-cta {
  margin-top: auto;
}

/* Zelfde 900px-drempel als `.zg-lineup` hierboven (bron: bewuste 2-koloms
   tussenstap tussen de 3-koloms desktop-grid en de 640px-compactering). */
@media (width <= 900px) {
  .zg-verder-lezen__grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* Bron compacteert de kaart-padding op klein scherm (zelfde `14px` als
   `.zg-model-body` in de "Merk-lineup"-sectie op 640px krijgt) — hier op de
   eigen `--text`-modifier i.p.v. de bron-descendant-selector
   `.zg-lineup .zg-model-card>div:last-child`, want die child-selector past
   niet op deze kaart (geen aparte body-`<div>`, de padding zit direct op
   de kaart-`<a>`). */
@media (width <= 640px) {
  .zg-model-card--text {
    padding: 14px;
  }
}

/* ── Koopgids-hero ────────────────────────────────────────────────────────
   Hero van `zetgoed/koopgids-hero` (data-gebonden block, Fase 6 Task 3, zie
   src/blocks/koopgids-hero/render.php). 1-op-1 geport uit de "Hero"-sectie
   van `templates/koopgids/Koopgids.dc.html` naar BEM + tokens.

   Classnamen-afweging: de bron gebruikt losse `.zg-kg-hero`/`.zg-kg-hero-h1`/
   `.zg-kg-hero-img`/`.zg-kg-pad`-classes (gedeeld met andere Koopgids-secties
   uit dezelfde brondocument, bv. het "Uitgelicht"-paneel verderop in de
   pagina). Deze sectie krijgt i.p.v. daarvan een eigen, volledige
   `.zg-koopgids-hero__*`-BEM-set (repo-conventie, zie docs/conventions/
   css.md §2) zodat de styling van dit block niet onbedoeld meebeweegt met
   toekomstige, ongerelateerde `.zg-kg-*`-secties uit latere Fase 6-taken.

   Sand-box (achtergrond/radius/padding) is 1-op-1 met `.zg-review-hero`
   hierboven, maar zonder diens absolute scoreseal — de koopgids-hero heeft
   geen scorecard. */
.zg-koopgids-hero {
  margin-top: 28px;
  margin-bottom: var(--section-y);
  padding: 56px;
  background: var(--sand-100);
  border-radius: var(--radius-xl);
}

.zg-koopgids-hero__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  align-items: center;
  gap: 56px;
}

.zg-koopgids-hero__title {
  margin: 16px 0 20px;
  font-size: 3.2rem;
  line-height: 1.04;
}

.zg-koopgids-hero__lead {
  max-width: 46ch;
  margin: 0 0 28px;
  color: var(--text-subtle);
  font-size: var(--fs-lead);
}

.zg-koopgids-hero__byline {
  display: flex;
  align-items: center;
  gap: 14px;
}

.zg-koopgids-hero__portrait {
  flex: none;
  width: 48px;
  height: 48px;
  object-fit: cover;

  /* `center top` sneed de bovenkant van het hoofd af; 18% zet het gezicht
     midden in de cirkel. Zie `.zg-overons-hero__photo`. */
  object-position: 50% 18%;
  border-radius: var(--radius-pill);
}

.zg-koopgids-hero__portrait--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--sand-300);
  color: var(--text-subtle);
  font-size: var(--fs-xs);
}

.zg-koopgids-hero__byline-text {
  line-height: 1.3;
}

.zg-koopgids-hero__byline-name {
  margin: 0;
  color: var(--text-heading);
  font-weight: var(--fw-semibold);
}

.zg-koopgids-hero__byline-stat {
  margin: 0;
  color: var(--text-subtle);
  font-size: var(--fs-sm);
}

.zg-koopgids-hero__image {
  display: block;
  width: 100%;
  height: 360px;
  object-fit: cover;
  border-radius: var(--radius-lg);
}

.zg-koopgids-hero__image--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-subtle);
  font-size: var(--fs-sm);
}

/* Bron-breekpunten 860px (1-koloms grid) en 640px (kop/lead/beeld
   compacteren) — zelfde tweetrapsverloop als elders in dit bestand (bv.
   `.zg-review-hero` hierboven bij 900px), hier op de bron-eigen 860px/640px
   omdat de bron die zelf zo declareert (`@media(max-width:860px)` resp.
   `@media(max-width:640px)` in Koopgids.dc.html). */
@media (width <= 860px) {
  .zg-koopgids-hero {
    padding: 36px;
  }

  .zg-koopgids-hero__grid {
    grid-template-columns: 1fr;
    gap: 26px;
  }
}

@media (width <= 640px) {
  .zg-koopgids-hero {
    padding: 24px;
  }

  .zg-koopgids-hero__title {
    font-size: 2rem;
  }

  .zg-koopgids-hero__lead {
    font-size: 1rem;
  }

  .zg-koopgids-hero__image {
    height: 220px;
  }
}

/* ── Koopgids-topkeuze ───────────────────────────────────────────────────
   Uitgelichte "onze topkeuze"-band van `zetgoed/koopgids-topkeuze`
   (editor-content block, Fase 6 Task 4, zie src/blocks/koopgids-topkeuze/
   render.php). 1-op-1 geport uit de "Uitgelicht: onze topkeuze"-sectie van
   `templates/koopgids/Koopgids.dc.html` naar BEM + tokens.

   Classnamen-afweging: de bron gebruikt de gedeelde `.zg-kg-feat`-class
   (ook elders in hetzelfde brondocument). Zelfde keuze als
   `.zg-koopgids-hero` hierboven: een eigen, volledige
   `.zg-koopgids-topkeuze__*`-BEM-set i.p.v. de bron-eigen `.zg-kg-*`-
   classes.

   Geen eigen margin: de bron declareert op deze sectie zelf geen
   margin/padding-top — de ruimte erboven komt van `.zg-koopgids-hero`'s
   eigen `margin-bottom: var(--section-y)` (de sectie waar dit paneel altijd
   direct op volgt, zie templates/single-zg_koopgids.html), de ruimte
   eronder van het volgende blok (`.zg-cta-band` heeft al zijn eigen
   `margin: var(--section-y) 0 0`). */
.zg-koopgids-topkeuze {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 28px;
  padding: 28px;
  background: var(--espresso-900);
  border-radius: var(--radius-xl);
}

/* Beeld-achtergrond: de bron geeft de 130×130-productfoto een kale
   `background:rgb(223,203,166)` — exact `--sand-300` (#dfcba6 =
   rgb(223,203,166)). */
.zg-koopgids-topkeuze__image {
  width: 130px;
  height: 130px;
  object-fit: contain;
  background: var(--sand-300);
  border-radius: var(--radius-md);
}

/* Editor-only placeholder (geen afbeelding-attribuut ingevuld) — zelfde
   patroon als `.zg-koopgids-hero__portrait--placeholder` hierboven. */
.zg-koopgids-topkeuze__image--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 8px;
  color: var(--text-subtle);
  font-size: var(--fs-xs);
  text-align: center;
  word-break: break-all;
}

.zg-koopgids-topkeuze__title {
  margin: 8px 0;
  color: var(--paper-50);
  font-family: var(--font-display);
  font-size: var(--fs-h3);
}

.zg-koopgids-topkeuze__desc {
  max-width: 52ch;
  margin: 0;
  color: var(--paper-100);
  opacity: 0.85;
}

.zg-koopgids-topkeuze__actions {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 12px;
}

.zg-koopgids-topkeuze__review-link {
  color: var(--paper-100);
  font-size: var(--fs-sm);
  text-align: center;
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* Bron-breekpunt 640px (1-koloms, gecentreerd) — zelfde `.zg-kg-feat`-regel
   als in Koopgids.dc.html zelf. */
@media (width <= 640px) {
  .zg-koopgids-topkeuze {
    grid-template-columns: 1fr;
    gap: 14px;
    padding: 24px;
    text-align: center;
  }

  .zg-koopgids-topkeuze__image {
    margin: 0 auto;
  }

  .zg-koopgids-topkeuze__desc {
    max-width: none;
  }

  .zg-koopgids-topkeuze__actions {
    align-items: stretch;
  }
}

/* ── Koopgids-ranglijst ──────────────────────────────────────────────────
   "Ranglijst"-sectie van `zetgoed/koopgids-ranglijst` (editor-content
   block, Fase 6 Task 5, `items`-attribute — zie src/blocks/
   koopgids-ranglijst/render.php voor het pros/cons- en sterren-datamodel).
   1-op-1 geport uit de "Ranglijst"-sectie van Koopgids.dc.html naar BEM +
   tokens. Geen eigen container-breedte/margin-auto: zelfde reden als
   `.zg-koopgids-topkeuze` hierboven — de content-breedte komt uit de
   pagina-template, alleen de bron-eigen `padding:var(--section-y) 0 0`
   hoort bij dit block. */
.zg-koopgids-ranglijst {
  padding: var(--section-y) 0 0;
}

.zg-koopgids-ranglijst__heading {
  margin: 12px 0 14px;
  overflow-wrap: break-word;
  font-size: var(--fs-h1);
}

/* Zie de toelichting bij `.zg-keuzehulp__title`: op smalle schermen past
   "espressomachines" niet op één regel bij de volle `--fs-h1` en brak het
   mid-woord af. Iets verkleinen zodat het langste woord past. */
@media (width <= 430px) {
  .zg-koopgids-ranglijst__heading {
    font-size: 2.25rem;
  }
}

.zg-koopgids-ranglijst__lead {
  max-width: 60ch;
  margin: 0 0 28px;
  color: var(--text-muted);
  font-size: 1.25rem;
}

/* "Bekijk alle reviews" onder de ranglijst: de uitgang voor wie zich niet in
   deze vier herkent. Gecentreerd omdat hij bij de hele lijst hoort en niet
   bij de laatste kaart. */
.zg-koopgids-ranglijst__all {
  display: flex;
  justify-content: center;
  margin-top: var(--space-8);
}

/* Editor-only samenvatting (geen bron-equivalent) — zie edit.js voor de
   read-only-preview-afweging. */
.zg-koopgids-ranglijst__preview-list {
  margin: 0;
  padding-left: 1.2em;
  color: var(--text-muted);
}

.zg-koopgids-ranglijst__list {
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.zg-koopgids-ranglijst__card {
  position: relative;
  display: grid;
  grid-template-columns: 220px minmax(0, 1fr);
  gap: 36px;
  padding: 32px;
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
}

.zg-koopgids-ranglijst__rank {
  position: absolute;
  top: 22px;
  left: 22px;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  color: var(--paper-50);
  font-family: var(--font-display);
  font-size: 18px;
  line-height: 1;
  background: var(--espresso-900);
  border-radius: var(--radius-pill);
}

.zg-koopgids-ranglijst__rank-num {
  display: block;
  transform: translateY(-1.5px);
}

/* Zegel op de #1-kaart: zelfde patroon als `.zg-review-hero__seal` hierboven
   (scroll-gekoppelde rotatie via assets/js/koopgids-ranglijst-seal.js,
   mirror van assets/js/review-hero.js). `--shadow-lg` i.p.v. de bron-
   letterlijke `drop-shadow(0 6px 16px rgba(0,0,0,.22))`: zelfde token-
   substitutie als `.zg-review-hero__seal` voor hetzelfde soort component. */
.zg-koopgids-ranglijst__seal {
  position: absolute;
  top: -30px;
  right: -24px;
  z-index: 3;
  filter: drop-shadow(var(--shadow-lg));
  will-change: transform;
}

.zg-koopgids-ranglijst__seal-bg {
  fill: var(--accent);
}

.zg-koopgids-ranglijst__seal-text {
  fill: var(--paper-50);
  font-family: var(--font-body);
  font-size: 21px;
  font-weight: var(--fw-semibold);
  letter-spacing: 4px;
  text-transform: uppercase;
}

.zg-koopgids-ranglijst__seal-icon {
  stroke: var(--paper-50);
  stroke-width: 3;
}

.zg-koopgids-ranglijst__image {
  align-self: start;
  width: 100%;
  height: 220px;
  background: var(--sand-300);
  border-radius: var(--radius-lg);
  object-fit: contain;
}

.zg-koopgids-ranglijst__title {
  margin: 8px 0;
  overflow-wrap: anywhere;
  color: var(--text-heading);
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  font-variation-settings: var(--display-vsettings);
}

.zg-koopgids-ranglijst__rating {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 16px;
}

.zg-koopgids-ranglijst__stars {
  color: var(--warning);
  letter-spacing: 2px;
}

.zg-koopgids-ranglijst__rating-text {
  color: var(--text-subtle);
  font-size: var(--fs-sm);
}

.zg-koopgids-ranglijst__desc {
  max-width: 68ch;
  margin: 0 0 20px;
  color: var(--text-muted);
}

.zg-koopgids-ranglijst__pc {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px 32px;
  margin-bottom: 24px;
}

.zg-koopgids-ranglijst__pc-item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

.zg-koopgids-ranglijst__pc-icon {
  flex: none;
  margin-top: 3px;
}

.zg-koopgids-ranglijst__pc-icon--pro {
  color: var(--success);
}

.zg-koopgids-ranglijst__pc-icon--con {
  color: var(--text-subtle);
}

.zg-koopgids-ranglijst__pc-item--pro span {
  color: var(--text-muted);
}

.zg-koopgids-ranglijst__pc-item--con span {
  color: var(--text-subtle);
}

.zg-koopgids-ranglijst__pricerow {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  padding-top: 20px;
  border-top: 1px solid var(--border);
}

.zg-koopgids-ranglijst__price-value {
  margin: 0;
  color: var(--text-heading);
  font-family: var(--font-display);
  font-size: 1.9rem;
  font-variation-settings: var(--display-vsettings);
}

.zg-koopgids-ranglijst__price-label {
  margin: 0;
  color: var(--text-subtle);
  font-size: var(--fs-xs);
}

.zg-koopgids-ranglijst__actions {
  display: flex;
  align-items: center;
  gap: 12px;
}

/* Bron-breekpunten 860px/640px — zelfde tweetrapsverloop als elders in dit
   bestand, hier op de bron-eigen `.zg-kg-rank`-media-queries. Het zegel
   krimpt hier al bij ≤860px i.p.v. pas bij de bron-eigen ≤640px: bij
   768px (fidelity-overflow-check) duwt het volle zegel (132px,
   `right:-30px`) de kaart voorbij de viewport — de bron zelf test dat
   bereik kennelijk niet. Bewuste, kleine afwijking van 1-op-1 om de
   overflow-eis (scrollWidth===innerWidth op 768px) te halen; ≤640px kreeg
   toch al hetzelfde kleinere zegel, dus dit is puur een eerder
   ingangspunt, geen ander eindresultaat op mobiel. */
@media (width <= 860px) {
  .zg-koopgids-ranglijst__card {
    grid-template-columns: 170px minmax(0, 1fr);
    gap: 24px;
    padding: 24px;
  }

  .zg-koopgids-ranglijst__seal {
    top: -14px;
    right: -6px;
    width: 88px;
    height: 88px;
  }
}

@media (width <= 640px) {
  .zg-koopgids-ranglijst__card {
    grid-template-columns: minmax(0, 1fr);
    gap: 18px;
    padding: 20px;
  }

  .zg-koopgids-ranglijst__image {
    height: 180px;
  }

  /* Pluspunten/minpunten van 2×2 naar één kolom (bron `.zg-kg-pc`
     @media(max-width:640px)) zodat de items op smalle schermen volledig
     uitlijnen i.p.v. in twee smalle kolommen te blijven. */
  .zg-koopgids-ranglijst__pc {
    grid-template-columns: 1fr;
    gap: 8px;
  }
}

/* ── Koopgids-vergelijktabel ─────────────────────────────────────────────
   "Vergelijkingstabel"-sectie van `zetgoed/koopgids-vergelijktabel`
   (editor-content block, Fase 6 Task 6, `eyebrow`/`title`/`columns`/`rows`-
   attributen — zie src/blocks/koopgids-vergelijktabel/render.php voor het
   index-gealigneerde kolom/rij-datamodel). 1-op-1 geport uit de
   "Vergelijkingstabel"-sectie van Koopgids.dc.html naar BEM + tokens; de
   sand-panel (`--sand-100`/`--radius-xl`/56px padding) is hier, anders dan
   `.zg-koopgids-topkeuze`/`.zg-koopgids-ranglijst` hierboven, WEL een
   bron-eigen paneel-achtergrond (bron: `.zg-kg-pad` met diezelfde drie
   waarden inline) — vandaar dat dit block, in tegenstelling tot die twee,
   zélf background/border-radius/padding zet i.p.v. alleen padding. De bron
   zet ook `margin:var(--section-y) 0 0` op diezelfde `.zg-kg-pad`-regel
   (zelfde patroon als `.zg-cta-band`, dat de identieke inline-margin ook
   zelf zet i.p.v. op het default block-gap te vertrouwen) — zonder die
   margin viel de sectie-afstand Ranglijst→Vergelijktabel terug op het
   default block-gap (24px) i.p.v. de bedoelde 120px (`--section-y`),
   gefixt in een follow-up na code review. */
.zg-koopgids-vergelijktabel {
  margin: var(--section-y) 0 0;
  padding: 56px;
  background: var(--sand-100);
  border-radius: var(--radius-xl);
}

.zg-koopgids-vergelijktabel__heading {
  margin: 12px 0 28px;
  font-size: var(--fs-h1);
}

/* Overflow-containment: dit is de #1 overflow-risico van dit block (zie
   task-brief). `overflow-x:auto` op déze wrapper — niet op `.zg-cmp` zelf
   én niet op de sectie — houdt de horizontale scroll van de tabel
   (`min-width:820px`, zie `.zg-cmp` hieronder) volledig BINNEN de wrapper.
   De wrapper zelf krijgt geen `min-width`/`width` groter dan 100%, dus hij
   drukt de pagina nooit breder; alleen zíjn eigen scrollbox groeit. */
.zg-koopgids-vergelijktabel__scroll {
  overflow-x: auto;
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
}

/* `.zg-cmp`/`.zg-trow` zijn bron-letterlijke classnamen (Koopgids.dc.html) —
   gedeeld tussen de desktop-tabelstijl hier en de mobiele card-collapse
   hieronder, dus bewust NIET hernoemd naar een
   `.zg-koopgids-vergelijktabel__*`-BEM-variant (puur traceerbaarheid naar
   de bron, zelfde afweging als `.zg-srow` op `zetgoed/specs-table`). */
.zg-cmp {
  width: 100%;
  min-width: 820px;
  border-collapse: collapse;
}

/* Scope-prefix `.zg-koopgids-vergelijktabel` op deze th/td-regels (i.p.v.
   kale `.zg-cmp thead th`/`.zg-trow td`) is puur om stylelint's
   `no-descending-specificity` tevreden te houden t.o.v. de eerder in dit
   bestand gedefinieerde `.zg-srow th`/`.zg-srow td`/`.zg-srow:first-child
   th/td` (specs-table, zie hierboven) — die eindigen op hetzelfde
   element-type met een hogere specificiteit. Geen functioneel verschil: de
   klasse-scope is altijd al aanwezig (de tabel staat nooit buiten dit
   block). */
.zg-koopgids-vergelijktabel .zg-cmp thead th {
  padding: 20px;
  color: var(--text-subtle);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.06em;
  text-align: left;
  text-transform: uppercase;
}

.zg-koopgids-vergelijktabel .zg-trow td {
  padding: 20px;
  color: var(--text-muted);
  font-size: 1rem;
  vertical-align: middle;
  border-top: 1px solid var(--border);
}

/* Model-cel (eerste kolom): bron `.zg-trow td:first-child{color:var(
   --text-heading);font-weight:600}` — nadrukkelijker dan de overige
   cellen, en op mobiel (zie hieronder) de kaart-titel. */
.zg-trow td:first-child {
  color: var(--text-heading);
  font-weight: var(--fw-semibold);
}

/* Mobiele card-collapse (bron `@media(max-width:640px)` op `.zg-cmp`):
   thead verdwijnt, elke rij wordt een kaart en `td::before` toont de
   kolomkop (`data-label`) als label vóór de waarde. De Model-cel (eerste
   kolom) krijgt GEEN `data-label` van render.php (zie de docblock daar) —
   dus `content:attr(data-label)` op die cel resolvet naar een lege string
   en er verschijnt nooit een "Model"-labelprefix. De Model-cel wordt
   special-cased tot een blok-titel via `:first-child` i.p.v.
   `[data-label="Model"]`: dat laatste (0,2,0/0,2,1) verloor het na code
   review van de algemene `.zg-cmp .zg-trow td`-regel (0,2,1) en
   `td::before`-regel (0,2,2) — je kunt een `::before` op 0,2,2 niet
   verslaan zonder de projectlimiet `selector-max-specificity: 0,3,0` te
   overschrijden. `.zg-cmp .zg-trow :first-child` (0,3,0, exact op de cap)
   wint wél van `.zg-cmp .zg-trow td` (0,2,1) — geen `::before`-override
   meer nodig, want zonder `data-label` genereert die pseudo-element toch
   al lege content. Ook hier laat `min-width:0` op `.zg-cmp` de tabel weer
   op volle breedte in de wrapper passen: de desktop-`min-width:820px`-druk
   vervalt volledig op dit breakpoint. */
@media (width <= 640px) {
  .zg-cmp {
    display: block;
    min-width: 0;
  }

  .zg-cmp thead {
    display: none;
  }

  .zg-cmp tbody {
    display: block;
  }

  .zg-cmp .zg-trow {
    display: block;
    padding: 18px 18px 6px;
    border-top: 1px solid var(--border);
  }

  .zg-cmp .zg-trow:first-child {
    border-top: 0;
  }

  .zg-cmp .zg-trow td {
    display: flex;
    justify-content: space-between;
    gap: 16px;
    padding: 8px 0;
    border: 0;
    font-size: 0.95rem;
    text-align: right;
  }

  .zg-cmp .zg-trow td::before {
    content: attr(data-label);
    flex: none;
    color: var(--text-subtle);
    font-weight: var(--fw-semibold);
    text-align: left;
  }

  /* `:first-child` i.p.v. een type-selector (`td`) erbij: met `td` zou de
     specificiteit (0,3,1) worden — boven de projectlimiet van 0,3,0
     (selector-max-specificity). Zonder `td` blijft het exact 0,3,0, en dat
     is precies genoeg om de algemene `.zg-cmp .zg-trow td`-regel (0,2,1)
     hierboven te verslaan (nodig, want anders wint bronvolgorde niet de
     juiste kant op). Geen losse `::before{display:none}`-regel meer nodig:
     zonder `data-label` op deze cel (zie render.php) is `content:attr(
     data-label)` sowieso leeg. */
  .zg-cmp .zg-trow :first-child {
    display: block;
    padding: 0 0 6px;
    color: var(--text-heading);
    font-family: var(--font-display);
    font-size: 1.15rem;
    text-align: left;
  }
}

/* ── Koopgids-koopwijzer ──────────────────────────────────────────────────
   "Koopwijzer"-sectie van `zetgoed/koopgids-koopwijzer` (editor-content
   block, Fase 6 Task 7, `eyebrow`/`heading`/`lead`/`image`/`items`-
   attributen — zie src/blocks/koopgids-koopwijzer/render.php voor het
   icoon-sleutel-whitelist-datamodel). 1-op-1 geport uit de "Koopwijzer"-
   sectie van Koopgids.dc.html naar BEM + tokens; de bron-eigen
   `padding:var(--section-y) 0 0` op de `<section>` hoort bij dit block,
   zelfde patroon als `.zg-koopgids-ranglijst` hierboven. */
.zg-koopgids-koopwijzer {
  padding: var(--section-y) 0 0;
}

.zg-koopgids-koopwijzer__grid {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
  align-items: start;
  gap: 64px;
}

/* Sticky sidebar (bron-letterlijk `position:sticky;top:32px;align-self:
   start`) — schakelt op `@media(width<=860px)` hieronder terug naar
   `position:static`, zelfde bron-regel als `.zg-kg-buy-side` in de
   `@media(max-width:860px)`-blok bovenaan dit bestand. */
.zg-koopgids-koopwijzer__side {
  display: flex;
  position: sticky;
  top: 32px;
  flex-direction: column;
  align-self: start;
}

.zg-koopgids-koopwijzer__heading {
  margin: 12px 0 16px;
  font-size: var(--fs-h1);
  line-height: 1.05;
}

.zg-koopgids-koopwijzer__lead {
  max-width: 38ch;
  margin: 0 0 36px;
  color: var(--text-muted);
  font-size: var(--fs-lead);
}

.zg-koopgids-koopwijzer__image {
  display: block;
  width: 100%;
  max-width: 300px;
  height: 280px;
  object-fit: cover;
  border-radius: var(--radius-lg);
}

/* Editor-only placeholder (geen afbeelding-attribuut ingevuld) — zelfde
   patroon als `.zg-koopgids-topkeuze__image--placeholder` hierboven. */
.zg-koopgids-koopwijzer__image--placeholder {
  display: flex;
  width: 100%;
  max-width: 300px;
  height: 280px;
  align-items: center;
  justify-content: center;
  padding: 8px;
  color: var(--text-subtle);
  font-size: var(--fs-xs);
  text-align: center;
  word-break: break-all;
  background: var(--sand-300);
  border-radius: var(--radius-lg);
}

.zg-koopgids-koopwijzer__list {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* Criteria-kaart: bron-eigen `background:var(--bg)` — niet `var(--surface)`
   zoals elders in dit thema (zie de docblock in render.php voor de
   token-afweging), letterlijk overgenomen omdat `--bg` al 1-op-1 bestaat
   in tokens.css. */
.zg-koopgids-koopwijzer__card {
  display: flex;
  align-items: flex-start;
  gap: 22px;
  padding: 24px 28px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}

.zg-koopgids-koopwijzer__icon {
  display: flex;
  flex: none;
  width: 52px;
  height: 52px;
  align-items: center;
  justify-content: center;
  color: var(--accent);
  border: 1.5px solid var(--accent);
  border-radius: var(--radius-pill);
}

.zg-koopgids-koopwijzer__icon svg {
  width: 24px;
  height: 24px;
}

.zg-koopgids-koopwijzer__title {
  margin: 0 0 6px;
  color: var(--text-heading);
  font-family: var(--font-display);
  font-size: var(--fs-h4);
  font-variation-settings: var(--display-vsettings);
}

.zg-koopgids-koopwijzer__text {
  margin: 0;
  color: var(--text-muted);
  line-height: 1.5;
}

/* Editor-only samenvatting (geen bron-equivalent) — zie edit.js voor de
   read-only-preview-afweging. */
.zg-koopgids-koopwijzer__preview-note {
  max-width: 60ch;
  margin: 0 0 12px;
  color: var(--text-muted);
}

.zg-koopgids-koopwijzer__preview-list {
  margin: 0;
  padding-left: 1.2em;
  color: var(--text-muted);
}

/* Responsive: bron `@media(max-width:860px)` op `.zg-kg-buy`/
   `.zg-kg-buy-side` (zie de `@media(max-width:860px)`-blok bovenaan dit
   bestand) — 1 kolom + sidebar niet langer sticky. */
@media (width <= 860px) {
  .zg-koopgids-koopwijzer__grid {
    /* `minmax(0, 1fr)`: een kale `1fr`-track krimpt niet onder de min-content
       van z'n inhoud, en de kop "Waar zit het verschil?" op `--fs-h1` duwde de
       kolom op 375px zo'n 30px buiten de pagina. */
    grid-template-columns: minmax(0, 1fr);
    gap: 32px;
  }

  .zg-koopgids-koopwijzer__side {
    position: static;
    top: auto;
  }
}

/* ── Vergelijker-hero ─────────────────────────────────────────────────────
   Hero van `zetgoed/vergelijker-hero` (data-gebonden block, Fase 7 Task 3,
   zie src/blocks/vergelijker-hero/render.php). 1-op-1 geport uit de
   "Hero + versus (sand band)"-sectie van
   `templates/vergelijker/Vergelijker.dc.html` (Claude Design project
   be0c1087-d8bf-4fd3-b9fa-995e7ea526a0) naar BEM + tokens. De
   reveal-on-scroll-animatie uit de bron (`.zg-reveal`) is bewust NIET
   geport (zelfde afweging als eerdere templates) — alleen de vs-badge-
   scroll-rotatie (assets/js/vergelijker-seal.js, op #zg-vs-seal) blijft
   over.

   Sand-box (achtergrond/radius/padding) is 1-op-1 met `.zg-koopgids-hero`
   hierboven. `.zg-vs-badge`'s bron-schaduw `0 6px 22px rgba(58,46,32,.14)`
   is vervangen door het dichtstbijzijnde schaduw-token (`--shadow-md`,
   `0 8px 24px rgb(58 46 32 / 8%)`) — kleuren strikt via tokens (Stylelint
   `color-no-hex` is hard-fail), geen losse rgba-literal. */
.zg-vergelijker-hero {
  margin-top: 26px;
  margin-bottom: var(--section-y);
  padding: 56px 48px 64px;
  background: var(--sand-100);
  border-radius: var(--radius-xl);
}

.zg-vergelijker-hero__head {
  text-align: center;
}

.zg-vergelijker-hero__title {
  max-width: 20ch;
  margin: 16px auto 18px;
  font-size: 3.2rem;
  line-height: 1.05;
}

.zg-vergelijker-hero__lead {
  max-width: 56ch;
  margin: 0 auto 22px;
  color: var(--text-subtle);
  font-size: var(--fs-lead);
}

.zg-vergelijker-hero__byline {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  color: var(--text-muted);
  font-size: var(--fs-sm);
}

.zg-vergelijker-hero__portrait {
  display: block;
  flex: none;
  width: 34px;
  height: 34px;
  object-fit: cover;

  /* Zie `.zg-overons-hero__photo`: staand portret in een vierkant kader. */
  object-position: 50% 18%;
  border-radius: var(--radius-pill);
}

.zg-vergelijker-hero__portrait--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--sand-300);
  color: var(--text-subtle);
  font-size: var(--fs-xs);
}

/* `.zg-vs-grid`/`.zg-machine`/`.zg-vs-badge` zijn bron-classnamen
   (Vergelijker.dc.html declareert ze zelf al zo, niet als losse
   `.zg-*-hero`-only-scoped varianten) — 1-op-1 overgenomen i.p.v. herdoopt
   naar `.zg-vergelijker-hero__*`, zodat een toekomstige tweede afnemer
   (bv. een compactere versus-widget elders) 'm zonder duplicatie kan
   hergebruiken. */
.zg-vs-grid {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: stretch;
  gap: 36px;
  margin-top: 48px;
}

.zg-machine {
  display: flex;
  flex-direction: column;
  padding: 28px;
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
}

.zg-machine__photo-wrap {
  margin-bottom: 22px;
  background: var(--sand-300);
  border-radius: var(--radius-lg);
}

.zg-machine__photo-wrap--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 300px;
  color: var(--text-subtle);
  font-size: var(--fs-xs);
}

.zg-machine__photo {
  display: block;
  width: 100%;
  height: 300px;
  object-fit: contain;
}

.zg-machine__body {
  margin-top: auto;
  text-align: center;
}

.zg-machine__title {
  margin: 0 0 8px;
  color: var(--text-heading);
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-variation-settings: var(--display-vsettings);
}

.zg-machine__rating {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  margin-bottom: 6px;
}

.zg-machine__stars {
  color: var(--warning);
  letter-spacing: 2px;
}

.zg-machine__score {
  color: var(--text-subtle);
  font-size: var(--fs-sm);
}

.zg-machine__price {
  margin: 0 0 18px;
  color: var(--text-muted);
}

.zg-vs-badge {
  z-index: 3;
  flex: none;
  align-self: center;
  width: 88px;
  height: 88px;
  margin: 0 -44px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--espresso-900);
  color: var(--paper-50);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-md);
  font-family: var(--font-display);
  font-size: 1.6rem;
  font-variation-settings: var(--display-vsettings);
}

/* Bron-breekpunt 820px: `.zg-vs-grid` naar 1 kolom, badge herpositioneert
   naar een horizontale streep tussen de gestapelde kaarten
   (`margin:-33px auto` i.p.v. `margin:0 -44px`). */
@media (width <= 820px) {
  .zg-vs-grid {
    grid-template-columns: 1fr;
  }

  .zg-vs-badge {
    margin: -33px auto;
  }
}

/* ── Vergelijker-intros ───────────────────────────────────────────────────
   Editor-content block `zetgoed/vergelijker-intros` (Fase 7 Task 4, zie
   src/blocks/vergelijker-intros/render.php). 1-op-1 geport uit de
   "Intro's per machine"-sectie van
   `templates/vergelijker/Vergelijker.dc.html` (Claude Design project
   be0c1087-d8bf-4fd3-b9fa-995e7ea526a0) naar BEM + tokens. De
   reveal-on-scroll-animatie uit de bron is bewust NIET geport, zelfde
   afweging als `.zg-vergelijker-hero` hierboven.

   `.zg-intro`/`.zg-intro--flip`/`.zg-intro-media` zijn bron-classnamen
   (Vergelijker.dc.html declareert ze zelf al zo) — 1-op-1 overgenomen
   i.p.v. herdoopt naar `.zg-vergelijker-intros__*`, zelfde afweging als
   `.zg-vs-grid`/`.zg-machine` bij Vergelijker-hero hierboven.
   `__title`/`__text`/`__text--muted` zijn wél thema-eigen BEM-elementen op
   `.zg-intro` (de bron stylet die kop/alinea's inline, zonder classnaam).
   De bron stylet ook het `<img>` binnen `.zg-intro-media` inline
   (`width:100%;height:340px;object-fit:contain`) zonder eigen classnaam;
   hier als `.zg-intro-media__image` (BEM-element, geen `.zg-intro-media
   img`-tagselector) — zelfde `__photo`-op-`__photo-wrap`-precedent als
   `.zg-machine__photo` bij Vergelijker-hero hierboven, en voorkomt een
   `no-descending-specificity`-conflict met de al bestaande `.zg-brand img`/
   `.zg-brand:hover img`-tagselectors verderop in dit bestand.

   Basisregel `.zg-intro-media` staat bewust vóór de compound
   `.zg-intro--flip .zg-intro-media` (die 'm overrulet) — zelfde
   `no-descending-specificity`-volgorde-eis als bij `.zg-media-panel--reverse
   .zg-media-panel__image` hierboven in dit bestand.

   `--fs-body` is 1.125rem (tokens.css), gelijk aan de expliciete
   `font-size:1.125rem` van de bron-lead-alinea — de tweede (gedempte)
   alinea in de bron zet geen eigen font-size en erft dus dezelfde 1.125rem
   via de body-default; `.zg-intro__text--muted` hoeft die daarom niet te
   herhalen, alleen kleur/line-height/margin wijken af. */
.zg-vergelijker-intros {
  display: flex;
  flex-direction: column;
  gap: var(--section-y);
  padding: var(--section-y) 0 0;
}

.zg-intro {
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
  align-items: center;
  gap: 48px;
}

.zg-intro-media {
  background: var(--sand-300);
  border-radius: var(--radius-lg);
}

.zg-intro-media__image {
  display: block;
  width: 100%;
  height: 340px;
  object-fit: contain;
}

.zg-intro--flip .zg-intro-media {
  order: 2;
}

.zg-intro__title {
  margin: 12px 0 16px;
  font-size: var(--fs-h2);
}

.zg-intro__text {
  max-width: 52ch;
  margin: 0 0 18px;
  color: var(--text-subtle);
  line-height: 1.65;
}

.zg-intro__text--muted {
  margin: 0;
  color: var(--text-muted);
  line-height: 1.6;
}

/* Editor-only samenvatting (geen bron-equivalent) — zie edit.js voor de
   read-only-preview-afweging. */
.zg-vergelijker-intros__editor-note {
  max-width: 60ch;
  margin: 0 0 12px;
  color: var(--text-muted);
}

.zg-vergelijker-intros__preview-list {
  margin: 0;
  padding-left: 1.2em;
  color: var(--text-muted);
}

/* Bron-breekpunt 820px: 1 kolom, geflipt beeld terug naar de
   documentvolgorde (order:0) i.p.v. na de tekst. */
@media (width <= 820px) {
  .zg-intro {
    grid-template-columns: 1fr;
  }

  .zg-intro--flip .zg-intro-media {
    order: 0;
  }
}

/* ── Vergelijker-spectabel ────────────────────────────────────────────────
   "Kop aan kop"-sectie van `zetgoed/vergelijker-spectabel` (editor-content
   block, Fase 7 Task 5, zie src/blocks/vergelijker-spectabel/render.php).
   1-op-1 geport uit de "Kop aan kop (spec table)"-sectie van
   `templates/vergelijker/Vergelijker.dc.html` (Claude Design project
   be0c1087-d8bf-4fd3-b9fa-995e7ea526a0) naar BEM + tokens — zie de
   uitgebreide reuse-afweging (t.o.v. `zetgoed/koopgids-vergelijktabel`) in
   de render.php-docblock.

   `.zg-vergelijker-spectabel`/`__inner`: full-bleed sand-band + alignfull,
   zelfde `is-layout-constrained`-probleem/oplossing als `.zg-merk-lineup`
   en `.zg-over-blok--band` hiervoor in dit bestand (zie die docblocks) —
   hier bewust `max-width:1120px` i.p.v. hun 1200px, gelijk aan de bron z'n
   eigen binnenwrapper én aan `contentSize` van single-zg_vergelijker.html.

   `.zg-tablepanel` is BEWUST hier canoniek geïntroduceerd (niet als
   `.zg-vergelijker-spectabel__panel`): de bron gebruikt exact dezelfde
   `.zg-tablepanel`-chrome (espresso-900-achtergrond/radius-xl/padding) voor
   zowel deze spec-tabel als de Puntenkaart erna (Fase 7 Task 6) — Task 6
   hergebruikt deze declaratie i.p.v. 'm te dupliceren (zie reuse.md §1/§4).
   `.zg-spec` zelf (de tabel-inhoud: th/td-stijl, kolombreedtes, hover) is
   NIET gedeeld — de Puntenkaart heeft een eigen `.zg-pk` met numerieke
   kolommen/totaalrij/winnaar-highlight (zie render.php-docblock).

   `#d9b48a` (bron-kopertint voor de niet-eerste-kolom-cellen) → `--sand-400`
   (`#d8bf93`, het dichtstbijzijnde token) — zelfde vervanging als
   `.zg-review-hero__seal`/`.zg-merk-hero__box` hierboven in dit bestand
   (zie die docblocks en render.php hier voor de volledige toelichting).

   Overflow-containment: `.zg-vergelijker-spectabel__scroll` is het #1
   overflow-risico van dit block (full-bleed band + tabel, zie task-brief).
   `overflow-x:auto` op déze wrapper — niet op `.zg-spec` zelf, niet op
   `.zg-tablepanel` — houdt de horizontale scroll van de tabel
   (`min-width:480px`, ruim genoeg voor de langste celtekst zonder geknepen
   te wrappen) volledig BINNEN de wrapper; de wrapper zelf krijgt geen
   breedte groter dan 100%, dus hij drukt de pagina nooit breder (zelfde
   patroon als `.zg-koopgids-vergelijktabel__scroll` hierboven). De bron
   heeft geen expliciete mobiele collapse voor `.zg-spec` (in tegenstelling
   tot `.zg-cmp`) — deze contained scroll is dus de 1-op-1-keuze i.p.v. een
   zelfverzonnen card-collapse.

   Top-margin (Fase 7 Task 9-reconciliatie): de bron zet op déze sectie
   ZOWEL `margin:var(--section-y) 0 0` ALS `padding:var(--section-y) 0`
   (~2× section-y vóór de band) — zelfde patroon als `.zg-vergelijker-rondes`
   hieronder (Task 7) en `.zg-koopgids-vergelijktabel` elders in dit bestand
   (Fase 6 Task 6). Task 5 liet die top-margin destijds weg; hier alsnog
   1-op-1 toegevoegd zodat beide full-bleed sand-banden van deze pagina
   consistent zijn én bron-getrouw. Kale `margin-top` op een full-bleed
   element (`width:100vw;margin-left:calc(50% - 50vw)`) is CSS-veilig: de
   horizontale centrering blijft ongemoeid — geverifieerd: scrollWidth blijft
   gelijk aan innerWidth op alle vier de getoetste viewports (zie
   task-9-report.md). */
.zg-vergelijker-spectabel {
  width: 100vw;
  margin-top: var(--section-y);
  margin-left: calc(50% - 50vw);
  background: var(--sand-100);
  padding: var(--section-y) 0;
}

.zg-vergelijker-spectabel__inner {
  /* content-box: max-width telt hier de CONTENTbreedte, net als
     .zg-hdr__bar en .zg-footer__inner. Zonder deze regel erven de inners
     die als wp:group in een template staan WP's border-box, waardoor hun
     inhoud 24px verder naar binnen valt dan de rest van de pagina. */
  box-sizing: content-box;
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 var(--space-6);
}

.zg-vergelijker-spectabel__heading {
  margin: 12px 0 32px;
  font-size: var(--fs-h1);
}

.zg-tablepanel {
  background: var(--espresso-900);
  border-radius: var(--radius-xl);
  padding: 8px 44px 28px;
}

.zg-vergelijker-spectabel__scroll {
  overflow-x: auto;
}

.zg-spec {
  width: 100%;
  min-width: 480px;
  border-collapse: collapse;
}

/* Scope-prefix `.zg-vergelijker-spectabel` op deze twee th/td-regels
   (i.p.v. kale `.zg-spec th`/`.zg-spec td`) is puur om stylelint's
   `no-descending-specificity` tevreden te houden t.o.v. eerder in dit
   bestand gedefinieerde hogere-specificiteit-selectors op hetzelfde
   element-type (`.zg-srow:first-child th`/`td` en `.zg-koopgids-
   vergelijktabel .zg-cmp thead th`) — zelfde afweging als die laatste
   hierboven (zie die docblock). De th-regel krijgt daarnaast een `thead`-
   type (`.zg-vergelijker-spectabel .zg-spec thead th`, semantisch correct:
   `th` bestaat hier alleen in `<thead>`) om exact op de specificiteit
   (0,2,2) van `.zg-koopgids-vergelijktabel .zg-cmp thead th` te komen —
   zonder die extra type zou (0,2,1) nog steeds dalend zijn. Geen
   functioneel verschil: de klasse-/type-scope is altijd al aanwezig (de
   tabel staat nooit buiten dit block, th nooit buiten thead). `td:first-
   child`/`:not(:first-child)` hieronder krijgen BEWUST geen dubbele
   klasse-prefix: dat zou de projectlimiet `selector-max-specificity: 0,3,0`
   overschrijden (0,3,1) — zie de `thead`/`tbody`-type-toevoeging daar in
   plaats daarvan. */
.zg-vergelijker-spectabel .zg-spec thead th {
  padding: 22px 0 16px;
  color: var(--paper-50);
  font-size: 1.0625rem;
  font-weight: var(--fw-bold);
  text-align: left;
  vertical-align: bottom;
}

.zg-vergelijker-spectabel .zg-spec td {
  padding: 20px 0;
  color: var(--sand-400);
  font-size: 1.0625rem;
  font-weight: var(--fw-regular);
  vertical-align: middle;
  border-top: 1px solid color-mix(in oklab, var(--paper-50) 16%, transparent);
}

.zg-spec td:first-child {
  width: 38%;
  color: var(--paper-50);
  font-weight: var(--fw-bold);
}

/* `thead`/`tbody`-type toegevoegd (i.p.v. een `.zg-vergelijker-spectabel`-
   klasse-prefix) om exact op de projectlimiet `selector-max-specificity:
   0,3,0` te blijven (0,2,2) terwijl de specificiteit toch hoog genoeg is om
   niet-dalend te zijn t.o.v. `.zg-koopgids-vergelijktabel .zg-cmp thead th`
   (0,2,2) hierboven — een klasse-prefix zou hier (0,3,1) worden, over de
   cap. Semantisch correct: `th` bestaat alleen in `<thead>`, `td` alleen in
   `<tbody>` (zie render.php). */
.zg-spec thead th:not(:first-child),
.zg-spec tbody td:not(:first-child) {
  padding-left: 32px;
}

.zg-spec tbody tr {
  transition: background 0.14s ease;
}

.zg-spec tbody tr:hover td {
  background: color-mix(in oklab, var(--paper-50) 5%, transparent);
}

/* ── Vergelijker-puntenkaart ──────────────────────────────────────────────
   "Score per onderdeel"-sectie van `zetgoed/vergelijker-puntenkaart`
   (editor-content block, Fase 7 Task 6, zie
   src/blocks/vergelijker-puntenkaart/render.php). 1-op-1 geport uit de
   "Puntenkaart"-sectie van `templates/vergelijker/Vergelijker.dc.html`
   (Claude Design project be0c1087-d8bf-4fd3-b9fa-995e7ea526a0) naar
   BEM + tokens.

   `.zg-tablepanel` wordt hier HERGEBRUIKT, niet opnieuw gedefinieerd — die
   chrome (espresso-900-achtergrond/radius-xl/padding) is canoniek
   geïntroduceerd bij `.zg-vergelijker-spectabel` hierboven (Task 5), zie
   de docblock daar. Dit block staat (i.t.t. de spectabel) NIET op een
   full-bleed sand-band — de bron plaatst de Puntenkaart-sectie gewoon in
   de constrained content-flow (`padding:var(--section-y) 0 0`), dus geen
   `alignfull`/`__inner`-wrapper nodig hier.

   `#d9b48a` (bron-kopertint van de scorecellen) → `--sand-400` (`#d8bf93`),
   zelfde vervanging als bij `.zg-spec` hierboven (zie die docblock) — het
   dichtstbijzijnde token, geen nieuwe ad-hoc hex.

   Specificiteit-cap (`selector-max-specificity: 0,3,0`, zie
   .stylelintrc.json — lexicografische vergelijking: bv. (0,2,3) < (0,3,0)
   omdat de b-component al lager is, ongeacht c) + stylelint's
   `no-descending-specificity`: die laatste regel vergelijkt selectors
   puur op hun rechter-compound (hier steevast `th`/`td`-varianten),
   ONGEACHT of de klasse-context (`.zg-pk` vs. `.zg-spec`/`.zg-srow`
   elders in dit bestand) daadwerkelijk kan overlappen — zelfde bekende
   valse-positieven-patroon als bij `.zg-vergelijker-spectabel .zg-spec
   thead th` hierboven (zie die docblock). De basis-th/td-regels krijgen
   daarom een `.zg-vergelijker-puntenkaart`-klasse-prefix + een `tr`-type
   (bv. `.zg-vergelijker-puntenkaart .zg-pk tbody tr td`, (0,2,3)) om niet
   onder eerder in het bestand gedefinieerde `td`/`th`-selectors te zakken;
   de `.zg-total`-subregels (`:first-child`/`.zg-num`) laten `.zg-pk` juist
   BEWUST WEG en gebruiken alleen `tbody`/`tr`-type + de klassen zelf (bv.
   `tbody tr.zg-total td:first-child`/`tbody tr.zg-total td.zg-num`,
   (0,2,3)) — met `.zg-pk` erbij zou (0,3,3) ontstaan, over de cap.
   `.zg-total` is uniek genoeg (komt alleen in dit blocks markup voor) om
   zonder `.zg-pk`-prefix veilig te scopen. De `tbody`-type is hier geen
   pure linter-plicht: zonder die derde type-selector landen deze
   `.zg-total`-overrides op (0,2,2) — GELIJK aan, maar niet hoger dan, de
   basisregel `.zg-vergelijker-puntenkaart .zg-pk tbody tr td` (0,2,3), die
   dan zou WINNEN ondanks latere bronvolgorde (specificiteit gaat voor
   volgorde). Zonder de `tbody`-toevoeging bleef de totaalrij-score
   zichtbaar `--sand-400` i.p.v. `--paper-50` — gevonden tijdens live
   verificatie, opgelost door deze regels naar (0,2,3) te tillen (gelijk
   aan de basisregel, en wint dan wél via bronvolgorde).

   `data-highlight`-winnaar-highlight: de bron-selector
   `.zg-pk[data-highlight="true"] td.zg-num.zg-win` heeft specificiteit
   (0,4,1) — boven de cap. `.zg-win` komt in de markup uitsluitend voor op
   `<td class="zg-num zg-win">` (zie render.php: de klasse wordt alleen
   door PHP op basis van de score-vergelijking toegevoegd, nooit uit
   user-input gekopieerd), dus de `td.zg-num`-prefix in de selector is
   overbodig voor correcte matching. De regel hieronder is daarom
   versimpeld tot `.zg-pk[data-highlight="true"] .zg-win`
   (specificiteit (0,3,0), exact op de cap) — functioneel identiek aan de
   bron: bij `highlight:false` (bron-prop `highlightWinners:false`) staat
   `data-highlight="false"` op de tabel en matcht deze regel niet, dus
   blijft de winnaar-kleur uit terwijl de `.zg-win`-klasse (en dus de rest
   van de tabel) ongewijzigd blijft — precies het bron-gedrag. */
.zg-vergelijker-puntenkaart {
  padding: var(--section-y) 0 0;
}

.zg-vergelijker-puntenkaart__heading {
  margin: 12px 0 32px;
  font-size: var(--fs-h1);
}

.zg-pk {
  width: 100%;
  border-collapse: collapse;
}

.zg-vergelijker-puntenkaart .zg-pk thead th {
  padding: 22px 0 18px;
  color: var(--paper-50);
  font-size: 1.0625rem;
  font-weight: var(--fw-bold);
  text-align: left;
}

.zg-pk thead th.zg-num,
.zg-pk tbody td.zg-num {
  width: 120px;
  text-align: right;
}

.zg-vergelijker-puntenkaart .zg-pk tbody tr td {
  padding: 20px 0;
  color: var(--sand-400);
  border-top: 1px solid color-mix(in oklab, var(--paper-50) 16%, transparent);
}

.zg-pk tbody tr td:first-child {
  color: var(--paper-50);
  font-weight: var(--fw-bold);
}

.zg-pk tbody td.zg-num {
  color: var(--sand-400);
  font-family: var(--font-display);
  font-size: 1.35rem;
  font-variation-settings: var(--display-vsettings);
}

.zg-pk thead th:not(:first-child),
.zg-pk tbody tr td:not(:first-child) {
  padding-left: 32px;
}

.zg-pk tbody tr.zg-total td {
  border-top: 2px solid color-mix(in oklab, var(--paper-50) 34%, transparent);
  font-weight: var(--fw-semibold);
}

tbody tr.zg-total td:first-child {
  color: var(--paper-50);
}

tbody tr.zg-total td.zg-num {
  color: var(--paper-50);
  font-size: 1.6rem;
}

.zg-pk[data-highlight="true"] .zg-win {
  color: var(--forest-200);
}

.zg-pk tbody tr {
  transition: background 0.14s ease;
}

.zg-pk tbody tr:hover td {
  background: color-mix(in oklab, var(--paper-50) 5%, transparent);
}

/* ── Vergelijker-rondes ───────────────────────────────────────────────────
   "Waar het verschil zit"-sectie van `zetgoed/vergelijker-rondes`
   (editor-content block, Fase 7 Task 7, zie
   src/blocks/vergelijker-rondes/render.php voor het icoon-sleutel-
   whitelist-datamodel). 1-op-1 geport uit de "Waar het verschil zit"-
   sectie van `templates/vergelijker/Vergelijker.dc.html` (Claude Design
   project be0c1087-d8bf-4fd3-b9fa-995e7ea526a0) naar BEM + tokens.

   `.zg-vergelijker-rondes`/`__inner`: full-bleed sand-band + alignfull,
   zelfde `is-layout-constrained`-probleem/oplossing als
   `.zg-vergelijker-spectabel` hierboven (Task 5, zie die docblock) —
   `max-width:1120px`, gelijk aan de bron z'n eigen binnenwrapper.

   Top-margin (fidelity-kanttekening — zie ook de render.php-docblock): de
   bron zet op déze sectie ZOWEL `margin:var(--section-y) 0 0` ALS
   `padding:var(--section-y) 0` (~2× section-y vóór de band). Task 5 liet
   die top-margin destijds weg; hier WEL 1-op-1 overgenomen omdat de
   opdracht voor deze taak expliciet bron-getrouwheid vraagt. Een kale
   `margin-top` op een full-bleed element (`width:100vw;margin-left:
   calc(50% - 50vw)`) is CSS-veilig: de horizontale centrering blijft
   ongemoeid, alleen de verticale positie t.o.v. de Puntenkaart erboven
   verandert — geen nieuw overflow-risico (geverifieerd: scrollWidth blijft
   gelijk aan innerWidth op alle vier de getoetste viewports, zie
   task-7-report.md). Fase 7 Task 9 (fidelity-pas) heeft Task 5
   gereconcilieerd door dezelfde top-margin daar alsnog toe te voegen (zie
   `.zg-vergelijker-spectabel` hierboven).

   `.zg-diff-grid`: bron-letterlijke `minmax(0,0.82fr) minmax(0,1.18fr)`
   asymmetrische kolomverdeling (smallere sticky intro, bredere kaarten-
   kolom) — NIET een gelijke 1fr/1fr-verdeling.

   Sticky intro: bron-letterlijk `position:sticky;top:40px` (geen
   `align-self:start` nodig zoals bij `.zg-koopgids-koopwijzer__side` —
   deze intro-`<div>` is zelf geen grid-item met een expliciete
   `align-items:stretch`-parent-default om te overschrijven, want
   `.zg-diff-grid` heeft al `align-items:start`, dus het sticky-element
   krijgt van nature al zijn eigen hoogte i.p.v. gerekt te worden). Geen
   losse mobiele "schakel terug naar static"-regel nodig zoals bij
   koopgids-koopwijzer: op `@media(max-width:820px)` hieronder wordt
   `.zg-diff-grid` een 1-koloms flow, waarin `position:sticky` vanzelf
   geen zichtbaar effect meer heeft omdat er geen langere buurkolom meer is
   om langs te scrollen — bron-letterlijk (`Vergelijker.dc.html` voegt ook
   geen aparte static-override toe voor dit block op 820px, i.t.t.
   Koopgids.dc.html's `.zg-kg-buy-side` op 860px).

   Diff-kaart/icoon/verdict-pill: bron-letterlijke tokens — `.zg-diff-card`
   op `--paper-100`/`--radius-lg`, `.zg-diff-ico` een 56px cirkel op
   `--forest-100`/`--forest-600` (groter dan koopgids-koopwijzer's 52px
   cirkel — bewust niet gelijkgetrokken, elke bron-sectie heeft haar eigen
   maat), `.zg-verdict--win` op `--forest-600`/`--paper-50`,
   `.zg-verdict--draw` op `--sand-300`/`--espresso-800`. Geen hex-kleuren
   nodig (i.t.t. `.zg-spec`/`.zg-pk` hierboven, die `#d9b48a` naar
   `--sand-400` vertaalden) — deze sectie's bron-kleuren zijn stuk voor
   stuk al tokens.

   `letter-spacing:0.08em` op `.zg-verdict` is een bron-letterlijke rauwe
   waarde (geen token — bestaand precedent voor rauwe letter-spacing-
   waarden elders in dit bestand, bv. de eyebrow-varianten verderop
   gebruiken wél `--tracking-eyebrow` maar meerdere andere secties in dit
   bestand een eigen rauwe px/em-waarde, zie bv. de `0.08em` bij de
   contentkaarten hierboven).

   `.zg-vergelijker-rondes__icon-key`: GEEN bron-equivalent — editor-only
   meta-badge die de icoon-sleutel toont in de read-only edit.js-preview
   (zie de docblock daar), analoog aan
   `.zg-koopgids-koopwijzer__preview-note`/`__preview-list` hierboven.

   Overflow: geen tabel/horizontale content hier (i.t.t. de spectabel/
   puntenkaart), dus geen `overflow-x:auto`-scrollwrapper nodig — de
   mobiele collapse (`@media(max-width:820px)`) is voldoende om
   `.zg-diff-grid` binnen de viewport te houden. */
.zg-vergelijker-rondes {
  width: 100vw;
  margin-top: var(--section-y);
  margin-left: calc(50% - 50vw);
  padding: var(--section-y) 0;
  background: var(--sand-100);
}

.zg-vergelijker-rondes__inner {
  /* content-box: max-width telt hier de CONTENTbreedte, net als
     .zg-hdr__bar en .zg-footer__inner. Zonder deze regel erven de inners
     die als wp:group in een template staan WP's border-box, waardoor hun
     inhoud 24px verder naar binnen valt dan de rest van de pagina. */
  box-sizing: content-box;
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 var(--space-6);
}

.zg-diff-grid {
  display: grid;
  grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr);
  align-items: start;
  gap: 56px;
}

.zg-vergelijker-rondes__intro {
  position: sticky;
  top: 40px;
}

.zg-vergelijker-rondes__heading {
  max-width: 12ch;
  margin: 12px 0 18px;
  font-size: var(--fs-h1);
}

.zg-vergelijker-rondes__lead {
  max-width: 46ch;
  margin: 0;
  color: var(--text-subtle);
  line-height: 1.65;
}

.zg-vergelijker-rondes__list {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.zg-diff-card {
  display: flex;
  align-items: flex-start;
  gap: 22px;
  padding: 26px 30px;
  background: var(--paper-100);
  border-radius: var(--radius-lg);
}

.zg-diff-ico {
  display: flex;
  flex: none;
  width: 56px;
  height: 56px;
  align-items: center;
  justify-content: center;
  color: var(--forest-600);
  background: var(--forest-100);
  border-radius: var(--radius-pill);
}

.zg-vergelijker-rondes__content {
  flex: 1;
  min-width: 0;
}

/* `flex-wrap:wrap` — GEEN bron-letterlijke regel (de bron zet hier kaal
   `display:flex;align-items:center;justify-content:space-between;gap:16px`
   zonder wrap), maar een noodzakelijke fidelity-correctie: bij smalle
   titels + de langere `.zg-verdict`-pill ("Winnaar Rancilio", nowrap) past
   de rij samen niet meer binnen de 375px-mobiele kaartbreedte — zonder
   wrap duwde de pill de rij (en daarmee de hele sectie) buiten de
   viewport (live gemeten met Chrome DevTools MCP: 73px page-overflow bij
   375px, opgelost door deze wrap + `min-width:0` op de titel hieronder).
   Bij voldoende breedte (desktop/tablet) blijft het resultaat identiek aan
   de bron — titel en pill blijven op één regel, `flex-wrap` heeft daar
   geen zichtbaar effect. */
.zg-vergelijker-rondes__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 16px;
  margin-bottom: 8px;
}

.zg-vergelijker-rondes__title {
  min-width: 0;
  margin: 0;
  color: var(--text-heading);
  font-family: var(--font-display);
  font-size: var(--fs-h4);
  font-variation-settings: var(--display-vsettings);
}

.zg-verdict {
  display: inline-flex;
  flex: none;
  align-items: center;
  gap: 7px;
  padding: 6px 14px;
  white-space: nowrap;
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  border-radius: var(--radius-pill);
}

.zg-verdict--win {
  color: var(--paper-50);
  background: var(--forest-600);
}

.zg-verdict--draw {
  color: var(--espresso-800);
  background: var(--sand-300);
}

.zg-vergelijker-rondes__text {
  margin: 0;
  color: var(--text-subtle);
  line-height: 1.6;
}

/* Editor-only icoon-sleutel-badge — geen bron-equivalent, zie de docblock
   hierboven en edit.js. */
.zg-vergelijker-rondes__icon-key {
  display: inline-block;
  margin-top: 8px;
  padding: 2px 8px;
  color: var(--text-subtle);
  font-size: var(--fs-xs);
  background: var(--sand-200);
  border-radius: var(--radius-sm);
}

/* Bron-breekpunt 820px (zelfde breakpoint als `.zg-intro`/`.zg-vs-grid`
   hierboven in dit bestand): `.zg-diff-grid` naar 1 kolom.

   `minmax(0, 1fr)` — NIET kaal `1fr` (dat is shorthand voor
   `minmax(auto, 1fr)`): met een kale `1fr` bleek de track te sizen op de
   automatische ("auto") minimumbreedte van de grid-items
   (`.zg-vergelijker-rondes__intro`/`__list`), oftewel hun min-content —
   in de praktijk breder dan de beschikbare 375px-viewport (gemeten
   ~454px), wat de sectie liet overflowen. `minmax(0, …)` is exact het
   patroon dat de DESKTOP-kolommen hierboven (`minmax(0,0.82fr)
   minmax(0,1.18fr)`) al gebruiken — hier bewust hetzelfde toegepast i.p.v.
   een kale `1fr`, zelfde reden. Live geverifieerd (Chrome DevTools MCP,
   375px-viewport): zonder deze fix `grid-template-columns` computed naar
   `453.953px` (overflow); met `minmax(0, 1fr)` naar `327px` (exact de
   beschikbare breedte binnen `.zg-vergelijker-rondes__inner`s
   `padding:0 24px`, geen overflow meer). Zie task-7-report.md voor de
   volledige overflow-metingen. */
@media (width <= 820px) {
  .zg-diff-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ── Vergelijker-kies ─────────────────────────────────────────────────────
   "Welke moet je kiezen?"-sectie van `zetgoed/vergelijker-kies`
   (Fase 7 Task 8, zie src/blocks/vergelijker-kies/render.php voor het
   datamodel/vink-icoon). 1-op-1 geport uit de "Welke moet je kiezen"-
   sectie van `templates/vergelijker/Vergelijker.dc.html` (Claude Design
   project be0c1087-d8bf-4fd3-b9fa-995e7ea526a0) naar BEM + tokens.

   GEEN full-bleed sand-band (i.t.t. `.zg-vergelijker-spectabel`/
   `.zg-vergelijker-rondes` hierboven) — de bron zet op déze sectie enkel
   `padding:var(--section-y) 0 0` (top-ritme, geen alignfull/marge-trucs);
   dit is een gewone sectie binnen de bestaande `max-width:1120px`-
   contentkolom van de vergelijker-pagina, net als `.zg-vergelijker-intros`.

   `#e2dcd2` kaartachtergrond (kale hex, `stylelint color-no-hex` verbiedt
   dit): `--stone-100` is het gekozen token en matcht die bron exact —
   zelfde bron-literal en zelfde token als het "De machine kort"-paneel
   (`.zg-panel--sand`, zie die docblock hierboven in dit bestand). Stond
   eerder op `--sand-100`; die substitutie is samen met dat paneel
   gecorrigeerd, zodat de twee niet uit elkaar lopen.

   `.zg-kies-grid`: bron-letterlijke `grid-template-columns:1fr 1fr`
   (`--space-6` = 24px gap, exact de bron-waarde). Op desktop/tablet is een
   kale `1fr 1fr` hier veilig (twee gelijke kolommen, geen sticky-context
   zoals `.zg-diff-grid`); bij `@media(width<=820px)` stackt de sectie
   sowieso naar 1 kolom (zie hieronder) — de `minmax(0, …)`-fix die
   `.zg-diff-grid` hierboven nodig had (Task 7-overflowbug) speelt hier dus
   pas een rol in die 1-koloms media-query zelf.

   Vink-icoon (`.zg-kies-card__icon`): de bron zet `flex:none;
   color:var(--success);margin-top:2px` als inline `style` op het `<svg>`
   zelf — hier verplaatst naar de omringende span-klasse (BEM-discipline,
   geen inline styles), zie de docblock in render.php. */
.zg-vergelijker-kies {
  padding: var(--section-y) 0 0;
}

.zg-vergelijker-kies__intro {
  max-width: 68ch;
  margin: 0 auto;
  text-align: center;
}

.zg-vergelijker-kies__heading {
  margin: 12px 0 14px;
  font-size: var(--fs-h1);
}

.zg-vergelijker-kies__lead {
  margin: 0 0 36px;
  color: var(--text-subtle);
  font-size: var(--fs-body);
  line-height: 1.6;
}

.zg-kies-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-6);
}

.zg-kies-card {
  padding: 32px;
  background: var(--stone-100);
  border-radius: var(--radius-lg);
}

.zg-kies-card__title {
  margin: 0 0 20px;
  color: var(--text-heading);
  font-family: var(--font-display);
  font-size: var(--fs-h4);
  font-variation-settings: var(--display-vsettings);
}

.zg-kies-card__list {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.zg-kies-card__item {
  display: flex;
  align-items: flex-start;
  gap: 12px;
}

.zg-kies-card__icon {
  flex: none;
  margin-top: 2px;
  color: var(--success);
}

.zg-kies-card__text {
  color: var(--text-muted);
}

/* Bron-breekpunt 820px (zelfde breakpoint als `.zg-diff-grid`/`.zg-intro`
   hierboven in dit bestand): `.zg-kies-grid` naar 1 kolom. `minmax(0, 1fr)`
   i.p.v. een kale `1fr` — zelfde Task 7-overflowles als `.zg-diff-grid`
   hierboven: een kale `1fr` sizet op het min-content van de kaart (hier:
   de langste checklist-tekstregel), wat op 375px breder kan zijn dan de
   beschikbare ruimte. */
@media (width <= 820px) {
  .zg-kies-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Overons-hero — Fase 8 Task 2 (src/blocks/overons-hero/render.php). 1-op-1
   geport uit de hero-sectie van `templates/overons/OverZetgoed.dc.html`
   (Claude Design project be0c1087-d8bf-4fd3-b9fa-995e7ea526a0): een
   `--sand-100`-panel (eyebrow/kop/lead + portret) boven een donkere
   (`--espresso-900`) 4-stat-balk met count-up (progressive enhancement,
   zie assets/js/overons-stats.js).

   Eyebrow: gebruikt de nieuwe `.eyebrow--espresso`-modifier (assets/css/
   base.css) — de bron-`.zg-eyebrow` op deze pagina is `var(--espresso-500)`
   i.p.v. het gebruikelijke gedempte `--text-muted`.

   Portret (`.zg-overons-hero__media`): BEWUST geen `.zg-ph` (die bron-class
   is een vast-hoge, geen-`object-fit`-tekst-placeholder — zie o.a.
   `zetgoed/merk-lineup`). Hier komt een echte foto, dus een eigen
   aspect-ratio-4/3-media-class met `object-fit:cover`.

   Accent-stat (`.zg-overons-hero__count--accent`): bron-literal `#d9b48a`
   kan niet (stylelint `color-no-hex`) → `var(--sand-400)`, zelfde
   token-vervanging als elders in dit thema (zie o.a. de `.eyebrow--sand`-
   docblock in base.css). `color-mix(in oklab, var(--paper-50) 16%,
   transparent)` (stat-divider) is een bron-letterlijke mix van een token en
   mag letterlijk (Global Constraints).

   Stat-divider: modifier-class per index (`$index > 0` in render.php)
   i.p.v. een `:not(:first-child)`-selector — houdt de specificiteit laag
   (`selector-max-specificity: 0,3,0`) en is consistent met hoe andere
   blocks in dit thema "alle-behalve-eerste"-styling toepassen via PHP-
   loopindex i.p.v. CSS-structural-pseudo's.

   `.zg-overons-hero__photo` (het `<img>` zelf) is een eigen BEM-element
   i.p.v. een `.zg-overons-hero__media img`-tagselector — zelfde
   `__photo`-op-`__media`-precedent als `.zg-machine__photo`/
   `.zg-intro-media__image` elders in dit bestand, en om dezelfde reden:
   een tagselector op `img` zou hier `no-descending-specificity` breken
   t.o.v. de eerder gedefinieerde `.zg-brand img`/`.zg-brand:hover img`
   (zie die docblock verderop in dit bestand). */
.zg-overons-hero {
  padding: 28px 0 var(--section-y);
}

.zg-overons-hero__wrap {
  background: var(--sand-100);
  border-radius: var(--radius-xl);
  padding: 56px;
}

.zg-overons-hero__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.95fr);
  align-items: start;
  gap: 56px;
  margin-bottom: 44px;
}

.zg-overons-hero__title {
  margin: 14px 0 18px;
  color: var(--text-heading);
  font-size: 3.1rem;
  line-height: 1.05;
  max-width: 14ch;
}

.zg-overons-hero__lead {
  margin: 0;
  max-width: 46ch;
  color: var(--text-subtle);
  font-size: var(--fs-lead);
  line-height: var(--lh-snug);
}

.zg-overons-hero__media {
  overflow: hidden;
  aspect-ratio: 4 / 3;
  border-radius: var(--radius-lg);
}

.zg-overons-hero__photo {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;

  /* Het portret is staand (7:10) en dit kader is liggend (4:3). Met de
     standaard `center`-uitsnede valt het hoofd er bovenlangs uit. Het
     gezicht zit op ongeveer 22% van boven, dus daar richten we de crop op. */
  object-position: 50% 22%;
}

.zg-overons-hero__statbar {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  background: var(--espresso-900);
  border-radius: var(--radius-lg);
  padding: 32px 8px;
}

.zg-overons-hero__stat {
  padding: 0 28px;
  text-align: center;
}

.zg-overons-hero__stat--divider {
  border-left: 1px solid color-mix(in oklab, var(--paper-50) 16%, transparent);
}

.zg-overons-hero__count {
  margin: 0;
  color: var(--paper-50);
  font-family: var(--font-display);
  font-size: 2rem;
  font-variation-settings: var(--display-vsettings);
}

.zg-overons-hero__count--accent {
  color: var(--sand-400);
}

.zg-overons-hero__stat-label {
  margin: 8px 0 0;
  color: var(--paper-100);
  font-size: var(--fs-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.7;
}

/* Bron-breekpunt 900px: hero-grid naar 1 kolom, statbalk naar 2 kolommen
   (de dividers vervallen — een 2x2-grid heeft geen zinvolle
   links-doorlopende rand meer per cel). */
@media (width <= 900px) {
  .zg-overons-hero__grid {
    grid-template-columns: 1fr;
  }

  .zg-overons-hero__statbar {
    grid-template-columns: repeat(2, 1fr);
    gap: 24px 0;
  }

  .zg-overons-hero__stat--divider {
    border-left: none;
  }
}

/* Bron-breekpunt 600px: kleiner panel-padding, kop schaalt terug (en
   verliest z'n `max-width` — de titel mag op smalle schermen de volle
   breedte gebruiken), statbalk krimpt zijn binnenpadding en cijfergrootte. */
@media (width <= 600px) {
  .zg-overons-hero__wrap {
    padding: 26px 20px;
    border-radius: var(--radius-lg);
  }

  .zg-overons-hero__title {
    max-width: none;
    font-size: 2rem;
  }

  .zg-overons-hero__statbar {
    padding: 24px 4px;
  }

  .zg-overons-hero__stat {
    padding: 0 14px;
  }

  .zg-overons-hero__count {
    font-size: 1.6rem;
  }
}

/* Bron-breekpunt 420px: kop schaalt nog een stap terug, statbalk naar 1
   kolom (op de smalste schermen past een 2x2-grid niet meer comfortabel). */
@media (width <= 420px) {
  .zg-overons-hero__title {
    font-size: 1.7rem;
  }

  .zg-overons-hero__statbar {
    grid-template-columns: 1fr;
  }
}

/* ── Overons-waarom ───────────────────────────────────────────────────────
   "Waarom we bestaan"-tekstsectie op /over-ons/ — Fase 8 Task 3
   (kale core-blocks in wordpress/scripts/seed-overons.php, geen custom
   block: de sectie is puur eyebrow + h2 + 2 alinea's). Smalle maatkolom,
   onder-ritme = --section-y (bron: `<section style="max-width:760px;
   padding:0 0 var(--section-y)">`). De eyebrow hergebruikt het gedeelde
   `.eyebrow.eyebrow--espresso`-primitief (Task 2). De kale core-`<h2>`
   pakt `--fs-h2`/`--text-heading` al 1-op-1 via base.css' bare
   h2-selector — geen override nodig. De kale core-`<p>` pakt
   `--fs-body`/`--lh-body` al via `body` (base.css), maar diens kleur is
   ongestyled `--text` (body-tekstkleur) i.p.v. de door de bron gewenste
   `--text-subtle` — vandaar de minimale descendant-regel hieronder.
   `:not(:first-child)` is bewust (i.p.v. `:not(.eyebrow)`, wat de
   `selector-class-pattern`-gate zou breken op de niet-`zg-`-class
   `.eyebrow`): zonder die uitsluiting zou deze regel de eyebrow-`<p>`
   (altijd het eerste kind van de group) z'n `.eyebrow--espresso`-kleur
   overschrijven, want die eyebrow is óók een kale `<p>` in dezelfde
   group. */
.zg-overons-waarom {
  max-width: 760px;
  padding: 0 0 var(--section-y);
}

.zg-overons-waarom p:not(:first-child) {
  color: var(--text-subtle);
}

/* ── Overons-testwerkwijze ────────────────────────────────────────────────
   "Zo testen we"-sectie op /over-ons/ — Fase 8 Task 4
   (src/blocks/overons-testwerkwijze/render.php). Sand-paneel (bron:
   `<section class="zg-work-sec" style="background:var(--surface-sand);
   border-radius:var(--radius-2xl);padding:56px 60px;
   margin-bottom:var(--section-y)">`) met eyebrow (gedeeld
   `.eyebrow.eyebrow--espresso`) + h2 + een genummerde 2-koloms grid van
   6 stappen. `__num` is BEWUST BEM (niet de bare `.zg-num`, die al
   table-scoped is in `.zg-pk`/vergelijker-puntenkaart — zie die docblock
   verderop in dit bestand). */
.zg-overons-testwerkwijze {
  background: var(--surface-sand);
  border-radius: var(--radius-2xl);
  padding: 56px 60px;
  margin-bottom: var(--section-y);
}

.zg-overons-testwerkwijze__heading {
  margin-top: 12px;
  margin-bottom: 40px;
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  color: var(--text-heading);
}

.zg-overons-testwerkwijze__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 32px 56px;
}

.zg-overons-testwerkwijze__step {
  display: flex;
  gap: 18px;
}

.zg-overons-testwerkwijze__num {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-pill);
  background: var(--espresso-900);
  color: var(--paper-50);
  font-weight: 600;
  font-size: 16px;
}

.zg-overons-testwerkwijze__step-body {
  min-width: 0;
}

.zg-overons-testwerkwijze__step-title {
  display: block;
  margin-bottom: 4px;
  color: var(--text-heading);
}

.zg-overons-testwerkwijze__step-text {
  color: var(--text-subtle);
}

/* Bron-breekpunt 900px: grid naar 1 kolom. */
@media (width <= 900px) {
  .zg-overons-testwerkwijze__grid {
    grid-template-columns: 1fr;
  }
}

/* Bron-breekpunt 600px: kleiner paneel-padding, grid-gap krimpt (kolommen
   zijn al 1 door de 900px-regel hierboven). */
@media (width <= 600px) {
  .zg-overons-testwerkwijze {
    padding: 34px 22px;
    border-radius: var(--radius-xl);
  }

  .zg-overons-testwerkwijze__grid {
    gap: 24px 0;
  }
}

/* ── Overons-scorecriteria ────────────────────────────────────────────────
   "Waarop we scoren"-sectie op /over-ons/ — Fase 8 Task 5
   (src/blocks/overons-scorecriteria/render.php). 1-op-1 geport uit de
   "Waarop we scoren"-sectie van `templates/overons/OverZetgoed.dc.html`
   (Claude Design project be0c1087-d8bf-4fd3-b9fa-995e7ea526a0) naar BEM +
   tokens.

   Reuse (audit-reuse-drempel 70% overschreden, dedup vereist): dit block
   hergebruikt de gedeelde `.zg-diff-grid`/`.zg-diff-card`/`.zg-diff-ico`-
   primitieven uit `zetgoed/vergelijker-rondes` hierboven (zie die
   uitgebreide docblock voor het grid-model, de sticky-intro-redenering en
   de 820px-collapse) — GEEN nieuwe grid-CSS hier, alleen de outline-
   modifiers direct hieronder. `.zg-overons-scorecriteria` zelf draagt geen
   layout-CSS (het is de CONSTRAINED wrapper — géén alignfull i.t.t.
   vergelijker-rondes' full-bleed band, deze sectie zit al binnen de
   1160px-`<main>` van page-over-ons.html) — alleen het bodemritme.

   `--outline`-modifiers: de vergelijker-variant is een gevulde
   `--forest-100`/`--forest-600`-cirkel op een `--paper-100`-card zonder
   rand; deze sectie is bron-letterlijk een `--bg`-card MET 1px `--border`
   en een bordered `--accent`-cirkel (52px i.p.v. 56px, `background:none`
   i.p.v. een fill) — vandaar `.zg-diff-card--outline`/`.zg-diff-ico--outline`
   in plaats van de basisklassen te overschrijven: de vergelijker-instantie
   blijft ongewijzigd (basisklassen renderen precies als voorheen, zie de
   regressie-check in de render.php-docblock).

   Eigen BEM (`__intro`/`__heading`/`__lead`/`__content`/`__title`/`__text`)
   i.p.v. de vergelijker-BEM: alleen de grid/card/ico-primitieven zijn
   gedeeld, niet de intro/content-classes (dit block heeft geen
   verdict-datamodel, dus ook geen `__row`/`.zg-verdict`-equivalent).
   `__intro` is bron-letterlijk `position:sticky;top:40px` (zelfde als
   `.zg-vergelijker-rondes__intro`); `__heading`/`__lead` bron-letterlijk
   identiek aan die tegenhangers (12ch/46ch/1.65 line-height) — vandaar
   geen nieuwe onderbouwing hier, zie de vergelijker-rondes-docblock.

   `__icon-key`: GEEN bron-equivalent — editor-only meta-badge (edit.js),
   zelfde patroon als `.zg-vergelijker-rondes__icon-key` hierboven.

   Responsive: `.zg-diff-grid` collapse't al naar 1 kolom op 820px via de
   gedeelde regel hierboven (zelfde class, dus deze instantie profiteert
   automatisch mee) — hier alleen de eigen sticky-intro terugzetten naar
   `static` op diezelfde 820px-breakpoint (i.t.t. vergelijker-rondes, waar
   geen aparte static-override nodig was — zie die docblock; hier WEL
   expliciet, bron-consistent met de opdracht voor deze sectie). */
.zg-overons-scorecriteria {
  margin-bottom: var(--section-y);
}

.zg-diff-card--outline {
  padding: 24px 28px;
  background: var(--bg);
  border: 1px solid var(--border);
}

.zg-diff-ico--outline {
  width: 52px;
  height: 52px;
  color: var(--accent);
  background: none;
  border: 1.5px solid var(--accent);
}

.zg-overons-scorecriteria__intro {
  position: sticky;
  top: 40px;
}

.zg-overons-scorecriteria__heading {
  max-width: 12ch;
  margin: 12px 0 18px;
  font-size: var(--fs-h1);
}

.zg-overons-scorecriteria__lead {
  max-width: 46ch;
  margin: 0;
  color: var(--text-subtle);
  line-height: 1.65;
}

.zg-overons-scorecriteria__list {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.zg-overons-scorecriteria__content {
  flex: 1;
  min-width: 0;
}

.zg-overons-scorecriteria__title {
  margin: 0 0 6px;
  color: var(--text-heading);
  font-family: var(--font-display);
  font-size: var(--fs-h4);
  font-variation-settings: var(--display-vsettings);
}

.zg-overons-scorecriteria__text {
  margin: 0;
  color: var(--text-subtle);
  line-height: 1.6;
}

/* Editor-only icoon-sleutel-badge — geen bron-equivalent, zie de docblock
   hierboven en edit.js. */
.zg-overons-scorecriteria__icon-key {
  display: inline-block;
  margin-top: 8px;
  padding: 2px 8px;
  color: var(--text-subtle);
  font-size: var(--fs-xs);
  background: var(--sand-200);
  border-radius: var(--radius-sm);
}

/* Bron-breekpunt 820px (zelfde breakpoint als de gedeelde `.zg-diff-grid`-
   collapse hierboven): de eigen sticky intro un-sticked: zonder deze regel
   blijft `position:sticky` technisch actief (onschadelijk in een
   1-koloms-flow, zie de vergelijker-rondes-docblock), maar hier expliciet
   teruggezet op `static` zoals de opdracht voor deze sectie vraagt. */
@media (width <= 820px) {
  .zg-overons-scorecriteria__intro {
    position: static;
  }
}

/* ── Overons-transparantie ────────────────────────────────────────────────
   "Hoe we geld verdienen"-sectie op /over-ons/ — Fase 8 Task 6
   (src/blocks/overons-transparantie/render.php). 1-op-1 geport uit de "Hoe
   we geld verdienen"-sectie van `templates/overons/OverZetgoed.dc.html`
   (Claude Design project be0c1087-d8bf-4fd3-b9fa-995e7ea526a0) naar BEM +
   tokens — MINUS de decoratieve `pf-anchor`/`pf-svg`-portafilter-tekening
   (progressive enhancement, zie de render.php-docblock: NIET geport).

   Full-bleed: zelfde `alignfull`-recept als `.zg-vergelijker-rondes`/
   `.zg-vergelijker-spectabel` hierboven (`width:100vw;margin-left:
   calc(50% - 50vw)`) — zie die docblocks voor de volledige
   `is-layout-constrained`-achtergrond. `__inner` centreert zichzelf op
   max-width 1160px (i.p.v. de vergelijker-secties' 1120px: deze pagina's
   `<main>` staat zelf al op contentSize 1160px, zie
   templates/page-over-ons.html) + de bron-letterlijke
   `padding:var(--section-y) 24px` — ANDERS dan de vergelijker-banden (die
   de verticale `--section-y`-padding op de BAND zelf zetten en de inner
   alleen horizontaal padden): de bron van déze sectie zet de padding
   inclusief verticale ritme direct op de gecentreerde binnenwrapper, de
   band zelf draagt alleen de achtergrondkleur + een `margin-bottom`
   (bron-letterlijk, scheidt de band van de eropvolgende auteurskaart-
   sectie).

   `.zg-overons-transparantie__body`: bron-letterlijke tekstkolom
   (`max-width:660px`); de intro-paragraaf krijgt daarbovenop een eigen
   `max-width:60ch` (bron-letterlijk — iets krapper dan 660px bij deze
   font-size, exact zoals de bron beide constraints tegelijk zet).

   `.zg-2col`: bron-letterlijke, NIET block-geprefixte gedeelde
   grid-utility-naam (`grid-template-columns:1fr 1fr;gap:0 56px`) — bewust
   letterlijk overgenomen i.p.v. een eigen `__grid`-BEM-naam, zie de
   opdracht voor deze taak. Nog geen andere afnemer in dit thema; een
   toekomstige tweede sectie met dezelfde 2-koloms-behoefte kan 'm
   hergebruiken.

   `.zg-overons-transparantie__check`: bron-letterlijke checklist-rij
   (`display:flex;align-items:flex-start;gap:12px;padding:16px 0;
   border-bottom:1px solid var(--border)`). Het vink-icoon zelf: zie de
   uitgebreide render.php-docblock voor de token-schone
   `stroke="currentColor"` + `color:var(--accent)`-aanpak (i.p.v. de
   bron-inline `stroke="var(--accent)"`) — zelfde patroon als
   `.zg-kies-card__icon` (`zetgoed/vergelijker-kies` hierboven in dit
   bestand). `svg{flex:none;margin-top:3px}` houdt het icoon verticaal
   uitgelijnd met de eerste tekstregel bij tweeregelige checklist-items.

   Responsive: bron-breekpunt 900px klapt `.zg-2col` naar 1 kolom (i.t.t. de
   vergelijker-/testwerkwijze-secties' 820px — deze sectie's tekstkolom
   (`max-width:660px`) is smaller dan de vergelijker-intro's, dus de bron
   kiest hier bewust een ruimer breekpunt vóór de checklist te vroeg zou
   knellen). Bron-breekpunt 600px: `__inner`-padding krimpt naar
   `48px 20px`, `.zg-2col`-gap blijft `0` (kolommen zijn al 1 door de
   900px-regel), de laatste rij verliest zijn `border-bottom` (geen
   "zwevende" onderrand aan het einde van de lijst) en elke rij krimpt naar
   `padding:14px 0`. */
.zg-overons-transparantie {
  width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-bottom: var(--section-y);
  background: var(--surface-sand);
}

.zg-overons-transparantie__inner {
  /* content-box: max-width telt hier de CONTENTbreedte, net als
     .zg-hdr__bar en .zg-footer__inner. Zonder deze regel erven de inners
     die als wp:group in een template staan WP's border-box, waardoor hun
     inhoud 24px verder naar binnen valt dan de rest van de pagina. */
  box-sizing: content-box;
  max-width: 1280px;
  margin: 0 auto;
  padding: var(--section-y) 24px;
}

.zg-overons-transparantie__body {
  max-width: 660px;
  margin: 0 0 40px;
}

.zg-overons-transparantie__heading {
  margin: 12px 0 16px;
  font-size: var(--fs-h1);
}

.zg-overons-transparantie__intro {
  max-width: 60ch;
  margin: 0;
  color: var(--text-subtle);
  line-height: 1.65;
}

.zg-2col {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 56px;
}

.zg-overons-transparantie__check {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 16px 0;
  border-bottom: 1px solid var(--border);
}

.zg-overons-transparantie__check svg {
  flex: none;
  margin-top: 3px;
  color: var(--accent);
}

.zg-overons-transparantie__check span {
  color: var(--text);
  line-height: 1.5;
}

@media (width <= 900px) {
  .zg-2col {
    grid-template-columns: 1fr;
  }
}

@media (width <= 600px) {
  .zg-overons-transparantie__inner {
    /* Horizontale gutter blijft 24px, gelijk aan de header en de <main>
       van elke template; alleen de verticale waarde is hier mobiel-specifiek. */
    padding: 48px 24px;
  }

  .zg-2col {
    gap: 0;
  }

  .zg-overons-transparantie__check {
    padding: 14px 0;
  }

  .zg-overons-transparantie__check:last-child {
    border-bottom: none;
  }
}

/* Overons-auteur — Fase 8 Task 7 (src/blocks/overons-auteur/render.php).
   1-op-1 geport uit de auteurskaart-sectie van OverZetgoed.dc.html: een
   `--surface-raised`-kaart met een `200px 1fr`-grid (portret links, tekst
   rechts).

   Portret (`.zg-overons-auteur__media`): BEWUST geen `.zg-ph` (die
   bron-class is een vast-hoge tekst-placeholder zonder object-fit-`<img>`)
   — zie de docblock in render.php. `.zg-overons-auteur__photo` (het `<img>`
   zelf) is een eigen BEM-element i.p.v. een `.zg-overons-auteur__media
   img`-tagselector, om dezelfde no-descending-specificity-reden als
   `zetgoed/overons-hero` (zie de docblock daar).

   Eyebrow: hergebruikt de bestaande `.eyebrow.eyebrow--espresso`-modifier
   (assets/css/base.css, Task 2). */
.zg-overons-auteur {
  display: grid;
  grid-template-columns: 200px 1fr;
  align-items: center;
  gap: 44px;
  margin-bottom: var(--section-y);
  padding: 40px;
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-2xl);
}

.zg-overons-auteur__media {
  aspect-ratio: 1 / 1;
  overflow: hidden;
  border-radius: var(--radius-lg);
}

.zg-overons-auteur__photo {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;

  /* Vierkant kader, staand portret: zie `.zg-overons-hero__photo`. */
  object-position: 50% 20%;
}

.zg-overons-auteur__name {
  margin: 8px 0 12px;
  font-family: var(--font-display);
  font-size: 1.6rem;
}

.zg-overons-auteur__bio {
  max-width: 64ch;
  margin: 0 0 1em;
  color: var(--text-subtle);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
}

.zg-overons-auteur__note {
  padding-top: 16px;
  border-top: 1px solid var(--border);
  color: var(--text-muted);
  font-size: 15px;
}

.zg-overons-auteur__link {
  color: var(--link);
  font-weight: 600;
}

@media (width <= 600px) {
  .zg-overons-auteur {
    grid-template-columns: 1fr;
    gap: 24px;
    padding: 28px 22px;
    text-align: center;
  }

  .zg-overons-auteur__media {
    max-width: 180px;
    margin: 0 auto;
  }
}

/* Overons-gerelateerd — Fase 8 Task 8 (src/blocks/overons-gerelateerd/render.php).
   1-op-1 geport uit de "Gerelateerde artikelen"-sectie van
   OverZetgoed.dc.html: een full-bleed `--paper-200`-band (BEWUST anders dan
   `.zg-overons-transparantie` hierboven, die `--surface-sand` gebruikt —
   de bron zet deze laatste pagina-sectie op de paper-achtergrond i.p.v. de
   zand-tint, zie de opdracht voor deze taak) met dezelfde full-bleed-truc
   (`width:100vw;margin-left:calc(50% - 50vw)` + een gecentreerde
   `__inner` op max-width 1160px) als `.zg-overons-transparantie`/
   `.zg-vergelijker-rondes` (zie die docblocks voor de volledige
   `is-layout-constrained`-achtergrond). Deze band is het LAATSTE block op
   de pagina (direct voor de footer) — GEEN `margin-bottom` (i.t.t.
   `.zg-overons-transparantie`/`.zg-overons-auteur` hierboven, die de
   volgende sectie op afstand houden).

   `__head`: bron-letterlijke header-rij (kop + "Bekijk alle posts"-knop,
   `justify-content:space-between` + `flex-wrap:wrap` zodat de knop bij
   smalle viewports onder de kop valt i.p.v. het geheel te comprimeren).

   `.zg-rel-grid`/`.zg-bc*`: gedeelde, NIET block-geprefixte kaart-classes
   (bron-letterlijke naam, "blog-card") — bewust een generieke naam i.p.v.
   `.zg-overons-gerelateerd__card`, zodat een toekomstige blog-index/-archief
   (zie de PLACEHOLDER-docblock in render.php: dit block wordt een
   `WP_Query`-lezer zodra het blog-CPT bestaat) dezelfde kaart-CSS
   hergebruikt i.p.v. een eigen kaart-variant te bouwen — zelfde
   reuse-afweging als `.zg-2col`/`.zg-tablepanel` elders in dit bestand.
   `.zg-bc-media__img` is een eigen BEM-element (geen `.zg-bc-media img`-
   tagselector), zelfde no-descending-specificity-afweging als
   `.zg-overons-auteur__photo` hierboven (zie de render.php-docblock).

   Responsive: bron-breekpunt 900px klapt `.zg-rel-grid` naar 1 kolom met
   een kleinere `gap` (32px i.p.v. 36px) — zelfde breekpunt als
   `.zg-2col` in de transparantie-sectie hierboven. */
.zg-overons-gerelateerd {
  width: 100vw;
  margin-left: calc(50% - 50vw);
  background: var(--paper-200);
}

.zg-overons-gerelateerd__inner {
  /* content-box: max-width telt hier de CONTENTbreedte, net als
     .zg-hdr__bar en .zg-footer__inner. Zonder deze regel erven de inners
     die als wp:group in een template staan WP's border-box, waardoor hun
     inhoud 24px verder naar binnen valt dan de rest van de pagina. */
  box-sizing: content-box;
  max-width: 1280px;
  margin: 0 auto;
  padding: 80px 24px 96px;
}

.zg-overons-gerelateerd__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  margin-bottom: 44px;
}

.zg-rel-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 36px;
}

.zg-bc {
  display: block;
  color: inherit;
  text-decoration: none;
  transition: transform 0.2s ease;
}

.zg-bc:hover {
  transform: translateY(-3px);
}

.zg-bc-media {
  aspect-ratio: 4 / 3;
  overflow: hidden;
  margin-bottom: 16px;
  border-radius: var(--radius-lg);
}

.zg-bc-media__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.zg-bc .zg-badge {
  margin-bottom: 12px;
}

.zg-bc-title {
  margin: 0 0 8px;
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  transition: color 0.2s ease;
}

.zg-bc:hover .zg-bc-title {
  color: var(--accent-press);
}

.zg-bc-date {
  margin: 0;
  color: var(--text-muted);
  font-size: var(--fs-sm);
}

@media (width <= 900px) {
  .zg-rel-grid {
    grid-template-columns: 1fr;
    gap: 32px;
  }
}

/* ── Contact-hero ─────────────────────────────────────────────────────────
   Hero-sectie van /contact/ — Fase 9 Task 2 (src/blocks/contact-hero/
   render.php). 1-op-1 geport uit de hero-sectie van `templates/contact/
   Contact.dc.html` (Claude Design project be0c1087-d8bf-4fd3-b9fa-
   995e7ea526a0): een `--sand-100`-panel (eyebrow/kop/lead + foto) —
   structureel gelijk aan `zetgoed/overons-hero`, maar BEWUST ZONDER diens
   donkere 4-stat-balk (count-up); deze pagina heeft geen statistieken.

   Eyebrow: hergebruikt de bestaande `.eyebrow--espresso`-modifier (assets/
   css/base.css), zelfde als overons-hero.

   Grid-classnaam: BEM `.zg-contact-hero__inner` i.p.v. de bron-literal
   `.zg-hero-inner` (niet block-geprefixt) — consistent met de
   `zg-`-BEM-conventie van dit thema.

   Foto (`.zg-contact-hero__media`): BEWUST geen `.zg-ph` (die bron-class is
   een vast-hoge, geen-`object-fit`-tekst-placeholder — zie o.a.
   `zetgoed/merk-lineup`). `.zg-contact-hero__photo` (het `<img>` zelf) is
   een eigen BEM-element i.p.v. een `.zg-contact-hero__media img`-
   tagselector — zelfde `__photo`-op-`__media`-precedent als
   `.zg-overons-hero__photo` (no-descending-specificity, zie die docblock
   hierboven). */
.zg-contact-hero {
  padding: 28px 0 var(--section-y);
}

.zg-contact-hero__inner {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
  align-items: center;
  gap: 56px;
  background: var(--sand-100);
  border-radius: var(--radius-xl);
  padding: 56px 60px;
}

.zg-contact-hero__title {
  margin: 14px 0 18px;
  overflow-wrap: anywhere;
  color: var(--text-heading);
  font-size: 3.1rem;
  line-height: 1.05;
  max-width: 16ch;
}

.zg-contact-hero__lead {
  margin: 0;
  max-width: 52ch;
  color: var(--text-subtle);
  font-size: var(--fs-lead);
  line-height: var(--lh-snug);
}

.zg-contact-hero__media {
  overflow: hidden;
  aspect-ratio: 4 / 3;
  border-radius: var(--radius-lg);
}

.zg-contact-hero__photo {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Bron-breekpunt 900px: hero-grid naar 1 kolom (zelfde breekpunt als
   overons-hero). */
@media (width <= 900px) {
  .zg-contact-hero__inner {
    grid-template-columns: minmax(0, 1fr);
    gap: 36px;
    padding: 40px;
  }
}

/* ── Form-controls (`.zg-field`/`.zg-control`/`.zg-select-wrap`) ─────────
   Fase 9 Task 3 — 1-op-1 geport uit `components/forms/Input.jsx`
   (Claude Design project be0c1087-d8bf-4fd3-b9fa-995e7ea526a0),
   `rem`-eenheden zoals bron. Thema-breed form-primitief (geen `/contact/`-
   specifieke naam): eerste afnemer is het CF7-formulier "Contact Zetgoed"
   (wordpress/scripts/seed-contact.php, Task 3), maar herbruikbaar voor
   toekomstige formulieren (bv. een keuzehulp).

   `.zg-control` wordt via CF7's `class:zg-control`-form-tag-optie op de
   gegenereerde `<input>`/`<select>`/`<textarea>` gezet. */
.zg-field {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  font-family: var(--font-body);
}

.zg-field__label {
  color: var(--text-heading);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
}

.zg-field__req {
  margin-left: 0.15em;
  color: var(--danger);
}

.zg-control {
  box-sizing: border-box;
  width: 100%;
  padding: 0.7rem 0.9rem;
  border: 1.5px solid var(--border-control);
  border-radius: var(--radius-sm);
  appearance: none;
  background: var(--surface-raised);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: 1.4;
  transition:
    border-color 0.16s ease,
    box-shadow 0.16s ease;
}

.zg-control::placeholder {
  color: var(--text-placeholder);
}

.zg-control:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 var(--ring-width) var(--focus-ring);
}

.zg-control:hover:not(:disabled) {
  border-color: var(--espresso-700);
}

/* stylelint-disable-next-line selector-class-pattern -- ".wpcf7-not-valid" is CF7's eigen vendor-classnaam (plugin-output op invalid velden), niet thema-BEM. */
.zg-control[aria-invalid="true"], .zg-control.wpcf7-not-valid {
  border-color: var(--danger);
}

textarea.zg-control {
  min-height: 7rem;
  resize: vertical;
}

.zg-select-wrap {
  position: relative;
  display: block;
}

.zg-select-wrap select {
  padding-right: 2.4rem;
  cursor: pointer;
}

.zg-select-wrap__chev {
  position: absolute;
  top: 50%;
  right: 0.95rem;
  width: 18px;
  height: 18px;
  color: var(--text-subtle);
  pointer-events: none;
  transform: translateY(-50%);
}

.zg-fieldgrid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 22px;
}

@media (width <= 640px) {
  .zg-fieldgrid {
    grid-template-columns: 1fr;
  }
}

/* CF7-specifieke overrides: CF7 wrapt elk veld in eigen markup
   (`.wpcf7-form-control-wrap`, en voor checkboxen een extra
   `.wpcf7-list-item`-span). De newsletter-checkbox is een BEWUSTE
   deviation t.o.v. de bron-`Checkbox.jsx` (custom `.zg-choice__box` met
   een eigen vink-icoon): CF7's checkbox-form-tag levert geen box-span om
   op te stylen, dus stylen we de native checkbox met `accent-color` —
   clean en toegankelijk (systeem-vinkje, correcte focus-ring/kleur zonder
   extra DOM). */
/* stylelint-disable-next-line selector-class-pattern -- ".wpcf7-list-item" is CF7's eigen vendor-classnaam. */
.zg-newsletter .wpcf7-list-item {
  margin: 0;
}

.zg-newsletter input[type="checkbox"] {
  width: 1.2rem;
  height: 1.2rem;
  margin-right: 0.6rem;
  accent-color: var(--accent);
  vertical-align: -0.25em;
}

.zg-form-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 20px;
  margin-top: 4px;
}

.zg-form-note {
  margin: 0;
  max-width: 26ch;
  color: var(--text-muted);
  font-size: 14px;
}

/* Submit-knop: CF7's `[submit class:zg-btn class:zg-btn--primary
   class:zg-btn--lg]` levert `<input type="submit" class="zg-btn
   zg-btn--primary zg-btn--lg …">` — `.zg-btn` (Button-sectie hierboven)
   zet al `cursor:pointer`/`font-family`/padding, dus dat werkt on
   `<input>` zonder extra CSS. */

/* CF7-response-output (succes/fout-melding onder het formulier). Deze
   zichtbare `.wpcf7-response-output` is zelf `aria-hidden="true"`; CF7 zet
   de voorleesbare melding in een APARTE `.screen-reader-response`-div met
   `<p role="status" aria-live="polite">` (dus SR-users horen de melding
   wél). Bewuste deviation t.o.v. de bron (die na verzenden een volledige
   panel-swap toont): CF7 rendert in plaats daarvan deze inline melding,
   die we hieronder on-brand stylen (groen succes / terracotta fout).

   Specificity-noot: CF7's eigen `includes/css/styles.css` (geladen VÓÓR
   components.css, bevestigd via de `<link>`-volgorde) zet de border-color
   al via `.wpcf7 form.sent .wpcf7-response-output` / `.wpcf7
   form.invalid .wpcf7-response-output` / `.wpcf7 form.failed
   .wpcf7-response-output` (elk 3 classes + 1 type = specificity (0,3,1)).
   Onze descendant-selectors hieronder hebben daarom ook de `.wpcf7`-
   ancestor nodig (zelfde specificity) om via bron-volgorde (wij laatst) te
   winnen — zonder die prefix blijft CF7's amber/rood/groen-default
   zichtbaar i.p.v. onze forest/terracotta-tokens (geverifieerd: zonder
   `.wpcf7`-prefix bleef `border-color` op CF7's `#ffb900`/`#dc3232`
   hangen, ondanks dat `background`/`color` — die CF7 niet zelf zet — al
   wél via onze regel doorkwamen).

   Dode-classnamen-noot: `.wpcf7-mail-sent-ok`/`.wpcf7-validation-
   errors`/`.wpcf7-mail-sent-ng` komen in de geïnstalleerde CF7-versie
   (6.1.6) NERGENS voor in de plugin-output (geverifieerd: geen enkele
   match in de plugin-source) — CF7 zet alleen de status-class
   (`sent`/`invalid`/`failed`/…) op het `<form>`-element zelf, nooit op
   `.wpcf7-response-output`. Ze blijven staan (bron-verbatim / forward-
   compat), maar de daadwerkelijk werkende selectors zijn de
   `.wpcf7 form.<status> .wpcf7-response-output`-varianten hieronder. */
/* stylelint-disable selector-class-pattern, selector-max-specificity --
   ".wpcf7-response-output" is CF7's eigen vendor-classnaam; de
   `.wpcf7 form`-ancestor-prefix (0,2,1) is nodig om CF7's eigen even
   specifieke default `.wpcf7 form .wpcf7-response-output` (margin 2em /
   padding 0.2em, styles.css) te overschrijven — zonder prefix wint CF7 op
   specificiteit en rendert onze spacing nooit. */
.wpcf7 form .wpcf7-response-output {
  margin: 18px 0 0;
  padding: 14px 18px;
  border-radius: var(--radius-sm);
  font-size: var(--fs-sm);
}
/* stylelint-enable selector-class-pattern, selector-max-specificity */

/* stylelint-disable selector-class-pattern, selector-max-specificity --
   CF7-vendor-classnamen (".wpcf7-mail-sent-ok"/".sent"/".wpcf7-response-
   output"), niet thema-BEM; de `.wpcf7`-ancestor-prefix is nodig om CF7's
   eigen even specifieke default-border-color te kunnen overschrijven (zie
   de specificity-noot hierboven), wat de (0,3,0)-cap overschrijdt. */
.wpcf7-mail-sent-ok, .wpcf7 form.sent .wpcf7-response-output {
  border: 1px solid var(--forest-200);
  background: var(--forest-100);
  color: var(--forest-700);
}
/* stylelint-enable selector-class-pattern, selector-max-specificity */

/* stylelint-disable selector-class-pattern, selector-max-specificity --
   CF7-vendor-classnamen (validatie-/fout-states: ".wpcf7-validation-
   errors"/".wpcf7-mail-sent-ng"/".invalid"/".failed"/".wpcf7-response-
   output"), niet thema-BEM; zelfde `.wpcf7`-specificity-noodzaak als
   hierboven. */
.wpcf7-validation-errors,
.wpcf7-mail-sent-ng,
.wpcf7 form.invalid .wpcf7-response-output,
.wpcf7 form.failed .wpcf7-response-output {
  border: 1px solid color-mix(in oklab, var(--danger) 40%, transparent);
  background: color-mix(in oklab, var(--danger) 12%, transparent);
  color: var(--danger);
}
/* stylelint-enable selector-class-pattern, selector-max-specificity */

/* stylelint-disable-next-line selector-class-pattern -- ".wpcf7-not-valid-
   tip" is CF7's eigen vendor-classnaam (inline veld-foutmelding). */
.wpcf7-not-valid-tip {
  margin-top: 0.35rem;
  color: var(--danger);
  font-size: var(--fs-xs);
}

/* ── Contact-formulier ────────────────────────────────────────────────────
   2-koloms sectie van /contact/ — Fase 9 Task 4 (src/blocks/contact-
   formulier/render.php). Links een form-card (surface-raised) die het CF7-
   formulier embed (form-control-CSS zie hierboven, Task 3), rechts een
   kanalen-aside (3 contactkanalen + een mini-CTA naar de keuzehulp).

   `.zg-chan-ico`: gedeelde `zetgoed_icon()`-wrapper is 26px (zie inc/
   icons.php) — hier CSS-verkleind naar de bron-22px via de `svg`-regel
   hieronder i.p.v. een aparte icon-maat in de whitelist zelf (die blijft
   26px voor de andere afnemers, vergelijker-rondes/overons-scorecriteria). */
.zg-contact-formulier {
  padding: 0 0 var(--section-y);
}

.zg-contact-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 0.85fr);
  gap: 64px;
  align-items: start;
}

.zg-form-card {
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-2xl);
  padding: 44px;
}

.zg-form-card__title {
  margin: 0 0 24px;
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  color: var(--text-heading);
}

/* Aside-kop "Direct contact": bron `<h2 style="font-size:var(--fs-h3);
   margin:12px 0 8px">`. Zonder deze override valt de kale <h2> terug op
   base.css' `--fs-h2` (te groot t.o.v. de form-card-kop). */
.zg-contact-aside__heading {
  margin: 12px 0 8px;
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  color: var(--text-heading);
}

.zg-chan {
  display: flex;
  gap: 18px;
  align-items: flex-start;
  padding: 22px 0;
  border-bottom: 1px solid var(--border);
}

.zg-chan:last-child {
  border-bottom: none;
}

.zg-chan-ico {
  flex: none;
  width: 48px;
  height: 48px;
  border-radius: var(--radius-pill);
  background: var(--forest-100);
  color: var(--forest-600);
  display: flex;
  align-items: center;
  justify-content: center;
}

.zg-chan-ico svg {
  width: 22px;
  height: 22px;
}

.zg-chan b {
  display: block;
  font-weight: 600;
  font-size: 17px;
  color: var(--text-heading);
  margin-bottom: 4px;
}

/* `.zg-contact-formulier`-prefix (i.p.v. kaal `.zg-chan p`): stylelints
   `no-descending-specificity` vergelijkt puur op de `p`-tagselector t.o.v.
   de eerder in dit bestand gedefinieerde `.zg-overons-waarom p:not(:first-
   child)` — zelfde bekende valse-positieve-patroon als elders in dit
   bestand (zie bv. de docblock bij `.zg-vergelijker-puntenkaart` hierboven).
   De wrapper-klasse-prefix tilt de specificiteit gelijk aan/boven die regel
   zonder de selector-betekenis te wijzigen (`.zg-contact-formulier` omvat
   `.zg-chan` altijd, zie render.php). */
.zg-contact-formulier .zg-chan p {
  margin: 0;
  font-size: 15px;
  color: var(--text-subtle);
  line-height: 1.5;
}

.zg-chan a {
  color: var(--link);
}

.zg-mini-cta {
  display: block;
  background: var(--surface-sand);
  border-radius: var(--radius-lg);
  padding: 26px;
  text-decoration: none;
  margin-top: 28px;
  transition: transform 0.16s ease;
}

.zg-mini-cta:hover {
  transform: translateY(-2px);
}

.zg-mini-cta h4 {
  font-family: var(--font-display);
  font-size: 1.3rem;
  color: var(--text-heading);
  margin: 0 0 8px;
}

/* Zelfde `.zg-contact-formulier`-prefix-reden als bij `.zg-chan p`
   hierboven (no-descending-specificity t.o.v. `.zg-overons-waarom
   p:not(:first-child)`). */
.zg-contact-formulier .zg-mini-cta p {
  font-size: 15px;
  color: var(--text-subtle);
  line-height: 1.5;
  margin: 0 0 14px;
}

.zg-mini-cta__go {
  font-size: 16px;
  font-weight: 600;
  color: var(--link);
}

/* Bron-breekpunt 900px: contact-grid naar 1 kolom (zelfde breekpunt als
   `.zg-contact-hero__inner`). */
@media (width <= 900px) {
  .zg-contact-grid {
    grid-template-columns: 1fr;
    gap: 44px;
  }
}

/* ── Contact-FAQ ──────────────────────────────────────────────────────────
   Sectiekop ("Voordat je schrijft" + "Veelgestelde vragen") boven het
   gedeelde `zetgoed/faq`-block op de contactpagina (Fase 9 Task 5). BEWUST
   een EIGEN class i.p.v. het `.zg-faq-section`-precedent uit Fase 4b
   (archive-zg_machine.html, hierboven): die class staat op `--fs-h1` (de
   grotere archief-kop), terwijl de contact-bron een `--fs-h2`-kop toont +
   de sectie zelf max-width:820px is (smaller dan de default constrained
   contentSize). `__title` zet alleen de margin — het font-size blijft de
   kale `<h2>`-default (`--fs-h2`, base.css) omdat de bron-kop daar al
   1-op-1 mee overeenkomt, geen override nodig. */
.zg-contact-faq {
  max-width: 820px;
  padding: 0 0 var(--section-y);
}

.zg-contact-faq__title {
  margin: 16px 0 28px;
}

/* ── Blog — journaal-hero ─────────────────────────────────────────────────
   `zetgoed/blog-hero` — de hero-band bovenaan het blog-overzicht
   (`templates/home.html`, Fase 10a Task 2). 1-op-1 geport uit de
   hero-sectie van `templates/blogoverzicht/BlogOverzicht.dc.html`.

   BEWUST `.zg-blog-hero` i.p.v. de bron-literal `.zg-hero`: die naam is te
   generiek (elders al impliciet gebruikt door andere hero-varianten) —
   hier een eigen, ondubbelzinnige BEM-naam.

   Media (`.zg-blog-hero__media`) hergebruikt de gedeelde `.zg-ph`-
   placeholder (hatch + `img{object-fit:cover}` + `.zg-ph-lbl`-pill, zie de
   `.zg-ph`-reconcile hierboven in dit bestand, Fase 10a Task 1) — hier
   alleen de blok-eigen afmeting (`aspect-ratio:16/9` + `border-radius`). */
.zg-blog-hero {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  gap: 40px;
  padding: 56px;
  background: var(--surface-sand);
  border-radius: var(--radius-2xl);
}

.zg-blog-hero__title {
  margin: 0 0 var(--space-3);
  font-family: var(--font-display);
  font-size: 3.6rem;
  line-height: 1.04;
  color: var(--text-heading);
  font-variation-settings: var(--display-vsettings);
}

.zg-blog-hero__lead {
  max-width: 40ch;
  margin: 0;
  font-size: var(--fs-lead);
  color: var(--text-subtle);
  line-height: 1.55;
}

.zg-blog-hero__media {
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-lg);
}

/* Bron-breekpunt 840px: 1 kolom + kleinere padding. */
@media (width <= 840px) {
  .zg-blog-hero {
    grid-template-columns: 1fr;
    padding: 36px;
  }
}

/* ── Blog — uitgelicht artikel ────────────────────────────────────────────
   `zetgoed/blog-featured` — het uitgelichte artikel (sticky/nieuwste post)
   op het blog-overzicht (`templates/home.html`, Fase 10a Task 3), ná de
   journaal-hero. 1-op-1 geport uit de "uitgelicht"-sectie van
   `templates/blogoverzicht/BlogOverzicht.dc.html`.

   Geen `alignfull`: het block leeft in de constrained `<main>` net als
   `.zg-blog-hero`. `.zg-blog-featured` zet alleen de sectie-top-ruimte —
   het grid zelf staat in `.zg-feat` (bron-classnaam, hier letterlijk
   aangehouden omdat de sectie geen andere afnemer heeft die 'm zou
   verwarren met een andere kaart-vorm).

   Media (`.zg-feat-media`) hergebruikt dezelfde `.zg-ph`-placeholder als
   `.zg-blog-hero__media` (hatch + `img{object-fit:cover}` +
   `.zg-ph-lbl`-pill, Fase 10a Task 1) — hier vierkant (`aspect-ratio:1/1`)
   i.p.v. 16:9.

   CTA: geen nieuwe accent-modifier — de knop hergebruikt het bestaande
   `.zg-btn-accent-wrap`-primitief (zie de "Button"-sectie hierboven),
   zelfde patroon als `zetgoed/cta-band`/`zetgoed/over-blok`/
   `zetgoed/review-byline`/`zetgoed/merk-verhaal`. */
.zg-blog-featured {
  padding: var(--section-y) 0 0;
}

.zg-feat {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  gap: 56px;
}

.zg-feat-media {
  aspect-ratio: 1 / 1;
  border-radius: var(--radius-lg);
}

.zg-blog-featured__title {
  margin: var(--space-3) 0;
  font-family: var(--font-display);
  font-size: 3.1rem;
  line-height: 1.06;
  color: var(--text-heading);
  font-variation-settings: var(--display-vsettings);
}

.zg-blog-featured__title a {
  color: inherit;
  text-decoration: none;
}

.zg-blog-featured__title a:hover {
  text-decoration: underline;
}

.zg-blog-featured__date {
  margin: 0 0 var(--space-3);
  font-size: 15px;
  color: var(--text-muted);
}

.zg-blog-featured__lead {
  max-width: 46ch;
  margin: 0 0 var(--space-5);
  font-size: var(--fs-lead);
  color: var(--text-subtle);
  line-height: 1.6;
}

/* Bron-breekpunt 840px: 1 kolom + kleinere gap; media 16:10 i.p.v. vierkant. */
@media (width <= 840px) {
  .zg-feat {
    grid-template-columns: 1fr;
    gap: 28px;
  }

  .zg-feat-media {
    aspect-ratio: 16 / 10;
  }
}

/* ── Blog — post-hero ─────────────────────────────────────────────────────
   `zetgoed/blog-post-hero` — de gecentreerde hero-band bovenaan een single
   blogpost (`templates/single.html`, Fase 10b Task 1). 1-op-1 geport uit de
   hero-sectie van `templates/blogpost/BlogPost.dc.html`.

   Data-gebonden (geen editoriale attributen, zie render.php): badge/titel/
   datum/featured image komen van de HUIDIGE post. Titel-styling
   (`color`/`font-variation-settings`) volgt dezelfde conventie als
   `.zg-blog-hero__title`/`.zg-blog-featured__title` hierboven.

   Media (`.zg-blog-post-hero__media`) hergebruikt de gedeelde `.zg-ph`-
   placeholder (hatch + `img{object-fit:cover}` + `.zg-ph-lbl`-pill, zie de
   `.zg-ph`-reconcile, Fase 10a Task 1) — hier breed (`aspect-ratio:16/9`)
   zoals `.zg-blog-hero__media`. */
.zg-blog-post-hero {
  padding: 56px 48px;

  /* De leeskolom eronder begon op het kale block-gap van 24px, waardoor de
     eerste alinea aan de zandkleurige hero vastplakte. Een artikel hoort met
     wat lucht te beginnen. */
  margin-bottom: 64px;
  text-align: center;
  background: var(--surface-sand);
  border-radius: var(--radius-2xl);
}

.zg-blog-post-hero__title {
  /* 16ch (~550px) brak deze koppen in vier korte regels midden op een verder
     lege band. 24ch komt op ~830px uit en valt daarmee samen met de
     `.zg-body`-leeskolom van 820px eronder, dus de kop loopt niet breder dan
     de tekst die erop volgt. */
  max-width: 24ch;

  /* Lange titelwoorden mogen breken: de kop staat op 3.6rem en past bij
     smalle schermen niet binnen de leeskolom. Zonder dit duwde 'm het hele
     document 19px opzij (gemeten @360px). Zelfde remedie als
     .zg-keuzehulp__title en .zg-koopgids-ranglijst__heading. */
  overflow-wrap: break-word;
  margin: 26px auto 20px;
  font-family: var(--font-display);
  font-size: 3.6rem;
  line-height: 1.05;
  color: var(--text-heading);
  font-variation-settings: var(--display-vsettings);
}

@media (width <= 640px) {
  .zg-blog-post-hero__title {
    /* 16ch is op deze breedte breder dan de leeskolom; laat de kop de
       beschikbare ruimte volgen in plaats van 'm te forceren. */
    max-width: none;
    font-size: 2.2rem;
  }
}

.zg-blog-post-hero__date {
  margin: 0 0 44px;
  font-size: 15px;
  color: var(--text-muted);
}

.zg-blog-post-hero__media {
  /* Breder dan de 16/9 van `.zg-blog-hero__media`: op 1280px werd dat een
     beeld van 720px hoog, waardoor je op een gemiddeld scherm eerst alleen
     de hero zag en geen woord van het artikel. */
  aspect-ratio: 21 / 9;
  border-radius: var(--radius-lg);
}

/* ── Blog — kaartgrid + paginatie ─────────────────────────────────────────
   `zetgoed/blog-grid` — het gepagineerde blog-kaartgrid, gedeeld door de
   posts-pagina (`/blog/`, `templates/home.html`, Fase 10a Task 4) en
   categorie-archieven (`/category/x/`, `templates/category.html`, Task 6).
   1-op-1 geport uit de "grid"-sectie van `templates/blogoverzicht/
   BlogOverzicht.dc.html`. Task 5 wikkelt dit block (samen met
   `zetgoed/blog-toolbar`) in de full-bleed `--paper-200`-band
   (`.zg-blog-band`) — dit block zelf is GEEN `alignfull`.

   `.zg-grid`: dit classname bestaat al (hierboven, review-archive-sectie:
   `repeat(3,minmax(0,1fr));gap:var(--space-6)` met EIGEN breekpunten
   1080/620/400px voor het machine-grid). De bron-blog-grid wil een eigen
   `gap:32px` + eigen breekpunten (1000px→2 kolommen, 640px→1 kolom) — een
   kale her-declaratie van `.zg-grid` zou een `no-duplicate-selectors`-
   conflict geven ÉN (erger) op specificiteit winnen van review-archive's
   regel omdat CSS bij gelijke specificiteit de laatst-gedefinieerde regel
   laat winnen, wat `/machines/` zou breken. Daarom hieronder een
   ANCESTOR-scoped override (`.zg-blog-grid .zg-grid`): de 3-koloms
   basisregel (`display:grid;grid-template-columns:repeat(3,…)`) blijft
   gedeeld/ongewijzigd vanuit de review-archive-regel (identieke
   kolomwaarde), alleen `gap` en de kleinere-viewport-kolomaantallen worden
   hier, binnen de scope van dit block, overschreven.

   `.zg-bc--blog`: modifier op het bestaande `.zg-bc`-kaart-primitief (zie
   hierboven bij "Related/Blog-kaart" — canoniek vanuit
   `zetgoed/overons-gerelateerd`, 4/3-media). De basis-`.zg-bc` heeft al
   geen achtergrond/rand/padding (het is al een kale `<a>`-reset), dus de
   `background/border/padding/text-align`-reset hieronder is een expliciete,
   niet-destructieve bevestiging (geen gedrags-wijziging op de bestaande
   4/3-afnemer) — regressie-check `/over-ons/`. Alleen de media-aspect-ratio
   (1/1 i.p.v. 4/3) en een iets grotere `margin-bottom` zijn een echte
   override, en die staan bewust ONDER de `.zg-bc--blog`-modifier-selector
   i.p.v. in de gedeelde `.zg-bc-media`-regel.

   `.zg-pager`: WP-core `paginate_links( ['type' => 'array'] )` genereert
   `<a class="page-numbers">`/`<span class="page-numbers current">` (en
   `<span class="page-numbers dots">` voor "…") — hier gestyled als pil-
   knoppen i.p.v. een eigen `.zg-page`-classname op de gegenereerde markup
   te plakken (fragiele string-surgery, zie de docblock in render.php).
   `.page-numbers`/`.current`/`.dots` zijn WP-core-vendor-classnamen, geen
   thema-BEM — vandaar de `stylelint-disable selector-class-pattern`-
   annotaties, zelfde toegestane patroon als de CF7-vendor-classnamen
   verderop in dit bestand. Bewust een ANDERE pager-styling dan het
   bestaande `.zg-page` (assets/css/components.css, review-archive-sectie:
   44px/`--radius-md`, nog-niet-functionele statische paginatie) — deze
   pager is de eerste ECHTE, functionele paginatie in het theme en volgt de
   bron-maten (46px/`--radius-pill`) 1-op-1 i.p.v. het bestaande, kleinere
   placeholder-patroon te hergebruiken. */
.zg-blog-grid .zg-grid {
  gap: 32px;
}

@media (width <= 1000px) {
  .zg-blog-grid .zg-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (width <= 640px) {
  .zg-blog-grid .zg-grid {
    grid-template-columns: 1fr;
  }
}

.zg-bc--blog {
  background: none;
  border: none;
  padding: 0;
  text-align: left;
}

.zg-bc--blog .zg-bc-media {
  aspect-ratio: 1 / 1;
  margin-bottom: 20px;
}

.zg-pager {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 10px;
  padding: 52px 0 0;
}

/* stylelint-disable selector-class-pattern, selector-max-specificity --
   ".page-numbers"/".current"/".dots" zijn WP-core `paginate_links()`-
   vendor-classnamen, geen thema-BEM (zie de docblock hierboven). */
.zg-pager .page-numbers {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 46px;
  height: 46px;
  padding: 0 8px;
  font-family: var(--font-body);
  font-size: var(--fs-body);
  color: var(--text-subtle);
  background: var(--surface-raised);
  border: 1.5px solid var(--border-control);
  border-radius: var(--radius-pill);
  text-decoration: none;
  transition:
    background 0.14s ease,
    color 0.14s ease,
    border-color 0.14s ease;
}

.zg-pager .page-numbers:hover {
  color: var(--text-heading);
  border-color: var(--espresso-700);
}

.zg-pager .page-numbers.current {
  color: var(--paper-50);
  background: var(--espresso-900);
  border-color: var(--espresso-900);
}

.zg-pager .page-numbers.dots {
  border-color: transparent;
  background: none;
  cursor: default;
}
/* stylelint-enable selector-class-pattern, selector-max-specificity */

/* Lege staat: geen resultaten in de huidige context (bv. een categorie
   zonder posts). Bron-"Nog geen artikelen"-kaart. */
.zg-blog-empty {
  padding: 72px 24px;
  text-align: center;
  background: var(--surface);
  border: 1px dashed var(--border-control);
  border-radius: var(--radius-xl);
}

.zg-blog-empty__title {
  margin: 0 0 var(--space-3);
  font-family: var(--font-display);
}

.zg-blog-empty__desc {
  max-width: 46ch;
  margin: 0 auto;
  color: var(--text-subtle);
}

/* ── Blog — full-bleed toolbar/grid-band ──────────────────────────────────
   `.zg-blog-band` wikkelt `zetgoed/blog-toolbar` + `zetgoed/blog-grid`
   samen in de bron-`paper-200`-band op het blog-overzicht (`templates/
   home.html`, Fase 10a Task 5) en categorie-archieven (`templates/
   category.html`, Task 6). 1-op-1 geport uit `templates/blogoverzicht/
   BlogOverzicht.dc.html`.

   Full-bleed: hier GEEN block-`render.php` (zoals `zetgoed/overons-
   transparantie`/`zetgoed/vergelijker-rondes`, zie de docblocks daar) maar
   een kaal `wp:group` in het TEMPLATE-bestand zelf, om twee losse blocks
   (toolbar + grid) samen in één band te wikkelen. Zelfde onderliggende
   `is-layout-constrained`-probleem/oplossing: `<main>` staat op
   `layout:{type:constrained,contentSize:1160px}`, dus WP forceert
   `margin-left/right:auto` op elke child zónder `.alignleft`/
   `.alignright`/`.alignfull` — vandaar de kale `alignfull`-class op de
   groep in home.html/category.html (geen `"align":"full"`-attribuut, dat
   zou de Site-editor een alignment-toolbar geven die niets anders dan
   "full" mag zijn). De binnenwrapper (`.zg-blog-band__inner`) is een
   TWEEDE geneste `wp:group` (géén `render.php`-`__inner`-div) die zichzelf
   op max-width 1160px + horizontale padding centreert — 1160px = dezelfde
   `contentSize` als `<main>` zelf, zodat de band-inhoud verticaal precies
   even breed is als de hero/featured erboven.

   Overflow-kritisch (zie de taakbrief): `width:100vw;margin-left:calc(50%
   - 50vw)` mag NOOIT horizontale scroll veroorzaken — geverifieerd op
   1440/1280px (`scrollWidth === innerWidth`). */
.zg-blog-band {
  width: 100vw;
  margin-top: var(--section-y);
  margin-left: calc(50% - 50vw);
  padding: 24px 0 72px;
  background: var(--paper-200);
}

.zg-blog-band__inner {
  /* content-box: max-width telt hier de CONTENTbreedte, net als
     .zg-hdr__bar en .zg-footer__inner. Zonder deze regel erven de inners
     die als wp:group in een template staan WP's border-box, waardoor hun
     inhoud 24px verder naar binnen valt dan de rest van de pagina. */
  box-sizing: content-box;
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 24px;
}

/* ── Blog — toolbar (categorie-chips + zoek + sorteer) ────────────────────
   `zetgoed/blog-toolbar` — de balk bovenaan `.zg-blog-band` (categorie-
   chips als LINKS + een zoek-link + een sorteer-`<select>`). 1-op-1
   geport uit de "toolbar"-sectie van `templates/blogoverzicht/
   BlogOverzicht.dc.html`.

   `.zg-chip[aria-current="page"]`: de actieve-staat-styling-hook. De bron
   gebruikt `[aria-pressed="true"]` op `<button>`-toggles (client-side
   filter, zie `zetgoed/review-archive`'s `.zg-fchip`); hier zijn de chips
   ECHTE links naar `/blog/`/`/category/x/` (server-side filter, zie
   render.php), dus `aria-current="page"` — de correcte WAI-ARIA-staat voor
   "link naar de huidige pagina" binnen een groep navigatie-links. Zelfde
   visuele uitkomst (espresso-900-vulling), andere/correctere selector.

   `.zg-search`/`.zg-sort`: bron-maten 46×46 pil (i.p.v. `.zg-resbar__
   select`'s losse hoogte hierboven bij review-archive) — eigen classnamen
   omdat dit een andere balk is met een eigen bron-layout (chips LINKS,
   zoek+sorteer RECHTS i.p.v. review-archive's teller+sorteer-only). */
.zg-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 44px 0 40px;
}

.zg-toolbar__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

.zg-toolbar__tools {
  display: flex;
  align-items: center;
  gap: 14px;
}

.zg-chip {
  padding: 9px 20px;
  font-family: var(--font-body);
  font-size: 17px;
  color: var(--text-subtle);
  text-decoration: none;
  background: transparent;
  border: 1.5px solid var(--border-control);
  border-radius: var(--radius-pill);
  cursor: pointer;
  transition:
    background 0.14s ease,
    color 0.14s ease,
    border-color 0.14s ease;
}

.zg-chip:hover {
  color: var(--text-heading);
  border-color: var(--espresso-700);
}

.zg-chip[aria-current="page"] {
  color: var(--paper-50);
  background: var(--espresso-900);
  border-color: var(--espresso-900);
}

.zg-search {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  color: var(--text-subtle);
  background: var(--surface-raised);
  border: 1.5px solid var(--border-control);
  border-radius: var(--radius-pill);
  transition:
    background 0.14s ease,
    color 0.14s ease,
    border-color 0.14s ease;
}

.zg-search:hover {
  color: var(--text-heading);
  border-color: var(--espresso-700);
}

.zg-sort-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
}

.zg-sort {
  padding: 0 44px 0 20px;
  height: 46px;
  font-family: var(--font-body);
  font-size: var(--fs-body);
  font-weight: var(--fw-semibold);
  color: var(--text-heading);
  background: var(--surface-raised);
  appearance: none;
  border: 1.5px solid var(--border-control);
  border-radius: var(--radius-pill);
  cursor: pointer;
}

.zg-sort-ico {
  position: absolute;
  right: 18px;
  color: var(--text-subtle);
  pointer-events: none;
}

/* Bron-breekpunt 640px: chips-rij + zoek/sorteer-rij onder elkaar,
   volledige breedte. */
@media (width <= 640px) {
  .zg-toolbar {
    flex-direction: column;
    align-items: stretch;
  }
}

/* ── Blog — categorie-archief compacte kop ────────────────────────────────
   `templates/category.html` (Fase 10a Task 6): categorie-archieven tonen
   GEEN journaal-hero/uitgelicht-artikel (dat hoort bij de posts-pagina,
   `templates/home.html`) — alleen een compacte kop boven de gedeelde
   `.zg-blog-band` (toolbar+grid, hierboven). Core `wp:query-title
   {"type":"archive","showPrefix":false}` rendert de kale termnaam (bv.
   "Herkomst") i.p.v. de WP-default "Categorie: Herkomst"-prefix —
   `showPrefix:false` is de block-eigen manier om dat compacter te maken,
   geen PHP-override nodig. Minimale, token-based styling hier — geen
   globale `.wp-block-query-title`-herstijl, scoped op de wrapper-groep.
   `.wp-block-query-title` is een WP-core block-classname (`core/
   query-title`'s vaste `style` handle), geen thema-BEM — vandaar de
   `stylelint-disable selector-class-pattern`, zelfde toegestane patroon
   als `.zg-pager .page-numbers` hierboven. */
.zg-blog-archive-heading {
  padding: 40px 0 0;
}

/* stylelint-disable-next-line selector-class-pattern -- WP-core `core/query-title`-vendor-classname. */
.zg-blog-archive-heading .wp-block-query-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--fs-h1);
  line-height: 1.04;
  color: var(--text-heading);
}

@media (width <= 640px) {
  /* stylelint-disable-next-line selector-class-pattern -- WP-core `core/query-title`-vendor-classname. */
  .zg-blog-archive-heading .wp-block-query-title {
    font-size: var(--fs-h2);
  }
}

/* ── Blog — leeskolom (prose) ─────────────────────────────────────────────
   De rijke body van een single blogpost (`templates/single.html`, Fase 10b
   Task 2) leeft in `post_content` — idempotent geseed als core-blocks
   binnen één `wp:group {"className":"zg-body"}` (`seed-blog.php`, de
   Colombia-post). 1-op-1 geport uit de leeskolom-sectie van `templates/
   blogpost/BlogPost.dc.html` (`.zg-body`/`.zg-h2`/`.zg-p`/`.zg-fig`/
   `.zg-fig-sq`/`.zg-two`/`.zg-bleed`).

   Scoping-keuze: eerst geprobeerd als `.zg-body`-gescoped DESCENDANT-
   selectors (`.zg-body h2`/`.zg-body p`, minder className-ruis in
   `post_content`), maar stylelint's `no-descending-specificity` brak op
   `.zg-body p` t.o.v. de bestaande `.zg-overons-waarom p:not(:first-child)`
   (hoger in dit bestand, hogere specificiteit) — een kale `p`-
   elementselector met lagere specificiteit ná die regel is een
   cascade-valkuil. Daarom, zoals de brief voorschrijft, teruggevallen op
   de bron-letterlijke expliciete classNames `.zg-h2`/`.zg-p` (1-op-1 met
   `BlogPost.dc.html`, waar elke kop/alinea al zo'n className draagt) —
   die worden in de seed als core-heading/core-paragraph-`className` gezet.

   `.zg-fig`/`.zg-fig-sq` zijn aspect-ratio-modifiers bovenop de gedeelde
   `.zg-ph`-placeholder (zie de `.zg-ph`-reconcile, Fase 10a Task 1) — breed
   (16/10) resp. vierkant (1/1) voor de in-tekst-beelden. `.zg-two` is het
   2-koloms beeldduo ("handen & filter" / "fermentatievat"); breekt naar 1
   kolom onder 640px (bron-breekpunt).

   `.zg-bleed` laat een beeld iets breder zijn dan de 820px-leeskolom via
   negatieve marges (bron-commentaar: "iets breder dan de leeskolom,
   gecentreerd"). OVERFLOW-KRITISCH: onder 1060px is de pagina zelf al
   smaller dan de volle bleed-breedte zou worden, dus reset de bron
   `.zg-bleed` daar naar 100%/geen negatieve marge — zonder die reset zou
   de negatieve marge op smalle viewports horizontale scroll veroorzaken. */
.zg-body {
  max-width: 820px;
  margin: 0 auto;
}

.zg-h2 {
  margin: 0 0 22px;
  font-family: var(--font-display);
  font-size: 2.5rem;
  line-height: 1.08;
  color: var(--text-heading);
  font-variation-settings: var(--display-vsettings);
}

.zg-p {
  margin: 0 0 20px;
  font-family: var(--font-body);
  font-size: var(--fs-lead);
  color: var(--text-subtle);
  line-height: 1.7;
}

.zg-fig {
  aspect-ratio: 16 / 10;
  border-radius: var(--radius-lg);
}

.zg-fig-sq {
  aspect-ratio: 1 / 1;
  border-radius: var(--radius-lg);
}

.zg-two {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 24px;
}

@media (width <= 640px) {
  .zg-two {
    grid-template-columns: 1fr;
  }
}

.zg-bleed {
  width: calc(100% + 240px);
  margin-right: -120px;
  margin-left: -120px;
}

@media (width <= 1060px) {
  .zg-bleed {
    width: 100%;
    margin-right: 0;
    margin-left: 0;
  }
}

/* ── Blog — featurelist (bulleted opsomming) ──────────────────────────────
   `zetgoed/blog-featurelist` (Fase 10b Task 3) — de "Wat Colombiaanse
   koffie bijzonder maakt"-opsomming in de leeskolom: vetgedrukte term +
   beschrijving, met een custom ronde bullet (`::before`, geen
   list-style/marker). 1-op-1 geport uit de `.zg-list`-sectie van
   `templates/blogpost/BlogPost.dc.html`.

   `.zg-blog-featurelist` is de block-brede wrapper-klasse (op hetzelfde
   `<ul>`-element als `.zg-list` — zie de toelichting in render.php) en
   draagt alleen de bron-`margin-bottom:var(--section-y)`; `.zg-list` zelf
   is de generieke lijst-styling (geen `.zg-blog-featurelist`-afhankelijkheid
   opgenomen mocht deze ooit elders herbruikt worden). */
.zg-list {
  display: flex;
  flex-direction: column;
  gap: 22px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.zg-list li {
  position: relative;
  padding-left: 26px;
}

.zg-list li::before {
  content: "";
  position: absolute;
  top: 11px;
  left: 4px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--espresso-700);
}

.zg-list b {
  display: block;
  margin-bottom: 5px;
  font-family: var(--font-body);
  font-size: 19px;
  font-weight: 600;
  color: var(--text-heading);
}

.zg-list span {
  display: block;
  font-family: var(--font-body);
  font-size: 17px;
  color: var(--text-subtle);
  line-height: 1.55;
}

.zg-blog-featurelist {
  margin-bottom: var(--section-y);
}

/* ── Blog — quote-tegel (core-quote-styling) ──────────────────────────────
   Fase 10b Task 3: styling van een core `wp:quote {"className":"zg-blog-
   quote"}` (GEEN nieuw block — zie de brief) tot de sand-quote-tegel uit
   `templates/blogpost/BlogPost.dc.html` (`.zg-quote`). Core-quote rendert
   `<blockquote class="wp-block-quote zg-blog-quote"><p>…</p><cite>…</cite>
   </blockquote>` — de bron gebruikt een `<footer>` voor de attributie,
   maar de core-quote-block gebruikt `<cite>`; de "—"-prefix zit in de
   seed-tekst zelf (zie seed-blog.php). `border:none` overschrijft WP-core's
   standaard `.wp-block-quote`-linkerrand.

   `.zg-body .zg-blog-quote p` (i.p.v. kaal `.zg-blog-quote p`): zelfde
   bekende `no-descending-specificity`-valse-positief t.o.v. de eerder in
   dit bestand gedefinieerde `.zg-overons-waarom p:not(:first-child)` als
   bij `.zg-chan p`/`.zg-mini-cta p` hierboven — de `.zg-body`-prefix tilt
   de specificiteit gelijk aan die regel zonder de selector-betekenis te
   wijzigen (de quote-tegel zit altijd binnen `.zg-body`, zie seed-blog.php). */
.zg-blog-quote {
  margin: 0 0 var(--section-y);
  padding: 44px 52px;
  background: var(--surface-sand);
  border: none;
  border-radius: var(--radius-xl);
}

.zg-body .zg-blog-quote p {
  margin: 0 0 22px;
  font-family: var(--font-display);
  font-size: 1.9rem;
  line-height: 1.3;
  color: var(--text-heading);
  font-variation-settings: var(--display-vsettings);
}

.zg-blog-quote cite {
  font-family: var(--font-body);
  font-size: 16px;
  color: var(--text-subtle);
  font-style: normal;
}

@media (width <= 640px) {
  .zg-blog-quote {
    padding: 32px 28px;
  }
}

/* ── Blog — video-still (statisch, GEEN speler) ───────────────────────────
   Fase 10b Task 4: `zetgoed/blog-video`. 1-op-1 geport uit de `.zg-video`-
   sectie van `templates/blogpost/BlogPost.dc.html`, met twee bewuste
   afwijkingen (zie render.php-docblock voor de a11y-redenering):
   - Geen `cursor:pointer` en geen `:hover .zg-play{transform:scale(...)}`
     (de bron heeft die) — dit is een NIET-interactief decoratief figuur,
     dus geen hover-affordance die suggereert dat er iets klikbaars is.
   - De bron zet `fill="var(--espresso-900)"` rechtstreeks op het
     play-`<svg>` (geen geldige CSS-custom-property-context op een
     presentatie-attribuut); hier is dat `fill="currentColor"` +
     `.zg-play svg{color:...}` — token-schoon, stylelint-`color-no-hex`-
     proof, en herbruikbaar mocht de knop later kleuren.

   `.zg-video` en `.zg-blog-video` zitten op HETZELFDE element (de
   block-wrapper is tegelijk de bron-`.zg-ph.zg-video`-placeholder) — zelfde
   twee-klassen-op-één-element-patroon als `.zg-list.zg-blog-featurelist`
   hierboven. `.zg-blog-video` draagt alleen de bron-`margin-bottom:40px`. */
.zg-video {
  position: relative;
  border-radius: var(--radius-lg);
  aspect-ratio: 16 / 9;
}

.zg-blog-video {
  margin-bottom: 40px;
}

.zg-play {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 76px;
  height: 76px;
  margin: auto;
  background: var(--paper-50);
  border-radius: 50%;
  box-shadow: var(--shadow-md);
}

.zg-play svg {
  color: var(--espresso-900);
}

/* ── Blog — deelbalk ───────────────────────────────────────────────────────
   Fase 10b Task 4: `zetgoed/blog-share`. 1-op-1 geport uit de "Deelbalk"-
   sectie van `templates/blogpost/BlogPost.dc.html` (de bron gebruikt hier
   alleen inline styles + de al-bestaande `.zg-social`-iconklasse — geen
   eigen genoemde wrapperklasse — dus `.zg-blog-share`/`.zg-blog-share__
   label`/`.zg-blog-share__links` zijn een nieuwe BEM-naamgeving voor de
   wrapper, 1-op-1 dezelfde bron-waarden). `.zg-social` (het ronde
   icoon-element) is de bron-letterlijke classnaam, hier hergebruikt door
   `zetgoed/blog-share`'s render.php — de eerste (en enige) afnemer. */
.zg-blog-share {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-top: 52px;
  padding-top: 28px;
  border-top: 1px solid var(--divider);
}

.zg-blog-share__label {
  font-family: var(--font-body);
  font-size: 16px;
  color: var(--text-muted);
}

.zg-blog-share__links {
  display: flex;
  gap: 12px;
}

.zg-social {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  color: var(--text-subtle);
  text-decoration: none;
  border: 1.5px solid var(--border-control);
  border-radius: 50%;
  transition:
    border-color 0.14s ease,
    color 0.14s ease,
    background 0.14s ease;
}

.zg-social:hover {
  color: var(--espresso-900);
  background: var(--paper-100);
  border-color: var(--espresso-900);
}

/* ── Keuzehulp — intro + quiz-mount + disclaimer ─────────────────────────
   Fase 11 Task 2: `zetgoed/keuzehulp` — de statische chrome van de
   `/keuzehulp/`-pagina (1-op-1 geport uit
   `templates/keuzehulp/Keuzehulp.dc.html`). Dit block rendert ALLEEN een
   gecentreerd 880px-paneel (sage-eyebrow-badge + kop + lead) en een leeg
   quiz-mountpoint; de eigenlijke quiz (voortgang/vraagkaarten/resultaat,
   klasses `.qz-*`) is client-side JS die pas in Task 3 wordt gebouwd —
   `.qz-app` hieronder krijgt daarom alleen een `min-height` (tegen
   layout-shift vóórdat de JS het mountpoint vult) en verder niets.
   `.qz-app` is de bron-letterlijke mountpoint-classnaam (net als
   `.qz-opt`/`.qz-optgrid`/etc. die in Task 3 volgen) — bewust NIET
   `zg-`-geprefixt voor traceerbaarheid naar de bron (zelfde afweging als
   `.zg-diff-*`/`.zg-bc` eerder), dus `selector-class-pattern` hieronder
   bewust uitgeschakeld voor deze ene regel. */
.zg-keuzehulp__panel {
  max-width: 880px;
  margin: 0 auto;
  padding: 32px 0 12px;
  text-align: center;
}

.zg-keuzehulp__title {
  max-width: 24ch;
  margin: 22px auto 16px;
  overflow-wrap: break-word;
  font-family: var(--font-display);
  font-size: var(--fs-h1);
  line-height: var(--lh-tight);
}

/* Op smalle schermen past het langste woord ("espressomachine") niet op
   één regel bij de volle `--fs-h1`, waardoor het lelijk mid-woord afbrak
   (de oude `overflow-wrap: anywhere` deed dat expres om overflow te
   voorkomen). De bron breekt op de spatie: verklein de kop iets zodat het
   woord past — geen mid-woord-breuk én geen horizontale overflow. */
@media (width <= 430px) {
  .zg-keuzehulp__title {
    font-size: 2.25rem;
  }
}

.zg-keuzehulp__lead {
  max-width: 52ch;
  margin: 0 auto 40px;
  font-size: var(--fs-lead);
  line-height: var(--lh-body);
  color: var(--text-subtle);
}

/* stylelint-disable-next-line selector-class-pattern -- bron-letterlijke
   mountpoint-classnaam (`.qz-app`), traceerbaarheid naar Keuzehulp.dc.html
   en de JS die het element selecteert (zie assets/js/keuzehulp.js). */
.qz-app {
  min-height: 400px;
}

.zg-keuzehulp__noscript {
  font-size: var(--fs-sm);
  line-height: var(--lh-body);
  color: var(--text-subtle);
}

/* Geen eigen `margin-bottom`: de disclaimer sluit de keuzehulp-pagina af en de
   ruimte naar de footer komt van het pagina-onderritme op `main` (zie
   "Pagina-onderritme" bij de footer), anders telt die er dubbel bovenop. */
.zg-keuzehulp__disclaimer {
  max-width: 880px;
  margin: 24px auto 0;
  font-size: var(--fs-xs);
  text-align: center;
  color: var(--text-muted);
}

/* ── Merkoverzicht — hero + resultaatbalk ─────────────────────────────────
   Fase 12 Task 2 (`zetgoed/merkoverzicht-hero`, src/blocks/merkoverzicht-
   hero/render.php). 1-op-1 geport uit de hero-sectie van `templates/
   merkoverzicht/Merkoverzicht.dc.html` (Claude Design project be0c1087-…)
   naar BEM + tokens — zelfde `padding-top`/titel/lead-opzet als de
   archief-hero hierboven (".zg-archive-hero"), hier een eigen namespace
   omdat dit wél een block is (met attribute-defaults) i.p.v. template-only
   markup.

   Eyebrow hergebruikt het gedeelde `.eyebrow`/`.eyebrow--terracotta`-
   primitief (zie base.css), zelfde afweging als elders in dit bestand.

   Resultaatbalk: hergebruikt `.zg-resbar`/`__text`/`__sort`/`__sort-wrap`/
   `__select`/`__select-ico` (geïntroduceerd door `zetgoed/review-archive`
   hierboven) — functioneel identieke teller+sorteer-select-structuur, geen
   tweede bijna-identieke CSS-set. De bron geeft déze resultaatbalk een
   `margin-top`/`border-top`/andere `padding-bottom` dan de review-archive-
   balk (die staat direct onder een filter-sidebar, deze onder een hero) —
   dat verschil leeft in de additieve scoping-class hieronder i.p.v. de
   gedeelde `.zg-resbar`-regel zelf aan te passen (zelfde patroon als
   `.zg-model-eyebrow` bovenop `.eyebrow--terracotta`, zie inc/merk-cards.php). */
.zg-merkoverzicht-hero {
  padding-top: 34px;
}

.zg-merkoverzicht-hero__title {
  max-width: 17ch;
  margin: 14px 0 18px;
  font-size: 3.4rem;
  line-height: 1.04;
}

.zg-merkoverzicht-hero__lead {
  max-width: 56ch;
  margin: 0;
  font-size: var(--fs-lead);
  color: var(--text-subtle);
}

.zg-merkoverzicht-hero__resultaatbalk {
  margin-top: 36px;
  padding-top: 28px;
  padding-bottom: 8px;
  border-top: 1px solid var(--border);
}

@media (width <= 640px) {
  .zg-merkoverzicht-hero__title {
    font-size: 2rem;
  }

  .zg-merkoverzicht-hero__lead {
    font-size: 1.05rem;
  }
}

/* ── Merkoverzicht — merk-kaartgrid ───────────────────────────────────────
   Fase 12 Task 2 (`zetgoed/merkoverzicht-grid`, src/blocks/merkoverzicht-
   grid/render.php). 1-op-1 geport uit de "Merkgrid"-sectie (`.zg-grid` +
   `.zg-bc*`) van Merkoverzicht.dc.html naar BEM + tokens, met TWEE
   hernoemingen t.o.v. de bron:

   1. `.zg-bc`/`.zg-bc-*` → `.zg-brandcard`/`__media`/`__badge`/`__body`/
      `__name`/`__desc`/`__stats`/`__stat-value`/`__stat-label`/`__foot`: de
      bron-naam is al in gebruik door de BLOG-kaart (`.zg-bc`, zie de
      "Related reviews"/blog-archief-secties elders in dit bestand) — een
      structureel ander component (aspect-ratio-beeld + titel + datum, geen
      sand-tegel/badge/stats/foot-cta). Hergebruik van de bron-naam zou de
      blog-kaart-CSS overschrijven/verwarren.
   2. bron-`.zg-grid` → `.zg-brandcard-grid`: die generieke naam is al
      elders in dit bestand herbruikt (`zetgoed/review-archive`, hierboven)
      met ANDERE breakpoints (620px→2-kol, 400px→1-kol) dan deze pagina
      vraagt (1000px→2-kol, 560px→1-kol, zie het fase-12-plan) — dezelfde
      selector met twee tegenstrijdige media-query-sets in hetzelfde
      bestand zou onvoorspelbaar zijn.

   `__stat-value`/`__stat-label` i.p.v. de bron-letterlijke kale `.v`/`.l`
   (niet-BEM, te generiek voor een globaal stylesheet) — zelfde
   BEM-vertaling als `.zg-merk-hero__stat-value`/`-label` elders in dit
   bestand.

   `.zg-brandcard__logo` (het `<img>` zelf) is een eigen BEM-element i.p.v.
   een `.zg-brandcard__media img`-tagselector — zelfde `__logo`-op-`__media`-
   precedent als `.zg-overons-hero__photo`/`.zg-machine__photo` elders in
   dit bestand: een tagselector op `img` zou hier `no-descending-
   specificity` breken t.o.v. de eerder gedefinieerde `.zg-brand img`/
   `.zg-brand:hover img` (zie die docblock verderop in dit bestand).

   CTA "Bekijk merk →" hergebruikt het gedeelde `.zg-model-cta`-primitief
   (introduced door `zetgoed/merk-lineup`, zie hierboven) — bron gebruikt
   daar zelf ook al deze exacte classnaam voor eenzelfde soort terracotta-
   pijl-link, dus geen derde bijna-identieke CTA-stijl. */
.zg-brandcard-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 24px;
}

@media (width <= 1000px) {
  .zg-brandcard-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (width <= 560px) {
  .zg-brandcard-grid {
    grid-template-columns: 1fr;
  }
}

.zg-brandcard {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  color: inherit;
  text-decoration: none;
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  transition:
    transform 0.16s ease,
    box-shadow 0.16s ease,
    border-color 0.16s ease;
}

.zg-brandcard:hover {
  color: inherit;
  text-decoration: none;
  transform: translateY(-3px);
  box-shadow: var(--shadow-md);
  border-color: var(--border-control);
}

.zg-brandcard__media {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 150px;
  padding: 24px;
  background: var(--sand-100);
  border-bottom: 1px solid var(--border);
}

.zg-brandcard__logo {
  display: block;
  width: auto;
  max-width: 78%;
  max-height: 38px;
  object-fit: contain;
}

.zg-brandcard__badge {
  position: absolute;
  top: 16px;
  left: 16px;
}

.zg-brandcard__body {
  display: flex;
  flex: 1;
  flex-direction: column;
  padding: 26px;
}

.zg-brandcard__name {
  margin: 8px 0 10px;
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  color: var(--text-heading);
  font-variation-settings: var(--display-vsettings);
}

.zg-brandcard__desc {
  /* `flex:1` laat de omschrijving de resterende hoogte opnemen, zodat de
     statsrij eronder op elke kaart op dezelfde hoogte begint. Zonder dit
     schoof hij mee met de lengte van de tekst: bij een merk met een korte
     omschrijving stond "Modellen getest" 28px hoger dan bij de buurkaart,
     terwijl "Bekijk merk" er wél al netjes onder uitlijnde. */
  flex: 1;
  margin: 0 0 22px;
  font-size: var(--fs-body);
  line-height: 1.55;
  color: var(--text-muted);
}

/* Twee gelijke kolommen in plaats van een flexrij met een vaste gap. Zo
   stonden "4 / Modellen getest" en "€399-999 / Prijsklasse" links tegen
   elkaar aan met een gat van ruim honderd pixels ernaast, en verschoof de
   prijskolom bovendien mee met de breedte van het label ernaast: bij een merk
   met twee cijfers stond hij ergens anders dan bij een merk met één. Met
   `1fr 1fr` krijgt elk cijfer z'n eigen helft en staan ze op elke kaart op
   dezelfde plek. `minmax(0, …)` zodat een lange prijsklasse de kolom niet
   breder duwt dan de kaart. */
.zg-brandcard__stats {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 26px;
  margin-bottom: 22px;
}

.zg-brandcard__stat-value {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.3rem;
  color: var(--text-heading);
  font-variation-settings: var(--display-vsettings);
}

.zg-brandcard__stat-label {
  margin: 3px 0 0;
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.zg-brandcard__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: auto;
  padding-top: 6px;
}

/* Bron `.zg-root h3{font-size:1.35rem!important}` @640: een blanket-regel
   die ELKE h3 binnen de pagina verkleint (hier: de merknaam, --fs-h3 =
   1.5rem op desktop). Geport als scoped class-override i.p.v. een generieke
   `h3`-tagselector (die zou ook de werkwijze-kaarttitels raken, welke een
   ANDER — hoger — mobiel doel hebben, zie die docblock verderop) en zonder
   `!important` (stylelint-verbod): dezelfde selector, later gedeclareerd,
   wint al op cascade. */
@media (width <= 640px) {
  .zg-brandcard__name {
    font-size: 1.35rem;
  }
}

/* ── Uitgelicht merk ──────────────────────────────────────────────────────
   Fase 12 Task 3 (`zetgoed/uitgelicht-merk`, src/blocks/uitgelicht-merk/
   render.php). 1-op-1 geport uit de "Uitgelicht merk"-sectie van
   Merkoverzicht.dc.html naar BEM + tokens: een sand full-bleed band met een
   2-koloms grid (bron `.zg-feat-grid`), links badge/logo/kop/tekst/knop,
   rechts een productbeeld.

   Full-bleed truc: zelfde `width:100vw;margin-left:calc(50% - 50vw)`-patroon
   als `.zg-over-blok--band`/`.zg-merk-lineup` hierboven — de buitenste
   `<section>` draagt alléén verticale padding + achtergrond (geen
   horizontale padding, anders telt `width:100vw` + eigen padding op tot
   > 100vw en veroorzaakt horizontale overflow), `__inner` centreert zichzelf
   op max-width 1200px + horizontale padding. `templates/page-merken.html`
   plaatst dit block als directe child van een `layout:"constrained"`-<main>,
   vandaar `alignfull` op de wrapper (render.php) — zelfde noodzaak als bij
   `.zg-merk-lineup`/`.zg-over-blok--band` (zie die docblocks).

   Badge: gedeeld `.zg-badge`/`--sage`/`--eyebrow`-primitief (zie de
   "Badge"-sectie hierboven), zelfde bron-component (`Badge variant="sage"
   eyebrow="true"`) als `zetgoed/merk-verhaal`'s "Waar {merk} voor staat".

   Knop: gedeeld `.zg-btn`/`--secondary` + `.zg-btn-accent-wrap`-hover-tint,
   zelfde primitief als `zetgoed/cta-band`/`zetgoed/over-blok`.

   Bewuste deviatie: de bron geeft `.zg-feat-box` op mobiel (@640)
   `padding:24px!important`, maar definieert nergens een BASIS-padding voor
   die class in dit brondocument (geen zichtbare `.zg-feat-box`-regel in de
   `\3c style>` van Merkoverzicht.dc.html, en `ds-base.js` — de gedeelde
   design-system-bundle — bevat 'm ook niet: vermoedelijk een dode/
   restant-regel uit een ander sjabloon). Zonder een desktop-tegenhanger is
   er niets zinnigs om op mobiel te verkleinen, dus hier bewust niet
   overgenomen — geen `!important` nodig (die stylelint toch verbiedt) én
   geen no-op-regel toevoegen. */
.zg-uitgelicht-merk {
  width: 100vw;
  margin: var(--section-y) calc(50% - 50vw) 0;
  padding: var(--section-y) 0;
  background: var(--sand-100);
}

.zg-uitgelicht-merk__inner {
  /* content-box: max-width telt hier de CONTENTbreedte, net als
     .zg-hdr__bar en .zg-footer__inner. Zonder deze regel erven de inners
     die als wp:group in een template staan WP's border-box, waardoor hun
     inhoud 24px verder naar binnen valt dan de rest van de pagina. */
  box-sizing: content-box;
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 var(--space-6);
}

.zg-uitgelicht-merk__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  gap: 56px;
  align-items: center;
}

.zg-uitgelicht-merk__badge {
  margin-bottom: 16px;
}

.zg-uitgelicht-merk__logo {
  display: block;
  width: auto;
  height: 32px;
  margin: 0 0 20px;
}

.zg-uitgelicht-merk__logo--placeholder {
  display: flex;
  align-items: center;
  width: fit-content;
  height: 32px;
  padding: 0 10px;
  margin: 0 0 20px;
  color: var(--text-subtle);
  font-size: var(--fs-sm);
  background: var(--sand-200);
  border-radius: var(--radius-sm);
}

.zg-uitgelicht-merk__heading {
  max-width: 18ch;
  margin: 0 0 20px;
  font-size: var(--fs-h2);
}

.zg-uitgelicht-merk__desc {
  max-width: 52ch;
  margin: 0 0 28px;
  color: var(--text-subtle);
  line-height: 1.65;
}

.zg-uitgelicht-merk__image {
  display: block;
  width: 100%;
  height: 100%;
  min-height: 380px;
  object-fit: contain;
  background: var(--sand-300);
  border-radius: var(--radius-lg);
}

.zg-uitgelicht-merk__image--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 380px;
  color: var(--text-subtle);
  font-size: var(--fs-sm);
  background: var(--sand-300);
  border-radius: var(--radius-lg);
}

@media (width <= 820px) {
  .zg-uitgelicht-merk__grid {
    grid-template-columns: 1fr;
  }
}

/* Bron `.zg-root h2{font-size:1.65rem!important}` @640 — deze kop is in de
   bron een `<h2 style="font-size:var(--fs-h2)">` (2.125rem op desktop),
   dus de blanket-regel verkleint 'm op mobiel. Zelfde scoped-override-
   patroon (geen `!important`) als `.zg-faq-section__title` hierboven. */
@media (width <= 640px) {
  .zg-uitgelicht-merk__heading {
    font-size: 1.65rem;
  }
}

/* ── Merkoverzicht — werkwijze ────────────────────────────────────────────
   Fase 12 Task 3 (`zetgoed/merkoverzicht-werkwijze`, src/blocks/
   merkoverzicht-werkwijze/render.php). 1-op-1 geport uit de "Zo beoordelen
   we merken"-sectie (`.zg-how`/`.zg-how-card`/`.zg-how-num`) van
   Merkoverzicht.dc.html naar deze block-namespace — geen tweede afnemer,
   dus geen los primitief (zelfde afweging als `zetgoed/overons-
   testwerkwijze`'s eigen `__num` i.p.v. het generieke, elders al
   table-scoped `.zg-num`).

   Eyebrow hergebruikt `.eyebrow`/`.eyebrow--terracotta` (bron-accent voor
   deze hele pagina, zie de Global Constraints in het fase-12-plan).

   Grid: `repeat(3, minmax(0, 1fr))` i.p.v. de bron-letterlijke
   `repeat(3,1fr)` — zelfde overflow-veilige patroon als
   `.zg-brandcard-grid` hierboven. */
.zg-merkoverzicht-werkwijze {
  padding: var(--section-y) 0 0;
}

.zg-merkoverzicht-werkwijze__heading {
  max-width: 20ch;
  margin: 12px 0 32px;
  font-size: var(--fs-h1);
}

.zg-merkoverzicht-werkwijze__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 24px;
}

@media (width <= 820px) {
  .zg-merkoverzicht-werkwijze__grid {
    grid-template-columns: 1fr;
  }
}

.zg-merkoverzicht-werkwijze__card {
  padding: 30px;
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}

.zg-merkoverzicht-werkwijze__num {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  margin-bottom: 18px;
  color: var(--accent);
  font-weight: var(--fw-semibold);
  background: var(--forest-100);
  border-radius: var(--radius-pill);
}

.zg-merkoverzicht-werkwijze__card-title {
  margin: 0 0 10px;
  font-family: var(--font-display);
  font-size: var(--fs-h4);
  color: var(--text-heading);
  font-variation-settings: var(--display-vsettings);
}

.zg-merkoverzicht-werkwijze__card-text {
  margin: 0;
  color: var(--text-muted);
  line-height: 1.6;
}

/* Bron `.zg-root h2{font-size:1.65rem!important}`/`h3{font-size:1.35rem
   !important}` @640: de sectiekop is een `<h2 style="font-size:var(--fs-h1)">`
   (3rem op desktop — bewust groter dan een gewone h2, zie de bron), de
   kaarttitels zijn `<h3 style="font-size:var(--fs-h4)">` (1.25rem). Beide
   worden door de bron-blanket-regel op mobiel naar dezelfde 1.65rem/1.35rem
   getrokken als elke andere h2/h3 op de pagina — hier scoped geport (geen
   `!important`), zelfde patroon als hierboven. Geplaatst NÁ de basisregels
   hierboven (i.p.v. direct onder de 820px-grid-media) zodat deze
   klasse-override qua brontekst-volgorde wint — zelfde specificiteit,
   latere declaratie wint, geen `!important` nodig. */
@media (width <= 640px) {
  .zg-merkoverzicht-werkwijze__heading {
    font-size: 1.65rem;
  }

  .zg-merkoverzicht-werkwijze__card-title {
    font-size: 1.35rem;
  }
}

/* ── Keuzehulp — quiz (voortgang, vraagkaart, resultaat) ─────────────────
   Fase 11 Task 3: `assets/js/keuzehulp.js` rendert de volledige quiz-DOM
   in `.qz-app` (progressief per stap) — deze CSS stuurt die gerenderde
   markup aan. De classes hieronder zijn BEWUST NIET `zg-`-geprefixt: ze
   volgen 1-op-1 de bron-letterlijke naamgeving uit Keuzehulp.dc.html
   (`.qz-opt`/`.qz-optgrid`/`.qz-altc`/`.qz-stars`/`.qz-linkbtn` waren al
   bron-literal per het fase-plan) — de nieuwe helper-classes die de bron
   niet apart benoemde (voortgangsbalk, vraagkaart, resultaat-layout,
   reden-pills, vergelijk-rij) volgen dezelfde `qz-`-stijl voor
   consistentie binnen dit ene quiz-onderdeel, zodat JS-selectors en
   CSS-selectors elkaar overal 1-op-1 spiegelen. Zelfde afweging als
   `.qz-app` hierboven (Task 2) en `.zg-diff-*`/`.zg-bc` eerder in dit
   bestand — `selector-class-pattern` staat daarom voor dit hele blok uit.

   Bron-`.th` (thumbnail binnen een alternatieven-kaart) is hier
   `.qz-altc__th`: een kale `.th` zou geen enkel naamgevingspatroon meer
   volgen, de BEM-child houdt 'm nog steeds traceerbaar aan `.qz-altc`. */
/* stylelint-disable selector-class-pattern */

.qz-progress {
  margin-bottom: 28px;
}

.qz-progress__label {
  margin: 0 0 10px;
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--text-muted);
}

.qz-progress__track {
  height: 8px;
  overflow: hidden;
  background: var(--paper-300);
  border-radius: var(--radius-pill);
}

.qz-progress__fill {
  height: 100%;
  background: var(--accent);
  border-radius: var(--radius-pill);
  transition: width 0.35s ease;
}

.qz-card {
  padding: 44px 44px 40px;
  background: var(--surface-sand);
  border-radius: var(--radius-2xl);
}

.qz-card__heading {
  margin: 0 0 14px;
  font-family: var(--font-display);
  font-size: 2rem;
  line-height: 1.15;
  font-variation-settings: var(--display-vsettings);
}

.qz-card__heading:focus-visible {
  outline: var(--ring-width) solid var(--focus-ring);
  outline-offset: 4px;
}

.qz-qnum {
  color: var(--accent);
}

.qz-card__intro {
  max-width: 56ch;
  margin: 0 0 28px;
  font-size: 17px;
  color: var(--text-subtle);
}

.qz-optgrid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px;
  margin-bottom: 28px;
}

.qz-opt {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 18px 20px;
  text-align: left;
  cursor: pointer;
  background: var(--surface-raised);
  border: 1.5px solid var(--border-control);
  border-radius: var(--radius-lg);
  transition:
    border-color 0.14s ease,
    box-shadow 0.14s ease;
}

.qz-opt b {
  font-size: var(--fs-body);
  color: var(--text-heading);
}

.qz-opt span {
  font-size: var(--fs-sm);
  color: var(--text-subtle);
}

.qz-opt:hover {
  border-color: var(--accent);
  box-shadow: var(--shadow-sm);
}

.qz-opt:focus-visible {
  outline: var(--ring-width) solid var(--focus-ring);
  outline-offset: 2px;
}

.qz-back {
  padding-left: 0;
}

/* Resultaat-paneel */
.qz-result {
  padding: 40px 44px;
  background: var(--surface-sand);
  border-radius: var(--radius-2xl);
}

.qz-winner-grid {
  display: grid;
  grid-template-columns: 1fr 1.15fr;
  align-items: center;
  gap: 32px;
  margin-top: 22px;
}

.qz-winner-media {
  aspect-ratio: 1 / 1;
  border-radius: var(--radius-lg);
}

.qz-stars {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 10px;
  font-size: 18px;
  letter-spacing: 2px;
  color: var(--amber-600);
}

.qz-stars small {
  font-family: var(--font-mono);
  font-size: 14px;
  letter-spacing: normal;
  color: var(--text-muted);
}

.qz-result__heading {
  margin: 0 0 12px;
  font-family: var(--font-display);
  font-size: 2.4rem;
  line-height: 1.12;
  font-variation-settings: var(--display-vsettings);
}

.qz-result__heading:focus-visible {
  outline: var(--ring-width) solid var(--focus-ring);
  outline-offset: 4px;
}

.qz-result__why {
  max-width: 56ch;
  margin: 0 0 22px;
  font-size: 17px;
  color: var(--text-subtle);
}

.qz-reasons {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 26px;
}

.qz-reason {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  font-size: var(--fs-xs);
  color: var(--text-subtle);
  background: var(--surface-raised);
  border-radius: var(--radius-pill);
}

.qz-reason svg {
  flex-shrink: 0;
}

.qz-reason-met svg {
  color: var(--accent);
}

.qz-reason-no svg {
  color: var(--danger);
}

.qz-cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.qz-compare {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 14px;
  padding-top: 24px;
  margin-top: 32px;
  border-top: 1.5px solid var(--border-control);
}

.qz-compare__label {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--text-muted);
}

.qz-compare__text {
  font-size: var(--fs-sm);
  color: var(--text-heading);
}

.qz-compare__link,
.qz-linkbtn {
  font-size: var(--fs-sm);
  color: var(--accent);
  text-decoration: none;
}

.qz-compare__link:hover,
.qz-linkbtn:hover {
  color: var(--accent-press);
  text-decoration: underline;
}

.qz-restart-wrap {
  margin: 28px 0;
  text-align: center;
}

.qz-alts {
  margin-top: 40px;
}

.qz-alts > .zg-badge {
  margin-bottom: 20px;
}

.qz-altgrid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 20px;
}

.qz-altc {
  display: grid;
  grid-template-columns: 120px 1fr;
  gap: 20px;
  padding: 18px;
  background: var(--surface-raised);
  border: 1.5px solid var(--border-control);
  border-radius: var(--radius-lg);
}

.qz-altc__th {
  aspect-ratio: 1 / 1;
  border-radius: var(--radius-md);
}

.qz-altc__body h3 {
  margin: 0 0 6px;
  font-size: var(--fs-h4);
  color: var(--text-heading);
}

.qz-altc__body .meta {
  margin: 0 0 8px;
  font-size: var(--fs-sm);
  color: var(--text-subtle);
}

.qz-altc__body .cmp {
  font-size: var(--fs-sm);
  color: var(--accent);
  text-decoration: none;
}

.qz-altc__body .cmp:hover {
  color: var(--accent-press);
  text-decoration: underline;
}

@media (width <= 640px) {
  .qz-card {
    padding: 32px 26px 28px;
  }

  .qz-optgrid {
    grid-template-columns: 1fr;
  }

  .qz-result {
    padding: 30px 26px;
  }

  .qz-winner-grid {
    grid-template-columns: 1fr;
  }

  .qz-altc {
    grid-template-columns: 88px 1fr;
  }
}
/* stylelint-enable selector-class-pattern */

/* ── Praktijk-fotocollage ─────────────────────────────────────────────────
   1-op-1 geport uit ProductReview.dc.html: ná de 3 tekst-subsecties van
   "In de praktijk" (Espresso-kwaliteit/Melk stomen/Onderhoud, kale core-
   blocks in wordpress/scripts/seed-sage-content.php) volgt een 2-koloms
   fotocollage. Geport als één `core/html`-blok (geen los custom block —
   puur structurele markup zonder editable data-model) i.p.v. geneste
   `core/columns`: de 0.72fr/1fr-rijverhouding en de aspect-ratio-crops
   zijn met kale core-column-breedtes niet 1-op-1 te halen. Alle kleur/
   spacing hier via tokens, geen inline hex (stylelint color-no-hex zou
   dat toch weigeren). */
.zg-praktijk-collage {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: start;
  gap: var(--space-6);
}

.zg-praktijk-collage__col {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

/* Standaardrij: twee gelijke kolommen (RECHTS-onderrij in de bron:
   molen + dual-boiler naast elkaar). De LINKS-bovenrij (molenfoto +
   bijschrift) gebruikt de `--molen`-modifier hieronder voor de
   0.72fr/1fr-verhouding uit de bron. */
.zg-praktijk-collage__row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  gap: var(--space-5);
}

.zg-praktijk-collage__row--molen {
  grid-template-columns: 0.72fr 1fr;
}

.zg-praktijk-collage__img {
  display: block;
  width: 100%;
  object-fit: cover;
  border-radius: var(--radius-lg);
}

.zg-praktijk-collage__img--portrait {
  aspect-ratio: 3 / 4;
}

.zg-praktijk-collage__img--square {
  aspect-ratio: 1 / 1;
}

.zg-praktijk-collage__cap {
  margin: 0;
  color: var(--text-muted);
  font-size: var(--fs-sm);
  line-height: 1.5;
}

/* Mobiel: de 2 hoofdkolommen stapelen naar 1 kolom — de binnenste rijen
   (molenfoto+bijschrift, molen+dual-boiler) blijven zij-aan-zij (bron
   houdt die verhouding ook op mobiel aan), zodat er geen 0-overflow op
   360–768px ontstaat door een te brede min-content-kolom. */
@media (width <= 768px) {
  .zg-praktijk-collage {
    grid-template-columns: 1fr;
  }
}

/* ── Bron-melding (zetgoed/bron-melding) ────────────────────────────────
   Kader boven aan een review die we niet zelf hebben getest. Verving een
   core-paragraaf met `sand-100`-achtergrond: die tint verschilt te weinig
   van de pagina-achtergrond (`--paper-50`), waardoor het kader onzichtbaar
   was en de belangrijkste transparantie-uitspraak van de pagina als een
   losse vetgedrukte zin las.

   Vandaar drie dragers in plaats van een achtergrondtint: een verticale
   accentlijn links (`--accent`, de merkgroene forest-600 die ook de zegel
   draagt), een zandvlak dat wél contrasteert (`--surface-sand`, sand-200)
   en een rond icoonvlak. Zelfde radius- en spacing-tokens als de andere
   kaarten in dit bestand, dus het valt binnen hetzelfde systeem. */
.zg-bron-melding {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 20px;
  align-items: start;

  /* Boven én onder ruimte. Stond eerder op `0 0 var(--space-10)`, wat klopte
     zolang dit blok alleen bovenaan de pagina stond: daar levert de blockGap
     onder de hero de aanloop. De `niet_zelf`-variant op de eigen-review-
     pagina's staat middenin de lopende tekst, en plakte daar tegen de alinea
     erboven. Staat het blok tóch als eerste in post_content, dan zet WP's
     eigen `:where(.is-layout-flow) > :first-child{margin-block-start:0}`
     (specificiteit 0,2,0) deze marge vanzelf weer op nul, dus de bovenkant
     van de pagina verandert niet. */
  margin: var(--space-10) 0;
  padding: 26px 30px 28px;
  background: var(--surface-sand);
  border-left: 4px solid var(--accent);
  border-radius: var(--radius-lg);
}

.zg-bron-melding__mark {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  color: var(--text-on-forest);
  background: var(--accent);
  border-radius: var(--radius-pill);
}

.zg-bron-melding__eyebrow {
  display: block;
  margin-bottom: 6px;
  color: var(--accent);
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: var(--fw-semibold);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.zg-bron-melding__heading {
  margin: 0 0 8px;
  color: var(--text-heading);
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-variation-settings: var(--display-vsettings);
  line-height: 1.25;
}

.zg-bron-melding__text {
  margin: 0;

  /* Bewust GEEN eigen `max-width`: base.css legt op `p` al de leesmaat van
     68ch die de hele site aanhoudt, en die hoort deze melding ook te volgen.
     Een eigen cap (62ch, 80ch) week daar zichtbaar van af terwijl de lopende
     tekst eronder gewoon op 68ch brak. */
  color: var(--text-subtle);
  font-size: var(--fs-md);
  line-height: 1.65;
}

.zg-bron-melding__link {
  display: inline-block;
  margin-top: 14px;
  color: var(--link);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.zg-bron-melding__link:hover,
.zg-bron-melding__link:focus-visible {
  color: var(--link-hover);
}

/* Mobiel: het icoonvlak boven de tekst i.p.v. ernaast — naast elkaar houdt
   het de tekstkolom onder de 40 tekens en breekt elke zin stuk. */
@media (width <= 600px) {
  .zg-bron-melding {
    grid-template-columns: minmax(0, 1fr);
    gap: 14px;
    padding: 22px 20px 24px;
  }
}

/* Label boven de twee intro-varianten van `zetgoed/review-byline`. Alleen
   edit.js rendert dit element: de frontend kiest er één op basis van
   `zg_getest` en heeft geen label nodig. Bewust zónder
   `.editor-styles-wrapper`-prefix, want de stylelint-selectorregel van dit
   theme laat alleen `zg-`-classes toe en de class bestaat sowieso alleen in
   de editor-DOM. */
.zg-review-byline__intro-label {
  display: block;
  margin-bottom: 4px;
  color: var(--text-muted);
  font-family: var(--font-body);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
