/* =========================================================================
   THE SHINE
   Borrowed from the card game: a band of light sweeps across a card, and the
   cards do not all light at once — it travels down the page, row by row, so
   there is always something arriving. On a booth screen that is what pulls an
   eye back; in use it is a quiet way of saying "these are the things you can
   press".

   The sweep is a short part of a long cycle. Most of the time nothing is
   moving on any given card, which is what keeps it from being noise.
   ========================================================================= */

/* Positioning is added by the script, and only to elements that are still
   static. Forcing position:relative here dropped every absolutely-positioned
   control back into the flow — the film panel's close button ended up in the
   bottom-left corner. */
.shine--rel { position: relative; }
.shine::after {
  content: '';
  position: absolute; inset: 0; z-index: 3;
  pointer-events: none; border-radius: inherit;
  overflow: hidden;
  /* the band itself: a narrow diagonal highlight */
  background: linear-gradient(
    74deg,
    transparent 42%,
    rgba(255, 255, 255, 0.10) 47%,
    var(--shine-hot, rgba(255, 255, 255, 0.55)) 50%,
    rgba(255, 255, 255, 0.10) 53%,
    transparent 58%);
  background-size: 260% 100%;
  background-repeat: no-repeat;
  background-position: 170% 0;
  animation: shine-sweep var(--shine-cycle, 7s) linear infinite;
  animation-delay: var(--shine-delay, 0s);
}

/* The sweep takes about a second of a seven-second cycle. Animating the
   background position rather than a transform means no extra layer and no
   clipping worries on a rounded card. */
@keyframes shine-sweep {
  0%   { background-position: 170% 0; }
  13%  { background-position: -70% 0; }
  100% { background-position: -70% 0; }
}

/* off-screen cards do not animate at all — the page is long */
.shine:not(.is-lit)::after { animation-play-state: paused; }

/* a dark card wants a hotter band; a white one a softer, wider one */
.acard--dark, .rcard, .ocard--dark { --shine-hot: rgba(255, 255, 255, 0.7); }
.acard:not(.acard--dark) { --shine-hot: rgba(255, 255, 255, 0.85); }

/* buttons get a tighter, quicker band so they read as pressable, not decorative */
.shine--btn::after {
  background: linear-gradient(
    74deg,
    transparent 40%,
    rgba(255, 255, 255, 0.22) 48%,
    rgba(255, 255, 255, 0.62) 50%,
    rgba(255, 255, 255, 0.22) 52%,
    transparent 60%);
  background-size: 240% 100%;
  background-repeat: no-repeat;
  background-position: 170% 0;
  animation-duration: var(--shine-cycle, 5.5s);
}

/* =========================================================================
   BOUNCE
   Every button presses in and springs back, and anything that opens a panel
   gives a small kick first so the panel feels like it came out of the press
   rather than appearing on top of it.
   ========================================================================= */
.btn, .acard, .rcard, .ocard, .gpick__btn, .rail__arrow,
.reelopen__go, .pop__x, .reelopen__close, .clogo-wrap, .herowatch {
  transition:
    transform 0.42s cubic-bezier(0.2, 1.35, 0.35, 1),
    box-shadow 0.28s ease,
    filter 0.2s ease;
}
.btn:active, .gpick__btn:active, .rail__arrow:active,
.reelopen__go:active, .pop__x:active, .reelopen__close:active {
  transform: scale(0.93);
  transition-duration: 0.08s;
}
.acard:active, .rcard:active, .ocard:active, .clogo-wrap:active {
  transform: scale(0.97);
  transition-duration: 0.1s;
}

/* the kick a control gives as it opens something */
@keyframes bounce-pop {
  0%   { transform: scale(0.93); }
  45%  { transform: scale(1.06); }
  70%  { transform: scale(0.985); }
  100% { transform: scale(1); }
}
.is-popping { animation: bounce-pop 0.42s cubic-bezier(0.2, 1.2, 0.3, 1); }

/* and the panel that arrives, springing rather than fading in */
.pop.is-open .pop__panel,
.gstage.is-open .gstage__panel {
  animation: panel-in 0.46s cubic-bezier(0.18, 1.3, 0.32, 1);
}
@keyframes panel-in {
  0%   { transform: scale(0.9) translateY(16px); opacity: 0; }
  60%  { transform: scale(1.015) translateY(0); opacity: 1; }
  100% { transform: scale(1) translateY(0); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .shine::after { animation: none; opacity: 0; }
  .is-popping, .pop.is-open .pop__panel, .gstage.is-open .gstage__panel { animation: none; }
  .btn, .acard, .rcard, .ocard, .gpick__btn, .rail__arrow,
  .reelopen__go, .pop__x, .reelopen__close, .clogo-wrap, .herowatch { transition: none; }
}

/* =========================================================================
   THE SHINE, ON THE HEADLINES
   The cards catch the light; the words above them did not, which left the
   biggest thing on every screen as the only dead thing on it.

   A headline cannot take the card treatment — that is a white band painted
   over a box, and a headline has no box, so it would read as a smear across
   the picture behind it. The light has to run through the LETTERS.

   The obvious way to do that is background-clip:text on the heading itself.
   It does not work here, and the reason is worth writing down: every headline
   on this site carries a blue text-shadow glow, and a text-shadow is painted
   ON TOP of the element's background. Turning the glyph fill transparent to
   let a gradient through just reveals the glow behind the letters — the
   headline goes translucent blue and the film reel shows through it.

   So the band is a second copy of the words, laid exactly over the real ones
   and clipped to its own glyphs. Everywhere except the band it is completely
   transparent, so what you see is the real headline, untouched, with a stripe
   of light crossing it. The copy carries an empty alt text so it stays out of
   the accessibility tree.
   ========================================================================= */
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .tshine { position: relative; }
  .tshine--rel { position: relative; }

  .tshine::after {
    content: attr(data-shine) / "";
    position: absolute;
    left: 0; top: 0; right: 0;
    pointer-events: none;
    /* everything about the text is inherited, so the copy wraps and sits
       exactly where the real words do — including the uppercase */
    font: inherit; letter-spacing: inherit; text-transform: inherit;
    text-align: inherit; white-space: inherit; line-height: inherit;
    /* no glow on the copy: the real headline underneath already has one, and
       two of them stacked is a blur */
    text-shadow: none;

    /* THE WIDTH OF THE BAND
       The image is three times the width of the headline, so a stop range of
       n% in the gradient paints 3n% of the word. Five per cent is a glint;
       fourteen, which is where this started, turns the whole word blue as the
       sweep goes past. */
    background-image: linear-gradient(
      74deg,
      transparent 47.4%,
      color-mix(in srgb, var(--tshine-hot, #4aa8ff) 45%, transparent) 49.2%,
      var(--tshine-hot, #4aa8ff) 50%,
      color-mix(in srgb, var(--tshine-hot, #4aa8ff) 45%, transparent) 50.8%,
      transparent 52.6%);
    background-size: 300% 100%;
    background-repeat: no-repeat;
    /* the image only covers the letters while the position is between 100%
       and 0%; outside that there is no background at all, and the animation
       stays strictly inside that range */
    background-position: 100% 0;
    -webkit-background-clip: text;
            background-clip: text;
    -webkit-text-fill-color: transparent;
            color: transparent;

    animation: tshine-sweep var(--tshine-cycle, 8.5s) linear infinite;
    animation-delay: var(--shine-delay, 0s);
  }

  /* the sweep is about a second of eight and a half — the rest of the time
     the copy is invisible, which is the point. 100% parks the band off the
     left of the word, 0% parks it off the right. */
  @keyframes tshine-sweep {
    0%   { background-position: 100% 0; }
    12%  { background-position: 0% 0; }
    100% { background-position: 0% 0; }
  }

  /* off screen it does not animate, and the gate is its own class: the
     headlines already use `is-lit` for the torch that follows the cursor, and
     borrowing it here lit every headline as though it were being hovered */
  .tshine:not(.is-shining)::after { animation-play-state: paused; }

  /* A headline mid-sweep while the torch is cutting a hole in it is two
     effects fighting over the same letters. The torch wins — it is the one
     the person is causing. */
  .tshine.is-lit::after { animation-play-state: paused; background-position: 0 0; }

  /* THE GLINT IS BLUE, EVERYWHERE.
     A white band through white letters is nothing at all — and the biggest
     headline on the site, "YOUR WORLD, UNDERSTOOD BY AI.", is pure white. The
     brand blue shows against white letters AND against the near-black ones on
     the chapter pages, so one value covers both and nobody has to know which
     background a heading is sitting on. */
}

@media (prefers-reduced-motion: reduce) {
  .tshine::after { display: none; }
}
