/* TITECH v3 · motion.css: owned by the work order listed in work-orders/DESIGN-SPEC.md §1. */
/* Scroll motion (DESIGN-SPEC §7). motion.js adds every hidden state below as a class, so with no JS,
   or after any JS error, none of them apply and all content shows. The hidden states also exist only
   inside `screen and (prefers-reduced-motion: no-preference)`: in print, under reduced motion, or in a
   browser that does not know the feature, the rules are absent and everything is visible.

   The rise uses the individual `translate` property, not `transform`: it composes with card hover
   transforms (components.css) and with the legacy `transform: none !important` overrides instead of
   competing with them, and a stagger delay on `translate` can never delay a hover. */

/* 1. Legacy .reveal: site.js decides when an element appears, these rules decide how it looks. */
.reveal {
  transition:
    opacity var(--dur-reveal) var(--ease-out) var(--m-delay, 0ms),
    translate var(--dur-reveal) var(--ease-out) var(--m-delay, 0ms),
    filter var(--dur-reveal) var(--ease-out) var(--m-delay, 0ms),
    /* .reveal sits on the cards themselves, so their hover states keep a quick, separate timing */
    transform var(--dur-fast) var(--ease-out),
    border-color var(--dur-hover) var(--ease-hover),
    background-color var(--dur-hover) var(--ease-hover),
    box-shadow var(--dur-hover) var(--ease-hover);
}

/* 2. Auto-reveal. motion.js: .m-pending (hidden) then .m-in (running), and both are removed once the
      transition ends, so the element goes back to its own hover and focus transitions. */
.m-pending { transition: none; }
.m-photo.m-pending img { transition: none; }
.m-in:not(.m-mask, .m-photo) {
  transition:
    opacity var(--dur-reveal) var(--ease-out) var(--m-delay, 0ms),
    translate var(--dur-reveal) var(--ease-out) var(--m-delay, 0ms),
    filter var(--dur-reveal) var(--ease-out) var(--m-delay, 0ms);
  will-change: opacity, translate, filter;
}
.m-mask.m-in {
  transition:
    clip-path var(--dur-reveal) var(--ease-out) var(--m-delay, 0ms),
    translate var(--dur-reveal) var(--ease-out) var(--m-delay, 0ms);
  will-change: clip-path, translate;
}
.m-photo.m-in {
  transition: clip-path var(--dur-image) var(--ease-out) var(--m-delay, 0ms);
  will-change: clip-path;
}
.m-photo.m-in img {
  transition: scale var(--dur-image) var(--ease-out) var(--m-delay, 0ms);
  will-change: scale;
}

/* The hidden states and their end states. */
@media screen and (prefers-reduced-motion: no-preference) {
  /* `transform: none` replaces the legacy translateY(18px) so the two offsets never add up. */
  .reveal.is-pending { opacity: 0; transform: none; translate: 0 var(--rise); filter: blur(6px); }
  .m-pending:not(.m-mask, .m-photo) { opacity: 0; translate: 0 var(--rise); filter: blur(6px); }

  /* Heading mask: the line rises out of a clip. The negative bottom inset keeps descenders and Arabic
     marks: -0.2em still cut the deepest Arabic descender at 768 to 1440px, -0.3em cuts nothing. */
  .m-mask.m-pending { clip-path: inset(0 0 100% 0); translate: 0 .35em; }
  .m-mask.m-in { clip-path: inset(0 0 -0.3em 0); }

  /* Image reveal: the wrapper opens while the photo settles from a slight zoom. */
  .m-photo.m-pending { clip-path: inset(6% 6% 6% 6% round var(--r-lg)); }
  .m-photo.m-pending img { scale: 1.08; }
  .m-photo.m-in { clip-path: inset(0 round var(--r-lg)); }
  .m-photo.m-in img { scale: 1; }
}

/* 3. Counters ([data-count]). Tabular figures keep the width steady while digits change. */
[data-count] { font-variant-numeric: tabular-nums; }

/* 4. Scroll progress: one 2px bar above the header (.site-header is z-index 100). */
.m-progress {
  position: fixed;
  inset-block-start: var(--wp-adminbar-offset, 0px);
  inset-inline: 0;
  z-index: 101;
  height: 2px;
  background: var(--g-spectrum);
  transform: scaleX(0);
  transform-origin: left center;
  pointer-events: none;
}
[dir="rtl"] .m-progress { transform-origin: right center; }

/* 5. Timeline draw. The legacy rule is a border: the top edge of each item on desktop, the inline-start
      edge of .timeline in the stacked layout. While .m-draw is set the border goes transparent (same
      box, so nothing shifts) and this line, driven by --m-progress (0 to 1), draws in its place. */
.timeline.m-draw { position: relative; }
.timeline.m-draw .timeline-item { border-block-start-color: transparent; }
.timeline.m-draw::before {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  height: 1px;
  background: var(--ctx-line-strong);
  transform: scaleX(var(--m-progress, 0));
  transform-origin: left center;
  pointer-events: none;
}
[dir="rtl"] .timeline.m-draw::before { transform-origin: right center; }
@media (max-width: 899px) {
  .timeline.m-draw { border-inline-start-color: transparent; }
  .timeline.m-draw::before,
  [dir="rtl"] .timeline.m-draw::before {
    inset-block: 0;
    inset-inline: -1px auto;
    width: 1px;
    height: auto;
    transform: scaleY(var(--m-progress, 0));
    transform-origin: center top;
  }
}

/* 6. Marquee. Markup: <div class="m-marquee"><div class="m-marquee__track">…items…</div></div>.
      motion.js clones the items once, so the track is two identical halves and moving it by -50% loops
      seamlessly. Each item carries its own trailing gap (a flex gap would leave the halves uneven).
      The original items must be at least as wide as the container. */
.m-marquee {
  --m-marquee-gap: var(--space-block);
  --m-dir: 1;
  overflow: hidden;
  overflow: clip;
}
[dir="rtl"] .m-marquee { --m-dir: -1; }
.m-marquee__track {
  display: flex;
  align-items: center;
  width: max-content;
  animation: m-marquee 40s linear infinite;
}
.m-marquee__track > * { flex: none; margin-inline-end: var(--m-marquee-gap); }
.m-marquee:is(:hover, :focus-within) .m-marquee__track { animation-play-state: paused; }
@keyframes m-marquee {
  to { transform: translateX(calc(var(--m-dir) * -50%)); }
}

/* 7. Reduced motion and print: nothing moves. The marquee becomes a static wrapped row, the timeline
      rule shows fully drawn, and the progress bar is hidden. */
@media (prefers-reduced-motion: reduce), print {
  .m-progress { display: none; }
  .timeline.m-draw::before,
  [dir="rtl"] .timeline.m-draw::before { transform: none; }
  .m-marquee__track { width: auto; flex-wrap: wrap; row-gap: calc(var(--m-marquee-gap) / 2); animation: none; }
}

/* 8. Ambient glow (owner, 2026-09-22): on the navy, a bright object lights the background around it in its own colour,
      "as if it's shining on the background". Subtle: the navy is lit, there is no halo. All of it is static, so it stays
      under reduced motion; only the photos' hover brightening goes there.

   Photos. Only photo frames in dark contexts are marked; motion.js gives a marked frame its loaded image as --m-glow-src
   and the class .m-glow. Behind the photo sits one blurred, saturated copy of it that reaches about 9% past the frame,
   so the spill around the edges is always the photo's own colours. It is screened onto the navy, so it adds light rather
   than laying a grey veil, and it sits under the section's hairlines and grain, like light on the background itself.
   For that the frame gives up its own isolation (surfaces.css: its grade then multiplies over the opaque photo alone,
   as before) and its clipping (overflow: hidden would cut the spill off; the photo and its grade round their own corners
   instead). Its box doesn't change: the copy is absolutely placed, and min-height: 0 keeps an unclipped aspect-ratio
   frame from growing to its photo's natural height. */
:where(.page-hero, .solutions-section, .section:has(.industry-list), .cta-band, .site-footer, .section-navy)
  :where(.industry-image, .solution-image, .page-hero-visual, .feature-image, .industry-overview-image) {
  --m-glow: 1;
}
.m-glow { overflow: visible; isolation: auto; }
.m-glow:is(.industry-image, .page-hero-visual) { min-width: 0; min-height: 0; }
.m-glow > img, .m-glow > picture, .m-glow > picture > img, .m-glow::after { border-radius: inherit; }
.m-glow::before {
  content: "";
  position: absolute;
  inset: -9%;
  z-index: -1; /* in the dark section's stacking context (surfaces.css isolates it): above its navy, under its content */
  background: var(--m-glow-src) center / cover no-repeat;
  filter: blur(40px) saturate(1.6);
  mix-blend-mode: screen;
  opacity: .32; /* screened, .32 lights the navy about as much as the brief's .35 to .45 copy laid over it would */
  pointer-events: none;
  transition: opacity var(--dur-hover) var(--ease-hover);
}
/* The ICT/IVMS line art is shown whole and mirrors in Arabic (extras.css), so its light does the same. */
.m-glow.page-hero-visual--art::before { background-size: contain; }
:is([dir="rtl"], html[lang^="ar"]) .m-glow.page-hero-visual--art::before { transform: scaleX(-1); }
/* While a photo is still revealing (motion.js, section 2) its frame is clip-pathed, so the light waits and then fades in. */
.m-glow:is(.m-pending, .m-in)::before { opacity: 0; }
@starting-style {
  .m-glow::before { opacity: 0; }
}
@media (max-width: 600px) {
  .m-glow::before { filter: blur(24px) saturate(1.6); }
}
/* The light rises a little with the photo's own hover (the industry rows brighten their photo too). Keyboard focus in the
   row lights it the same way. .38 is the ceiling: brighter, the spill under the hovered row title (--c-signal-soft) takes
   it under 4.5:1. */
@media (prefers-reduced-motion: no-preference) {
  .m-glow:hover::before,
  :is(.industry-row, .solution-row, .service-card, .capability-card, [data-spotlight]):focus-within .m-glow::before { opacity: .38; }
}
@media (prefers-reduced-motion: no-preference) and (hover: hover) {
  :is(.industry-row, .solution-row, .service-card, .capability-card, [data-spotlight]):hover .m-glow::before { opacity: .38; }
}

/* Mono white logos on the navy (the dark featured tiles, the footer logo): a soft white light around them, a 16px spread at
   .22 plus a tight 5px core, since thin mono strokes alone gather too little light to show. The tile logos are masks, and a
   mask clips its own element's filter (filter runs first), so the light goes on the row that holds them. The Featured
   Expertise hub's navy deep dives (WO-20, .xp-logos) carry the tiles' logo rows, so they are lit the same way. */
.featured-tile--dark :is(.featured-tile__logo, .featured-tile__logos),
.section-navy .xp-logos {
  filter: drop-shadow(0 0 5px rgb(255 255 255 / .16)) drop-shadow(0 0 16px rgb(255 255 255 / .22));
}

/* Inline SVG art on the page-hero navy (the orbit drawing): a soft light in the signal tone. The featured pages' own hero
   drawings (.fp-hero-visual) already carry theirs (pages.css). */
.page-hero .page-hero-visual > svg {
  filter: drop-shadow(0 0 16px rgb(127 176 255 / .18));
}

@media print, (forced-colors: active) {
  .m-glow::before { content: none; }
  .featured-tile--dark :is(.featured-tile__logo, .featured-tile__logos),
  .section-navy .xp-logos,
  .page-hero .page-hero-visual > svg { filter: none; }
}
