/* ===========================================================================
   SHARED — every static page loads this                     added 2026-09-23
   The parts that are not about any one page: the base rules, the preloader,
   the entrance animation, the plan-section timeline, the Command Center modal,
   the draft-note styling and the closing CTA.

   These all mirror something the home page does, so they belong with the
   chrome rather than with a page. A page's own stylesheet
   (assets/<slug>.css) carries only what is unique to it.

   Do not add page-specific rules here.
   =========================================================================== */

/* Lenis runs here too now, and it brings its own
   .lenis.lenis-smooth{scroll-behavior:auto!important} from the compiled CSS,
   which outranks this. So this is the fallback for the one case Lenis sits
   out: reduced motion, where the SmoothScroll component skips it entirely. */
html {
  scroll-behavior: smooth
}

@media (prefers-reduced-motion:reduce) {
  html {
    scroll-behavior: auto
  }
}

/* The compiled CSS applies content-visibility:auto to every section after the
   first. Skipped rendering confuses the measurements this page depends on —
   the reveal observer and Lenis's own document height — on a page this short
   it buys nothing. That selector scores 0,1,2 (the :not() carries
   :first-of-type), so this one has to match it rather than undercut it. */
main>section:not(:first-of-type) {
  content-visibility: visible
}

/* The same rule also names main>div.mt-dark, which is the MEDIATAMER ticker.
   Left alone it reserves contain-intrinsic-size's 900px while off-screen and
   collapses to its real 74px on approach - an 800px jump as you scroll to the
   footer. Matching specificity (0,1,2), and this file loads later. */
main>div.mt-dark {
  content-visibility: visible
}

body {
  background: #fff
}

/* the header is fixed, so an in-page anchor would otherwise land underneath it */
main [id] {
  scroll-margin-top: 100px
}



/* --- preloader ------------------------------------------------------------
   The same overlay the home page shows, generated into every page by
   scripts/build-pages.py so there is one copy of the markup.

   Hidden by default and only shown once .mt-anim lands — the inline head
   script adds that synchronously. So a reader without JavaScript never sees a
   navy sheet they cannot dismiss, which is the one way a static preloader can
   go badly wrong. That script also carries a hard failsafe that tears the
   overlay down after 5s no matter what, in case mt-chrome.js never arrives. */
[data-preloader] {
  display: none
}

.mt-anim [data-preloader] {
  display: grid
}

@media (prefers-reduced-motion:reduce) {
  .mt-anim [data-preloader] {
    display: none
  }
}

/* the mark's paths rise into place; --pre-rise is each path's own height x1.3,
   measured from its bounding box by mt-chrome.js, which is what GSAP's
   yPercent:130 resolves to */
.mt-anim [data-pre-mark] path {
  opacity: 0;
  transform: translateY(var(--pre-rise, 120px))
}

[data-preloader].is-running [data-pre-mark] path {
  opacity: 1;
  transform: none;
  transition: opacity .55s cubic-bezier(.215, .61, .355, 1),
    transform .55s cubic-bezier(.215, .61, .355, 1)
}

[data-pre-inner] {
  transition: opacity .25s ease, transform .25s ease
}

[data-preloader].is-clearing [data-pre-inner] {
  opacity: 0;
  transform: translateY(-12px)
}

/* power4.inOut, the curve the component uses for the wipe */
[data-preloader].is-leaving {
  transform: translateY(-100%);
  transition: transform .65s cubic-bezier(.77, 0, .175, 1)
}

/* --- entrance animation --------------------------------------------------
   Matched to the home page rather than invented. Two mechanisms there, and
   only two — everything else on that page simply appears, which is why an
   inner page that fades in every block reads as a different site:

     MaskReveal   every line of the hero column wipes up out of a clip-path —
                  the eyebrow, each headline line, the subhead and the button
                  row, not the headline alone. The hero passes
                  selector: "[data-line], h1 > span", which is six elements
                  there and five here.
                  set   {clipPath: inset(0 0 100% 0), y: 24, opacity: .001}
                  to    {clipPath: inset(0 0 -10% 0), y: 0, opacity: 1,
                         duration: .7, ease: power3.out, stagger: .07}
                  Runs on load.

     RevealStagger  card grids lift in.
                  set   {y: 44, opacity: 0}
                  to    {y: 0, opacity: 1, duration: .85,
                         ease: power3.out, stagger: .1}
                  Fires when the grid reaches an IntersectionObserver
                  threshold of 0.15.

   GSAP's power3.out is cubic-bezier(.215,.61,.355,1). The stagger delays are
   set per element by mt-chrome.js.

   Both are gated on .mt-anim, which an inline head script adds synchronously,
   so a reader without JavaScript gets the page at full opacity. */

.mt-anim [data-mask-line] {
  clip-path: inset(0 0 100% 0);
  transform: translateY(24px);
  opacity: .001;
  transition: clip-path .7s cubic-bezier(.215, .61, .355, 1),
    transform .7s cubic-bezier(.215, .61, .355, 1),
    opacity .7s cubic-bezier(.215, .61, .355, 1)
}

.mt-anim [data-mask-line].is-in {
  clip-path: inset(0 0 -10% 0);
  transform: none;
  opacity: 1
}

.mt-anim [data-reveal] {
  opacity: 0;
  transform: translateY(44px);
  transition: opacity .85s cubic-bezier(.215, .61, .355, 1),
    transform .85s cubic-bezier(.215, .61, .355, 1)
}

.mt-anim [data-reveal].is-in {
  opacity: 1;
  transform: none
}

/* set by the script when it decides not to animate, so the end state lands
   with no transition instead of playing a stale one */
.mt-anim [data-mask-line].no-anim,
.mt-anim [data-reveal].no-anim {
  transition: none
}

@media (prefers-reduced-motion:reduce) {

  .mt-anim [data-mask-line],
  .mt-anim [data-reveal] {
    opacity: 1;
    transform: none;
    clip-path: none;
    transition: none
  }
}

@media print {

  .mt-anim [data-mask-line],
  .mt-anim [data-reveal] {
    opacity: 1;
    transform: none;
    clip-path: none
  }
}

/* --- FAQ accordion ---------------------------------------------------------------
   Still <details>/<summary>: it opens with no JavaScript and needs no ARIA of
   our own. The heading column sticks while the list scrolls past it.
   The open panel fades in with an animation rather than a transition — a
   closed <details> does not render its contents at all, so there is no start
   state for a transition to run from, but an animation plays the moment the
   element is first displayed. */
.mt-faq-shell {
  display: grid;
  gap: 40px
}

@media (min-width:1024px) {
  .mt-faq-shell {
    grid-template-columns: minmax(0, .82fr) minmax(0, 1.18fr);
    gap: 64px;
    align-items: start
  }

  .mt-faq-aside {
    position: sticky;
    top: 124px
  }
}

.mt-faq-list {
  display: grid;
  gap: 12px
}

.mt-faq {
  position: relative;
  background: #fff;
  border: 1px solid var(--mt-border);
  border-radius: 16px;
  overflow: hidden;
  transition: border-color .3s ease, box-shadow .45s ease, transform .3s ease
}

/* the orange rail marks the open one; it is a pseudo-element so it can grow
   out of nothing rather than shifting the text by a border's width */
.mt-faq::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  background: var(--mt-orange);
  transform: scaleY(0);
  transform-origin: 50% 0;
  transition: transform .32s cubic-bezier(.22, 1, .36, 1)
}

.mt-faq[open] {
  border-color: rgba(255, 107, 53, .42);
  box-shadow: 0 20px 44px -26px rgba(26, 31, 46, .34)
}

.mt-faq[open]::before {
  transform: scaleY(1)
}

.mt-faq:hover {
  border-color: rgba(255, 107, 53, .30)
}

.mt-faq summary {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 19px 22px;
  cursor: pointer;
  list-style: none
}

.mt-faq summary::-webkit-details-marker {
  display: none
}

/* Safari/Chrome */
.mt-faq summary::marker {
  content: ""
}

/* Firefox */
.mt-faq summary:focus-visible {
  outline: 3px solid var(--mt-orange);
  outline-offset: -3px
}

/* the index, in the same treatment home gives its service cards */
.mt-faq__n {
  flex: 0 0 auto;
  font-family: var(--font-heading), "Arial Narrow", sans-serif;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: .14em;
  color: var(--mt-orange);
  font-variant-numeric: tabular-nums
}

.mt-faq__q {
  flex: 1 1 auto;
  font-size: 17px;
  line-height: 1.35;
  font-weight: 700;
  color: var(--mt-navy)
}

.mt-faq summary:hover .mt-faq__q {
  color: var(--mt-orange-dark)
}

.mt-faq__icon {
  flex: 0 0 30px;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: 999px;
  background: var(--mt-bg-soft, #f8f9fc);
  color: var(--mt-orange);
  transition: transform .35s cubic-bezier(.22, 1.2, .36, 1), background-color .3s ease
}

.mt-faq[open] .mt-faq__icon {
  transform: rotate(180deg);
  background: rgba(255, 107, 53, .14)
}

.mt-faq__body {
  padding: 0 22px 22px 60px
}

.mt-faq[open] .mt-faq__body {
  animation: mt-faq-in .3s cubic-bezier(.22, 1, .36, 1) both
}

@keyframes mt-faq-in {
  from {
    opacity: 0;
    transform: translateY(-6px)
  }
}

.mt-faq__body p {
  margin: 0;
  font-size: 15px;
  line-height: 1.7;
  color: var(--mt-text-muted)
}

/* A dummy answer, bracketed and labelled. It reads at the same size and
   colour as a real answer — only the bracket and the bold label mark it out —
   so the section can be judged on its layout. The full draft note is still in
   the markup beside it, hidden, so nothing is lost. */
.mt-faq__body .mt-faq__ph {
  margin: 0;
  font-size: 15px;
  line-height: 1.7;
  color: var(--mt-text-muted)
}

.mt-faq__ph strong {
  font-weight: 800;
  color: var(--mt-navy)
}

/* a visible draft note inside an answer loses its top margin, since the
   summary above already provides the spacing */
.mt-faq__body [data-draft-note] {
  margin-top: 0
}

@media (max-width:480px) {
  .mt-faq__body {
    padding-left: 22px
  }
}

@media (prefers-reduced-motion:reduce) {

  .mt-faq,
  .mt-faq::before,
  .mt-faq__icon {
    transition: none
  }

  .mt-faq[open] .mt-faq__body {
    animation: none
  }
}

/* No chrome styles live here. The header, the footer and the mobile
   dropdown use only classes that already ship in the compiled CSS, so
   their design is the home page's, untouched. */

/* The magnetic hover and the parallax are driven per frame by mt-chrome.js,
   not by a CSS transition.

   A transition was the first attempt and it felt wrong: every pointermove
   restarts it from wherever the button currently is, so the motion lags the
   cursor and stutters. GSAP's quickTo — what the home page uses — retargets a
   live tween instead, which is why those buttons feel glued to the pointer.
   The script now interpolates every frame to match, so nothing here should
   transition transform or it would fight the loop.

   The buttons keep Tailwind's own transition-colors duration-200 for hover
   colour, which is all the home page has beyond the tween. */
@media (prefers-reduced-motion:reduce) {

  [data-magnetic],
  [data-parallax] {
    transform: none !important
  }
}

/* --- 06 process -----------------------------------------------------------
   The section is the home page's, so its layout is all Tailwind classes in the
   markup. Two things have to be restated here because they live in
   index.html's own inline <style> blocks and never reach the compiled sheet:
   the step-04 hide, and the Command Center trigger and modal. */


/* The scroll-linked fill. GSAP drives it with quickTo(duration .45,
   ease power2.out) on the home page; power2.out is cubic-bezier(.25,.46,.45,.94).
   mt-chrome.js sets --tl-progress. */
[data-tl-fill] {
  transform: scaleY(var(--tl-progress, 0));
  transform-origin: top center;
  transition: transform .45s cubic-bezier(.25, .46, .45, .94)
}

@media (prefers-reduced-motion:reduce) {
  [data-tl-fill] {
    transform: scaleY(1);
    transition: none
  }
}

/* --- Command Center modal, copied from index.html #mt-cc-style ----------- */
/* ===========================================================================
   COMMAND CENTER WALKTHROUGH — client request 2026-09-23
   Placed in §3.4 step 02 ("We build your foundation"), the moment the visitor
   is asked to believe in a CRM they cannot see. Deliberately NOT styled as one
   of the three §04 entry points: it captures nothing and goes nowhere, so it
   must not compete with Start Now / Book a Consultation / the free guide.
   =========================================================================== */
.mt-cc-trigger {
  margin-top: 16px;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  border: 1px solid rgba(255, 255, 255, .22);
  background: transparent;
  color: #fff;
  border-radius: 999px;
  padding: 8px 15px 8px 9px;
  font: 700 13px/1 inherit;
  font-family: inherit;
  cursor: pointer;
  transition: border-color .25s ease, background-color .25s ease, color .25s ease
}

.mt-cc-trigger:hover {
  border-color: var(--mt-orange);
  color: var(--mt-orange);
  background: rgba(255, 107, 53, .08)
}

.mt-cc-trigger:focus-visible {
  outline: 3px solid var(--mt-orange);
  outline-offset: 3px
}

.mt-cc-trigger__icon {
  display: grid;
  place-items: center;
  flex: 0 0 22px;
  width: 22px;
  height: 22px;
  border-radius: 999px;
  background: var(--mt-orange);
  color: var(--mt-navy)
}

.mt-cc-trigger__time {
  font-weight: 600;
  font-size: 12px;
  opacity: .62
}

/* --- modal --- */
.mt-cc-backdrop {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(10, 13, 20, .88)
}

.mt-cc-backdrop[data-open] {
  display: flex
}

@supports (backdrop-filter:blur(4px)) {
  .mt-cc-backdrop[data-open] {
    backdrop-filter: blur(6px)
  }
}

.mt-cc-dialog {
  position: relative;
  width: 100%;
  max-width: 980px;
  border-radius: 20px;
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, .12);
  background: #000;
  box-shadow: 0 40px 110px -30px rgba(0, 0, 0, .85);
  animation: mt-cc-in .35s cubic-bezier(.22, 1.2, .36, 1) both
}

@keyframes mt-cc-in {
  from {
    opacity: 0;
    transform: translateY(14px) scale(.985)
  }

  to {
    opacity: 1;
    transform: none
  }
}

@media (prefers-reduced-motion:reduce) {
  .mt-cc-dialog {
    animation: none
  }
}

.mt-cc-dialog video {
  display: block;
  width: 100%;
  aspect-ratio: 16/9;
  background: #000
}

.mt-cc-close {
  position: absolute;
  top: 12px;
  right: 12px;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, .28);
  background: rgba(0, 0, 0, .55);
  color: #fff;
  cursor: pointer;
  transition: background-color .2s, color .2s, border-color .2s
}

.mt-cc-close:hover {
  background: var(--mt-orange);
  color: var(--mt-navy);
  border-color: var(--mt-orange)
}

.mt-cc-close:focus-visible {
  outline: 3px solid var(--mt-orange);
  outline-offset: 2px
}

@media (max-width:680px) {
  .mt-cc-backdrop {
    padding: 14px
  }

  .mt-cc-close {
    width: 36px;
    height: 36px
  }
}


/* --- draft notes ----------------------------------------------------------
   Visible on purpose: these are the copy doc's own C-numbered placeholders and
   the client reviews this build. Hide every one of them before launch with
   [data-draft-note]{display:none} — one rule, nothing to unpick. */
[data-draft-note] {
  margin-top: 20px;
  padding: 16px 18px;
  border: 1px dashed rgba(255, 107, 53, .55);
  border-radius: 14px;
  background: rgba(255, 107, 53, .06);
  font-size: 13px;
  line-height: 1.6;
  color: var(--mt-text-muted)
}

.mt-dark [data-draft-note] {
  color: var(--mt-text-on-dark);
  background: rgba(255, 107, 53, .08)
}

[data-draft-note] b {
  display: block;
  margin-bottom: 4px;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--mt-orange)
}

/* --- 07 closing ----------------------------------------------------------
   The home page's #book section. Everything visual is Tailwind classes or
   inline styles in the markup; only these two rules live in index.html's
   <style id="mt-s310-style"> and so have to be restated here. */


.mt-s310-sub {
  margin-top: 26px;
  max-width: 36rem;
  font-size: 17px;
  line-height: 1.6;
  color: var(--mt-text-on-dark);
  text-align: center
}

@media (max-width:680px) {
  .mt-s310-sub {
    font-size: 15px
  }
}

/* --- 01 hero --------------------------------------------------------------
   .mt-hero-visual, mt-glow, mt-grain and the arbitrary max-w-[42%] utilities
   all come from the compiled design-system CSS, so the hero column and the
   visual panel size exactly as they do on the home page. Only the two rules
   the home page keeps in its own inline <style> have to be restated here. */

/* index.html #mt-hero-video-style lays the panel out as a flex column; above
   920px .mt-hero-visual is absolute with top:0/bottom:0, so the figure needs
   an alignment of its own or it stretches to the panel.
   Top, not centre: the photo starts level with the eyebrow, so the two columns
   of the hero begin on the same line. Home keeps its own centred copy in that
   inline style - this file never loads there. */
.mt-hero-visual {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  pointer-events: none
}

/* index.html #mt-s31-style steps the headline down from the build's original
   clamp(38px,5.7vw,77px). At 77px a long line overruns the 42% column and
   spills across the visual, and the hero clips. Same ceiling as the home page
   so the two headlines read at the same size. */
[data-hero] h1 {
  font-size: clamp(33px, 5vw, 66px)
}

@media (max-width:319px) {
  [data-hero] h1>span {
    white-space: normal
  }
}

/* Every inner hero stands as tall as the home page's, and starts its copy at
   the same height.

   The padding already matched home exactly (168/128 here, 152/96 and 136/96
   further down), but the section's HEIGHT did not: .mt-hero-visual is
   absolute, so only the text column set it. That left the band anywhere from
   627px (Case Studies) to 804px (SEO) against home's 909px.

   The floor is set to the tallest content these pages actually carry rather
   than to home's own height: home fills its band with a visual that stretches,
   these carry a fixed-ratio photo, so matching home's 900px left a dead strip
   under the frame. The section has exactly one in-flow child - .mt-shell;
   mt-glow and the particle canvas are both absolute - so it can be laid out as
   a column, and the shell told to fill what the padding leaves.

   Filling rather than centring is the point. Centring the shell pushed the
   copy down the band - the eyebrow landed 246-290px from the top against
   home's 198px. Filled, the text column starts hard against the top padding
   exactly as home's does, while the visual panel - which is pinned top:0 /
   bottom:0 inside the shell - spans the whole box, so the photo centres in it
   with equal clearance above and below whatever the headline does.

   flex:1 0 auto rather than a fixed min-height, so this holds at 920-1023px
   too, where the padding steps to 152/96 and the box is a different size.
   Below 920px the column stacks and every hero grows past this on its own. */
@media (min-width:920px) {

  /* :not([data-hero-plain]) - the Blog hero carries no photo, so the floor
     that exists to hold one would only leave a dead strip under text. */
  [data-hero]:not([data-hero-plain]) {
    min-height: 745px;
    display: flex;
    flex-direction: column
  }

  [data-hero] .mt-shell {
    flex: 1 0 auto
  }
}

/* 745px used to be raised to 820px from 1024px up, because the headline is
   clamp(33px, 5vw, 66px) and so grows with the viewport until it caps around
   1320px - which takes the text column with it. Measured at 1440 with the
   floor off, the natural heights are:

     SEO & GBP 814   Social Media 785   Who We Help 757   Website+CRM 725
     Yelp 723        About 716          Google Ads 695    Pricing 695
     Case Studies 632

   820 was the least that held all nine at the same height. It is 745 now, by
   request (20 Oct 2026), so the six pages at or under it are 75px shorter and
   the three above it stand at their own natural height instead - SEO, Social
   Media and Who We Help are 12, 40 and 69px taller than the rest. Nothing
   overflows or clips; the heroes simply no longer all agree.

   If they should agree again, the fix is in the copy rather than here: bring
   those three hero columns under 449px (745 minus the 168/128 padding) and
   this one number holds every page. */


/* The hero visual is the blob, with the photo inside it.
   The real blob is a Three.js scene: IcosahedronGeometry(1.16, 28) displaced
   by two octaves of simplex noise, shaded navy (#2d3548) with an orange
   fresnel rim. It cannot run here — it needs React, react-three-fiber and the
   684KB three.js chunk, none of which an inner page loads, and its shader has
   no texture sampling, so a photo could not go inside it as it stands.
   So the silhouette is reproduced in SVG at the same noise amplitude and
   octave ratio, the photo is clipped to it, and the fresnel is rebuilt as a
   rim stroke plus an outer bloom. The outline morphs on a slow loop, as the
   displaced mesh does. */
/* --- 01 hero photo -------------------------------------------------------
   Both inner pages show a plain photo where the home page runs its blob (the
   blob stays in each page's markup, hidden). The border is a conic gradient on a square that is three
   times the figure and spins about its centre, clipped by the figure's own
   overflow — so a bright segment travels the edge like a snake while the
   faint white base ring keeps the whole outline visible. Rotating an element
   costs one composited transform; animating a gradient's angle instead would
   need @property and repaint every frame. */
.mt-hero-shot {
  position: relative;
  margin: 0;
  width: 100%;
  max-width: 600px;
  padding: 4px;
  border-radius: 26px;
  overflow: hidden;
  isolation: isolate;
  background: rgba(255, 255, 255, .14);
  box-shadow: 0 0 34px -6px rgba(255, 107, 53, .30)
}

.mt-hero-shot__snake {
  position: absolute;
  inset: -100%;
  z-index: 0;
  background: conic-gradient(from 0deg,
      rgba(255, 107, 53, 0) 0deg 205deg,
      rgba(255, 107, 53, .28) 258deg,
      var(--mt-orange) 296deg,
      #fff2ea 320deg,
      var(--mt-orange) 342deg,
      rgba(255, 107, 53, 0) 360deg);
  animation: mt-snake 4.5s linear infinite
}

@keyframes mt-snake {
  to {
    transform: rotate(1turn)
  }
}

/* The frame carries its own padding on the page's own navy, so the photo sits
   inset from the travelling border instead of running straight into it. Two
   radii, each stepped in by its padding, keep the three curves concentric:
   26 outer → 22 frame → 14 photo. */
.mt-hero-shot__frame {
  position: relative;
  z-index: 1;
  display: block;
  padding: 10px;
  border-radius: 22px;
  background: var(--mt-navy)
}

.mt-hero-shot img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4/3;
  object-fit: cover;
  border-radius: 14px
}

/* the same steps down the blob took, so the column still balances */
@media (max-width:919px) {
  .mt-hero-shot {
    max-width: 460px
  }
}

@media (max-width:480px) {
  .mt-hero-shot {
    max-width: 340px
  }
}

@media (prefers-reduced-motion:reduce) {
  .mt-hero-shot__snake {
    animation: none
  }
}

/* --- tile grid -------------------------------------------------------
   The home page's problem-section card, restated. Those rules live in
   index.html's <style id="mt-s32-style"> and never reach the compiled
   stylesheet, so an inner page cannot reference .mt-s32-card — it has to carry
   its own copy. Values below are that block's, unchanged.

   Four columns rather than three: eight values split 4+4, where the home
   page's three would leave a ragged 3+3+2. */
.mt-tile-grid {
  display: grid;
  gap: 20px;
  margin-top: 56px
}

@media (min-width:640px) {
  .mt-tile-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr))
  }
}

@media (min-width:1024px) {
  .mt-tile-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr))
  }
}

/* data-reveal belongs on .mt-tile-cell, never on the tile itself: the reveal
   owns transform on whatever carries it, and .mt-anim [data-reveal].is-in
   (0,3,0) beats .mt-tile:hover (0,2,0) - which suppressed the lift outright -
   while its 850ms transition would drive the hover too. Home separates the
   two onto different elements for exactly this reason. */
.mt-tile-cell {
  height: 100%
}

.mt-tile {
  position: relative;
  height: 100%;
  background: #fff;
  border: 1px solid var(--mt-border);
  border-radius: 20px;
  padding: 30px 26px;
  isolation: isolate
}

/* pointer-tracked sheen — mt-chrome.js feeds --mx/--my, as the home page's
   own #mt-s32-pointer script does */
.mt-tile::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  z-index: -1;
  opacity: 0;
  transition: opacity .45s ease;
  background: radial-gradient(420px circle at var(--mx, 50%) var(--my, 50%),
      rgba(255, 107, 53, .10), transparent 62%)
}

/* the lift's shadow, faded rather than transitioned. A moving 44px blur
   repaints the whole card every frame; opacity composites. Outer shadows are
   clipped to outside the border box, so this never tints the card itself. */
.mt-tile::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  z-index: -1;
  opacity: 0;
  box-shadow: 0 22px 48px -24px rgba(26, 31, 46, .42);
  transition: opacity .3s ease
}

@media (hover:hover) {

  /* 300ms on cubic-bezier(.22,1,.36,1) and -8px: the home page's own card
     hover, to the frame. The old 550ms springy curve is what read as sluggish. */
  .mt-tile {
    will-change: transform;
    transition: transform .3s cubic-bezier(.22, 1, .36, 1), border-color .3s ease
  }

  .mt-tile:hover {
    transform: translateY(-8px);
    border-color: rgba(255, 107, 53, .45)
  }

  .mt-tile:hover::before {
    opacity: 1
  }

  .mt-tile:hover::after {
    opacity: 1
  }

  .mt-tile__rule {
    transition: width .3s cubic-bezier(.22, 1, .36, 1)
  }

  .mt-tile:hover .mt-tile__rule {
    width: 56px
  }
}

@media (prefers-reduced-motion:reduce) {

  .mt-tile,
  .mt-tile__rule,
  .mt-tile::before {
    transition: none
  }

  .mt-tile:hover {
    transform: none
  }

  .mt-tile:hover::after,
  .mt-tile:hover::before {
    opacity: 0
  }
}

.mt-tile__rule {
  display: block;
  width: 32px;
  height: 3px;
  border-radius: 999px;
  background: var(--mt-orange)
}

/* h3 inherits the global display face, as every other card's does */
.mt-tile h3 {
  margin: 18px 0 0;
  font-size: 20px;
  line-height: 1.25
}

.mt-tile p {
  margin: 12px 0 0;
  font-size: 15px;
  line-height: 1.65;
  color: var(--mt-text-muted)
}


/* --- GHL booking form ------------------------------------------------------
   Requested 2026-09-24. Note it can only take effect on an element that is in
   OUR document: the booking form renders inside a cross-origin iframe on
   api.leadconnectorhq.com, and no stylesheet of ours reaches into that - a
   rule for the form's own internals has to go in GHL's form builder instead.
   Harmless either way, and it applies the moment such a wrapper exists on the
   page side. */
.ghl-form-wrap {
  margin-left: inherit !important
}


/* 2. The shell carries py-20, so 80px sat under the bottom bar. */
footer>.mt-shell {
  padding-bottom: 20px
}