diff --git a/index-1440.png b/index-1440.png deleted file mode 100644 index e5a43297..00000000 Binary files a/index-1440.png and /dev/null differ diff --git a/web/scripts/check-build.mjs b/web/scripts/check-build.mjs index 30e533c7..c43ba4ce 100644 --- a/web/scripts/check-build.mjs +++ b/web/scripts/check-build.mjs @@ -68,10 +68,45 @@ if (!/\.card\{[^}]*min-width:0/.test(css.replace(/\s+/g, ""))) { // --color-bg does not exist in this theme; the page paints --color-ink. { const flat = css.replace(/\s+/g, ""); - if (!/\.cmp[^{]*first-child\)?\{[^}]*position:sticky/.test(flat)) { - fail.push("comparison tables have no sticky first column — the row label scrolls away from its Yes/No cells on mobile"); - } else if (/\.cmp[^{]*first-child\)?\{[^}]*background:var\(--color-bg\)/.test(flat)) { - fail.push("sticky comparison column uses `--color-bg`, which this theme does not define — it resolves to transparent and cells scroll under the label"); + if (!/first-child\)?\{[^}]*position:sticky/.test(flat)) { + fail.push("wide tables have no sticky first column — the row label scrolls away from its cells on mobile"); + } else if (/first-child\)?\{[^}]*background:var\(--color-bg\)/.test(flat)) { + fail.push("sticky column uses `--color-bg`, which this theme does not define — it resolves to transparent and cells scroll under the label"); + } + /* BOTH class names, because the fix was applied to one and not the other. + .cmp got it; .patch -- the interactive routing matrix on the home page, the + worse case at 403px of 736 hidden -- was missed for a full commit. A check + naming only the class that was fixed would have passed throughout. */ + for (const cls of ["cmp", "patch"]) { + if (!new RegExp(`\\.${cls}[,)][^{]*first-child`).test(flat)) { + fail.push(`.${cls} tables are not in the sticky-first-column rule — their label column scrolls away on mobile`); + } + } + /* TWO PSEUDO-ELEMENT AFFORDANCES, and both need a haystack wider than the + CSS bundle plus a pattern looser than the source spelling. + + WHERE. Astro inlines a component's scoped styles into the pages that use + it, so `.shot-open`'s rule is in features.html and in NO file under + _astro/. A check reading only the bundle reports it missing on a build + where it is present and working -- which is how the first version of this + check failed, against a glyph already verified in a browser. + + HOW SPELLED. Lightning CSS rewrites `::after` to the CSS2 `:after`, which + is two bytes shorter and identical in meaning. Matching the source + spelling finds nothing in the output. `::?after` accepts either. */ + const built = flat + readdirSync(DIST) + .filter((f) => f.endsWith(".html")) + .map((f) => readFileSync(join(DIST, f), "utf8").replace(/\s+/g, "")) + .join(""); + + if (!/\.scroll-hint::?after\{/.test(built)) { + fail.push("no `.scroll-hint::after` in the built output — wide tables give no sign they scroll on mobile"); + } + /* The lightbox expand glyph. `cursor: zoom-in` is the only other affordance + and it does not exist on a touch device -- which is the viewport where the + screenshots are smallest and expanding them matters most. */ + if (!/\.shot-open::?after\{/.test(built)) { + fail.push("no `.shot-open::after` in the built output — the lightbox is undiscoverable on touch, where `cursor: zoom-in` means nothing"); } } diff --git a/web/src/components/Lightbox.astro b/web/src/components/Lightbox.astro index 7528b1c4..aecaf6b2 100644 --- a/web/src/components/Lightbox.astro +++ b/web/src/components/Lightbox.astro @@ -107,12 +107,48 @@ does not change the page's appearance -- only its behaviour. */ :global(.shot-open) { display: block; + position: relative; width: 100%; padding: 0; border: 0; background: none; cursor: zoom-in; } + + /* A VISIBLE GLYPH, because `cursor: zoom-in` says nothing to a phone. + + A touch device has no pointer to change shape, so on the viewport where + these images are SMALLEST the affordance was entirely absent: measured at + 390px each 2880x1800 capture renders at 335x210 CSS px, an 8.6x reduction, + and nothing on screen suggested it could be opened. The one reader who most + needs to expand the picture was the one reader never told they could. + + Drawn in the corner rather than centred so it never covers the part of the + screenshot someone is trying to read, and always present rather than + hover-only -- a hover-revealed hint has the same problem as the cursor. */ + :global(.shot-open::after) { + content: ""; + position: absolute; + right: 0.625rem; + bottom: 0.625rem; + width: 1.75rem; + height: 1.75rem; + border: 1px solid var(--color-line-strong, #39435a); + border-radius: 0.5rem; + background-color: rgb(11 13 17 / 0.72); + /* The two-arrow expand mark, inline so it costs no request. currentColor is + not available to a background image, hence the literal stroke. */ + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='%23edf2f8' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 4h4v4M8 16H4v-4M16 4l-5 5M4 16l5-5'/%3E%3C/svg%3E"); + background-position: center; + background-repeat: no-repeat; + background-size: 1rem 1rem; + backdrop-filter: blur(4px); + transition: background-color 140ms ease; + pointer-events: none; + } + :global(.shot-open:hover::after) { + background-color: rgb(11 13 17 / 0.9); + } :global(.shot-open:focus-visible) { outline: 2px solid var(--color-primary-bright, #78a6e8); outline-offset: 3px; diff --git a/web/src/components/MixMatrix.astro b/web/src/components/MixMatrix.astro index fdcad5dc..453e9796 100644 --- a/web/src/components/MixMatrix.astro +++ b/web/src/components/MixMatrix.astro @@ -50,7 +50,7 @@ const DESTS = [
| Capability | -- polyemesis - | -- Restreamer - | -- restream.io - | -
|---|---|---|---|
| {cap} | -{poly} | -- {rs} - | -- {ri} - | -
- All three restream one input to many platforms. The difference is what - happens to the audio on the way: polyemesis is the only one that lets - this platform take the clean mix while that one takes - the full mix, from a single upload and without re-encoding the picture. -
- + It cost authority rather than bytes. A reader who studied it here and + met the same table again on /comparison learned that the page named + for the comparison had nothing further to offer, which is the wrong + thing to teach about the page doing the arguing. Keeping two copies + honest also needed a build check, and that check existed because they + had already drifted twice. + + What is left is the one row that is actually the product's thesis, and + a door to the page that carries the evidence. The claim still appears + on this page; the argument now lives in exactly one place. */} ++ The row that matters +
++ Per-destination audio mix from one multitrack ingest. +
++ polyemesis Yes · + Restreamer No · + restream.io No +
++ All three restream one input to many platforms. The difference is what + happens to the audio on the way: polyemesis is the only one that lets + this platform take the clean mix while that one takes + the full mix, from a single upload and without re-encoding the picture. +
+ +