/* ==========================================================================
   INSIDE & OUT CONSORTIUM - MOTION & INTERACTION
   ==========================================================================
   All hover states, transitions and scroll-reveal behaviour live here, in one
   file, so the whole motion layer can be toned down or removed without
   touching layout. Loads after site.css.

   Everything is driven off the --accent token, so each member company's
   buttons, glows and underlines pick up that company's colour automatically.

   ---- TWO SAFETY RULES, PLEASE KEEP THEM ----

   1. Reveal-on-scroll is gated behind html[data-reveal], which reveal.js adds
      at runtime. With JavaScript disabled the rule never applies and all
      content is visible. Never write a bare `.rv { opacity: 0 }`, because that
      hides content permanently for anyone without JS, including some crawlers.

   2. prefers-reduced-motion is honoured properly: content still appears, it
      just appears instantly. Motion sickness is a real accessibility need and
      a marketing site is not worth triggering it.

   ---- THE TIMING HIERARCHY ----
   Small things move faster than big things. Buttons lift 2px in 0.15s, cards
   lift 4px in 0.25s, sections fade in over 0.6s. Getting this backwards is
   what makes a site feel sluggish.
   ========================================================================== */

:root {
  /* Shared easing. A gentle decelerate: fast out of the gate, soft landing. */
  --ease-out: cubic-bezier(0.22, 0.61, 0.36, 1);

  --dur-fast: 0.15s;   /* buttons, chips, small affordances */
  --dur-mid:  0.25s;   /* cards, panels */
  --dur-slow: 0.6s;    /* section reveals */
}

/* ==========================================================================
   1. SCROLL REVEAL
   ========================================================================== */

html[data-reveal] .rv {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity var(--dur-slow) var(--ease-out),
              transform var(--dur-slow) var(--ease-out);
}

html[data-reveal] .rv.in {
  opacity: 1;
  transform: none;
}

/* Deliberately no will-change here. Opacity and transform are already
   compositor-friendly, and hinting on every reveal target would promote a dozen
   layers per page before any of them are needed, which costs memory on exactly
   the cheap phones most of this traffic arrives on. */

/* ==========================================================================
   2. BUTTONS
   ==========================================================================
   Note the deliberately uneven durations: the lift is quicker than the glow,
   so the button feels responsive while the shadow trails slightly behind it.
   ========================================================================== */

.btn {
  transition: transform var(--dur-fast) var(--ease-out),
              box-shadow 0.2s var(--ease-out),
              background 0.2s ease,
              border-color 0.2s ease,
              color 0.2s ease;
}

/* Accent-tinted glow rather than a grey drop shadow. The negative spread is
   what makes it read as light cast on the surface below instead of a box. */
.btn-primary {
  box-shadow: 0 8px 22px -8px rgba(22, 35, 58, 0.45);            /* fallback */
  box-shadow: 0 8px 22px -8px color-mix(in srgb, var(--accent) 55%, transparent);
}
.btn-primary:hover {
  transform: translateY(-2px);
  box-shadow: 0 16px 34px -10px rgba(22, 35, 58, 0.5);           /* fallback */
  box-shadow: 0 16px 34px -10px color-mix(in srgb, var(--accent) 65%, transparent);
}

.btn-ghost:hover  { transform: translateY(-2px); }
.btn-white:hover  { transform: translateY(-2px); box-shadow: 0 14px 30px -12px rgba(0, 0, 0, 0.45); }

/* Press feedback. Without this a lifted button feels like it never gets
   clicked, because the hover state hides the click. */
.btn:active {
  transform: translateY(0) scale(0.985);
  transition-duration: 0.06s;
}

/* ==========================================================================
   3. CARDS
   ==========================================================================
   Bigger surface, so a bigger and slower movement than a button.
   ========================================================================== */

.card, .post-card, .stat, .form-card {
  transition: transform var(--dur-mid) var(--ease-out),
              box-shadow var(--dur-mid) var(--ease-out),
              border-color var(--dur-mid) ease;
}

.card:hover, .post-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 26px 52px -34px rgba(22, 35, 58, 0.5);           /* fallback */
  box-shadow: 0 26px 52px -34px color-mix(in srgb, var(--accent) 45%, transparent),
              0 2px 6px rgba(22, 35, 58, 0.04);
  border-color: color-mix(in srgb, var(--accent) 28%, var(--line));
}

/* The form card should not bounce when you mouse over a field. */
.form-card:hover { transform: none; }

/* Icon tiles pick up a soft directional tint, and grow a touch with the card.
   155deg keeps the light source consistent across every card on the page. */
.icon-badge {
  background: linear-gradient(155deg, var(--accent-wash), var(--white));
  border: 1px solid var(--line);
  transition: transform var(--dur-mid) var(--ease-out);
}
.icon-badge.solid { background: var(--accent); border-color: transparent; }
.why-item .icon-badge { background: var(--navy-deep); border-color: transparent; }

.card:hover .icon-badge { transform: scale(1.07); }

/* ==========================================================================
   4. MEMBER COMPANY CARDS
   ==========================================================================
   These are the main navigation device on the home page, so they get a little
   more than the generic card: an accent wash that sweeps in behind the text.
   ========================================================================== */

.co-card {
  position: relative;
  overflow: hidden;
  transition: transform var(--dur-mid) var(--ease-out),
              box-shadow var(--dur-mid) var(--ease-out),
              border-color var(--dur-mid) ease;
}

.co-card::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background: linear-gradient(160deg,
              color-mix(in srgb, var(--co-accent, var(--accent)) 12%, transparent),
              transparent 62%);
  opacity: 0;
  transition: opacity var(--dur-mid) ease;
  pointer-events: none;
}

.co-card > * { position: relative; z-index: 1; }
.co-card:hover::before { opacity: 1; }

.co-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 26px 52px -34px rgba(22, 35, 58, 0.5);           /* fallback */
  box-shadow: 0 26px 52px -34px color-mix(in srgb, var(--co-accent, var(--accent)) 50%, transparent);
}

/* ==========================================================================
   5. LINKS, CHIPS AND SMALL AFFORDANCES
   ========================================================================== */

/* Card links nudge in the direction of travel. Cheaper than animating the
   arrow glyph and it works with the arrows already in the markup. */
.card-link {
  display: inline-block;
  transition: transform 0.18s var(--ease-out), color 0.18s ease;
}
.card:hover .card-link, .co-card:hover .card-link { transform: translateX(4px); }
.card-link:hover { transform: translateX(4px); }

a.chip {
  transition: transform 0.18s var(--ease-out),
              border-color 0.18s ease, color 0.18s ease, box-shadow 0.18s ease;
}
a.chip:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 14px -8px rgba(22, 35, 58, 0.4);
}

/* Nav: underline draws out from the centre. */
nav.primary-nav a { position: relative; }
nav.primary-nav a::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -5px;
  height: 2px;
  border-radius: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: center;
  transition: transform 0.22s var(--ease-out);
}
nav.primary-nav a:hover::after,
nav.primary-nav a[aria-current="page"]::after { transform: scaleX(1); }

/* On mobile the nav stacks into a panel, where a centred underline reads as a
   stray rule. Suppress it there. */
@media (max-width: 1020px) {
  nav.primary-nav a::after { display: none; }
}

.has-sub > button { transition: color 0.18s ease; }
.has-sub > button::after { transition: transform 0.22s var(--ease-out); display: inline-block; }
.has-sub[data-open="true"] > button::after { transform: rotate(180deg); }

.subnav a { transition: background 0.15s ease, padding-left 0.18s var(--ease-out); }
.subnav a:hover { padding-left: 16px; }

/* Footer and article links */
footer.site-footer a { transition: color 0.18s ease; }
.article a:not(.btn) { transition: color 0.18s ease, text-decoration-color 0.18s ease; }

/* ==========================================================================
   6. HEADER
   ==========================================================================
   The header stays sticky because the phone number is the conversion path on a
   home services site. It gains a shadow once the page has scrolled, so it
   separates from content instead of floating ambiguously over it.
   ========================================================================== */

header.site-header {
  transition: box-shadow var(--dur-mid) ease, border-color var(--dur-mid) ease;
}
header.site-header[data-scrolled="true"] {
  box-shadow: 0 4px 20px -6px rgba(22, 35, 58, 0.14);
  border-bottom-color: transparent;
}

/* ==========================================================================
   7. DRAWN UNDERLINE FOR EMPHASISED WORDS
   ==========================================================================
   Wrap a phrase in <span class="mark">...</span> to have an accent rule draw
   itself in underneath. Uses a background image rather than a pseudo-element
   so that a phrase wrapping across two or three lines gets underlined on
   every line, which is the whole difficulty with this effect.

   Use it sparingly. One per page, on the phrase that carries the promise.
   ========================================================================== */

.mark {
  background-image: linear-gradient(var(--accent-soft), var(--accent-soft));
  background-repeat: no-repeat;

  /* Anchored to the BOTTOM of the box, with padding added below the descender
     so the rule sits under the text rather than through it. Positioning this
     with a percentage lands it on the baseline, behind the glyphs, where it is
     effectively invisible. */
  background-position: 0 100%;
  padding-bottom: 0.12em;

  /* Default state is DRAWN. With JavaScript off the underline is simply there,
     which is the correct fallback. reveal.js opts into the animation below. */
  background-size: 100% 0.12em;
}

/* Once the motion layer is live, start undrawn and draw on scroll-in, so a mark
   halfway down the page animates when it is actually seen rather than having
   quietly finished during page load. */
html[data-reveal] .mark {
  background-size: 0% 0.12em;
  transition: background-size 0.8s var(--ease-out) 0.15s;
}

/* The end state is a real declaration, not an animation fill state. If the
   transition never runs the rule still applies and the underline is drawn. */
html[data-reveal] .mark.mark-in { background-size: 100% 0.12em; }

/* ==========================================================================
   8. ESTIMATOR
   ========================================================================== */

.opt {
  transition: border-color var(--dur-fast) ease,
              background var(--dur-fast) ease,
              box-shadow var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}
.opt:active { transform: translateY(0) scale(0.99); transition-duration: 0.06s; }
.opt .opt-ico { transition: transform var(--dur-mid) var(--ease-out); }
.opt:hover .opt-ico { transform: scale(1.1); }

/* The result panel arrives rather than snapping in, because it is the payoff
   of the whole flow. */
.est-range {
  /* No fill-mode on purpose: see note at the foot of this section. */
  animation: result-in 0.5s var(--ease-out);
}
.est-breakdown > div {
  animation: result-in 0.4s var(--ease-out);
}
.est-breakdown > div:nth-child(1) { animation-delay: 0.06s; }
.est-breakdown > div:nth-child(2) { animation-delay: 0.12s; }
.est-breakdown > div:nth-child(3) { animation-delay: 0.18s; }
.est-breakdown > div:nth-child(4) { animation-delay: 0.24s; }
.est-breakdown > div:nth-child(5) { animation-delay: 0.30s; }
.est-breakdown > div:nth-child(n+6) { animation-delay: 0.36s; }

@keyframes result-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

/* NOTE ON FILL MODE. None of the entrance animations above declare `both` or
   `forwards`, which is deliberate. A fill mode makes the browser apply the
   from-state before the animation starts, so an animation that never starts
   leaves the element stuck at opacity 0. The estimator result is the payoff of
   the entire lead generation flow, and it must not be able to render invisible.
   Without a fill mode the element's ordinary styles apply and the animation is
   pure decoration on top. The animation begins on the frame the element is
   inserted, so there is no visible flash. */

/* ==========================================================================
   9. FORMS
   ========================================================================== */

input[type="text"], input[type="email"], input[type="tel"],
input[type="number"], select, textarea {
  transition: border-color 0.16s ease, box-shadow 0.16s ease, background 0.16s ease;
}
input:hover:not(:focus), select:hover:not(:focus), textarea:hover:not(:focus) {
  border-color: color-mix(in srgb, var(--accent) 35%, var(--line));
}

.form-status { animation: result-in 0.3s var(--ease-out); }
.route-preview[data-visible="true"] { animation: result-in 0.3s var(--ease-out); }

/* ==========================================================================
   10. FAQ
   ========================================================================== */

.faq-item summary { transition: color 0.16s ease; }
.faq-item summary:hover { color: var(--accent); }
.faq-item summary::after { transition: transform 0.2s var(--ease-out); display: inline-block; }
.faq-item[open] summary::after { transform: rotate(180deg); }
.faq-item[open] .faq-body { animation: result-in 0.32s var(--ease-out); }

/* ==========================================================================
   11. BACK TO TOP
   ==========================================================================
   Injected by reveal.js so no page markup carries it. Genuinely useful on the
   long blog posts and on the member company pages.
   ========================================================================== */

.to-top {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 70;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  border: none;
  background: var(--accent);
  color: var(--white);
  font-size: 1.05rem;
  line-height: 1;
  display: grid;
  place-items: center;
  box-shadow: 0 10px 26px -10px rgba(22, 35, 58, 0.55);
  opacity: 0;
  transform: translateY(10px);
  pointer-events: none;
  transition: opacity var(--dur-mid) ease,
              transform var(--dur-mid) var(--ease-out),
              background 0.18s ease;
}
.to-top[data-visible="true"] { opacity: 1; transform: none; pointer-events: auto; }
.to-top:hover { background: var(--navy-deep); transform: translateY(-2px); }
.to-top:active { transform: translateY(0) scale(0.95); }

@media (max-width: 620px) {
  .to-top { right: 14px; bottom: 14px; width: 42px; height: 42px; }
}

/* ==========================================================================
   12. PORTFOLIO AND MEDIA
   ========================================================================== */

.portfolio-item {
  transition: transform var(--dur-mid) var(--ease-out), box-shadow var(--dur-mid) ease;
}
.portfolio-item:hover {
  transform: translateY(-3px);
  box-shadow: 0 20px 40px -26px rgba(22, 35, 58, 0.5);
}
.portfolio-item span { transition: background 0.2s ease; }
.portfolio-item:hover span { background: color-mix(in srgb, var(--accent) 88%, black); }

.post-thumb { transition: filter var(--dur-mid) ease; }
.post-card:hover .post-thumb { filter: saturate(1.15) brightness(1.02); }

/* ==========================================================================
   13. REDUCED MOTION
   ==========================================================================
   Content still appears. It just appears without travelling. Note that hover
   colour and shadow changes are kept, because they are affordance feedback
   rather than motion, and removing them would make the site feel broken.
   ========================================================================== */

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

  html[data-reveal] .rv,
  html[data-reveal] .rv.in {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }

  html[data-reveal] .mark,
  html[data-reveal] .mark.mark-in {
    transition: none;
    background-size: 100% 0.12em;
  }

  .est-range,
  .est-breakdown > div,
  .form-status,
  .route-preview[data-visible="true"],
  .faq-item[open] .faq-body {
    animation: none !important;
  }

  .btn:hover, .btn-ghost:hover, .btn-white:hover,
  .card:hover, .co-card:hover, .post-card:hover,
  a.chip:hover, .portfolio-item:hover, .to-top:hover,
  .card:hover .icon-badge, .opt:hover .opt-ico,
  .card:hover .card-link, .co-card:hover .card-link, .card-link:hover {
    transform: none;
  }

  nav.primary-nav a::after { transition: none; }
  .subnav a:hover { padding-left: 12px; }
  .to-top { transition: opacity 0.2s ease; }
}
