/* ==========================================================================
   UXfactor — fonts
   ========================================================================== */

@font-face {
  font-family: 'Sloth';
  src: url('../assets/fonts/Sloth-Light.woff2') format('woff2'),
       url('../assets/fonts/Sloth-Light.ttf') format('truetype');
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Sloth';
  src: url('../assets/fonts/Sloth-Regular.woff2') format('woff2'),
       url('../assets/fonts/Sloth-Regular.ttf') format('truetype');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Sloth';
  src: url('../assets/fonts/Sloth-SemiBold.woff2') format('woff2'),
       url('../assets/fonts/Sloth-SemiBold.ttf') format('truetype');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Sloth';
  src: url('../assets/fonts/Sloth-Bold.woff2') format('woff2'),
       url('../assets/fonts/Sloth-Bold.ttf') format('truetype');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Sloth';
  src: url('../assets/fonts/Sloth-ExtraBold.woff2') format('woff2'),
       url('../assets/fonts/Sloth-ExtraBold.ttf') format('truetype');
  font-weight: 800;
  font-style: normal;
  font-display: swap;
}

/* ==========================================================================
   UXfactor — design tokens
   ========================================================================== */

:root {
  /* Colors */
  --color-bg: #000036;
  --color-bg-lavender-10: rgba(188, 185, 255, 0.1);
  --color-bg-lavender-20: rgba(188, 185, 255, 0.2);
  --color-white: #ffffff;
  --color-black: #000000;
  --color-lavender: #bcb9ff;
  --color-lavender-50: rgba(188, 185, 255, 0.5);
  --color-lime: #e8ff55;
  --color-blue: #3c12dc;
  --color-text-p: rgba(255, 255, 255, 0.8);

  /* Typography */
  --font-family: 'Sloth', -apple-system, BlinkMacSystemFont, sans-serif;
  --tracking-tight: -0.01em;

  /* Layout */
  --container-max: 1600px;
  --container-padding: 24px;
  --header-height: 96px;

  /* Motion */
  --ease: cubic-bezier(0.4, 0, 0.2, 1);
}

/* ==========================================================================
   Reset
   ========================================================================== */

*,
*::before,
*::after {
  box-sizing: border-box;
}

* {
  margin: 0;
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  min-height: 100svh;
  background: var(--color-bg);
  color: var(--color-white);
  font-family: var(--font-family);
  font-size: 20px;
  font-weight: 300;
  line-height: 1.4;
  letter-spacing: var(--tracking-tight);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img {
  display: block;
  max-width: 100%;
}

a {
  color: inherit;
  text-decoration: none;
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  cursor: pointer;
}

ul {
  list-style: none;
  padding: 0;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ==========================================================================
   Utilities
   ========================================================================== */

.container {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--container-padding);
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  top: -100px;
  left: 24px;
  z-index: 1000;
  padding: 12px 20px;
  background: var(--color-lime);
  color: var(--color-black);
  border-radius: 50px;
  font-weight: 600;
  transition: top 0.2s var(--ease);
}

.skip-link:focus {
  top: 24px;
}

.text-accent {
  color: var(--color-lavender);
  font-weight: 700;
}

:focus-visible {
  outline: 2px solid var(--color-lime);
  outline-offset: 3px;
}

/* ==========================================================================
   Buttons
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 16px 20px;
  border-radius: 50px;
  font-size: 18px;
  font-weight: 600;
  line-height: 24px;
  letter-spacing: var(--tracking-tight);
  white-space: nowrap;
  transition: transform 0.2s var(--ease), background-color 0.2s var(--ease), border-color 0.2s var(--ease), color 0.2s var(--ease);
}

.btn:hover {
  transform: translateY(-2px);
}

.btn img {
  width: 24px;
  height: 24px;
}

/* Rotates the (default east/rightward-pointing) arrow icon to point down
   — same rotate(90deg)/-90deg convention already used for the beloftes/
   waarden carousel nav buttons. */
.btn-icon--down {
  transform: rotate(90deg);
}

/* Reuses the default east/rightward-pointing arrow icon for a "back"
   button instead of shipping a separate west-pointing asset — same
   flip already used on .page-back's own icon. */
.btn-icon--flip {
  transform: scaleX(-1);
}

.btn-primary {
  background: var(--color-lime);
  color: var(--color-black);
}

.btn-primary:hover {
  background: #f2ff8a;
}

.btn-secondary {
  background: transparent;
  color: var(--color-lavender);
  border: 2px solid var(--color-lavender-50);
}

.btn-secondary:hover {
  border-color: var(--color-lavender);
  color: var(--color-white);
  background: var(--color-bg-lavender-10);
}

/* ==========================================================================
   Header
   ========================================================================== */

/* position: fixed (not sticky) so <main> starts at the same y: 0 as the
   header instead of being pushed down by its flow-height — the header
   floats transparently over the hero, rather than the hero starting only
   once the header ends. Every other page's own top-of-page content
   (.page-hero, .hero--compact) compensates by adding --header-height back
   into its own top padding, so it still lands the same distance below the
   header as before; only the homepage's hero is meant to genuinely extend
   underneath it. */
.site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  background: transparent;
  backdrop-filter: blur(0px);
  -webkit-backdrop-filter: blur(0px);
  border-bottom: 1px solid transparent;
  transition: background-color 0.5s var(--ease), backdrop-filter 0.5s var(--ease), border-color 0.5s var(--ease);
}

/* Applied by js/script.js once the page has scrolled past 100px, removed
   again once scrolled back above it — see initHeaderScroll. rgba mirrors
   --color-bg (#000036) — a plain hex var can't carry alpha, and this needs
   to stay partly see-through for the blur. */
.site-header.is-scrolled {
  background: rgba(0, 0, 54, 0.5);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border-bottom-color: rgba(255, 255, 255, 0.06);
}

.header-inner {
  display: flex;
  align-items: center;
  gap: 40px;
  height: var(--header-height);
}

.logo img {
  height: 24px;
  width: auto;
}

.nav {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-inline-start: 32px;
}

.nav a {
  padding: 12px 20px;
  font-size: 18px;
  font-weight: 500;
  color: var(--color-lime);
  white-space: nowrap;
  border-radius: 99px;
  transition: opacity 0.2s var(--ease), background-color 0.2s var(--ease);
}

.nav a:hover {
  color: var(--color-white)
}

.nav a.is-active,
.mobile-nav a.is-active {
  background: rgba(255, 255, 255, 0.1);
  color: var(--color-white);
}

.nav a.is-active:hover {
  opacity: 1;
}

/* Floating round button (not a header control) — lives outside <header>
   in the markup, as a fixed sibling, since .site-header always carries a
   backdrop-filter (even blur(0px) pre-scroll), and a non-none filter/
   backdrop-filter value makes its element the containing block for any
   position:fixed descendant. Nesting this inside <header> would confine
   it to the header's own ~header-height-tall box instead of the true
   viewport corner. Same reasoning applies to .mobile-nav below. */
.nav-fab {
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  position: fixed;
  right: 16px;
  bottom: 16px;
  z-index: 200;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  border: 2px solid var(--color-lavender-50);
  background: rgba(0, 0, 54, 0.5);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  transition: border-color 0.2s var(--ease), background-color 0.2s var(--ease);
}

.nav-fab:hover {
  border-color: var(--color-lavender);
  background: rgba(0, 0, 54, 0.7);
}

.nav-fab span {
  display: block;
  width: 22px;
  height: 2px;
  background: var(--color-white);
  transition: transform 0.25s var(--ease), opacity 0.25s var(--ease);
}

.nav-fab[aria-expanded="true"] span:nth-child(1) {
  transform: translateY(7px) rotate(45deg);
}

.nav-fab[aria-expanded="true"] span:nth-child(2) {
  opacity: 0;
}

.nav-fab[aria-expanded="true"] span:nth-child(3) {
  transform: translateY(-7px) rotate(-45deg);
}

/* Fullscreen, not a header dropdown — position:fixed + inset:0 covers the
   true viewport (see the .nav-fab comment above for why this has to live
   outside <header>). justify-content:flex-end anchors the link list to
   the bottom of the screen, near the .nav-fab that opened it, rather than
   starting the list at the top. */
.mobile-nav {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 150;
  flex-direction: column;
  /* flex-start (not the stretch default) so each link sizes to its own
     text instead of spanning the full width — needed for the pill
     background below to hug the text like its desktop counterpart,
     rather than becoming a full-width rounded bar. */
  align-items: flex-start;
  justify-content: flex-end;
  gap: 8px;
  padding: 24px var(--container-padding) 96px;
  background: rgba(0, 0, 54, 0.92);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
}

/* Matches .nav a's own pill treatment (padding scaled up for this menu's
   larger font, same border-radius) — .nav a.is-active's shared background
   below is what actually turns this into a visible pill. No divider
   between items; .mobile-nav's own gap is the only separation now. */
.mobile-nav a {
  padding: 16px 24px;
  font-size: 24px;
  font-weight: 600;
  color: var(--color-lime);
  border-radius: 99px;
  transition: background-color 0.2s var(--ease), color 0.2s var(--ease);
}

.mobile-nav[data-open="true"] {
  display: flex;
}

/* Locked by JS (see initMobileNav in js/script.js) while the fullscreen
   menu is open, so the page behind it can't be scrolled at the same time. */
body.nav-open {
  overflow: hidden;
}

/* ==========================================================================
   Hero
   ========================================================================== */

.hero {
  position: relative;
  padding-block: 80px 120px;
  overflow: hidden;
}

/* Homepage-only: fills the full viewport height and centers its content
   vertically, leaving maximum room for the hero-orb animation on either
   side. Genuinely extends underneath the fixed header (no --header-height
   subtracted) since the header is transparent by default and the orb is
   meant to reach all the way to the true top of the page. .hero's own
   padding-block above is left untouched since uxfactor.html still uses the
   plain .hero for its own (non-centered) hero treatment. */
.hero--home {
  min-height: 100svh;
  display: flex;
  align-items: center;
}

.hero .container {
  position: relative;
  z-index: 1;
}

/* --hero-scroller-size/--hero-scroller-height are set by
   initHeroScroller() in js/script.js on mobile (<=768px) — sized from the
   widest oneliner's actual measured width so the text fills the viewport
   exactly, rather than the vw-based clamp below, which was tuned
   conservatively and leaves the text looking small on phones. The clamp
   stays as the fallback for every other viewport, untouched. */
.hero-scroller {
  height: var(--hero-scroller-height, clamp(48px, 10.8vw, 115.2px));
  overflow: hidden;
  margin-top: 0;
  margin-bottom: 8px;
}

.hero-scroller__list {
  transition: transform 0.6s var(--ease);
}

.hero-scroller__list li {
  height: var(--hero-scroller-height, clamp(48px, 10.8vw, 115.2px));
  display: flex;
  align-items: center;
  font-size: var(--hero-scroller-size, clamp(22.8px, 6.24vw, 86.4px));
  font-weight: 800;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--color-white);
  white-space: nowrap;
}

.hero-subtitle {
  font-size: clamp(24px, 3.4vw, 42px);
  font-weight: 400;
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: var(--color-white);
  margin-bottom: 48px;
}

/* The "ux" in the hero subtitle doubles as the hero-orb's click/tap
   trigger (see js/hero-orb.js) — this lights it up like the primary CTA
   so it reads as interactive rather than just a styled word. .is-active
   is toggled by JS on click, not :hover, so the color stays in sync with
   the actual (toggled) orb state rather than just cursor position, and
   works identically on touch. */
.hero-subtitle .text-accent {
  cursor: pointer;
  transition: color 0.2s var(--ease);
}

.hero-subtitle .text-accent.is-active {
  color: var(--color-lime);
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
}

/* Same frosted-glass treatment as .site-header.is-scrolled/.nav-fab
   (rgba(0,0,54,0.5) + blur(16px)) — this button isn't scroll-state
   driven like the header, so it's applied permanently rather than
   toggled by a class. */
.hero-actions .btn-secondary {
  background: rgba(0, 0, 54, 0.2);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

/* ==========================================================================
   Brand statement — standalone section between the hero and Succesverhalen
   ========================================================================== */

.brand-statement {
  padding-block: 96px 192px;
  /* No scroll-margin-top here on purpose: the hero CTA centers this
     section in the viewport via scrollIntoView({ block: 'center' }) in
     js/script.js, and scroll-margin is folded into that calculation too
     — a top-only margin would skew the centering off by half its value
     rather than clear the fixed header (which centering already clears
     anyway, unless the section is nearly as tall as the viewport). */
}

.brand-statement__text {
  max-width: 948px;
  font-size: clamp(20px, 2.4vw, 32px);
  font-weight: 400;
  line-height: 1.5;
  color: var(--color-white);
  margin-bottom: 24px;
}

.brand-statement__tagline {
  font-size: clamp(20px, 2.4vw, 32px);
  font-weight: 700;
  color: var(--color-lavender);
}

/* ==========================================================================
   Page hero — simple title + intro used on secondary pages
   ========================================================================== */

/* +var(--header-height) since <main> now starts at the same y: 0 as the
   fixed header (see .site-header) instead of being pushed down by it —
   this keeps the title landing exactly 96px below the header's own bottom
   edge, same as before, rather than 96px below the true page top (which
   would land it partly underneath the header). */
.page-hero {
  position: relative;
  padding-block: calc(var(--header-height) + 96px) 0;
}

/* Matches .page-hero's own top padding, so pages using .hero (uxfactor)
   instead of .page-hero (tools-talent, beloftes, waarden) still land
   their title at the same distance from the header. */
.hero--compact {
  padding-top: calc(var(--header-height) + 96px);
}

.page-hero__title {
  font-size: clamp(36px, 5.2vw, 64px);
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--color-white);
  margin-bottom: 24px;
}

.page-hero__text {
  font-size: clamp(20px, 2.4vw, 32px);
  font-weight: 400;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.6);
  max-width: 938px;
}

/* Wraps just the title (not the whole .page-hero), so .page-back can be
   positioned relative to the title's own box instead of computed
   independently from --header-height — a fixed amount from the title
   itself, so it can never drift out of sync if the hero's own top
   offset formula ever changes. */
.page-hero__heading {
  position: relative;
}

/* Circular "back" button used at the top of nested secondary pages
   (e.g. Beloftes, Waarden — both nested under the UXfactor page). Sits
   directly in front of (left of, roughly vertically centered with) the
   title, per Figma (node 324:1636): title at y:254, button at y:265/
   size 56 — an 11px drop from the title's own top edge, now expressed
   directly against .page-hero__heading (the title's own containing
   block) rather than via --header-height. */
.page-back {
  position: absolute;
  top: 11px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  border: 2px solid var(--color-lavender-50);
  transition: border-color 0.2s var(--ease), background-color 0.2s var(--ease);
}

.page-back:hover {
  border-color: var(--color-lavender);
  background: var(--color-bg-lavender-10);
}

/* Toggled by initPageBackSticky (js/script.js) once scrolling would carry
   the button past 56px below the header — locks it there instead, on the
   >900px layout where it still sits beside the title (left is set inline
   by the same script, matching whatever its natural position measured
   out to, so it doesn't jump sideways on very wide viewports where
   .container itself sits inset from the true viewport edge).
   z-index matches the mobile floating rule below — without it, fixed
   positioning alone doesn't guarantee paint order: pages like Beloftes/
   Waarden give their own content an explicit z-index (to layer foreground
   text above their oversized ghost text), and any element with a positive
   z-index paints above a z-index:auto element regardless of position,
   silently trapping clicks on the button once scrolled past it. */
.page-back.is-stuck {
  position: fixed;
  top: calc(var(--header-height) + 56px);
  z-index: 200;
  background: rgba(0, 0, 54, 0.5);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
}

/* Aligns page-hero content, and — via the block below — every homepage
   content section, with the primary nav's first link: 150 (logo) + 40
   (header-inner gap) + 32 (nav's own margin-inline-start) + 20 (nav
   link's own left padding) = 242, measured directly against .nav a's
   rendered text position at the default (>1080px) nav spacing.
   Re-computed for the 901-1080px range where nav's spacing shrinks (see
   the matching override in the responsive section), and dropped once
   nav becomes a hamburger at 900px, since there's no button to align to. */
.page-hero__title,
.page-hero__text,
.about-sections,
.hero-scroller,
.hero-subtitle,
.hero-actions,
.brand-statement__text,
.brand-statement__tagline,
.uxploring__title,
.vuistregels__inner,
.succesverhalen__inner,
.case-hero__label,
.case-body__inner,
.contact-details,
.contact-maps-link,
.not-found__content,
.case-more__inner,
.cases__inner,
.partners-viewport,
.proclaimer__inner {
  margin-left: 242px;
}

/* .uxploring__label/__text/.btn deliberately aren't in the shared list
   above — they align with the left edge of the "X" in "UXploring" (the
   title's own second character), not with the nav column like everything
   else, matching the same "align with the X" convention used on the
   Beloftes page's own label/description/nav (see beloftes.css). "U" and
   "X" don't necessarily render at the same width, and .uxploring__title
   itself shifts with viewport-driven clamp() font sizes, so this can't
   be a fixed offset — --uxploring-content-margin is set by
   initUxploringContentAlign() in js/script.js, which measures the
   title's actual rendered "X" position directly. The 242px fallback
   below (matching the title's own "U"-aligned offset) only matters
   before that first run or if JS fails to load. */
.uxploring__label,
.uxploring__text,
.uxploring .btn {
  margin-left: var(--uxploring-content-margin, 242px);
}

/* .page-back is positioned absolutely, relative to .page-hero__heading
   — which, unlike .page-hero, already sits inset by the container's own
   padding (it's nested inside .container, not flush with the viewport
   edge) — so container-padding is already "spent" getting to this
   containing block and doesn't need adding again here. Flush with
   .logo's own right edge (the logo image's own 150px width, no other
   offset) on desktop, so the button's left edge lines up with where the
   wordmark ends. Reset to the containing block's own edge (0, since
   it's already at container-padding) at 900px and below (see responsive
   section) — there's less room to spare on narrow viewports. */
.page-back {
  left: 150px;
}

.page-back img {
  transform: scaleX(-1);
}

/* ==========================================================================
   About sections — heading + paragraphs + button, used on secondary pages
   ========================================================================== */

/* Wraps the two .about-section articles on the UXfactor page (the only
   place two of these ever sit side by side) — side-by-side columns on
   desktop, collapsing to the usual single stacked column at the
   sitewide 900px breakpoint (see the responsive section). margin-left
   (242/228/0, via the shared nav-alignment list above) lives here, on
   the grid itself, not on each column — putting it on both columns
   individually (as the old .about-section__inner did) pushed the right
   column an unwanted extra 242px into its own grid cell.
   Nested inside its own plain .container (rather than carrying the
   .container class itself, as before) so it stays a width:auto block —
   .container's width:100% combined with this same margin-left would
   otherwise overflow the viewport by exactly the margin's own width,
   since percentage widths resolve independently of margins. Being
   width:auto instead lets the browser solve margin-left + width against
   the containing block itself, the same way .page-hero__title/__text
   do, so it lines up with the hero above it at every viewport width
   without ever spilling past the edge. */
.about-sections {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  column-gap: 48px;
  max-width: 1024px;
}

/* Flattened: this used to be .about-section > .about-section__inner > an
   unnamed grouping div around the title+text, purely to give that pair
   its own flex-gap separate from the button below. Margin-bottom on
   .about-section__text (below) does that job instead, so the wrapper and
   the grouping div are both gone — title, text and button are direct
   children here. */
.about-section {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  max-width: 640px;
  padding-block: 48px;
}

.about-section__title {
  font-size: clamp(24px, 2.4vw, 32px);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--color-lavender);
  margin-bottom: 16px;
}

.about-section__text {
  font-size: 20px;
  font-weight: 300;
  line-height: 1.55;
  letter-spacing: -0.01em;
  color: var(--color-text-p);
}

.about-section__text p + p {
  margin-top: 16px;
}

/* Scoped to a direct child of .about-section (not a bare .about-section__text
   rule) since that class is also reused as plain paragraph typography on
   the case-study and proclaimer pages, inside their own gap-based flex
   containers — a margin there would double up with those containers'
   own gap. This replaces the 48px flex-gap the removed wrapper div used
   to provide between the content block and the button below it. */
.about-section > .about-section__text {
  margin-bottom: 48px;
}

/* ==========================================================================
   Case studies
   ========================================================================== */

.cases {
  padding-block: 80px 176px;
}

.cases__inner {
  display: flex;
  flex-direction: column;
  gap: 48px;
}

/* Prevents the button from stretching to the grid's own full width —
   .cases__inner has no align-items override, so it defaults to stretch,
   which .cases-grid actually needs (to lay out its two equal columns
   across the full available width). */
.cases__inner > .btn {
  align-self: flex-start;
}

.cases-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 40px 32px;
}

/* flex-wrap (rather than a fixed-width breakpoint) so the content
   column always wraps below the image once there's genuinely not enough
   row width left for it — at a 300px-capped, non-shrinking image and a
   two-column grid, a fixed breakpoint doesn't track the actual available
   width (which also depends on the shared left-alignment margin), and
   the old rule let content overflow into the next column instead of
   stacking. */
.case-card {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 32px;
}

/* flex-wrap decides line breaks from each item's *hypothetical* size
   (basis clamped by min/max-width, not its post-shrink size), so the
   image's basis below is deliberately smaller than the visual target
   (300px) — that's reached via flex-grow once a line actually has the
   room, while the smaller basis is what keeps this pair on one line at
   ordinary desktop widths. .case-card__content's min-width is set from
   the actual "Lees dit succesverhaal" button's own unwrapped width
   (matching .btn's sitewide white-space:nowrap) — smaller than that and
   the button overflows its column instead of the card ever wrapping. */
.case-card__image {
  flex: 1 1 180px;
  max-width: 300px;
  min-width: 140px;
  aspect-ratio: 1 / 1;
  border-radius: 16px;
  overflow: hidden;
}

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

.case-card__content {
  flex: 1 1 240px;
  min-width: 240px;
  display: flex;
  flex-direction: column;
  gap: 32px;
}

.case-card__label {
  display: flex;
  align-items: center;
  gap: 16px;
}

.case-card__label img {
  width: 24px;
  height: 24px;
}

.case-card__label span {
  font-size: 18px;
  font-weight: 600;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  color: var(--color-lavender);
}

.case-card h3 {
  font-size: clamp(24px, 2.4vw, 32px);
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: -0.01em;
  color: var(--color-white);
  max-width: 400px;
}

.case-card .btn {
  align-self: flex-start;
}

/* ==========================================================================
   UXploring
   ========================================================================== */

.uxploring {
  position: relative;
  padding-block: 120px 192px;
  overflow: hidden;
}

/* --uxploring-bg-top is set by initUxploringBgAlign() in js/script.js —
   .uxploring__title's own position shifts with viewport-driven clamp()
   font sizes and content reflow, while this element's -6%-of-section
   fallback doesn't track that at all, so the two drift out of vertical
   alignment at some widths. JS measures the title's actual rendered
   center and feeds it back here; the % value only matters before that
   first run (or if JS fails to load). */
.uxploring__bg {
  position: absolute;
  top: var(--uxploring-bg-top, -6%);
  left: 50%;
  transform: translateX(-50%);
  font-size: clamp(120px, 22vw, 400px);
  font-weight: 800;
  letter-spacing: -0.02em;
  color: transparent;
  -webkit-text-stroke: 1px rgba(188, 185, 255, 0.3);
  white-space: nowrap;
  pointer-events: none;
  z-index: 0;
}

.uxploring .container {
  position: relative;
  z-index: 1;
}

.uxploring__title {
  font-size: clamp(56px, 11vw, 180px);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--color-lavender);
  margin-bottom: clamp(32px, 6vw, 64px);
}

.uxploring__label {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-bottom: 24px;
}

.uxploring__label span {
  font-size: 18px;
  font-weight: 600;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  color: var(--color-lime);
}

.uxploring__text {
  font-size: clamp(18px, 1.8vw, 24px);
  font-weight: 700;
  line-height: 1.5;
  letter-spacing: -0.01em;
  color: var(--color-white);
  max-width: 748px;
  margin-bottom: 40px;
}

/* ==========================================================================
   Partners strip
   ========================================================================== */

.partners {
  padding-block: 64px 112px;
  opacity: 0.7;
}

/* Nested inside a plain .container (not carrying that class itself) so
   the shared margin-left below (242/228/0, same as .cases__inner) can
   inset this element's own left edge — where the scroll actually gets
   cut off — without fighting .container's own width:100%, the way
   adding margin-left directly to a .container element always does.
   The mask fades logos out before they hit that hard overflow:hidden
   edge, rather than having them clip abruptly mid-logo. */
.partners-viewport {
  overflow: hidden;
  mask-image: linear-gradient(to right, transparent, black 6%, black 94%, transparent);
  -webkit-mask-image: linear-gradient(to right, transparent, black 6%, black 94%, transparent);
}

/* Two back-to-back copies of the same logo group, animated by exactly
   -50% (one group's width) so the loop is seamless — by the time the
   first group has scrolled fully offscreen, the second is sitting
   exactly where the first started, and the translate snaps back to 0%
   with nothing visibly changing. width: max-content so the track is
   sized to its own (doubled) content instead of shrinking to fit the
   viewport, which is what actually gives it room to scroll. */
.partners-track {
  display: flex;
  width: max-content;
  animation: partners-scroll 35s linear infinite;
  will-change: transform;
}

.partners-track__group {
  display: flex;
  align-items: center;
  flex-shrink: 0;
  gap: 96px;
  padding-right: 96px;
}

.partners-track img {
  height: 40px;
  width: auto;
  flex-shrink: 0;
}

@keyframes partners-scroll {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-50%);
  }
}

/* ==========================================================================
   Footer
   ========================================================================== */

.site-footer {
  background: var(--color-bg-lavender-10);
  margin-top: 40px;
}

/* Grid (not .footer-brand simply stacked above .footer-columns) so
   .footer-download sits in its own column, top-aligned with .footer-main
   (and so with the logo, its first item) instead of starting lower down,
   after .footer-brand's own height has already been spent above it. */
.footer-top {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: start;
  gap: 48px;
  padding-block: 64px 48px;
}

.footer-brand {
  margin-bottom: 40px;
}

.footer-brand img {
  height: 24px;
  width: auto;
  margin-bottom: 16px;
}

.footer-tagline {
  font-size: 24px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--color-blue);
}

.footer-columns {
  display: grid;
  grid-template-columns: auto auto;
  justify-content: start;
  gap: 48px 96px;
  align-items: start;
}

.footer-address {
  font-style: normal;
  font-size: 20px;
  font-weight: 300;
  line-height: 1.55;
  color: var(--color-text-p);
  white-space: nowrap;
}

.footer-address__title {
  font-weight: 600;
  color: var(--color-white);
}

.footer-address a {
  text-decoration: underline;
  text-underline-offset: 2px;
}

.footer-nav {
  display: flex;
  flex-direction: column;
  gap: 12px;
  font-size: 18px;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.footer-nav a {
  color: var(--color-lime);
  text-decoration: underline;
  text-underline-offset: 3px;
  width: fit-content;
}

.footer-download {
  display: flex;
  flex-direction: column;
  align-items: start;
  gap: 24px;
  padding: 24px;
  max-width: 311px;
  border: 2px solid var(--color-lavender-50);
  border-radius: 16px;
}

.footer-download__title {
  font-size: 18px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--color-text-p);
  margin-bottom: 8px;
}

.footer-download__text {
  font-size: 16px;
  font-weight: 300;
  letter-spacing: -0.01em;
  color: var(--color-lavender);
}

.footer-bottom {
  background: var(--color-bg);
}

.footer-bottom__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 8px 24px;
  padding-block: 16px;
  font-size: 16px;
}

.footer-bottom__company {
  font-weight: 600;
}

.footer-bottom__year {
  font-weight: 300;
}

.footer-bottom__link {
  text-decoration: underline;
  text-underline-offset: 3px;
  font-weight: 300;
}

/* ==========================================================================
   Responsive
   ========================================================================== */

@media (max-width: 1024px) {
  /* .footer-download drops below .footer-main (rather than sitting
     beside it) once there's not enough width left for it to stay a
     comfortably-sized column of its own. */
  .footer-top {
    grid-template-columns: 1fr;
  }

  .footer-download {
    max-width: none;
  }
}

@media (max-width: 1080px) {
  .nav {
    gap: 0;
    margin-inline-start: 24px;
  }

  .nav a {
    padding: 12px 14px;
    font-size: 16px;
  }

  /* 150 (logo) + 40 (gap) + 24 (nav's own margin-inline-start here)
     + 14 (nav link's own left padding at this breakpoint) = 228 */
  .page-hero__title,
  .page-hero__text,
  .about-sections,
  .hero-scroller,
  .hero-subtitle,
  .hero-actions,
  .brand-statement__text,
  .brand-statement__tagline,
  .uxploring__title,
  .vuistregels__inner,
  .succesverhalen__inner,
  .case-hero__label,
  .case-body__inner,
  .contact-details,
  .contact-maps-link,
  .not-found__content,
  .case-more__inner,
  .cases__inner,
  .partners-viewport,
  .proclaimer__inner {
    margin-left: 228px;
  }
}

@media (max-width: 900px) {
  .nav {
    display: none;
  }

  .nav-fab {
    display: flex;
  }

  /* Nav is a hamburger here, so there's no "first button" to align
     with — drop the offset back to the container's own edge. */
  .page-hero__title,
  .page-hero__text,
  .about-sections,
  .hero-scroller,
  .hero-subtitle,
  .hero-actions,
  .brand-statement__text,
  .brand-statement__tagline,
  .uxploring__title,
  .vuistregels__inner,
  .succesverhalen__inner,
  .case-hero__label,
  .case-body__inner,
  .contact-details,
  .contact-maps-link,
  .not-found__content,
  .case-more__inner,
  .cases__inner,
  .partners-viewport,
  .proclaimer__inner {
    margin-left: 0;
  }

  /* No room to sit beside the title at this width (nav is a hamburger,
     content is edge-to-edge) — float it in the bottom-left corner instead,
     mirroring .nav-fab's own bottom-right placement and glass treatment,
     so it stays reachable while scrolling through a long page (case
     detail, Beloftes, Waarden) instead of only existing back at the very
     top. Every page using .page-back is one of those three, so this
     replaces the old "stack 72px above the title" rule outright rather
     than living alongside it. */
  .page-back {
    position: fixed;
    top: auto;
    left: 16px;
    bottom: 16px;
    z-index: 200;
    background: rgba(0, 0, 54, 0.5);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
  }

  .cases-grid {
    grid-template-columns: 1fr;
    gap: 56px;
  }

  .about-sections {
    grid-template-columns: 1fr;
  }

  /* .cases-grid is a single column here, so .case-card would otherwise
     have plenty of room to stay row-flex (flex-wrap only steps in once
     content genuinely can't fit) — stacked is still the better look at
     this width regardless, so it's forced explicitly rather than left to
     flex-wrap's own judgment. */
  .case-card {
    flex-direction: column;
    align-items: flex-start;
  }

  /* flex-basis/grow/shrink on these two are meant for the row layout —
     once .case-card flips to flex-direction:column, "basis" and friends
     resolve against height (the new main axis) instead of width, which
     otherwise left both items sized far too short here. Explicit
     width:100% sidesteps that axis-flip entirely. */
  .case-card__image,
  .case-card__content {
    flex: 0 0 auto;
    width: 100%;
  }

  /* The base rule's 300px cap is a row-layout concern (keeping the
     square thumbnail from dominating next to the text) — irrelevant now
     that it's stacked above the text with the full card width to itself. */
  .case-card__image {
    max-width: 100%;
  }

}

@media (max-width: 640px) {
  :root {
    --container-padding: 20px;
    --header-height: 72px;
  }

  /* +var(--header-height) (already redefined to 72px above, in this same
     block) — same reasoning as .page-hero's own padding: keeps uxfactor's
     title (which falls back to this rule at this width, since .hero's own
     mobile override wins the cascade over .hero--compact here) clear of
     the now-overlapping fixed header. Harmless for index.html's
     .hero--home too, which only uses this as a flex-centered minimum
     margin, not a positioning offset. */
  .hero {
    padding-block: calc(var(--header-height) + 48px) 72px;
  }

  .hero-actions {
    flex-direction: column;
    align-items: stretch;
  }

  .btn {
    width: 100%;
  }

  /* "Ontdek alle beloftes" — the only standalone (non-hero-actions,
     non-card) CTA on the homepage — stays sized to its own content
     instead of stretching full-width like the stacked hero/card
     buttons above. */
  .uxploring .btn {
    width: auto;
  }

  .footer-columns {
    grid-template-columns: 1fr;
  }

  .footer-bottom__inner {
    flex-direction: column;
    align-items: flex-start;
  }
}
