/* Impact: the counter scene. Every selector is scoped to .impact--counter.

   A near-black scene between Team and Get in touch. The title and
   standfirst at the top; a ruled board of five figures, each digit in its
   own window so it can roll like a counter; along the foot, a period line
   from May 2024 to the steel as-at mark, running on past it toward the
   screen edge, with the legal fine print hanging under the mark.

   Everything down to the choreography block is the complete static
   section: every figure at its value, the line fully drawn. */

.impact--counter {
  --counter-rule: rgb(250 250 248 / 0.14);
  --counter-line: rgb(250 250 248 / 0.52);
  --counter-fine: #797D83;                 /* 4.6:1 on ink: still legible (AA), the quietest line of the scene */
  --counter-size: clamp(3.25rem, 16.5vw, 4.5rem);
  --counter-pad: 1.375rem;
  --counter-gap: 2.75rem;
  /* the left edge of the page's content column, measured on a full width grid */
  --counter-inset: max(var(--gutter), (100% - var(--max)) / 2);
  /* how far the page travels while the line draws */
  --counter-draw: min(6rem, 11svh);

  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--counter-gap);
  padding-block: 5rem 3.5rem;
  overflow-x: clip;
  background: var(--ink);
  color: var(--on-dark-muted);
  color-scheme: dark;
  caret-color: var(--accent-on-dark);
  --ring: var(--on-dark);
}

.impact--counter ::selection { background: var(--accent-on-dark); color: var(--ink); }

/* The scene ends on a hairline across the content column, so the footer
   below reads as the next band on the same ink */
.impact--counter::after {
  content: "";
  position: absolute;
  left: var(--counter-inset);
  right: var(--counter-inset);
  bottom: 0;
  height: 1px;
  background: var(--counter-rule);
}

/* ------------------------------------------------------------------
   Title and standfirst
   ------------------------------------------------------------------ */

.impact--counter .counter-head { text-align: center; }

.impact--counter .counter-head__title {
  font-weight: 300;
  font-size: clamp(1.875rem, 1.2rem + 2.2vw, 2.875rem);
  line-height: 1.2;
  letter-spacing: var(--track-display);
  padding-left: var(--track-display);
  text-transform: uppercase;
  color: var(--on-dark);
}

.impact--counter .counter-head__standfirst {
  max-width: 40em;
  margin: 0.875rem auto 0;
  font-size: clamp(1rem, 0.9rem + 0.45vw, 1.25rem);
  line-height: 1.6;
  color: var(--on-dark-muted);
  text-wrap: balance;
}

/* ------------------------------------------------------------------
   The board: one ruled column on phones, three over two from 45rem.
   From 45rem every cell keeps a line for its opening word, so the
   figures in a row stand level whether or not they have one.
   ------------------------------------------------------------------ */

.impact--counter .counter-board {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  border-top: 1px solid var(--counter-rule);
}

.impact--counter .counter-cell {
  position: relative;
  display: grid;
  grid-template-rows: auto auto auto;
  align-content: start;
  min-width: 0;
  padding-block: var(--counter-pad);
  border-bottom: 1px solid var(--counter-rule);
  line-height: 1.5;
}

.impact--counter .counter-cell__pre {
  grid-row: 1;
  align-self: end;
}

.impact--counter .counter-cell__label {
  grid-row: 3;
  max-width: 24em;
  text-wrap: pretty;
}

/* Each figure stands alone in its cell, so the digits are set at their
   own proportional widths, as display figures: a leading 1 starts on the
   text edge with the words above and below, and 26.4, 1,800 and 2.40 keep
   normal spacing. Kerning is off inside the number, because the wheels set
   every digit on its own: the plain figure without the script then fills
   exactly the same box as the wheels. */
.impact--counter .counter-cell__figure {
  grid-row: 2;
  display: block;
  margin-block: 0 0.0625em;
  font-size: var(--counter-size);
  font-weight: 200;
  line-height: 1.25;                        /* the wheel window; impact.js uses the same */
  /* The rolling strips wait far below their windows until they roll. The windows hide them with
     clip-path, which does not stop them counting as scrollable overflow, so the page would run on
     past the footer. Clipping here stops that. The box reaches 0.2em out each side (margin), with
     the padding taking it back, so the digits' 0.16em reach and the US$ side bearing are never cut,
     and the wheels themselves keep overflow visible, so their baselines are untouched. */
  overflow: clip;
  margin-inline: -0.2em;
  padding-inline: 0.2em;
  letter-spacing: 0;
  color: var(--on-dark);
  font-variant-numeric: lining-nums proportional-nums;
  white-space: nowrap;
  -webkit-user-select: none;
  user-select: none;
}

.impact--counter .counter-cell__num { font-kerning: none; }

/* Currency and units stay put beside the wheels, a step smaller, every one
   the same visible gap from its figure (about a word space, 0.15em of the
   figure, measured stroke to stroke). US$ is drawn back by the side bearing
   of its U, so its first stroke meets the text edge. */
.impact--counter .counter-cell__unit {
  --counter-unit-gap: 0.21em;
  font-size: 0.44em;
  font-weight: 300;
  letter-spacing: 0.02em;
}

.impact--counter .counter-cell__unit--before {
  margin-left: -0.1em;
  margin-right: var(--counter-unit-gap);
}

.impact--counter .counter-cell__unit--after,
.impact--counter .counter-cell__unit--word { margin-left: var(--counter-unit-gap); }

/* A closing 0 brings its own round side bearing, so the unit after it sits
   closer to keep the same visible gap: 10 % and 2.40 m */
.impact--counter .counter-cell:nth-child(4) .counter-cell__unit--after { --counter-unit-gap: 0.165em; }
.impact--counter .counter-cell:nth-child(5) .counter-cell__unit--after { --counter-unit-gap: 0.105em; }

/* A digit window: the digit itself holds the width, height and baseline,
   unseen; the strip of digits above it ends on the same digit, so at rest
   the window shows the exact value. The window is a little taller than a
   digit, so the fade at its top and bottom edges dissolves a digit only
   while it travels and never touches one at rest. Sideways the window
   reaches 0.16em past its digit into its neighbours, with the margins
   taking that back out of the line, so a wider digit rolling through a
   narrow 1 is not cut off. */
.impact--counter .counter-cell__wheel {
  position: relative;
  display: inline-block;
  padding-inline: 0.16em;
  margin-inline: -0.16em;
  clip-path: inset(0);
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 20%, #000 80%, transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0%, #000 20%, #000 80%, transparent 100%);
}

.impact--counter .counter-cell__digit { visibility: hidden; }

.impact--counter .counter-cell__strip {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  white-space: pre;
  text-align: center;
}

@media (min-width: 45rem) {
  .impact--counter .counter-board { grid-template-columns: repeat(6, minmax(0, 1fr)); }

  .impact--counter .counter-cell {
    grid-template-rows: minmax(1.5em, auto) auto auto;
    grid-column: span 2;
    padding-inline: 0 clamp(1rem, 2vw, 2rem);
  }

  .impact--counter .counter-cell:nth-child(n+4) { grid-column: span 3; }

  .impact--counter .counter-cell:nth-child(2),
  .impact--counter .counter-cell:nth-child(3),
  .impact--counter .counter-cell:nth-child(5) {
    padding-left: clamp(1rem, 2.4vw, 2.5rem);
    border-left: 1px solid var(--counter-rule);
  }
}

/* ------------------------------------------------------------------
   The period line and the fine print
   A full width grid: the content column's left edge, the span to the
   as-at mark, the rest of the content column, and the right margin the
   line runs out into.
   ------------------------------------------------------------------ */

.impact--counter .counter-foot {
  display: grid;
  gap: 2rem;
}

/* On phones the as-at mark stands where its two labels fit, flush left under
   it in one block: the column after the mark is exactly as wide as the longer
   label, and May 2024 keeps the rest. */
.impact--counter .counter-period {
  display: grid;
  grid-template-columns:
    var(--counter-inset)
    minmax(0, 1fr)
    max-content
    var(--counter-inset);
  grid-template-rows: 1.5rem auto auto;
  row-gap: 0.625rem;
  font-size: 0.75rem;
  font-weight: 500;
  line-height: 1.5;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.impact--counter .counter-period__origin,
.impact--counter .counter-period__past,
.impact--counter .counter-period__tick,
.impact--counter .counter-period__ahead {
  grid-row: 1;
  align-self: center;
  pointer-events: none;
}

.impact--counter .counter-period__origin {
  grid-column: 2;
  justify-self: start;
  width: 1px;
  height: 0.625rem;
  background: var(--counter-line);
}

.impact--counter .counter-period__past {
  grid-column: 2;
  height: 1px;
  background: var(--counter-line);
}

/* The as-at mark: the section's one steel accent */
.impact--counter .counter-period__tick {
  grid-column: 3;
  justify-self: start;
  width: 1px;
  height: 100%;
  background: var(--accent-on-dark);
}

/* Past the mark the line keeps going, thinning out to the screen edge */
.impact--counter .counter-period__ahead {
  grid-column: 3 / 5;
  height: 1px;
  margin-left: 1px;
  background: linear-gradient(to right, rgb(250 250 248 / 0.4), rgb(250 250 248 / 0.22) 45%, rgb(250 250 248 / 0));
}

.impact--counter .counter-period__start {
  grid-row: 2;
  grid-column: 2;
  justify-self: start;
  color: var(--on-dark-muted);
}

.impact--counter .counter-period__stamp {
  grid-row: 2;
  grid-column: 3;
  display: grid;
  justify-items: start;
  row-gap: 0.375rem;
  min-width: 0;
}

.impact--counter .counter-period__asof { color: var(--accent-on-dark); }

.impact--counter .counter-period__next { color: var(--on-dark-muted); }

/* The legal fine print closes the scene as one long, quiet line under the
   period line, across the whole content width from May 2024 to the far
   edge. It must never read as another label: the labels above are upright
   tracked capitals in medium weight, so the note is light italic sentence
   case, the smallest and dimmest type of the scene, set a little apart */
.impact--counter .counter-note {
  grid-row: 3;
  grid-column: 2 / 4;
  max-width: none;
  margin-top: clamp(0.875rem, 5svh - 1.5rem, 2.25rem);
  font-size: 0.6875rem;
  font-style: italic;
  font-weight: 300;
  line-height: 1.6;
  letter-spacing: 0.01em;
  text-transform: none;
  color: var(--counter-fine);
  /* centred on the full width, so whatever length the line takes, the space
     either side is even */
  text-align: center;
  /* one line where it fits; where it must wrap, lines of even length, so a
     word is never left alone on a line of its own */
  text-wrap: balance;
}

/* ------------------------------------------------------------------
   From 45rem: one full screen scene. Title at the top, the board in the
   middle, the period line and fine print at the foot. The scene is at
   least the height of the screen under the bar, so where the Impact link
   lands it fills that screen, and it ends with no more than its own
   bottom padding before the footer.
   The figures and all the spacing are tied to the screen's height, and
   both give way faster than the height itself, so on a laptop screen
   (1280x800, 1366x768, 1280x720) the whole scene down to the last line
   of fine print still fits under the bar, while on a desktop screen the
   figures keep their full size and the spacing its air.
   ------------------------------------------------------------------ */

@media (min-width: 45rem) {
  .impact--counter {
    --counter-size: clamp(2.75rem, min(6.4vw, 12.5svh - 1.5rem), 6rem);
    --counter-u: clamp(1rem, 9.5svh - 3.125rem, 4.5rem);
    --counter-pad: max(0.75rem, var(--counter-u) * 0.5);
    --counter-gap: var(--counter-u);
    --counter-at: 0.58;                     /* where the as-at mark sits along the content width (no scale implied) */
    min-height: calc(100vh - var(--nav-h) + 1px);
    min-height: calc(100svh - var(--nav-h) + 1px);   /* the closing hairline falls just below the landing view */
    justify-content: space-between;
    padding-block: calc(var(--counter-u) * 1.2) var(--counter-u);
  }

  .impact--counter .counter-head__standfirst { margin-top: clamp(0.75rem, 1.6svh, 1.25rem); }

  .impact--counter .counter-foot { gap: max(0.75rem, var(--counter-u) * 0.6); }

  .impact--counter .counter-period {
    grid-template-columns:
      var(--counter-inset)
      calc((100% - 2 * var(--counter-inset)) * var(--counter-at))
      minmax(0, 1fr)
      var(--counter-inset);
    font-size: 0.8125rem;
    letter-spacing: 0.12em;
  }

  .impact--counter .counter-period__stamp {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    column-gap: 1.5rem;
  }
}

/* A short laptop window (1280x620, 1024x640): the spacing and figures give way a little more, so
   the last line of fine print is still in view where the Impact link lands. Laptops only: a phone
   held sideways keeps its figures as they were. */
@media (min-width: 64rem) and (max-height: 40rem) {
  .impact--counter {
    --counter-u: 0.875rem;
    --counter-size: clamp(2.5rem, 12.5svh - 1.75rem, 6rem);
  }
}

/* ------------------------------------------------------------------
   Scroll choreography
   As the scene comes up, every counter rolls from zero into its value,
   the rightmost wheel travelling furthest, a beat behind the one before;
   then the line draws from May 2024 to the as-at mark and runs on past
   it. Everything has settled before the Impact link's landing position:
   from 45rem the board runs on the scene's own entry, which ends at 84%
   (the scene is at least the height of the screen under the bar, so the
   link lands it at its full entry, and still past 86% where a short screen
   makes the scene taller than that); on phones the first three run on
   the scene too, ending by 88%, and the fourth and fifth, which sit below
   the landing view, on their own rows.
   The fallback in impact.js mirrors every animation here.
   ------------------------------------------------------------------ */

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .impact--counter { view-timeline: --impact-counter-scene block; }
    .impact--counter .counter-cell { view-timeline: --impact-counter-row block; }

    .impact--counter .counter-head__title,
    .impact--counter .counter-head__standfirst {
      animation: impact-counter-rise linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 100%;
    }

    .impact--counter .counter-cell__strip {
      animation: impact-counter-roll linear both;
      animation-timeline: --impact-counter-scene;
    }

    .impact--counter .counter-cell:nth-child(1) .counter-cell__strip { animation-range: entry 28% entry 58%; }
    .impact--counter .counter-cell:nth-child(2) .counter-cell__strip { animation-range: entry 46% entry 76%; }
    .impact--counter .counter-cell:nth-child(3) .counter-cell__strip { animation-range: entry 64% entry 88%; }
    .impact--counter .counter-cell:nth-child(n+4) .counter-cell__strip {
      animation-timeline: --impact-counter-row;
      animation-range: entry 20% cover 32%;
    }

    /* A tall phone shows the fourth figure where the link lands, so there
       it runs on the scene with the first three and has settled by then */
    @media (min-height: 53.125rem) {
      .impact--counter .counter-cell:nth-child(4) .counter-cell__strip {
        animation-timeline: --impact-counter-scene;
        animation-range: entry 70% entry 90%;
      }
    }

    @media (min-width: 45rem) {
      .impact--counter .counter-cell:nth-child(n) .counter-cell__strip { animation-timeline: --impact-counter-scene; }
      .impact--counter .counter-cell:nth-child(1) .counter-cell__strip { animation-range: entry 26% entry 60%; }
      .impact--counter .counter-cell:nth-child(2) .counter-cell__strip { animation-range: entry 30% entry 64%; }
      .impact--counter .counter-cell:nth-child(3) .counter-cell__strip { animation-range: entry 34% entry 68%; }
      .impact--counter .counter-cell:nth-child(4) .counter-cell__strip { animation-range: entry 48% entry 80%; }
      .impact--counter .counter-cell:nth-child(5) .counter-cell__strip { animation-range: entry 52% entry 84%; }
    }

    /* The line draws while the page travels one short stretch past the
       line's arrival at the bottom of the screen; the tick is 0.75rem
       taller than the line on either side, so its range is shifted by that */
    .impact--counter .counter-period__past {
      animation: impact-counter-draw linear both;
      animation-timeline: view();
      animation-range: cover 0px cover calc(var(--counter-draw) * 0.55);
    }

    .impact--counter .counter-period__tick {
      animation: impact-counter-open linear both;
      animation-timeline: view();
      animation-range: cover calc(var(--counter-draw) * 0.45 + 0.75rem) cover calc(var(--counter-draw) * 0.7 + 0.75rem);
    }

    .impact--counter .counter-period__ahead {
      animation: impact-counter-draw linear both;
      animation-timeline: view();
      animation-range: cover calc(var(--counter-draw) * 0.6) cover var(--counter-draw);
    }

    /* On a short screen the line reaches the landing view too late to draw
       there without being caught halfway, so it simply stands drawn; and
       where the scene is taller than the screen the landing reaches only
       (height - bar) / height of the entry (81% on a landscape phone), so
       the second row settles earlier */
    @media (min-width: 45rem) and (max-height: 46rem) {
      .impact--counter .counter-period__past,
      .impact--counter .counter-period__tick,
      .impact--counter .counter-period__ahead {
        animation: none;
      }

      .impact--counter .counter-cell:nth-child(4) .counter-cell__strip { animation-range: entry 44% entry 74%; }
      .impact--counter .counter-cell:nth-child(5) .counter-cell__strip { animation-range: entry 48% entry 78%; }
    }
  }
}

/* The same scenes where the browser does not run the block above:
   scroll-fallback.js plays them (configured in impact.js)
   and marks <html> with .sdt-fallback. The scene pins nothing and changes
   no layout, so the class only switches the CSS versions off. */
@media (prefers-reduced-motion: no-preference) {
  html.sdt-fallback .impact--counter .counter-head__title,
  html.sdt-fallback .impact--counter .counter-head__standfirst,
  html.sdt-fallback .impact--counter .counter-cell__strip,
  html.sdt-fallback .impact--counter .counter-period__past,
  html.sdt-fallback .impact--counter .counter-period__tick,
  html.sdt-fallback .impact--counter .counter-period__ahead {
    animation: none;
  }
}

@keyframes impact-counter-rise {
  from { opacity: 0.6; transform: translateY(1rem); }
  to { opacity: 1; transform: translateY(0); }
}

/* --counter-from is set on each strip by impact.js: the strip's
   travel, one window per step. A travelling wheel stays a little dim and
   comes to full white only as it settles on its digit, so a total passing
   through on the way never reads as a figure. */
@keyframes impact-counter-roll {
  0% { transform: translateY(var(--counter-from, 0em)); opacity: 0.4; }
  80% { opacity: 0.6; }
  100% { transform: translateY(0em); opacity: 1; }
}

@keyframes impact-counter-draw {
  from { clip-path: inset(0 100% 0 0); }
  to { clip-path: inset(0 0 0 0); }
}

@keyframes impact-counter-open {
  from { clip-path: inset(50% 0 50% 0); }
  to { clip-path: inset(0 0 0 0); }
}
