/* Base (from the original Claude Design canvas) */
html { scroll-behavior: smooth; }
body { margin: 0; background: #FFFCF6; color: #2C2620; font-family: 'Jost', system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
* { box-sizing: border-box; }
a { color: #7C6B4C; text-decoration: none; }
a:hover { color: #5E5039; }
img { display: block; max-width: 100%; }
::selection { background: #DFC79E; }
:focus-visible { outline: 2px solid #7C6B4C; outline-offset: 3px; }
summary::-webkit-details-marker { display: none; }

/* Home "Waar ik je bij help" service cards -- subtle grow on hover. */
.service-card { transition: transform 0.3s ease; }
.service-card:hover, .service-card:focus-within { transform: scale(1.03); }

@keyframes marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@keyframes rise { from { opacity: 0; transform: translateY(18px); } to { opacity: 1; transform: none; } }
@keyframes blink { 0%, 45% { opacity: 1; } 55%, 100% { opacity: 0; } }
@keyframes typedot { 0%, 60%, 100% { transform: translateY(0); opacity: 0.45; } 30% { transform: translateY(-6px); opacity: 1; } }
@keyframes pop { from { opacity: 0; transform: translateY(10px) scale(0.94); } to { opacity: 1; transform: none; } }

/* ---------- Page routing (SPA-style, matches the canvas's internal state) ---------- */
.page { display: none; }
.page.active { display: block; }

/* ---------- Nav "Diensten" dropdown ---------- */
.nav-dropdown-panel { display: none; }
.nav-dropdown:hover .nav-dropdown-panel,
.nav-dropdown:focus-within .nav-dropdown-panel { display: block; }

/* Buttons used as nav links / page-router triggers should look like the rest of the design, not browser defaults */
button { font-family: inherit; }

/* Testimonial typing/reveal bubbles and the contact "sent" state rely on the
   native [hidden] attribute (toggled from js/main.js) — no extra CSS needed. */

/* ---------- Portfolio: laptop mockup with hover-scroll screenshot ----------
   Uses the real MacBook render (images/laptop-mockup.png), cropped tight to
   the visible laptop (no more dead transparent margin) so the frame fills
   its card and there's no big gap before the caption. The screenshot is
   overlaid inside the screen area (measured in code from the source PNG). */
.laptop-mockup-img {
  display: block;
  position: relative;
  width: 100%;
}

.laptop-mockup-img .frame-img {
  display: block;
  width: 100%;
  height: auto;
  pointer-events: none;
  transition: transform 0.35s ease, filter 0.35s ease;
}

.laptop-mockup-img:hover .frame-img,
.laptop-mockup-img:focus-visible .frame-img,
.laptop-mockup-img.is-active .frame-img {
  transform: translateY(-4px);
  filter: drop-shadow(0 22px 30px rgba(44, 38, 32, 0.28));
}

/* "Sticker" badge, top-right corner of the mockup — like a "bekijk live" tag */
.portfolio-badge {
  position: absolute;
  top: 1%;
  right: 3%;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: #7C6B4C;
  color: #FAF4EB;
  font-family: 'Jost', sans-serif;
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 12px 18px;
  border-radius: 999px;
  box-shadow: 0 14px 26px -10px rgba(44, 38, 32, 0.5);
  transition: transform 0.3s ease, background 0.3s ease;
}
.laptop-mockup-img:hover .portfolio-badge,
.laptop-mockup-img:focus-visible .portfolio-badge,
.laptop-mockup-img.is-active .portfolio-badge {
  transform: scale(1.06);
  background: #5E5039;
}

.laptop-mockup-img .screen-overlay {
  position: absolute;
  left: 10.65%;
  right: 10.81%;
  top: 6.69%;
  bottom: 9.47%;
  overflow: hidden;
  border-radius: 2px;
  background: #fff;
}

.laptop-screenshot {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top;
  display: block;
  transition: object-position 6s ease;
}

/* On hover, the screenshot scrolls from top to bottom inside the screen —
   pure CSS (object-position is animatable), no JS needed. */
.laptop-mockup-img:hover .laptop-screenshot,
.laptop-mockup-img:focus-visible .laptop-screenshot,
.laptop-mockup-img.is-active .laptop-screenshot {
  object-position: bottom;
}

/* On touch devices (no real :hover), .is-active is toggled by JS: the first
   tap plays the scroll-through preview, a second tap on an already-active
   mockup follows the link. The badge text has to say so: "Scroll" up front
   (hinting what a tap does), then "Bekijk live — tik nogmaals" once active
   (hinting the second tap opens the site). The real "Bekijk live" text node
   stays in the DOM for accessibility — only visually swapped here. */
@media (hover: none) {
  .portfolio-badge {
    font-size: 0;
  }
  .portfolio-badge::before {
    content: "Scroll";
    font-size: 12.5px;
  }
  .laptop-mockup-img.is-active .portfolio-badge::before {
    content: "Bekijk live — tik nogmaals";
  }
}

/* ---------- Generic cross-fade group ----------
   Any .fade-group's .fade-item children stack via position:absolute (so
   rotating which one is visible never reflows the layout) and cross-fade
   through the opacity transition below. main.js rotates .is-active on an
   interval; this file only supplies the visual fade. */
.fade-group { position: relative; }
.fade-item {
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.9s ease;
}
.fade-item.is-active {
  opacity: 1;
  pointer-events: auto;
}

/* ---------- Generic scroll-into-view text reveal ----------
   Any .scroll-reveal element starts invisible/lowered and fades+lifts up
   the first time it's actually scrolled into view -- .in-view is added
   once by js/main.js via IntersectionObserver (generic block, one
   observer per element) and never removed, so scrolling back up doesn't
   undo it. Started life as .opmaat-devices-fade (webdesign.html's
   "Website op maat" only) and got generalized here once the same effect
   was wanted across the rest of the page's sections too -- kept generic
   (no per-section name) so any future section can opt in with just this
   one class, no new CSS/JS. Text only by convention (headings, labels,
   paragraphs) -- buttons, photos and anything already carrying its own
   reveal/animation (e.g. .kw-slide's crossfade, .werkwijze-fade's
   scroll-pinned mask) are left out to avoid two effects fighting over
   the same opacity. */
.scroll-reveal {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 0.9s ease, transform 0.9s ease;
}
.scroll-reveal.in-view { opacity: 1; transform: translateY(0); }
@media (prefers-reduced-motion: reduce) {
  .scroll-reveal { opacity: 1; transform: none; transition: none; }
}

/* ---------- Webdesign hero: fading portfolio showcase ----------
   .hero-shots is the .fade-group; each .fade-item is a link wrapping one
   of the images/hero-shot-*.png graphics. Those PNGs are pre-cropped and
   rescaled versions of the original images/portfolio-*.png collages (see
   the crop script this was generated with) so every one's laptop lands
   at the same size/position on its own 1200x1200 canvas -- since the box
   below shares that same 1:1 ratio, no object-fit math is needed to keep
   the laptop steady across fades, just plain 100%/100% sizing. */
.hero-shots {
  width: 100%;
  aspect-ratio: 1 / 1;
}
.hero-shots .fade-item {
  display: block;
}
.hero-shots .fade-item img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}

/* ---------- Webdesign page: minimal "trust grid" ----------
   No card boxes/shadows -- a checkmark, a small letter-spaced heading and
   centered body copy per column, with a thin vertical rule between
   columns instead of individual borders. A left-border divider only
   makes sense within a single row, so it collapses to a top border once
   the grid wraps to 2 columns, then to 1. */
.expect-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
}
.expect-item {
  padding: 0 28px;
  text-align: center;
  border-left: 1px solid rgba(124, 107, 76, 0.22);
}
.expect-item:first-child { border-left: none; }
.expect-check {
  width: 18px;
  height: 18px;
  margin: 0 auto 20px;
  display: block;
  color: #7C6B4C;
}
.expect-heading {
  margin: 0 0 16px;
  font-family: 'Jost', sans-serif;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #7C6B4C;
}
.expect-text {
  margin: 0;
  font-size: 15.5px;
  line-height: 1.75;
  color: #57503F;
  font-weight: 300;
}
.expect-text strong { color: #4A4238; font-weight: 500; }

@media (max-width: 900px) {
  .expect-grid { grid-template-columns: 1fr 1fr; row-gap: 40px; }
  .expect-item:nth-child(2n+1) { border-left: none; }
  .expect-item:nth-child(n+3) { border-top: 1px solid rgba(124, 107, 76, 0.22); padding-top: 36px; }
  /* Match the body-copy size used in the "Beste investering" section right
     above this one on mobile (its paragraphs are 17px/1.8), instead of
     staying at the smaller desktop size. */
  .expect-text { font-size: 17px; line-height: 1.8; }
}
@media (max-width: 620px) {
  .expect-grid { grid-template-columns: 1fr; row-gap: 36px; }
  .expect-item { border-left: none !important; }
  .expect-item:not(:first-child) { border-top: 1px solid rgba(124, 107, 76, 0.22); padding-top: 32px; }
  /* Extra breathing room on the smallest screens -- the section's own 5vw
     padding shrinks fast on a narrow phone, so it's topped up here rather
     than raised sitewide for every .mobile-reorder-style section.
     !important: both sections set their own padding shorthand inline,
     which otherwise always wins over a plain external declaration
     regardless of media query, no matter how it's ordered in the file. */
  .expect-section { padding-left: 9vw !important; padding-right: 9vw !important; }
  /* On mobile, "Beste investering" stacks as title -> photo (see
     .mobile-reorder), so its own bottom padding plus this section's top
     padding is the whole gap between that photo and "Wat je van mij mag
     verwachten" below it -- both same background color, so shrinking it
     doesn't introduce a visible seam. Desktop (photo beside the text, not
     above it) keeps the original rhythm. */
  .invest-section { padding-bottom: 6px !important; }
  .expect-section { padding-top: 6px !important; }
}

/* "De details" section (webdesign.html): photo + 2x2 category breakdown.
   On mobile, text comes first (with its 2x2 grid collapsed to a single
   column, so the 4 categories read top-to-bottom in DOM order --
   Strategie, Website design, Basic SEO, Lancering) and the photo drops to
   the bottom, instead of desktop's photo-left/text-right layout.
   !important throughout: both grids set their own grid-template-columns
   inline, which otherwise always wins over an external declaration. */
@media (max-width: 900px) {
  .details-reorder { grid-template-columns: 1fr !important; }
  .details-photo-col { order: 2; }
  .details-text-col { order: 1; }
  .details-grid { grid-template-columns: 1fr !important; }
  /* Centered, like "Website op maat" right above this section, instead of
     staying left-aligned now that it's a single column. */
  .details-text-col { text-align: center; }
  /* List items (nested one level deeper than the category labels) sized
     to match that same section's 17px/1.8 body copy -- !important since
     each item's own font-size/line-height is set inline. */
  .details-grid > div > div > p { font-size: 17px !important; line-height: 1.8 !important; }
}

/* ---------- Content page: two-row video showcase (hover-to-play marquee) ---------- */
.video-showcase {
  background: #2C2620;
  padding: 40px 0 48px;
  overflow: hidden;
}

/* Light variant — same marquee, used where it sits on a light section (e.g. home) */
.video-showcase--light {
  background: #F8F2E6;
  /* More breathing room than the base variant: above "Werk uit de feed"
     and below the second video row — homepage-only. */
  padding: 90px 0;
}
.video-showcase--light .video-card {
  box-shadow: 0 18px 34px -20px rgba(63, 59, 59, 0.28);
  /* Wider cards so at most 6 are ever visible across the marquee's own
     1400px cap (6 × 220px + 5 × 16px gap = 1400px), instead of the ~8 the
     default 150px card width fit — homepage-only, via this light variant. */
  width: 220px;
}
.video-showcase--light .video-marquee {
  /* More room between the "Werk uit de feed" text and the first row. */
  margin-top: 44px;
}

.video-marquee {
  max-width: 1400px;
  margin: 22px auto 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.video-row {
  overflow: hidden;
  /* fade the row's edges so cards scroll in/out softly instead of clipping hard */
  mask-image: linear-gradient(90deg, transparent, #000 4%, #000 96%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 4%, #000 96%, transparent);
}

.video-track {
  display: flex;
  gap: 16px;
  width: max-content;
  animation: video-track-left 46s linear infinite;
}

.video-row-reverse .video-track {
  animation-name: video-track-right;
  animation-duration: 38s;
}

/* .video-row-mobile-only: hidden on desktop, shown below 900px -- for a
   marquee that's deliberately trimmed to one row on desktop (e.g.
   content-creatie.html's "Mijn werk", where the second row was removed
   to keep the section shorter) but should still show a second row on
   mobile, where the request was to keep both. Same 900px handoff as the
   other desktop/mobile split components on the site (.feed-devices-
   desktop/-mobile, .testi-desktop/-mobile). */
.video-row-mobile-only { display: none; }
@media (max-width: 900px) {
  .video-row-mobile-only { display: block; }
}

/* .case-proof--mobile-only: same desktop/mobile handoff as
   .video-row-mobile-only above, but for a single .case-proof inside a
   .case-media--collage -- the Get Fit with Yasmin "voedingsplannen" shot
   made that collage too tall on desktop (it's a full stitched page
   capture, so height:auto renders it very long), but should stay exactly
   as it was on mobile, where there's no height complaint. */
.case-proof.case-proof--mobile-only { display: none; }
@media (max-width: 900px) {
  .case-proof.case-proof--mobile-only { display: block; }
}

/* .mobile-only: same desktop/mobile handoff as .video-row-mobile-only
   above, but for hiding a whole section on desktop while keeping it on
   mobile -- first used on index.html's "Ons werk" video marquee, which the
   client wants dropped from the desktop homepage (redundant with the
   laptop/portfolio sections above it there) but kept as-is on mobile. */
.mobile-only { display: none; }
@media (max-width: 900px) {
  .mobile-only { display: block; }
}

/* index.html's "Ons werk" ONLY -- now that its label/heading/button are
   gone (see the HTML comment on .video-showcase.mobile-only there), the
   base .video-showcase padding (40px top, 48px bottom -- uneven because
   it used to sit right under that header div, not because of the videos
   themselves) read as lopsided with nothing but the marquee left in the
   section. Scoped to .mobile-only so content-creatie.html's and
   portfolio.html's own .video-showcase instances, which still have that
   header, keep the original padding. */
.video-showcase.mobile-only { padding-top: 48px; }

.video-row:hover .video-track {
  animation-play-state: paused;
}

@keyframes video-track-left {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}
@keyframes video-track-right {
  from { transform: translateX(-50%); }
  to { transform: translateX(0); }
}

.video-card {
  position: relative;
  flex: none;
  width: 150px;
  aspect-ratio: 9 / 16;
  border-radius: 16px;
  overflow: hidden;
  background: #1a1611;
  box-shadow: 0 18px 34px -18px rgba(0, 0, 0, 0.6);
}

.video-el {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  filter: brightness(0.82) saturate(0.95);
  transition: filter 0.35s ease, transform 0.35s ease;
}

.video-card:hover .video-el,
.video-card:focus-visible .video-el {
  filter: brightness(1.05) saturate(1.05);
  transform: scale(1.035);
}

@media (max-width: 620px) {
  .video-card { width: 120px; }
  /* .video-showcase--light .video-card (2 classes) otherwise outranks this
     plain single-class rule regardless of media query, and would keep the
     homepage's cards at the wider desktop 220px here too. */
  .video-showcase--light .video-card { width: 120px; }
}

/* ---------- Portfolio: client-feed marquee, one row on desktop ----------
   Two independent .video-marquee tracks (see portfolio.html), swapped by
   this same 620px line -- same technique as .testi-mobile/.testi-desktop.
   Below it, the normal two-row markup (untouched). Above it, a single row
   cycling all 12 photos instead of splitting them across two rows, per
   request -- picking one over the other by breakpoint rather than
   reshuffling which photos go where at runtime. */
.feed-marquee--desktop { display: none; }
@media (min-width: 621px) {
  .feed-marquee--mobile { display: none; }
  .feed-marquee--desktop { display: flex; }
}

/* ---------- FAQ accordion (animated expand/collapse) ---------- */
.faq-q {
  width: 100%;
  padding: 24px 0;
  background: none;
  border: 0;
  cursor: pointer;
  text-align: left;
  font-family: 'Cormorant Garamond', serif;
  font-size: 24px;
  color: #3A342B;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 20px;
}

.faq-icon {
  flex: none;
  color: #7C6B4C;
  font-size: 22px;
  line-height: 1;
  transition: transform 0.3s ease;
}

.faq-item.open .faq-icon {
  transform: rotate(135deg);
}

/* grid-template-rows 0fr -> 1fr is the standard trick for animating
   height:auto content without measuring pixel heights in JS */
.faq-a-wrap {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.4s ease;
}

.faq-item.open .faq-a-wrap {
  grid-template-rows: 1fr;
}

.faq-a-inner {
  overflow: hidden;
}

/* ---------- Floating "Plan afspraak" booking widget ---------- */
#booking-fab {
  position: fixed;
  right: 26px;
  bottom: 26px;
  z-index: 300;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  background: #7C6B4C;
  color: #FAF4EB;
  border: 0;
  border-radius: 999px;
  padding: 16px 24px;
  font-family: 'Jost', sans-serif;
  font-weight: 500;
  font-size: 13.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
  box-shadow: 0 18px 34px -14px rgba(44, 38, 32, 0.55);
  transition: background 0.25s ease, transform 0.25s ease;
}
#booking-fab:hover { background: #5E5039; transform: translateY(-2px); }
#booking-fab svg { width: 18px; height: 18px; flex: none; }

#booking-widget[hidden] {
  display: none;
}

#booking-widget {
  position: fixed;
  right: 26px;
  bottom: 26px;
  z-index: 301;
  width: 380px;
  max-width: calc(100vw - 52px);
  max-height: min(600px, calc(100vh - 150px));
  background: #FFFCF6;
  border-radius: 24px;
  box-shadow: 0 34px 70px -20px rgba(44, 38, 32, 0.5);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  animation: booking-in 0.25s ease both;
}

@keyframes booking-in {
  from { opacity: 0; transform: scale(0.94) translateY(14px); }
  to { opacity: 1; transform: none; }
}

.booking-header {
  flex: none;
  background: #2C2620;
  color: #FAF4EB;
  padding: 20px 22px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.booking-title {
  font-family: 'Cormorant Garamond', serif;
  font-size: 20px;
}
#booking-close {
  background: none;
  border: 0;
  color: #FAF4EB;
  opacity: 0.75;
  cursor: pointer;
  padding: 4px;
  display: flex;
}
#booking-close:hover { opacity: 1; }
#booking-close svg { width: 18px; height: 18px; }

.booking-body {
  padding: 26px 24px 28px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  /* fixed-ish height so switching between steps (options / form / thanks)
     doesn't resize the widget — the tallest step (the form) sets the floor */
  min-height: 420px;
}

.booking-step {
  flex: 1;
  animation: booking-step-in 0.3s ease;
}

@keyframes booking-step-in {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .booking-step { animation: none; }
}

.booking-q {
  margin: 0 0 18px;
  font-family: 'Cormorant Garamond', serif;
  font-weight: 400;
  font-size: 21px;
  color: #2C2620;
}

.booking-options {
  display: grid;
  gap: 10px;
}
.booking-option {
  text-align: left;
  padding: 15px 18px;
  border-radius: 14px;
  border: 1px solid rgba(124, 107, 76, 0.28);
  background: #FFFFFF;
  font-family: 'Jost', sans-serif;
  font-size: 15px;
  color: #2C2620;
  cursor: pointer;
  transition: border-color 0.2s ease, background 0.2s ease;
}
.booking-option:hover,
.booking-option:focus-visible {
  border-color: #7C6B4C;
  background: #F8F2E6;
}
.booking-option.is-selected {
  border-color: #7C6B4C;
  background: #F8F2E6;
  color: #7C6B4C;
  font-weight: 500;
  position: relative;
  padding-right: 42px;
}
.booking-option.is-selected::after {
  content: '✓';
  position: absolute;
  right: 18px;
  top: 50%;
  transform: translateY(-50%);
  font-weight: 600;
}

.booking-next {
  margin-top: 18px;
  width: 100%;
  background: #7C6B4C;
  color: #FAF4EB;
  border: 0;
  border-radius: 999px;
  padding: 15px;
  font-family: 'Jost', sans-serif;
  font-weight: 500;
  font-size: 13px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background 0.2s ease;
}
.booking-next:hover { background: #5E5039; }
.booking-next:disabled {
  opacity: 0.5;
  cursor: default;
}

.booking-back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: none;
  border: 0;
  padding: 0;
  margin-bottom: 16px;
  font-family: 'Jost', sans-serif;
  font-size: 12.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #8A7C68;
  cursor: pointer;
}
.booking-back svg { width: 14px; height: 14px; }
.booking-back:hover { color: #7C6B4C; }

.booking-chip {
  display: inline-block;
  margin: 0 0 18px;
  padding: 7px 14px;
  background: #F8F2E6;
  border-radius: 999px;
  font-family: 'Jost', sans-serif;
  font-size: 12px;
  font-weight: 600;
  color: #7C6B4C;
}

#booking-form {
  display: grid;
  gap: 16px;
}
#booking-form label {
  display: grid;
  gap: 6px;
  font-family: 'Jost', sans-serif;
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #8A7C68;
}
#booking-form input {
  border: 0;
  border-bottom: 1px solid rgba(124, 107, 76, 0.35);
  background: none;
  padding: 10px 2px;
  font-family: 'Jost', sans-serif;
  font-size: 16px;
  color: #2C2620;
  letter-spacing: normal;
  text-transform: none;
}
#booking-form input:focus-visible {
  outline: none;
  border-color: #7C6B4C;
}
#booking-form textarea {
  border: 0;
  border-bottom: 1px solid rgba(124, 107, 76, 0.35);
  background: none;
  padding: 10px 2px;
  font-family: 'Jost', sans-serif;
  font-size: 15px;
  color: #2C2620;
  letter-spacing: normal;
  text-transform: none;
  resize: vertical;
}
#booking-form textarea:focus-visible {
  outline: none;
  border-color: #7C6B4C;
}
#booking-form button[type="submit"] {
  margin-top: 6px;
  background: #7C6B4C;
  color: #FAF4EB;
  border: 0;
  border-radius: 999px;
  padding: 15px;
  font-family: 'Jost', sans-serif;
  font-weight: 500;
  font-size: 13px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background 0.2s ease;
}
#booking-form button[type="submit"]:hover { background: #5E5039; }
#booking-form button[type="submit"]:disabled {
  opacity: 0.65;
  cursor: default;
}

.booking-error[hidden] { display: none; }
.booking-error {
  margin: 0;
  font-size: 13px;
  line-height: 1.6;
  color: #B4483B;
}
.booking-error a { color: inherit; text-decoration: underline; }

.booking-powered-by {
  margin: 22px 0 0;
  text-align: center;
  font-family: 'Jost', sans-serif;
  font-size: 11px;
  letter-spacing: 0.06em;
  color: #8A7C68;
  opacity: 0.75;
}
.booking-powered-by a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.booking-powered-by a:hover { color: #7C6B4C; }

/* ---------- Contact page: WhatsApp button ---------- */
.whatsapp-btn {
  margin-top: 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  background: #25D366;
  color: #FFFFFF;
  border-radius: 999px;
  padding: 15px 30px;
  cursor: pointer;
  font-family: 'Jost', sans-serif;
  font-size: 13px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  transition: background 0.2s ease;
}
.whatsapp-btn svg { width: 18px; height: 18px; flex: none; }
.whatsapp-btn:hover { background: #1DA851; color: #FFFFFF; }

/* ---------- Legal pages: privacybeleid / algemene voorwaarden ---------- */
.legal-body {
  max-width: 760px;
  margin: 0 auto;
  padding: 20px 5vw 110px;
}
.legal-body h2 {
  margin: 46px 0 14px;
  font-family: 'Cormorant Garamond', serif;
  font-weight: 500;
  font-size: 25px;
  color: #2C2620;
}
.legal-body h2:first-child { margin-top: 0; }
.legal-body p,
.legal-body li {
  margin: 0 0 14px;
  font-size: 16px;
  line-height: 1.75;
  color: #57503F;
  font-weight: 300;
}
.legal-body ul { margin: 0 0 14px; padding-left: 22px; }
.legal-body a { color: #7C6B4C; }
.legal-body a:hover { color: #5E5039; }
.legal-body .legal-note {
  margin-top: 40px;
  padding: 22px 24px;
  background: #F8F2E6;
  border-radius: 16px;
  font-size: 13.5px;
  line-height: 1.7;
  color: #57503F;
}

/* ---------- Cookie consent banner ---------- */
#cookie-banner[hidden] { display: none; }
.cookie-banner {
  position: fixed;
  left: 26px;
  bottom: 26px;
  z-index: 300;
  width: 400px;
  max-width: calc(100vw - 52px);
  background: #2C2620;
  color: #EDE3D3;
  border-radius: 20px;
  padding: 24px 26px;
  box-shadow: 0 28px 60px -20px rgba(44, 38, 32, 0.55);
  display: flex;
  flex-direction: column;
  gap: 16px;
  animation: booking-in 0.3s ease both;
}
.cookie-text {
  margin: 0;
  font-size: 14px;
  line-height: 1.65;
  font-weight: 300;
  opacity: 0.88;
}
.cookie-link {
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  font: inherit;
  color: #E8D3AC;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.cookie-link:hover { color: #FAF4EB; }
.cookie-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.cookie-btn {
  flex: 1;
  border-radius: 999px;
  padding: 12px 18px;
  font-family: 'Jost', sans-serif;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease;
  white-space: nowrap;
}
.cookie-btn-secondary {
  background: none;
  border: 1px solid rgba(237, 227, 211, 0.3);
  color: #EDE3D3;
}
.cookie-btn-secondary:hover { border-color: rgba(237, 227, 211, 0.55); }
.cookie-btn-primary {
  background: #7C6B4C;
  border: 1px solid #7C6B4C;
  color: #FAF4EB;
}
.cookie-btn-primary:hover { background: #8f7c58; }

@media (max-width: 640px) {
  /* Full-width, flush with the bottom edge — allowed to sit over the "Plan
     afspraak" FAB underneath rather than being pushed up to clear it. */
  .cookie-banner {
    left: 12px;
    right: 12px;
    bottom: 12px;
    width: auto;
    max-width: none;
  }
}

/* ---------- background-attachment:fixed photos: broken on mobile Safari ----------
   iOS Safari frequently fails to paint a fixed background at all inside a
   scrolling page (a long-standing engine quirk), which made the hero photo
   — and the same photo reused behind the testimonials and the "Over mij"
   band — disappear entirely on phones instead of just losing the parallax.
   Below 900px we fall back to a normal scrolling background, which every
   mobile browser renders correctly. */
@media (max-width: 900px) {
  .bg-fixed-photo {
    background-attachment: scroll, scroll !important;
  }
}

/* ---------- Portfolio page: service filter pills ----------
   Sticks flush under the header once scrolled past its natural position
   (see the HTML comment on .portfolio-filter-bar in portfolio.html for
   why it's a separate full-width element rather than sticky right inside
   the narrower hero section -- sticky's containing block is its normal-
   flow parent, and nesting it inside the short hero section would make
   it unstick again as soon as that section scrolled out of view). The
   blurred, near-page-color background is the same treatment as the
   header itself, so the two read as one continuous pinned toolbar rather
   than two different bars. top:63px is the header's real measured height
   -- not the 71px used elsewhere on this site (.werkwijze-fade), which
   turns out not to match the header either; that one just hides the gap
   under its own gradient.

   The negative margin-top pulls this bar up to sit ON the hero photo
   instead of in the plain cream space below it -- the hero section above
   reserves matching extra bottom padding for exactly this, so the bar
   lands just under the hero copy rather than overlapping it. Still a
   sibling of the hero <section> in the DOM (not nested inside it), so
   the sticky behavior above still works.

   No background/blur/border of its own while resting in its natural
   spot -- the pills need to read directly on the hero photo there with
   no visible strip behind them, per feedback that even the translucent
   cream bar this used to have showed as a hard seam across the photo.
   Once scrolled up far enough to actually stick under the header, the
   photo is gone -- .is-stuck (toggled by JS, see the IntersectionObserver
   block in main.js right after the filter-pills wiring; plain CSS has no
   way to select "currently stuck") brings the same cream/blur bar back
   for that state, so the pills sit "in" a strip again instead of
   floating loose over whatever page content is scrolling underneath. */
.portfolio-filter-bar {
  position: sticky;
  top: 63px;
  z-index: 40;
  margin-top: -90px;
  /* Extra bottom padding while resting on the photo (below the pills,
     before the grid starts) -- overridden back down once .is-stuck below
     kicks in, so the pinned strip itself stays the original compact
     height rather than growing every time it's stuck under the header. */
  padding: 16px 0 48px;
  transition: background 0.2s ease, padding 0.2s ease;
}
.portfolio-filter-bar.is-stuck {
  padding: 16px 0;
  background: rgba(255, 252, 246, 0.92);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid rgba(124, 107, 76, 0.14);
}
.portfolio-filter {
  display: flex;
  flex-wrap: nowrap;
  overflow-x: auto;
  gap: 12px;
  scrollbar-width: none;
}
.portfolio-filter::-webkit-scrollbar { display: none; }
/* Each pill carries its own solid beige fill (the site's light card
   color, #F8F2E6) rather than relying on a bar-level background behind
   it -- .portfolio-filter-bar itself has none (see that class's own
   comment), since this bar rests directly on the hero photo before it's
   scrolled up to stick under the header, then over the plain page
   background after. A solid fill per pill reads cleanly against the
   photo without the hard rectangular seam a bar-wide background left
   behind it, and still works once stuck over the plain page below. */
.portfolio-filter-btn {
  flex: none;
  background: #F8F2E6;
  border: 1px solid #F8F2E6;
  border-radius: 999px;
  padding: 13px 26px;
  font-family: 'Jost', sans-serif;
  font-size: 12.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #57503F;
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}
.portfolio-filter-btn:hover { background: #FFFCF6; border-color: #7C6B4C; color: #4A4238; }
.portfolio-filter-btn[aria-selected="true"] {
  background: #7C6B4C;
  border-color: #7C6B4C;
  color: #FAF4EB;
}

/* ---------- Portfolio page: "En nog veel meer" grid filler card ----------
   Fills the empty 9th slot the webdesign grid's last row (3+3+2) would
   otherwise leave. Own class rather than an inline `display:flex` so
   [hidden] below can actually win: an inline style always beats the UA
   stylesheet's [hidden]{display:none} regardless of specificity, so
   without this rule the portfolio filter's el.hidden = true did nothing
   and the card stayed visible under every tab, not just "Webdesign" --
   same fix already used for .case-mini-row above. */
.portfolio-filler {
  margin: 0;
  background: #F8F2E6;
  border-radius: 26px;
  padding: 24px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  aspect-ratio: 4 / 3;
  gap: 14px;
}
.portfolio-filler[hidden] { display: none; }

/* ---------- Bottom-of-page CTA band: dark card, left copy / right tilted video grid ---------- */
.cta-card {
  position: relative;
  max-width: 1400px;
  margin: 0 auto;
  border-radius: 32px;
  overflow: hidden;
  background: radial-gradient(130% 150% at 10% 12%, #423826 0%, #2C2620 55%, #1D1811 100%);
  display: grid;
  grid-template-columns: 1fr 0.95fr;
  align-items: stretch;
  min-height: 520px;
}

.cta-card-text {
  position: relative;
  z-index: 2;
  padding: 64px 56px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 22px;
}

.cta-badge {
  display: inline-flex;
  align-self: flex-start;
  padding: 9px 18px;
  border-radius: 999px;
  background: rgba(232, 211, 172, 0.14);
  color: #E8D3AC;
  font-family: 'Jost', sans-serif;
  font-weight: 500;
  font-size: 12.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.cta-card-text h2 {
  margin: 0;
  max-width: 22ch;
  font-family: 'Cormorant Garamond', serif;
  font-weight: 300;
  font-size: clamp(32px, 3.4vw, 50px);
  line-height: 1.1;
  color: #FAF4EB;
}

.cta-check {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 0;
  max-width: 36ch;
  font-family: 'Jost', sans-serif;
  font-size: 15px;
  line-height: 1.6;
  color: rgba(250, 244, 235, 0.72);
  font-weight: 300;
}
.cta-check svg { width: 18px; height: 18px; flex: none; margin-top: 1px; color: #E8D3AC; }

.cta-card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 6px;
}
.cta-btn-primary {
  background: #FFFCF6;
  color: #5E5039;
  border: 0;
  border-radius: 999px;
  padding: 16px 32px;
  font-family: 'Jost', sans-serif;
  font-size: 13px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background 0.2s ease;
}
.cta-btn-primary:hover { background: #F8F2E6; }

.cta-btn-secondary {
  background: rgba(255, 252, 246, 0.08);
  color: #FAF4EB;
  border: 1px solid rgba(250, 244, 235, 0.28);
  border-radius: 999px;
  padding: 16px 30px;
  font-family: 'Jost', sans-serif;
  font-size: 13px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease;
}
.cta-btn-secondary:hover { background: rgba(255, 252, 246, 0.16); border-color: rgba(250, 244, 235, 0.5); }

.cta-card-media {
  position: relative;
  overflow: hidden;
  /* Pulling the grid's own edge back from the seam (below) still leaves the
     rotated cards sweeping back toward it at some scroll positions — a
     rotated box's corners travel further than its unrotated edge suggests.
     A soft fade on that one edge means whatever does reach it disappears
     gracefully instead of getting sliced by a hard straight line. */
  mask-image: linear-gradient(90deg, transparent, #000 16%);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 16%);
}

.cta-media-grid {
  position: absolute;
  /* Left inset is positive (pulled in), not negative like the other three
     sides: those are meant to bleed past the card's true edge (fine — it's
     clipped there on purpose), but the left side meets the text panel, and
     a card rotated to cross that seam gets sliced through its middle
     instead of cleanly cropped. Keeping the grid's own left edge inset
     away from the seam means only the gap between cards ever reaches it. */
  inset: -18% -10% -18% 10%;
  display: flex;
  gap: 14px;
  transform: rotate(-9deg) scale(1.3);
  transform-origin: center;
}

/* No overflow:hidden here on purpose — clipping only happens at the outer
   .cta-card edge (its rounded corners + overflow:hidden). Clipping it here
   too chopped individual cards off mid-height wherever a column's own box
   boundary happened to fall, instead of only at the card's true edge. */
.cta-media-col { flex: 1; }
.cta-media-col-b { margin-top: -70px; }
.cta-media-col-c { margin-top: -140px; }

.cta-media-track {
  display: flex;
  flex-direction: column;
  gap: 14px;
  width: 100%;
  animation: cta-scroll-up 32s linear infinite;
}
.cta-media-col-b .cta-media-track { animation-duration: 26s; animation-direction: reverse; }
.cta-media-col-c .cta-media-track { animation-duration: 38s; }

@keyframes cta-scroll-up {
  from { transform: translateY(0); }
  to { transform: translateY(-50%); }
}

.cta-vcard {
  position: relative;
  flex: none;
  width: 100%;
  aspect-ratio: 9 / 15;
  border-radius: 16px;
  overflow: hidden;
  background: #1a1611;
  box-shadow: 0 18px 30px -16px rgba(0, 0, 0, 0.6);
}

/* Website-screenshot variant of .cta-vcard for the webdesign page's CTA
   marquee (portfolio screenshots instead of the portrait video clips used
   elsewhere) -- same card/track/column scrolling mechanics. The source
   images (images/portfolio-*-full.jpg) are full-page scrolling captures,
   roughly 1:2.4-1:2.7 tall -- a landscape card cropped them down to just
   a sliver of the hero, so this is deliberately elongated (taller than
   .cta-vcard's own 9:15) instead, to actually show more of each site. */
.cta-icard {
  position: relative;
  flex: none;
  width: 100%;
  aspect-ratio: 2 / 5;
  border-radius: 16px;
  overflow: hidden;
  background: #1a1611;
  box-shadow: 0 18px 30px -16px rgba(0, 0, 0, 0.6);
}
.cta-icard-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top;
  display: block;
}

@media (prefers-reduced-motion: reduce) {
  .cta-media-track { animation: none; }
}

@media (max-width: 900px) {
  .cta-card {
    grid-template-columns: 1fr;
    min-height: 0;
  }
  .cta-card-text { padding: 48px 28px 36px; }
  .cta-card-media {
    height: 240px;
    /* Seam is the top edge here (media band sits below the text), so the
       fade runs top-to-bottom instead of the desktop left-to-right one. */
    mask-image: linear-gradient(180deg, transparent, #000 20%);
    -webkit-mask-image: linear-gradient(180deg, transparent, #000 20%);
  }
  .cta-media-grid {
    /* Top is pulled in (positive), not bled out, for the same reason as
       the desktop layout's left inset: on mobile the media band sits
       below the text, so its top edge is the seam — bleeding past it
       sliced cards in half there instead of cropping cleanly. */
    inset: 10% -6% -14% -6%;
    transform: rotate(-7deg) scale(1.3);
  }
  .cta-card-actions { flex-direction: column; }
  .cta-btn-primary, .cta-btn-secondary { width: 100%; text-align: center; }
}

#booking-sent[hidden] {
  display: none;
}

#booking-sent {
  display: flex;
  flex-direction: column;
  justify-content: center;
  text-align: center;
  padding: 30px 6px 10px;
}
.booking-sent-title {
  margin: 0 0 10px;
  font-family: 'Cormorant Garamond', serif;
  font-size: 28px;
  font-weight: 300;
  color: #2C2620;
}
.booking-sent-text {
  margin: 0;
  font-family: 'Jost', sans-serif;
  font-size: 15px;
  line-height: 1.7;
  color: #57503F;
  font-weight: 300;
}

.booking-again {
  margin: 22px auto 0;
  background: none;
  border: 1px solid rgba(124, 107, 76, 0.35);
  border-radius: 999px;
  padding: 13px 26px;
  font-family: 'Jost', sans-serif;
  font-weight: 500;
  font-size: 12.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #7C6B4C;
  cursor: pointer;
  transition: border-color 0.2s ease, background 0.2s ease;
}
.booking-again:hover {
  border-color: #7C6B4C;
  background: #F8F2E6;
}

@media (max-width: 640px) {
  #booking-fab {
    right: 16px;
    bottom: 16px;
    padding: 14px 20px;
    font-size: 12.5px;
  }
  #booking-widget {
    right: 0;
    left: 0;
    bottom: 0;
    top: 0;
    width: 100%;
    max-width: 100%;
    max-height: 100%;
    border-radius: 0;
  }
  .booking-body { padding: 28px 22px; }
}

/* ---------- Stats band: thin dividers between the count-up numbers ---------- */
@media (min-width: 700px) {
  .stat-item:not(:first-child) {
    border-left: 1px solid rgba(250, 244, 235, 0.22);
  }
}

/* ---------- Social page hero: title + live "stories" mockup ----------
   Desktop: title and copy stack in the left column, the story mockup fills
   the right column spanning both rows. Mobile: title, then story, then copy.
   width:100% alongside max-width/margin:auto -- without it, this being a
   grid ITEM of its own parent's implicit grid (the .bg-fixed-photo hero
   section it sits in sets display:grid inline) makes its horizontal auto
   margins override the default stretch sizing, so it shrink-wraps to its
   own content's max-content width instead of filling up to max-width:
   harmless-looking while the title text is long enough to exceed 1400px
   on its own (content-creatie.html's, say) since the cap kicks in either
   way, but a shorter title (social-media-beheer.html's "Social media
   beheer") made the whole hero visibly narrower than before. width:100%
   makes it actually fill its grid cell first, so max-width is what caps
   it, not content length. */
.social-hero {
  width: 100%;
  max-width: 1400px;
  margin: 0 auto;
  padding: 90px 5vw 100px;
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  grid-template-areas: "title story" "copy story";
  column-gap: 70px;
  row-gap: 26px;
  align-items: center;
}
.social-hero-title {
  grid-area: title;
  margin: 0;
  font-family: 'Cormorant Garamond', serif;
  font-weight: 300;
  font-size: clamp(38px, 4.6vw, 68px);
  line-height: 1.08;
}
.story-mock { grid-area: story; }
.feed-trio { grid-area: story; }
.social-hero-copy { grid-area: copy; }

/* ---------- Social page hero: three-feed slide (alternative to .story-mock) ----------
   Same grid-area as .story-mock (see above) so it drops into the same
   hero slot -- three real client feeds side by side, each in its own
   .phone-mock-frame (same bezel as every other phone mockup on the
   site -- .phone-mock-frame's own width:100% just fills whatever width
   its wrapper gives it, so a plain width on the wrapper is what drives
   the side/mid size difference here), the middle one wider than its two
   neighbours.

   The three phones are genuinely repositioned, not cross-faded: each
   .feed-trio-item is absolutely positioned within this (fixed-size,
   position:relative) container, and js/main.js only ever toggles which
   of .slot-left/.slot-mid/.slot-right class a phone carries -- never
   touches its image -- so the `left`/`width`/`top` transition below is
   what makes them visibly slide and resize into their new slot every
   3s. --side-w/--mid-w/--gap drive every other value here via calc(),
   so the mobile override just needs to redefine those three. */
.feed-trio {
  position: relative;
  --side-w: 130px;
  --mid-w: 170px;
  --gap: 14px;
  --ratio: 2.16667; /* phone-mock-frame's own 9/19.5 aspect ratio, inverted */
  width: calc(var(--side-w) * 2 + var(--mid-w) + var(--gap) * 2);
  height: calc(var(--mid-w) * var(--ratio));
  margin: 0 auto;
}
.feed-trio-item {
  position: absolute;
  transition: left 0.6s ease, width 0.6s ease, top 0.6s ease;
}
.feed-trio-item.slot-left,
.feed-trio-item.slot-right {
  width: var(--side-w);
  top: calc((var(--mid-w) * var(--ratio) - var(--side-w) * var(--ratio)) / 2);
}
/* z-index is per TARGET slot, not per phone -- since the rotation always
   moves everyone exactly one step left, whichever phone is heading into
   a given slot is always the same role every single tick, so a static
   per-slot stack order is all this needs (no JS bookkeeping of who's
   arriving from where). Ranked by how far each phone is travelling:
   the one arriving in the middle (short hop from the right) is always
   in front, the one leaving the middle for the left is the middle
   layer, and the one wrapping all the way from left to right -- the
   longest hop, crossing behind both of the others -- stays lowest so
   it's never briefly seen in front of them mid-slide. Previously only
   .slot-mid had a z-index at all, so the two side slots (both left at
   the auto default) tied and fell back to DOM order, which is what let
   the wrapping phone poke out in front for part of its trip. */
.feed-trio-item.slot-left { left: 0; z-index: 2; }
.feed-trio-item.slot-right { left: calc(var(--side-w) + var(--mid-w) + var(--gap) * 2); z-index: 1; }
.feed-trio-item.slot-mid {
  left: calc(var(--side-w) + var(--gap));
  width: var(--mid-w);
  top: 0;
  z-index: 3;
}

@media (max-width: 900px) {
  .feed-trio { --side-w: 92px; --mid-w: 120px; --gap: 10px; }
}

/* Thinner bezel, tighter corners for .phone-mock-frame at these small
   sizes -- the base frame's 36px radius/10px padding are tuned for its
   usual 220-230px width and read as a thick, overly rounded pill down
   here instead of an iPhone. Same scoped-modifier pattern as
   .kw-phone-frame elsewhere (see that class's own comment): add a
   second class rather than touch .phone-mock-frame itself, so every
   other phone mockup on the site keeps its original bezel.

   padding/border-radius are calc()'d from --side-w/--mid-w (not fixed
   px) via the compound .slot-* / .feed-trio-phone selectors below, at a
   constant ratio of the phone's own current width -- a fixed px radius
   that looked right on the 170px mid phone turned into a fat, oversized
   pill on the 92px mobile side phones, since it didn't shrink along
   with them. Keeping it a ratio of the live width means it also stays
   correct through the slide/resize transition itself, not just at the
   two breakpoints' end states -- and rides along on that same
   transition (padding/border-radius added below) for a frame that
   visibly thins and tightens as it grows into the middle slot, instead
   of snapping between two fixed looks.

   The notch/home-indicator don't need the same treatment: sized in %
   (not px), they resolve against .phone-mock-frame's own box on every
   render, so they already track any width automatically. */
.feed-trio-phone { transition: padding 0.6s ease, border-radius 0.6s ease; }
.feed-trio-phone .phone-mock-notch { width: 23%; height: 3%; top: 1.1%; border-radius: 0 0 8px 8px; }
.feed-trio-phone .phone-mock-home { width: 22%; height: 0.8%; bottom: 1.4%; }
.feed-trio-item.slot-mid .feed-trio-phone {
  padding: calc(var(--mid-w) * 0.024);
  border-radius: calc(var(--mid-w) * 0.13);
}
.feed-trio-item.slot-mid .feed-trio-phone .phone-mock-img {
  border-radius: calc(var(--mid-w) * 0.13 - 3px);
}
.feed-trio-item.slot-left .feed-trio-phone,
.feed-trio-item.slot-right .feed-trio-phone {
  padding: calc(var(--side-w) * 0.024);
  border-radius: calc(var(--side-w) * 0.13);
}
.feed-trio-item.slot-left .feed-trio-phone .phone-mock-img,
.feed-trio-item.slot-right .feed-trio-phone .phone-mock-img {
  border-radius: calc(var(--side-w) * 0.13 - 3px);
}

@media (max-width: 900px) {
  .social-hero {
    grid-template-columns: 1fr;
    grid-template-areas: "title" "story" "copy";
    row-gap: 30px;
  }
}

.story-mock {
  position: relative;
  width: 100%;
  max-width: 320px;
  margin: 0 auto;
  aspect-ratio: 9 / 16;
  border-radius: 26px;
  overflow: hidden;
  background: #1a1611;
  box-shadow: 0 34px 60px -22px rgba(44, 38, 32, 0.5);
}

.story-progress {
  position: absolute;
  top: 12px;
  left: 12px;
  right: 12px;
  z-index: 3;
  display: flex;
  gap: 5px;
}
.story-seg {
  flex: 1;
  height: 3px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.32);
  overflow: hidden;
}
.story-seg i {
  display: block;
  height: 100%;
  width: 0;
  background: #FFFFFF;
  border-radius: 999px;
}
.story-seg.done i { width: 100%; transition: none; }
.story-seg.active i { width: 100%; transition: width 4s linear; }

.story-header {
  position: absolute;
  top: 24px;
  left: 14px;
  right: 14px;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 9px;
}
.story-avatar {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  overflow: hidden;
  background: #F0DED4;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  font-family: 'Cormorant Garamond', serif;
  font-style: italic;
  font-weight: 600;
  font-size: 12px;
  color: #9E7461;
}

.story-nav {
  position: absolute;
  inset: 0;
  width: 50%;
  z-index: 2;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}
.story-nav-prev { left: 0; }
.story-nav-next { left: auto; right: 0; }

/* Visual hint (chevrons) that the story is navigable — shown on hover,
   pointer-events:none so they never block the .story-nav buttons beneath. */
.story-arrow {
  position: absolute;
  top: 50%;
  z-index: 4;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: rgba(20, 17, 13, 0.45);
  display: flex;
  align-items: center;
  justify-content: center;
  color: #FAF4EB;
  opacity: 0;
  transform: translateY(-50%);
  transition: opacity 0.25s ease;
  pointer-events: none;
}
.story-arrow svg { width: 16px; height: 16px; }
.story-arrow-prev { left: 10px; }
.story-arrow-next { right: 10px; }
.story-mock:hover .story-arrow,
.story-mock:focus-within .story-arrow {
  opacity: 1;
}

.story-handle {
  color: #FFFFFF;
  font-family: 'Jost', sans-serif;
  font-size: 13px;
  font-weight: 600;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
}
.story-time {
  color: rgba(255, 255, 255, 0.75);
  font-family: 'Jost', sans-serif;
  font-size: 12px;
}

.story-media {
  position: absolute;
  inset: 0;
}
.story-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.3s ease;
}
.story-video.active { opacity: 1; }

.story-caption {
  position: absolute;
  left: 16px;
  right: 16px;
  bottom: 22px;
  z-index: 3;
  background: rgba(20, 17, 13, 0.55);
  backdrop-filter: blur(6px);
  color: #FAF4EB;
  font-family: 'Jost', sans-serif;
  font-size: 13.5px;
  font-weight: 500;
  line-height: 1.5;
  padding: 12px 16px;
  border-radius: 14px;
}

/* ---------- iPhone-style mockup for the client-feed examples ---------- */
.phone-mock-frame {
  position: relative;
  width: 100%;
  max-width: 230px;
  aspect-ratio: 9 / 19.5;
  background: #151210;
  border-radius: 36px;
  padding: 10px;
  box-shadow: 0 26px 50px -18px rgba(44, 38, 32, 0.45);
}
.phone-mock-notch {
  position: absolute;
  top: 10px;
  left: 50%;
  transform: translateX(-50%);
  width: 72px;
  height: 20px;
  background: #151210;
  border-radius: 0 0 16px 16px;
  z-index: 2;
}
.phone-mock-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 26px;
  display: block;
}
.phone-mock-home {
  position: absolute;
  bottom: 9px;
  left: 50%;
  transform: translateX(-50%);
  width: 70px;
  height: 4px;
  background: rgba(255, 255, 255, 0.55);
  border-radius: 999px;
  z-index: 2;
}

/* ---------- Text+photo hero sections: sane mobile order ----------
   Each section keeps its own desktop grid-template-columns (fixed ratio,
   photo left or right) plus a grid-template-areas that places its title,
   photo and copy blocks — set inline per section since the ratios/areas
   differ per page. Every child MUST be assigned to its named area (grid
   only auto-places items in DOM order otherwise, ignoring the areas
   entirely — that's what caused the desktop layout to break). Below
   900px every section collapses to a single column and re-stacks as
   title → photo → copy, regardless of the desktop order, so the photo
   is never squeezed down to a sliver next to the text. */
.mr-title { grid-area: title; }
.mr-photo { grid-area: photo; }
.mr-copy { grid-area: copy; }

@media (max-width: 900px) {
  .mobile-reorder {
    grid-template-columns: 1fr !important;
    grid-template-areas: "title" "photo" "copy" !important;
    row-gap: 28px !important;
  }
}

/* "Beste investering" section (webdesign.html) on mobile: stacks as
   title (incl. the button) -> photo, per .mobile-reorder above, so that
   shared row-gap is also the gap between the button and the photo below
   it -- widened here just for this instance instead of raising the
   shared default. The photo itself is also sized down and centered,
   rather than stretching to the full column width like other
   .mobile-reorder photos do. */
@media (max-width: 900px) {
  .mobile-reorder.invest-reorder { row-gap: 55px !important; }
  .invest-photo { max-width: 300px; margin: 0 auto; }
}

/* ---------- Testimonials: desktop scattered bubbles vs. mobile marquee ----------
   Two independent markups sharing one section — only one is ever visible,
   swapped by media query, so neither can affect the other's layout. */
.testi-mobile { display: none; }

@media (max-width: 900px) {
  /* !important: .testi-desktop carries an inline display:grid, which
     otherwise beats a plain class rule regardless of media query. */
  .testi-desktop { display: none !important; }
  .testi-mobile { display: block; }
}

/* Negative margin cancels out the parent section's own 5vw side padding
   (set inline on index.html's "Klanten aan het woord" <section>, shared
   with .testi-desktop) so the row -- and with it, the mask fade above --
   bleeds all the way to the screen edge instead of fading well short of
   it. Only ever affects this row: .testi-mobile itself is display:none
   above 900px, so the pulled-out edges never show up next to the
   (untouched) desktop layout. */
.testi-mobile-row {
  overflow: hidden;
  margin: 0 -5vw;
  mask-image: linear-gradient(90deg, transparent, #000 4%, #000 96%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 4%, #000 96%, transparent);
}

.testi-mobile-track {
  display: flex;
  gap: 16px;
  width: max-content;
  animation: testi-mobile-scroll 26s linear infinite;
}
.testi-mobile-track.paused { animation-play-state: paused; }

@keyframes testi-mobile-scroll {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
  .testi-mobile-track { animation: none; }
}

.testi-mobile-card {
  flex: none;
  width: 78vw;
  max-width: 320px;
  background: #FFFFFF;
  border-radius: 22px;
  padding: 22px 24px 20px;
  box-shadow: 0 18px 34px -20px rgba(44, 38, 32, 0.4);
}
.testi-mobile-name {
  margin: 0 0 8px;
  font-family: 'Jost', sans-serif;
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #8A7C68;
}
.testi-mobile-text {
  margin: 0;
  font-family: 'Jost', sans-serif;
  font-size: 15.5px;
  line-height: 1.55;
  color: #2C2620;
  font-weight: 400;
}
.testi-mobile-heart { margin: 12px 0 0; font-size: 17px; line-height: 1; }

.testi-mobile-hint {
  margin: 18px 0 0;
  font-family: 'Jost', sans-serif;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(250, 244, 235, 0.55);
  text-align: center;
}

/* ---------- Laptop-mockup grids (home "Bekijk mijn werk", Portfolio page) ----------
   minmax(460px, 1fr) refuses to shrink below 460px, so on phones (< 460px
   wide) each card was wider than the viewport and got cut off / forced a
   horizontal scroll. Below 640px, collapse to one full-width column. */
@media (max-width: 640px) {
  .laptop-grid {
    grid-template-columns: 1fr !important;
  }
}

/* ---------- Home "Bekijk mijn werk": results + video-trio split ----------
   Sits below .laptop-grid in place of the old wide clip6-9 marquee.
   Left column reuses .case-media/.case-ba-row/.case-stat-badge etc. from
   the Explorista case block (see social-media-beheer.html) standalone --
   those classes don't depend on the .case-block grid they normally sit
   in, just on .case-media's own position:relative. Right column is a
   static (non-scrolling) 3-up row built from the plain .video-card/
   .video-el vocabulary, deliberately NOT wrapped in .video-marquee/
   .video-track since this is meant to read as a small fixed sample, not
   a moving slider. 0.8fr/1.2fr -- flipped from the .case-block ratio this
   started as -- per feedback that the results side should read smaller
   and the video trio bigger; .home-proof-results' own max-width (set on
   .case-media inline in index.html) is sized down to match, and
   .home-proof-video-row's cards are sized up past the site's default
   150px .video-card, both below. Collapses to one column under 900px,
   same breakpoint as .case-block and the other split sections on the
   site. */
/* Overrides the shared .case-ba-item's width: clamp(120px, 17vw, 210px)
   (see social-media-beheer.html's own Explorista case block, where that
   floor/ceiling is correct) -- .case-media's inline max-width in
   index.html alone can't shrink the phones past that 120px floor,
   because .case-ba-row (flex) and .case-media (grid) both leave their
   single child's default min-width:auto in place, which resolves to
   that child's own max-content size and overflows straight past a
   smaller max-width on an ancestor instead of actually shrinking to fit
   it (the classic flex/grid "children ignore the parent's cap" gotcha).
   A fixed, much smaller width here sidesteps that entirely -- nothing
   left to shrink away from, so no min-width fight to lose. */
.home-proof-results .case-ba-item { width: 180px; }
.home-proof-results .case-ba-row { gap: 12px; }
/* The badge's own number/label text (.case-stat-badge-num/-label) size
   themselves off the VIEWPORT (clamp(...,5.2vw,...) / plain vw) in the
   shared rule, matching the badge's own vw-driven width there -- the two
   scale together at any viewport, by design. Here the badge's width is
   instead tied to .case-ba-item's fixed 140px above, so left alone the
   text would stay full (viewport-scale) size against a much smaller
   circle and overflow it. container-type on the badge itself switches
   its two text children to cqw (sized off the BADGE's own inline size,
   not the viewport), re-establishing that same proportional fit at this
   one-off smaller size. */
.home-proof-badge { container-type: inline-size; }
.home-proof-badge .case-stat-badge-num { font-size: 24cqw; }
.home-proof-badge .case-stat-badge-label { font-size: 7.5cqw; }
.home-proof-split {
  display: grid;
  grid-template-columns: 0.8fr 1.2fr;
  gap: clamp(32px, 5vw, 64px);
  /* start, not center: the two "eyebrow" labels above each column need to
     line up at the top -- centering the row would sink the shorter video
     column's label below the taller results column's, since it centers
     each item's box against the tallest one. */
  align-items: start;
}
.home-proof-video-row {
  display: flex;
  justify-content: center;
  gap: 20px;
}
/* Bigger than the site's default 150px .video-card (and the 220px
   .video-showcase--light one) -- this row only ever shows 3 cards, so it
   can afford to spend more width per card than a marquee that needs many
   visible at once. Capped well under the ~686px this column actually
   gets out of .home-proof-split's 1.2fr share (at the section's own
   ~1150-1260px content width, post-narrowing to match "Zo werkt het"):
   .video-card is `flex: none` sitewide (never shrinks), so 3 of them
   plus 2 gaps wider than that share would force the grid track to grow
   past its 1.2fr and squeeze .home-proof-results' column down with it,
   instead of the two sides splitting 0.8fr/1.2fr as intended. */
.home-proof-video-row .video-card { width: 190px; }
/* Follower-count badge, pinned to the "before" phone's own corner --
   reference screenshot showed it sitting right over the phone's top-left
   corner, untilted, roughly 3/4 the phone's width across. .home-proof-
   badge-anchor (position:relative, wraps just that one .case-proof
   button, not the whole .case-media row) is what makes that possible:
   the badge is sized and positioned purely in terms of its OWN box via
   `transform: translate(...)` in % (relative to the badge's own width/
   height, per the CSS spec for transform percentages) rather than via
   top/left % (which -- if measured against the anchor's box instead --
   would come out relative to the anchor's height, wildly different from
   its width on a tall portrait phone screenshot). That makes this one
   rule correct at every viewport width with no separate mobile query:
   width itself is a plain % of the anchor, so the whole badge scales
   down together with the phone image it sits on. Higher specificity than
   the shared .case-stat-badge base rule (top/right/transform) and its
   own 900px centering query further down this file (2 classes beats 1,
   regardless of media-query nesting or source order) -- and since this
   markup never carries the old .case-stat-badge--left modifier, that
   class's rules don't apply here at all. */
.home-proof-badge-anchor { position: relative; }
/* Two classes, not one -- .home-proof-badge alone is the same specificity
   as the base .case-stat-badge rule below it, and being EARLIER in the
   file it was actually losing that tie for every property they both set
   (top/width/transform), source order deciding ties between equal-
   specificity rules. Chaining both classes on the selector is what
   actually delivers the "beats the shared rule regardless of order"
   this block's comment above promises. */
.case-stat-badge.home-proof-badge {
  top: 0;
  left: 0;
  right: auto;
  width: 74%;
  /* translateY only -10%: the "Social media beheer" eyebrow label sits a
     fixed 18px (margin-bottom, in index.html) above .case-media, a gap
     that does NOT scale down with the badge/phone -- so how much the
     badge may protrude above its own anchor is capped by that fixed 18px
     at the LARGEST the badge ever gets (item width caps at 210px per
     .case-ba-item's own clamp, so badge caps at 0.74*210=155.4px here),
     not by how it looks at any one breakpoint. -10% of that worst case
     is ~15.5px, clearing the 18px gap with a couple px to spare; a
     bigger negative value (e.g. -28%, used in an earlier pass here)
     clears fine on desktop's often much-wider .case-ba-item but overlaps
     the label badly once .case-ba-item hits its own 120px mobile floor,
     which is exactly the "sticker sits in front of the text on mobile"
     bug that came back reported after that pass. */
  transform: translate(-38%, -10%);
}
@media (max-width: 620px) {
  /* Narrower gap alongside the shrunk card width -- 3 × 28vw + 2 × 10px
     stays clear of 100vw with room for the section's own 5vw side
     padding either side. */
  .home-proof-video-row { gap: 10px; }
  .home-proof-video-row .video-card { width: 28vw; }
}
@media (max-width: 900px) {
  .home-proof-split { grid-template-columns: 1fr; gap: 56px; }
}

/* ---------- Mobile navbar: hamburger + slide-down panel ----------
   The desktop <nav> is a flex row that just wraps when it runs out of
   room, which on phones pushed items into a messy second row on top of
   the logo. Below 860px we hide that row entirely and swap in a
   hamburger button that reveals a full-width vertical link list. */
.mobile-nav-toggle { display: none; }
.mobile-nav-panel { display: none; }

@media (max-width: 860px) {
  /* !important: nav carries its own inline display:flex. */
  header .site-nav { display: none !important; }

  .mobile-nav-toggle {
    display: block;
    position: relative;
    width: 34px;
    height: 34px;
    margin-left: auto;
    background: none;
    border: 0;
    padding: 0;
    cursor: pointer;
    flex: none;
  }
  /* Absolutely positioned and centered via top:50%/margin-top, then offset
     with translateY — not flex + gap. A gap-based layout left this open to
     the three lines landing a pixel or two off from a perfect stack on some
     mobile engines, so the open state's X never fully closed over the
     hamburger it was supposed to replace. This version has all three lines
     converge on the exact same center point before rotating, so there's no
     layout math left that can drift. */
  .mobile-nav-toggle span {
    position: absolute;
    left: 0;
    top: 50%;
    width: 100%;
    height: 2px;
    margin-top: -1px;
    border-radius: 2px;
    background: #4A4238;
    transition: transform 0.25s ease, opacity 0.25s ease;
  }
  .mobile-nav-toggle span:nth-child(1) { transform: translateY(-7px); }
  .mobile-nav-toggle span:nth-child(2) { transform: translateY(0); }
  .mobile-nav-toggle span:nth-child(3) { transform: translateY(7px); }
  .mobile-nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(0) rotate(45deg); }
  .mobile-nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
  .mobile-nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(0) rotate(-45deg); }

  /* Fullscreen takeover: fixed so it covers the whole viewport, but the
     sticky header (z-index 50) still paints above it, which is what
     leaves the logo + now-an-✕ toggle visible and clickable to close. */
  .mobile-nav-panel.open {
    display: flex;
    position: fixed;
    inset: 0;
    z-index: 40;
    flex-direction: column;
    justify-content: center;
    gap: 6px;
    padding: 90px 8vw 60px;
    background: #FFFCF6;
    font-family: 'Jost', sans-serif;
    font-size: 21px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: #4A4238;
    overflow-y: auto;
  }
  .mobile-nav-link {
    background: none;
    border: 0;
    text-align: left;
    padding: 16px 2px;
    cursor: pointer;
    font: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    color: inherit;
    border-bottom: 1px solid rgba(124, 107, 76, 0.14);
  }
  .mobile-nav-cta {
    margin-top: 22px;
    background: #7C6B4C;
    color: #FAF4EB;
    border: 0;
    border-radius: 999px;
    padding: 18px 2px;
    text-align: center;
    font-size: 15px;
  }
}

/* Lock background scroll while the fullscreen mobile menu is open. */
body.mobile-nav-open { overflow: hidden; }

/* ---------- Contact page: stack to one column on mobile ----------
   The 1fr/1fr grid squeezed the booking-teaser box into a half-width
   column that ran off the right edge of the screen on phones. */
@media (max-width: 860px) {
  .contact-grid {
    grid-template-columns: 1fr !important;
    gap: 40px !important;
  }
}

/* ---------- Footer: stack to one column on mobile ----------
   The 1.3fr/1fr/1fr grid squeezed the "Contact" column so hard on
   phones that the email address ran off the edge of the screen. */
@media (max-width: 780px) {
  .footer-grid {
    grid-template-columns: 1fr !important;
    gap: 40px !important;
  }
}

/* ---------- Social media page: "Mijn werkwijze" timeline ---------- */
.werkwijze {
  display: grid;
  grid-template-columns: 0.85fr 1.15fr;
  gap: 70px;
  align-items: start;
}
/* Scrolls normally with the page until it reaches the vertical center of
   the viewport, then locks there while the steps on the right keep
   scrolling past it. `top` is a fallback here — main.js measures the
   block's real height (it varies with content/image size) and overrides
   this with the exact offset that lands it centered once stuck, so the
   value below only matters before that script runs. */
.werkwijze-intro {
  position: sticky;
  top: 50vh;
  padding-left: 96px;
}
.werkwijze-phone {
  margin: 36px 0 0 40px;
  max-width: 215px;
}
.werkwijze-step {
  position: relative;
  display: grid;
  grid-template-columns: 24px 1fr;
  column-gap: 28px;
  padding-bottom: 52px;
}
.werkwijze-step:last-child { padding-bottom: 0; }
/* Clears step 01 of .werkwijze-fade's zone (see below), which now paints
   from the very top of .werkwijze-steps -- without this the gradient would
   sit directly over "01. Kennismaking" the moment the section appears. */
.werkwijze-fade + .werkwijze-step { margin-top: 72px; }
.werkwijze-marker { position: relative; display: flex; justify-content: center; }
.werkwijze-marker::before {
  content: "";
  position: absolute;
  top: 22px;
  bottom: -52px;
  width: 1px;
  background: rgba(124, 107, 76, 0.25);
}
/* Let the line overshoot a bit past both ends instead of stopping flush at
   the first/last dot -- matches the reference screenshot, where the
   divider visibly runs on above step 01 and below the last step's text. */
.werkwijze-fade + .werkwijze-step .werkwijze-marker::before { top: -40px; }
.werkwijze-step:last-child .werkwijze-marker::before { bottom: -40px; }
.werkwijze-dot {
  margin-top: 5px;
  width: 11px;
  height: 11px;
  border-radius: 999px;
  background: #7C6B4C;
  flex: none;
}
.werkwijze-label {
  margin: 0 0 10px;
  font-family: 'Jost', sans-serif;
  font-weight: 500;
  font-size: 12.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #7C6B4C;
}
.werkwijze-text {
  margin: 0;
  max-width: 52ch;
  font-size: 16px;
  line-height: 1.75;
  color: #57503F;
  font-weight: 300;
}
.werkwijze-text strong { color: #4A4238; font-weight: 500; }

/* The *next* section is pulled up to overlap the tail end of this one and
   painted above it (z-index), so once you scroll far enough it visibly
   slides up and over the steps instead of just continuing on below them. */
.werkwijze-section {
  position: relative;
  z-index: 1;
}
/* Big empty run below the last step, so .werkwijze-intro stays pinned (and
   hidden behind .werkwijze-cover, see below) for as long as that next
   section takes to rise up and fully cover it, instead of unsticking while
   still partly exposed. The value below is a fallback for before layout
   runs -- main.js measures the real intro height and viewport and
   overrides this with the exact figure that guarantees full coverage on
   this particular screen (a fixed px here can't, since that distance
   depends on both). */
.werkwijze-steps {
  position: relative;
  padding-bottom: 1160px;
}
/* Fade the tops of the steps as they scroll up underneath the sticky header,
   instead of having them cut off hard -- step 01 included, same as every
   other step. Visible from the moment the section comes into view (not
   just once actually pinned), so it's painted unconditionally here rather
   than toggled on by scroll position. Pinned flush with the header so
   there's no gap for text to flash through unfaded, and mostly opaque so
   it actually hides what's behind it rather than just tinting it.

   As the very first thing in .werkwijze-steps, this sits at the top of the
   column even before it's actually pinned -- painting its gradient there
   too, right over the top of step 01. The `.werkwijze-fade + .werkwijze-step`
   margin-top (above) is what keeps that gradient off step 01's own text. */
.werkwijze-fade {
  position: sticky;
  top: 71px;
  height: 64px;
  margin-bottom: -64px;
  background: linear-gradient(to bottom, #FFFCF6 55%, transparent);
  pointer-events: none;
  z-index: 3;
}
/* Pulled up to overlap the tail end of .werkwijze-section -- see the
   .werkwijze-steps comment above; margin-top here is likewise just a
   fallback main.js overrides once it can measure the real layout. */
.werkwijze-cover {
  position: relative;
  z-index: 2;
  margin-top: -1060px;
  border-radius: 32px 32px 0 0;
  box-shadow: 0 -40px 60px -30px rgba(44, 38, 32, 0.22);
}
/* .werkwijze-section carries an explicit z-index (see above) so .werkwijze-
   cover can paint above it -- but that also puts the *whole* section's
   stacking context above any later, unpositioned sibling by default, so
   once the sticky intro lets go and trails behind past the end of the
   (very tall) cover section, it would otherwise show through on top of
   whatever section comes next. Stacking this next section above fixes the
   paint order, but that section's own background is transparent (its
   padding sits around an inset card, not filled edge to edge) -- so an
   opaque background here is what actually stops anything showing through
   that gap, the z-index only decides paint order between two things that
   are already opaque. */
.werkwijze-cover + section {
  position: relative;
  z-index: 3;
  background: #FFFCF6;
}

@media (max-width: 900px) {
  .werkwijze { grid-template-columns: 1fr; gap: 48px; }
  .werkwijze-intro { position: static; padding-left: 0; }
  .werkwijze-phone { max-width: 220px; margin: 40px auto 0; }
  .werkwijze-steps { padding-bottom: 0; }
  .werkwijze-fade + .werkwijze-step { margin-top: 0; }
  .werkwijze-fade { display: none; }
  .werkwijze-cover { margin-top: 0; border-radius: 0; box-shadow: none; }
}

/* ---------- Portfolio: case studies ----------
   Each individual client's content is its own light card (see .case-block
   below), sat directly on the page background. .case-studies is what the
   existing filter JS (main.js) shows/hides, via its own data-category
   and an inline grid-column:1/-1 to span the full row of .laptop-grid;
   a second case is just another .case-block copied inside it, alternating
   sides with the .case-block--flip modifier. */

/* Lighter than the page it sits on (the page itself is already a light
   cream) rather than the usual tinted-*darker* card elsewhere on this
   site -- the soft shadow is what keeps its edge visible against a
   background so close to it in tone. */
.case-block {
  display: grid;
  grid-template-columns: 1.15fr 0.85fr;
  gap: clamp(28px, 4vw, 56px);
  align-items: center;
  background: #FFFCF6;
  border: 1px solid rgba(124, 107, 76, 0.12);
  border-radius: 28px;
  padding: clamp(28px, 4vw, 48px);
  box-shadow: 0 40px 80px -48px rgba(44, 38, 32, 0.25);
}
/* :not(--mini): two .case-block--mini cards are laid out side by side in
   .case-mini-row (a sibling relationship too), not stacked -- this margin
   otherwise pushed the second one down and (since a stretched grid item's
   height shrinks by its own margin) made the two mini cards different
   heights instead of matching. */
.case-block + .case-block:not(.case-block--mini) { margin-top: 32px; }
/* Alternates the side the copy sits on from one case to the next --
   add this class on every other .case-block, nothing else changes. */
.case-block--flip .case-copy { order: 2; }
.case-block--flip .case-media { order: 1; }

.case-name {
  margin: 0 0 8px;
  font-family: 'Cormorant Garamond', serif;
  font-weight: 500;
  font-size: clamp(24px, 2.2vw, 30px);
  color: #2C2620;
}
.case-sub {
  margin: 0 0 24px;
  font-size: 14.5px;
  line-height: 1.6;
  color: #8A7C68;
}

.case-list {
  margin: 0 0 32px;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 9px;
}
.case-list li {
  position: relative;
  padding-left: 17px;
  font-size: 15px;
  line-height: 1.6;
  color: #57503F;
  font-weight: 300;
}
.case-list li::before {
  content: "–";
  position: absolute;
  left: 0;
  color: #7C6B4C;
}

/* The stats, compact -- an icon, the figure, a small unit label, repeated
   in a row (2x2 on mobile) rather than blown up into tiles; the numbers
   still carry the block since nothing else here is this size or color. */
.case-metrics {
  display: grid;
  grid-template-columns: repeat(4, auto);
  gap: 22px;
}
.case-metric { display: grid; gap: 7px; justify-items: start; }
.case-metric svg { width: 19px; height: 19px; color: #7C6B4C; }
.case-metric-num {
  font-family: 'Jost', sans-serif;
  font-weight: 600;
  font-size: 19px;
  color: #2C2620;
}
.case-metric-label {
  font-size: 10.5px;
  line-height: 1.35;
  letter-spacing: 0.03em;
  color: #8A7C68;
}

/* Holds the case's proof image(s) -- stacked full-width if there's ever
   more than one, each uncropped (natural aspect ratio) since these are
   screenshots/infographics where cropping would cut off real data.
   position:relative so a .case-stat-badge can be pinned to this box
   directly (as on social-media-beheer.html) without needing the
   .case-media--collage variant below, which also sets this but layers
   on a lot more that a single-image/row case doesn't need. */
.case-media {
  position: relative;
  display: grid;
  gap: 14px;
}
/* Variant for a case with several proofs shown as a playful scattered
   collage instead of the default single stacked column -- same tilted,
   uneven-size, staggered-baseline feel as .shot-compare above (some
   frames raised on a margin-bottom, alternating rotation, one clearly
   bigger than the rest), just extended from 2 photos to however many
   .case-proof--* size modifiers are used. Each proof keeps its own
   .case-proof-caption underneath, upright (unrotated) for readability --
   only .case-proof-frame (the image itself) tilts, via the nested rule
   under each size modifier below. */
.case-media--collage {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: center;
  gap: clamp(18px, 3vw, 34px);
  padding: 34px 4px 0;
}
.case-media--collage .case-proof { width: auto; flex: none; }
/* Four sizes, smallest to largest -- pick per image based on how much
   visual weight it should carry, not necessarily reading order. */
.case-media--collage .case-proof--sm { width: clamp(84px, 13vw, 130px); margin-bottom: 0; }
.case-media--collage .case-proof--md { width: clamp(96px, 15vw, 150px); margin-bottom: 22px; }
.case-media--collage .case-proof--lg { width: clamp(104px, 16vw, 165px); margin-bottom: 30px; }
.case-media--collage .case-proof--xl { width: clamp(150px, 23vw, 250px); margin-bottom: 0; }
.case-media--collage .case-proof--sm .case-proof-frame { transform: rotate(5deg); }
.case-media--collage .case-proof--md .case-proof-frame { transform: rotate(-4deg); }
.case-media--collage .case-proof--lg .case-proof-frame { transform: rotate(-3deg); }
.case-media--collage .case-proof--xl .case-proof-frame { transform: rotate(3deg); }
/* The headline result, pinned on top of the collage like a sticker --
   deliberately the loudest thing in the card (bigger type than even
   .case-name), tilted to match the playful photo scatter instead of
   sitting quietly in a bullet list. Positioned top-right so it overlaps
   the corner of the tallest (.case-proof--xl) photo rather than
   floating in empty space. */
.case-stat-badge {
  position: absolute;
  top: -6px;
  right: clamp(4px, 6vw, 40px);
  z-index: 5;
  width: clamp(112px, 15vw, 156px);
  aspect-ratio: 1;
  border-radius: 50%;
  background: #7C6B4C;
  color: #FAF4EB;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 6px;
  transform: rotate(-9deg);
  box-shadow: 0 22px 40px -14px rgba(44, 38, 32, 0.55);
}
.case-stat-badge-num {
  font-family: 'Cormorant Garamond', serif;
  font-weight: 600;
  font-size: clamp(32px, 5.2vw, 46px);
  line-height: 1;
}
.case-stat-badge-label {
  margin-top: 5px;
  font-size: 9.5px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  line-height: 1.3;
  opacity: 0.9;
}
@media (max-width: 900px) {
  .case-media--collage { padding-top: 68px; }
  .case-stat-badge { top: -50px; right: 50%; transform: translateX(50%) rotate(-6deg); }
}
.case-proof {
  display: block;
  width: 100%;
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  cursor: zoom-in;
  text-align: left;
  font: inherit;
  color: inherit;
}
.case-proof-frame {
  position: relative;
  display: block;
  border-radius: 16px;
  overflow: hidden;
  background: #F8F2E6;
  box-shadow: 0 18px 34px -22px rgba(44, 38, 32, 0.4);
}
.case-proof-frame img {
  width: 100%;
  height: auto;
  display: block;
}
/* Small "enlarge" hint, bottom-right of the thumbnail -- always faintly
   visible (so touch visitors know it's tappable too), brightening with
   the same hover/focus that scales the image. */
.case-proof-zoom {
  position: absolute;
  right: 9px;
  bottom: 9px;
  width: 27px;
  height: 27px;
  border-radius: 999px;
  background: rgba(44, 38, 32, 0.55);
  color: #FAF4EB;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0.8;
  transition: opacity 0.3s ease, background 0.3s ease;
}
.case-proof-zoom svg { width: 13px; height: 13px; }
.case-proof:hover .case-proof-zoom,
.case-proof:focus-visible .case-proof-zoom { opacity: 1; background: #2C2620; }
.case-proof-caption {
  display: block;
  margin-top: 10px;
  font-size: 12.5px;
  line-height: 1.5;
  color: #8A7C68;
}

/* ---------- Portfolio: mini case studies (e.g. Klikko / Stroeckmanhoeve) ----------
   Two lighter cases side by side, together exactly as wide as the main
   .case-block above. Each is a .case-block with this modifier: stacked
   copy-then-media (not the side-by-side split) with a 2-photo before/after
   row, since there's less to say per case than a full case-study. */
.case-mini-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 32px;
  margin-top: 32px;
}
/* Without this, the portfolio filter's el.hidden = true (main.js) does
   nothing here: this class's own `display: grid` is an author-stylesheet
   rule, which beats the UA stylesheet's [hidden]{display:none} even
   though both are single-selector specificity, because author always
   outranks user-agent regardless of specificity -- so the row stayed
   visible under every filter, not just "Social media beheer". Same fix
   already used for #booking-widget / #case-lightbox below. */
.case-mini-row[hidden] { display: none; }
/* Copy stacks above media here (single column), so the base .case-block
   gap (tuned for the side-by-side split, up to 56px) reads as far too
   much air between the last bullet and the photos -- cut way down. */
.case-block--mini { grid-template-columns: 1fr; gap: 22px; }
/* .case-list's own margin-bottom (tuned for the full-size case, where it's
   followed by another whole section) is on top of that gap -- drop it so
   the 12px above is the only space before the photos. */
.case-block--mini .case-list { margin-bottom: 0; }
/* Match the body text size used by the Explorista Travel case above
   (16px) -- the shared .case-list li default (15px) reads a touch
   smaller next to it. */
.case-block--mini .case-list li { font-size: 16px; }
/* Sized (not stretched) rather than cropped -- these proofs are full phone
   screenshots and the whole screenshot should stay visible, so each is
   shown at its natural aspect ratio (inherited from .case-proof-frame's
   base rules) just at a small fixed width instead of filling the card. */
/* flex, not grid: a grid "auto" column still grows to soak up the
   container's leftover space (same as 1fr) when nothing else is
   flexible, which left a big gap between these two fixed-width thumbs
   despite the 12px gap. Flex items with an explicit width don't grow. */
.case-block--mini .case-media { display: flex; gap: 24px; justify-content: center; }
.case-block--mini .case-proof { width: 120px; flex: none; }

@media (max-width: 900px) {
  .case-block,
  .case-block--flip { grid-template-columns: 1fr; gap: 32px; }
  .case-mini-row { grid-template-columns: 1fr; }
  .case-block--flip .case-copy { order: 1; }
  .case-block--flip .case-media { order: 2; }
  .case-metrics { grid-template-columns: repeat(2, 1fr); row-gap: 20px; }
}

/* ---------- Case study proof: lightbox ----------
   One instance in the page (see portfolio.html), reused by every
   .case-proof button across every case block -- wired generically in
   js/main.js so a second case needs no lightbox markup or JS of its own. */
#case-lightbox[hidden] { display: none; }
#case-lightbox {
  position: fixed;
  inset: 0;
  z-index: 400;
  background: rgba(28, 24, 20, 0.88);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 60px 24px;
}
.case-lightbox-figure {
  margin: 0;
  max-width: min(640px, 100%);
  animation: pop 0.25s ease both;
}
.case-lightbox-figure img {
  display: block;
  width: 100%;
  max-height: 76vh;
  object-fit: contain;
  border-radius: 16px;
  background: #FFFCF6;
}
.case-lightbox-figure figcaption {
  margin-top: 16px;
  text-align: center;
  font-size: 14px;
  color: #EDE3D3;
}
.case-lightbox-close {
  position: fixed;
  top: 22px;
  right: 26px;
  background: rgba(255, 252, 246, 0.12);
  border: 0;
  border-radius: 999px;
  width: 42px;
  height: 42px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #FAF4EB;
  cursor: pointer;
}
.case-lightbox-close:hover { background: rgba(255, 252, 246, 0.22); }
.case-lightbox-close svg { width: 18px; height: 18px; }

/* ---------- Portfolio: video results row ----------
   Centered, wrapping row on desktop; a swipeable horizontal strip on
   mobile instead of stacking, so five cards don't turn into a long
   vertical scroll. The negative margin/matching padding lets cards swipe
   flush to the screen edges despite the section's own 5vw padding. */
.case-video-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 20px;
}
@media (max-width: 620px) {
  .case-video-row {
    flex-wrap: nowrap;
    justify-content: flex-start;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    margin: 0 -5vw;
    /* Side padding, not the 5vw the section itself uses -- half the
       *viewport* (50vw, not 50%: this element's own negative margin
       already bleeds it edge to edge, but a % padding still resolves
       against its containing block -- the still-inset parent -- so 50%
       would undercount) minus half a card's own 240px width, so the
       first card centers in the middle of the screen (not flush left)
       with the next one's edge left peeking in on the right, hinting
       there's more to swipe through. Same padding on the right centers
       the last card too instead of stranding it against the edge. */
    /* Bottom padding needs to clear .case-proof-frame's own box-shadow
       (0 18px 34px -22px), not just look balanced -- overflow-x: auto
       here forces overflow-y to auto too (a same-element auto/visible
       mix isn't allowed, so the visible axis is upgraded), and an auto
       y-axis clips any shadow bleeding past the content box. 10px cut
       the shadow off; 30px comfortably clears it. */
    padding: 4px calc(50vw - 120px) 30px;
    scrollbar-width: none;
  }
  .case-video-row::-webkit-scrollbar { display: none; }
  .case-video-row .case-proof {
    flex: none;
    scroll-snap-align: center;
  }
  /* No point tapping a zoom hint when the card is already swiped to
     center stage and a tap opens the lightbox directly. */
  .case-video-row .case-proof-zoom { display: none; }
}

/* ---------- Portfolio: video content trio (e.g. "Personal TikTok") ----------
   Reusable case-study variant for showcasing real embedded videos instead
   of screenshot proofs: copy on one side, three actual <video> players on
   the other, each with its own compact result row underneath. Built as
   modifiers on the existing .case-block/.case-copy/.case-media (same card,
   spacing and scroll-reveal as every other case study above) so a second
   content pillar (BSR, Explorista, ICON, Templates, UGC all already have
   footage in videos/) is just a copy of this block with new copy, three
   new <video src>s and new .video-stat numbers -- nothing else to build.

   .case-block--video only swaps the copy/media column ratio: three
   vertical videos need more room than the single photo/collage the base
   .case-block was sized for -- weighted further toward media than that
   ratio (and given a bit more padding/gap than the base card) so the
   videos themselves read larger. */
.case-block--video {
  grid-template-columns: 0.52fr 1.48fr;
  padding: clamp(32px, 4.6vw, 58px);
}
/* For a two-row .video-trio (six clips, e.g. UGC Content): the base
   .case-block centers .case-copy against the full media height, which
   reads fine against one row but leaves the text floating mid-way down
   next to two -- this re-tops it so the copy's first line lines up with
   the top of the first video row instead. */
.case-block--video-top { align-items: start; }
.case-video-copy {
  margin: 0;
  font-size: 16px;
  line-height: 1.8;
  color: #57503F;
  font-weight: 300;
}
.video-trio {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(16px, 2.4vw, 28px);
}
/* Real <video controls>, not the silent looping .video-el used in the
   marquees elsewhere -- these are meant to be watched with sound, so
   native controls stay visible rather than autoplaying muted. 9/16 frame
   matches the TikTok/Reels footage itself; object-fit:cover only kicks in
   if a given clip's own ratio differs slightly. */
.video-proof-frame {
  border-radius: 16px;
  overflow: hidden;
  background: #000;
  aspect-ratio: 9 / 16;
  box-shadow: 0 18px 34px -22px rgba(44, 38, 32, 0.4);
}
.video-proof-frame video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* The optional per-video results ("eventueel wat video resultaten").
   Preferred option: the real analytics-app screenshot for that video --
   it drops straight in as a .case-proof/.case-proof-frame like every
   other proof image on this page (click-to-enlarge lightbox included,
   wired generically in js/main.js), just needs a top margin to sit under
   the video above it. */
.video-proof > .case-proof { margin-top: 12px; }
/* Fallback option for a video with no screenshot yet: compact typed-out
   rows instead, rather than the bigger icon+number .case-metric tiles
   above, since three of these need to fit under one narrow video column
   each. Leave a video's .video-proof-stats out entirely when there's
   nothing to show at all yet. */
.video-proof-stats {
  margin-top: 12px;
  display: grid;
  gap: 7px;
}
.video-stat {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  background: #F8F2E6;
  border-radius: 10px;
  padding: 8px 12px;
}
.video-stat-num {
  font-family: 'Jost', sans-serif;
  font-weight: 600;
  font-size: 14px;
  color: #2C2620;
  white-space: nowrap;
}
.video-stat-label {
  font-size: 10.5px;
  line-height: 1.3;
  color: #8A7C68;
  text-align: right;
}
@media (max-width: 900px) {
  /* Same breakpoint as the base .case-block's own 1-column collapse just
     above -- without this override, this unconditional (no media query)
     .case-block--video rule would otherwise win the cascade over that
     earlier media-query rule by sitting later in the file, and keep the
     desktop column split on mobile. */
  .case-block--video { grid-template-columns: 1fr; }
}
@media (max-width: 560px) {
  /* Three video columns get too narrow to read below ~560px -- stack
     them instead, capped to a comfortable single-video width. */
  .video-trio { grid-template-columns: 1fr; max-width: 260px; margin: 0 auto; }
}

/* .case-proof, sized to exactly one .video-trio column (same 3-column/
   gap math as that grid, so it lines up flush under the first video
   rather than just approximating its width) for a single combined result
   screenshot covering the whole trio (e.g. Explorista Travel's overall
   video performance) rather than a separate proof under each video --
   same click-to-enlarge .case-proof/.case-proof-frame as everywhere else
   on the page, sitting below .video-trio as a sibling in .case-media. */
.video-trio-result { width: calc((100% - 2 * clamp(16px, 2.4vw, 28px)) / 3); margin-top: 10px; }
@media (max-width: 560px) {
  /* .video-trio itself drops to one full-width (capped 260px) column at
     this breakpoint -- match that instead of the three-column math above. */
  .video-trio-result { width: 100%; max-width: 260px; margin-left: auto; margin-right: auto; }
}

/* Fallback for when there's no combined screenshot yet: typed-out cards
   in the same "Key metrics" look as the dashboard screenshots themselves
   -- label on top, bold figure below -- sized and positioned the same as
   .video-trio-result above. */
.video-metrics {
  margin-top: 10px;
  width: calc((100% - 2 * clamp(16px, 2.4vw, 28px)) / 3);
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}
.video-metric {
  background: #FFFCF6;
  border: 1px solid rgba(124, 107, 76, 0.16);
  border-radius: 12px;
  padding: 13px 15px;
}
.video-metric--lead { border-color: #7C6B4C; background: #F8F2E6; }
.video-metric-label {
  display: block;
  margin-bottom: 6px;
  font-size: 11.5px;
  color: #8A7C68;
}
.video-metric-num {
  font-family: 'Jost', sans-serif;
  font-weight: 600;
  font-size: 19px;
  color: #2C2620;
}
@media (max-width: 560px) {
  .video-metrics { width: 100%; max-width: 260px; margin-left: auto; margin-right: auto; }
}

/* ---------- Portfolio: before/after slider (BSR Waalsprong) ----------
   Text left ("voor") / iPhone slider / text right ("na"), same source
   order as the visual reading direction so mobile can just collapse the
   grid to one column with no re-ordering needed. Card styling matches
   .case-block (light card on the page's cream background); the copy
   columns reuse .case-name/.case-sub/.case-list rather than inventing
   parallel classes. */
.ba-block {
  display: grid;
  grid-template-columns: 1fr minmax(220px, 260px) 1fr;
  align-items: center;
  gap: clamp(28px, 4vw, 56px);
  background: #FFFCF6;
  border: 1px solid rgba(124, 107, 76, 0.12);
  border-radius: 28px;
  padding: clamp(28px, 4vw, 48px);
  box-shadow: 0 40px 80px -48px rgba(44, 38, 32, 0.25);
}
.ba-block .case-list { margin-bottom: 0; }
/* Match the body text size used by the Explorista Travel case above
   (16px) -- the shared .case-list li default (15px) reads a touch
   smaller next to it. */
.ba-block .case-list li { font-size: 16px; }
/* Wider middle column than the phone one (220-260px, upright 9/19.5) --
   same card, just a wider slot. Originally sized for a single
   .laptop-mockup-img; now holds the two side-by-side .shot-compare
   screenshots instead, hence the extra width.

   Must stay ABOVE the max-width:900px block below: it has the same
   (0,1,0) specificity as the plain .ba-block mobile override there, so
   whichever of the two comes later in the file wins the tie -- keeping
   this one first lets that later, conditional rule collapse the laptop
   variant back to one column on mobile too, instead of silently
   overriding it back to 3 columns at every width (the bug that made the
   .shot-compare pictures overflow the card on phones). */
.ba-block--laptop { grid-template-columns: 1fr minmax(340px, 560px) 1fr; }
@media (max-width: 900px) {
  .ba-block { grid-template-columns: 1fr; text-align: center; justify-items: center; }
  .ba-block .case-list { text-align: left; }
}

/* The slider itself fills the same slot .phone-mock-img normally would --
   dropped in place of it inside a plain .phone-mock-frame, so it inherits
   that frame's bezel/notch/home-indicator for free. */
.ba-slider {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: 26px;
  overflow: hidden;
  background: #FFFCF6;
  /* Drives both the clip-path below and the handle's `left` -- set once
     from HTML (initial position) and then only ever from JS. */
  --ba-pos: 50%;
  touch-action: pan-y;
}
.ba-after,
.ba-before {
  position: absolute;
  inset: 0;
}
.ba-after img,
.ba-before img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top;
  display: block;
}
/* Laptop variant: dropped inside .laptop-mockup-img's own .screen-overlay
   (see the "portfolio laptop mockup" rules above) instead of a
   .phone-mock-frame, so it inherits that overlay's position/size and
   only needs to match its 2px screen-corner radius -- the phone radius
   (26px) would poke out past the laptop's much squarer screen corners. */
.ba-slider--laptop { border-radius: 2px; }
/* The "voor" layer sits on top, clipped to only the left `--ba-pos`
   sliver -- the "na" layer underneath is what shows through the rest. */
.ba-before {
  clip-path: inset(0 calc(100% - var(--ba-pos)) 0 0);
}
.ba-tag {
  position: absolute;
  top: 14px;
  z-index: 2;
  padding: 5px 11px;
  border-radius: 999px;
  background: rgba(44, 38, 32, 0.55);
  color: #FAF4EB;
  font-family: 'Jost', sans-serif;
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.ba-tag--before { left: 14px; }
.ba-tag--after { right: 14px; }
.ba-handle {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--ba-pos);
  z-index: 3;
  width: 2px;
  background: rgba(255, 252, 246, 0.85);
  transform: translateX(-50%);
  pointer-events: none;
}
.ba-handle-btn {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 32px;
  height: 32px;
  border-radius: 999px;
  background: #FFFCF6;
  color: #2C2620;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 6px 16px -6px rgba(44, 38, 32, 0.5);
}
.ba-handle-btn svg { width: 15px; height: 15px; }
/* Transparent native range input laid over the whole frame -- gives the
   drag (mouse + touch, since a range thumb responds to both natively)
   and keyboard support for free instead of hand-rolling pointer events. */
.ba-range {
  position: absolute;
  inset: 0;
  z-index: 4;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: ew-resize;
  -webkit-appearance: none;
  appearance: none;
}
/* A thumb sized to match the track (100%/100%, the previous value here)
   makes the browser's own click/drag-position math divide by
   (trackWidth - thumbWidth) = 0 -- every click then snapped straight to
   the minimum instead of tracking the pointer. A normal small thumb
   (invisible anyway, via the parent's opacity: 0) keeps that math sane
   across the full 0-100 range while still giving a full-height drag
   surface (the input itself is sized to the whole frame). */
.ba-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 28px;
  height: 28px;
}
.ba-range::-moz-range-thumb {
  width: 28px;
  height: 28px;
  border: 0;
}

/* Trio of voor/na swipers below the BSR Waalsprong slider above -- same
   .ba-slider component (and the same generic JS wiring), just three
   phones side by side instead of the single phone + case-copy either
   side: these three don't need their own paragraph of copy, just a name
   underneath each. */
.ba-trio {
  display: flex;
  justify-content: center;
  gap: 80px;
  margin-top: 55px;
}
.ba-trio-item { display: flex; flex-direction: column; align-items: center; flex: none; }
.ba-trio-item .phone-mock-frame { width: 220px; max-width: 220px; }
.ba-trio-caption {
  margin: 16px 0 0;
  font-family: 'Cormorant Garamond', serif;
  font-weight: 500;
  font-size: 19px;
  color: #2C2620;
  text-align: center;
}
@media (max-width: 900px) {
  .ba-trio { flex-direction: column; align-items: center; max-width: 260px; margin: 32px auto 0; }
}

/* ---------- Portfolio: static old/new screenshot compare ----------
   Alternative to .ba-slider for a before/after that doesn't need dragging:
   old screenshot smaller and raised on the left, new one larger and
   grounded on the right, a curved arrow leading the eye between them.
   Both frames crop to a fixed portrait ratio (object-fit: cover, top-
   anchored) since the source screenshots are full-page and far taller
   than that.

   Frame/arrow sizes are viewport-relative clamps rather than percentages
   of the (variable-width) middle grid column -- percentages of that
   column plus a fixed-ish arrow reliably overflowed it on narrow screens,
   since the column shrinks to roughly the full viewport width once
   .ba-block drops to one column below 900px. Tying sizing to vw instead
   keeps old + arrow + new comfortably under the column's real width at
   every size, while the upper max() bound still caps it at the original
   desktop dimensions. */
.shot-compare {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: clamp(7px, 2.2vw, 28px);
  padding: 30px 6px 6px;
}
.shot-compare-frame {
  flex-shrink: 0;
  aspect-ratio: 3 / 4;
  border-radius: 12px;
  overflow: hidden;
  background: #FFFCF6;
  box-shadow: 0 30px 60px -30px rgba(44, 38, 32, 0.45);
}
.shot-compare-frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top;
  display: block;
}
.shot-compare-old {
  width: clamp(78px, 24vw, 190px);
  margin-bottom: clamp(14px, 4vw, 34px);
  transform: rotate(-4deg);
}
.shot-compare-new {
  width: clamp(104px, 32vw, 250px);
  transform: rotate(3deg);
}
/* Flat, mostly-horizontal curve (left-to-right reading direction) with
   just a slight upward lift at the end -- a steeper/taller viewBox here
   reads as pointing up rather than across. */
.shot-compare-arrow {
  flex-shrink: 0;
  align-self: center;
  width: clamp(30px, 7.5vw, 58px);
  height: clamp(14px, 3.6vw, 28px);
  margin-bottom: clamp(8px, 2.6vw, 22px);
  color: #B08A5F;
}

/* ---------- Empty-profile placeholder for the slider's "voor" side ----------
   Deliberately generic (no app chrome or logo) -- an illustration of "no
   profile yet", not a screenshot of any real or specific app. */
.ba-empty {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 34px 16px 0;
  background: #FFFEFB;
}
.ba-empty-avatar {
  width: 52px;
  height: 52px;
  border-radius: 999px;
  border: 1.5px dashed #D8C9AC;
  background: #F8F2E6;
}
.ba-empty-line {
  display: block;
  height: 7px;
  border-radius: 999px;
  background: #EEE4D0;
  margin-top: 10px;
}
.ba-empty-line--name { width: 74px; }
.ba-empty-line--bio { width: 100px; }
.ba-empty-stats {
  display: flex;
  gap: 20px;
  margin-top: 16px;
}
.ba-empty-stats div { text-align: center; }
.ba-empty-stats b {
  display: block;
  font-family: 'Jost', sans-serif;
  font-weight: 600;
  font-size: 13px;
  color: #B7A98C;
}
.ba-empty-stats small {
  display: block;
  margin-top: 2px;
  font-size: 8px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #C8BB9F;
}
.ba-empty-caption {
  margin: 16px 0 0;
  font-size: 9.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #C8BB9F;
}
.ba-empty-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 2px;
  width: 100%;
  margin-top: 12px;
}
.ba-empty-grid span {
  aspect-ratio: 1;
  border: 1px dashed #E7DCC5;
  background: #FCF9F1;
}

/* ---------- "Kind words" testimonial section (social-media-beheer) ----------
   A pinned photo background (see .bg-fixed-photo) behind a rotating quote
   on the left and a static before/after phone pair on the right. Only one
   .kw-slide is ever visible -- plain display swap (same approach as the
   booking widget's steps) rather than an animated height/layout
   transition, so a slide with more lines than the last never fights a
   transition for its own size. js/main.js wraps that swap in a quick
   opacity fade instead (.kw-fading, see .kw-slides/.kw-phones below and
   that JS block's own comment), so the swap itself stays instant and
   layout-safe while still reading as a crossfade to the visitor. */
.kind-words-grid {
  position: relative;
  z-index: 1;
  max-width: 1400px;
  margin: 0 auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  /* flex, not a two-column grid: a grid column sized by fr keeps the full
     share of the row regardless of how short the quote is, which is what
     left a big empty gap next to the phones once the quote text got
     smaller. Flex children instead size to their own content, so .kw-copy
     and .kw-phones sit exactly `gap` apart no matter which slide shows. */
  gap: 135px;
}
/* Laptop variant (webdesign page): align-items:center on .kind-words-grid
   re-centers .kw-phones every time .kw-copy's height changes with the
   active quote's line count, so the single MacBook mockup visibly drifts
   up/down between reviews. Pinning both columns to the top instead means
   the laptop sits at one fixed spot regardless of how long a given quote
   runs. Scoped to this modifier so the phone-pair pages keep their
   original vertically-centered look. */
.kind-words-grid.kw-align-top { align-items: flex-start; }
.kw-copy { max-width: 580px; }
.kw-eyebrow {
  margin: 0 0 18px;
  font-family: 'Cormorant Garamond', serif;
  font-style: italic;
  font-weight: 400;
  font-size: clamp(28px, 3.2vw, 40px);
  color: #FAF4EB;
}
/* Fade wrapper for the arrow-triggered crossfade -- js/main.js toggles
   .kw-fading on this (and on .kw-phones below) just before it swaps
   which .kw-slide/.kw-phone-set is active, then removes it right after,
   so the actual content swap happens invisibly at the bottom of the dip
   instead of as a hard cut. */
.kw-slides { transition: opacity 220ms ease; }
.kw-slides.kw-fading { opacity: 0; }
@media (prefers-reduced-motion: reduce) {
  .kw-slides, .kw-phones { transition: none; }
}
.kw-slide {
  display: none;
  /* Reset: <blockquote>'s default margin (~40px left/right in Chrome) is
     what was indenting the quote relative to .kw-eyebrow and .kw-nav
     above/below it, which carry no such default. */
  margin: 0;
  /* Fixed-ish min-height keeps the nav arrows from jumping up/down as
     slides of different lengths swap in -- 4 lines is the longest quote
     in this set. */
  min-height: 120px;
}
.kw-slide.is-active { display: block; }
.kw-quote {
  margin: 0 0 22px;
  max-width: 46ch;
  font-family: 'Cormorant Garamond', serif;
  font-weight: 400;
  font-size: clamp(18px, 1.75vw, 23px);
  line-height: 1.5;
  color: #FAF4EB;
}
.kw-author {
  display: flex;
  align-items: center;
  gap: 16px;
}
.kw-avatar {
  width: 46px;
  height: 46px;
  border-radius: 50%;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 252, 246, 0.14);
  border: 1px solid rgba(250, 244, 235, 0.4);
  font-family: 'Cormorant Garamond', serif;
  font-style: italic;
  font-weight: 600;
  font-size: 15px;
  color: #FAF4EB;
}
.kw-author-text { display: flex; flex-direction: column; gap: 3px; }
.kw-name {
  font-family: 'Jost', sans-serif;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #FAF4EB;
}
.kw-source {
  font-family: 'Jost', sans-serif;
  font-size: 12.5px;
  color: rgba(250, 244, 235, 0.62);
}
.kw-nav {
  display: flex;
  gap: 12px;
  margin-top: 46px;
}
.kw-arrow {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 1px solid rgba(250, 244, 235, 0.4);
  background: none;
  color: #FAF4EB;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease;
}
.kw-arrow:hover { background: rgba(255, 252, 246, 0.14); border-color: rgba(250, 244, 235, 0.7); }
.kw-arrow svg { width: 17px; height: 17px; }

/* A lighter, more playful take on .phone-mock-frame for this section only
   (scoped via .kw-phone-frame, so "Voorbeelden uit klantfeeds" and every
   other page keep the original thicker-bezel mockup) -- thinner bezel,
   and the two frames tilted a few degrees apart with the "after" phone a
   touch bigger, rather than sitting perfectly flush like a spec sheet. */
/* Same arrow-triggered crossfade as .kw-slides above, kept as a separate
   rule since .kw-phones needs its own position:relative regardless. */
.kw-phones { position: relative; transition: opacity 220ms ease; }
.kw-phones.kw-fading { opacity: 0; }
/* Each review has its own before/after pair (e.g. Jermaine's slide shows
   Je Haar Boutique, not Bart's Smart Building Partners) -- only the set
   matching the active .kw-slide is shown, same display swap as the
   quotes themselves, kept in sync by index from JS. */
.kw-phone-set { display: none; }
.kw-phone-set.is-active {
  display: flex;
  justify-content: center;
  align-items: flex-end;
  gap: 36px;
}
.kw-phone-col { text-align: center; }
.kw-phone-frame {
  padding: 5px;
  border-radius: 32px;
  box-shadow: 0 22px 44px -16px rgba(20, 17, 14, 0.55);
  transition: transform 0.3s ease;
}
.kw-phone-frame .phone-mock-notch {
  width: 52px;
  height: 14px;
  border-radius: 0 0 10px 10px;
  top: 5px;
}
.kw-phone-frame .phone-mock-img { border-radius: 27px; }
.kw-phone-frame .phone-mock-home { width: 50px; bottom: 6px; }

.kw-phone-col--before .kw-phone-frame { max-width: 150px; transform: rotate(-5deg); }
.kw-phone-col--after .kw-phone-frame { max-width: 178px; transform: rotate(4deg); }
.kw-phone-col--before { margin-bottom: 8px; }
.kw-phone-col--after { margin-bottom: -10px; }

.kw-phone-caption {
  display: block;
  margin-top: 14px;
  font-family: 'Jost', sans-serif;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(250, 244, 235, 0.72);
}

@media (max-width: 900px) {
  .kw-phones { order: -1; }
  .kw-phone-col--before .kw-phone-frame { max-width: 132px; }
  .kw-phone-col--after .kw-phone-frame { max-width: 156px; }
  /* social-media-beheer.html's "Lieve woorden" opts back out of the
     phones-first order above -- see the kw-text-first comment in that
     page's HTML. */
  .kw-text-first .kw-phones { order: 1; }
  /* Laptop variant only (webdesign page, plus its reuse in portfolio.html's
     webdesign tab): the shared 135px .kind-words-grid gap becomes a huge
     vertical gap once the two columns wrap on mobile, and the section's
     own 210px top/bottom padding (set inline, scoped to this one section
     per page) was sized for the desktop phone-pair layout, not this
     shorter stacked one -- both combined made the section feel far too
     tall with far too much air between the laptop and the review below
     it. Scoped to :has(.kw-align-top) so the phone-pair "Lieve woorden"
     instances (social-media-beheer.html, portfolio.html's own) and
     desktop are both untouched. */
  .kind-words-grid.kw-align-top { gap: 32px; }
  .bg-fixed-photo:has(> .kind-words-grid.kw-align-top) {
    padding-top: 90px !important;
    padding-bottom: 90px !important;
  }
}

/* Laptop variant of the "kind words" media column (webdesign page): a
   single MacBook mockup per review instead of a before/after phone pair
   -- .kw-phone-set still drives the show/active swap (same JS, same
   class), .kw-laptop just sizes the .laptop-mockup-img that sits inside
   it instead of the two .kw-phone-col children used elsewhere. */
.kw-phone-set.kw-laptop-set.is-active { display: block; }
.kw-laptop { max-width: 460px; margin: 0 auto; }
.kw-laptop .laptop-mockup-img { width: 100%; }

@media (max-width: 900px) {
  .kw-laptop { max-width: 340px; }
}

/* "Nog iets extra's nodig?" add-on grid (webdesign page): fixed 4-column
   layout so the columns stay side by side on desktop regardless of how
   wide the thumbnails are (auto-fit would wrap them once 4 columns no
   longer fit their minmax floor), collapsing to 2 then 1 column only at
   the mobile breakpoints. */
.extras-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 36px; }

@media (max-width: 900px) {
  .extras-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 620px) {
  .extras-grid { grid-template-columns: 1fr; gap: 60px; }
  /* Bigger photo + text and more side breathing room per a reference
     screenshot, once the grid is down to one column -- !important since
     the photo/title/text/section padding are all set inline. */
  .extras-section { padding-left: 10vw !important; padding-right: 10vw !important; }
  .extras-img { width: 100% !important; height: auto !important; max-width: 320px !important; aspect-ratio: 185 / 240; }
  .extras-title { font-size: 32px !important; }
  .extras-text { font-size: 18px !important; line-height: 1.65 !important; }
  .extras-tag { font-size: 12.5px !important; }
  /* extras-eyebrow/extras-heading deliberately have no mobile override
     here anymore -- their inline base styles now match "Wat je krijgt" /
     "Website op maat" exactly (12px eyebrow, clamp(32px,3.6vw,54px)
     heading), and that clamp() already shrinks correctly on its own at
     narrow widths, so forcing a fixed 14px/40px here would just pull
     this section's sizing out of sync with those again. */
  /* "Mijn werkwijze" intro -- applied on webdesign.html, social-media-
     beheer.html and content-creatie.html (each has its own eyebrow/
     heading with these classes added; index.html's doesn't carry them
     and keeps the smaller default). */
  .werkwijze-eyebrow { font-size: 14px !important; }
  .werkwijze-heading { font-size: 40px !important; }
  /* Less run-up into this section from whatever precedes it, on the same
     three pages as above -- scoped to .wd-werkwijze-section (added to
     each page's .werkwijze-section) rather than lowering .werkwijze-
     section's shared padding, which index.html also relies on. */
  .wd-werkwijze-section { padding-top: 40px !important; padding-bottom: 55px !important; }
}

/* More room between the intro's mockup (laptop on webdesign.html, phone on
   social-media-beheer.html / content-creatie.html) and "01. Kennismaking"
   once the intro stacks above the steps on mobile -- .wd-werkwijze-grid
   (added to each of those three pages' .werkwijze div) scopes this
   instead of raising .werkwijze's shared 48px mobile gap, which
   index.html keeps as-is. Same 900px breakpoint as .werkwijze's own
   collapse. */
@media (max-width: 900px) {
  .wd-werkwijze-grid { gap: 90px !important; }
}

/* "Over mij" -> FAQ -> final CTA, mobile only, on webdesign.html, social-
   media-beheer.html and content-creatie.html (each section below carries
   these classes on all three pages): smaller photo, and less run-up
   between each of these three sections. All three share the same
   #FFFCF6-ish background, so shrinking the padding here doesn't
   introduce a visible seam. !important since each section sets its own
   padding inline. */
@media (max-width: 620px) {
  .overmij-photo { max-width: 360px !important; margin: 0 auto !important; }
  .overmij-section { padding-top: 55px !important; padding-bottom: 28px !important; }
  /* Gap between the button (end of the "title" area, which the button
     lives in) and the photo below it, once this stacks on mobile --
     overrides the shared .mobile-reorder row-gap for this instance only. */
  .mobile-reorder.overmij-section { row-gap: 60px !important; }
  .faq-section { padding-top: 28px !important; padding-bottom: 40px !important; }
  .final-cta-section { padding-top: 30px !important; }
  /* "Mijn verhaal" (over-mij.html) -- stack the eyebrow/heading above the
     story paragraphs instead of the two-column desktop layout. */
  .mr-story-grid { grid-template-columns: 1fr !important; gap: 24px !important; }
}

/* "Zo ziet dat er dan uit" (social-media-beheer.html) -- desktop pinned-
   copy treatment. .feed-devices-desktop is the scroll track: its own
   height (not its sticky child's) is what sets how long the copy stays
   pinned, since the sticky child is the only in-flow content and would
   otherwise size the track to exactly 100vh. The two phones are pulled
   out of flow with position:absolute so they add no height of their
   own and instead just scroll past at a fixed spot in the (much
   taller) track -- each one's own `top` is what times its pass, since
   the sticky copy has no other in-flow sibling and so sits flush with
   the track's own top edge: a phone placed at "100vh + copy's own
   height" starts exactly one screen-height below the viewport at the
   instant the copy locks into its pinned position, then rides up
   through the frame as the page keeps scrolling underneath it, purely
   as a side effect of being an ordinary (non-sticky) element in a very
   tall container. The left phone (.feed-devices-a) is placed at that
   point so its pass starts right as the pin begins; the right phone
   (.feed-devices-b) sits noticeably further down the track so its pass
   only starts once the left one is already under way. Once you've
   scrolled past the track's bottom the pin releases and the copy slides
   away under "Wat ik voor je doe" below. Hidden below 901px in favour
   of the plain static .feed-devices-mobile block (see the HTML comment
   above this section for why: a pinned block over a stack of
   full-width sections reads as broken on a phone). */
.feed-devices-section { background: #FFFCF6; }
.feed-devices-desktop { position: relative; height: 300vh; }
.feed-devices-sticky {
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2;
}
/* Copy width and phone width both scale with viewport width (clamp/min
   rather than fixed px) so the gap between them never closes up on a
   narrower desktop window -- a fixed-px pairing looked fine at a wide
   1300px+ window but collided with the phones as soon as the window
   narrowed toward the 900px handoff to .feed-devices-mobile. */
.feed-devices-copy { max-width: min(640px, 44vw); padding: 0 16px; }
.feed-devices-phone { position: absolute; z-index: 1; }
.feed-devices-phone .phone-mock-frame { max-width: clamp(125px, 13vw, 190px); margin: 0 auto; }
/* index.html's "Even eerlijk" drops a .laptop-mockup-img into one
   .feed-devices-phone slot instead of a .phone-mock-frame (see that
   section's own HTML comment) -- bigger than .opmaat-devices-site's own
   laptops (webdesign.html) it was first sized like, per the client's call
   that it read too small next to the phone opposite it -- and, same as
   those, pointer-events:none so it never triggers its own :hover
   scroll-through preview while riding past the pinned copy here. */
.feed-devices-phone .laptop-mockup-img { max-width: clamp(240px, 24vw, 380px); margin: 0 auto; pointer-events: none; }
.feed-devices-a { top: 115vh; left: 4vw; transform: rotate(-8deg); }
.feed-devices-b { top: 155vh; right: 4vw; transform: rotate(8deg); }

/* Thinner bezel, tighter corners for .phone-mock-frame at the 150-ish px
   size used in .feed-devices-mobile -- same fix, and same reason, as
   .feed-trio-phone/.kw-phone-frame elsewhere (see their own comments):
   the base frame's 36px radius/10px padding are tuned for its usual
   220-230px width and read as a thick, overly rounded pill down here. */
.feed-devices-mobile-phone { padding: 4px; border-radius: 20px; }
.feed-devices-mobile-phone .phone-mock-notch { width: 23%; height: 3%; top: 1.1%; border-radius: 0 0 8px 8px; }
.feed-devices-mobile-phone .phone-mock-home { width: 22%; height: 0.8%; bottom: 1.4%; }
.feed-devices-mobile-phone .phone-mock-img { border-radius: 17px; }

/* index.html's "Even eerlijk" ONLY -- MOBILE view. The other 6 pages
   reusing .feed-devices-section for their own two-phone "Zo ziet dat er
   dan uit" keep the original plain static .feed-devices-mobile block
   (background/padding set inline per that page's own copy, see the HTML
   there); this one got its own pinned-copy/scroll-over-devices track
   instead, per the client's call to bring that treatment to mobile here
   specifically (desktop above is untouched, still the original side-by-
   side .feed-devices-a/-b). Same mechanic, and the same device sizing, as
   .opmaat-devices-desktop/-sticky/-copy/-site below (see that block's own
   comment for the full reasoning) -- copied here with its own class names
   rather than reused directly since .opmaat-devices-section has no
   .feed-devices-desktop counterpart to hand off to above 900px. */
.feed-devices-mobile-track { position: relative; height: 300vh; }
.feed-devices-mobile-sticky {
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2;
}
.feed-devices-mobile-copy { max-width: 760px; padding: 0 16px; text-align: center; }
.feed-devices-mobile-device { position: absolute; z-index: 3; pointer-events: none; }
.feed-devices-mobile-device-a { top: 115vh; left: 3vw; transform: rotate(-6deg); }
.feed-devices-mobile-device-b { top: 155vh; right: 3vw; transform: rotate(6deg); }
.feed-devices-mobile-device-a .phone-mock-frame { max-width: clamp(110px, 11vw, 165px); margin: 0 auto; }
.feed-devices-mobile-device-b .laptop-mockup-img { max-width: clamp(200px, 20vw, 320px); margin: 0 auto; }

.feed-devices-mobile { display: none; }

@media (max-width: 900px) {
  .feed-devices-desktop { display: none; }
  .feed-devices-mobile { display: block; }
}

/* "Website op maat" (webdesign.html) -- one markup for every screen
   size, same choice as .results-showcase-section on social-media-
   beheer.html (see that class's own HTML comment for why) -- so this
   uses the same pinned-copy/scroll-past-devices mechanics as .feed-
   devices-desktop above (see that block's own comment) at every width,
   no separate simplified mobile block. The copy is deliberately sized
   WITHOUT reserving side clearance for the two devices, and the devices
   ride ABOVE the pinned copy rather than behind it (z-index:3 vs. the
   copy's z-index:2 -- same order as .results-showcase-photo over
   .results-showcase-sticky, and -- on mobile only -- index.html's own
   .feed-devices-mobile-device above too, which copies this same
   treatment there): the client's call was that the copy should read as
   wide and the photo as big as they comfortably can regardless of
   screen size, even if that means a
   passing laptop covers part of the text for a moment rather than
   staying clear of it in its own lane. */
.opmaat-devices-section { background: #F8F2E6; }
.opmaat-devices-desktop { position: relative; height: 300vh; }
.opmaat-devices-sticky {
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2;
}
.opmaat-devices-copy { max-width: 760px; padding: 0 16px; text-align: center; }
/* pointer-events:none since these now ride ABOVE the copy (see the HTML
   comment): without it, a laptop passing over the "Vraag een voorstel
   aan" button at just the wrong scroll position would silently eat the
   click instead of letting it reach the (purely decorative, non-
   interactive) laptop through to the real button underneath. */
.opmaat-devices-site { position: absolute; z-index: 3; width: clamp(200px, 20vw, 320px); pointer-events: none; }
/* Reversed from .feed-devices-a/-b: the right laptop's `top` is the
   smaller one, so ITS pass starts first, with the left one following
   once it's already under way -- see the HTML comment on this section
   for why. */
.opmaat-devices-right { top: 115vh; right: 3vw; transform: rotate(6deg); }
.opmaat-devices-left { top: 155vh; left: 3vw; transform: rotate(-6deg); }

/* "Resultaten van mijn klanten" (social-media-beheer.html) -- the
   before/after phones sit side by side in .case-ba-row, dropped into
   .case-media in place of its default single stacked column (see
   .case-media's own comment above), each with an italic caption
   underneath modeled on the phone captions in "Zo ziet dat er dan uit"
   higher up this page. */
.case-ba-row { display: flex; gap: clamp(14px, 3vw, 28px); justify-content: center; }
.case-ba-item { width: clamp(120px, 17vw, 210px); text-align: center; }
.case-ba-caption { margin: 14px 0 0; font-family: 'Cormorant Garamond', serif; font-style: italic; font-weight: 400; font-size: 16px; color: #7C6B4C; }

/* Mirrors .case-stat-badge's default top-right placement (see that
   class's own comment, above) onto the top-left corner instead, since
   here the badge sits over .case-media's phones on the left side of
   the card rather than a photo collage on the right -- same circle,
   just flipped rotation so it still tilts away from the corner it
   overlaps. */
.case-stat-badge--left { right: auto; left: clamp(4px, 6vw, 24px); transform: rotate(9deg); }
@media (max-width: 900px) {
  .case-stat-badge--left { left: 50%; transform: translateX(-50%) rotate(6deg); }
}

/* Pinned to the very top-left corner, poking off the edge of the mockup,
   at every breakpoint -- unlike .case-stat-badge--left it never re-centers
   on mobile, since the point here is keeping it clear of the laptop
   screen (not just clear of the "Bekijk live" pill). Doubled-up class on
   the selector beats the plain .case-stat-badge mobile media query above
   regardless of source order. */
.case-stat-badge.case-stat-badge--corner {
  top: -18px;
  right: auto;
  left: -18px;
  transform: rotate(-8deg);
}
/* Above mobile, shrink below the shared .case-stat-badge clamp (which
   tops out at 156px) so the badge leaves more of the laptop screen
   showing -- mobile keeps the default size, already sized right. */
@media (min-width: 901px) {
  .case-stat-badge.case-stat-badge--corner {
    width: clamp(100px, 9.5vw, 124px);
    padding-top: 2px;
    padding-bottom: 10px;
  }
}
@media (max-width: 900px) {
  .case-stat-badge.case-stat-badge--corner {
    top: -14px;
    left: -14px;
    transform: rotate(-8deg);
  }
}

/* The two TikTok-results screenshots ("het bewijs") sit inside
   .case-copy, not .case-media, since they're proof for a claim made in
   the copy rather than the case's main before/after image. */
.case-video-proof { display: flex; flex-wrap: wrap; gap: 14px; }
.case-video-proof .case-proof { width: clamp(90px, 12vw, 140px); flex: none; }

/* Modifier for this one card (on top of the shared .case-block, which
   portfolio.html's cases also use unmodified): a bit more room top and
   bottom than the shared padding gives, and a second, much bigger and
   darker shadow layered behind the existing one -- .case-block's own
   shadow alone barely reads once the card is floating over "Dit soort
   resultaten..."'s dark photo (see that section's own z-index comment)
   rather than the light page background every other .case-block sits
   on, so it needs a shadow dark/large enough to still ground the card
   against either backdrop. */
.case-block--float {
  padding-top: calc(clamp(28px, 4vw, 48px) + 32px);
  padding-bottom: calc(clamp(28px, 4vw, 48px) + 32px);
  box-shadow: 0 40px 80px -48px rgba(44, 38, 32, 0.25), 0 70px 100px -35px rgba(20, 17, 14, 0.55);
}

/* "Dit soort resultaten zijn bekend terrein voor ons!" (social-media-
   beheer.html) -- desktop pinned-photo-and-copy treatment. Same track/
   sticky mechanics as .feed-devices-desktop above (a tall track whose
   only in-flow child is the sticky box, so the track's own height --
   not the sticky child's -- sets the pin duration; everything else is
   pulled out of flow with position:absolute so it scrolls past at a
   fixed spot instead of adding height of its own), but the sticky box
   here carries a background photo and its darkening overlay along with
   the heading, all three layered in one box so they lock together in
   perfect sync rather than needing to be kept in sync by hand. */
.results-showcase-section { position: relative; background: #14110E; }
.results-showcase-track { position: relative; height: 270vh; }
.results-showcase-sticky {
  position: sticky;
  top: 0;
  height: 100vh;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 32px;
}
.results-showcase-bg { position: absolute; inset: 0; background-size: cover; background-position: center 60%; }
/* Flat color, not a gradient -- js/main.js animates its opacity
   continuously as you scroll through the track (see that block's
   comment), which a plain color can do smoothly with nothing more than
   an opacity value; a gradient would need its color stops themselves
   rewritten every frame to darken the same way. */
.results-showcase-overlay { position: absolute; inset: 0; background: #14110E; opacity: 0.3; }
/* Fades and lifts in the first time it's actually seen -- .in-view is
   added once by js/main.js via IntersectionObserver (same one-shot
   pattern as the other scroll reveals on this page, e.g. .case-count-
   up), separate from the darkening-overlay scroll listener above,
   which has to keep running for as long as the section stays pinned
   rather than fire once. */
.results-showcase-heading {
  position: relative;
  z-index: 1;
  max-width: 680px;
  margin: 0;
  padding: 0 24px;
  text-align: center;
  font-family: 'Cormorant Garamond', serif;
  font-weight: 300;
  font-size: clamp(30px, 4.2vw, 58px);
  line-height: 1.2;
  color: #FAF4EB;
  opacity: 0;
  transform: translateY(26px);
  transition: opacity 1s ease, transform 1s ease;
}
.results-showcase-heading.in-view { opacity: 1; transform: translateY(0); }
/* "Bekijk portfolio" CTA -- same outline-on-dark button look as
   index.html's hero "Bekijk mijn werk" (transparent fill, cream
   border/text). Lives right inside .results-showcase-sticky, below the
   heading, so it stays pinned to the photo like the heading does --
   unlike the heading though, its opacity/transform aren't a one-shot
   .in-view class flip: js/main.js's darkening-overlay scroll listener
   (see that block's comment) sets them directly every frame, driven by
   how far .results-showcase-sticky itself has moved off the top of the
   viewport. That's 0 for the entire time the sticky box is genuinely
   stuck (top stays pinned at 0), so the button stays invisible through
   the whole pin -- then climbs to 1 over the first stretch of scroll
   AFTER the pin releases and the sticky box actually starts moving
   with the page again, i.e. exactly when "de achtergrond weer gaat
   scrollen". No CSS transition on opacity/transform here (unlike the
   heading) since a per-frame JS-driven value already reads as smooth;
   a transition would only add lag against the scroll it's tracking.
   Its hover state is a real :hover rule instead of this page's usual
   style-hover attribute (see that helper's own comment in js/main.js)
   deliberately: style-hover resets the WHOLE style attribute back to
   the element's original markup on mouseleave, which would clobber the
   opacity/transform the scroll listener just wrote -- and scrolling
   content under a stationary cursor genuinely fires mouseenter/
   mouseleave, so this isn't just a hover-while-scrolling edge case. */
.results-showcase-cta {
  position: relative;
  z-index: 1;
  background: none;
  color: #FAF4EB;
  border: 1px solid rgba(250, 244, 235, 0.55);
  border-radius: 999px;
  padding: 17px 34px;
  cursor: pointer;
  font-family: 'Jost', sans-serif;
  font-size: 13px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  opacity: 0;
}
.results-showcase-cta:hover { background: rgba(255, 252, 246, 0.14); border-color: #FAF4EB; }
@media (prefers-reduced-motion: reduce) {
  .results-showcase-heading { opacity: 1; transform: none; transition: none; }
}
/* The five results, spread down the track at their own fixed spot each
   (see .feed-devices-a/-b above for why a plain fixed position here is
   enough to make them scroll past normally) -- alternating side and
   rotation so they read as a loose scatter, with the middle one
   (photo-3) placed close enough to center to actually cross over the
   pinned heading as it passes, per the reference the client sent.
   photo-1's own top is what keeps it off-screen until the photo+text
   lock into place: a plain (non-sticky) element positioned 100vh+ down
   a track whose sticky sibling occupies the first 100vh isn't visible
   until scrolling has advanced that far, same trick as .feed-devices-a
   above -- 4vh past that minimum is enough to read as "just after" the
   pin rather than simultaneous with it. The other four then follow in
   quick succession (16vh apart, much tighter than the 40vh+ gaps
   elsewhere on this page) so all five have cycled through well before
   the track runs out, keeping the whole section shorter to scroll
   through. */
.results-showcase-photo { position: absolute; z-index: 3; width: clamp(180px, 18vw, 250px); }
.results-showcase-photo .case-proof-frame { box-shadow: 0 30px 60px -25px rgba(0, 0, 0, 0.6); }
.results-showcase-photo-1 { top: 104vh; left: 7vw; transform: rotate(-6deg); }
.results-showcase-photo-2 { top: 120vh; right: 8vw; transform: rotate(5deg); }
.results-showcase-photo-3 { top: 136vh; left: 34vw; transform: rotate(-4deg); }
.results-showcase-photo-4 { top: 152vh; right: 9vw; transform: rotate(6deg); }
.results-showcase-photo-5 { top: 168vh; left: 9vw; transform: rotate(-5deg); }

/* "Heb je nog geen goede foto's of video's?" (social-media-beheer.html):
   the phone (reuses .phone-mock-frame, see that class's own comment)
   sits half off the left edge of the BSR Waalsprong shoot photo --
   .bsr-shoot-wrap is the positioning context (it carries .mr-photo/
   .invest-photo, so it's the same box the grid already places, no
   extra wrapper-specific sizing needed) and the phone is centered
   exactly on that edge via left:0 + translateX(-50%), so half its
   width lands on the photo and half hangs off it. */
.bsr-shoot-wrap { position: relative; }
.bsr-shoot-phone { position: absolute; top: 50%; left: 0; transform: translate(-50%, -50%); z-index: 2; }
@media (max-width: 620px) {
  /* Smaller and pulled in less far off the edge -- .invest-photo caps
     out at 300px wide on mobile (see its own CSS), so the default
     -50%-of-230px overhang risks running past the viewport edge on a
     narrow phone. */
  .bsr-shoot-phone { max-width: 130px; transform: translate(-32%, -50%); }
}
