/* ============================================================
   VISIGENCE: motion
   The hand's vocabulary, page-wide: what moves when a pointer arrives on
   something it can press. Loaded last, so it states each answer once over
   the component sheets rather than inside each of them.

     .sr-only        words kept for a screen reader when the drawing of them
                     is split into letters or scrambled
     .roll           a label set twice, letter by letter, that turns over
                     under the hand (scripts/studio/type.js)
     .scramble       an address that decodes under the hand
     .cursor__label  the verb the lens carries over what it is about to press
     .form-stage     where the dots wait and build an object
                     (scripts/studio/forms.js)
     the header's progress filament, the questions that open on a curve,
     and a ring that holds still under the hand.
   ============================================================ */

.sr-only {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---------- The roll ----------
   Each letter is a box one line tall with its twin hung beneath it; on the
   hand, every letter rises by its own height, one beat after its neighbour,
   so the twin lands where it stood. The twin is lit in the hover colour, so
   the word changes colour by being REPLACED rather than tinted. */
.roll {
  display: inline-flex;
  overflow: hidden;
  vertical-align: top;
  line-height: 1.25;
  padding-block: .04em;
  margin-block: -.04em;
}
.roll__c {
  position: relative;
  display: inline-block;
  white-space: pre;
  transition: transform 560ms var(--ease-out) calc(var(--ci, 0) * 14ms);
}
.roll__c::after {
  content: attr(data-ch);
  position: absolute;
  left: 0;
  top: 100%;
  color: var(--roll-to, currentColor);
}
:is(a, button):is(:hover, :focus-visible) .roll__c { transform: translateY(-100%); }

/* What each family rolls to. */
.site-nav a, .footer__nav a, .chapter-signals a { --roll-to: var(--cyan-3); }
.text-link { --roll-to: var(--cyan-3); }
.cta { --roll-to: currentColor; }
/* The nav's own colour change is the roll's now; the item underneath keeps
   its colour so the replaced word is the only thing that changes. */
.site-nav a:hover, .site-nav a:focus-visible { color: var(--text-1); }
.footer__nav a:hover, .footer__nav a:focus-visible { color: inherit; }

/* ---------- The text link ----------
   The underline was a fifth of the word at rest and the whole word on the
   hand. It now leaves to the right and comes back from the left, so the
   line reads as travelling with the eye rather than as growing. */
.text-link::after { transform: scaleX(.22); transform-origin: 0 50%; }
.text-link:is(:hover, :focus-visible)::after {
  animation: link-line 720ms var(--ease-out) both;
}
@keyframes link-line {
  0%   { transform: scaleX(.22); transform-origin: 100% 50%; }
  38%  { transform: scaleX(0);   transform-origin: 100% 50%; }
  39%  { transform: scaleX(0);   transform-origin: 0 50%; }
  100% { transform: scaleX(1);   transform-origin: 0 50%; }
}

/* ---------- The call to action ----------
   A light that fills the button from the side the hand came in. */
.cta::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(100deg, transparent 20%, rgba(255, 255, 255, .16) 46%, transparent 62%);
  transform: translateX(-120%);
  pointer-events: none;
}
.cta:is(:hover, :focus-visible)::after { transform: translateX(120%); transition: transform 900ms var(--ease-out); }

/* ---------- The address ---------- */
.scramble { display: inline-block; white-space: pre; }

/* ---------- The cursor's label ----------
   Over anything that says what a press will do, the lens fills with the
   studio's light and carries the verb. */
.cursor__label {
  position: absolute;
  top: 0;
  left: 0;
  transform: translate(-50%, -50%) scale(.4);
  font-family: var(--font-mono);
  font-size: 9.5px;
  font-weight: 500;
  line-height: 1;
  letter-spacing: .2em;
  text-transform: uppercase;
  white-space: nowrap;
  color: #02050c;
  opacity: 0;
  transition: opacity 220ms ease, transform 460ms cubic-bezier(.34, 1.45, .5, 1);
  pointer-events: none;
}
.cursor.is-on.is-label .cursor__label { opacity: 1; transform: translate(-50%, -50%) scale(1); }
.cursor.is-on.is-label .cursor__ring {
  transform: scale(1.78);
  background: rgba(99, 216, 255, .94);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  box-shadow: 0 8px 34px -6px rgba(34, 230, 255, .55);
}
.cursor.is-label .cursor__dot { opacity: 0; }

/* ---------- The stages ----------
   The canvas runs past its box on every side, so the bloom a formed object
   throws is never cut at a straight edge. It never takes a click: the row
   it stands in is the target. */
.form-stage {
  position: relative;
  display: block;
  pointer-events: none;
}
.form-stage__canvas {
  position: absolute;
  inset: -14%;
  width: 128%;
  height: 128%;
  display: block;
  max-width: none;
  pointer-events: none;
}

/* ---------- The header's progress ----------
   One filament along the foot of the header, as long as the reader is far
   down the page: drawn by the scroll itself, so it costs no script. Only
   where the browser can tie an animation to the scroll, and only while the
   header is over content. */
@supports (animation-timeline: scroll()) {
  .site-header::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: -1px;
    height: 1px;
    background: linear-gradient(90deg, var(--signal), var(--cyan-2) 70%, var(--cyan-3));
    box-shadow: 0 0 10px rgba(34, 230, 255, .45);
    transform-origin: 0 50%;
    transform: scaleX(0);
    opacity: 0;
    transition: opacity 360ms var(--ease-ui);
    animation: page-progress linear both;
    animation-timeline: scroll(root block);
    pointer-events: none;
  }
  .site-header.is-scrolled::after { opacity: .9; }
  @keyframes page-progress { to { transform: scaleX(1); } }
}

/* ---------- The questions ----------
   They open on a curve rather than a cut, where the browser can animate to
   an intrinsic height; elsewhere they open as they always did. */
.faq { interpolate-size: allow-keywords; }
.faq__item::details-content {
  block-size: 0;
  overflow: clip;
  transition: block-size 560ms var(--ease-out), content-visibility 560ms allow-discrete;
}
.faq__item[open]::details-content { block-size: auto; }
.faq__item summary { transition: color var(--t-micro) var(--ease-ui), padding-left 520ms var(--ease-out); }
.faq__item summary:hover,
.faq__item summary:focus-visible { padding-left: 12px; }
.faq__item summary::after { transition: transform 520ms var(--ease-out); }

/* ---------- The ring holds still under the hand ----------
   A ball under the pointer is a ball about to be pressed, and the ring it
   rides stops so the target does not slide out from under the press. */
.wall__field:has(.ball:hover) .wall__slot { animation-play-state: paused; }

/* ---------- Reduced motion ----------
   The swaps stay and the travel goes: a rolled label changes in place, the
   wash and the rule are simply there, the filament is not drawn. */
@media (prefers-reduced-motion: reduce) {
  .roll__c { transition: none; }
  :is(a, button):is(:hover, :focus-visible) .roll__c { transform: none; }
  .text-link:is(:hover, :focus-visible)::after { animation: none; transform: scaleX(1); }
  .cta::after { display: none; }
  .site-header::after { display: none; }
}

@media (forced-colors: active) {
  .form-stage { display: none; }
  .cta::after, .site-header::after { display: none; }
}

@media print {
  .form-stage, .cursor__label { display: none !important; }
  .roll { display: none; }
  .sr-only { position: static !important; width: auto; height: auto; margin: 0; clip: auto; clip-path: none; overflow: visible; }
}
