/*! ------------------------------------------------
 * Fader — Project Overrides
 * ------------------------------------------------
 * Loaded after main.css. Everything here is a deliberate
 * deviation from the base template.
 *
 *  01. Brand logo
 *  02. Hero controls
 *  03. Brand wordmark scaling
 *  04. Light section (former accent section)
 *  05. Dark menu overlay
 *  06. Menu media panel
 *  07. Static hero cover
 *  08. Flat main menu
 *  09. Pricing cards
 *  10. Hero call to action
 *  11. Inner page header clearance
 *  12. Stable viewport height on mobile
 *  13. Blur band clears the footer at rest
 * ------------------------------------------------ */

/* ------------------------------------------------*/
/* 01. Brand logo                                   */
/* ------------------------------------------------*/
/* The logo is a supplied wordmark, not the template's inline icon.
   The asset is fader-logo-black.svg — an SVG wrapper around a 2000x1000
   PNG, so it is not truly resolution-independent, but it carries 5x the
   pixels of the .webp it replaced and stops the mark looking soft at
   these sizes. Inside it the wordmark occupies a centred 1468x356 area
   (~4.12:1). The box below matches that ratio and uses object-fit: cover
   so the surrounding transparent padding is trimmed.

   The artwork is black on transparency, so it is inverted rather than
   recoloured, exactly as the menu panel does it (section 06) — the ink
   is rgb(10,2,3), which inverts to a white that reads as pure white over
   the hero video. invert() leaves the alpha channel alone, so the
   transparent margin stays transparent rather than turning into a box.

   The mark carries the top-left corner over the full-bleed hero, so it
   is sized well past the template's inline icon. Every box keeps the
   4.08:1 ratio — deviating from it re-crops the wordmark rather than
   scaling it.

   The base width is capped by the layout, not by taste: body is
   `min-width: 360px` with `overflow-x: unset !important`, and the
   header starts at left: 3rem. Anything past ~33rem here puts the mark
   through the right edge and gives the whole page a horizontal
   scrollbar on phones. */
.mxd-logo__image,
.menu-logo__image {
  width: 31rem;
  height: 7.6rem;
  -o-object-fit: cover;
     object-fit: cover;
}

.mxd-logo__image {
  -webkit-filter: invert(1);
          filter: invert(1);
}

@media only screen and (min-width: 768px) {
  .mxd-logo__image,
  .menu-logo__image {
    width: 38.8rem;
    height: 9.5rem;
  }
}

@media only screen and (min-width: 1600px) {
  .mxd-logo__image,
  .menu-logo__image {
    width: 46.9rem;
    height: 11.5rem;
  }
}

/* ------------------------------------------------*/
/* 02. Hero controls                                */
/* ------------------------------------------------*/
/* The left-hand control was removed — keep the remaining
   control pinned to the right instead of centering it. */
@media only screen and (min-width: 768px) {
  .mxd-hero-04__controls {
    -webkit-box-pack: end;
    -moz-box-pack: end;
    -ms-flex-pack: end;
    justify-content: flex-end;
  }
}

/* Below 768px the row loses that right-hand pin and goes back to a
   full-width column on the vertical centre line — which is exactly where
   the booking CTA sits (section 10), so the two print on top of each
   other. Drop the cue to the foot of the cover instead: it clears the
   CTA and is where a scroll hint belongs on a phone anyway.

   top/transform both have to be unwound — the base rule centres with
   top: 50% *and* translateY(-50%), so resetting one still leaves the
   other applied. */
@media only screen and (max-width: 767px) {
  .mxd-hero-04__controls {
    top: auto;
    bottom: 4rem;
    -webkit-transform: none;
       -moz-transform: none;
        -ms-transform: none;
            transform: none;
  }
}

/* ------------------------------------------------*/
/* 03. Brand wordmark scaling                       */
/* ------------------------------------------------*/
/* The base 28cqw is tuned for a 6-character mark in a monospace
   face (6 chars x 0.6em advance x 28cqw = ~100cqw).
   The 5-character mark needs 28 x 6/5 = 33.6cqw to span the
   same full-bleed width. */
.fullwidth-text__content,
.fw-mark__content {
  font-size: 33.6cqw;
}

@media only screen and (min-width: 992px) {
  .fullwidth-text__content,
  .fw-mark__content {
    font-size: 33.6cqw;
  }
}

/* ------------------------------------------------*/
/* 04. Light section (former accent section)        */
/* ------------------------------------------------*/
/* The accent (blue) section is now white with black type. */
.bg-color-accent {
  background-color: var(--nl-white);
}

.bg-color-accent h2.accent,
.bg-color-accent h3.accent,
.bg-color-accent h4.accent,
.bg-color-accent .manifest.manifest-accent,
.bg-color-accent .mxd-process-points__icon.accent {
  color: var(--nl-black);
}

.bg-color-accent .manifest.manifest-accent span {
  color: rgba(var(--nl-black-rgb), 0.45);
}

.bg-color-accent p.t-accent {
  color: rgba(var(--nl-black-rgb), 0.65);
}

.bg-color-accent .tag-permanent {
  color: var(--nl-black);
}

.bg-color-accent .mxd-process-points__divider.accent {
  background-color: rgba(var(--nl-black-rgb), 0.15);
}

/* ------------------------------------------------*/
/* 05. Dark menu overlay                            */
/* ------------------------------------------------*/
/* The template paints the fullscreen menu in the "opposite"
   palette, which under the site's permanent dark scheme means a
   light beige panel with dark type. The menu should instead match
   the page's own black surface (--base) with light type.

   Every colour inside .mxd-menu resolves through the --*-opp-*
   tokens — background, shadow gradients, links, dividers — so
   re-pointing those tokens at the dark set on the menu root
   recolours the whole overlay in one place. */
.mxd-menu {
  --base-opp: var(--base--dark);
  --base-opp-rgb: var(--base-rgb--dark);
  --t-opp-bright: var(--t-bright--dark);
  --t-opp-medium: var(--t-medium--dark);
  --t-opp-muted: var(--t-muted--dark);
  --t-opp-muted-extra: var(--t-muted-extra--dark);
}

/* The bottom data bar lost its left-hand column (the template
   credit), so the remaining copyright column spans the full width
   instead of sitting in the right third. */
@media only screen and (min-width: 1200px) {
  .mxd-menu__data .menu-data__right {
    width: 100%;
    padding-left: 0;
  }
}

/* ------------------------------------------------*/
/* 06. Menu media panel                             */
/* ------------------------------------------------*/
/* A single looping video, replacing the earlier rAF-swapped image
   stack — so mxdMenuMediaCycle now finds no .menu-media__cycle and
   returns null. Every call site guards with `?.`, so it idles.

   Selectors are scoped through .menu-media__wrapper to outrank
   main.css's `.menu-media__wrapper video` sizing rule. */
.menu-media__wrapper .menu-media__video {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  -o-object-fit: cover;
     object-fit: cover;
  -o-object-position: center center;
     object-position: center center;
  pointer-events: none;
}

/* Scrim, same idea as .mxd-hero-04__cover — the video runs bright
   enough in places to cost the white brand mark its contrast. Sits
   under .menu-media__logo, which holds z-index 2. */
.menu-media__wrapper::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(var(--nl-black-rgb), 0.35);
  z-index: 1;
}

/* Brand mark centred over the video. The supplied asset is black
   artwork on transparency, so it is inverted rather than recoloured
   — the mark is rgb(10,2,3), which inverts to a white that reads as
   pure white over video.

   80%, not 72%: the SVG's viewBox used to be cropped 212 units short of
   its artwork, which both shifted the mark right of centre and left the
   ink filling 82% of the element rather than 73%. Restoring the viewBox
   fixed the centring and shrank the mark, so the width grew to match
   what it rendered at before. */
.menu-media__wrapper .menu-media__logo {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 80%;
  height: auto;
  -o-object-fit: contain;
     object-fit: contain;
  -webkit-transform: translate(-50%, -50%);
     -moz-transform: translate(-50%, -50%);
      -ms-transform: translate(-50%, -50%);
          transform: translate(-50%, -50%);
  -webkit-filter: invert(1);
          filter: invert(1);
  pointer-events: none;
  z-index: 2;
}

/* Below 1200px main.css sets .mxd-menu__media to display:none — the
   panel is a column of a two-column layout that stops existing. Rather
   than lose the video there, it is promoted out of the flex flow and
   stretched behind the whole overlay, with the links riding on top.

   Taking it out of flow matters: .mxd-menu__content is a flex row of
   media (flex:2) and navigation (flex:3), so absolute positioning is
   what lets the navigation claim the full width instead of three
   fifths of it. The offsets resolve against .mxd-menu__content, which
   is both position:fixed and transformed.

   Ordered after the block rules above so these plainly win — the
   selectors are matched in specificity, so source order decides. */
@media only screen and (max-width: 1199px) {
  .mxd-menu__media {
    display: block;
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 0;
  }

  /* Half-strength black over the video. Carrying nav links, contact
     details and the legal line now, not just the brand mark, so it
     needs more than the desktop panel's 0.35. */
  .menu-media__wrapper::after {
    background-color: rgba(var(--nl-black-rgb), 0.5);
  }

  /* The centred mark would land directly behind the link column. */
  .menu-media__wrapper .menu-media__logo {
    display: none;
  }

  /* Above the backdrop. Already position:relative in main.css, so this
     only needs the stacking order. */
  .mxd-menu__navigation {
    z-index: 1;
  }
}

/* ------------------------------------------------*/
/* 07. Static hero cover                            */
/* ------------------------------------------------*/
/* The hero is a single full-bleed cover, not the template's scrubbed
   horizontal slider. The section keeps its original
   .mxd-hero-04__wrap / __slider box (100vh), so only the media itself
   needs unwinding: the template pre-scales it to 1.25 as the resting
   state for the parallax tween, which without that tween just crops
   25% off every edge for no reason. */
.mxd-hero-04__media.static img {
  will-change: auto;
  -webkit-transform: none;
     -moz-transform: none;
      -ms-transform: none;
          transform: none;
}

/* The cover is a looping video. The template only ever sizes an <img>
   inside __media, so the video repeats that geometry: fill the 100vh box
   and cover it, cropping overflow rather than letterboxing.

   object-fit needs an explicit width/height to work against — a bare
   <video> falls back to its intrinsic 300x150 and floats in the corner. */
.mxd-hero-04__media.static video {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  -o-object-fit: cover;
     object-fit: cover;
  /* Centre the crop, so a portrait viewport keeps the middle of the frame
     rather than the left edge. */
  -o-object-position: center center;
     object-position: center center;
  pointer-events: none;
}

/* Contrast layer over the photo. The template's __cover is a flat
   soft-light wash, which lifts the midtones but leaves the logo and
   the scroll control sitting on whatever happens to be behind them.

   Two stops, both painted in normal blend mode above the cover:
   the radial pulls the frame edges and corners down (the control sits
   hard right, centred), the linear adds a top band for the logo and
   the hamburger. The centre is left untouched so the photo still
   reads as the subject. */
.mxd-hero-04__vignette {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  background:
    radial-gradient(ellipse 92% 78% at 50% 50%,
      rgba(var(--nl-black-rgb), 0) 42%,
      rgba(var(--nl-black-rgb), 0.72) 100%),
    linear-gradient(to bottom,
      rgba(var(--nl-black-rgb), 0.5) 0%,
      rgba(var(--nl-black-rgb), 0) 24%);
}

/* ------------------------------------------------*/
/* 08. Flat main menu                               */
/* ------------------------------------------------*/
/* The menu items are plain links now, not accordion toggles, so the
   hover treatment has to move off .main-menu__toggle — which no longer
   exists — and onto the link itself. */
.no-touch a.main-menu__link:hover .main-menu__caption {
  color: var(--t-opp-medium);
}

/* ------------------------------------------------*/
/* 09. Pricing cards                                */
/* ------------------------------------------------*/
/* The hover blob is an inline SVG filled with var(--highlight), which
   under the dark scheme is a saturated blue (#002bba). Re-pointing the
   token on the table recolours every card's blob in one place — the
   same trick section 05 uses on the menu. */
.mxd-pricing-table {
  --highlight: var(--nl-white);
}

/* At full strength the blob washes out the price and the body copy
   sitting on top of it. It only needs to read as a soft lift on hover,
   so it comes in at a fraction of the template's opacity. */
.no-touch .mxd-pricing-table__inner:hover .mxd-pricing-table__bg {
  opacity: 0.1;
}

/* Every price is a two-value range ("8500–12500") — roughly three times
   the width the template's 7.4rem figure was drawn for, which overflows
   the card at the 3-column breakpoint. */
.pricing-data__amount {
  font-size: 4rem;
  letter-spacing: -0.2rem;
}

/* The template zeroes the item margin at this breakpoint because its
   pricing block was a single row of three. Ten cards wrap to four rows,
   which then butt straight into each other.

   This is row-gap on the flex row rather than a restored margin-bottom:
   the template's `:last-of-type` rule only clears the final *card*, so
   reinstating the margin would leave dead space under the last row. */
@media only screen and (min-width: 1200px) {
  .mxd-pricing-table .row {
    row-gap: 4rem;
  }
}

/* ------------------------------------------------*/
/* 10. Hero call to action                          */
/* ------------------------------------------------*/
/* The booking CTA sits over the centre line of the 100vh cover, on its
   own layer rather than inside .mxd-hero-04__controls — that row is a
   space-between flex line pinned right (section 02), so a second child
   would push the scroll cue around instead of centring. z-index 3 puts
   it on the same layer as the controls and the arrow, above __slider.

   Positioned by stretching the layer over the whole cover and using
   flex, not by a 50% offset plus translate(-50%, -50%): the button
   carries .loading-item, and app.js tweens that with GSAP, which writes
   its own transform on the element and would overwrite the centring
   half of it. GSAP only ever touches the <a>, so the translate below
   sits safely on the layer.

   translateY(16%) is measured, not eyeballed. The cover video is a
   3-second loop of a bright field crossed by one dark wave; sampling
   its luma across the loop puts that wave between 62% and 70% of the
   hero height at every frame, darkest at 66%. 50% + 16% lands the
   label in it.

   The layer spans the full cover, so it has to stay transparent to the
   pointer or it would swallow every click on the hero — the button
   itself takes them back below. */
.mxd-hero-04__cta {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: -webkit-box;
  display: -moz-box;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-align: center;
     -moz-box-align: center;
      -ms-flex-align: center;
          align-items: center;
  -webkit-box-pack: center;
     -moz-box-pack: center;
      -ms-flex-pack: center;
  justify-content: center;
  -webkit-transform: translateY(16%);
     -moz-transform: translateY(16%);
      -ms-transform: translateY(16%);
          transform: translateY(16%);
  z-index: 3;
  pointer-events: none;
  /* Renders the label as the photographic negative of whatever frame of
     the video is behind it, so it stays legible on a moving background
     with no border or fill to carry it. Same technique the template uses
     for .mxd-hero-04__arrow and the landing hero headline. */
  mix-blend-mode: difference;
}

/* A 1px square rule around bare type — no fill, no radius. .btn brings
   no border, radius or background of its own, so every one of these is
   an addition rather than an override; border-radius is stated anyway
   because a square corner is the point, not an accident of the base.

   Padding is in em so the frame keeps its proportions across the three
   font sizes below instead of needing three more declarations.

   The sizes match the cap height of the FADER wordmark in the header.
   That mark is a bitmap: 400x200 natural, with the FADER line occupying
   48 natural pixels, drawn through object-fit: cover into the boxes in
   section 01. Cover scale is width-driven at every breakpoint
   (310/400, 388/400, 469/400), so the rendered cap height is 37.2px,
   46.6px and 56.3px. JetBrains Mono has a cap height of 0.73em, so the
   font size to match is that number over 0.73.

   Change a logo box in section 01 and these have to be recomputed. */
.btn-negative {
  pointer-events: auto;
  /* Wide enough to frame the label, tight enough that the widest case
     — 8 characters at the base size — still clears a 360px viewport,
     which is where body bottoms out. */
  padding: 0.3em 0.5em;
  border: 1px solid var(--nl-white);
  border-radius: 0;
  background-color: transparent;
  color: var(--nl-white);
  /* 37.2px cap / 0.73 */
  font: normal 700 5.1rem/1.1 var(--_font-accent);
  text-transform: uppercase;
  letter-spacing: 0.05rem;
  -webkit-transition: background-color var(--_animspeed-medium) var(--_animbezier),
                      color var(--_animspeed-medium) var(--_animbezier);
     -moz-transition: background-color var(--_animspeed-medium) var(--_animbezier),
                      color var(--_animspeed-medium) var(--_animbezier);
          transition: background-color var(--_animspeed-medium) var(--_animbezier),
                      color var(--_animspeed-medium) var(--_animbezier);
}

.btn-negative .btn-caption {
  display: -webkit-inline-box;
  display: -moz-inline-box;
  display: -ms-inline-flexbox;
  display: inline-flex;
  white-space: nowrap;
}

/* Under difference, a white fill inverts the whole button area and a
   black caption resolves back to the original backdrop colour — so hover
   reads as the negative of the resting state, not as a flat swatch.

   Filling is also what keeps the box a fixed width on hover. Opening the
   tracking instead would push a 340px button past a 360px viewport. */
.no-touch .btn-negative:hover {
  background-color: var(--nl-white);
  color: var(--nl-black);
}

@media only screen and (min-width: 768px) {
  .btn-negative {
    /* 46.6px cap / 0.73 */
    font-size: 6.4rem;
  }
}

@media only screen and (min-width: 1600px) {
  .btn-negative {
    /* 56.3px cap / 0.73 */
    font-size: 7.7rem;
  }
}

/* ------------------------------------------------*/
/* 11. Inner page header clearance                  */
/* ------------------------------------------------*/

/* main.css clears the permanent header with 6.3rem (7rem above 768px),
   which is exactly the height of the template's small logo plus the
   header's own 3rem top padding. Our wordmark is 7.6rem tall and grows
   to 11.5rem, so the template's value drops the breadcrumbs straight on
   top of it. These are the same sum recomputed for our logo — header
   padding + logo height — plus ~1.5rem so the first line clears rather
   than touches. */
.mxd-page-content.inner-page-content {
  padding-top: 12rem;
}

@media only screen and (min-width: 768px) {
  .mxd-page-content.inner-page-content {
    padding-top: 14rem;
  }
}

@media only screen and (min-width: 1600px) {
  .mxd-page-content.inner-page-content {
    padding-top: 16rem;
  }
}

/* ------------------------------------------------*/
/* 12. Stable viewport height on mobile             */
/* ------------------------------------------------*/

/* app.js's mxdViewportHeight() writes --vh from window.innerHeight on
   every resize event, and main.css sizes the hero cover with
   calc(var(--vh) * 100) below 1200px.

   On a phone that is a scroll jump: starting to scroll collapses the
   browser's own toolbar, which fires resize, which grows --vh, which
   makes the cover taller mid-gesture — so the page lengthens and
   everything under the hero slides down while the finger is still moving.

   svh is the small viewport height: the height with the toolbar showing,
   which by definition does not change when the toolbar retracts. Sizing
   the cover in it drops the dependency on --vh altogether, so the resize
   event no longer moves anything. Above 1200px there is no retracting
   toolbar and svh equals vh, which is why this is not scoped to a query —
   main.css's own 1200px rule is same-specificity and earlier in source
   order, so it would otherwise win back the vh value on desktop.

   100vh stays as the fallback for browsers without svh (Safari < 15.4,
   Chrome < 108): the cover is then slightly taller than the visible area,
   which is the template's pre-existing behaviour, not a regression.

   main.css uses --vh in two other places, .mxd-hero-fullheight and
   .mxd-hero-02. Neither class is in this project's markup, so neither is
   overridden here. */
.mxd-hero-04__wrap {
  height: 100vh;
  height: 100svh;
}

/* ------------------------------------------------*/
/* 13. Blur band clears the footer at rest          */
/* ------------------------------------------------*/

/* No CSS in the template keeps its footer out of the blur.
   .blur-container is z-index 10 and .mxd-footer is position:relative with
   z-index auto, so the band paints over the footer and blurs it — on
   purpose, while the footer scrolls past. What clears it at the bottom is
   app.js: mxdBlur() gives every .blur-section a ScrollTrigger ending at
   "bottom bottom" and hides the band on onLeave. The footer is the last
   .blur-section, so the band switches off as the page bottoms out.

   On the template's own pages that switch happens with room to spare,
   because the trigger is measured against a shorter layout than the one
   that ends up on screen: the placeholder images load from dummyimage.com
   after ScrollTrigger.refresh() and push the footer ~99px further down.
   Its end therefore sits ~99px above the real maximum scroll, and the last
   stretch of the page arrives unblurred. Measured on services.html:
   trigger end 8280, maximum scroll 8379.

   Our images are local and decoded before that refresh, so our trigger is
   exact — end lands *on* the maximum scroll. Measured, the band is still on
   at max - 0.5px and only switches off on the final pixel, which leaves the
   footer blurred through the whole smooth-scroll settle and makes the
   switch depend on Lenis landing on an exact integer.

   So give the trigger the same slack deliberately. Padding on body puts
   100px of page below the footer's box, which is what "bottom bottom"
   measures, so the end moves 100px earlier. body and .mxd-footer share
   background-color: var(--base), so the strip reads as part of the footer
   rather than as a gap.

   It has to be below the footer's box, not inside it: padding on .mxd-footer
   would move the content up but leave the box bottom on the page bottom, so
   the trigger would not move at all. */
body {
  padding-bottom: 100px;
}
