/* =========================================================
   DOWNLOADS-ENTRANCE.CSS
   The scroll entrance for the content below the hero on the
   downloads page: the search band, the two section heads with
   their brand rules, the seven category rows and the document
   rows.

   The language here is deliberately quieter than the projects
   gallery's. This is a working list of documents, so there is
   no mask reveal, no scale, no shadow and no card movement --
   only a short rise and fade, group by group, so the list reads
   as being set out in order rather than performed.

   ANIMATION ONLY -- opacity and transform, neither of which
   affects the box an element occupies. No layout, spacing,
   typography, colour, icon, content or structure is changed,
   and the hero entrance and the page transition are untouched.

   ---------------------------------------------------------
   THE HOVER RULE
   A `transition` declaration replaces any earlier one on the
   same element, so nothing here declares one on an element that
   already has one:

     .dl-list__item   has `transition: border-color` for its
     .dl-doc          hover (interactions.css) -- NEITHER row is
                      touched. Their CHILDREN carry the entrance
                      instead, all on ONE shared delay, so the
                      name, the type, the icon, the file details
                      and the download link arrive as a single
                      unit rather than five animations.
     .dl-doc__get-icon,
     .dl-list__arrow  carry their own hover transitions -- NOT
                      touched. Only .dl-doc__get, the link around
                      the icon, fades.
     .dl-search__field  keeps its :focus-within border exactly as
                      it is; the entrance rides on the wrapper
                      around it.

   THE DOTTED RULES BETWEEN ROWS ARE LEFT ALONE, on purpose. They
   are `1px dotted`, and a scaleX reveal stretches a dotted
   border -- the dots smear and the spacing changes as it draws.
   Leaving them in place also reads correctly here: the list's
   structure is already set out and the documents fill into it.

   SAFETY: nothing is hidden until js/downloads-entrance.js adds
   .is-armed. If that script is missing or fails, no rule here
   matches and the whole page is simply there from the first
   paint. Every armed state is scoped to `:not(.is-in)`, so once
   a group is revealed it carries no entrance declaration at all.
   ========================================================= */

:root {
  /* The same curve as every other entrance on the site,
     restated so the files stay independent of load order. */
  --dlx-ease: cubic-bezier(0.40, 0, 0.20, 1);

  --dlx-head: 600ms;
  --dlx-red: 170ms;
  --dlx-at-red: 100ms;
  --dlx-blue: 320ms;
  --dlx-at-blue: 260ms;
  --dlx-row: 500ms;
  --dlx-step: 70ms;     /* between neighbouring rows */
  --dlx-filters: 520ms;
  --dlx-refilter: 220ms;/* a row returning after a search */
}

/* ---------------------------------------------------------
   1 — THE SEARCH BAND
   First entry only. Its own behaviour, including the focus
   underline, is untouched.
   --------------------------------------------------------- */
.dl-search__inner.is-armed:not(.is-in) {
  opacity: 0;
  transform: translateY(6px);
}

.dl-search__inner.is-armed.is-in {
  opacity: 1;
  transform: translateY(0);
  transition: opacity var(--dlx-filters) var(--dlx-ease),
              transform var(--dlx-filters) var(--dlx-ease);
}

/* ---------------------------------------------------------
   2 — A SECTION HEAD AND ITS BRAND RULE
   No new rule is added anywhere: these two sections already
   have one in the markup, and only those are animated.
   --------------------------------------------------------- */
.dl-section__head.is-armed:not(.is-in) .dl-section__heading {
  opacity: 0;
  transform: translateY(10px);
}

.dl-section__head.is-armed.is-in .dl-section__heading {
  opacity: 1;
  transform: translateY(0);
  transition: opacity var(--dlx-head) var(--dlx-ease),
              transform var(--dlx-head) var(--dlx-ease);
}

/* RED LEADS. The markup is red-first and in this RTL row the
   first child lands physically right, so red is the segment at
   the line's right end: it draws first and blue grows leftward
   out of it. transform-origin is PHYSICAL and is never flipped
   by direction. */
.dl-section__head.is-armed .dl-rule__segment {
  transform-origin: right center;
}

.dl-section__head.is-armed:not(.is-in) .dl-rule__segment {
  transform: scaleX(0);
}

.dl-section__head.is-armed.is-in .dl-rule__segment--red {
  transform: scaleX(1);
  transition: transform var(--dlx-red) var(--dlx-ease) var(--dlx-at-red);
}

.dl-section__head.is-armed.is-in .dl-rule__segment--blue {
  transform: scaleX(1);
  transition: transform var(--dlx-blue) var(--dlx-ease) var(--dlx-at-blue);
}

/* ---------------------------------------------------------
   3 — THE ROWS
   The group is what is watched, not the individual rows: a row
   hidden by the search has no box at all and would never
   intersect, so it would have stayed armed for good. Observing
   the list and staging its rows in CSS avoids that entirely.
   --------------------------------------------------------- */

.dl-list.is-armed:not(.is-in) .dl-list__number,
.dl-list.is-armed:not(.is-in) .dl-list__label,
.dl-docs.is-armed:not(.is-in) .dl-doc__icon,
.dl-docs.is-armed:not(.is-in) .dl-doc__name,
.dl-docs.is-armed:not(.is-in) .dl-doc__meta,
.dl-docs.is-armed:not(.is-in) .dl-doc__get {
  opacity: 0;
  transform: translateY(8px);
}

.dl-list.is-armed.is-in:not(.is-done) .dl-list__number,
.dl-list.is-armed.is-in:not(.is-done) .dl-list__label,
.dl-docs.is-armed.is-in:not(.is-done) .dl-doc__icon,
.dl-docs.is-armed.is-in:not(.is-done) .dl-doc__name,
.dl-docs.is-armed.is-in:not(.is-done) .dl-doc__meta,
.dl-docs.is-armed.is-in:not(.is-done) .dl-doc__get {
  opacity: 1;
  transform: translateY(0);
  transition: opacity var(--dlx-row) var(--dlx-ease) var(--dlx-at, 0ms),
              transform var(--dlx-row) var(--dlx-ease) var(--dlx-at, 0ms);
}

/* One step per row, capped at the tenth so a long list never
   turns into a cascade that outruns the reader. */
.dl-list.is-armed .dl-list__item:nth-child(1),
.dl-docs.is-armed .dl-doc:nth-child(1)  { --dlx-at: 0ms; }
.dl-list.is-armed .dl-list__item:nth-child(2),
.dl-docs.is-armed .dl-doc:nth-child(2)  { --dlx-at: var(--dlx-step); }
.dl-list.is-armed .dl-list__item:nth-child(3),
.dl-docs.is-armed .dl-doc:nth-child(3)  { --dlx-at: calc(var(--dlx-step) * 2); }
.dl-list.is-armed .dl-list__item:nth-child(4),
.dl-docs.is-armed .dl-doc:nth-child(4)  { --dlx-at: calc(var(--dlx-step) * 3); }
.dl-list.is-armed .dl-list__item:nth-child(5),
.dl-docs.is-armed .dl-doc:nth-child(5)  { --dlx-at: calc(var(--dlx-step) * 4); }
.dl-list.is-armed .dl-list__item:nth-child(6),
.dl-docs.is-armed .dl-doc:nth-child(6)  { --dlx-at: calc(var(--dlx-step) * 5); }
.dl-list.is-armed .dl-list__item:nth-child(7),
.dl-docs.is-armed .dl-doc:nth-child(7)  { --dlx-at: calc(var(--dlx-step) * 6); }
.dl-list.is-armed .dl-list__item:nth-child(8),
.dl-docs.is-armed .dl-doc:nth-child(8)  { --dlx-at: calc(var(--dlx-step) * 7); }
.dl-list.is-armed .dl-list__item:nth-child(9),
.dl-docs.is-armed .dl-doc:nth-child(9)  { --dlx-at: calc(var(--dlx-step) * 8); }
.dl-list.is-armed .dl-list__item:nth-child(n + 10),
.dl-docs.is-armed .dl-doc:nth-child(n + 10) { --dlx-at: calc(var(--dlx-step) * 9); }

/* ---------------------------------------------------------
   4 — AFTER THE ENTRANCE: THE SEARCH
   The existing filter is not touched -- it still works by
   setting `hidden` on a row, exactly as it does now. Once the
   group has finished arriving (.is-done), a row that comes back
   from a search fades in over 220ms instead of appearing at
   once. @starting-style is what makes that possible without
   going near the filter's script: it supplies the from-state
   for an element that has just become rendered, which is
   precisely what `hidden` being removed does. Where it is not
   supported the rule is ignored and the search behaves exactly
   as it does today.
   --------------------------------------------------------- */
.dl-list.is-done .dl-list__number,
.dl-list.is-done .dl-list__label,
.dl-docs.is-done .dl-doc__icon,
.dl-docs.is-done .dl-doc__name,
.dl-docs.is-done .dl-doc__meta,
.dl-docs.is-done .dl-doc__get {
  opacity: 1;
  transition: opacity var(--dlx-refilter) var(--dlx-ease);
}

@starting-style {
  .dl-list.is-done .dl-list__number,
  .dl-list.is-done .dl-list__label,
  .dl-docs.is-done .dl-doc__icon,
  .dl-docs.is-done .dl-doc__name,
  .dl-docs.is-done .dl-doc__meta,
  .dl-docs.is-done .dl-doc__get {
    opacity: 0;
  }
}

/* ---------------------------------------------------------
   REDUCED MOTION — everything present, nothing moves
   --------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .dl-search__inner.is-armed:not(.is-in),
  .dl-section__head.is-armed:not(.is-in) .dl-section__heading,
  .dl-list.is-armed:not(.is-in) .dl-list__number,
  .dl-list.is-armed:not(.is-in) .dl-list__label,
  .dl-docs.is-armed:not(.is-in) .dl-doc__icon,
  .dl-docs.is-armed:not(.is-in) .dl-doc__name,
  .dl-docs.is-armed:not(.is-in) .dl-doc__meta,
  .dl-docs.is-armed:not(.is-in) .dl-doc__get {
    opacity: 1;
    transform: none;
  }

  .dl-section__head.is-armed:not(.is-in) .dl-rule__segment {
    transform: none;
  }

  .dl-search__inner.is-armed.is-in,
  .dl-section__head.is-armed.is-in .dl-section__heading,
  .dl-section__head.is-armed.is-in .dl-rule__segment,
  .dl-list.is-armed.is-in .dl-list__number,
  .dl-list.is-armed.is-in .dl-list__label,
  .dl-docs.is-armed.is-in .dl-doc__icon,
  .dl-docs.is-armed.is-in .dl-doc__name,
  .dl-docs.is-armed.is-in .dl-doc__meta,
  .dl-docs.is-armed.is-in .dl-doc__get,
  .dl-list.is-done .dl-list__number,
  .dl-list.is-done .dl-list__label,
  .dl-docs.is-done .dl-doc__icon,
  .dl-docs.is-done .dl-doc__name,
  .dl-docs.is-done .dl-doc__meta,
  .dl-docs.is-done .dl-doc__get {
    transition: none;
  }
}
