fix: pull-to-refresh now checks actual scroll container

Find scrollable parent instead of using window.scrollY, which is
always 0 now that outlet is the scroll container. Prevents accidental
refresh triggers when scrolling up mid-content.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

Changed files
+27 -2
src
components
+27 -2
src/components/molecules/grain-pull-to-refresh.js
··· 41 41 42 42 #startY = 0; 43 43 #currentY = 0; 44 + #scrollContainer = null; 44 45 45 46 constructor() { 46 47 super(); ··· 51 52 52 53 connectedCallback() { 53 54 super.connectedCallback(); 55 + this.#scrollContainer = this.#findScrollContainer(); 54 56 this.addEventListener('touchstart', this.#onTouchStart, { passive: true }); 55 57 this.addEventListener('touchmove', this.#onTouchMove, { passive: false }); 56 58 this.addEventListener('touchend', this.#onTouchEnd, { passive: true }); 57 59 } 58 60 61 + #findScrollContainer() { 62 + let el = this.parentElement; 63 + while (el) { 64 + // Check through shadow DOM boundaries 65 + if (el.scrollHeight > el.clientHeight) { 66 + const style = getComputedStyle(el); 67 + if (style.overflowY === 'auto' || style.overflowY === 'scroll') { 68 + return el; 69 + } 70 + } 71 + // Traverse up through shadow roots 72 + el = el.parentElement || el.getRootNode()?.host; 73 + } 74 + return null; 75 + } 76 + 77 + #getScrollTop() { 78 + if (this.#scrollContainer) { 79 + return this.#scrollContainer.scrollTop; 80 + } 81 + return window.scrollY; 82 + } 83 + 59 84 disconnectedCallback() { 60 85 this.removeEventListener('touchstart', this.#onTouchStart); 61 86 this.removeEventListener('touchmove', this.#onTouchMove); ··· 68 93 69 94 #onTouchStart = (e) => { 70 95 if (this.refreshing) return; 71 - if (window.scrollY > 0) return; 96 + if (this.#getScrollTop() > 0) return; 72 97 73 98 this.#startY = e.touches[0].clientY; 74 99 this._pulling = true; ··· 80 105 this.#currentY = e.touches[0].clientY; 81 106 const diff = this.#currentY - this.#startY; 82 107 83 - if (diff > 0 && window.scrollY === 0) { 108 + if (diff > 0 && this.#getScrollTop() === 0) { 84 109 e.preventDefault(); 85 110 // Apply resistance 86 111 this._pullDistance = Math.min(diff * 0.5, MAX_PULL);