/* MRLN MEDIA — supplementary stylesheet (Client-First build)
   ONLY what the Webflow style API genuinely cannot express. Everything else —
   including hover/focus states, pseudo-element content, the grain texture,
   placeholder colour, step dividers and the flood alternation — now lives as
   native Webflow styles and is visible in the Designer.

   What remains here, and why:
     1. tag-level resets  — tag styles cannot be created via the API
     2. @keyframes        — no API representation
     3. runtime state classes the motion layer adds (.anim-ready, .is-open,
        .is-hot, .is-stuck, .is-on, .is-busy, .split_*, .lenis.*)
     4. descendant + combined pseudo selectors (:hover::after)
     5. attribute selectors ([disabled], [aria-current], [data-anim])
     6. browser surfaces (::selection, scrollbar, per-ground focus rings)
     7. prefers-reduced-motion, vendor-prefixed properties
     8. exact small-screen breakpoints (700px / 600px do not map to Webflow's) */

/* ---- 1. tag resets (Webflow's own defaults would otherwise leak) ---- */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{margin:0;background:var(--color--paper,#FBF5EE);color:var(--color--ink,#150A11);
  /* clip, NOT hidden. overflow-x:hidden computes overflow-y to auto, which
     makes this element a scroll container - and then position:sticky on the
     case cards resolves against a box that never scrolls, so nothing pins.
     overflow-x:clip does the same horizontal job and leaves overflow-y
     visible. Same change is on .page-wrapper. */
  overflow-x:clip;overflow-y:visible;-webkit-font-smoothing:antialiased;
  font-feature-settings:"kern" 1,"liga" 1,"cv11" 1}
img,svg,video{display:block;max-width:100%}
h1,h2,h3,h4,p,figure,blockquote{margin:0}
ul,ol{padding:0;margin:0;list-style:none}
a{color:inherit;text-decoration:none;text-underline-offset:.18em;text-decoration-thickness:.08em}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
input,textarea{font:inherit;color:inherit}

/* ---- 6. browser surfaces ---- */
::selection{background:#FF66C4;color:#150A11}
:root{scrollbar-color:#FF66C4 #F3E9DD;scrollbar-width:thin}
::-webkit-scrollbar{width:11px;height:11px}
::-webkit-scrollbar-track{background:#F3E9DD}
::-webkit-scrollbar-thumb{background:#FF66C4;border:3px solid #F3E9DD;border-radius:99px}
::-webkit-scrollbar-thumb:hover{background:#D91E8F}
:focus{outline:none}
:focus-visible{outline:3px solid #D91E8F;outline-offset:3px;border-radius:2px}
.is-ink :focus-visible,.is-pink :focus-visible{outline-color:#FFE600}
.is-yellow :focus-visible{outline-color:#150A11}
[type=text],[type=email],textarea{caret-color:#D91E8F}

/* ---- 4. hover on a parent: the button wipe (.button_wipe) and link line
   (.text-link_line) are real elements styled in the Designer; only the
   trigger lives here, because a parent's hover cannot be set natively. ---- */
.button:hover .button_wipe,.button:focus-visible .button_wipe{transform:translateY(0)}
.button:hover .button_icon{transform:translateX(4px)}
.text-link:hover .text-link_line,.text-link:focus-visible .text-link_line{transform:scaleX(1)}
.wordmark.is-cta:hover .wordmark_icon{transform:translateX(4px)}

/* ---- 5. attribute selectors ---- */
.button[disabled]{opacity:.45;cursor:not-allowed;transform:none;box-shadow:none}
.button[disabled] .button_wipe{display:none}
.nav_link[aria-current=true]{color:#FF66C4}

/* ---- 2 + 3. keyframes and the runtime state classes that use them ---- */
.button.is-busy{cursor:progress}
.button.is-busy .button_icon{animation:spin 1s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}

.cursor__d,.cursor__r{position:absolute;left:0;top:0;border-radius:99px;transform:translate(-50%,-50%)}
/* The dot used to grow by transitioning width and height, which animates
   LAYOUT for 450ms every time the pointer crosses a link. scale() is the same
   picture on the compositor: 15/10 = 1.5, 7/10 = 0.7. */
.cursor__d{width:10px;height:10px;background:#F53FAE;
  transition:transform .45s cubic-bezier(.16,1,.3,1)}
.cursor__r{width:10px;height:10px;border:1.5px solid #F53FAE;opacity:0}
/* No will-change here on purpose. It was tried and measured: with the pointer
   swept 180 times, style/prepaint/layerize/raster counts were IDENTICAL with
   and without it (397/397/217/5), so a permanent GPU layer would buy nothing.
   Chrome already handles this transform write cheaply. */
.cursor{transition:opacity .3s linear}
.cursor.is-on{opacity:1}
.cursor.is-hot .cursor__d{transform:translate(-50%,-50%) scale(1.5)}
.cursor.is-hot .cursor__r{animation:cur-ripple 1.25s cubic-bezier(.16,1,.3,1) infinite}
.cursor.is-down .cursor__d{transform:translate(-50%,-50%) scale(.7)}
@keyframes cur-ripple{
  0%{transform:translate(-50%,-50%) scale(1);opacity:.85}
  100%{transform:translate(-50%,-50%) scale(4.4);opacity:0}
}
@media (hover:none),(pointer:coarse){.cursor{display:none}}

/* ---- 3 + 4. case stack: staggered pin offsets ------------------------------
   React Bits ScrollStack pins each card `itemStackDistance` (30px) lower than
   the one before it, so the stack reads as a deck with visible edges rather
   than each card hiding the last completely. `.case-card` supplies the base
   `top`; these add the per-index offset. nth-child, so it cannot be expressed
   as a Webflow class. */
.case-stack .case-card:nth-child(2){top:calc(clamp(4.5rem,10vh,7rem) + 30px)}
.case-stack .case-card:nth-child(3){top:calc(clamp(4.5rem,10vh,7rem) + 60px)}
.case-stack .case-card:nth-child(4){top:calc(clamp(4.5rem,10vh,7rem) + 90px)}
/* Below 920px the card is a single column (text above image) and is taller
   than a pinned card's room, so the image fell under the next card and could
   never be reached. Measured up to 211px lost at 360x600, and the per-card
   offsets above cost the third card another 60px. Let them flow instead; the
   stacking deck is a desktop-width effect. The offsets are inert once static. */
@media (max-width:919px){
  .case-card{position:static}
}

/* ---- 8. nav panel: short-viewport guard ------------------------------------
   The panel centres its content, and its vertical padding is symmetric so the
   visible gap above the links equals the gap below. It used to carry
   `padding-top: calc(4.6rem + ...)` as clearance for the nav bar, which sits
   OVER the panel at z-index 1004 - but combined with justify-content:center
   that made the top gap 65px larger than the bottom one (measured 283 vs 218
   at 900px tall).

   The clearance only actually matters when the viewport is short enough that
   centred content would rise under the bar. Measured: with symmetric padding
   the first link sits at 111px at 600px tall and 91px at 560px, against a bar
   bottom edge of 86px - so symmetry is safe to about 550px. The guard starts
   at 580px, which keeps ~15px of clearance at the boundary.
   Webflow has no max-height breakpoints, so this lives here.
   v27: on landscape phones the menu is taller than the screen, and centred
   overflow pushed the first link up under the bar (and under the logo once the
   drawer went full width on Mobile L), so short screens start at the top and
   the panel scrolls. */
@media (max-height: 580px){
  .nav_panel{padding-top:calc(4.6rem + 1.5rem);justify-content:flex-start}
}



.plate{transform:translate(-50%,-50%) scale(.85) rotate(-6deg);
  transition:opacity .4s cubic-bezier(.16,1,.3,1),transform .55s cubic-bezier(.16,1,.3,1);
  box-shadow:0 30px 60px -22px rgba(21,10,17,.55)}
.plate.is-on{opacity:1;transform:translate(-50%,-50%) scale(1) rotate(-4deg)}
@media (max-width:1023px),(hover:none){.plate{display:none}}

/* ---- 3. nav open / stuck states (descendant selectors) ---- */
.nav.is-stuck .nav_scrim{background:rgba(251,245,238,.82);backdrop-filter:blur(14px) saturate(140%);
  box-shadow:0 1px 0 rgba(21,10,17,.14)}
.nav.is-open .nav_scrim{background:rgba(251,245,238,0);backdrop-filter:blur(0px);box-shadow:none}
.nav.is-open .nav_toggle{color:#FBF5EE}
.nav.is-open .nav_panel,.nav.is-open .nav_prelayer{pointer-events:auto}
.nav:not(.is-open) .nav_panel,.nav:not(.is-open) .nav_prelayer-yellow{box-shadow:none}

/* ---- 3. services accordion states (descendant selectors) ---- */
.services_row.is-hot .services_flood{transform:scaleY(1);opacity:1;
  transition:transform .6s cubic-bezier(.16,1,.3,1),opacity .12s linear}
/* The flood is position:absolute;inset:0 on the ROW, so it also sits behind
   the accordion body. Only .services_head used to invert, which left the
   body's inherited paper text sitting on a spot colour: measured 1.07:1 on
   the yellow rows (2 and 4) and 2.44:1 on the pink ones (1 and 3). Invert the
   whole row instead, so the body follows the head. Ink gives 15.3:1 on yellow
   and 7.34:1 on pink. */
.services_row.is-hot{color:#150A11}
.services_row.is-hot .services_head{color:#150A11;padding-inline:clamp(.5rem,1.6vw,1.5rem)}
.services_row.is-hot .services_number{color:#57082F}
.services_row:nth-child(2n).is-hot .services_number{color:#5C4200}
.services_row.is-open .services_plus{transform:rotate(135deg)}

/* ---- 3. split-text primitives (generated by the motion layer) ---- */
.split_word{display:inline-block}
.split_char{display:inline-block}
.split_line{display:block;overflow:hidden}
.split_line-inner{display:block}
/* NB: no will-change here on purpose. These reveal once and never animate
   again; 55 permanent GPU layers is a bad trade for a one-shot tween, and
   GSAP promotes during the tween anyway. */
.reveal-words span{transition:color .3s linear}

/* ---- 3 + 5. animation defaults (JS adds .anim-ready to arm) ---- */
.anim-ready [data-anim=up]{opacity:0;transform:translateY(28px)}
.anim-ready [data-anim=fade]{opacity:0}
.anim-ready [data-anim=blur]{opacity:0;filter:blur(14px);transform:translateY(14px)}
.anim-ready [data-anim=clip]{clip-path:inset(0 0 100% 0)}
.anim-ready .split_char{opacity:0}
.anim-ready .reveal-words span{color:rgba(21,10,17,.24)}

/* ---- 3 + 5. pre-arm, stamped by an inline <head> script ----------------
   The rules above are armed by .anim-ready, which the motion layer adds only
   after GSAP, ScrollTrigger, Lenis and mrln-motion.js have all loaded. That
   keeps the page readable without JavaScript, but it also means the hero is
   painted in its final state first and then vanishes to animate in — measured
   at ~120ms visible on a throttled connection, longer on a slow one.

   .anim-pre carries the same hidden state from the very first paint. It is
   added by a two-line inline script in the <head>, which runs before the body
   is painted, and it removes itself after 3s so a failed CDN fetch degrades to
   visible content rather than a permanently blank hero. Reduced-motion
   visitors never get the class at all.

   [data-split] needs the container hidden, not .split_char: the characters do
   not exist until JS splits the text, so pre-JS there is nothing to hide. */
.anim-pre [data-anim=up]{opacity:0;transform:translateY(28px)}
.anim-pre [data-anim=fade]{opacity:0}
.anim-pre [data-anim=blur]{opacity:0;filter:blur(14px);transform:translateY(14px)}
.anim-pre [data-anim=clip]{clip-path:inset(0 0 100% 0)}
.anim-pre [data-split]{opacity:0}
/* .anim-ready lands before the split runs, so the container comes back while
   the individual characters are still hidden by the rule above. */
.anim-ready [data-split]{opacity:1}


/* ---- 8. exact small-screen breakpoints ----
   Moved to the Designer's Mobile L breakpoint (767px) on 2026-10-14:
   .section-head_row stacking, the max-width-20ch/22ch reset, .tilt_badge and
   .case-card_stats. Nothing left here. */

/* ---- 7. reduced motion ---- */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;
    transition-duration:.001ms!important;scroll-behavior:auto!important}
  .anim-ready [data-anim]{opacity:1!important;transform:none!important;filter:none!important;clip-path:none!important}
  .anim-ready .split_char{opacity:1!important;transform:none!important}
  .anim-ready .reveal-words span{color:inherit!important}
  .blob{display:none}
  .grain{animation:none!important}
}

/* ---- lenis ---- */
html.lenis,html.lenis body{height:auto}
.lenis.lenis-smooth{scroll-behavior:auto!important}
.lenis.lenis-smooth [data-lenis-prevent]{overscroll-behavior:contain}
.lenis.lenis-stopped{overflow:hidden}
.lenis.lenis-smooth iframe{pointer-events:none}
:root{--mrln-yellow:var(--color--yellow--300)}
@keyframes mrln-rise{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}
@keyframes mrln-pop{0%{opacity:0;transform:rotate(-24deg) scale(.4)}70%{opacity:1;transform:rotate(-4deg) scale(1.08)}100%{opacity:1;transform:rotate(-8deg) scale(1)}}
.form_message.is-success{animation:mrln-rise .5s cubic-bezier(.2,.8,.2,1) both}
.form_message.is-success .form_success-sticker{animation:mrln-pop .65s cubic-bezier(.2,.9,.3,1.3) .2s both}
@media (prefers-reduced-motion:reduce){.form_message.is-success,.form_message.is-success .form_success-sticker{animation:none}}

/* Zo gaat het: the tabbed stack needs about 560px of height; on shorter screens (phones in landscape) the cards flow, and the GSAP scale is cancelled. */
@media (max-height:560px){.step-card{position:static;transform:none!important}}
/* Closed menu: hide the panel once the 0.32s close sweep ends, so its links leave the tab order; visible again the instant .is-open is added. */
.nav_panel{visibility:hidden;transition:visibility 0s linear .35s}
.nav.is-open .nav_panel{visibility:visible;transition-delay:0s}
/* ---- Moved out of the Designer, 2026-10-14 ---------------------------------
   These had no control in the Style panel, so they sat under "Custom
   properties", where they read as fragile to whoever edits the site. They are
   typographic and behavioural details nobody needs to tweak, so they live here.
   Everything else that used to be custom (clamp sizes, inset, isolation,
   will-change, overflow-x/y, min() widths) is now a plain native value. */
.page-wrapper{overflow-x:clip}
.nav_panel{overscroll-behavior:contain}
.heading-display{font-variation-settings:"wdth" 100,"opsz" 96}
.heading-display.is-hero,.heading-display.is-h2,.step-card_title,.case-col_title,.form_success-title{text-wrap:balance}
.text-tabular,.case-col_stats{font-variant-numeric:tabular-nums lining-nums}
.case-col_glyph,.marquee{-webkit-user-select:none;user-select:none}
textarea{resize:vertical}
/* Werk: tags, title, serif line and stats line up across the three cards. The
   Designer has no subgrid option; without this rule each card keeps its own
   four auto rows, which is how the canvas shows it. The row span lives here
   too: set in the Designer, it made the canvas draw five narrow columns. */
@media (min-width:992px){.case-col{grid-row:span 4;grid-template-rows:subgrid}}
