/* ==========================================================================
   motion.css — hero sequence, scroll reveals, pointer depth, micro-states.
   Loads last so it can override component transitions.

   Only `transform`, `opacity`, `filter` and `box-shadow` are animated. Nothing
   here touches a layout property, which is why CLS stays at 0.

   The global prefers-reduced-motion reset lives in base.css so it applies even
   if this file fails to load; the block at the bottom additionally resets the
   STARTING states so nothing can be left invisible or displaced.
   ========================================================================== */

/* ====================================================== HERO INTRO (load)
   Eyebrow rule draws, headline and lead rise, CTAs follow, then the
   photograph is unveiled and the feature list settles. ~900 ms in total,
   then the composition is still.

   Everything here runs from keyframes on load, NOT from an observer, so a
   JavaScript failure leaves the hero fully visible.

   LCP safety: the H1 text and the hero <img> are never at opacity 0. The H1
   plays a transform-only rise; the photograph is revealed by sliding a
   paper-coloured CURTAIN off it (an overlay transform), while the image
   itself is opaque from its first frame. */

@keyframes hero-rise {
  from { transform: translateY(16px); opacity: 0; }
  to   { transform: translateY(0);    opacity: 1; }
}
@keyframes hero-rise-opaque {
  from { transform: translateY(16px); }
  to   { transform: translateY(0); }
}
@keyframes eyebrow-draw {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}
@keyframes curtain-off {
  from { transform: scaleX(1); }
  to   { transform: scaleX(0); }
}
@keyframes photo-settle {
  from { transform: scale(1.08); }
  to   { transform: scale(var(--photo-rest, 1.03)); }
}

.hero__copy > * {
  animation: hero-rise var(--dur-3) var(--ease-out) backwards;
}
/* The H1 itself does not move: its WORDS do (below). */
.hero__copy > :nth-child(2) { animation: none; }
.hero__copy > :nth-child(3) { animation-delay: var(--delay-copy); }
.hero__copy > :nth-child(4) { animation-delay: calc(var(--delay-copy) + var(--step-copy)); }
.hero__copy > :nth-child(5) { animation-delay: calc(var(--delay-copy) + 2 * var(--step-copy)); }

/* Headline: each word rises into place, 60 ms apart, then a blue stroke
   draws under the accent word. ~800 ms, then still. */
@keyframes word-rise {
  from { transform: translateY(.45em) rotate(2deg); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
.hero h1 .w {
  animation: word-rise var(--dur-4) var(--ease-out) backwards;
  animation-delay: calc(var(--delay-word) + var(--w, 0) * var(--stagger));
}
.hero h1 em { position: relative; }
.hero h1 em::after {
  content: '';
  position: absolute;
  /* Raised clear of the next line's ascenders ('votre'): the accent word
     has no descenders, so the stroke can sit inside its own line box. */
  left: .02em; right: .02em; bottom: .15em;
  height: .08em;
  border-radius: var(--r-pill);
  background: var(--blue-500);
  opacity: .28;
  transform-origin: left center;
  animation: eyebrow-draw var(--dur-4) var(--ease-out) var(--delay-stroke) backwards;
}

.hero__frame::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--paper-2);
  transform-origin: right center;
  animation: curtain-off var(--dur-4) var(--ease-inout) var(--delay-frame) both;
  pointer-events: none;
}
.hero__img {
  transform: scale(var(--photo-rest, 1.03));
  animation: photo-settle var(--dur-settle) var(--ease-out) var(--delay-frame) backwards;
}
.hero__caption,
.sector-strip__list li {
  animation: hero-rise var(--dur-3) var(--ease-out) backwards;
  animation-delay: calc(var(--delay-strip) + var(--i, 0) * var(--stagger));
}

/* Sector strip: the icon lifts and a blue rule grows under the label. */
.sector-strip__list svg { transition: transform var(--dur-2) var(--ease-spring); }
.sector-strip__list a::after {
  content: '';
  position: absolute;
  left: 30%; right: 30%; bottom: 0;
  height: 2px;
  background: var(--blue-500);
  transform: scaleX(0);
  transition: transform var(--dur-2) var(--ease-out);
}
.sector-strip__list a:hover svg,
.sector-strip__list a:focus-visible svg { transform: translateY(-4px) scale(1.1); }
.sector-strip__list a:hover::after,
.sector-strip__list a:focus-visible::after { transform: scaleX(1); }

/* Reading progress (CSS scroll-driven animation, components.css). */
@keyframes progress-grow { to { transform: scaleX(1); } }

/* Header settles in from above on load. */
@keyframes header-in {
  from { transform: translateY(-12px); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
.site-header__inner { animation: header-in var(--dur-3) var(--ease-out) backwards; }

/* The primary CTA's arrow leans toward the form it leads to. */
.btn:hover .btn__icon--trail { transform: translateX(3px); }

/* ================================================= POINTER DEPTH (hero)
   Triple-gated: a fine pointer that can hover, a wide viewport, and no
   reduced-motion request. animation.js writes --px/--py (-1..1) and adds
   .is-live only under the same conditions, so on touch nothing applies.

   The frame tilts by at most 2.5deg; the photograph inside drifts the other
   way by a few pixels. It rests at scale 1.03, so the drift never exposes an
   edge. Depth, not a gimmick: it reads as a print catching the light. */

@media (hover: hover) and (pointer: fine) and (min-width: 64rem) {
  .hero__art.is-live { perspective: 1600px; }
  .hero__art.is-live .hero__frame {
    transform: rotateY(calc(var(--px) * 2.5deg)) rotateX(calc(var(--py) * -2deg));
    transition: transform var(--dur-3) var(--ease-out), box-shadow var(--dur-3) var(--ease-out);
  }
  .hero__art.is-live .hero__img {
    translate: calc(var(--px) * -6px) calc(var(--py) * -5px);
    transition: translate var(--dur-3) var(--ease-out);
  }
  .hero__art.is-hover .hero__frame { will-change: transform; }
}

/* ======================================================= SCROLL REVEALS
   animation.js adds `.reveal` (plus an optional variant) to groups, then
   `.is-in` once. The observer unobserves after firing, so nothing re-animates
   on scroll-back.

   IMPORTANT: `.reveal` is added BY JAVASCRIPT, never authored in the HTML.
   Without JS the elements simply have no `.reveal` class and are fully
   visible — content is never hidden behind a script. */

.reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity var(--dur-3) var(--ease-out),
              transform var(--dur-3) var(--ease-out);
  transition-delay: calc(var(--i, 0) * var(--stagger));
}
/* Cards rise slightly less and scale, so a grid does not read as a wave. */
.reveal--scale {
  transform: translateY(14px) scale(.975);
}
/* Table and wide media slide from the reading direction. */
.reveal--side {
  transform: translateX(-16px);
}
.reveal.is-in {
  opacity: 1;
  transform: none;
}

/* The hairline above a section CTA draws in rather than appearing. */
.section-cta {
  position: relative;
}
.section-cta.is-in::after {
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  height: 1px;
  background: var(--accent-text);
  opacity: .5;
  transform-origin: left center;
  animation: eyebrow-draw var(--dur-4) var(--ease-out) backwards;
}

/* ============================================================ MICRO-STATES */

/* Primary button: a light sweep on hover. Clipped to the pill, runs on
   transform only, and never on touch (no hover media = no rule). */
@media (hover: hover) and (pointer: fine) {
  .btn--primary, .btn--whatsapp { position: relative; overflow: hidden; }
  .btn--primary::after, .btn--whatsapp::after {
    content: '';
    position: absolute;
    inset: 0 auto 0 0;
    width: 40%;
    background: linear-gradient(90deg, transparent, var(--sheen), transparent);
    transform: translateX(-150%) skewX(-18deg);
    transition: transform var(--dur-4) var(--ease-out);
    pointer-events: none;
  }
  .btn--primary:hover::after, .btn--whatsapp:hover::after {
    transform: translateX(320%) skewX(-18deg);
  }
}

/* Product cards: the render eases in closer, the arrow slides toward the
   form, the card lifts. */
.product-card__visual img { transition: transform var(--dur-4) var(--ease-out); }
.product-card__cta .btn__icon { transition: transform var(--dur-2) var(--ease-out); }
.product-card { transition: transform var(--dur-2) var(--ease-out), box-shadow var(--dur-2) var(--ease-out); }
@media (hover: hover) and (pointer: fine) {
  .product-card:hover { transform: translateY(-4px); box-shadow: var(--sh-2); }
  .product-card:hover .product-card__visual img { transform: scale(1.045); }
  .product-card:hover .product-card__cta .btn__icon { transform: translateX(4px); }
}

/* CTA band: the hotel badge swings on its lanyard when it enters, damped to
   rest in ~1.6 s. Pivot = top centre, where the lanyard leaves the frame. */
@keyframes badge-swing {
  0%   { transform: rotate(-9deg); }
  22%  { transform: rotate(6deg); }
  44%  { transform: rotate(-3.2deg); }
  64%  { transform: rotate(1.6deg); }
  82%  { transform: rotate(-.6deg); }
  100% { transform: rotate(0); }
}
.cta-band__art img { transform-origin: 50% 0; }
.cta-band__art.is-in img { animation: badge-swing var(--dur-swing) var(--ease-inout) var(--delay-swing) both; }
@media (hover: hover) and (pointer: fine) {
  .cta-band__art img { transition: transform var(--dur-4) var(--ease-out); }
  .cta-band__art.is-in:hover img { animation: none; transform: rotate(-2.5deg); }
}

.industry-item__icon { transition: transform var(--dur-2) var(--ease-spring); }
.industry-item:hover .industry-item__icon { transform: scale(1.14) rotate(-3deg); }

/* Custom-item icons nudge toward the reading direction. */
.custom-item__icon { transition: transform var(--dur-2) var(--ease-spring); }
.custom-item:hover .custom-item__icon { transform: translateX(3px) scale(1.08); }

/* Step numerals fill on hover of their step. */
.step__num { transition: background-color var(--dur-2) var(--ease-inout),
                         color var(--dur-2) var(--ease-inout),
                         transform var(--dur-2) var(--ease-spring); }
.step:hover .step__num {
  background: var(--blue-500);
  color: var(--on-accent);
  transform: scale(1.06);
}

/* FAQ chevron. The panel itself is not height-animated: that would mean
   animating layout. An instant panel with an animated chevron is the honest
   trade (docs/MOTION-GUIDELINES.md). */
.faq-item__chev { transition: transform var(--dur-2) var(--ease-out); }
.faq-item[open] .faq-item__chev { transform: rotate(180deg); }
@keyframes faq-answer-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}
.faq-item[open] .faq-item__a {
  animation: faq-answer-in var(--dur-2) var(--ease-out);
}

/* Form feedback. Errors appear WITHOUT motion — an animated error delays a
   message the user needs immediately — but the field itself signals. */
.field__control { transition: border-color var(--dur-1) var(--ease-inout),
                              box-shadow var(--dur-1) var(--ease-inout); }
@keyframes panel-in {
  from { opacity: 0; transform: translateY(8px) scale(.99); }
  to   { opacity: 1; transform: none; }
}
.form-panel:not([hidden]) { animation: panel-in var(--dur-3) var(--ease-out); }
.form-summary:not([hidden]) { animation: panel-in var(--dur-2) var(--ease-out); }

/* Floating WhatsApp button entrance. */
@keyframes fab-in {
  from { opacity: 0; transform: translateY(24px) scale(.82); }
  to   { opacity: 1; transform: none; }
}
.sticky-contact:not([hidden])[data-suppressed='false'] {
  animation: fab-in var(--dur-3) var(--ease-spring);
}

/* Body scroll lock while the drawer is open. Set by navigation.js. */
body.has-drawer-open { overflow: hidden; }

/* Drawer links arrive in sequence once the panel is open. */
@keyframes drawer-link-in {
  from { opacity: 0; transform: translateX(14px); }
  to   { opacity: 1; transform: none; }
}
.nav-drawer[data-open='true'] .nav-drawer__link {
  animation: drawer-link-in var(--dur-2) var(--ease-out) backwards;
}
.nav-drawer[data-open='true'] .nav-drawer__link { animation-delay: calc(var(--delay-word) + var(--i, 0) * var(--step-drawer)); }

/* ====================================================== SECTION DETAILS */

/* Section eyebrows draw their leading rule as the heading arrives. */
.section-head.reveal .eyebrow::before {
  transform-origin: left center;
  transform: scaleX(0);
  transition: transform var(--dur-4) var(--ease-out) var(--delay-rule);
}
.section-head.reveal.is-in .eyebrow::before { transform: scaleX(1); }

/* Industry tiles: the icon chip fills with the accent on hover. */
.industry-item__icon { transition: transform var(--dur-2) var(--ease-spring), background-color var(--dur-2) var(--ease-inout), color var(--dur-2) var(--ease-inout); }
.industry-item:hover .industry-item__icon { background: var(--blue-600); color: var(--on-accent); }

/* Comparison sheets: the yes-ticks pop once their sheet is in view. */
@keyframes tick-pop {
  0%   { transform: scale(0); }
  70%  { transform: scale(1.25); }
  100% { transform: scale(1); }
}
.compare__card.reveal.is-in .compare__icon--yes {
  animation: tick-pop var(--dur-3) var(--ease-spring) backwards;
  animation-delay: calc(var(--delay-tick) + var(--i, 0) * var(--stagger));
}
.compare__card { transition: box-shadow var(--dur-2) var(--ease-out), transform var(--dur-2) var(--ease-out); }
@media (hover: hover) and (pointer: fine) {
  .compare__card:hover { transform: translateY(-4px); box-shadow: var(--sh-2); }
}

/* Process steps: numerals pop in sequence as the steps arrive. */
@keyframes num-pop {
  from { transform: scale(.4); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
.step.reveal.is-in .step__num {
  animation: num-pop var(--dur-3) var(--ease-spring) backwards;
  animation-delay: calc(var(--step-num) + var(--i, 0) * var(--step-num));
}

/* Floating WhatsApp: two soft halos the first time it appears, then calm. */
@keyframes fab-halo {
  from { transform: scale(1); opacity: .45; }
  to   { transform: scale(1.7); opacity: 0; }
}
.fab { position: relative; }
.sticky-contact[data-suppressed='false'] .fab::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--wa-green);
  z-index: -1;
  animation: fab-halo var(--dur-halo) var(--ease-out) var(--delay-halo) 2 both;
  pointer-events: none;
}

/* ======================================================== REDUCED MOTION
   base.css already neutralises durations globally. These rules additionally
   remove the STARTING states, so nothing can be left invisible or displaced
   if an animation never runs. */

@media (prefers-reduced-motion: reduce) {
  .hero__copy > *,
  .hero__caption,
  .hero h1 .w,
  .hero h1 em::after,
  .sector-strip__list li,
  .site-header__inner,
  .compare__card .compare__icon--yes,
  .step .step__num,
  .hero__img,
  .nav-drawer__link,
  .form-panel,
  .form-summary,
  .faq-item[open] .faq-item__a,
  .section-cta.is-in::after {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
  /* The floating button only loses its ENTRANCE animation. It must keep
     obeying data-suppressed, or reduced-motion users would get a button
     parked over the quote form's submit control. A blanket
     `opacity:1 !important` here previously defeated exactly that. */
  .sticky-contact { animation: none !important; transition: none !important; }
  .sticky-contact[data-suppressed='false'] { opacity: 1; transform: none; }
  .sticky-contact[data-suppressed='true'] { opacity: 0; transform: none; pointer-events: none; }

  .section-head .eyebrow::before { transform: scaleX(1) !important; transition: none !important; }
  .sticky-contact .fab::before { display: none !important; }
  .scroll-progress { display: none !important; }
  .sector-strip__list a:hover svg, .sector-strip__list a:focus-visible svg,
  .compare__card:hover { transform: none !important; }
  /* The curtain is removed outright, never left frozen over the product. */
  .hero__frame::after { display: none !important; }
  .btn--primary::after, .btn--whatsapp::after { display: none !important; }

  .reveal, .reveal.is-in, .reveal--scale, .reveal--side {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  .hero__art .hero__frame {
    transform: none !important;
    will-change: auto !important;
  }
  .hero__art .hero__img { translate: none !important; }
  .product-card:hover, .product-card:hover .product-card__visual img, .product-card:hover .product-card__cta .btn__icon,
  .cta-band__art img, .cta-band__art.is-in img, .cta-band__art.is-in:hover img,
  .industry-item:hover .industry-item__icon,
  .custom-item:hover .custom-item__icon,
  .step:hover .step__num,
  .btn:hover, .btn:active, .card:hover, .fab:hover, .fab:active {
    transform: none !important;
  }
}
