// Measures a rendered page and reports where the layout is wrong. // // This function is not run here. It is serialised with toString() and evaluated // inside the browser by scripts/device-sweep.mjs, so it may use only what a page // has: no imports, no Node globals, no closure over anything in this file. // // It measures rather than guesses. Every box is compared against its nearest // CLIPPING ancestor instead of document.scrollWidth, which lies the moment any // container carries overflow-x: hidden or clip — and this site's body does, so // a page can slice content off its right edge and still report a scrollWidth // equal to the viewport. That is exactly how the header CTA at iPad portrait // survived a fix and a release. // // It reports eight kinds: clipped, past_viewport, document_scrolls, // media_overflow, sticky_occluded, active_tab_offscreen, tiny_text and // touch_target. Each finding carries a severity, a devtools-pasteable selector // path, and the numbers it was decided on, so a finding can be re-measured // rather than re-argued. // // Provenance: lifted from the Privacy LLC site's scripts/css-qc.mjs, which is // where the thresholds were argued out and where the comments explaining each // one were written. Copied rather than shared because the two repositories have // no common package; if a threshold changes in one, it does not change in the // other. The driver here differs from that one in a way that matters: css-qc // declares Playwright device profiles but only ever calls setViewportSize, so // its deviceScaleFactor, isMobile and hasTouch fields never take effect and it // is a width sweep wearing a phone's clothes. export const audit = function audit() { const EPS = 1; const vw = window.innerWidth; const vh = window.innerHeight; const findings = []; const push = (f) => findings.push(f); /** A selector a human can paste into devtools. Short, not unique-at-all-costs. */ function pathOf(el) { const parts = []; let node = el; while (node && node.nodeType === 1 && parts.length < 4) { let part = node.tagName.toLowerCase(); // `getAttribute`, not `.id`. A