/* ============================================
   TEXT ROLL — the shared CTA hover animation
   ============================================
   One rolling-letter hover used by every text CTA on the site
   (home top nav, About contact links, project link buttons, dock…).

   HOW TO USE
   1. Include this file and text-roll.js on the page.
   2. Add class "cta-roll" to any element whose text should roll on hover.
      Icons/images inside the element are left alone automatically.
   3. For content built by JavaScript, call  window.applyTextRoll(element)
      after building it.
   Optional: put class "roll-group" on a PARENT (e.g. the dock pill) to make
   hovering anywhere on the parent roll the text inside it.

   HOW IT WORKS
   text-roll.js wraps each letter in a small clipped box (.roll-ch) holding
   TWO copies of the letter stacked vertically (.roll). On hover the stack
   slides up one line: the letter escapes through the top while its twin
   rises in from the bottom, with a light tilt — the same motion language
   as the home screen's caption wave.

   ╔══════════════════════════════════════════╗
   ║  CONTROL PANEL — every dial lives here.  ║
   ║  Change a value, refresh, see it.        ║
   ╚══════════════════════════════════════════╝ */
:root {
  --roll-duration: 0.75s;                        /* how long each letter's roll takes */
  --roll-ease: cubic-bezier(0.16, 1, 0.3, 1);    /* easing — same as the caption wave */
  --roll-tilt: 30deg;                            /* light rotation while rolling */
  --roll-step: 0.01;                             /* seconds between letters (the wave; read by text-roll.js) */
  --roll-line: 1.2em;                            /* height of the letter window (one text line) */
  --roll-gap: 0.35em;                            /* buffer between the two letter copies, so descenders
                                                    (the tails of p, y, g…) never peek into the window */
  /* NOTE: text-roll.js overrides --roll-line / --roll-gap per CTA with
     whole-pixel values (same proportions), so the letters land exactly on
     the pixel grid — no 1px "settle" when the roll finishes. To change the
     proportions, edit the matching numbers in text-roll.js too. */
}

/* The clipping window: one line tall, everything outside is invisible.
   Line-height is pinned INSIDE the window so the roll distance is exact
   no matter what line-height the surrounding text uses. */
.roll-ch {
  display: inline-block;
  overflow: hidden;
  height: var(--roll-line);
  vertical-align: top;
  /* The line box INSIDE the window matches the window exactly, so the two
     copies stack at whole-pixel offsets and the travel distance is exact. */
  line-height: var(--roll-line);
}

/* The sliding stack of two letter copies. */
.roll-ch .roll {
  display: inline-block;
  transition: transform var(--roll-duration) var(--roll-ease);
  transition-delay: var(--d, 0s);        /* per-letter stagger, set by text-roll.js */
}
.roll-ch .roll span {
  display: block;                        /* the two copies stack vertically */
  transform-origin: bottom center;       /* tilt pivots from the letter base */
  transition: transform var(--roll-duration) var(--roll-ease);
  transition-delay: var(--d, 0s);
}
/* The twin waits below, pre-tilted; it straightens as it rises in.
   The margin is the descender buffer: without it, the tails of letters
   like p/y/g on the slid-away copy would peek back into the window. */
.roll-ch .roll span:nth-child(2) {
  transform: rotate(var(--roll-tilt));
  margin-top: var(--roll-gap);
}

/* ── The hover: slide the stack up one line (plus the buffer) ──
   Triggered by hovering the CTA itself, or a .roll-group parent.
   Only on devices with a REAL hover (mouse/trackpad): on touch screens the
   browser fakes a hover on tap, which would fire the roll after the press.
   (text-roll.js also skips the letter-splitting entirely on touch devices —
   this media query is the belt-and-braces CSS half of that.) */
@media (hover: hover) {
  .cta-roll:hover .roll-ch .roll,
  .roll-group:hover .cta-roll .roll-ch .roll {
    transform: translateY(calc(-1 * (var(--roll-line) + var(--roll-gap))));
  }
  .cta-roll:hover .roll-ch .roll span:nth-child(1),
  .roll-group:hover .cta-roll .roll-ch .roll span:nth-child(1) {
    transform: rotate(calc(-1 * var(--roll-tilt)));   /* leaving letter tilts away */
  }
  .cta-roll:hover .roll-ch .roll span:nth-child(2),
  .roll-group:hover .cta-roll .roll-ch .roll span:nth-child(2) {
    transform: rotate(0deg);                          /* arriving twin lands straight */
  }
}

/* ── Reduced motion: no roll, just a simple underline on hover ── */
@media (prefers-reduced-motion: reduce) {
  .roll-ch .roll,
  .roll-ch .roll span { transition: none; }
  .cta-roll:hover .roll-ch .roll,
  .roll-group:hover .cta-roll .roll-ch .roll { transform: none; }
  .cta-roll:hover .roll-ch .roll span:nth-child(1),
  .roll-group:hover .cta-roll .roll-ch .roll span:nth-child(1) { transform: none; }
  .cta-roll:hover,
  .roll-group:hover .cta-roll { text-decoration: underline; text-underline-offset: 4px; }
}
