@layer tokens, reset, base, layout, components, utilities;

/* ==========================================================================
   TOKENS
   Kleuren komen één-op-één uit de oude Flutter-theming (app_colors.dart),
   zodat de huisstijl exact gelijk blijft.
   ========================================================================== */
@layer tokens {
  :root {
    /* Merkkleuren */
    --c-primary: #9c0613;
    --c-primary-deep: #5e0e16;
    --c-cta: #ba1423;
    --c-cta-from: #9c0613;
    --c-cta-to: #be1524;
    --c-cta-hover: #494242;

    /* Inkt en vlakken */
    --c-ink: #37342f;
    --c-ink-muted: #6b6660;
    --c-white: #fff;
    --c-surface-content: #ebe7dc;
    --c-surface-quote: #c6baaa;
    --c-surface-dark: #4d494a;
    --c-surface-soft: #eee;

    /* Typografie */
    --font-body: "Raleway", ui-sans-serif, system-ui, -apple-system, "Segoe UI",
      sans-serif;
    /*
     * LET OP bij dit font: Apothecary tekent zijn glyphs klein in de em-box —
     * kaphoogte 0,20 em, tegen 0,73 em voor een gewoon font. Een fontgrootte
     * van 18px levert hier dus 3,6px letterhoogte op en is onleesbaar. Maar
     * opschalen helpt niet: de tekening is vervuild (386 punten per letter),
     * dus groot gezet valt de letter uit elkaar.
     *
     * Kortom: gebruik dit font alleen waar de bestaande site het ook groot
     * gebruikt (herotitel 100px, kopjes 60px, citaat 70px). Voor kleine tekst
     * --font-display-clean, en voor labels gewoon Raleway.
     */
    --font-display: "Typewriter", "Courier New", ui-monospace, monospace;
    /* Schone variant voor kleine formaten; Raleway vangt ontbrekende tekens op
       zodat een accent leesbaar blijft in plaats van te verdwijnen. */
    --font-display-clean: "Typewriter Clean", "Raleway", ui-monospace, monospace;
    /* Alleen voor het spreukenblok op de homepage */
    --font-saying: "Special Elite", "Courier New", ui-monospace, monospace;

    /* Fluid typeschaal — de bovengrenzen zijn de oorspronkelijke Flutter-maten */
    /* 20px op desktop en 18px gestapeld, zoals de bestaande site */
    --fs-body: clamp(1.125rem, 0.95rem + 0.5vw, 1.25rem);
    --fs-lead: clamp(1.06rem, 0.98rem + 0.4vw, 1.35rem);
    --fs-small: clamp(0.82rem, 0.79rem + 0.14vw, 0.9rem);
    /*
     * 60px op 1440, met 55px als bodem — precies wat de bestaande site doet.
     * Dat minimum moet hoog: dit font tekent een kaphoogte van maar 0,20 em,
     * dus 55px levert 11px letterhoogte op. Bij de eerdere bodem van 44px was
     * dat 8,8px, en dan is de kop optisch kléiner dan de 17px Raleway eronder,
     * hoe groot het getal er ook uitziet.
     */
    --fs-eyebrow: clamp(3.4375rem, 4.167vw, 3.75rem);
    /* 100px op 1440, met 60px als bodem: dat is wat de bestaande site op elk
       scherm onder ~864px aanhoudt. Zakte hij lager, dan werd de titel op een
       telefoon merkbaar kleiner dan het origineel. */
    --fs-hero: clamp(3.75rem, 6.944vw, 6.25rem);
    --fs-section: clamp(0.95rem, 0.7rem + 1.2vw, 1.125rem);
    /*
     * Opgemeten op de bestaande site: 55px op 390 breed, 60px op 768, 70px op
     * desktop. Het minimum ligt hoog omdat dit font een kaphoogte van 0,20 em
     * heeft en anders onleesbaar klein wordt.
     */
    --fs-quote: clamp(3.4rem, 7.8vw, 4.375rem);

    /*
     * Tracking in em, niet in px — daardoor schaalt het mee en breken de koppen
     * niet af op smalle schermen (de bug op de huidige site).
     *
     * De waarden zijn licht negatief, precies als de bestaande site. De
     * luchtige typemachine-look komt niet uit tracking maar uit het font zelf:
     * Apothecary-Typewriter heeft een advance van 0,35 em.
     *
     * Voor de herotitel staat er -3px en geen -0.03em. Dat is hetzelfde op
     * 1440 (100px letter), maar de bestaande site houdt die -3px ook aan bij
     * 60px op mobiel; met een em-waarde zou het daar -1,8px worden en past de
     * titel niet meer op één regel.
     */
    --tracking-display: -3px;
    --tracking-eyebrow: -0.05em;
    --tracking-quote: -0.0857em;

    --lh-tight: 1.15;
    /* 23/20 = 1.15 op de bestaande site. Krap voor lopende tekst, maar het
       is bewust zo vormgegeven; de alinea-afstand vangt het op. Gestapeld gaat
       de bestaande site naar 26,1/18 = 1.45 — zie het mediablok onderaan deze
       laag. */
    --lh-body: 1.15;
    --lh-quote: 1.45;

    /* Ruimte */
    --space-2xs: clamp(0.25rem, 0.23rem + 0.1vw, 0.31rem);
    --space-xs: clamp(0.5rem, 0.46rem + 0.2vw, 0.63rem);
    --space-sm: clamp(0.75rem, 0.69rem + 0.31vw, 0.94rem);
    --space-md: clamp(1rem, 0.92rem + 0.41vw, 1.25rem);
    --space-lg: clamp(1.5rem, 1.38rem + 0.62vw, 1.88rem);
    --space-xl: clamp(2rem, 1.63rem + 1.85vw, 3rem);
    --space-2xl: clamp(3rem, 2.26rem + 3.7vw, 5rem);
    --space-3xl: clamp(4rem, 2.89rem + 5.56vw, 7rem);

    /* Maten */
    --w-page: 1240px;
    --w-prose: 66ch;
    --gutter: clamp(1.25rem, 0.8rem + 2.2vw, 4.375rem);

    --header-h: 140px;
    --header-h-compact: 60px;

    --radius: 0;
    --shadow-soft: 0 2px 18px rgb(55 52 47 / 12%);
    --shadow-lift: 0 8px 30px rgb(55 52 47 / 18%);

    --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
    --dur-fast: 180ms;
    --dur: 320ms;
    --dur-slow: 800ms;
  }

  @media (width < 60rem) {
    :root {
      /* 133 = 20 + logo 93 + 20, net als de bestaande site op mobiel */
      --header-h: 133px;
      --header-h-compact: 64px;

      /*
       * 26,1/18 = 1.45. De krappe 1.15 van de brede opmaak werkt op een kolom
       * van 350 niet meer; de bestaande site zet hem daar dan ook ruimer.
       */
      --lh-body: 1.45;
    }
  }
}

/* ==========================================================================
   RESET
   ========================================================================== */
@layer reset {
  *,
  *::before,
  *::after {
    box-sizing: border-box;
  }

  * {
    margin: 0;
  }

  html {
    -webkit-text-size-adjust: 100%;
    tab-size: 4;
  }

  body {
    min-height: 100svh;
    line-height: var(--lh-body);
    /*
     * Bewust GEEN -webkit-font-smoothing: antialiased. Op macOS rendert dat
     * tekst merkbaar dunner dan de standaard subpixel-rendering, waardoor de
     * koppen, het citaat en het menu lichter oogden dan op de bestaande site.
     */
  }

  /*
   * De reveal-animaties zetten elementen tijdelijk 3rem opzij. Zonder clipping
   * scrollt de pagina daardoor horizontaal tot ze in beeld komen.
   *
   * Dit hoort op `main` en niet op `body`: overflow op body propageert naar de
   * viewport, waardoor body zelf `visible` blijft en er niets geklipt wordt.
   * `clip` en niet `hidden`, want `hidden` maakt er een scrollcontainer van.
   */
  main {
    overflow-x: clip;
  }

  img,
  video,
  canvas,
  svg {
    display: block;
    max-width: 100%;
  }

  /*
   * Blok, niet `display: contents`. Bij `contents` telt de <source> mee als
   * grid-item van een <figure>, waardoor er een lege rij van 0px ontstaat en
   * de gap het beeld een paar pixels omlaag duwt.
   *
   * Let op: hierdoor meet `height: 100%` op een <img> zich aan de <picture> en
   * niet meer aan de omhullende laag. Voor de schermvullende achtergronden moet
   * de picture dus zelf ook 100% hoog zijn — zie .media-fill hieronder.
   */
  picture {
    display: block;
  }

  input,
  button,
  textarea,
  select {
    font: inherit;
    color: inherit;
  }

  p,
  h1,
  h2,
  h3,
  h4 {
    overflow-wrap: break-word;
  }

  ul[class],
  ol[class] {
    list-style: none;
    padding: 0;
  }

  :target {
    scroll-margin-top: calc(var(--header-h-compact) + var(--space-lg));
  }
}

/* ==========================================================================
   FONTS
   Zelf gehost: geen verzoek naar Google, dus sneller en geen AVG-vraagstuk.
   ========================================================================== */
@layer base {
  @font-face {
    font-family: "Raleway";
    src: url("/assets/fonts/raleway.woff2") format("woff2-variations");
    font-weight: 100 900;
    font-style: normal;
    font-display: swap;
  }

  @font-face {
    font-family: "Special Elite";
    src: url("/assets/fonts/special-elite.woff2") format("woff2");
    font-weight: 400;
    font-style: normal;
    font-display: swap;
  }

  /*
   * Twee typemachine-fonts, met een reden.
   *
   * "Typewriter" (Apothecary) is het font van de bestaande site en heeft een
   * bewust vervuilde tekening: de letter 'e' bestaat uit 386 punten in 26
   * contouren. Op groot formaat, zoals de herotitel op 100px, is dat precies
   * het gewenste effect.
   *
   * "Typewriter Clean" (atwriter) is schoon: 56 punten, 2 contouren, en een
   * kaphoogte van 0,73 em tegen 0,20 em. Alleen dít font is bruikbaar op klein
   * formaat; Apothecary zou daar naar circa 56px moeten en valt dan uit
   * elkaar. Het is ook wat de Flutter-versie voor de reviewtitels gebruikte.
   *
   * Let op: Typewriter Clean heeft maar 94 tekens, zonder accenten en euro.
   * De build waarschuwt als een reviewtitel iets bevat dat erbuiten valt.
   */
  @font-face {
    font-family: "Typewriter Clean";
    src: url("/assets/fonts/typewriter-clean.woff2") format("woff2");
    font-weight: 400;
    font-style: normal;
    font-display: swap;
  }

  @font-face {
    font-family: "Typewriter";
    src: url("/assets/fonts/typewriter.woff2") format("woff2");
    /* Eén gewicht, geen bereik: bij een bereik denkt de browser dat het font
       700 zelf levert en maakt hij geen synthetische vette voor de
       reviewtitel, die in het origineel wél vet is. */
    font-weight: 400;
    font-style: normal;
    font-display: swap;
  }

  html {
    scroll-behavior: smooth;
  }

  body {
    font-family: var(--font-body);
    font-size: var(--fs-body);
    font-weight: 400;
    color: var(--c-ink);
    background: var(--c-white);
  }

  h1,
  h2,
  h3 {
    font-weight: 400;
    line-height: var(--lh-tight);
    text-wrap: balance;
  }

  p {
    text-wrap: pretty;
  }

  a {
    color: inherit;
    text-decoration-thickness: 1px;
    text-underline-offset: 0.18em;
  }

  a:hover {
    color: var(--c-primary);
  }

  /* Zichtbare focus voor toetsenbordgebruik, niet bij muisklik */
  :focus-visible {
    outline: 3px solid var(--c-primary);
    outline-offset: 3px;
  }

  ::selection {
    background: var(--c-surface-quote);
    color: var(--c-ink);
  }
}

/* ==========================================================================
   LAYOUT
   ========================================================================== */
@layer layout {
  .container {
    width: min(100% - var(--gutter) * 2, var(--w-page));
    margin-inline: auto;
  }

  .container--wide {
    width: min(100% - var(--gutter) * 2, 1400px);
  }

  .section {
    position: relative;
    padding-block: var(--space-2xl);
  }

  .section--content {
    background: var(--c-surface-content);
  }

  .section--flush {
    padding-block: 0;
  }

  /* Afstand tot de sectierand verschilt per pagina; zie compositions.json */
  @media (width >= 87.5rem) {
    .section--composition { padding-block-start: var(--section-top, 34px); }
  }

  /*
   * .composition — de kern van de layout.
   *
   * De oude Flutter-versie zette citaat en beeld op absolute pixelcoördinaten
   * (Point(822, -104) enzovoort). Hier liggen de drie blokken in één grid-cel
   * over elkaar heen, elk verschoven met een eigen marge:
   *
   *   - horizontaal met een percentage van de container, zodat het meeschaalt
   *     (en negatief mag zijn — op /coaching steekt het citaat links buiten de
   *     container, precies als op de bestaande site);
   *   - verticaal in pixels, want het origineel gebruikt daar ook vaste
   *     offsets.
   *
   * De waarden per pagina komen uit src/_data/compositions.json en worden
   * gegenereerd naar /assets/css/compositions.css.
   *
   * Onder 60rem vervalt alles en stapelen de blokken gewoon onder elkaar.
   */
  .composition {
    display: grid;
    gap: var(--space-xl);
    grid-template-columns: minmax(0, 1fr);
  }

  /*
   * Gestapeld staan citaat en beeld vóór de tekst, net als op de bestaande
   * site. In de DOM staat de tekst eerst, omdat dat de leesvolgorde is die je
   * wilt voorlezen; de volgorde hier is puur visueel.
   *
   * Maten opgemeten op de bestaande site bij 390 breed, per pagina; ze staan in
   * compositions.json onder `gestapeld` en komen hier binnen als --st-*.
   *
   * Wat op alle pagina's gelijk is staat hier hard: het citaatblok loopt van
   * 10px links tot 10px rechts en begint 30px boven de sectierand, dus over de
   * hero heen. Wat verschilt — de bloghoogte, de tekengrootte en hoever het
   * beeld eroverheen valt — komt uit de data. Zonder die begrenzing werd de
   * foto op een tablet breder dan op desktop, wat nergens op slaat.
   */
  @media (width < 87.5rem) {
    .composition { gap: 0; }

    /* Het citaat steekt over de hero; de sectiepadding erboven vervalt dus. */
    .section--composition { padding-block-start: 0; }

    .composition > .quote {
      order: 1;
      /* Van schermrand tot schermrand op 10px na, ongeacht de container. */
      margin-inline: calc(10px - var(--gutter));
      min-height: var(--st-quote-h);
      margin-block-start: -30px;
    }

    .composition > .figure {
      order: 2;
      width: min(100%, var(--st-fig-w, 350px));
      margin-inline: auto;
      margin-block-start: calc(-1 * var(--st-fig-overlap, 30px));
    }

    .composition__body {
      order: 3;
      margin-block-start: var(--space-xl);
    }
  }

  /*
   * Pas vanaf 1400px: smaller past deze opmaak simpelweg niet. De bestaande
   * site probeert het daar wel en duwt het citaat dan deels buiten beeld (op
   * 1280 staat het op x=-30) — dat nemen we bewust niet over.
   */
  @media (width >= 87.5rem) {
    .section--composition > .container {
      width: min(100% - 40px, var(--comp-max, 1280px));
    }

    .composition {
      /* Terugvalwaarden; per pagina overschreven via .composition--<naam> */
      --section-top: 34px;
      --text-x: -569.5px;
      --text-w: 704px;
      --quote-x: 180.5px;
      --quote-w: 410px;
      --quote-h: 300px;
      --quote-rise: 100px;
      --fig-x: 334.5px;
      --fig-w: 326px;
      --fig-ratio: 326 / 460;
      --fig-drop: 90px;

      gap: 0;
      grid-template-columns: 100%;
    }

    /* Alle blokken in dezelfde cel; de celhoogte volgt het hoogste blok */
    .composition > * {
      grid-area: 1 / 1;
      justify-self: start;
      /* Zonder dit rekken ze uit tot de volle celhoogte en klopt de hoogte
         van het citaat niet meer. */
      align-self: start;
    }

    /* 50% is de middenlijn van de container; de offsets zijn daar vaste
       pixelafstanden vanaf, precies zoals de bestaande site het doet. */
    .composition__body {
      margin-inline-start: calc(50% + var(--text-x));
      width: var(--text-w);
      display: grid;
      align-content: start;
    }

    .composition > .quote {
      margin-inline-start: calc(50% + var(--quote-x));
      width: var(--quote-w);
      min-height: var(--quote-h);
      /* quote-rise is gemeten vanaf de bovenkant van de sectie; de
         sectiepadding komt er dus nog bij. */
      margin-block-start: calc(-1 * (var(--quote-rise) + var(--section-top)));
    }

    .composition > .figure {
      margin-inline-start: calc(50% + var(--fig-x));
      width: var(--fig-w);
      /* fig-drop is gemeten vanaf de bovenkant van de sectie */
      margin-block-start: calc(var(--fig-drop) - var(--section-top));
    }

    .composition > .figure img { aspect-ratio: var(--fig-ratio); }

    /* De kop staat 46px boven de bodytekst; de afstand tot de sectierand
       verschilt per pagina en zit in --section-top. */
    .composition__body > .eyebrow { margin-block-end: 46px; }
  }

  /*
   * Gestapeld 40px, opgemeten op de bestaande site. Zonder deze regel had de
   * kop daar helemaal geen ondermarge — die 46px hierboven zit in het
   * desktop-mediablok — en plakte hij tegen de eerste alinea.
   */
  @media (width < 87.5rem) {
    .composition__body > .eyebrow { margin-block-end: 40px; }
  }

  .prose {
    max-width: var(--w-prose);
  }

  .prose--wide {
    max-width: 78ch;
  }

  /*
   * Tekstblok zonder citaat of beeld ernaast. Dat loopt op de bestaande site
   * over de volle containerbreedte en heeft een ruimere regelafstand (30/20 =
   * 1.5) dan de tekst naast een compositie (23/20 = 1.15).
   */
  .prose--full {
    max-width: none;
    line-height: 1.5;
  }

  .prose--full > * + * {
    margin-block-start: 16px;
  }

  .prose > * + * {
    margin-block-start: var(--space-md);
  }

  .prose h2 {
    margin-block-end: var(--space-lg);
  }

  .prose ul {
    /* 40px inspringing met gewone disc-bullets, zoals de bestaande site */
    padding-inline-start: 40px;
    list-style: disc;
  }

  .prose a {
    color: var(--c-primary);
    text-decoration: underline;
  }

  .stack {
    display: grid;
    gap: var(--space-md);
    align-content: start;
  }
}

/* ==========================================================================
   COMPONENTEN
   ========================================================================== */
@layer components {
  /* --- Typewriter-koppen ------------------------------------------------ */
  .display,
  .eyebrow {
    font-family: var(--font-display);
    font-weight: 400;
    /*
     * 1.2 en niet 1: op de bestaande site is de regelbox van de herotitel 120px
     * bij een letter van 100px. Met 1 komt de tekst een kleine 20px lager uit,
     * omdat de hero vanaf de onderkant is uitgelijnd.
     */
    line-height: 1.2;
  }

  .display {
    font-size: var(--fs-hero);
    letter-spacing: var(--tracking-display);
  }

  .eyebrow {
    font-size: var(--fs-eyebrow);
    font-weight: 400;
    letter-spacing: var(--tracking-eyebrow);
    color: var(--c-primary);
  }

  .display--light { color: var(--c-white); }
  .display--dark  { color: var(--c-ink); }

  /* --- Knoppen ---------------------------------------------------------- */
  .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-xs);
    /*
     * 250x43 is de standaard. Een paar knoppen hebben een langer label en
     * krijgen een bredere variant mee vanuit de content (block.cta.width).
     */
    width: 250px;
    max-width: 100%;
    height: 43px;
    padding-inline: var(--space-md);
    border: 0;
    border-radius: var(--radius);
    background-image: linear-gradient(180deg, var(--c-cta-from), var(--c-cta-to));
    background-color: var(--c-cta);
    color: var(--c-white);
    font-size: var(--fs-body);
    line-height: 1.2;
    text-align: center;
    text-decoration: none;
    cursor: pointer;
    transition: background-color var(--dur) var(--ease),
                transform var(--dur-fast) var(--ease),
                box-shadow var(--dur) var(--ease);
  }

  .btn:hover,
  .btn:focus-visible {
    background-image: none;
    background-color: var(--c-cta-hover);
    color: var(--c-white);
    box-shadow: var(--shadow-lift);
  }

  .btn:active { background-color: var(--c-primary-deep); transform: translateY(1px); }
  .btn[disabled], .btn[aria-disabled="true"] { opacity: 0.6; cursor: not-allowed; }
  .btn--300 { width: 300px; }
  .btn--350 { width: 350px; }

  /* Blijft bestaan zodat bestaande markup niet breekt */
  .btn--wide { width: 250px; }


  .btn-row { display: flex; gap: var(--space-md); flex-wrap: wrap; }
  .btn-row--start  { justify-content: flex-start; }
  .btn-row--center { justify-content: center; }
  .btn-row--end    { justify-content: flex-end; }

  /* --- Header ------------------------------------------------------------
   * Bovenaan is de balk volledig doorzichtig met een groot logo, zodat de
   * hero-foto er ongehinderd doorheen komt. Tijdens het scrollen wordt hij
   * geleidelijk lager, witter en kleiner.
   *
   * Dat verloop hangt aan --p (0 bovenaan, 1 na 100px scrollen), die site.js
   * per frame bijwerkt. Geen CSS-transitie hier: die zou achterlopen op de
   * scrollpositie en juist gaan haperen.
   * --------------------------------------------------------------------- */
  .site-header {
    --p: 0;
    /* Platte px-waarden, geen clamp(): een clamp() binnen deze interpolatie
       valt in browsers om naar 0 zodra --p precies 1 is. Vandaar losse
       waarden per breekpunt hieronder. */
    /* 250x100 op de bestaande site */
    --logo-h: 100px;
    --logo-h-compact: 40px;

    /* De hoogte van dit moment, tussen vol en compact in. Ook de menulade
       hangt hieraan, anders staat die los van de balk zodra je scrolt. */
    --header-nu: calc(
      var(--header-h) - (var(--header-h) - var(--header-h-compact)) * var(--p)
    );

    position: fixed;
    inset-block-start: 0;
    inset-inline: 0;
    z-index: 100;
    height: var(--header-nu);
    background: rgb(255 255 255 / calc(var(--p) * 100%));
    box-shadow: 0 2px 18px rgb(55 52 47 / calc(var(--p) * 12%));
  }

  /* Zonder JavaScript blijft --p op 0 staan; dan wél een witte balk, anders
     is de navigatie boven lichte delen van een foto slecht leesbaar. */
  .no-js .site-header {
    background: var(--c-white);
    height: var(--header-h-compact);
  }

  .site-header__inner {
    display: grid;
    /*
     * Vaste middenkolom, geen `auto`. Het logo krimpt tijdens het scrollen; bij
     * een auto-kolom zouden de zijkolommen daardoor breder worden en schuiven
     * de menu-items horizontaal mee. Met een vaste breedte staat het menu stil.
     */
    grid-template-columns: 1fr 250px 1fr;
    align-items: center;
    height: 100%;
    gap: var(--space-md);
  }

  .site-header__logo {
    justify-self: center;
    display: block;
  }

  .site-header__logo img {
    width: auto;
    height: calc(
      var(--logo-h) - (var(--logo-h) - var(--logo-h-compact)) * var(--p)
    );
  }

  .no-js .site-header__logo img { height: var(--logo-h-compact); }

  @media (width < 75rem) {
    .site-header { --logo-h: 76px; --logo-h-compact: 36px; }
  }

  /*
   * Onder 60rem stond het logo op 52px, terwijl de bestaande site daar 93px
   * aanhoudt (100 zodra de breedte het toelaat) — bijna twee keer zo groot.
   * Bovenaan de pagina dus 93; tijdens het scrollen krimpt het wel naar 40,
   * want anders past het niet in een balk die je in beeld wilt houden. Dat
   * krimpen is een bewuste afwijking: de bestaande site laat de hele balk op
   * mobiel gewoon wegscrollen, waardoor het menu onbereikbaar wordt.
   */
  @media (width < 60rem) {
    .site-header { --logo-h: 93px; --logo-h-compact: 40px; }
  }

  /* --- Navigatie (breed scherm) ----------------------------------------- */
  /* nav en panel vallen weg uit de layout, zodat de groepen en het logo
     directe grid-items van de balk worden. */
  .nav,
  .nav__panel { display: contents; }

  .nav__group {
    display: flex;
    align-items: center;
    /* Vrijwel geen tussenruimte: op de bestaande site liggen Corina en Contact
       tegen elkaar aan (1px ertussen). De padding van de links zelf zorgt voor
       de leesbare afstand. */
    gap: 1px;
  }

  /*
   * Expliciete kolommen én rij. De DOM-volgorde is start, end, logo — de twee
   * groepen zitten samen in het lade-paneel en komen dus vóór het logo.
   *
   * grid-row: 1 is hier niet optioneel: zonder die regel staat de cursor na
   * het plaatsen van `end` in kolom 3, en schuift `logo` (dat terug wil naar
   * kolom 2) door sparse auto-placement naar een tweede rij. Het logo hangt
   * dan onder de balk in plaats van erin.
   */
  /* Marges afgestemd op de bestaande site: 'Aanbod' begint op x=252 en
     'Contact' eindigt op x=1255 bij een container van 1240px. */
  .nav__group--start {
    grid-column: 1;
    grid-row: 1;
    justify-self: start;
    margin-inline-start: clamp(0.5rem, 10.06vw, 9rem);
  }

  .site-header__logo { grid-column: 2; grid-row: 1; }

  .nav__group--end {
    grid-column: 3;
    grid-row: 1;
    justify-self: end;
    margin-inline-end: clamp(0.5rem, 3.83vw, 4rem);
  }

  .nav__link,
  .nav__trigger {
    display: inline-flex;
    align-items: center;
    gap: 0.25em;
    padding: var(--space-xs) var(--space-sm);
    background: none;
    border: 0;
    /* 20px op breed scherm, gelijk aan de huidige site */
    font-size: clamp(1rem, 0.8rem + 0.55vw, 1.25rem);
    font-weight: 400;
    color: var(--c-ink);
    text-decoration: none;
    cursor: pointer;
    transition: color var(--dur-fast) var(--ease);
  }

  .nav__link:hover,
  .nav__trigger:hover,
  .nav__trigger[aria-expanded="true"],
  .nav__link[aria-current="page"] { color: var(--c-primary); }

  .nav__chevron {
    width: 0.6em;
    height: 0.6em;
    border-right: 2px solid currentcolor;
    border-bottom: 2px solid currentcolor;
    rotate: -45deg;
    translate: 0 -0.1em;
    transition: rotate var(--dur-fast) var(--ease);
  }

  .nav__trigger[aria-expanded="true"] .nav__chevron { rotate: 45deg; translate: 0 -0.2em; }

  .nav__submenu {
    position: absolute;
    min-width: 12rem;
    margin: 0;
    padding: var(--space-xs) 0;
    background: var(--c-white);
    box-shadow: var(--shadow-soft);
    display: grid;
  }

  .nav__submenu[hidden] { display: none; }

  .nav__submenu a {
    display: block;
    padding: var(--space-xs) var(--space-lg);
    font-size: var(--fs-small);
    text-decoration: none;
    transition: color var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease);
  }

  .nav__submenu a:hover { color: var(--c-primary); background: var(--c-surface-content); }

  /* --- Hamburger + lade (smal scherm) ----------------------------------- */
  .nav__toggle {
    display: none;
    justify-self: end;
    width: 48px;
    height: 48px;
    padding: 0;
    border: 0;
    background: none;
    cursor: pointer;
  }

  .nav__toggle span,
  .nav__toggle span::before,
  .nav__toggle span::after {
    display: block;
    width: 26px;
    height: 3px;
    background: var(--c-primary);
    transition: transform var(--dur) var(--ease), opacity var(--dur-fast) var(--ease);
  }

  .nav__toggle span { margin-inline: auto; position: relative; }
  .nav__toggle span::before,
  .nav__toggle span::after { content: ""; position: absolute; inset-inline-start: 0; }
  .nav__toggle span::before { top: -8px; }
  .nav__toggle span::after  { top: 8px; }

  .nav__toggle[aria-expanded="true"] span { background: transparent; }
  .nav__toggle[aria-expanded="true"] span::before { transform: translateY(8px) rotate(45deg); }
  .nav__toggle[aria-expanded="true"] span::after  { transform: translateY(-8px) rotate(-45deg); }

  @media (width < 60rem) {
    .site-header__inner { grid-template-columns: 1fr auto; }
    .site-header__logo  { grid-column: 1; justify-self: start; }
    .nav__toggle        { grid-column: 2; display: block; }

    /*
     * De lade schuift onder de balk vandaan en is precies zo hoog als zijn
     * inhoud. Schermvullend zou het niet zijn: drie regels op een wit vlak van
     * 800 hoog is vooral veel leegte. De maximumhoogte is er voor het geval het
     * menu ooit groeit; dan wordt de lade zelf scrollbaar.
     */
    .nav__panel {
      display: flex;
      position: fixed;
      inset: var(--header-nu) 0 auto;
      max-height: calc(100svh - var(--header-nu));
      flex-direction: column;
      align-items: stretch;
      justify-content: flex-start;
      gap: 0;
      padding: 0;
      background: var(--c-white);
      box-shadow: var(--shadow-soft);
      overflow-y: auto;
      overscroll-behavior: contain;
      translate: 0 -100%;
      opacity: 0;
      visibility: hidden;
      transition: translate var(--dur) var(--ease), opacity var(--dur) var(--ease),
                  visibility 0s linear var(--dur);
    }

    .site-header.is-open .nav__panel {
      translate: 0 0;
      opacity: 1;
      visibility: visible;
      transition-delay: 0s;
    }

    .nav__group {
      flex-direction: column;
      align-items: stretch;
      gap: 0;
      grid-column: auto;
      justify-self: auto;
      margin-inline: 0;
    }

    .nav__dropdown { display: block; }

    /*
     * Elke regel is een volle-breedte rij met een haarlijn eronder, zodat het
     * een menu is en geen verzameling losse woorden. 56px hoog: comfortabel
     * aan te tikken.
     */
    .nav__link,
    .nav__trigger {
      display: flex;
      justify-content: center;
      width: 100%;
      min-height: 56px;
      padding: var(--space-sm) var(--gutter);
      font-size: 1.25rem;
      border-block-end: 1px solid var(--c-surface-content);
    }

    /* Het submenu is een inspringend blok onder zijn knop, geen zwevend kaartje */
    .nav__submenu {
      position: static;
      box-shadow: none;
      background: var(--c-surface-content);
      justify-items: stretch;
      gap: 0;
      padding-block: 0;
    }

    .nav__submenu a {
      min-height: 48px;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 1.05rem;
      border-block-end: 1px solid var(--c-white);
    }

    /* Geen haarlijn op de onderrand van de lade zelf; daar staat de schaduw al. */
    .nav__group--end > :last-child { border-block-end: 0; }
  }

  /* --- Hero -------------------------------------------------------------
   * Maten opgemeten op de bestaande site bij 1440px breed: hoogte 600, titel
   * op y=299, knop op y=499. Titel en knop staan dus niet gecentreerd maar in
   * de onderste helft, met 80px ertussen.
   * --------------------------------------------------------------------- */
  .hero {
    position: relative;
    display: grid;
    align-content: end;
    justify-items: center;
    row-gap: var(--hero-gap, clamp(1.5rem, 5.55vw, 5rem));
    /*
     * Vast 600px, net als de bestaande site (min-height op de rij daar). Niet
     * meeschalend met de breedte: op 1920 zou dat 800px worden.
     *
     * De svh-grens is de enige afwijking: op een telefoon van 667px hoog zou
     * 600px bijna het hele scherm vullen.
     */
    min-height: min(var(--hero-h, 600px), 78svh);
    /*
     * De onderruimte bepaalt waar titel en knop uitkomen, want de inhoud is
     * vanaf de onderkant uitgelijnd. Op de bestaande site is die ruimte 151px
     * bij 390 breed en 59px bij 1440; deze formule raakt beide precies. Met de
     * oude clamp bleef er op mobiel 24px over en botste de knop tegen het
     * citaatblok dat 30px over de hero heen komt.
     */
    padding: calc(var(--header-h) + var(--space-lg)) var(--gutter)
             clamp(3.7rem, 185px - 8.76vw, 9.44rem);
    text-align: center;
    isolation: isolate;
    overflow: hidden;
  }

  /*
   * Met een subtitel zijn de twee tussenruimtes ongelijk: op de bestaande site
   * 30px tussen titel en subtitel en 56px tussen subtitel en knop (opgemeten op
   * 1440). Eén row-gap kan dat niet, dus die gaat op nul en de afstanden staan
   * als marges op de elementen zelf.
   */
  .hero--with-subtitle { --hero-gap: 0px; }

  .hero--with-subtitle .hero__subtitle { margin-block-start: clamp(0.75rem, 2.08vw, 1.9rem); }
  .hero--with-subtitle .btn { margin-block-start: clamp(1.25rem, 3.89vw, 3.5rem); }

  /*
   * /corina heeft op de bestaande site 614px — maar alleen zolang die site zijn
   * brede opmaak gebruikt. Gestapeld is ook die hero gewoon 600, en dat is hier
   * geen detail: het citaat hangt 30px over de onderrand van de hero, dus die
   * 14px zou alles eronder meeschuiven.
   */
  @media (width >= 62rem) {
    .hero--tall { --hero-h: 614px; }
  }

  .hero--compact {
    min-height: min(400px, 58svh);
    align-content: center;
  }

  /*
   * Schermvullend, met alles in het midden. Voor de 404: daar is de foto het
   * hele verhaal en staat er verder niets onder.
   *
   * svh en niet vh: op een telefoon rekent vh met een adresbalk die er soms
   * wel en soms niet is, waardoor de pagina net iets te hoog wordt en er een
   * scrollbalk verschijnt voor niets.
   */
  .hero--vol {
    min-height: 100svh;
    align-content: center;
    padding-block: calc(var(--header-h) + var(--space-lg)) var(--space-2xl);
  }

  .hero--vol .hero__subtitle {
    justify-self: center;
    max-width: 34rem;
    text-wrap: pretty;
  }

  .hero {
    background-repeat: no-repeat;
    background-position: 50% 50%;
    background-size: cover;
    /*
     * `fixed` schaalt het beeld op de viewport in plaats van op dit blok. Dat
     * bepaalt de uitsnede — en het laat de foto stilstaan terwijl de tekst
     * eroverheen schuift, precies zoals de bestaande site.
     *
     * iOS negeert background-attachment: fixed en scrollt gewoon mee; dan is
     * de foto nog steeds goed te zien, alleen zonder het stilstaan.
     */
    background-attachment: fixed;
  }

  /*
   * Onder 62rem terug naar `scroll`, precies zoals de bestaande site doet.
   * Dit is geen cosmetisch verschil: bij `fixed` wordt het beeld op de
   * viewport geschaald in plaats van op de hero, en op een telefoon van
   * 390x844 levert dat een heel andere uitsnede op — vooral lucht in plaats
   * van de keien. Bij `scroll` wordt het op het hero-blok van 390x600
   * geschaald en klopt de uitsnede weer.
   */
  @media (width < 62rem) {
    .hero { background-attachment: scroll; }
  }

  /* Eén regel per hero-afbeelding; de klasse komt uit de bestandsnaam.
     image-set() levert WebP waar de browser dat ondersteunt. */
  .hero--bg-hero1 {
    background-image: image-set(
      url("/assets/img/generated/hero1-1200.webp") type("image/webp"),
      url("/assets/img/hero1.jpg") type("image/jpeg")
    );
  }

  .hero--bg-coaching {
    background-image: image-set(
      url("/assets/img/generated/coaching-1200.webp") type("image/webp"),
      url("/assets/img/coaching.png") type("image/png")
    );
  }

  .hero--bg-stilteretraite {
    background-image: image-set(
      url("/assets/img/generated/stilteretraite-1200.webp") type("image/webp"),
      url("/assets/img/stilteretraite.jpg") type("image/jpeg")
    );
  }

  .hero--bg-path-the-way-home {
    background-image: image-set(
      url("/assets/img/generated/path-the-way-home-1200.webp") type("image/webp"),
      url("/assets/img/path-the-way-home.jpg") type("image/jpeg")
    );
  }

  .hero--bg-bloem {
    background-image: image-set(
      url("/assets/img/generated/bloem-1200.webp") type("image/webp"),
      url("/assets/img/bloem.jpg") type("image/jpeg")
    );
  }

  /*
   * Licht grijs, geen zwart. De bestaande site legt hier rgba(206,202,202,.33)
   * overheen; dat wast de foto uit en maakt de donkere tekst erop leesbaar.
   * Met een zwarte laag gebeurt het omgekeerde.
   */
  .hero--overlay::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background: rgb(206 202 202 / 33%);
  }

  .hero--pos-left   { background-position: 0% 50%; }
  .hero--pos-bottom { background-position: 50% 100%; }

  /* Geen ch-maat hier: die rekent niet met de letterspatiëring, waardoor de
     titel veel te vroeg zou afbreken. */
  .hero__title { max-width: 100%; }

  .hero__subtitle {
    /* Over de volle breedte: de hero is een grid met justify-items:center, dus
       zonder stretch krijgt deze alinea alleen zijn max-content breedte en
       breekt de tekst op de verkeerde plek. */
    justify-self: stretch;
    max-width: 100%;
    font-size: 20px;
    line-height: 23px;
    font-weight: 400;
    color: #494242;
  }

  .hero__subtitle strong { font-weight: 700; }

  .hero--light .hero__subtitle { color: var(--c-white); }

  /* --- Citaatblok ------------------------------------------------------- */
  .quote {
    display: grid;
    /* Verticaal bovenin (de plaatsing komt uit --quote-pad-top), horizontaal
       gecentreerd — dat laatste geldt op alle vier de pagina's. */
    align-content: start;
    text-align: center;
    /*
     * Geen text-wrap: pretty hier. Dat algoritme breekt regels eerder af om de
     * rag te verbeteren, waardoor de regelval afwijkt van de bestaande site.
     * Bij een citaat met vaste blokmaten moet de afbreking gewoon greedy zijn.
     *
     * Let op de waarde: text-wrap kent alleen wrap | nowrap | balance | stable
     * | pretty. `normal` bestaat niet en wordt stilzwijgend genegeerd.
     */
    text-wrap: wrap;
    /* circa 20px binnenruimte op 1440 breed */
    padding: clamp(1rem, 1.4vw, 1.25rem);
    /*
     * Geen min-height hier. Die verschilt per pagina en staat in de
     * layout-laag (.composition > .quote). Zou hij hier blijven staan, dan won
     * hij alsnog: de components-laag komt ná layout, en laagvolgorde gaat vóór
     * specificiteit.
     */
    background: var(--c-surface-quote);
    color: var(--c-white);
    font-family: var(--font-display);
    font-size: var(--fs-quote);
    /* 70/70 op de bestaande site */
    line-height: 1;
    letter-spacing: var(--tracking-quote);
  }

  /*
   * Ook op de <p> zelf, niet alleen op het blok: `p { text-wrap: pretty }` uit
   * de basislaag wint van overerving, en dat algoritme breekt regels eerder af
   * om de rag te verbeteren — waardoor de regelval afwijkt van het origineel.
   */
  .quote p { margin: 0; text-wrap: wrap; }

  /* --- Beeld met bijschrift ---------------------------------------------
   * Geen `margin: 0` hier: de reset zet marges al op nul, en omdat de
   * components-laag ná de layout-laag komt zou die shorthand de offsets van
   * .composition__aside overschrijven — laagvolgorde gaat namelijk vóór
   * specificiteit.
   * --------------------------------------------------------------------- */
  .figure { display: grid; gap: var(--space-2xs); }

  .figure img {
    width: 100%;
    height: auto;
    object-fit: cover;
  }

  /*
   * Het beeld hoort vóór het citaat, op élke schermbreedte. Dit moet expliciet:
   * het citaat is `position: relative` vanwege de fly-in, en een gepositioneerd
   * element schildert boven een gewone in-flow broer — ook al staat die later
   * in de DOM.
   *
   * Deze regel stond eerder in het desktop-mediablok, waardoor het gestapeld
   * juist andersom uitpakte.
   */
  .composition > .figure {
    position: relative;
    z-index: 2;
  }


  .figure figcaption {
    font-size: var(--fs-small);
    color: var(--c-ink-muted);
  }

  /* --- Losse beeldband --------------------------------------------------- */
  .band {
    display: block;
    width: 100%;
    max-height: clamp(18rem, 45vw, 37.8rem);
    object-fit: cover;
  }

  .band--center { object-position: center center; }
  .band--bottom { object-position: center bottom; }
  .band--top    { object-position: center top; }

  .prose__cta { margin-block-start: var(--space-xl); }

  /* --- Spreukenblok ------------------------------------------------------ */
  /*
   * Spreukenblok. Dit blok bestaat alleen in de Flutter-versie — op de
   * WordPress-site staan daar enkel twee knoppen zonder tekst. De maten komen
   * dus uit home_page.dart: sectie 570 hoog, kaarten 480x410, knop 250 breed.
   */
  .sayings {
    position: relative;
    display: grid;
    align-content: center;
    min-height: 570px;
    padding-block: var(--space-xl);
    isolation: isolate;
  }

  .sayings__media { position: absolute; inset: 0; z-index: -1; }

  .sayings__media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    /*
     * De foto is staand (1500x2000) en toont de Engel van Culemborg. Bij cover
     * in een sectie van 1425x572 wordt hij 1425x1900 — 1328px speling.
     *
     * 30% is niet geschat maar overgenomen: de bestaande site zet hier
     * `background-position: 0% 30%`, wat neerkomt op 398px omhoog. Stond eerder
     * op 48,6% (647px) en dan zie je een kleine 250px te veel van de
     * onderbouw, met de pijler en de leuning in beeld.
     *
     * Als percentage en niet als vaste pixelwaarde, want de speling is niet
     * constant. Zodra de kaarten stapelen wordt de sectie 927 in plaats van 572
     * hoog en houdt cover nul speling over; een vaste -398px schoof de foto dan
     * volledig uit beeld en liet de onderkant leeg. Een percentage kan per
     * definitie niet verder schuiven dan er speling is.
     *
     * Horizontaal maakt het niets uit: deze foto is altijd breedte-gedreven,
     * dus daar is de speling nul.
     */
    object-position: center 30%;
  }

  .sayings__grid {
    display: grid;
    /* 150px tussen de kaarten */
    gap: clamp(1.5rem, 10.4vw, 150px);
    /* Vaste kaartbreedte van 480, niet uitrekken */
    grid-template-columns: repeat(auto-fit, minmax(0, 480px));
    justify-content: center;
    margin-inline: auto;
  }

  .saying {
    display: grid;
    /* Niet gecentreerd: op de bestaande site staat het blok lager in de kaart,
       met weinig ruimte onder de knop. */
    align-content: end;
    justify-items: center;
    gap: 45px;
    padding: clamp(1.25rem, 2.5vw, 2rem) clamp(1.25rem, 2.5vw, 2rem) 15px;
    min-height: 410px;
    background: var(--c-surface-dark);
    color: #eae6e2;
    text-align: center;
  }

  .saying__text {
    /*
     * Hier wél -webkit-font-smoothing: antialiased, als enige plek op de site.
     * Lichte tekst op een donkere achtergrond rendert met de standaard
     * subpixel-smoothing op macOS merkbaar zwaarder; de rest van de site heeft
     * juist de standaard nodig om niet te dun te ogen.
     */
    -webkit-font-smoothing: antialiased;
    /*
     * Special Elite 32px / 1.4 / 0.5px, zoals in de Flutter-versie. Die versie
     * schaalt helemaal niet mee; gestapeld zakt het hier naar 28, want lager
     * wordt het een ander blok in plaats van hetzelfde blok op een klein scherm.
     */
    font-family: var(--font-saying);
    font-size: clamp(1.75rem, 1.4rem + 1.4vw, 2rem);
    line-height: 1.4;
    letter-spacing: 0.5px;
  }

  /*
   * Gestapeld staan de kaarten onder elkaar en is er breedte over: op de
   * bestaande site lopen ze dan tot vlak langs de schermrand. De tekst gaat
   * hier naar het midden van de kaart in plaats van naar onderen. Op de
   * bestaande site staat de knop namelijk over de laatste regels heen — dat is
   * daar het gevolg van een knop die absoluut onderin gepositioneerd is boven
   * een gecentreerde tekst, niet van een keuze, dus dat nemen we niet over.
   */
  @media (width < 40rem) {
    .sayings > .container { --gutter: 10px; }
    .sayings__grid { gap: 40px; }

    .saying {
      align-content: center;
      gap: 30px;
      padding: 24px 16px;
    }
  }

  /*
   * Achtergrondlagen die hun sectie volledig vullen. De <picture> moet expliciet
   * meeschalen, anders krijgt de <img> zijn natuurlijke hoogte en heeft
   * object-position geen speling om mee te werken.
   */
  .sayings__media > picture,
  .reviews__media > picture,
  .contact__media > picture {
    width: 100%;
    height: 100%;
  }

  /*
   * De balk staat vast bovenaan en ligt over de inhoud heen. Pagina's met een
   * hero houden daar zelf rekening mee; zonder hero moet de eerste sectie de
   * ruimte krijgen, anders begint de tekst achter het logo.
   */
  .main--zonder-hero > :first-child {
    padding-block-start: calc(var(--header-h) + var(--space-xl));
  }

  /* --- Blog: overzicht ---------------------------------------------------
   * Per artikel een strook met de foto links en de tekst rechts. Onder de
   * 56rem stapelt het; de foto houdt zijn verhouding zodat de rij niet
   * verspringt terwijl de beelden laden.
   * --------------------------------------------------------------------- */
  .posts {
    display: grid;
    gap: var(--space-2xl);
    max-width: 64rem;
    margin-inline: auto;
    padding: 0;
    list-style: none;
  }

  .post-card {
    display: grid;
    gap: var(--space-lg);
    align-items: start;
  }

  /*
   * Gestapeld een liggende uitsnede; naast de tekst neemt de foto exact de
   * hoogte van de teaser over (zie het mediablok onderaan dit blok). In beide
   * gevallen snijdt `cover` vanuit het midden bij, dus een staande foto
   * verliest boven en onder en een liggende foto aan de zijkanten.
   *
   * `height` moet erbij: de <img> krijgt via zijn width- en height-attributen
   * al een hoogte mee, en zolang die niet op auto staat doet aspect-ratio
   * niets. Vandaar dat de foto eerst op ware hoogte bleef staan.
   */
  .post-card__beeld,
  .post-card__beeld picture {
    display: block;
  }

  .post-card__beeld img {
    width: 100%;
    height: auto;
    aspect-ratio: 3 / 2;
    /* Gestapeld op een tablet zou 3:2 een foto van ruim 460 hoog geven, en
       dan verdwijnt de teaser onder de vouw. Boven dit plafond snijdt cover
       er boven en onder vanaf. */
    max-height: 20rem;
    object-fit: cover;
    object-position: center;
  }

  .post-card__datum {
    font-size: var(--fs-small);
    color: var(--c-ink-muted);
    margin-block-end: var(--space-2xs);
  }

  .post-card__titel {
    font-family: var(--font-display);
    /* Kleiner dan een sectiekop: dit zijn er meerdere onder elkaar. Ook hier
       geldt dat dit font maar 0,20 em kaphoogte tekent, vandaar de hoge bodem. */
    font-size: clamp(2.5rem, 2.2rem + 1.2vw, 3.125rem);
    line-height: 1.15;
    letter-spacing: var(--tracking-eyebrow);
    color: var(--c-primary);
    margin-block-end: var(--space-sm);
  }

  .post-card__titel a {
    color: inherit;
    text-decoration: none;
  }

  .post-card__titel a:hover { text-decoration: underline; }

  .post-card__intro { margin-block-end: var(--space-md); }

  .post-card__meer {
    display: inline-block;
    color: var(--c-primary);
    font-weight: 700;
    text-decoration: none;
  }

  .post-card__meer:hover { text-decoration: underline; }

  @media (width >= 56rem) {
    .post-card {
      grid-template-columns: 380px minmax(0, 1fr);
      gap: var(--space-xl);
      /* De foto rekt mee met de tekstkolom, zodat er geen wit onder de teaser
         overblijft. Geen vast getal: de teaser bepaalt de hoogte. */
      align-items: stretch;
    }

    .post-card__beeld,
    .post-card__beeld picture { height: 100%; }

    .post-card__beeld img {
      height: 100%;
      /* De verhouding komt nu uit de hoogte van de tekst ernaast */
      aspect-ratio: auto;
      max-height: none;
    }
  }

  /* --- Blog: artikel ------------------------------------------------------ */
  .artikel__kop { text-align: center; }

  .artikel__datum {
    font-size: var(--fs-small);
    color: var(--c-ink-muted);
    margin-block-end: var(--space-sm);
  }

  .artikel__titel { margin-block-end: var(--space-lg); }

  .artikel__intro {
    max-width: 46rem;
    margin-inline: auto;
    font-size: var(--fs-lead);
    font-style: italic;
  }

  .artikel__tekst {
    max-width: 46rem;
    margin-inline: auto;
  }

  /* De tussenkopjes in een artikel zijn vette Raleway, net als in de
     accordeon — geen typemachine, anders wordt de pagina onrustig. */
  .artikel__tekst strong { font-weight: 700; }

  .artikel__beelden {
    display: grid;
    gap: var(--space-md);
    max-width: 46rem;
    margin-inline: auto;
    margin-block-start: var(--space-xl);
  }

  @media (width >= 48rem) {
    .artikel__beelden:has(> :nth-child(2)) {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }
  }

  .artikel__terug {
    max-width: 46rem;
    margin-inline: auto;
    margin-block-start: var(--space-2xl);
  }

  .artikel__terug a {
    color: var(--c-primary);
    text-decoration: none;
  }

  .artikel__terug a:hover { text-decoration: underline; }

  /* --- Reviews ------------------------------------------------------------
   * Maten uit review_block3.dart: sectie 600 hoog, inhoud max 800 breed,
   * zwarte overlay op 10%, sterren in amber, alles direct op de foto.
   * --------------------------------------------------------------------- */
  .reviews {
    position: relative;
    display: grid;
    align-content: center;
    min-height: 600px;
    padding-block: var(--space-xl);
    isolation: isolate;
  }

  .reviews__media { position: absolute; inset: 0; z-index: -1; }

  .reviews__media img { width: 100%; height: 100%; object-fit: cover; }

  .reviews__media::after {
    content: "";
    position: absolute;
    inset: 0;
    background: rgb(0 0 0 / 10%);
  }

  /*
   * min-width: 0 is hier geen detail. .reviews is een raster, dus de container
   * is een rasteritem, en die krijgen automatisch een ondergrens ter grootte
   * van hun inhoud. Die ondergrens wint van de breedte die .container zelf
   * opgeeft: op een scherm van 390 werd de container 1052 breed en liep de
   * reviewtekst gewoon buiten beeld door. Op elk niveau tot aan de review zelf
   * die ondergrens uitzetten, anders duwt de onderste hem er weer doorheen.
   */
  .reviews > .container { min-width: 0; }

  .reviews__track {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 100%;
    min-width: 0;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
  }

  .reviews__track::-webkit-scrollbar { display: none; }

  /* Geen kaart: de tekst staat direct op de foto */
  .review {
    scroll-snap-align: center;
    display: grid;
    justify-items: center;
    min-width: 0;
    max-width: 800px;
    margin-inline: auto;
    padding-inline: var(--space-md);
    text-align: center;
    color: var(--c-white);
  }

  .review__stars {
    display: flex;
    gap: 4px;
    color: #ffc107;
    margin-block-end: 32px;
  }

  .review__title {
    /*
     * NIET de 18px uit de Flutter-code overnemen: daar stond atwriter onder,
     * met een kaphoogte van 0,73 em. Apothecary tekent zijn glyphs veel kleiner
     * in de em-box (0,20 em), dus 18px levert daar 3,6px letterhoogte op —
     * onleesbaar.
     *
     * 56px geeft 11,2px kaphoogte, in lijn met de andere kopjes op de site, en
     * is tegelijk het maximum waarbij de langste titel ("Verbinding door de
     * waarheid te vertellen", 39 tekens) nog op één regel binnen de 800px past.
     *
     * Extra letterspatiëring is niet nodig: met een advance van 0,35 em tegen
     * een kaphoogte van 0,20 em staat dit font vanzelf al ruim.
     */
    font-family: var(--font-display-clean);
    /*
     * 18px met 9px spatiëring, exact zoals review_block3.dart. Dit font heeft
     * een kaphoogte van 0,73 em, dus dat levert hier 13px letterhoogte op.
     *
     * Die spatiëring maakt de langste titel 733px breed. Dat past vanaf ~800px
     * schermbreedte; daaronder loopt het vloeiend terug naar 16/4, zodat de
     * titel op één regel blijft in plaats van in drieën te breken.
     */
    font-size: clamp(1rem, 0.92rem + 0.32vw, 18px);
    line-height: 1.4;
    letter-spacing: clamp(4px, 0.9px + 0.79vw, 9px);
    font-weight: 700;
    color: var(--c-white);
    margin-block-end: 24px;
  }

  .review__text {
    /* 20px zoals review_block3.dart, maar de langste review is 470 tekens: op
       een kolom van 280 werd dat 22 regels en een sectie van 1055 hoog. */
    font-size: clamp(1rem, 0.75rem + 1vw, 1.25rem);
    line-height: 1.6;
    letter-spacing: 0.2px;
    font-style: italic;
    color: var(--c-white);
    margin-block-end: 24px;
  }

  .review__author {
    font-size: 16px;
    font-weight: 400;
    color: rgb(255 255 255 / 70%);
  }

  /*
   * De bediening blijft onzichtbaar tot je er met de muis overheen gaat of
   * erin tabt. Zo blijft het beeld rustig zoals bedoeld, terwijl de carrousel
   * wel te pauzeren en te bedienen is.
   */
  .reviews__controls {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: var(--space-sm);
    margin-block-start: var(--space-lg);
    opacity: 0;
    transition: opacity var(--dur) var(--ease);
  }

  .reviews:hover .reviews__controls,
  .reviews:focus-within .reviews__controls { opacity: 1; }

  /* Smal scherm: minder lucht rondom, want de sectie is hier toch al hoger dan
     de 600 uit het ontwerp. */
  @media (width < 40rem) {
    .review { padding-inline: var(--space-sm); }
    .review__stars { margin-block-end: 16px; }
    .review__title { margin-block-end: 16px; }
  }

  .reviews__dots { display: flex; gap: var(--space-xs); }

  .reviews__dot {
    width: 12px;
    height: 12px;
    padding: 0;
    border: 2px solid var(--c-white);
    border-radius: 50%;
    background: transparent;
    cursor: pointer;
    transition: background-color var(--dur-fast) var(--ease);
  }

  .reviews__dot[aria-current="true"] { background: var(--c-white); }

  .reviews__arrow {
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    border: 0;
    border-radius: 50%;
    background: rgb(255 255 255 / 85%);
    color: var(--c-ink);
    font-size: 1.1rem;
    cursor: pointer;
    transition: background-color var(--dur-fast) var(--ease);
  }

  .reviews__arrow:hover { background: var(--c-white); color: var(--c-primary); }

  /* --- Accordeon (native details/summary) -------------------------------- */
  .accordion { display: grid; gap: var(--space-sm); max-width: 64rem; margin-inline: auto; }

  .accordion__heading { margin-block-end: var(--space-xl); }
  .accordion__note {
    margin-block-start: var(--space-xl);
    max-width: 60ch;
    margin-inline: auto;
  }
  .accordion__cta { margin-block-start: var(--space-lg); }


  .accordion__item { background: var(--c-surface-soft); }

  .accordion__summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-md);
    /* 42 hoog, zoals de bestaande site; met 23px regelhoogte blijft er 9,5px
       ruimte boven en onder. */
    height: 42px;
    padding-block: 0;
    padding-inline: var(--space-lg);
    background: var(--c-cta);
    color: var(--c-white);
    /* De bestaande site gebruikt hier Raleway 20/23, geen typemachine-font */
    font-size: 20px;
    line-height: 23px;
    cursor: pointer;
    list-style: none;
    transition: background-color var(--dur) var(--ease);
  }

  .accordion__summary::-webkit-details-marker { display: none; }
  .accordion__summary:hover { background: var(--c-cta-hover); }
  .accordion__item[open] .accordion__summary { background: #37342f; }

  /* Plusje dat een minnetje wordt: de verticale streep verdwijnt bij openen. */
  .accordion__icon {
    position: relative;
    flex: none;
    width: 18px;
    height: 18px;
  }

  .accordion__icon::before,
  .accordion__icon::after {
    content: "";
    position: absolute;
    inset-block-start: 50%;
    inset-inline-start: 0;
    width: 100%;
    height: 2px;
    background: currentcolor;
    translate: 0 -50%;
  }

  .accordion__icon::after {
    rotate: 90deg;
    transition: opacity var(--dur) var(--ease), rotate var(--dur) var(--ease);
  }

  .accordion__item[open] .accordion__icon::after { opacity: 0; rotate: 0deg; }

  /*
   * Zonder JavaScript klapt <details> gewoon open en dicht; dat is de
   * terugval. Met JavaScript animeert site.js de hoogte van dit paneel, want
   * een CSS-transitie werkt hier niet: een gesloten <details> zet zijn inhoud
   * op display:none, dus er valt niets te animeren.
   */
  .accordion__panel { overflow: hidden; }

  .accordion__panel > div { overflow: hidden; }

  /* Zelfde tekstopmaak als de rest van de bestaande site: 20/23 met 16px
     tussenruimte, en lijsten met gewone bullets op 40px inspringing. */
  .accordion__content {
    padding: var(--space-lg);
    font-size: 20px;
    line-height: 23px;
  }

  .accordion__content > * + * { margin-block-start: 16px; }

  .accordion__content ul {
    padding-inline-start: 40px;
    list-style: disc;
  }

  .accordion__content strong { font-weight: 700; }

  .facts { display: grid; gap: var(--space-xs); }
  .facts div { display: flex; gap: 0.4em; flex-wrap: wrap; }
  .facts dt { font-weight: 700; }
  .facts dd { margin: 0; }

  .note { font-size: var(--fs-small); color: var(--c-ink-muted); }

  /* --- Contact ------------------------------------------------------------
   * Dit blok is op de bestaande site tegelijk de footer. Maten daar opgemeten:
   * sectie 680 hoog, foto met background-position 0% 120%, en een WIT masker
   * van 66% eroverheen — geen donker masker, en geen kaart om het formulier.
   * --------------------------------------------------------------------- */
  .contact {
    position: relative;
    display: grid;
    align-content: center;
    min-height: 680px;
    padding-block: var(--space-lg);
    background: var(--c-white);
    isolation: isolate;
  }

  .contact__media { position: absolute; inset: 0; z-index: -1; }

  .contact__media > picture { width: 100%; height: 100%; }

  .contact__media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: 0% 120%;
  }

  .contact__media::after {
    content: "";
    position: absolute;
    inset: 0;
    /* Wit, niet zwart: de foto wordt uitgewassen en de tekst blijft donker. */
    background: rgb(255 255 255 / 66%);
  }

  .contact__grid {
    display: grid;
    gap: var(--space-xl);
    grid-template-columns: minmax(0, 1fr);
  }

  @media (width >= 60rem) {
    /* De bestaande site gebruikt hier een container van 1280 in plaats van
       1240, met kolommen van 563 en 525 en 179 ertussen. */
    .contact > .container { width: min(100% - 40px, 1280px); }

    .contact__grid {
      grid-template-columns: 563px 525px;
      column-gap: 179px;
      justify-content: start;
    }
  }

  .contact__heading {
    font-family: var(--font-display);
    margin-block-end: var(--space-md);
    /* 60px met -3px spatiëring, zoals de andere kopjes op de bestaande site */
    font-size: var(--fs-eyebrow);
    line-height: 1;
    letter-spacing: var(--tracking-eyebrow);
    color: var(--c-ink);
  }

  .contact__address,
  .contact__lines {
    font-style: normal;
    line-height: 1.5;
  }

  .contact__lines { margin-block-start: var(--space-md); }
  .contact__lines a { color: var(--c-ink); }
  .contact__col--details .note { margin-block-start: var(--space-lg); }

  .contact__copyright {
    margin-block-start: var(--space-lg);
    text-align: center;
    font-size: var(--fs-small);
  }

  .contact__copyright a { color: var(--c-ink); }

  /* Krap gehouden: anders groeit de sectie ruim voorbij de 680 van het origineel */
  .form { display: grid; gap: var(--space-sm); }

  .form__row {
    display: grid;
    gap: var(--space-md);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
  }

  .field { display: grid; gap: var(--space-2xs); }

  .field label { font-size: var(--fs-small); font-weight: 700; }

  .field input,
  .field textarea {
    width: 100%;
    padding: 0.7em 0.9em;
    border: 1px solid rgb(0 0 0 / 14%);
    border-radius: var(--radius);
    background: var(--c-white);
    font-size: var(--fs-body);
    transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
  }

  .field input:focus,
  .field textarea:focus {
    outline: 0;
    border-color: var(--c-primary);
    box-shadow: 0 0 0 3px rgb(156 6 19 / 18%);
  }

  .field textarea { resize: vertical; min-height: 5.5rem; }

  .field--invalid input,
  .field--invalid textarea { border-color: var(--c-cta); }

  .field__error { font-size: var(--fs-small); color: var(--c-primary); }
  .field__error:empty { display: none; }

  .checkbox { display: flex; gap: var(--space-sm); align-items: flex-start; font-size: var(--fs-small); }
  .checkbox input { width: 1.25rem; height: 1.25rem; flex: none; margin-block-start: 0.15em; accent-color: var(--c-primary); }
  .checkbox a { color: var(--c-primary); }

  /* Honeypot: onzichtbaar voor mensen, wel invulbaar voor bots */
  .hp {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .form__status { display: grid; gap: var(--space-xs); padding: var(--space-md); font-size: var(--fs-small); }
  .form__status:empty { display: none; }
  .form__status[data-state="ok"]    { background: #e6f0e6; border-inline-start: 4px solid #2e7d32; }
  .form__status[data-state="error"] { background: #fbe9e9; border-inline-start: 4px solid var(--c-cta); }
  .form__status[data-state="busy"]  { background: var(--c-surface-quote); color: var(--c-ink); }

  /* De footer is geen apart blok meer: het contactblok vervult die rol,
     precies zoals op de bestaande site. Zie .contact hierboven. */

  /* --- Skiplink ----------------------------------------------------------- */
  .skip-link {
    position: absolute;
    inset-block-start: 0;
    inset-inline-start: 50%;
    translate: -50% -120%;
    z-index: 200;
    padding: var(--space-sm) var(--space-lg);
    background: var(--c-primary);
    color: var(--c-white);
    text-decoration: none;
    transition: translate var(--dur) var(--ease);
  }

  .skip-link:focus { translate: -50% 0; color: var(--c-white); }
}

/* ==========================================================================
   UTILITIES
   ========================================================================== */
@layer utilities {
  .visually-hidden:not(:focus):not(:active) {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .text-center { text-align: center; }

  /*
   * Het citaatblok vliegt bij het openen van de pagina in, met een fade en een
   * halve seconde vertraging. De tekening eronder is direct zichtbaar — je ziet
   * dus eerst alleen haar, en dan schuift het citaat eroverheen.
   *
   * fill-mode `both` houdt vóór de vertraging de begintoestand vast, dus het
   * blok is die eerste halve seconde onzichtbaar.
   */
  /*
   * 375px, en de richting volgt de kant waar het citaat staat: van rechts op
   * home en corina, van links op coaching en stilteretraite. --fly-x komt uit
   * compositions.json.
   *
   * Eerder hing dit aan een klasse .composition--mirror; die verviel toen de
   * geometrie per pagina ging. Vandaar dat het van links invliegen stil was
   * komen te vallen.
   */
  @keyframes quote-fly-in {
    from { opacity: 0; translate: var(--fly-x, 375px) 0; }
    to   { opacity: 1; translate: 0 0; }
  }

  /*
   * Tekengrootte en binnenmarges van het citaat verschillen per pagina: 70px
   * op home, 65 op corina, 60 op coaching en stilteretraite, met 20 of 50
   * zijmarge. De waarden komen uit compositions.json.
   *
   * Deze regels staan bewust hier in de components-laag en niet bij de rest van
   * de compositie in de layout-laag: components komt later, dus daar zou de
   * basisregel van .quote hierboven overheen gaan.
   */
  @media (width >= 87.5rem) {
    .composition > .quote {
      font-size: var(--quote-fs);
      /* -6 op home en corina, -3 op coaching en stilteretraite */
      letter-spacing: var(--quote-ls);
      padding-inline: var(--quote-pad-x);
      padding-block: 0;
    }

    /* Verticale plek van de tekst in het blok; op home negatief, omdat de
       regelbox van dit font boven het blok uitsteekt. */
    .composition > .quote > p { margin-block-start: var(--quote-pad-top); }
  }

  /*
   * Gestapeld heeft het citaat eigen maten, ook weer per pagina: 60px op home,
   * 55 op de rest, en de tekst begint 0 tot 50px onder de bovenrand. De
   * zijmarge is overal 20. Zie compositions.json onder `gestapeld`.
   */
  @media (width < 87.5rem) {
    .composition > .quote {
      font-size: var(--st-quote-fs);
      letter-spacing: var(--st-quote-ls);
      padding-inline: 20px;
      padding-block: 0;
    }

    .composition > .quote > p { margin-block-start: var(--st-quote-pad-top); }
  }

  /*
   * z-index expliciet: het citaat hoort ACHTER de tekening. Zonder dit komt het
   * juist ervoor, omdat `translate` uit de animatie een stacking context maakt
   * en zo'n element boven een gewone in-flow broer schuift — ook al staat die
   * later in de DOM.
   */
  .quote {
    position: relative;
    z-index: 1;
    animation: quote-fly-in 900ms var(--ease) 500ms both;
  }

  /* --- Reveal bij scrollen ------------------------------------------------ */
  .reveal {
    opacity: 0;
    transition: opacity var(--dur-slow) var(--ease),
                translate var(--dur-slow) var(--ease);
  }

  /* Op smalle schermen alleen verticaal bewegen: zijwaarts schuiven oogt daar
     onrustig en heeft geen ruimte. */
  .reveal--right,
  .reveal--left { translate: 0 2rem; }
  .reveal--up   { translate: 0 2rem; }

  @media (width >= 60rem) {
    .reveal--right { translate: 3rem 0; }
    .reveal--left  { translate: -3rem 0; }
  }

  .reveal.is-visible { opacity: 1; translate: 0 0; }

  /* Zonder JS moet alles gewoon zichtbaar zijn */
  .no-js .reveal { opacity: 1; translate: 0 0; }

  /* Wie minder beweging wil, krijgt geen beweging. */
  @media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
      animation-duration: 0.01ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: 0.01ms !important;
      scroll-behavior: auto !important;
    }

    .reveal { opacity: 1; translate: 0 0; }
  }
}
