/* ==========================================================================
   animations — every @keyframes, the scroll-reveal system, and the single
   consolidated prefers-reduced-motion block.

   This file is loaded LAST. The reduced-motion block at the bottom has to
   sit after every rule it turns off, several of which (.grain-layer,
   .marquee-track, html{scroll-behavior}) are switched off without
   !important and would otherwise lose to their component stylesheet.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Keyframes
   -------------------------------------------------------------------------- */

@keyframes driftScenery {
  from { background-position-x: 0; }
  to { background-position-x: -6386px; }
}

/* Retained after the intro splash was removed: .globe-marker still uses it
   for the pulsing Namibia dot on the facts globe. */
@keyframes splashPulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.55; }
}

@keyframes grainShift {
  0%   { transform: translate(0, 0); }
  25%  { transform: translate(-4%, 3%); }
  50%  { transform: translate(3%, -3%); }
  75%  { transform: translate(-3%, -4%); }
  100% { transform: translate(0, 0); }
}

@keyframes marqueeScroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

/* --------------------------------------------------------------------------
   Scroll-reveal — content fades and rises into place as you scroll to it.

   Everything that starts hidden is gated behind .js-reveal on <html>.
   Only index.html carries that class, because only index.html loads the
   JavaScript that ever adds .revealed. Without the gate these rules also
   matched the nested <h2>s on privacy.html and terms.html, which have no
   script to reveal them and so rendered permanently invisible.

   The gate is a plain class in the markup rather than something main.js
   adds, so there is no window between first paint and script execution in
   which the content flashes visible and then hides.

   NOTE ON SPECIFICITY: every selector in the hiding rule must weigh no more
   than the .revealed rule below it, or it wins on specificity and the element
   stays hidden forever no matter what class JavaScript adds.

     hiding  .js-reveal section.block .label   -> (0,3,1)  ties, later wins
     hiding  .js-reveal section.block h2       -> (0,2,2)  lower, loses
     reveal  .js-reveal section.block .revealed-> (0,3,1)

   This is exactly what `h3.subhead` got wrong: at (0,3,2) it outranked the
   reveal rule, so "Destinations", "Explore the Map", "Freshwater" and
   "Saltwater" received .revealed and stayed at opacity 0 regardless. The
   visible symptom was the .subhead-note lines below them rendering as bare,
   unheaded text.

   Writing it as `.subhead` instead of `h3.subhead` brings it to (0,3,1), in
   line with every sibling. It selects the same elements — .subhead is only
   ever used on those four h3s, and it does not match .subhead-note, which is
   a different class token. Audited: all nine groups in this system now reach
   opacity 1 once revealed.
   -------------------------------------------------------------------------- */

.js-reveal section.block .label,
.js-reveal section.block h2,
.js-reveal section.block .subhead,
.js-reveal section.block .body-text,
.js-reveal section.block .team-list,
.js-reveal section.block .namibia-map-wrap,
.js-reveal section.block .contact-grid {
  opacity: 0;
  transform: translateY(26px);
  transition: opacity 0.8s cubic-bezier(0.16, 1, 0.3, 1), transform 0.8s cubic-bezier(0.16, 1, 0.3, 1);
}
.js-reveal section.block .revealed {
  opacity: 1;
  transform: translateY(0);
}

/* Photo grids cascade in tile by tile rather than fading as one flat block */
.js-reveal section.block .photo-grid .photo-tile {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1), transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}
.js-reveal section.block .photo-grid.revealed .photo-tile {
  opacity: 1;
  transform: translateY(0);
}

/* Per-tile stagger, 45ms apart. Kept as explicit nth-child rules rather than
   folded into a custom property: the largest grid holds 27 tiles but only the
   first 16 were ever given a delay, so tiles 17+ animate with no delay at all.
   A single var-driven rule would have to reproduce that cut-off anyway, and
   this way the timing is provably unchanged. */
.js-reveal section.block .photo-grid.revealed .photo-tile:nth-child(1) { transition-delay: 0.045s; }
.js-reveal section.block .photo-grid.revealed .photo-tile:nth-child(2) { transition-delay: 0.09s; }
.js-reveal section.block .photo-grid.revealed .photo-tile:nth-child(3) { transition-delay: 0.135s; }
.js-reveal section.block .photo-grid.revealed .photo-tile:nth-child(4) { transition-delay: 0.18s; }
.js-reveal section.block .photo-grid.revealed .photo-tile:nth-child(5) { transition-delay: 0.225s; }
.js-reveal section.block .photo-grid.revealed .photo-tile:nth-child(6) { transition-delay: 0.27s; }
.js-reveal section.block .photo-grid.revealed .photo-tile:nth-child(7) { transition-delay: 0.315s; }
.js-reveal section.block .photo-grid.revealed .photo-tile:nth-child(8) { transition-delay: 0.36s; }
.js-reveal section.block .photo-grid.revealed .photo-tile:nth-child(9) { transition-delay: 0.405s; }
.js-reveal section.block .photo-grid.revealed .photo-tile:nth-child(10) { transition-delay: 0.45s; }
.js-reveal section.block .photo-grid.revealed .photo-tile:nth-child(11) { transition-delay: 0.495s; }
.js-reveal section.block .photo-grid.revealed .photo-tile:nth-child(12) { transition-delay: 0.54s; }
.js-reveal section.block .photo-grid.revealed .photo-tile:nth-child(13) { transition-delay: 0.585s; }
.js-reveal section.block .photo-grid.revealed .photo-tile:nth-child(14) { transition-delay: 0.63s; }
.js-reveal section.block .photo-grid.revealed .photo-tile:nth-child(15) { transition-delay: 0.675s; }
.js-reveal section.block .photo-grid.revealed .photo-tile:nth-child(16) { transition-delay: 0.72s; }

/* Animated accent underline on section labels as they reveal.
   Not gated: these only draw a line, they never hide content. */
section.block .label {
  position: relative;
  display: inline-flex;
  padding-bottom: 4px;
}
section.block .label::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  height: 1px;
  width: 0;
  background: var(--rust);
  transition: width 0.8s ease 0.3s;
}
section.block .label.revealed::after {
  width: 100%;
}

/* --------------------------------------------------------------------------
   Reduced motion — one block, consolidating the six that were scattered
   through the original stylesheet (at lines 510, 796, 859, 950, 1112 and
   1246). Source order within the block is preserved.

   `* { transition: none !important }` stays first, as it did originally.
   The two later `transition: none !important` rules below it declare the
   same value at higher specificity, so the outcome is identical either way.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { transition: none !important; }
  .scenery-layer::before { animation: none !important; }

  .topo-bg { transition: none !important; }

  section.block .label,
  section.block h2,
  section.block .subhead,
  section.block .body-text,
  section.block .photo-grid,
  section.block .team-list,
  section.block .namibia-map-wrap,
  section.block .contact-grid {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }

  .grain-layer { animation: none; }

  .marquee-track { animation: none; }

  /* The globe's Namibia marker pulses on splashPulse. It was the one infinite
     animation this block did not switch off, so it kept pulsing for a visitor
     who had asked for reduced motion. animations.css loads last and the
     specificity matches, so a plain declaration wins on order — same form as
     the two rules above. */
  .globe-marker { animation: none; }
}
