/* ==========================================================================
   CYCSPAC — animation layer
   --------------------------------------------------------------------------
   Contract with assets/animations.js:

   1. PRE-HIDE lives here, scoped to `html.anim-ready .sel:not(.is-revealed)`.
      `anim-ready` is added by a synchronous inline boot script in each page's
      <head>, and only when motion is allowed. No JS / no class / expired
      guard  =>  everything renders normally. Content is never permanently
      hidden because of a script failure.

   2. animations.js tweens those same selectors to their natural state, then
      adds `.is-revealed` and clears its own inline styles.

   Why clearProps + is-revealed: an inline transform left on an element would
   permanently outrank the site's existing `:hover { transform: ... }` rules
   (.button, .system-card, .application-list a, .scenario-preview-grid a img).
   Removing the inline styles AND de-scoping the pre-hide keeps hover intact.

   Consequence: always animate TO a value with gsap.to(). Never gsap.from() —
   the "current" value a from-tween captures here would be the hidden one.
   ========================================================================== */

/* --- scroll progress rail ------------------------------------------------ */
.anim-progress {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 300;
  width: 100%;
  height: 2px;
  background: linear-gradient(90deg, var(--signal, #0369a1), var(--survey, #0891b2));
  transform: scaleX(0);
  transform-origin: 0 50%;
  pointer-events: none;
}

/* --- page transition ------------------------------------------------------
   EXIT uses the .anim-curtain div created by animations.js.
   ENTRANCE is a pure-CSS sweep that lifts on its own timer: it never depends
   on a script running, so a blocked or failing animations.js can leave the
   page briefly un-styled but can never leave it covered. */
.anim-ready body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 400;
  pointer-events: none;
  background: linear-gradient(180deg, var(--ink, #0f172a), #102a3d);
  transform-origin: 50% 0;
  animation: anim-veil-lift 0.52s cubic-bezier(0.16, 1, 0.3, 1) 0.05s forwards;
}
@keyframes anim-veil-lift {
  to {
    transform: scaleY(0);
  }
}

.anim-curtain {
  position: fixed;
  inset: 0;
  z-index: 400;
  background: var(--ink, #0f172a);
  transform: scaleY(0);
  transform-origin: 50% 0;
  pointer-events: none;
}

/* --- header condense on scroll ------------------------------------------- */
.site-header {
  transition: background-color 0.35s var(--ease, ease), box-shadow 0.35s ease;
}
.site-header.is-condensed {
  background: rgba(244, 247, 249, 0.97);
  box-shadow: 0 10px 30px rgba(15, 23, 42, 0.09);
}
.site-header.is-condensed .nav-shell {
  min-height: 62px;
}
.nav-shell {
  transition: min-height 0.35s var(--ease, ease);
}

/* nav underline sweep — ::after is unused on these two selectors */
.primary-nav > a:not(.button),
.nav-dropdown-toggle {
  position: relative;
}
.primary-nav > a:not(.button)::after,
.nav-dropdown-toggle::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -6px;
  width: 100%;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 0.32s var(--ease, ease);
}
.primary-nav > a:not(.button):hover::after,
.primary-nav > a[aria-current="page"]::after,
.nav-dropdown-toggle:hover::after,
.nav-dropdown-toggle[aria-expanded="true"]::after {
  transform: scaleX(1);
}

/* --- button shine (CSS only; existing translateY lift untouched) ---------
   Scoped to .anim-ready rather than :not(.is-revealed): a decorative pseudo
   element never hides content, so it must survive the reveal. */
.anim-ready .button {
  position: relative;
  overflow: hidden;
  transition: transform 0.28s var(--ease, ease), background-color 0.28s ease,
    border-color 0.28s ease, box-shadow 0.28s ease;
}
.anim-ready .button::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 45%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, 0.32), transparent);
  transform: translateX(-160%);
  transition: transform 0.62s var(--ease, ease);
  pointer-events: none;
}
.anim-ready .button:hover::before {
  transform: translateX(320%);
}

/* --- card spotlight follows the pointer (custom props set by JS) ----------
   .hero-photo-frame and .scenario-preview-grid a already own ::after in the
   base stylesheet, so they are deliberately excluded here. */
.anim-ready .system-card,
.anim-ready .route-card,
.anim-ready .series-product {
  position: relative;
}
.anim-ready .system-card::after,
.anim-ready .route-card::after,
.anim-ready .series-product::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: 0;
  background: radial-gradient(420px circle at var(--mx, 50%) var(--my, 50%),
      rgba(8, 145, 178, 0.13), transparent 62%);
  transition: opacity 0.4s ease;
}
.anim-ready .system-card:hover::after,
.anim-ready .route-card:hover::after,
.anim-ready .series-product:hover::after {
  opacity: 1;
}

/* ==========================================================================
   PRE-PAINT FROM-STATE
   Keep this list in sync with the REVEAL_GROUPS array in animations.js.
   Nothing may be hidden here unless animations.js reveals it.
   ========================================================================== */

/* hero + inner hero
   IMPORTANT: every intro-owned selector must stay scoped inside `.hero` or
   `.inner-hero`. The first-screen timeline only runs when one of those exists,
   so a bare `.hero-actions` would also match the CTA rows on pages without a
   hero (products.html, 404.html) and hide them permanently. */
.anim-ready .hero .market-line:not(.is-revealed),
.anim-ready .hero .hero-lede:not(.is-revealed),
.anim-ready .hero .hero-actions:not(.is-revealed),
.anim-ready .hero .hero-facts > div:not(.is-revealed),
.anim-ready .hero .visual-toolbar:not(.is-revealed),
.anim-ready .hero .visual-note:not(.is-revealed),
.anim-ready .inner-hero .market-line:not(.is-revealed),
.anim-ready .inner-hero .hero-actions:not(.is-revealed),
.anim-ready .inner-hero p:not(.market-line):not(.is-revealed),
.anim-ready .quality-stage-rail a:not(.is-revealed) {
  opacity: 0;
  transform: translateY(18px);
}
.anim-ready .hero h1:not(.is-revealed),
.anim-ready .inner-hero h1:not(.is-revealed) {
  opacity: 0;
}

/* media frames reveal with a clip wipe; inner image settles from a slight zoom */
.anim-ready .hero .hero-photo-frame:not(.is-revealed),
.anim-ready .inner-hero figure:not(.is-revealed),
.anim-ready .media-frame:not(.is-revealed),
.anim-ready .route-card-media:not(.is-revealed),
.anim-ready .media-explainer figure:not(.is-revealed),
.anim-ready .quality-strip figure:not(.is-revealed),
.anim-ready .scenario-preview-grid a:not(.is-revealed) {
  clip-path: inset(0 0 100% 0);
}
.anim-ready .hero .hero-photo:not(.is-revealed),
.anim-ready .inner-hero figure img:not(.is-revealed),
.anim-ready .scenario-preview-grid a img:not(.is-revealed),
.anim-ready .quality-strip figure img:not(.is-revealed),
.anim-ready .media-frame img:not(.is-revealed),
.anim-ready .route-card-media img:not(.is-revealed),
.anim-ready .media-explainer figure img:not(.is-revealed) {
  transform: scale(1.085);
}

/* section headings and prose blocks */
.anim-ready .section-heading:not(.is-revealed),
.anim-ready .configuration-head:not(.is-revealed),
.anim-ready .evidence-title-block:not(.is-revealed),
.anim-ready .application-copy:not(.is-revealed),
.anim-ready .brief-intro:not(.is-revealed),
.anim-ready .decision-band:not(.is-revealed),
.anim-ready .next-system:not(.is-revealed),
.anim-ready .about-story > *:not(.is-revealed),
.anim-ready .evidence-map:not(.is-revealed),
.anim-ready .media-source-note:not(.is-revealed),
.anim-ready .table-note:not(.is-revealed) {
  opacity: 0;
  transform: translateY(26px);
}

/* interactive panels */
.anim-ready .selector fieldset:not(.is-revealed),
.anim-ready #recommendation-result:not(.is-revealed) {
  opacity: 0;
  transform: translateY(22px);
}

/* card and list groups — children stagger, containers stay put */
.anim-ready .product-line-showcase:not(.is-revealed),
.anim-ready .system-card:not(.is-revealed),
.anim-ready .delivery-solution-card:not(.is-revealed),
.anim-ready .route-card:not(.is-revealed),
.anim-ready .series-product:not(.is-revealed),
.anim-ready .principles-grid article:not(.is-revealed),
.anim-ready .resource-list article:not(.is-revealed),
.anim-ready .application-detail-list article:not(.is-revealed),
.anim-ready .scenario-grid article:not(.is-revealed),
.anim-ready .engineering-split > *:not(.is-revealed),
.anim-ready .application-list a:not(.is-revealed),
.anim-ready .process-track li:not(.is-revealed),
.anim-ready .configuration-map li:not(.is-revealed),
.anim-ready .evidence-drawers details:not(.is-revealed),
.anim-ready .quality-process-grid figure:not(.is-revealed) {
  opacity: 0;
  transform: translateY(30px);
}
.anim-ready .config-core:not(.is-revealed) {
  opacity: 0;
  transform: scale(0.82);
}

.anim-ready .compare-table tbody tr:not(.is-revealed) {
  opacity: 0;
  transform: translateY(14px);
}

/* GSAP writes per frame; suppress CSS transitions while a tween is running */
.anim-ready .is-animating {
  transition: none !important;
}

/* ==========================================================================
   REDUCED MOTION — the boot script never adds .anim-ready, and this
   belt-and-braces block guarantees nothing can stay hidden if it somehow does.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  html.anim-ready *,
  html.anim-ready {
    opacity: 1 !important;
    transform: none !important;
    clip-path: none !important;
  }
  .anim-progress,
  .anim-curtain {
    display: none !important;
  }
}

/* Safety net: `anim-ready` is added by a synchronous script, so if the deferred
   animations.js never runs (blocked, 404, syntax error) this rule un-hides
   everything after 1.6s. Content is never permanently invisible. */
@keyframes anim-boot-failsafe {
  to {
    --anim-failsafe: 1;
  }
}
html.anim-ready:not(.anim-booted) {
  animation: anim-boot-failsafe 0s linear 1.6s forwards;
}
html.anim-ready:not(.anim-booted) .market-line,
html.anim-ready:not(.anim-booted) .hero-lede,
html.anim-ready:not(.anim-booted) .hero-actions,
html.anim-ready:not(.anim-booted) .hero-facts > div,
html.anim-ready:not(.anim-booted) .visual-toolbar,
html.anim-ready:not(.anim-booted) .visual-note,
html.anim-ready:not(.anim-booted) h1,
html.anim-ready:not(.anim-booted) p,
html.anim-ready:not(.anim-booted) figure,
html.anim-ready:not(.anim-booted) img,
html.anim-ready:not(.anim-booted) a,
html.anim-ready:not(.anim-booted) li,
html.anim-ready:not(.anim-booted) tr,
html.anim-ready:not(.anim-booted) details,
html.anim-ready:not(.anim-booted) article,
html.anim-ready:not(.anim-booted) section > *,
html.anim-ready:not(.anim-booted) fieldset,
html.anim-ready:not(.anim-booted) .config-core,
html.anim-ready:not(.anim-booted) .section-heading,
html.anim-ready:not(.anim-booted) .media-source-note,
html.anim-ready:not(.anim-booted) .table-note {
  opacity: 1;
  transform: none;
  clip-path: none;
}

/* ==========================================================================
   REDUCED MOTION — the boot script never adds .anim-ready, and this
   belt-and-braces block guarantees nothing can stay hidden if it somehow does.
   Only the pre-hide is neutralised; the site's own colour transitions survive.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  html.anim-ready *,
  html.anim-ready {
    opacity: 1 !important;
    transform: none !important;
    clip-path: none !important;
    animation: none !important;
  }
  .anim-progress,
  .anim-curtain {
    display: none !important;
  }
}
