/*
 * Fit Roofing — motion layer (every page).
 *
 * The signature is the roofline. F.I.T. is Foundation, Integrity, Trust, and
 * the logo draws those letters as a bronze gable — so the gable is what moves
 * between pages, the bronze is what draws under every section title, and the
 * whole layer is warm, weighted and unhurried. Nothing bounces.
 *
 * Contract (same as the In The Light and Cognexa sites):
 *  - Content ships visible. The ONLY thing that hides anything is
 *    html.fit-motion, which assets/fit-motion.js adds after confirming it can
 *    animate and the visitor has not asked for reduced motion. No JS, a
 *    script error, or prefers-reduced-motion => the page is exactly the mirror.
 *  - Only opacity, transform and clip-path are animated. Nothing lays out or
 *    repaints during a scroll.
 *  - Elementor's own entrance animations (.elementor-invisible + _animation)
 *    are left alone; the script never tags those widgets.
 *  - Nothing is keyed to an Elementor element id. Targets are tagged at
 *    runtime by widget type and structure.
 */

:root {
  --fit-primary: #826749;   /* Elementor global "primary"   — logo bronze  */
  --fit-accent:  #A1805A;   /* Elementor global "accent"    — logo copper  */
  --fit-ink:     #1A1A1A;   /* Elementor global "secondary"                */
  --fit-bronze:  linear-gradient(90deg, var(--fit-primary), var(--fit-accent));
  --fit-ease:    cubic-bezier(.22, .61, .36, 1);   /* settle              */
  --fit-ease-io: cubic-bezier(.76, 0, .24, 1);     /* the curtain         */
  --fit-scroll:  0;
}

/* ==========================================================================
   1. Page transitions — the roofline curtain.
   Leaving a page, a bronze gable rises from the foot of the screen: a roof
   going on. Arriving, the same gable lifts away to reveal the page. Both are
   pseudo-elements on <html> driven by pure CSS animations, so the arrive
   curtain finishes even if the script on the next page never runs.

   html.fit-arriving is set by a two-line inline guard in <head> (see
   tools/inject-motion.mjs) from a sessionStorage flag the previous page
   wrote — before the body is painted, so there is no flash.
   ========================================================================== */
html.fit-arriving::before,
html.fit-leaving::after {
  content: "";
  position: fixed;
  left: 0;
  right: 0;
  top: -22vh;
  height: 122vh;
  z-index: 2147483200;
  background:
    linear-gradient(165deg, #B08A62 0%, var(--fit-accent) 30%, var(--fit-primary) 62%, #5E4A35 100%);
  /* The gable: a 16% notch on a 122vh box puts the peak's shoulders just
     above the viewport when the curtain is fully on. */
  clip-path: polygon(0 16%, 50% 0, 100% 16%, 100% 100%, 0 100%);
  will-change: transform;
}
html.fit-arriving::before {
  pointer-events: none;
  transform: translate3d(0, 0, 0);
  animation: fitCurtainLift .68s var(--fit-ease-io) .1s forwards;
}
html.fit-leaving::after {
  pointer-events: auto;
  transform: translate3d(0, 100%, 0);
  animation: fitCurtainRise .42s var(--fit-ease-io) forwards;
}
@keyframes fitCurtainLift {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(0, -100%, 0); }
}
@keyframes fitCurtainRise {
  from { transform: translate3d(0, 100%, 0); }
  to   { transform: translate3d(0, 0, 0); }
}
/* The page sinks back a touch as the roof goes on over it. */
html.fit-leaving body {
  opacity: .6;
  transition: opacity .38s ease;
}
html.fit-leaving .fit-progress,
html.fit-leaving .fit-scroll-cue { opacity: 0 !important; }
/* The chat launcher sits at the largest z-index a browser accepts, so the
   curtain cannot cover it; it steps out for the second the curtain is up.

   The arriving side hides it with an animation, deliberately without
   `forwards`. html.fit-arriving is put on by the inline guard in <head>, and
   the only thing that ever takes it off again is fit-motion.js — so if that
   script is blocked, throws, or never arrives, the class stays for the whole
   page view. A static rule would then hide the chat launcher until the
   visitor navigated again, on a storm-restoration site where that launcher
   is a lead. When the animation ends the element simply goes back to its own
   visibility, so the failure mode heals itself. The animation is also what
   beats the widget's inline styles now that the !important is gone:
   animations outrank inline declarations in the cascade.

   The leaving side keeps the plain rule — it has to hold for as long as the
   navigation takes, however long that is, and fit-motion.js drops the class
   after three seconds if the navigation never lands. */
html.fit-arriving #chat-widget-push-to-talk,
html.fit-arriving #welcomeMessages,
html.fit-arriving #welcomeMessagesCross,
html.fit-arriving iframe[src*="fastbots" i] {
  animation: fitChatStepOut 1.1s linear;
}
@keyframes fitChatStepOut {
  from { visibility: hidden; }
  to   { visibility: hidden; }
}
html.fit-leaving #chat-widget-push-to-talk,
html.fit-leaving #welcomeMessages,
html.fit-leaving #welcomeMessagesCross,
html.fit-leaving iframe[src*="fastbots" i] {
  visibility: hidden !important;
}

/* ==========================================================================
   2. Reading progress — a bronze hairline across the very top.
   ========================================================================== */
.fit-progress {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 3px;
  z-index: 100000;
  pointer-events: none;
  background: var(--fit-bronze);
  transform-origin: left center;
  transform: scaleX(var(--fit-scroll));
  opacity: 0;
  transition: opacity .4s ease;
}
html.fit-motion .fit-progress { opacity: 1; }

/* ==========================================================================
   3. The sticky header gains weight once it is floating over content.
   Elementor Pro adds .elementor-sticky--active when it pins. Inset line
   rather than a border, so nothing shifts.
   ========================================================================== */
.elementor-sticky {
  transition: box-shadow .35s ease;
}
.elementor-sticky--active {
  box-shadow:
    inset 0 -1px 0 rgba(130, 103, 73, .3),
    0 10px 28px -16px rgba(26, 26, 26, .45);
}

/* ==========================================================================
   4. Scroll reveals
   ========================================================================== */
/* The hidden state exists only BEFORE reveal (:not(.fit-in)). Once revealed
   the element carries no transform of ours at all, so hover lifts, the owner
   parallax and the inline card tilt are free to use the property — a
   `transform: none` on .fit-in would outrank every one of them. The
   transition lives on the base rule because it is read from the state being
   entered. */
html.fit-motion [data-fit-reveal] {
  transition:
    opacity   .74s var(--fit-ease),
    transform .74s var(--fit-ease);
  transition-delay: var(--fit-delay, 0ms);
}
html.fit-motion [data-fit-reveal]:not(.fit-in) {
  opacity: 0;
  transform: translate3d(0, 26px, 0);
}
/* "zoom" is the only direction the script ever writes (fit-motion.js tags a
   target with 'zoom' or ''), so it is the only one that gets a rule. */
html.fit-motion [data-fit-reveal="zoom"]:not(.fit-in)  { transform: scale(.955); }
/* Once in, hover must answer at hover speed, not with the reveal's stagger. */
html.fit-motion .fit-tile.fit-in {
  transition: transform .4s var(--fit-ease), opacity .74s var(--fit-ease);
  transition-delay: 0ms;
}
/* Revealed because something inside it took keyboard focus: appear at once,
   so focus is never on something mid-fade. */
html.fit-motion [data-fit-reveal].fit-in-now {
  transition: none !important;
}

/* Section titles draw a short bronze rule beneath themselves, a beat after
   they land. The script adds .fit-h2 to the <h2> (and --center when the
   heading is centred), and skips any heading Elementor already follows with
   a divider widget. */
html.fit-motion .fit-h2 { position: relative; }
html.fit-motion .fit-h2::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -.32em;
  width: 58px;
  height: 3px;
  border-radius: 2px;
  background: var(--fit-bronze);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform .8s var(--fit-ease) .28s;
}
html.fit-motion .fit-h2--center::after {
  left: 50%;
  margin-left: -29px;
  transform-origin: center;
}
/* On a bronze band the bronze rule would vanish; draw it in the band's ink. */
html.fit-motion .fit-h2--light::after {
  background: rgba(255, 255, 255, .85);
}
/* Drawn either by our reveal (.fit-in on the widget) or by Elementor's own
   entrance (.animated on the widget) — whichever the heading uses. */
html.fit-motion [data-fit-reveal].fit-in .fit-h2::after,
html.fit-motion .elementor-widget.animated .fit-h2::after { transform: scaleX(1); }

/* Elementor divider widgets draw in the direction the eye reads. */
html.fit-motion .elementor-widget-divider[data-fit-reveal] .elementor-divider-separator {
  transform-origin: left center;
  transition: transform .8s var(--fit-ease) .18s;
}
html.fit-motion .elementor-widget-divider[data-fit-reveal]:not(.fit-in) .elementor-divider-separator {
  transform: scaleX(0);
}

/* Photographs settle rather than appear. */
html.fit-motion [data-fit-reveal] .elementor-widget-image img,
html.fit-motion .elementor-widget-image[data-fit-reveal] img {
  transition: transform 1.1s var(--fit-ease);
}
html.fit-motion [data-fit-reveal]:not(.fit-in) .elementor-widget-image img,
html.fit-motion .elementor-widget-image[data-fit-reveal]:not(.fit-in) img {
  transform: scale(1.03);
}

/* Stat counters lift a touch further so the row reads as a beat of its own. */
html.fit-motion .elementor-widget-counter[data-fit-reveal]:not(.fit-in) { transform: translate3d(0, 34px, 0); }

/* ==========================================================================
   5. The hero photograph, on every page that opens with one.
   The script finds the first page container with a cover photograph, moves
   that photograph onto its own layer behind the content, and the layer drifts
   (Ken Burns) while scrolling parallaxes it at a fraction of the page.
   The container's own Elementor overlay (::before) still paints over it.
   ========================================================================== */
.fit-hero {
  isolation: isolate;
}
/* Three layers. The clip is the container's exact box and is never
   transformed, so the photograph is framed precisely as the mirror frames it.
   The mover is what the script parallaxes. The photograph sits 4% larger than
   the box, and that 4% — not a bleed under `cover`, which would zoom the
   picture by a quarter — is the slack the mover travels in; the script caps
   the travel at it, so an edge can never show. */
.fit-hero-bg {
  position: absolute;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
}
.fit-hero-bg__move {
  position: absolute;
  inset: 0;
  transform: translate3d(0, var(--fit-hero-y, 0px), 0);
  will-change: transform;
}
.fit-hero-bg__img {
  position: absolute;
  inset: 0;
  background-repeat: no-repeat;
  background-size: cover;
  background-position: center;
  transform: scale(1.04);
  will-change: transform;
}
@media (prefers-reduced-motion: no-preference) {
  .fit-hero-bg__img { animation: fitKenBurns 32s ease-in-out infinite alternate; }
  /* No point drifting a promoted layer nobody can see. */
  .fit-hero--idle .fit-hero-bg__img { animation-play-state: paused; }
}
@keyframes fitKenBurns {
  from { transform: scale(1.04); }
  to   { transform: scale(1.08); }
}

/* ==========================================================================
   6. Hover — precise pointers only. A touch screen latches :hover after a
      tap and leaves a card stuck lifted, so all of this is gated.
   ========================================================================== */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {

  /* Service cards (image + heading + text + button in one container).
     The script adds .fit-card; JS tilt writes an inline transform on top. */
  .fit-card {
    position: relative;
    overflow: hidden; /* the sill follows the card's rounded corners… */
    overflow: clip;   /* …without making the card a scroll container */
    /* No will-change here. It was held permanently and bought a compositing
       layer per card for the life of the page — 8 of them on the homepage,
       with the pointer nowhere near the grid. Chromium promotes the card on
       hover anyway, from both this transition and the JS tilt's 3D
       transform, and releases the layer again on leave. */
    transition:
      transform  .45s var(--fit-ease),
      box-shadow .45s var(--fit-ease);
  }
  html.fit-motion .fit-card.fit-in {
    transition:
      transform  .45s var(--fit-ease),
      box-shadow .45s var(--fit-ease),
      opacity    .74s var(--fit-ease);
    transition-delay: 0ms;
  }
  html.fit-motion .fit-card.fit-in .elementor-widget-image img {
    transition: transform .9s var(--fit-ease);
  }
  .fit-card:hover {
    transform: translateY(-6px);
    box-shadow: 0 30px 52px -24px rgba(26, 26, 26, .42);
  }
  /* A bronze sill draws along the foot of the card. */
  .fit-card::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 3px;
    background: var(--fit-bronze);
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform .5s var(--fit-ease);
    pointer-events: none;
  }
  .fit-card:hover::after { transform: scaleX(1); }
  .fit-card .elementor-widget-image,
  .fit-card .elementor-widget-image .elementor-widget-container {
    overflow: hidden;
  }
  .fit-card .elementor-widget-image img {
    transition: transform .9s var(--fit-ease);
  }
  .fit-card:hover .elementor-widget-image img {
    transform: scale(1.05);
  }
  .fit-card .elementor-heading-title {
    transition: color .3s ease;
  }
  .fit-card:hover .elementor-heading-title {
    color: var(--fit-primary);
  }

  /* Value / process tiles (icon + heading + text): a quiet lift, and the
     icon steps up to meet the pointer. */
  .fit-tile {
    transition: transform .4s var(--fit-ease);
  }
  .fit-tile:hover { transform: translateY(-4px); }
  .fit-tile .elementor-widget-icon .elementor-icon {
    transition: transform .4s var(--fit-ease), color .3s ease;
  }
  .fit-tile:hover .elementor-widget-icon .elementor-icon {
    transform: translateY(-3px) scale(1.06);
  }

  /* Every Elementor button: lift, deepen the shadow, and let a trailing icon
     step forward. Colours stay whatever the kit set. */
  .elementor-button {
    transition:
      transform        .35s var(--fit-ease),
      box-shadow       .35s var(--fit-ease),
      background-color .25s ease,
      color            .25s ease,
      border-color     .25s ease;
  }
  .elementor-button:hover,
  .elementor-button:focus-visible {
    transform: translateY(-2px);
    box-shadow: 0 14px 28px -12px rgba(26, 26, 26, .45);
  }
  .elementor-button:active {
    transform: translateY(0);
    box-shadow: 0 6px 12px -8px rgba(26, 26, 26, .4);
  }
  .elementor-button .elementor-button-icon {
    transition: transform .35s var(--fit-ease);
  }
  .elementor-button:hover .elementor-button-icon {
    transform: translateX(3px);
  }

  /* Nav links are left to Elementor: the kit already draws a bronze pointer
     underline on hover and on the active item, using the same ::after. */

  /* Social icons: the same small lift. */
  .elementor-social-icon {
    transition: transform .3s var(--fit-ease), background-color .25s ease;
  }
  .elementor-social-icon:hover { transform: translateY(-3px); }

  /* Linked photographs anywhere in the content settle on hover. */
  .elementor-widget-image a img {
    transition: transform .8s var(--fit-ease);
  }
  .elementor-widget-image a:hover img {
    transform: scale(1.03);
  }
}

/* ==========================================================================
   6b. Seams between stacked pattern bands.

   Two sections that paint the same pattern on the same ground should read as
   one band. Elementor gives each its own overlay strength (0.7 on one, 0.9
   on the next) and restarts the tiling at every section boundary, so a flat
   tonal step and a broken lattice show across the join. The script matches
   the overlay to the first section of the run and offsets the tiling to
   continue through it; this rule is the only way to reach ::before, whose
   opacity Elementor sets at a specificity a plain class cannot beat.
   ========================================================================== */
.fit-seam::before { opacity: var(--fit-seam-op) !important; }

/* ==========================================================================
   7. The lattice dimple.

   The bronze diamond pattern is the background of ~42 sections across the
   site. Under the pointer it presses in: a shadow crescent above the cursor
   and a bronze catch below it, because the light on this site comes from the
   top and that pair is what makes a flat surface read as a hollow rather
   than a glow. Inside the hollow the lattice is drawn again and magnified,
   so the diamonds bow outward like a net being pushed into.

   A click lands like a hailstone — a bronze ring cracking outward. This is a
   storm-restoration roofer; hail is the thing their phone rings about.

   Everything is painted by one absolutely-positioned child at z-index 0 (see
   the note on the rule below for why not -1), so it sits above the section's
   own background and below every word on it. Pointer coordinates arrive as
   two custom properties; nothing here reads layout. Adjacent bands that the
   script merged into one run are lit together, so the glow crosses the join
   as one circle instead of being sliced in half at it.
   ========================================================================== */
.fit-lattice { position: relative; }

/* z-index 0, not -1. Elementor paints its background overlay on the
   section's own ::before — position:absolute, z-index:auto — and a child at
   -1 is drawn underneath it, so on every dark band the dimple was rendered
   and then covered by opaque black. At 0, and as the first child, it paints
   after that overlay; the script lifts the section's real children to
   position:relative so they still paint above this. */
.fit-lattice-dimple {
  --fit-lat-r: 118px;
  --fit-lat-lit: rgba(201, 162, 110, .4);
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  transition: opacity .45s var(--fit-ease);
}
.fit-lattice.is-dimpled .fit-lattice-dimple { opacity: 1; }
@media (max-width: 767px) { .fit-lattice-dimple { --fit-lat-r: 120px; } }

/* The hollow itself. */
.fit-lattice-dimple::before {
  content: "";
  position: absolute;
  inset: 0;
  /* A soft warm pool, nothing more. Earlier versions drew a shadow crescent
     and a rim to fake a hollow; an even ring reads as a halo and the shadow
     over a white band reads as dirt. The depth now comes from the lattice
     itself in ::after — the light pool only warms the ground under it. */
  background:
    radial-gradient(circle var(--fit-lat-r) at var(--fit-mx, 50%) var(--fit-my, 50%),
      var(--fit-lat-lit) 0%, transparent 72%);
}

/* The lattice again, magnified around the pointer and masked to the hollow. */
.fit-lattice-dimple::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: var(--fit-lat-img);
  background-size: var(--fit-lat-size, 20% auto);
  background-position: var(--fit-lat-pos, 0 0);
  background-repeat: repeat;
  /* Barely magnified — at 1.08 the doubled lines read as blur, not depth,
     and 1.02 is as much lift as a dark ground will swallow. The depth comes
     from the bronze catching light instead: inside the disc the lattice is
     redrawn with a filter that makes it stand out against its own ground.
     Only dark bands get any of this (see fit-motion.js) — on the white
     counters band the lattice is dark bronze on near-white, with no headroom
     to lift, and every attempt at it read as a smudge. */
  transform: scale(1.02);
  transform-origin: var(--fit-mx, 50%) var(--fit-my, 50%);
  filter: var(--fit-lat-filter, brightness(2.3) saturate(1.35));
  -webkit-mask-image: radial-gradient(circle var(--fit-lat-r) at var(--fit-mx, 50%) var(--fit-my, 50%),
    #000 0%, rgba(0, 0, 0, .88) 34%, transparent 78%);
  mask-image: radial-gradient(circle var(--fit-lat-r) at var(--fit-mx, 50%) var(--fit-my, 50%),
    #000 0%, rgba(0, 0, 0, .88) 34%, transparent 78%);
}

/* The hailstone strike. Removed on animationend, so none of these persist. */
.fit-strike {
  position: absolute;
  left: var(--fit-sx, 50%);
  top: var(--fit-sy, 50%);
  width: 0;
  height: 0;
  border-radius: 50%;
  border: 3px solid rgba(201, 162, 110, .6);
  transform: translate(-50%, -50%);
  pointer-events: none;
  animation: fitStrike .8s cubic-bezier(.22, .61, .36, 1) forwards;
}
@keyframes fitStrike {
  from { width: 10px;  height: 10px;  opacity: .95; border-width: 3px; }
  to   { width: 460px; height: 460px; opacity: 0;   border-width: 1px; }
}

/* ==========================================================================
   8. Respect the visitor. This wins over everything above.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  .fit-lattice-dimple { display: none !important; }
  .fit-strike { display: none !important; animation: none !important; }
  html.fit-arriving::before,
  html.fit-leaving::after { display: none !important; animation: none !important; }
  html.fit-leaving body { opacity: 1 !important; }
  html.fit-motion [data-fit-reveal],
  html.fit-motion [data-fit-reveal].fit-in,
  html.fit-motion [data-fit-reveal] .elementor-divider-separator,
  html.fit-motion .fit-h2::after,
  html.fit-motion [data-fit-reveal] img,
  .fit-hero-bg, .fit-hero-bg__move, .fit-hero-bg__img,
  .fit-card, .fit-tile, .elementor-button {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
    animation: none !important;
  }
  .fit-progress { display: none !important; }
}

/* ==========================================================================
   9. Guides and area lists: plain link blocks that sit just before the
   closing call to action on service, city and area pages. Static, no motion.
   ========================================================================== */
.fit-guides {
  box-sizing: border-box;
  max-width: 1140px;
  margin: 0 auto;
  padding: 56px 20px;
}
.fit-guides h2 {
  margin: 0 0 20px;
  font-family: var(--e-global-typography-primary-font-family, "AR One Sans"), sans-serif;
  font-weight: 600;
  font-size: 30px;
  line-height: 1.2;
  color: var(--fit-ink);
}
.fit-guides ul {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 12px;
}
.fit-guides li {
  margin: 0;
}
.fit-guides a {
  font-family: var(--e-global-typography-text-font-family, "Roboto"), sans-serif;
  font-size: 18px;
  line-height: 1.5;
  color: var(--fit-primary);
  text-decoration: underline;
  text-underline-offset: 3px;
  overflow-wrap: anywhere;
}
.fit-guides a:hover,
.fit-guides a:focus-visible {
  color: var(--fit-ink);
}
@media (max-width: 767px) {
  .fit-guides {
    padding: 40px 16px;
  }
  .fit-guides h2 {
    font-size: 24px;
  }
  .fit-guides a {
    font-size: 16px;
  }
}
